diff options
Diffstat (limited to 'meta/classes/sstate.bbclass')
-rw-r--r-- | meta/classes/sstate.bbclass | 281 |
1 files changed, 211 insertions, 70 deletions
diff --git a/meta/classes/sstate.bbclass b/meta/classes/sstate.bbclass index b47b9c23bf..62e45cb4a8 100644 --- a/meta/classes/sstate.bbclass +++ b/meta/classes/sstate.bbclass @@ -3,19 +3,41 @@ SSTATE_VERSION = "3" SSTATE_MANIFESTS ?= "${TMPDIR}/sstate-control" SSTATE_MANFILEPREFIX = "${SSTATE_MANIFESTS}/manifest-${SSTATE_MANMACH}-${PN}" -def generate_sstatefn(spec, hash, d): +def generate_sstatefn(spec, hash, taskname, siginfo, d): + if taskname is None: + return "" + extension = ".tgz" + # 8 chars reserved for siginfo + limit = 254 - 8 + if siginfo: + limit = 254 + extension = ".tgz.siginfo" if not hash: hash = "INVALID" - return hash[:2] + "/" + spec + hash + fn = spec + hash + "_" + taskname + extension + # If the filename is too long, attempt to reduce it + if len(fn) > limit: + components = spec.split(":") + # Fields 0,5,6 are mandatory, 1 is most useful, 2,3,4 are just for information + # 7 is for the separators + avail = (254 - len(hash + "_" + taskname + extension) - len(components[0]) - len(components[1]) - len(components[5]) - len(components[6]) - 7) // 3 + components[2] = components[2][:avail] + components[3] = components[3][:avail] + components[4] = components[4][:avail] + spec = ":".join(components) + fn = spec + hash + "_" + taskname + extension + if len(fn) > limit: + bb.fatal("Unable to reduce sstate name to less than 255 chararacters") + return hash[:2] + "/" + hash[2:4] + "/" + fn SSTATE_PKGARCH = "${PACKAGE_ARCH}" SSTATE_PKGSPEC = "sstate:${PN}:${PACKAGE_ARCH}${TARGET_VENDOR}-${TARGET_OS}:${PV}:${PR}:${SSTATE_PKGARCH}:${SSTATE_VERSION}:" SSTATE_SWSPEC = "sstate:${PN}::${PV}:${PR}::${SSTATE_VERSION}:" -SSTATE_PKGNAME = "${SSTATE_EXTRAPATH}${@generate_sstatefn(d.getVar('SSTATE_PKGSPEC'), d.getVar('BB_UNIHASH'), d)}" +SSTATE_PKGNAME = "${SSTATE_EXTRAPATH}${@generate_sstatefn(d.getVar('SSTATE_PKGSPEC'), d.getVar('BB_UNIHASH'), d.getVar('SSTATE_CURRTASK'), False, d)}" SSTATE_PKG = "${SSTATE_DIR}/${SSTATE_PKGNAME}" SSTATE_EXTRAPATH = "" SSTATE_EXTRAPATHWILDCARD = "" -SSTATE_PATHSPEC = "${SSTATE_DIR}/${SSTATE_EXTRAPATHWILDCARD}*/${SSTATE_PKGSPEC}" +SSTATE_PATHSPEC = "${SSTATE_DIR}/${SSTATE_EXTRAPATHWILDCARD}*/*/${SSTATE_PKGSPEC}*_${SSTATE_PATH_CURRTASK}.tgz*" # explicitly make PV to depend on evaluated value of PV variable PV[vardepvalue] = "${PV}" @@ -50,6 +72,7 @@ BB_HASHFILENAME = "False ${SSTATE_PKGSPEC} ${SSTATE_SWSPEC}" SSTATE_ARCHS = " \ ${BUILD_ARCH} \ + ${BUILD_ARCH}_${ORIGNATIVELSBSTRING} \ ${BUILD_ARCH}_${SDK_ARCH}_${SDK_OS} \ ${BUILD_ARCH}_${TARGET_ARCH} \ ${SDK_ARCH}_${SDK_OS} \ @@ -58,6 +81,7 @@ SSTATE_ARCHS = " \ ${PACKAGE_ARCH} \ ${PACKAGE_EXTRA_ARCHS} \ ${MACHINE_ARCH}" +SSTATE_ARCHS[vardepsexclude] = "ORIGNATIVELSBSTRING" SSTATE_MANMACH ?= "${SSTATE_PKGARCH}" @@ -293,6 +317,8 @@ def sstate_install(ss, d): if os.path.exists(i): with open(i, "r") as f: manifests = f.readlines() + # We append new entries, we don't remove older entries which may have the same + # manifest name but different versions from stamp/workdir. See below. if filedata not in manifests: with open(i, "a+") as f: f.write(filedata) @@ -317,8 +343,9 @@ def sstate_installpkg(ss, d): from oe.gpg_sign import get_signer sstateinst = d.expand("${WORKDIR}/sstate-install-%s/" % ss['task']) - sstatefetch = d.getVar('SSTATE_PKGNAME') + '_' + ss['task'] + ".tgz" - sstatepkg = d.getVar('SSTATE_PKG') + '_' + ss['task'] + ".tgz" + d.setVar("SSTATE_CURRTASK", ss['task']) + sstatefetch = d.getVar('SSTATE_PKGNAME') + sstatepkg = d.getVar('SSTATE_PKG') if not os.path.exists(sstatepkg): pstaging_fetch(sstatefetch, d) @@ -330,9 +357,11 @@ def sstate_installpkg(ss, d): sstate_clean(ss, d) d.setVar('SSTATE_INSTDIR', sstateinst) - d.setVar('SSTATE_PKG', sstatepkg) if bb.utils.to_boolean(d.getVar("SSTATE_VERIFY_SIG"), False): + if not os.path.isfile(sstatepkg + '.sig'): + bb.warn("No signature file for sstate package %s, skipping acceleration..." % sstatepkg) + return False signer = get_signer(d, 'local') if not signer.verify(sstatepkg + '.sig'): bb.warn("Cannot verify signature on sstate package %s, skipping acceleration..." % sstatepkg) @@ -372,7 +401,7 @@ def sstate_installpkgdir(ss, d): for state in ss['dirs']: prepdir(state[1]) - os.rename(sstateinst + state[0], state[1]) + bb.utils.rename(sstateinst + state[0], state[1]) sstate_install(ss, d) for plain in ss['plaindirs']: @@ -384,7 +413,7 @@ def sstate_installpkgdir(ss, d): dest = plain bb.utils.mkdirhier(src) prepdir(dest) - os.rename(src, dest) + bb.utils.rename(src, dest) return True @@ -440,8 +469,9 @@ python sstate_hardcode_path_unpack () { def sstate_clean_cachefile(ss, d): import oe.path - sstatepkgfile = d.getVar('SSTATE_PATHSPEC') + "*_" + ss['task'] + ".tgz*" if d.getVarFlag('do_%s' % ss['task'], 'task'): + d.setVar("SSTATE_PATH_CURRTASK", ss['task']) + sstatepkgfile = d.getVar('SSTATE_PATHSPEC') bb.note("Removing %s" % sstatepkgfile) oe.path.remove(sstatepkgfile) @@ -451,7 +481,7 @@ def sstate_clean_cachefiles(d): ss = sstate_state_fromvars(ld, task) sstate_clean_cachefile(ss, ld) -def sstate_clean_manifest(manifest, d, prefix=None): +def sstate_clean_manifest(manifest, d, canrace=False, prefix=None): import oe.path mfile = open(manifest) @@ -469,7 +499,9 @@ def sstate_clean_manifest(manifest, d, prefix=None): if entry.endswith("/"): if os.path.islink(entry[:-1]): os.remove(entry[:-1]) - elif os.path.exists(entry) and len(os.listdir(entry)) == 0: + elif os.path.exists(entry) and len(os.listdir(entry)) == 0 and not canrace: + # Removing directories whilst builds are in progress exposes a race. Only + # do it in contexts where it is safe to do so. os.rmdir(entry[:-1]) else: os.remove(entry) @@ -507,7 +539,7 @@ def sstate_clean(ss, d): for lock in ss['lockfiles']: locks.append(bb.utils.lockfile(lock)) - sstate_clean_manifest(manifest, d) + sstate_clean_manifest(manifest, d, canrace=True) for lock in locks: bb.utils.unlockfile(lock) @@ -612,10 +644,9 @@ def sstate_package(ss, d): tmpdir = d.getVar('TMPDIR') sstatebuild = d.expand("${WORKDIR}/sstate-build-%s/" % ss['task']) - sstatepkg = d.getVar('SSTATE_PKG') + '_'+ ss['task'] + ".tgz" + d.setVar("SSTATE_CURRTASK", ss['task']) bb.utils.remove(sstatebuild, recurse=True) bb.utils.mkdirhier(sstatebuild) - bb.utils.mkdirhier(os.path.dirname(sstatepkg)) for state in ss['dirs']: if not os.path.exists(state[1]): continue @@ -635,7 +666,7 @@ def sstate_package(ss, d): continue bb.error("sstate found an absolute path symlink %s pointing at %s. Please replace this with a relative link." % (srcpath, link)) bb.debug(2, "Preparing tree %s for packaging at %s" % (state[1], sstatebuild + state[0])) - os.rename(state[1], sstatebuild + state[0]) + bb.utils.rename(state[1], sstatebuild + state[0]) workdir = d.getVar('WORKDIR') sharedworkdir = os.path.join(d.getVar('TMPDIR'), "work-shared") @@ -645,10 +676,9 @@ def sstate_package(ss, d): pdir = plain.replace(sharedworkdir, sstatebuild) bb.utils.mkdirhier(plain) bb.utils.mkdirhier(pdir) - os.rename(plain, pdir) + bb.utils.rename(plain, pdir) d.setVar('SSTATE_BUILDDIR', sstatebuild) - d.setVar('SSTATE_PKG', sstatepkg) d.setVar('SSTATE_INSTDIR', sstatebuild) if d.getVar('SSTATE_SKIP_CREATION') == '1': @@ -664,10 +694,25 @@ def sstate_package(ss, d): # All hooks should run in SSTATE_BUILDDIR. bb.build.exec_func(f, d, (sstatebuild,)) - bb.siggen.dump_this_task(sstatepkg + ".siginfo", d) + # SSTATE_PKG may have been changed by sstate_report_unihash + siginfo = d.getVar('SSTATE_PKG') + ".siginfo" + if not os.path.exists(siginfo): + bb.siggen.dump_this_task(siginfo, d) + else: + try: + os.utime(siginfo, None) + except PermissionError: + pass + except OSError as e: + # Handle read-only file systems gracefully + import errno + if e.errno != errno.EROFS: + raise e return +sstate_package[vardepsexclude] += "SSTATE_SIG_KEY" + def pstaging_fetch(sstatefetch, d): import bb.fetch2 @@ -686,6 +731,7 @@ def pstaging_fetch(sstatefetch, d): localdata.setVar('FILESPATH', dldir) localdata.setVar('DL_DIR', dldir) localdata.setVar('PREMIRRORS', mirrors) + localdata.setVar('SRCPV', d.getVar('SRCPV')) # if BB_NO_NETWORK is set but we also have SSTATE_MIRROR_ALLOW_NETWORK, # we'll want to allow network access for the current set of fetches. @@ -704,10 +750,14 @@ def pstaging_fetch(sstatefetch, d): localdata.setVar('SRC_URI', srcuri) try: fetcher = bb.fetch2.Fetch([srcuri], localdata, cache=False) + fetcher.checkstatus() fetcher.download() except bb.fetch2.BBFetchException: - break + pass + +pstaging_fetch[vardepsexclude] += "SRCPV" + def sstate_setscene(d): shared_state = sstate_state_fromvars(d) @@ -748,18 +798,20 @@ sstate_task_postfunc[dirs] = "${WORKDIR}" # set as SSTATE_BUILDDIR. Will be run from within SSTATE_BUILDDIR. # sstate_create_package () { - TFILE=`mktemp ${SSTATE_PKG}.XXXXXXXX` - - # Exit earlu if it already exists + # Exit early if it already exists if [ -e ${SSTATE_PKG} ]; then + [ ! -w ${SSTATE_PKG} ] || touch ${SSTATE_PKG} return fi - # Use pigz if available - OPT="-czS" - if [ -x "$(command -v pigz)" ]; then - OPT="-I pigz -cS" - fi + mkdir --mode=0775 -p `dirname ${SSTATE_PKG}` + TFILE=`mktemp ${SSTATE_PKG}.XXXXXXXX` + + # Use pigz if available + OPT="-czS" + if [ -x "$(command -v pigz)" ]; then + OPT="-I pigz -cS" + fi # Need to handle empty directories if [ "$(ls -A)" ]; then @@ -776,10 +828,13 @@ sstate_create_package () { chmod 0664 $TFILE # Skip if it was already created by some other process if [ ! -e ${SSTATE_PKG} ]; then - mv -f $TFILE ${SSTATE_PKG} + # Move into place using ln to attempt an atomic op. + # Abort if it already exists + ln $TFILE ${SSTATE_PKG} && rm $TFILE else rm $TFILE fi + [ ! -w ${SSTATE_PKG} ] || touch ${SSTATE_PKG} } python sstate_sign_package () { @@ -809,7 +864,7 @@ python sstate_report_unihash() { sstate_unpack_package () { tar -xvzf ${SSTATE_PKG} # update .siginfo atime on local/NFS mirror - [ -w ${SSTATE_PKG}.siginfo ] && [ -h ${SSTATE_PKG}.siginfo ] && touch -a ${SSTATE_PKG}.siginfo + [ -O ${SSTATE_PKG}.siginfo ] && [ -w ${SSTATE_PKG}.siginfo ] && [ -h ${SSTATE_PKG}.siginfo ] && touch -a ${SSTATE_PKG}.siginfo # Use "! -w ||" to return true for read only files [ ! -w ${SSTATE_PKG} ] || touch --no-dereference ${SSTATE_PKG} [ ! -w ${SSTATE_PKG}.sig ] || [ ! -e ${SSTATE_PKG}.sig ] || touch --no-dereference ${SSTATE_PKG}.sig @@ -818,12 +873,11 @@ sstate_unpack_package () { BB_HASHCHECK_FUNCTION = "sstate_checkhashes" -def sstate_checkhashes(sq_data, d, siginfo=False, currentcount=0, **kwargs): +def sstate_checkhashes(sq_data, d, siginfo=False, currentcount=0, summary=True, **kwargs): found = set() + foundLocal = set() + foundNet = set() missed = set() - extension = ".tgz" - if siginfo: - extension = extension + ".siginfo" def gethash(task): return sq_data['unihash'][task] @@ -850,11 +904,12 @@ def sstate_checkhashes(sq_data, d, siginfo=False, currentcount=0, **kwargs): spec, extrapath, tname = getpathcomponents(tid, d) - sstatefile = d.expand("${SSTATE_DIR}/" + extrapath + generate_sstatefn(spec, gethash(tid), d) + "_" + tname + extension) + sstatefile = d.expand("${SSTATE_DIR}/" + extrapath + generate_sstatefn(spec, gethash(tid), tname, siginfo, d)) if os.path.exists(sstatefile): bb.debug(2, "SState: Found valid sstate file %s" % sstatefile) found.add(tid) + foundLocal.add(tid) continue else: missed.add(tid) @@ -900,28 +955,31 @@ def sstate_checkhashes(sq_data, d, siginfo=False, currentcount=0, **kwargs): fetcher.checkstatus() bb.debug(2, "SState: Successful fetch test for %s" % srcuri) found.add(tid) + foundNet.add(tid) if tid in missed: missed.remove(tid) except: missed.add(tid) bb.debug(2, "SState: Unsuccessful fetch test for %s" % srcuri) pass - bb.event.fire(bb.event.ProcessProgress(msg, len(tasklist) - thread_worker.tasks.qsize()), d) + if len(tasklist) >= min_tasks: + bb.event.fire(bb.event.ProcessProgress(msg, len(tasklist) - thread_worker.tasks.qsize()), d) tasklist = [] + min_tasks = 100 for tid in sq_data['hash']: if tid in found: continue spec, extrapath, tname = getpathcomponents(tid, d) - sstatefile = d.expand(extrapath + generate_sstatefn(spec, gethash(tid), d) + "_" + tname + extension) + sstatefile = d.expand(extrapath + generate_sstatefn(spec, gethash(tid), tname, siginfo, d)) tasklist.append((tid, sstatefile)) if tasklist: - msg = "Checking sstate mirror object availability" - bb.event.fire(bb.event.ProcessStarted(msg, len(tasklist)), d) + nproc = min(int(d.getVar("BB_NUMBER_THREADS")), len(tasklist)) - import multiprocessing - nproc = min(multiprocessing.cpu_count(), len(tasklist)) + if len(tasklist) >= min_tasks: + msg = "Checking sstate mirror object availability" + bb.event.fire(bb.event.ProcessStarted(msg, len(tasklist)), d) bb.event.enable_threadlock() pool = oe.utils.ThreadedPool(nproc, len(tasklist), @@ -932,40 +990,39 @@ def sstate_checkhashes(sq_data, d, siginfo=False, currentcount=0, **kwargs): pool.wait_completion() bb.event.disable_threadlock() - bb.event.fire(bb.event.ProcessFinished(msg), d) - - # Likely checking an individual task hash again for multiconfig sharing of sstate tasks so skip reporting - if len(sq_data['hash']) == 1: - return found + if len(tasklist) >= min_tasks: + bb.event.fire(bb.event.ProcessFinished(msg), d) inheritlist = d.getVar("INHERIT") if "toaster" in inheritlist: evdata = {'missed': [], 'found': []}; for tid in missed: spec, extrapath, tname = getpathcomponents(tid, d) - sstatefile = d.expand(extrapath + generate_sstatefn(spec, gethash(tid), d) + "_" + tname + ".tgz") + sstatefile = d.expand(extrapath + generate_sstatefn(spec, gethash(tid), tname, False, d)) evdata['missed'].append((bb.runqueue.fn_from_tid(tid), bb.runqueue.taskname_from_tid(tid), gethash(tid), sstatefile ) ) for tid in found: spec, extrapath, tname = getpathcomponents(tid, d) - sstatefile = d.expand(extrapath + generate_sstatefn(spec, gethash(tid), d) + "_" + tname + ".tgz") + sstatefile = d.expand(extrapath + generate_sstatefn(spec, gethash(tid), tname, False, d)) evdata['found'].append((bb.runqueue.fn_from_tid(tid), bb.runqueue.taskname_from_tid(tid), gethash(tid), sstatefile ) ) bb.event.fire(bb.event.MetadataEvent("MissedSstate", evdata), d) - # Print some summary statistics about the current task completion and how much sstate - # reuse there was. Avoid divide by zero errors. - total = len(sq_data['hash']) - complete = 0 - if currentcount: - complete = (len(found) + currentcount) / (total + currentcount) * 100 - match = 0 - if total: - match = len(found) / total * 100 - bb.plain("Sstate summary: Wanted %d Found %d Missed %d Current %d (%d%% match, %d%% complete)" % (total, len(found), len(missed), currentcount, match, complete)) + if summary: + # Print some summary statistics about the current task completion and how much sstate + # reuse there was. Avoid divide by zero errors. + total = len(sq_data['hash']) + complete = 0 + if currentcount: + complete = (len(found) + currentcount) / (total + currentcount) * 100 + match = 0 + if total: + match = len(found) / total * 100 + bb.plain("Sstate summary: Wanted %d Local %d Network %d Missed %d Current %d (%d%% match, %d%% complete)" % (total, len(foundLocal), len(foundNet),len(missed), currentcount, match, complete)) if hasattr(bb.parse.siggen, "checkhashes"): bb.parse.siggen.checkhashes(sq_data, missed, found, d) return found +setscene_depvalid[vardepsexclude] = "SSTATE_EXCLUDEDEPS_SYSROOT" BB_SETSCENE_DEPVALID = "setscene_depvalid" @@ -1080,29 +1137,45 @@ addhandler sstate_eventhandler sstate_eventhandler[eventmask] = "bb.build.TaskSucceeded" python sstate_eventhandler() { d = e.data - # When we write an sstate package we rewrite the SSTATE_PKG - spkg = d.getVar('SSTATE_PKG') - if not spkg.endswith(".tgz"): + writtensstate = d.getVar('SSTATE_CURRTASK') + if not writtensstate: taskname = d.getVar("BB_RUNTASK")[3:] spec = d.getVar('SSTATE_PKGSPEC') swspec = d.getVar('SSTATE_SWSPEC') if taskname in ["fetch", "unpack", "patch", "populate_lic", "preconfigure"] and swspec: d.setVar("SSTATE_PKGSPEC", "${SSTATE_SWSPEC}") d.setVar("SSTATE_EXTRAPATH", "") - sstatepkg = d.getVar('SSTATE_PKG') - bb.siggen.dump_this_task(sstatepkg + '_' + taskname + ".tgz" ".siginfo", d) + d.setVar("SSTATE_CURRTASK", taskname) + siginfo = d.getVar('SSTATE_PKG') + ".siginfo" + if not os.path.exists(siginfo): + bb.siggen.dump_this_task(siginfo, d) + else: + try: + os.utime(siginfo, None) + except PermissionError: + pass + except OSError as e: + # Handle read-only file systems gracefully + import errno + if e.errno != errno.EROFS: + raise e + } SSTATE_PRUNE_OBSOLETEWORKDIR ?= "1" -# Event handler which removes manifests and stamps file for -# recipes which are no longer reachable in a build where they -# once were. +# +# Event handler which removes manifests and stamps file for recipes which are no +# longer 'reachable' in a build where they once were. 'Reachable' refers to +# whether a recipe is parsed so recipes in a layer which was removed would no +# longer be reachable. Switching between systemd and sysvinit where recipes +# became skipped would be another example. +# # Also optionally removes the workdir of those tasks/recipes # -addhandler sstate_eventhandler2 -sstate_eventhandler2[eventmask] = "bb.event.ReachableStamps" -python sstate_eventhandler2() { +addhandler sstate_eventhandler_reachablestamps +sstate_eventhandler_reachablestamps[eventmask] = "bb.event.ReachableStamps" +python sstate_eventhandler_reachablestamps() { import glob d = e.data stamps = e.stamps.values() @@ -1128,11 +1201,21 @@ python sstate_eventhandler2() { i = d.expand("${SSTATE_MANIFESTS}/index-" + a) if not os.path.exists(i): continue + manseen = set() + ignore = [] with open(i, "r") as f: lines = f.readlines() - for l in lines: + for l in reversed(lines): try: (stamp, manifest, workdir) = l.split() + # The index may have multiple entries for the same manifest as the code above only appends + # new entries and there may be an entry with matching manifest but differing version in stamp/workdir. + # The last entry in the list is the valid one, any earlier entries with matching manifests + # should be ignored. + if manifest in manseen: + ignore.append(l) + continue + manseen.add(manifest) if stamp not in stamps and stamp not in preservestamps and stamp in machineindex: toremove.append(l) if stamp not in seen: @@ -1163,6 +1246,8 @@ python sstate_eventhandler2() { with open(i, "w") as f: for l in lines: + if l in ignore: + continue f.write(l) machineindex |= set(stamps) with open(mi, "w") as f: @@ -1172,3 +1257,59 @@ python sstate_eventhandler2() { if preservestamps: os.remove(preservestampfile) } + + +# +# Bitbake can generate an event showing which setscene tasks are 'stale', +# i.e. which ones will be rerun. These are ones where a stamp file is present but +# it is stable (e.g. taskhash doesn't match). With that list we can go through +# the manifests for matching tasks and "uninstall" those manifests now. We do +# this now rather than mid build since the distribution of files between sstate +# objects may have changed, new tasks may run first and if those new tasks overlap +# with the stale tasks, we'd see overlapping files messages and failures. Thankfully +# removing these files is fast. +# +addhandler sstate_eventhandler_stalesstate +sstate_eventhandler_stalesstate[eventmask] = "bb.event.StaleSetSceneTasks" +python sstate_eventhandler_stalesstate() { + d = e.data + tasks = e.tasks + + bb.utils.mkdirhier(d.expand("${SSTATE_MANIFESTS}")) + + for a in list(set(d.getVar("SSTATE_ARCHS").split())): + toremove = [] + i = d.expand("${SSTATE_MANIFESTS}/index-" + a) + if not os.path.exists(i): + continue + with open(i, "r") as f: + lines = f.readlines() + for l in lines: + try: + (stamp, manifest, workdir) = l.split() + for tid in tasks: + for s in tasks[tid]: + if s.startswith(stamp): + taskname = bb.runqueue.taskname_from_tid(tid)[3:] + manname = manifest + "." + taskname + if os.path.exists(manname): + bb.debug(2, "Sstate for %s is stale, removing related manifest %s" % (tid, manname)) + toremove.append((manname, tid, tasks[tid])) + break + except ValueError: + bb.fatal("Invalid line '%s' in sstate manifest '%s'" % (l, i)) + + if toremove: + msg = "Removing %d stale sstate objects for arch %s" % (len(toremove), a) + bb.event.fire(bb.event.ProcessStarted(msg, len(toremove)), d) + + removed = 0 + for (manname, tid, stamps) in toremove: + sstate_clean_manifest(manname, d) + for stamp in stamps: + bb.utils.remove(stamp) + removed = removed + 1 + bb.event.fire(bb.event.ProcessProgress(msg, removed), d) + + bb.event.fire(bb.event.ProcessFinished(msg), d) +} |