Patch failure handling updates, added a 'patch' concrete class, so we can apply patches to quilt-native itself.

git-svn-id: https://svn.o-hand.com/repos/poky/trunk@670 311d38ba-8fff-0310-9ca6-ca027cbcb966
This commit is contained in:
Chris Larson 2006-08-28 20:36:36 +00:00
parent 01056cdbea
commit ecb32bdee7
3 changed files with 160 additions and 99 deletions

View File

@ -13,9 +13,11 @@ def base_dep_prepend(d):
# INHIBIT_DEFAULT_DEPS doesn't apply to the patch command. Whether or not # INHIBIT_DEFAULT_DEPS doesn't apply to the patch command. Whether or not
# we need that built is the responsibility of the patch function / class, not # we need that built is the responsibility of the patch function / class, not
# the application. # the application.
patchdeps = bb.data.getVar("PATCH_DEPENDS", d, 1) patchdeps = bb.data.getVar("PATCHTOOL", d, 1)
if patchdeps and not patchdeps in bb.data.getVar("PROVIDES", d, 1): if patchdeps:
deps = patchdeps patchdeps = "%s-native" % patchdeps
if not patchdeps in bb.data.getVar("PROVIDES", d, 1):
deps = patchdeps
if not bb.data.getVar('INHIBIT_DEFAULT_DEPS', d): if not bb.data.getVar('INHIBIT_DEFAULT_DEPS', d):
if (bb.data.getVar('HOST_SYS', d, 1) != if (bb.data.getVar('HOST_SYS', d, 1) !=
@ -685,7 +687,7 @@ python () {
# Patch handling # Patch handling
inherit patch inherit patch
EXPORT_FUNCTIONS do_clean do_mrproper do_fetch do_unpack do_configure do_compile do_install do_package do_patch do_populate_pkgs do_stage EXPORT_FUNCTIONS do_clean do_mrproper do_fetch do_unpack do_configure do_compile do_install do_package do_populate_pkgs do_stage
MIRRORS[func] = "0" MIRRORS[func] = "0"
MIRRORS () { MIRRORS () {

View File

@ -1,9 +1,11 @@
def patch_init(): # Copyright (C) 2006 OpenedHand LTD
def patch_init(d):
import os, sys import os, sys
def md5sum(fname): def md5sum(fname):
import md5, sys import md5, sys
f = file(fname, 'rb') f = file(fname, 'rb')
m = md5.new() m = md5.new()
while True: while True:
@ -13,12 +15,12 @@ def patch_init():
m.update(d) m.update(d)
f.close() f.close()
return m.hexdigest() return m.hexdigest()
class CmdError(Exception): class CmdError(Exception):
def __init__(self, exitstatus, output): def __init__(self, exitstatus, output):
self.status = exitstatus self.status = exitstatus
self.output = output self.output = output
def __str__(self): def __str__(self):
return "Command Error: exit status: %d Output:\n%s" % (self.status, self.output) return "Command Error: exit status: %d Output:\n%s" % (self.status, self.output)
@ -27,17 +29,17 @@ def patch_init():
self.path = path self.path = path
def __str__(self): def __str__(self):
return "Error: %s not found." % self.path return "Error: %s not found." % self.path
def runcmd(args, dir = None): def runcmd(args, dir = None):
import commands import commands
if dir: if dir:
olddir = os.path.abspath(os.curdir) olddir = os.path.abspath(os.curdir)
if not os.path.exists(dir): if not os.path.exists(dir):
raise NotFoundError(dir) raise NotFoundError(dir)
os.chdir(dir) os.chdir(dir)
# print("cwd: %s -> %s" % (olddir, self.dir)) # print("cwd: %s -> %s" % (olddir, self.dir))
try: try:
args = [ commands.mkarg(str(arg)) for arg in args ] args = [ commands.mkarg(str(arg)) for arg in args ]
cmd = " ".join(args) cmd = " ".join(args)
@ -46,92 +48,143 @@ def patch_init():
if exitstatus != 0: if exitstatus != 0:
raise CmdError(exitstatus >> 8, output) raise CmdError(exitstatus >> 8, output)
return output return output
finally: finally:
if dir: if dir:
os.chdir(olddir) os.chdir(olddir)
class PatchError(Exception): class PatchError(Exception):
def __init__(self, msg): def __init__(self, msg):
self.msg = msg self.msg = msg
def __str__(self): def __str__(self):
return "Patch Error: %s" % self.msg return "Patch Error: %s" % self.msg
import bb, bb.data, bb.fetch import bb, bb.data, bb.fetch
class PatchSet(object): class PatchSet(object):
defaults = { defaults = {
"strippath": 1 "strippath": 1
} }
def __init__(self, dir, d): def __init__(self, dir, d):
self.dir = dir self.dir = dir
self.d = d self.d = d
self.patches = [] self.patches = []
self._current = None self._current = None
def current(self): def current(self):
return self._current return self._current
def Clean(self): def Clean(self):
""" """
Clean out the patch set. Generally includes unapplying all Clean out the patch set. Generally includes unapplying all
patches and wiping out all associated metadata. patches and wiping out all associated metadata.
""" """
raise NotImplementedError() raise NotImplementedError()
def Import(self, patch, force): def Import(self, patch, force):
if not patch.get("file"): if not patch.get("file"):
if not patch.get("remote"): if not patch.get("remote"):
raise PatchError("Patch file must be specified in patch import.") raise PatchError("Patch file must be specified in patch import.")
else: else:
patch["file"] = bb.fetch.localpath(patch["remote"], self.d) patch["file"] = bb.fetch.localpath(patch["remote"], self.d)
for param in PatchSet.defaults: for param in PatchSet.defaults:
if not patch.get(param): if not patch.get(param):
patch[param] = PatchSet.defaults[param] patch[param] = PatchSet.defaults[param]
if patch.get("remote"): if patch.get("remote"):
patch["file"] = bb.data.expand(bb.fetch.localpath(patch["remote"], self.d), self.d) patch["file"] = bb.data.expand(bb.fetch.localpath(patch["remote"], self.d), self.d)
patch["filemd5"] = md5sum(patch["file"]) patch["filemd5"] = md5sum(patch["file"])
def Push(self, force): def Push(self, force):
raise NotImplementedError() raise NotImplementedError()
def Pop(self, force): def Pop(self, force):
raise NotImplementedError() raise NotImplementedError()
def Refresh(self, remote = None, all = None): def Refresh(self, remote = None, all = None):
raise NotImplementedError() raise NotImplementedError()
class PatchTree(PatchSet):
def __init__(self, dir, d):
PatchSet.__init__(self, dir, d)
def Import(self, patch, force = None):
""""""
PatchSet.Import(self, patch, force)
self.patches.insert(self._current or 0, patch)
def _applypatch(self, patch, force = None, reverse = None):
shellcmd = ["patch", "<", patch['file'], "-p", patch['strippath']]
if reverse:
shellcmd.append('-R')
shellcmd.append('--dry-run')
try:
output = runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
except CmdError:
if force:
shellcmd.pop(len(shellcmd) - 1)
output = runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
else:
import sys
raise sys.exc_value
return output
def Push(self, force = None, all = None):
if all:
for i in self.patches:
if self._current is not None:
self._current = self._current + 1
else:
self._current = 0
self._applypatch(i, force)
else:
if self._current is not None:
self._current = self._current + 1
else:
self._current = 0
self._applypatch(self.patches[self._current], force)
def Pop(self, force = None, all = None):
if all:
for i in self.patches:
self._applypatch(i, force, True)
else:
self._applypatch(self.patches[self._current], force, True)
def Clean(self):
""""""
class QuiltTree(PatchSet): class QuiltTree(PatchSet):
def _runcmd(self, args): def _runcmd(self, args):
runcmd(["quilt"] + args, self.dir) runcmd(["quilt"] + args, self.dir)
def _quiltpatchpath(self, file): def _quiltpatchpath(self, file):
return os.path.join(self.dir, "patches", os.path.basename(file)) return os.path.join(self.dir, "patches", os.path.basename(file))
def __init__(self, dir, d): def __init__(self, dir, d):
PatchSet.__init__(self, dir, d) PatchSet.__init__(self, dir, d)
self.initialized = False self.initialized = False
def Clean(self): def Clean(self):
try: try:
self._runcmd(["pop", "-a", "-f"]) self._runcmd(["pop", "-a", "-f"])
except CmdError: except CmdError:
if sys.exc_value.output.strip() == "No patch removed": pass
pass
else:
raise PatchError("Unable to clean patches from tree:\n"+str(sys.exc_value))
except NotFoundError: except NotFoundError:
pass pass
runcmd(["rm", "-rf", os.path.join(self.dir, "patches"), os.path.join(self.dir, ".pc")]) # runcmd(["rm", "-rf", os.path.join(self.dir, "patches"), os.path.join(self.dir, ".pc")])
self.initialized = True self.initialized = True
def InitFromDir(self): def InitFromDir(self):
# read series -> self.patches # read series -> self.patches
seriespath = os.path.join(self.dir, 'patches', 'series') seriespath = os.path.join(self.dir, 'patches', 'series')
@ -148,7 +201,7 @@ def patch_init():
patch["strippath"] = parts[1][2:] patch["strippath"] = parts[1][2:]
self.patches.append(patch) self.patches.append(patch)
series.close() series.close()
# determine which patches are applied -> self._current # determine which patches are applied -> self._current
try: try:
output = runcmd(["quilt", "applied"], self.dir) output = runcmd(["quilt", "applied"], self.dir)
@ -162,45 +215,45 @@ def patch_init():
if os.path.basename(patch["quiltfile"]) == output[-1]: if os.path.basename(patch["quiltfile"]) == output[-1]:
self._current = self.patches.index(patch) self._current = self.patches.index(patch)
self.initialized = True self.initialized = True
def Import(self, patch, force = None): def Import(self, patch, force = None):
if not self.initialized: if not self.initialized:
self.InitFromDir() self.InitFromDir()
PatchSet.Import(self, patch, force) PatchSet.Import(self, patch, force)
args = ["import", "-p", patch["strippath"]] args = ["import", "-p", patch["strippath"]]
if force: if force:
args.append("-f") args.append("-f")
args.append(patch["file"]) args.append(patch["file"])
self._runcmd(args) self._runcmd(args)
patch["quiltfile"] = self._quiltpatchpath(patch["file"]) patch["quiltfile"] = self._quiltpatchpath(patch["file"])
patch["quiltfilemd5"] = md5sum(patch["quiltfile"]) patch["quiltfilemd5"] = md5sum(patch["quiltfile"])
# TODO: determine if the file being imported: # TODO: determine if the file being imported:
# 1) is already imported, and is the same # 1) is already imported, and is the same
# 2) is already imported, but differs # 2) is already imported, but differs
self.patches.insert(self._current or 0, patch) self.patches.insert(self._current or 0, patch)
def Push(self, force = None, all = None): def Push(self, force = None, all = None):
# quilt push [-f] # quilt push [-f]
args = ["push"] args = ["push"]
if force: if force:
args.append("-f") args.append("-f")
if all: if all:
args.append("-a") args.append("-a")
self._runcmd(args) self._runcmd(args)
if self._current is not None: if self._current is not None:
self._current = self._current + 1 self._current = self._current + 1
else: else:
self._current = 0 self._current = 0
def Pop(self, force = None, all = None): def Pop(self, force = None, all = None):
# quilt pop [-f] # quilt pop [-f]
args = ["pop"] args = ["pop"]
@ -208,15 +261,15 @@ def patch_init():
args.append("-f") args.append("-f")
if all: if all:
args.append("-a") args.append("-a")
self._runcmd(args) self._runcmd(args)
if self._current == 0: if self._current == 0:
self._current = None self._current = None
if self._current is not None: if self._current is not None:
self._current = self._current - 1 self._current = self._current - 1
def Refresh(self, **kwargs): def Refresh(self, **kwargs):
if kwargs.get("remote"): if kwargs.get("remote"):
patch = self.patches[kwargs["patch"]] patch = self.patches[kwargs["patch"]]
@ -227,7 +280,7 @@ def patch_init():
import shutil import shutil
if not patch.get("file") and patch.get("remote"): if not patch.get("file") and patch.get("remote"):
patch["file"] = bb.fetch.localpath(patch["remote"], self.d) patch["file"] = bb.fetch.localpath(patch["remote"], self.d)
shutil.copyfile(patch["quiltfile"], patch["file"]) shutil.copyfile(patch["quiltfile"], patch["file"])
else: else:
raise PatchError("Unable to do a remote refresh of %s, unsupported remote url scheme %s." % (os.path.basename(patch["quiltfile"]), type)) raise PatchError("Unable to do a remote refresh of %s, unsupported remote url scheme %s." % (os.path.basename(patch["quiltfile"]), type))
@ -239,31 +292,31 @@ def patch_init():
elif kwargs.get("patch"): elif kwargs.get("patch"):
args.append(os.path.basename(self.patches[kwargs["patch"]]["quiltfile"])) args.append(os.path.basename(self.patches[kwargs["patch"]]["quiltfile"]))
self._runcmd(args) self._runcmd(args)
class Resolver(object): class Resolver(object):
def __init__(self, patchset): def __init__(self, patchset):
raise NotImplementedError() raise NotImplementedError()
def Resolve(self): def Resolve(self):
raise NotImplementedError() raise NotImplementedError()
def Revert(self): def Revert(self):
raise NotImplementedError() raise NotImplementedError()
def Finalize(self): def Finalize(self):
raise NotImplementedError() raise NotImplementedError()
# Patch resolver which relies on the user doing all the work involved in the # Patch resolver which relies on the user doing all the work involved in the
# resolution, with the exception of refreshing the remote copy of the patch # resolution, with the exception of refreshing the remote copy of the patch
# files (the urls). # files (the urls).
class UserResolver(Resolver): class UserResolver(Resolver):
def __init__(self, patchset): def __init__(self, patchset):
self.patchset = patchset self.patchset = patchset
# Force a push in the patchset, then drop to a shell for the user to # Force a push in the patchset, then drop to a shell for the user to
# resolve any rejected hunks # resolve any rejected hunks
def Resolve(self): def Resolve(self):
olddir = os.path.abspath(os.curdir) olddir = os.path.abspath(os.curdir)
os.chdir(self.patchset.dir) os.chdir(self.patchset.dir)
try: try:
@ -274,21 +327,21 @@ def patch_init():
return return
print(sys.exc_value) print(sys.exc_value)
print('NOTE: dropping user into a shell, so that patch rejects can be fixed manually.') print('NOTE: dropping user into a shell, so that patch rejects can be fixed manually.')
os.system('/bin/sh') os.system('/bin/sh')
# Construct a new PatchSet after the user's changes, compare the # Construct a new PatchSet after the user's changes, compare the
# sets, checking patches for modifications, and doing a remote # sets, checking patches for modifications, and doing a remote
# refresh on each. # refresh on each.
oldpatchset = self.patchset oldpatchset = self.patchset
self.patchset = oldpatchset.__class__(self.patchset.dir, self.patchset.d) self.patchset = oldpatchset.__class__(self.patchset.dir, self.patchset.d)
for patch in self.patchset.patches: for patch in self.patchset.patches:
oldpatch = None oldpatch = None
for opatch in oldpatchset.patches: for opatch in oldpatchset.patches:
if opatch["quiltfile"] == patch["quiltfile"]: if opatch["quiltfile"] == patch["quiltfile"]:
oldpatch = opatch oldpatch = opatch
if oldpatch: if oldpatch:
patch["remote"] = oldpatch["remote"] patch["remote"] = oldpatch["remote"]
if patch["quiltfile"] == oldpatch["quiltfile"]: if patch["quiltfile"] == oldpatch["quiltfile"]:
@ -303,17 +356,18 @@ def patch_init():
os.chdir(olddir) os.chdir(olddir)
raise raise
os.chdir(olddir) os.chdir(olddir)
# Throw away the changes to the patches in the patchset made by resolve() # Throw away the changes to the patches in the patchset made by resolve()
def Revert(self): def Revert(self):
raise NotImplementedError() raise NotImplementedError()
# Apply the changes to the patches in the patchset made by resolve() # Apply the changes to the patches in the patchset made by resolve()
def Finalize(self): def Finalize(self):
raise NotImplementedError() raise NotImplementedError()
g = globals() g = globals()
g["PatchSet"] = PatchSet g["PatchSet"] = PatchSet
g["PatchTree"] = PatchTree
g["QuiltTree"] = QuiltTree g["QuiltTree"] = QuiltTree
g["Resolver"] = Resolver g["Resolver"] = Resolver
g["UserResolver"] = UserResolver g["UserResolver"] = UserResolver
@ -322,45 +376,48 @@ def patch_init():
addtask patch after do_unpack addtask patch after do_unpack
do_patch[dirs] = "${WORKDIR}" do_patch[dirs] = "${WORKDIR}"
python base_do_patch() { python patch_do_patch() {
import re import re
import bb.fetch import bb.fetch
patch_init() patch_init(d)
src_uri = (bb.data.getVar('SRC_URI', d, 1) or '').split() src_uri = (bb.data.getVar('SRC_URI', d, 1) or '').split()
if not src_uri: if not src_uri:
return return
patchsetmap = { patchsetmap = {
"patch": PatchTree,
"quilt": QuiltTree, "quilt": QuiltTree,
} }
cls = patchsetmap[bb.data.getVar('PATCHTOOL', d, 1) or 'quilt'] cls = patchsetmap[bb.data.getVar('PATCHTOOL', d, 1) or 'quilt']
resolvermap = { resolvermap = {
"user": UserResolver, "user": UserResolver,
} }
rcls = resolvermap[bb.data.getVar('PATCHRESOLVE', d, 1) or 'user'] rcls = resolvermap[bb.data.getVar('PATCHRESOLVE', d, 1) or 'user']
s = bb.data.getVar('S', d, 1) s = bb.data.getVar('S', d, 1)
path = os.getenv('PATH')
os.putenv('PATH', bb.data.getVar('PATH', d, 1))
patchset = cls(s, d) patchset = cls(s, d)
patchset.Clean() patchset.Clean()
resolver = rcls(patchset) resolver = rcls(patchset)
workdir = bb.data.getVar('WORKDIR', d, 1) workdir = bb.data.getVar('WORKDIR', d, 1)
for url in src_uri: for url in src_uri:
(type, host, path, user, pswd, parm) = bb.decodeurl(url) (type, host, path, user, pswd, parm) = bb.decodeurl(url)
if not "patch" in parm: if not "patch" in parm:
continue continue
bb.fetch.init([url],d) bb.fetch.init([url],d)
url = bb.encodeurl((type, host, path, user, pswd, [])) url = bb.encodeurl((type, host, path, user, pswd, []))
local = os.path.join('/', bb.fetch.localpath(url, d)) local = os.path.join('/', bb.fetch.localpath(url, d))
# did it need to be unpacked? # did it need to be unpacked?
dots = os.path.basename(local).split(".") dots = os.path.basename(local).split(".")
if dots[-1] in ['gz', 'bz2', 'Z']: if dots[-1] in ['gz', 'bz2', 'Z']:
@ -368,45 +425,45 @@ python base_do_patch() {
else: else:
unpacked = local unpacked = local
unpacked = bb.data.expand(unpacked, d) unpacked = bb.data.expand(unpacked, d)
if "pnum" in parm: if "pnum" in parm:
pnum = parm["pnum"] pnum = parm["pnum"]
else: else:
pnum = "1" pnum = "1"
if "pname" in parm: if "pname" in parm:
pname = parm["pname"] pname = parm["pname"]
else: else:
pname = os.path.basename(unpacked) pname = os.path.basename(unpacked)
if "mindate" in parm: if "mindate" in parm:
mindate = parm["mindate"] mindate = parm["mindate"]
else: else:
mindate = 0 mindate = 0
if "maxdate" in parm: if "maxdate" in parm:
maxdate = parm["maxdate"] maxdate = parm["maxdate"]
else: else:
maxdate = "20711226" maxdate = "20711226"
pn = bb.data.getVar('PN', d, 1) pn = bb.data.getVar('PN', d, 1)
srcdate = bb.data.getVar('SRCDATE_%s' % pn, d, 1) srcdate = bb.data.getVar('SRCDATE_%s' % pn, d, 1)
if not srcdate: if not srcdate:
srcdate = bb.data.getVar('SRCDATE', d, 1) srcdate = bb.data.getVar('SRCDATE', d, 1)
if srcdate == "now": if srcdate == "now":
srcdate = bb.data.getVar('DATE', d, 1) srcdate = bb.data.getVar('DATE', d, 1)
if (maxdate < srcdate) or (mindate > srcdate): if (maxdate < srcdate) or (mindate > srcdate):
if (maxdate < srcdate): if (maxdate < srcdate):
bb.note("Patch '%s' is outdated" % pname) bb.note("Patch '%s' is outdated" % pname)
if (mindate > srcdate): if (mindate > srcdate):
bb.note("Patch '%s' is predated" % pname) bb.note("Patch '%s' is predated" % pname)
continue continue
bb.note("Applying patch '%s'" % pname) bb.note("Applying patch '%s'" % pname)
try: try:
patchset.Import({"file":unpacked, "remote":url, "strippath": pnum}, True) patchset.Import({"file":unpacked, "remote":url, "strippath": pnum}, True)
@ -415,3 +472,5 @@ python base_do_patch() {
raise bb.build.FuncFailed(str(sys.exc_value)) raise bb.build.FuncFailed(str(sys.exc_value))
resolver.Resolve() resolver.Resolve()
} }
EXPORT_FUNCTIONS do_patch

View File

@ -238,6 +238,12 @@ export BUILD_STRIP = "${BUILD_PREFIX}strip"
export MAKE = "make" export MAKE = "make"
EXTRA_OEMAKE = "-e MAKEFLAGS=" EXTRA_OEMAKE = "-e MAKEFLAGS="
##################################################################
# Patch handling.
##################################################################
PATCHTOOL = 'quilt'
PATCHRESOLVE = 'user'
################################################################## ##################################################################
# Build flags and options. # Build flags and options.
################################################################## ##################################################################
@ -336,12 +342,6 @@ SRC_URI = "file://${FILE}"
MKTEMPDIRCMD = "mktemp -d -q ${TMPBASE}" MKTEMPDIRCMD = "mktemp -d -q ${TMPBASE}"
MKTEMPCMD = "mktemp -q ${TMPBASE}" MKTEMPCMD = "mktemp -q ${TMPBASE}"
# Program to be used to patch sources, use 'inherit patcher' to overwrite this:
PATCHCLEANCMD = 'if [ -n "`quilt applied`" ]; then quilt pop -a -R -f || exit 1; fi'
PATCHCMD = "pnum='%s'; name='%s'; patch='%s'; mkdir -p patches ; quilt upgrade >/dev/null 2>&1; quilt import -f -p $pnum -P $name $patch; chmod u+w patches/$name; quilt push"
PATCH_DEPENDS = "quilt-native"
# GNU patch tries to be intellgent about checking out read-only files from # GNU patch tries to be intellgent about checking out read-only files from
# a RCS, which freaks out those special folks with active Perforce clients # a RCS, which freaks out those special folks with active Perforce clients
# the following makes patch ignore RCS: # the following makes patch ignore RCS: