From patchwork Fri Oct 23 10:22:11 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anusmita Dutta Mazumder X-Patchwork-Id: 101783 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.3856.1792750965131725995 for ; Fri, 23 Oct 2026 03:22:45 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: body hash did not verify" header.i=@arm.com header.s=foss header.b=mP/mO8Qm; spf=pass (domain: arm.com, ip: 217.140.110.172, mailfrom: anusmita.duttamazumder@arm.com) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 66B491650; Fri, 23 Oct 2026 03:22:41 -0700 (PDT) Received: from e142472.cambridge.arm.com (e142472.arm.com [10.2.210.46]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 17B5D3F7B4; Fri, 23 Oct 2026 03:22:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1792750964; bh=ygnOx1XjOvnAcQ0Cj8iomVIr25giU7dODJXFQ7o7K/Q=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mP/mO8Qm2wcibe0tHZRLPAf/Y7774KpK4FXVpRu27VSAnkcJhBltu9Z3a8fXdzR3h 63Xz1VC3jwm+EjUmu37RI9lWcOe5LPCOvMCJQtd/cfP3MejmFW+0jEfP9mr3lReFpW yiTFRwB8m+tp/NIQMXFHYiuL4RCH7JyG0kFfglEI= From: Anusmita Dutta Mazumder To: openembedded-core@lists.openembedded.org Cc: Anusmita Dutta Mazumder Subject: [PATCH 1/5] devtool: Support multiconfig recipe workflows Date: Fri, 23 Oct 2026 11:22:11 +0100 Message-ID: <20261023102215.4043005-2-anusmita.duttamazumder@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20261023102215.4043005-1-anusmita.duttamazumder@arm.com> References: <20261023102215.4043005-1-anusmita.duttamazumder@arm.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Fri, 23 Oct 2026 10:22:46 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/248499 Preserve multiconfig-qualified targets when extracting and configuring sources. Use the recipe name without the multiconfig prefix for workspace lookups. Keep the prefix when calling BitBake from reset, update-recipe, and finish. Fixes [YOCTO #13933] Signed-off-by: Anusmita Dutta Mazumder --- scripts/lib/devtool/standard.py | 78 +++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 18 deletions(-) diff --git a/scripts/lib/devtool/standard.py b/scripts/lib/devtool/standard.py index 0d933e68b1..84fa19c7aa 100644 --- a/scripts/lib/devtool/standard.py +++ b/scripts/lib/devtool/standard.py @@ -23,6 +23,7 @@ from collections import OrderedDict from devtool import exec_build_env_command, setup_tinfoil, check_workspace_recipe, use_external_build, setup_git_repo, recipe_to_append, get_bbclassextend_targets, update_unlockedsigs, check_prerelease_version, check_git_repo_dirty, check_git_repo_op, DevtoolError from devtool import parse_recipe +import bb.runqueue import bb.utils logger = logging.getLogger('devtool') @@ -30,6 +31,21 @@ logger = logging.getLogger('devtool') override_branch_prefix = 'devtool-override-' +def _prefix_multiconfig_targets(mc, targets): + """Add a multiconfig prefix to build targets.""" + if not mc: + return targets + + return ' '.join(target if target.startswith('mc:') else + 'mc:%s:%s' % (mc, target) + for target in targets.split()) + + +def _get_multiconfig_targets(d, targets): + """Add the recipe datastore's multiconfig prefix to build targets.""" + return _prefix_multiconfig_targets(d.getVar('BB_CURRENT_MC'), targets) + + def add(args, config, basepath, workspace): """Entry point for the devtool 'add' subcommand""" import bb.data @@ -596,7 +612,7 @@ def _extract_source(srctree, keep_temp, devbranch, sync, config, basepath, works task = None # Run the fetch + unpack tasks - res = tinfoil.build_targets(pn, + res = tinfoil.build_targets(_get_multiconfig_targets(d, pn), task, handle_events=True) finally: @@ -829,7 +845,8 @@ def modify(args, config, basepath, workspace): f.write('%s\n' % fname) task = 'do_configure' - res = tinfoil.build_targets(pn, task, handle_events=True) + target = _get_multiconfig_targets(rd, pn) + res = tinfoil.build_targets(target, task, handle_events=True) # Copy .config to workspace kconfpath = rd.getVar('B') @@ -1919,7 +1936,7 @@ def _update_recipe(recipename, workspace, rd, mode, appendlayerdir, wildcard_ver def update_recipe(args, config, basepath, workspace): """Entry point for the devtool 'update-recipe' subcommand""" - check_workspace_recipe(workspace, args.recipename) + workspacepn = check_workspace_recipe(workspace, args.recipename) if args.append: if not os.path.exists(args.append): @@ -1941,7 +1958,11 @@ def update_recipe(args, config, basepath, workspace): if args.dry_run: dry_run_output = tempfile.TemporaryDirectory(prefix='devtool') dry_run_outdir = dry_run_output.name - updated, _, _ = _update_recipe(args.recipename, workspace, rd, args.mode, args.append, args.wildcard_version, args.no_remove, args.initial_rev, dry_run_outdir=dry_run_outdir, no_overrides=args.no_overrides, force_patch_refresh=args.force_patch_refresh) + updated, _, _ = _update_recipe( + workspacepn, workspace, rd, args.mode, args.append, + args.wildcard_version, args.no_remove, args.initial_rev, + dry_run_outdir=dry_run_outdir, no_overrides=args.no_overrides, + force_patch_refresh=args.force_patch_refresh) if updated: rf = rd.getVar('FILE') @@ -1968,11 +1989,17 @@ def status(args, config, basepath, workspace): return 0 -def _reset(recipes, no_clean, remove_work, config, basepath, workspace): +def _reset(recipes, no_clean, remove_work, config, basepath, workspace, + bitbake_targets=None): """Reset one or more recipes""" import bb.process import oe.path + if bitbake_targets is None: + bitbake_targets = recipes + if len(bitbake_targets) != len(recipes): + raise DevtoolError('Reset recipes and BitBake targets do not match') + def clean_preferred_provider(pn, layerconf_path): """Remove PREFERRED_PROVIDER from layer.conf'""" import re @@ -2002,11 +2029,15 @@ def _reset(recipes, no_clean, remove_work, config, basepath, workspace): # it uses BBCLASSEXTEND, then we need to also clean the other # variants targets = [] - for recipe in recipes: - targets.append(recipe) + for recipe, bitbake_target in zip(recipes, bitbake_targets): + targets.append(bitbake_target) recipefile = workspace[recipe]['recipefile'] if recipefile and os.path.exists(recipefile): - targets.extend(get_bbclassextend_targets(recipefile, recipe)) + variants = get_bbclassextend_targets(recipefile, recipe) + mc, _ = bb.runqueue.split_mc(bitbake_target) + variants = _prefix_multiconfig_targets(mc, + ' '.join(variants)) + targets.extend(variants.split()) try: exec_build_env_command(config.init_path, basepath, 'bitbake -c clean %s' % ' '.join(targets)) except bb.process.ExecutionError as e: @@ -2086,23 +2117,27 @@ def _reset(recipes, no_clean, remove_work, config, basepath, workspace): def reset(args, config, basepath, workspace): """Entry point for the devtool 'reset' subcommand""" - recipes = "" + recipes = [] + bitbake_targets = [] if args.recipename: if args.all: raise DevtoolError("Recipe cannot be specified if -a/--all is used") else: for recipe in args.recipename: - check_workspace_recipe(workspace, recipe, checksrc=False) + workspacepn = check_workspace_recipe(workspace, recipe, + checksrc=False) + recipes.append(workspacepn) + bitbake_targets.append(recipe) elif not args.all: raise DevtoolError("Recipe must be specified, or specify -a/--all to " "reset all recipes") if args.all: recipes = list(workspace.keys()) - else: - recipes = args.recipename + bitbake_targets = recipes - _reset(recipes, args.no_clean, args.remove_work, config, basepath, workspace) + _reset(recipes, args.no_clean, args.remove_work, config, basepath, workspace, + bitbake_targets=bitbake_targets) return 0 @@ -2137,14 +2172,14 @@ def finish(args, config, basepath, workspace): import bb import oe.recipeutils - check_workspace_recipe(workspace, args.recipename) + workspacepn = check_workspace_recipe(workspace, args.recipename) dry_run_suffix = ' (dry-run)' if args.dry_run else '' # Grab the equivalent of COREBASE without having to initialise tinfoil corebasedir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..')) - srctree = workspace[args.recipename]['srctree'] + srctree = workspace[workspacepn]['srctree'] check_git_repo_op(srctree, [corebasedir]) dirty = check_git_repo_dirty(srctree) if dirty: @@ -2175,7 +2210,7 @@ def finish(args, config, basepath, workspace): # If it's an upgrade, grab the original path origpath = None origfilelist = None - append = workspace[args.recipename]['bbappend'] + append = workspace[workspacepn]['bbappend'] with open(append, 'r') as f: for line in f: if line.startswith('# original_path:'): @@ -2217,7 +2252,12 @@ def finish(args, config, basepath, workspace): if args.dry_run: dry_run_output = tempfile.TemporaryDirectory(prefix='devtool') dry_run_outdir = dry_run_output.name - updated, appendfile, removed = _update_recipe(args.recipename, workspace, rd, args.mode, appendlayerdir, wildcard_version=True, no_remove=False, no_report_remove=removing_original, initial_rev=args.initial_rev, dry_run_outdir=dry_run_outdir, no_overrides=args.no_overrides, force_patch_refresh=args.force_patch_refresh) + updated, appendfile, removed = _update_recipe( + workspacepn, workspace, rd, args.mode, appendlayerdir, + wildcard_version=True, no_remove=False, + no_report_remove=removing_original, initial_rev=args.initial_rev, + dry_run_outdir=dry_run_outdir, no_overrides=args.no_overrides, + force_patch_refresh=args.force_patch_refresh) removed = [os.path.relpath(pth, recipedir) for pth in removed] # Remove any old files in the case of an upgrade @@ -2306,7 +2346,9 @@ def finish(args, config, basepath, workspace): if args.dry_run: logger.info('Resetting recipe (dry-run)') else: - _reset([args.recipename], no_clean=no_clean, remove_work=remove_work, config=config, basepath=basepath, workspace=workspace) + _reset([workspacepn], no_clean=no_clean, remove_work=remove_work, + config=config, basepath=basepath, workspace=workspace, + bitbake_targets=[args.recipename]) return 0 From patchwork Fri Oct 23 10:22:12 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anusmita Dutta Mazumder X-Patchwork-Id: 101784 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.3825.1792750971402950293 for ; Fri, 23 Oct 2026 03:22:51 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: body hash did not verify" header.i=@arm.com header.s=foss header.b=cujWxy6p; spf=pass (domain: arm.com, ip: 217.140.110.172, mailfrom: anusmita.duttamazumder@arm.com) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 9DCC51650; Fri, 23 Oct 2026 03:22:47 -0700 (PDT) Received: from e142472.cambridge.arm.com (e142472.arm.com [10.2.210.46]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 481153F7B4; Fri, 23 Oct 2026 03:22:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1792750970; bh=ouSj5AqedxrwRxm4D05wPLeoYxHQ3oiD2lxoo952RPw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=cujWxy6pZoQMCQ73Jmjf1cDA/GXidutddxXxtGh3kNV8bMKC0I+yebkdJs1guPdt9 jmRy6gjaXd6ZT985NmlFvP9cP2EOPKPBjU7y68l6wUknNInlEMoaR/QHeCouz/esTe F9g7N9wCVs61QH1Ahw0tCUXTTrh7/K8znUPO0Pvk= From: Anusmita Dutta Mazumder To: openembedded-core@lists.openembedded.org Cc: Anusmita Dutta Mazumder Subject: [PATCH 2/5] devtool: Check kernel configure result Date: Fri, 23 Oct 2026 11:22:12 +0100 Message-ID: <20261023102215.4043005-3-anusmita.duttamazumder@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20261023102215.4043005-1-anusmita.duttamazumder@arm.com> References: <20261023102215.4043005-1-anusmita.duttamazumder@arm.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Fri, 23 Oct 2026 10:22:56 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/248500 Stop source modification when the kernel do_configure task fails instead of continuing with a potentially stale configuration. Signed-off-by: Anusmita Dutta Mazumder --- scripts/lib/devtool/standard.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/lib/devtool/standard.py b/scripts/lib/devtool/standard.py index 84fa19c7aa..1f6ae1d931 100644 --- a/scripts/lib/devtool/standard.py +++ b/scripts/lib/devtool/standard.py @@ -847,6 +847,9 @@ def modify(args, config, basepath, workspace): task = 'do_configure' target = _get_multiconfig_targets(rd, pn) res = tinfoil.build_targets(target, task, handle_events=True) + if not res: + raise DevtoolError('Configuring source for %s failed' % + target) # Copy .config to workspace kconfpath = rd.getVar('B') From patchwork Fri Oct 23 10:22:13 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anusmita Dutta Mazumder X-Patchwork-Id: 101785 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.3859.1792750974608110783 for ; Fri, 23 Oct 2026 03:22:54 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: body hash did not verify" header.i=@arm.com header.s=foss header.b=mAeQJAxp; spf=pass (domain: arm.com, ip: 217.140.110.172, mailfrom: anusmita.duttamazumder@arm.com) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 023A81650; Fri, 23 Oct 2026 03:22:51 -0700 (PDT) Received: from e142472.cambridge.arm.com (e142472.arm.com [10.2.210.46]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id A9CA93F7B4; Fri, 23 Oct 2026 03:22:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1792750974; bh=qcpAsmRVXKeoqBbFLk5t43Hc9p9ua3dAYkEp5+lScFQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mAeQJAxpFe5r3Y6dp2w9BG2zYr97hOpbmhKQKVUGiEmGGy05dD8fpkAq4v8RlNRvS 4m4fHQioS0+DbqKo618YlTlH522yT8YWXVyhZGn6qiSD4GIkzxhq4j30IHfIIjY1Fk YIYZJUEgaQvvCAEDXL/Hi7C+eIFoWX+UMBL4586w= From: Anusmita Dutta Mazumder To: openembedded-core@lists.openembedded.org Cc: Anusmita Dutta Mazumder Subject: [PATCH 3/5] devtool: Isolate multiconfig workspace entries Date: Fri, 23 Oct 2026 11:22:13 +0100 Message-ID: <20261023102215.4043005-4-anusmita.duttamazumder@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20261023102215.4043005-1-anusmita.duttamazumder@arm.com> References: <20261023102215.4043005-1-anusmita.duttamazumder@arm.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Fri, 23 Oct 2026 10:22:56 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/248501 Store workspace appends in multiconfig-specific directories and select them using BB_CURRENT_MC to prevent changes from affecting same-named recipes in other configurations. Fixes [YOCTO #13933] Signed-off-by: Anusmita Dutta Mazumder --- scripts/devtool | 24 ++++++++++++++++++++++-- scripts/lib/devtool/__init__.py | 8 ++++++-- scripts/lib/devtool/standard.py | 7 ++++--- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/scripts/devtool b/scripts/devtool index 39cebec0d8..bae7c8bbe4 100755 --- a/scripts/devtool +++ b/scripts/devtool @@ -28,6 +28,11 @@ import scriptutils import argparse_oe logger = scriptutils.logger_create('devtool') +# Only expose workspace appends belonging to the current configuration. +workspace_append_pattern = ("${LAYERDIR}/appends/" + "${@'multiconfig/%s/' % d.getVar('BB_CURRENT_MC') " + "if d.getVar('BB_CURRENT_MC') else ''}*.bbappend") + class ConfigHandler: basepath = None @@ -99,9 +104,21 @@ def read_workspace(basepath, context): if not context.fixed_setup: _enable_workspace_layer(context.config.workspace_path, context.config, basepath) + # Upgrade existing workspace layers as well as newly created ones. + layerconf = os.path.join(context.config.workspace_path, 'conf', 'layer.conf') + with open(layerconf) as f: + contents = f.read() + updated = contents.replace('${LAYERDIR}/appends/*.bbappend', workspace_append_pattern) + if updated != contents: + with open(layerconf, 'w') as f: + f.write(updated) + logger.debug('Reading workspace in %s' % context.config.workspace_path) externalsrc_re = re.compile(r'^EXTERNALSRC(:pn-([^ =]+))? *= *"([^"]*)"$') - for fn in glob.glob(os.path.join(context.config.workspace_path, 'appends', '*.bbappend')): + appenddir = os.path.join(context.config.workspace_path, 'appends') + appendfiles = glob.glob(os.path.join(appenddir, '*.bbappend')) + appendfiles += glob.glob(os.path.join(appenddir, 'multiconfig', '*', '*.bbappend')) + for fn in appendfiles: with open(fn, 'r') as f: pnvalues = {} pn = None @@ -130,6 +147,9 @@ def read_workspace(basepath, context): if not pnvalues.get('srctreebase', None): pnvalues['srctreebase'] = pnvalues['srctree'] logger.debug('Found recipe %s' % pnvalues) + if pn in workspace: + raise DevtoolError("Multiple workspace appends found for recipe '%s'; " + "only one configuration can be modified at a time" % pn) workspace[pn] = pnvalues return workspace @@ -166,7 +186,7 @@ def _create_workspace(workspacedir, basepath, layerseries=None): f.write('# ### workspace layer auto-generated by devtool ###\n') f.write('BBPATH =. "$' + '{LAYERDIR}:"\n') f.write('BBFILES += "$' + '{LAYERDIR}/recipes/*/*.bb \\\n') - f.write(' $' + '{LAYERDIR}/appends/*.bbappend"\n') + f.write(' %s"\n' % workspace_append_pattern) f.write('BBFILE_COLLECTIONS += "workspacelayer"\n') f.write('BBFILE_PATTERN_workspacelayer = "^$' + '{LAYERDIR}/"\n') f.write('BBFILE_PATTERN_IGNORE_EMPTY_workspacelayer = "1"\n') diff --git a/scripts/lib/devtool/__init__.py b/scripts/lib/devtool/__init__.py index 9d227e2032..f728f7ada0 100644 --- a/scripts/lib/devtool/__init__.py +++ b/scripts/lib/devtool/__init__.py @@ -133,13 +133,15 @@ def setup_tinfoil(config_only=False, basepath=None, tracking=False): def parse_recipe(config, tinfoil, pn, appends, filter_workspace=True): """Parse the specified recipe""" import bb.providers + import bb.runqueue try: recipefile = tinfoil.get_recipe_file(pn) except bb.providers.NoProvider as e: logger.error(str(e)) return None if appends: - append_files = tinfoil.get_file_appends(recipefile) + mc, _ = bb.runqueue.split_mc(pn) + append_files = tinfoil.get_file_appends(recipefile, mc) if filter_workspace: # Filter out appends from the workspace append_files = [path for path in append_files if @@ -276,7 +278,7 @@ def setup_git_repo(repodir, version, devbranch, basetag='devtool-base', d=None): if os.path.exists(os.path.join(repodir, '.gitmodules')): bb.process.run('git submodule foreach --recursive "git tag -f --no-sign %s"' % basetag, cwd=repodir) -def recipe_to_append(recipefile, config, wildcard=False): +def recipe_to_append(recipefile, config, wildcard=False, mc=''): """ Convert a recipe file to a bbappend file path within the workspace. NOTE: if the bbappend already exists, you should be using @@ -287,6 +289,8 @@ def recipe_to_append(recipefile, config, wildcard=False): if wildcard: appendname = re.sub(r'_.*', '_%', appendname) appendpath = os.path.join(config.workspace_path, 'appends') + if mc: + appendpath = os.path.join(appendpath, 'multiconfig', mc) appendfile = os.path.join(appendpath, appendname + '.bbappend') return appendfile diff --git a/scripts/lib/devtool/standard.py b/scripts/lib/devtool/standard.py index 1f6ae1d931..0f7cfe132f 100644 --- a/scripts/lib/devtool/standard.py +++ b/scripts/lib/devtool/standard.py @@ -540,7 +540,7 @@ def _extract_source(srctree, keep_temp, devbranch, sync, config, basepath, works initial_rev = None recipefile = d.getVar('FILE') - appendfile = recipe_to_append(recipefile, config) + appendfile = recipe_to_append(recipefile, config, mc=d.getVar('BB_CURRENT_MC')) is_kernel_yocto = bb.data.inherits_class('kernel-yocto', d) # We need to redirect WORKDIR, STAMPS_DIR etc. under a temporary @@ -804,7 +804,8 @@ def modify(args, config, basepath, workspace): srctree) recipefile = rd.getVar('FILE') - appendfile = recipe_to_append(recipefile, config, args.wildcard) + appendfile = recipe_to_append(recipefile, config, args.wildcard, + mc=rd.getVar('BB_CURRENT_MC')) if os.path.exists(appendfile): raise DevtoolError("Another variant of recipe %s is already in your " "workspace (only one variant of a recipe can " @@ -2076,7 +2077,7 @@ def _reset(recipes, no_clean, remove_work, config, basepath, workspace, # This should always be true if recipefile is set, but just in case preservedir(os.path.dirname(recipefile)) # We don't automatically create this dir next to appends, but the user can - preservedir(os.path.join(config.workspace_path, 'appends', pn)) + preservedir(os.path.join(os.path.dirname(appendfile), pn)) srctreebase = workspace[pn]['srctreebase'] if os.path.isdir(srctreebase): From patchwork Fri Oct 23 10:22:14 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anusmita Dutta Mazumder X-Patchwork-Id: 101786 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.3827.1792750977313640941 for ; Fri, 23 Oct 2026 03:22:57 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: body hash did not verify" header.i=@arm.com header.s=foss header.b=Y3fgZqyH; spf=pass (domain: arm.com, ip: 217.140.110.172, mailfrom: anusmita.duttamazumder@arm.com) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id B2A291650; Fri, 23 Oct 2026 03:22:53 -0700 (PDT) Received: from e142472.cambridge.arm.com (e142472.arm.com [10.2.210.46]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 5C1083F7B4; Fri, 23 Oct 2026 03:22:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1792750976; bh=dXWlN9jjjiluWUqL6zp/YHn3+7Ka0goVIsug7Ve6W40=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Y3fgZqyHeF0M/TU4mY+a+ASwzeUu0R03ALhJHm5TDHKnjePGoHRzs5k8rWgYOebjV +p4edqphbyJHB9fati25VRDyOjMOsV72azM0YGVKnOLYLDdwHOuopWxUTUpAJL8Sx9 7JNESsTNBiw1WyWNeZ2YuhlRexjNih+igzr05IIQ= From: Anusmita Dutta Mazumder To: openembedded-core@lists.openembedded.org Cc: Anusmita Dutta Mazumder Subject: [PATCH 4/5] devtool: Record kernel baseline after revision validation Date: Fri, 23 Oct 2026 11:22:14 +0100 Message-ID: <20261023102215.4043005-5-anusmita.duttamazumder@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20261023102215.4043005-1-anusmita.duttamazumder@arm.com> References: <20261023102215.4043005-1-anusmita.duttamazumder@arm.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Fri, 23 Oct 2026 10:23:06 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/248502 Devtool creates its kernel branch before the source is reset to the commit specified by the recipe. This can leave the workspace on the wrong kernel version and fail the build. Create the devtool branch after revision validation so it uses the commit specified by the recipe. Signed-off-by: Anusmita Dutta Mazumder --- meta/classes/devtool-source.bbclass | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meta/classes/devtool-source.bbclass b/meta/classes/devtool-source.bbclass index f29f40588f..24f8a3b616 100644 --- a/meta/classes/devtool-source.bbclass +++ b/meta/classes/devtool-source.bbclass @@ -52,7 +52,8 @@ python() { # Hook in pre/postfuncs is_kernel_yocto = bb.data.inherits_class('kernel-yocto', d) if is_kernel_yocto: - unpacktask = 'do_kernel_checkout' + # Record the devtool baseline after the kernel SRCREV is enforced. + unpacktask = 'do_validate_branches' d.appendVarFlag('do_configure', 'postfuncs', ' devtool_post_configure') else: unpacktask = 'do_unpack' From patchwork Fri Oct 23 10:22:15 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anusmita Dutta Mazumder X-Patchwork-Id: 101787 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.3861.1792750979708833133 for ; Fri, 23 Oct 2026 03:22:59 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: body hash did not verify" header.i=@arm.com header.s=foss header.b=T9IPWyk7; spf=pass (domain: arm.com, ip: 217.140.110.172, mailfrom: anusmita.duttamazumder@arm.com) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 18B111650; Fri, 23 Oct 2026 03:22:56 -0700 (PDT) Received: from e142472.cambridge.arm.com (e142472.arm.com [10.2.210.46]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id C04003F7B4; Fri, 23 Oct 2026 03:22:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1792750979; bh=S2/5EqP2l37RlG2hEb/e3l9ky9zayTRHpooM3WGh2Rw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=T9IPWyk7I7qqPS1f4y/fC3Zv+uEsa2LsVJIkRDLgV0GCE5ruFL641HeNP7f3z6yRZ m5PS8XF3UIDftPEAd3B8fkpoPDT4Qxlo6Qii4cYWyIXAt7MuzTqHnomK3tPV8rmVxT E8JbdjlTayk8DHJniX6tKfICWkYRhjPWpqngyvgw= From: Anusmita Dutta Mazumder To: openembedded-core@lists.openembedded.org Cc: Anusmita Dutta Mazumder Subject: [PATCH 5/5] oe-selftest: devtool: Add multiconfig workflow tests Date: Fri, 23 Oct 2026 11:22:15 +0100 Message-ID: <20261023102215.4043005-6-anusmita.duttamazumder@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20261023102215.4043005-1-anusmita.duttamazumder@arm.com> References: <20261023102215.4043005-1-anusmita.duttamazumder@arm.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Fri, 23 Oct 2026 10:23:06 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/248503 Cover modify, build, reset, update-recipe and finish with a recipe available only in the selected multiconfig. Check workspace isolation between configurations. [YOCTO #13933] Signed-off-by: Anusmita Dutta Mazumder --- meta/lib/oeqa/selftest/cases/devtool.py | 133 ++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index b848623a6e..1b83ee16d4 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -1327,6 +1327,139 @@ class DevtoolModifyTests(DevtoolBase): self.assertExists(os.path.join(source_repo_gitsm_gitmodules, 'bitbake'), 'Submodule not found') self.assertExists(os.path.join(source_repo_gitsm_gitmodules, 'bitbake-gitsm-test1'), 'Submodule not found') +class DevtoolMulticonfigTests(DevtoolBase): + + def setUp(self): + super(DevtoolMulticonfigTests, self).setUp() + self.recipe = 'devtool-multiconfig-test' + self.target = 'mc:devtooltest:%s' % self.recipe + self.tempdir = tempfile.mkdtemp(prefix='devtoolqa') + self.track_for_cleanup(self.tempdir) + self.track_for_cleanup(self.workspacedir) + self.srctree = os.path.join(self.tempdir, 'source') + self.layerdir = os.path.join(self.tempdir, 'meta-devtool-multiconfig') + + self.append_config('BBMULTICONFIG = "devtooltest"\n') + # Only this configuration satisfies the test recipe's compatibility + # restriction, regardless of the machine used by the selftest runner. + self.write_config('MACHINEOVERRIDES .= ":devtool-mc"\n' + 'TMPDIR = "${TOPDIR}/tmp-devtool-mc"\n', 'devtooltest') + self.add_command_to_tearDown('bitbake -c clean %s' % self.target) + self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') + self._create_temp_layer(self.layerdir, True, 'devtoolmulticonfig') + + self.recipedir = os.path.join(self.layerdir, 'recipes-test', self.recipe) + filesdir = os.path.join(self.recipedir, self.recipe) + os.makedirs(filesdir) + archive = 'devtool-test-subdir.tar.gz' + shutil.copyfile(os.path.join(get_test_layer(), 'recipes-test', 'devtool', + 'devtool-test-subdir', archive), + os.path.join(filesdir, archive)) + with open(os.path.join(self.recipedir, self.recipe + '_1.0.bb'), 'w') as f: + f.write('LICENSE = "MIT"\n' + 'LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"\n' + 'inherit allarch\n' + 'INHIBIT_DEFAULT_DEPS = "1"\n' + 'COMPATIBLE_MACHINE = "devtool-mc"\n' + 'SRC_URI = "file://devtool-test-subdir.tar.gz"\n' + 'S = "${UNPACKDIR}/devtool-test-subdir"\n' + 'B = "${WORKDIR}/build"\n' + 'do_compile() {\n' + ' cp ${S}/testfile ${B}/testfile\n' + '}\n' + 'do_install() {\n' + ' install -Dm0644 ${B}/testfile ${D}${datadir}/${BPN}/testfile\n' + '}\n' + 'FILES:${PN} = "${datadir}/${BPN}"\n') + + def _modify(self, wildcard=False): + runCmd('devtool modify %s %s %s' % + ('--wildcard' if wildcard else '', self.target, self.srctree)) + self._check_src_repo(self.srctree) + append = os.path.join(self.workspacedir, 'appends', 'multiconfig', + 'devtooltest', self.recipe + ('_%.bbappend' if wildcard else '_1.0.bbappend')) + self.assertExists(append) + self.assertEqual(get_bb_var('EXTERNALSRC', self.target), self.srctree) + self.assertIn(self.recipe, runCmd('devtool status').output) + return append + + def test_devtool_multiconfig_modify_build_reset(self): + """Extract, build and clean a recipe unavailable in the default config.""" + result = bitbake('%s -n' % self.recipe, ignore_status=True) + self.assertNotEqual(result.status, 0) + self.assertIn('incompatible with machine', result.output) + + append = self._modify() + with open(os.path.join(self.srctree, 'testfile'), 'a') as f: + f.write('\nmulticonfig workspace change\n') + runCmd('devtool build %s' % self.target) + builddir = get_bb_var('B', self.target) + with open(os.path.join(builddir, 'testfile')) as f: + self.assertIn('multiconfig workspace change', f.read()) + + # Do not use --no-clean: cleaning must retain the multiconfig prefix. + runCmd('devtool reset %s' % self.target) + self.assertNotExists(append) + self.assertNotIn(self.recipe, runCmd('devtool status').output) + self.assertFalse(get_bb_var('EXTERNALSRC', self.target)) + + def test_devtool_multiconfig_update_finish(self): + """Update and finish using qualified targets and bare workspace keys.""" + append = self._modify() + for command, marker in [('update-recipe', 'update'), ('finish', 'finish')]: + with open(os.path.join(self.srctree, 'testfile'), 'a') as f: + f.write('\nmulticonfig %s change\n' % marker) + runCmd('git commit -am "Multiconfig %s change" ' + '-m "Upstream-Status: Inappropriate [selftest]"' % marker, + cwd=self.srctree) + destination = self.layerdir if command == 'finish' else '' + runCmd('devtool %s %s %s' % (command, self.target, destination)) + patches = glob.glob(os.path.join(self.recipedir, self.recipe, '*.patch')) + self.assertTrue(patches, 'No patches written by %s' % command) + contents = '' + for patch in patches: + with open(patch) as f: + contents += f.read() + self.assertIn('multiconfig %s change' % marker, contents) + if command == 'update-recipe': + self.assertExists(append) + self.assertIn(self.recipe, runCmd('devtool status').output) + + self.assertNotExists(append) + self.assertNotIn(self.recipe, runCmd('devtool status').output) + self.assertFalse(get_bb_var('EXTERNALSRC', self.target)) + bitbake('%s -c patch' % self.target) + with open(os.path.join(get_bb_var('S', self.target), 'testfile')) as f: + contents = f.read() + self.assertIn('multiconfig update change', contents) + self.assertIn('multiconfig finish change', contents) + + def test_devtool_multiconfig_workspace_isolation(self): + """A wildcard workspace append must affect only its configuration.""" + self.append_config('COMPATIBLE_MACHINE:pn-%s = ".*"\n' + 'BBMULTICONFIG += "devtoolother"\n' % self.recipe) + self.write_config('TMPDIR = "${TOPDIR}/tmp-devtool-other"\n', 'devtoolother') + other_target = 'mc:devtoolother:%s' % self.recipe + append = self._modify(wildcard=True) + self.assertFalse(get_bb_var('EXTERNALSRC', self.recipe)) + self.assertFalse(get_bb_var('EXTERNALSRC', other_target)) + + result = runCmd('devtool modify -n %s %s' % (self.recipe, self.srctree), + ignore_status=True) + self.assertNotEqual(result.status, 0) + self.assertIn('already in your workspace', result.output) + self.assertExists(append) + runCmd('devtool reset %s' % self.target) + self.assertNotExists(append) + + # The default configuration's append must likewise stay out of MCs. + runCmd('devtool modify -n %s %s' % (self.recipe, self.srctree)) + self.assertEqual(get_bb_var('EXTERNALSRC', self.recipe), self.srctree) + self.assertFalse(get_bb_var('EXTERNALSRC', self.target)) + self.assertFalse(get_bb_var('EXTERNALSRC', other_target)) + runCmd('devtool reset %s' % self.recipe) + + class DevtoolUpdateTests(DevtoolBase): def test_devtool_update_recipe(self):