diff options
author | Paul Eggleton <paul.eggleton@microsoft.com> | 2020-07-08 04:46:17 +0000 |
---|---|---|
committer | Paul Eggleton <paul.eggleton@linux.microsoft.com> | 2020-10-13 19:24:40 -0700 |
commit | 805e5f50760610e9d8fc0531c96ec8b74371892b (patch) | |
tree | 026ebface3034c82b67680cfd8c9293f078371f8 | |
parent | 8d78b819c2ec33fce3a34254fa90864ee5fa7617 (diff) | |
download | openembedded-core-contrib-paule/retain.tar.gz |
classes: add class for retaining build resultspaule/retain
If you are running your builds inside an environment where you don't
have access to the build tree (e.g. an autobuilder where you can only
download final artifacts such as images), then debugging build failures
can be difficult - you can't examine log files, the source tree or
output files. When enabled, this class does two things:
1) Triggers on task failure and saves a tarball of the work directory
for the task's recipe
2) Optionally saves tarballs of a list of nominated directories
It puts these tarballs in a configurable location, where they can be
picked up by a separate process and made available as downloadable
artifacts.
Signed-off-by: Paul Eggleton <paul.eggleton@microsoft.com>
-rw-r--r-- | meta/classes/retain.bbclass | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/meta/classes/retain.bbclass b/meta/classes/retain.bbclass new file mode 100644 index 0000000000..e98765f63d --- /dev/null +++ b/meta/classes/retain.bbclass @@ -0,0 +1,103 @@ +# Creates a tarball of the work directory for a recipe when one of its +# tasks fails, as well as (optionally) other nominated directories. +# Useful in cases where the environment in which builds are run is +# ephemeral or otherwise inaccessible for examination during +# debugging. +# +# To enable, simply add the following to your configuration: +# +# INHERIT += "retain" +# +# You can also specify extra directories to save at the end of the build +# upon failure or always (space-separated) e.g.: +# +# RETAIN_EXTRADIRS_FAILURE = "${LOG_DIR} ${TMPDIR}/pkgdata" +# RETAIN_EXTRADIRS_ALWAYS = "${BUILDSTATS_BASE}" +# +# If you wish to use a different tarball name prefix you can do so by +# adding a : followed by the desired prefix (no spaces) e.g. to use +# "buildlogs" for the tarball of ${LOG_DIR} you would do this: +# +# RETAIN_EXTRADIRS_FAILURE = "${LOG_DIR}:buildlogs ${TMPDIR}/pkgdata" +# +# Notes: +# * For this to be useful you also need corresponding logic in your build +# orchestration tool to pick up any files written out to RETAIN_OUTDIR +# (with the other assumption being that no files are present there at +# the start of the build). +# * Work directories can be quite large, so saving them can take some time +# and of course space. +# * Extra directories must naturally be populated at the time the retain +# goes to save them (build completion); to try ensure this for things +# that are also saved on build completion (e.g. buildstats), put the +# INHERIT += "retain" after the INHERIT += lines for the class that +# is writing out the data that you wish to save. +# * The tarballs have the tarball name as a top-level directory so that +# multiple tarballs can be extracted side-by-side easily. +# +# Copyright (c) 2020 Microsoft Corporation +# +# SPDX-License-Identifier: GPL-2.0-only +# + +RETAIN_OUTDIR ?= "${TMPDIR}/retained" +RETAIN_EXTRADIRS_FAILURE ?= "" +RETAIN_EXTRADIRS_ALWAYS ?= "" +RETAIN_ENABLED ?= "1" + + +def retain_retain_dir(desc, tarprefix, path, tarbasepath, d): + import datetime + + outdir = d.getVar('RETAIN_OUTDIR') + bb.utils.mkdirhier(outdir) + tstamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S") + tarname = '%s_%s' % (tarprefix, tstamp) + tarfp = os.path.join(outdir, '%s.tar.gz' % tarname) + tardir = os.path.relpath(path, tarbasepath) + cmdargs = ['tar', 'czf', tarfp] + # Prefix paths within the tarball with the tarball name so that + # multiple tarballs can be extracted side-by-side + cmdargs += ['--transform', 's:^:%s/:' % tarname] + cmdargs += [tardir] + bb.plain('NOTE: retain: saving %s to %s' % (desc, tarfp)) + try: + bb.process.run(cmdargs, cwd=tarbasepath) + except bb.process.ExecutionError as e: + # It is possible for other tasks to be writing to the workdir + # while we are tarring it up, in which case tar will return 1, + # but we don't care in this situation (tar returns 2 for other + # errors so we we will see those) + if e.exitcode != 1: + bb.warn('retain: error saving %s: %s' % (desc, str(e))) + + +addhandler retain_workdir_handler +retain_workdir_handler[eventmask] = "bb.build.TaskFailed bb.event.BuildCompleted" + +python retain_workdir_handler() { + if d.getVar('RETAIN_ENABLED') != '1': + return + + if isinstance(e, bb.build.TaskFailed): + pn = d.getVar('PN') + workdir = d.getVar('WORKDIR') + base_workdir = d.getVar('BASE_WORKDIR') + taskname = d.getVar('BB_CURRENTTASK') + desc = 'workdir for failed task %s.do_%s' % (pn, taskname) + retain_retain_dir(desc, 'workdir_%s' % pn, workdir, base_workdir, d) + elif isinstance(e, bb.event.BuildCompleted): + paths = d.getVar('RETAIN_EXTRADIRS_ALWAYS').split() + if e._failures: + paths += d.getVar('RETAIN_EXTRADIRS_FAILURE').split() + + for path in list(set(paths)): + if ':' in path: + path, itemname = path.rsplit(':', 1) + else: + itemname = os.path.basename(path) + if os.path.exists(path): + retain_retain_dir(itemname, itemname, path, os.path.dirname(path), d) + else: + bb.warn('retain: extra directory %s does not currently exist' % path) +} |