Sharing data between virtual images only worked when rm_work.bbclass was not active.
To support rm_work.bbclass, the new do_swupd_list_bundle generates the necessary information about the rootfs before do_rm_work removes the rootfs. The output files and the mega image rootfs.tar get excluded from the cleanup via the new RM_WORK_EXCLUDE_ITEMS. While at it, some inaccurate comments get removed. As a side effect of the more granular work split, it is now possible to make swupd images depend on exactly those bundle images that they contain. Now it is possible to build a swupd image without first having to build all swupd images, which might speed up a build (less work on the critical path). Fixes: [YOCTO #10799] Signed-off-by: Patrick Ohly <patrick.o...@intel.com> --- classes/swupd-image.bbclass | 32 ++++++++++++++++-- classes/swupdimage.bbclass | 7 ++-- lib/swupd/bundles.py | 81 ++++++++++++++++++++++----------------------- lib/swupd/rootfs.py | 6 ++-- 4 files changed, 76 insertions(+), 50 deletions(-) diff --git a/classes/swupd-image.bbclass b/classes/swupd-image.bbclass index 5ba9cfb..78a26ac 100644 --- a/classes/swupd-image.bbclass +++ b/classes/swupd-image.bbclass @@ -123,6 +123,8 @@ inherit distro_features_check REQUIRED_DISTRO_FEATURES = "systemd" python () { + import os + ver = d.getVar('OS_VERSION', True) or 'invalid' try: ver = int(ver) @@ -151,7 +153,15 @@ python () { mega_rootfs = d.getVar('IMAGE_ROOTFS', True) mega_rootfs = mega_rootfs.replace('/' + pn +'/', '/bundle-%s-mega/' % (pn_base or pn)) d.setVar('MEGA_IMAGE_ROOTFS', mega_rootfs) - d.setVar('MEGA_IMAGE_ARCHIVE', mega_rootfs + '.tar') + mega_archive = mega_rootfs + '.tar' + workdir = d.getVar('WORKDIR') + d.setVar('MEGA_IMAGE_ARCHIVE', mega_archive) + mega_archive_rel = os.path.relpath(mega_archive, workdir) + if os.path.sep not in mega_archive_rel: + # The mega archive is in our work directory and must be + # preserved for other virtual images even when rm_work.bbclass + # is active. + d.appendVar('RM_WORK_EXCLUDE_ITEMS', ' ' + mega_archive_rel) if pn_base is not None: # Swupd images must depend on the mega image having been @@ -271,6 +281,19 @@ do_image_append () { swupd.rootfs.create_rootfs(d) } +# The content lists of each rootfs get stored separately and +# need to be preserved when rm_work.bbclass is active. +# That information is used by do_stage_swupd_inputs in the +# base recipe. +RM_WORK_EXCLUDE_ITEMS += "swupd${SWUPD_ROOTFS_MANIFEST_SUFFIX} swupd${SWUPD_IMAGE_MANIFEST_SUFFIX}" +python do_swupd_list_bundle () { + import swupd.bundles + + swupd.bundles.list_bundle_contents(d) +} +do_swupd_list_bundle[depends] = "${@ '${SWUPD_IMAGE_PN}:do_swupd_list_bundle' if '${SWUPD_IMAGE_PN}' != '${PN}' else '' }" +addtask do_swupd_list_bundle after do_image before do_build + # Some files should not be included in swupd manifests and therefore never be # updated on the target (i.e. certain per-device or machine-generated files in # /etc when building for a statefule OS). Add the target paths to this list to @@ -295,9 +318,12 @@ fakeroot python do_stage_swupd_inputs () { swupd.bundles.copy_core_contents(d) swupd.bundles.copy_bundle_contents(d) } -addtask stage_swupd_inputs after do_image before do_swupd_update +addtask stage_swupd_inputs after do_swupd_list_bundle before do_swupd_update do_stage_swupd_inputs[dirs] = "${SWUPDIMAGEDIR} ${SWUPDMANIFESTDIR} ${DEPLOY_DIR_SWUPD}/maps/" -do_stage_swupd_inputs[depends] += "virtual/fakeroot-native:do_populate_sysroot" +do_stage_swupd_inputs[depends] += " \ + virtual/fakeroot-native:do_populate_sysroot \ + ${@ ' '.join(['bundle-${SWUPD_IMAGE_PN}-%s:do_swupd_list_bundle' % x for x in '${SWUPD_BUNDLES}'.split()]) } \ +" python do_fetch_swupd_inputs () { import swupd.bundles diff --git a/classes/swupdimage.bbclass b/classes/swupdimage.bbclass index 4b64fa9..cb77140 100644 --- a/classes/swupdimage.bbclass +++ b/classes/swupdimage.bbclass @@ -56,8 +56,11 @@ python swupdimage_virtclass_handler () { # variable triggers the creation of the IMGDEPLOYDIR that we # are going to write into. e.data.setVar("do_rootfs", " bb.utils.mkdirhier(d.getVar('IMAGE_ROOTFS', True))\n") - # Depend on complete bundle generation in the base image. - dep = ' %s:do_stage_swupd_inputs' % pn_base + # Depends on the content files from those bundles which contribute to the + # image. + imageext = d.getVar('IMAGE_BUNDLE_NAME', True) or '' + imagebundles = d.getVarFlag('SWUPD_IMAGES', imageext, True).split() if imageext else [] + dep = ' '.join(['bundle-%s-%s:do_swupd_list_bundle' % (pn_base, x) for x in imagebundles]) e.data.appendVarFlag('do_image', 'depends', dep) # Ensure update stream is generated when only building virt image dep = ' %s:do_swupd_update' % pn_base diff --git a/lib/swupd/bundles.py b/lib/swupd/bundles.py index 18400b8..24947a8 100644 --- a/lib/swupd/bundles.py +++ b/lib/swupd/bundles.py @@ -55,20 +55,13 @@ def copy_core_contents(d): d -- the bitbake datastore """ imagedir = d.expand('${SWUPDIMAGEDIR}/${OS_VERSION}') - corefile = d.expand('${SWUPDIMAGEDIR}/${OS_VERSION}/os-core') + fulltar = d.expand('${SWUPDIMAGEDIR}/${OS_VERSION}/full.tar') contentsuffix = d.getVar('SWUPD_ROOTFS_MANIFEST_SUFFIX', True) - imagesuffix = d.getVar('SWUPD_IMAGE_MANIFEST_SUFFIX', True) - fullfile = d.expand('${SWUPDIMAGEDIR}/${OS_VERSION}/full') - bundle = d.expand('${SWUPDIMAGEDIR}/${OS_VERSION}/full.tar') - rootfs = d.getVar('IMAGE_ROOTFS', True) + source = d.expand('${WORKDIR}/swupd') + target = d.expand('${SWUPDIMAGEDIR}/${OS_VERSION}/os-core') - # Generate a manifest of the bundle content. bb.utils.mkdirhier(imagedir) - unwanted_files = (d.getVar('SWUPD_FILE_BLACKLIST', True) or '').split() - swupd.utils.create_content_manifests(rootfs, - corefile + contentsuffix, - corefile + imagesuffix, - unwanted_files) + shutil.copy(source + contentsuffix, target + contentsuffix) # Create full.tar.gz instead of directory - speeds up # do_stage_swupd_input from ~11min in the Ostro CI to 6min. @@ -77,51 +70,47 @@ def copy_core_contents(d): # directly with the rootfs of the main image recipe. havebundles = (d.getVar('SWUPD_BUNDLES', True) or '') != '' if not havebundles: - manifest_files = [] - for suffix in (contentsuffix, imagesuffix): - shutil.copy2(corefile + suffix, fullfile + suffix) - manifest_files.extend(swupd.utils.manifest_to_file_list(fullfile + suffix)) + rootfs = d.getVar('IMAGE_ROOTFS', True) + workdir = d.getVar('WORKDIR', True) bb.debug(1, "Copying from image rootfs (%s) to full bundle (%s)" % (rootfs, bundle)) - swupd.path.copyxattrfiles(d, manifest_files, rootfs, bundle, True) + swupd.path.copyxattrfiles(d, source + contentsuffix, rootfs, fulltar, True) else: - mega_rootfs = d.getVar('MEGA_IMAGE_ROOTFS', True) mega_archive = d.getVar('MEGA_IMAGE_ARCHIVE', True) - swupd.utils.create_content_manifests(mega_rootfs, - fullfile + contentsuffix, - fullfile + imagesuffix, - unwanted_files) - os.link(mega_archive, bundle) + if os.path.exists(fulltar): + os.unlink(fulltar) + os.link(mega_archive, fulltar) -def stage_image_bundle_contents(d, bundle): +def list_bundle_contents(d): """ - Determine bundle contents which aren't part of os-core from the mega-image rootfs + Determine bundle contents, i.e. the rootfs entries which are + in the bundle and (for non-os-core bundles) not in the os-core. - For an image-based bundle, generate a list of files which exist in the - bundle but not os-core and stage those files from the mega image rootfs to - the swupd inputs directory + Creates the .content.txt files. Must be called when + the base image has created its rootfs, because those + entries need to be excluded. d -- the bitbake datastore - bundle -- the name of the bundle to be staged """ # Construct paths to manifest files and directories pn = d.getVar('PN', True) - corefile = d.expand('${SWUPDIMAGEDIR}/${OS_VERSION}/os-core') - bundlefile = d.expand('${SWUPDIMAGEDIR}/${OS_VERSION}/') + bundle + base_pn = d.getVar('PN_BASE', True) + rootfs = d.getVar('IMAGE_ROOTFS', True) + contentbase = os.path.join(d.getVar('WORKDIR', True), 'swupd') contentsuffix = d.getVar('SWUPD_ROOTFS_MANIFEST_SUFFIX', True) imagesuffix = d.getVar('SWUPD_IMAGE_MANIFEST_SUFFIX', True) - megarootfs = d.getVar('MEGA_IMAGE_ROOTFS', True) - imagesrc = megarootfs.replace('mega', bundle) # Generate the manifest of the bundle image's file contents, # excluding blacklisted files and the content of the os-core. - bb.debug(3, 'Writing bundle image file manifests %s' % bundlefile) unwanted_files = set((d.getVar('SWUPD_FILE_BLACKLIST', True) or '').split()) - unwanted_files.update(['/' + x for x in swupd.utils.manifest_to_file_list(corefile + contentsuffix)]) - swupd.utils.create_content_manifests(imagesrc, - bundlefile + contentsuffix, - bundlefile + imagesuffix, + if base_pn: + parts = d.getVar('WORKDIR', True).rsplit(pn, 1) + os_core_content = parts[0] + base_pn + parts[1] + '/swupd' + contentsuffix + unwanted_files.update(['/' + x for x in swupd.utils.manifest_to_file_list(os_core_content)]) + swupd.utils.create_content_manifests(rootfs, + contentbase + contentsuffix, + contentbase + imagesuffix, unwanted_files) def stage_empty_bundle(d, bundle): @@ -140,16 +129,24 @@ def copy_bundle_contents(d): """ Stage bundle contents - Copy the contents of all bundles from the mega image rootfs to the swupd - inputs directory to ensure that any image postprocessing which modifies - files is reflected in os-core bundle + Copy the content list of all existing bundles to the swupd + inputs directory. Empty bundles no longer have any content, + so an empty directory is enough. d -- the bitbake datastore """ - bb.debug(1, 'Copying contents of bundles for %s from mega image rootfs' % d.getVar('PN', True)) + import shutil + + pn = d.getVar('PN', True) + bb.debug(1, 'Copying contents of bundles for %s' % pn) bundles = (d.getVar('SWUPD_BUNDLES', True) or '').split() + workdir = d.getVar('WORKDIR', True) + bundledir = d.expand('${SWUPDIMAGEDIR}/${OS_VERSION}') + contentsuffix = d.getVar('SWUPD_ROOTFS_MANIFEST_SUFFIX', True) + parts = workdir.rsplit(pn, 1) for bndl in bundles: - stage_image_bundle_contents(d, bndl) + shutil.copyfile(parts[0] + ('bundle-%s-%s' % (pn, bndl)) + parts[1] + '/swupd' + contentsuffix, + os.path.join(bundledir, bndl + contentsuffix)) bundles = (d.getVar('SWUPD_EMPTY_BUNDLES', True) or '').split() for bndl in bundles: stage_empty_bundle(d, bndl) diff --git a/lib/swupd/rootfs.py b/lib/swupd/rootfs.py index b0bed27..41e6e0b 100644 --- a/lib/swupd/rootfs.py +++ b/lib/swupd/rootfs.py @@ -39,6 +39,7 @@ def create_rootfs(d): imagebundles = d.getVarFlag('SWUPD_IMAGES', imageext, True).split() if imageext else [] rootfs = d.getVar('IMAGE_ROOTFS', True) rootfs_contents = set() + parts = d.getVar('WORKDIR', True).rsplit(pn, 1) if not pn_base: # the base image import subprocess @@ -57,13 +58,12 @@ def create_rootfs(d): else: # non-base image, i.e. swupdimage manifest = d.expand("${DEPLOY_DIR_SWUPD}/image/${OS_VERSION}/os-core") for suffix in suffixes: - rootfs_contents.update(manifest_to_file_list(manifest + suffix)) + rootfs_contents.update(manifest_to_file_list(parts[0] + pn_base + parts[1] + '/swupd' + suffix)) bb.debug(3, 'rootfs_contents has %s entries' % (len(rootfs_contents))) for bundle in imagebundles: - manifest = d.expand("${DEPLOY_DIR_SWUPD}/image/${OS_VERSION}/") + bundle for suffix in suffixes: - rootfs_contents.update(manifest_to_file_list(manifest + suffix)) + rootfs_contents.update(manifest_to_file_list(parts[0] + ('bundle-%s-%s' % (pn_base, bundle)) + parts[1] + '/swupd' + suffix)) mega_archive = d.getVar('MEGA_IMAGE_ARCHIVE', True) bb.debug(2, 'Re-copying rootfs contents from mega image %s to %s' % (mega_archive, rootfs)) -- 2.11.0 -- _______________________________________________ yocto mailing list yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/yocto