From patchwork Sun Aug 16 22:14:40 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95480 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 559E5C5DF7C for ; Sun, 16 Aug 2026 22:15:21 +0000 (UTC) Received: from mta-64-227.siemens.flowmailer.net (mta-64-227.siemens.flowmailer.net [185.136.64.227]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.18818.1786918513652759509 for ; Sun, 16 Aug 2026 15:15:14 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=e64wzUK5; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.227, mailfrom: fm-1329275-202608162215105e922d17a30002071e-hafgv4@rts-flowmailer.siemens.com) Received: by mta-64-227.siemens.flowmailer.net with ESMTPSA id 202608162215105e922d17a30002071e for ; Mon, 17 Aug 2026 00:15:11 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=QHLo+BKxXu33FplVcx1AVuUamysT79lwxjYsH68bsdc=; b=e64wzUK5F31dvZFnwaBqRizcurUfJJIJhg7zyLkOxzAsyi8yAvknU15n82C/ZQJ9cuV70f AiKqDaYrFW28SMGt6iP3K59MC4uNR9wtKO/McRXYV96Zd+sgmxjN3rUN6LRJEl+s018C/3bD pgmm3PBHRrg/3uM5qHjAAWdf9mdo7W1UJpuBxMdM7ZdfctasJk4ZjsdoM4Xwmez2j8QTWdE0 ZPi4cbykZHVh7QqRycHIgs9ddV2uuLnpp2801GMLB1CcS24DMYCeCvZK3UHAMCuOwFwkkwfa n6Ue8T+2GzgH89mgjJfZHc4qQF4SVc4cCEpV3AJ2XKsnBrUrSdEobjzA==; From: AdrianF To: bitbake-devel@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 1/8] cooker: fix bitbake -b silently ignoring bbappends Date: Mon, 17 Aug 2026 00:14:40 +0200 Message-ID: <20260816221507.155861-2-adrian.freihofer@siemens.com> In-Reply-To: <20260816221507.155861-1-adrian.freihofer@siemens.com> References: <20260816221507.155861-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sun, 16 Aug 2026 22:15:21 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/bitbake-devel/message/19958 From: Adrian Freihofer "bitbake -b " builds the recipe without applying any of its .bbappend files. buildFileInternal() resolves appends via self.collections[mc].get_file_appends(fn), but self.collections[mc] is only ever filled in by collect_bbfiles(), called from updateCache() - a path -b deliberately skips. matchFiles(), the one -b-path function that does call collect_bbfiles(), built a fresh CookerCollectFiles into a throwaway local instead of self.collections[mc], so the append list stayed empty (or, on a memory-resident server, stale from the last full parse - e.g. missing a devtool/externalsrc workspace .bbappend added since). Nothing warns that the built metadata differs from disk. Make matchFiles() refresh self.collections[mc] itself so the later append lookup for the same fn sees the same fresh collection. AI-Generated: Uses GitHub Copilot Signed-off-by: Adrian Freihofer --- lib/bb/cooker.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/bb/cooker.py b/lib/bb/cooker.py index 4b6ba3196..108551a60 100644 --- a/lib/bb/cooker.py +++ b/lib/bb/cooker.py @@ -1322,8 +1322,10 @@ You can also remove the BB_HASHSERVE_UPSTREAM setting, but this may result in si if bf.startswith("/") or bf.startswith("../"): bf = os.path.abspath(bf) - collections = {mc: CookerCollectFiles(self.bbfile_config_priorities, mc)} - filelist, masked, searchdirs = collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc]) + # The only place the "bitbake -b" path fills in the bbappends which + # buildFileInternal() then reads back from self.collections[mc]. + self.collections[mc] = CookerCollectFiles(self.bbfile_config_priorities, mc) + filelist, masked, searchdirs = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc]) try: os.stat(bf) bf = os.path.abspath(bf) From patchwork Sun Aug 16 22:14:41 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95476 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id DDCBFC5DF74 for ; Sun, 16 Aug 2026 22:15:20 +0000 (UTC) Received: from mta-65-225.siemens.flowmailer.net (mta-65-225.siemens.flowmailer.net [185.136.65.225]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.18662.1786918513787792485 for ; Sun, 16 Aug 2026 15:15:15 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=XCX5hBcK; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.225, mailfrom: fm-1329275-20260816221511a2d9ae8261000207c4-v4ewgi@rts-flowmailer.siemens.com) Received: by mta-65-225.siemens.flowmailer.net with ESMTPSA id 20260816221511a2d9ae8261000207c4 for ; Mon, 17 Aug 2026 00:15:11 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=N1l4n262KqRN8BUlD795V5kG6BfqJ1qcsaC3kwKrVwI=; b=XCX5hBcKJTh+eLURTVryjW8dyWtq9HdjX9XnHqwPRVCV2RhQcCEsjY+3PcbDqR07gP0mG3 wpDy4zeS5m/2XriZnhtj3LXwcU8wcKNgsX+dxDcwz7YzXSPf51LEaI22wwQHvsUeY2ykCLyE /H9ugDPBLnLJNqsPVKLvX9KIjZSiorPdhHNTQATnWypjAmHW/4F4fFl/KFAagl8zVAVic4hW HRDI+xB07bgfWtVf2HgJ+6OBv1zx/fq1/ATLusfIru1ATa5cDhWCrWo9hmZbt++F1JzkdRXW YREmiIG6p+MW27CEHWAovUtt2mHPfFqhIocQ6jGkkTHLFIvBQ9oC4FVg==; From: AdrianF To: bitbake-devel@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 2/8] tests/cooker: add a shared bitbake-subprocess test base class Date: Mon, 17 Aug 2026 00:14:41 +0200 Message-ID: <20260816221507.155861-3-adrian.freihofer@siemens.com> In-Reply-To: <20260816221507.155861-1-adrian.freihofer@siemens.com> References: <20260816221507.155861-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sun, 16 Aug 2026 22:15:20 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/bitbake-devel/message/19963 From: Adrian Freihofer Tests that run bitbake or tinfoil in a subprocess against a temporary build directory can leave behind a memory-resident bitbake server (and, if BB_HASHSERVE=auto, a hashserv) rooted at TOPDIR. That server must release the directory before the caller's TemporaryDirectory context manager can safely remove it, or cleanup can race a still-running server holding files open there. Add _BitbakeSubprocessTestCase with _run_subprocess()/_shutdown() to provide this consistently, so the tests added on top of it in the next commits don't each have to hand-roll the subprocess/wait boilerplate - and, more importantly, don't get a chance to forget the wait. AI-Generated: Uses GitHub Copilot Signed-off-by: Adrian Freihofer --- lib/bb/tests/cooker.py | 52 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/lib/bb/tests/cooker.py b/lib/bb/tests/cooker.py index 9e524ae34..c49375ed8 100644 --- a/lib/bb/tests/cooker.py +++ b/lib/bb/tests/cooker.py @@ -7,12 +7,62 @@ # import unittest +import contextlib import os +import subprocess +import sys +import tempfile +import time import bb, bb.cooker import re import logging -# Cooker tests + +class _BitbakeSubprocessTestCase(unittest.TestCase): + """Common helpers for tests that run bitbake/tinfoil in a subprocess. + + Shared because every such subprocess can start a memory-resident bitbake + server (and, if BB_HASHSERVE=auto, a hashserv) rooted at TOPDIR, and both + must release that directory before the caller's TemporaryDirectory can be + safely removed. + """ + + def _run_subprocess(self, cmd, env, cwd): + proc = subprocess.run( + cmd, + env=env, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + cwd=cwd, + ) + if proc.returncode: + self.fail('%s failed: %s' % (cmd, proc.stdout)) + return proc.stdout + + def _shutdown(self, builddir): + """Wait for the bitbake server and hashserv to release builddir. + + Must run before the caller's TemporaryDirectory is removed, so it + cannot be a tearDown(). + """ + deadline = time.monotonic() + 30 + while time.monotonic() < deadline: + if not any(os.path.exists(os.path.join(builddir, p)) + for p in ('hashserve.sock', 'bitbake.lock')): + return + time.sleep(0.5) + + @contextlib.contextmanager + def _build_dir(self, prefix='tinfoiltest'): + """TemporaryDirectory that also waits out _shutdown() before removal.""" + with tempfile.TemporaryDirectory(prefix=prefix) as builddir: + try: + yield builddir + finally: + self._shutdown(builddir) + + class CookerTest(unittest.TestCase): def setUp(self): # At least one variable needs to be set From patchwork Sun Aug 16 22:14:42 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95478 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 43786C5DF7B for ; Sun, 16 Aug 2026 22:15:21 +0000 (UTC) Received: from mta-64-228.siemens.flowmailer.net (mta-64-228.siemens.flowmailer.net [185.136.64.228]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.18820.1786918513981367473 for ; Sun, 16 Aug 2026 15:15:14 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=lKTJ2PXr; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.228, mailfrom: fm-1329275-202608162215111c66f403060002071e-0ishme@rts-flowmailer.siemens.com) Received: by mta-64-228.siemens.flowmailer.net with ESMTPSA id 202608162215111c66f403060002071e for ; Mon, 17 Aug 2026 00:15:11 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=6J85fSpDKPsXWHDTZZ7Xe8eirjAj37YNt4RskIzRLjY=; b=lKTJ2PXrk74HnyJfrZXS26A5lIfn7LCcjPss1j77BolLiRy7sLqidbQVMz62UoJN4wOrGK BFJko/v3iw/K8V83fJi5eD2YhJONWgTAYYc2W2GCb9l8MYPn+oIj5SffV7k8J16i6W2eLv0e CDRLRQ2VlM5d0CDQK8zva/PLDVHMAKpkJwbVHsfG0hGE6iCyXLRT2FAw2CFsVB/CBf+7cG9T 6cWitr7Bk5OGH25PTJe1N8yNlFrnh9B9yarIoBhrqJoGr3NEF8oJnotqQAfRMfiFSKfLlCTP SIpsad3cvF/DhDKb+zWy1//4qjq1kRwugJGwpVEi6mb4p3N0sRFiP/CA==; From: AdrianF To: bitbake-devel@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 3/8] tests/cooker: add a bitbake -b bbappend test Date: Mon, 17 Aug 2026 00:14:42 +0200 Message-ID: <20260816221507.155861-4-adrian.freihofer@siemens.com> In-Reply-To: <20260816221507.155861-1-adrian.freihofer@siemens.com> References: <20260816221507.155861-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sun, 16 Aug 2026 22:15:21 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/bitbake-devel/message/19959 From: Adrian Freihofer Add BuildFileTest.test_buildfile_applies_bbappends() to cover the buildfile ("bitbake -b") mode, which had no test coverage at all. The test writes a recipe plus a matching .bbappend into a temporary directory, points EXTRA_BBFILES at both, and runs a task that records the value of a variable the bbappend overrides. It then asserts the bbappend's value was the one in effect. It uses the parse-tests BBPATH rather than runqueue-tests because its bitbake.conf already globs *.bbappend and the recipe needs no task scaffolding beyond its own. Without the preceding fix the recorded value is the recipe's default, i.e. -b built the recipe with the bbappend silently dropped. Built on _BitbakeSubprocessTestCase so the "bitbake -b" subprocess's server is properly waited on before the TemporaryDirectory cleanup runs. AI-Generated: Uses GitHub Copilot Signed-off-by: Adrian Freihofer --- lib/bb/tests/cooker.py | 46 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/lib/bb/tests/cooker.py b/lib/bb/tests/cooker.py index c49375ed8..c32694cc5 100644 --- a/lib/bb/tests/cooker.py +++ b/lib/bb/tests/cooker.py @@ -119,3 +119,49 @@ class CookerTest(unittest.TestCase): expected = [] self.assertEqual(log_handler.logdata, expected) + + +class BuildFileTest(_BitbakeSubprocessTestCase): + """Tests for the buildfile ("bitbake -b") mode.""" + + # parse-tests BBPATH: minimal bitbake.conf whose BBFILES honours + # EXTRA_BBFILES and already includes *.bbappend + _parsetests = os.path.realpath(os.path.join(os.path.dirname(__file__), "parse-tests")) + + recipe = """\ +MARKER ??= "no-bbappend" +python do_marker() { + with open(d.expand("${TOPDIR}/marker.log"), "w") as f: + f.write(d.getVar("MARKER")) +} +addtask marker +""" + + bbappend = 'MARKER = "bbappend-applied"\n' + + def test_buildfile_applies_bbappends(self): + """bitbake -b must build the recipe with its bbappends applied. + + buildFileInternal() looks the appends up in self.collections[mc], which + on the -b path is only ever populated by matchFiles(). + """ + with tempfile.TemporaryDirectory(prefix="buildfilerecipes") as recipes, \ + self._build_dir(prefix="buildfiletest") as builddir: + recipe = os.path.join(recipes, "appendtest.bb") + with open(recipe, "w") as f: + f.write(self.recipe) + with open(os.path.join(recipes, "appendtest.bbappend"), "w") as f: + f.write(self.bbappend) + + env = os.environ.copy() + env["BBPATH"] = self._parsetests + env["BB_ENV_PASSTHROUGH_ADDITIONS"] = "TOPDIR EXTRA_BBFILES" + env["TOPDIR"] = builddir + env["EXTRA_BBFILES"] = "%s/*.bb %s/*.bbappend" % (recipes, recipes) + + cmd = ["bitbake", "-b", recipe, "-c", "marker"] + self._run_subprocess(cmd, env, builddir) + + with open(os.path.join(builddir, "marker.log")) as f: + self.assertEqual(f.read(), "bbappend-applied", + "bitbake -b did not apply the recipe's bbappend") From patchwork Sun Aug 16 22:14:43 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95473 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 562ADC5B572 for ; Sun, 16 Aug 2026 22:15:19 +0000 (UTC) Received: from mta-64-227.siemens.flowmailer.net (mta-64-227.siemens.flowmailer.net [185.136.64.227]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.18658.1786918513597642071 for ; Sun, 16 Aug 2026 15:15:15 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=TLn6fV1T; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.227, mailfrom: fm-1329275-2026081622151152511fc91900020713-0bc_vz@rts-flowmailer.siemens.com) Received: by mta-64-227.siemens.flowmailer.net with ESMTPSA id 2026081622151152511fc91900020713 for ; Mon, 17 Aug 2026 00:15:11 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=Z80GARX2coisDMT58FFgCiS2Mdk+Hz2R7++QHzW0Fzs=; b=TLn6fV1TlgmC81f+98Z1yVJzaKvsYqK5ac2mGgvVQFwLY7PrnTK+k2bJdp8GjD3lTYMGXY ICHAM6nRS0mzweEeHfpoMwDX8Qk3Ht5M4jZsc2Ts8w0ukEIPljjIIDM2G1NwWzNY+XwT4Vgb +XihW37zkBB7YJky+h1RZSXRaTXjHNAid6iUDPf0bNc9h9y/GUQDbcIjcUexj1c7KYAevJ0W TQRN15MZPU5plSTI765FaAFF2+yBAQYNTg94AYO5sbf3Rjxa9txngUhdrzk3UBvdp7a+D6dg /vL9JuLHOXRUKHoFhFOJuMPVhEWfgl1+wON5iOPYZ1ozJaGJIUXNR43w==; From: AdrianF To: bitbake-devel@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 4/8] command: fix setConfig coercing bool config values to truthy strings Date: Mon, 17 Aug 2026 00:14:43 +0200 Message-ID: <20260816221507.155861-5-adrian.freihofer@siemens.com> In-Reply-To: <20260816221507.155861-1-adrian.freihofer@siemens.com> References: <20260816221507.155861-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sun, 16 Aug 2026 22:15:19 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/bitbake-devel/message/19966 From: Adrian Freihofer CommandsSync.setConfig() unconditionally stringified the value with str(params[1]) before assigning it to the cooker configuration attribute. This breaks boolean values, since str(True) and str(False) are both non-empty and therefore both truthy. That makes it impossible to turn a boolean option back off over the command interface: setting 'force' to False leaves configuration.force holding the truthy string "False", so it stays effectively enabled for the rest of the bitbake server session and spuriously invalidates tasks in later, unrelated builds sharing that session. Preserve the caller's original type instead of coercing to str. The only other caller (cookerdata.py) already passes a plain string, so this does not change behavior for it. AI-Generated: Uses GitHub Copilot Signed-off-by: Adrian Freihofer --- lib/bb/command.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bb/command.py b/lib/bb/command.py index 59a979ee9..b57c5d4a3 100644 --- a/lib/bb/command.py +++ b/lib/bb/command.py @@ -228,7 +228,7 @@ class CommandsSync: Set the value of variable in configuration """ varname = params[0] - value = str(params[1]) + value = params[1] setattr(command.cooker.configuration, varname, value) def enableDataTracking(self, command, params): From patchwork Sun Aug 16 22:14:44 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95479 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2AC25C5DF7D for ; Sun, 16 Aug 2026 22:15:21 +0000 (UTC) Received: from mta-64-228.siemens.flowmailer.net (mta-64-228.siemens.flowmailer.net [185.136.64.228]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.18819.1786918513979435499 for ; Sun, 16 Aug 2026 15:15:14 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=Xl0CmDWx; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.228, mailfrom: fm-1329275-2026081622151137320fd7b0000207ba-eaxmd6@rts-flowmailer.siemens.com) Received: by mta-64-228.siemens.flowmailer.net with ESMTPSA id 2026081622151137320fd7b0000207ba for ; Mon, 17 Aug 2026 00:15:11 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=OEv6tFJRl4U/kYGr7+7L3KMeFAhUjQDCluBqS8sTTU0=; b=Xl0CmDWxxtFKAlOgtMcr0mmxDRji591JvqYnjeMZq093JxHlir4NLM2NUNlGFJzdTJdlm2 Z5OJn75sqry0x2M9DLGdq2hLydHoumdB1Jycc66Z13BH0OZVsHnSeZmshBnQIdGsS3eXWpxt 5SCaOm86xfu9sXaEhT1W5AMdnkzUt4WLEAvacKq3MokCdd2aku79ngxKFoFPKTTIykxBaXva 6EP50Os3d+XPaTsLe3KtAQv/6GL/yFko0lm4kKfR4OXU7lQICsvjtX+gc16drYy/oNOP2c9r JhcHb8aTCSFINrXwix0bAcUB97HJzMiAA1Vr3JZkQUHIQAQyxjqUgtYQ==; From: AdrianF To: bitbake-devel@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 5/8] cooker: add a buildFile mode that runs a single task Date: Mon, 17 Aug 2026 00:14:44 +0200 Message-ID: <20260816221507.155861-6-adrian.freihofer@siemens.com> In-Reply-To: <20260816221507.155861-1-adrian.freihofer@siemens.com> References: <20260816221507.155861-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sun, 16 Aug 2026 22:15:21 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/bitbake-devel/message/19960 From: Adrian Freihofer devtool ide-sdk lets the IDE build directly (e.g. via cmake or meson, outside of bitbake). do_install still needs bitbake: it needs pseudo, and it usually does more than the underlying build tool's own install step (e.g. `cmake --build --target install`) would - packaging-related fixups the recipe or its classes add on top. It must run as only that one already-prepared task against the source the developer just edited - not the whole recipe: predecessors, if they ran again, would rebuild/overwrite the very output the caller is about to inspect or has already staged. Add a taskonly argument to buildFileInternal(), plumbed through the buildFile command, that also clears the intra-recipe task parents, leaving the requested task as the runqueue's only entry. Default unchanged, so "bitbake -b" behaviour is unaffected. Note that for example: - buildFileInternal() cannot do this: it drops external dependencies but keeps intra-recipe task ordering ('addtask X after Y'), so requesting do_install on an unbuilt recipe still pulls in do_fetch, do_unpack, do_patch, do_prepare_recipe_sysroot, do_configure and do_compile. That's the right default for "bitbake -b", but not here. - --runonly can't express this either: mark_active() ignores its depth argument and recurses over depends, so the whole chain stays active regardless. AI-Generated: Uses GitHub Copilot Signed-off-by: Adrian Freihofer --- lib/bb/command.py | 6 +++++- lib/bb/cooker.py | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/bb/command.py b/lib/bb/command.py index b57c5d4a3..1b16884fa 100644 --- a/lib/bb/command.py +++ b/lib/bb/command.py @@ -622,9 +622,13 @@ class CommandsAsync: internal = params[2] else: internal = False + if len(params) > 3: + taskonly = params[3] + else: + taskonly = False if internal: - command.cooker.buildFileInternal(bfile, task, fireevents=False, quietlog=True) + command.cooker.buildFileInternal(bfile, task, fireevents=False, quietlog=True, taskonly=taskonly) else: command.cooker.buildFile(bfile, task) buildFile.needcache = False diff --git a/lib/bb/cooker.py b/lib/bb/cooker.py index 108551a60..0321478d3 100644 --- a/lib/bb/cooker.py +++ b/lib/bb/cooker.py @@ -1368,7 +1368,7 @@ You can also remove the BB_HASHSERVE_UPSTREAM setting, but this may result in si self.buildFileInternal(buildfile, task) - def buildFileInternal(self, buildfile, task, fireevents=True, quietlog=False): + def buildFileInternal(self, buildfile, task, fireevents=True, quietlog=False, taskonly=False): """ Build the file matching regexp buildfile """ @@ -1418,6 +1418,12 @@ You can also remove the BB_HASHSERVE_UPSTREAM setting, but this may result in si self.recipecaches[mc].rundeps[fn] = defaultdict(list) self.recipecaches[mc].runrecs[fn] = defaultdict(list) + if taskonly: + # Drop the intra-recipe task ordering too ('addtask X after Y'), so + # that task is the only entry left in the runqueue. + task_deps = self.recipecaches[mc].task_deps[fn] + task_deps['parents'] = {t: [] for t in task_deps['tasks']} + bb.parse.siggen.setup_datacache(self.recipecaches) # Invalidate task for target if force mode active From patchwork Sun Aug 16 22:14:45 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95477 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0C1F3C5DF7A for ; Sun, 16 Aug 2026 22:15:21 +0000 (UTC) Received: from mta-64-226.siemens.flowmailer.net (mta-64-226.siemens.flowmailer.net [185.136.64.226]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.18661.1786918513787729393 for ; Sun, 16 Aug 2026 15:15:15 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=gLDGg8jD; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.226, mailfrom: fm-1329275-2026081622151127603b22800002074b-ixyfkk@rts-flowmailer.siemens.com) Received: by mta-64-226.siemens.flowmailer.net with ESMTPSA id 2026081622151127603b22800002074b for ; Mon, 17 Aug 2026 00:15:11 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=EJa+rFJHd+b8IRBuxWlquBJ1y7PKeOSmUlQyuNmQofs=; b=gLDGg8jDo2mfG+bb1BjLnSuduyiGZ/pBe9Td0oDYNV2CZFs2P7ru9dbOLk0RrVzl/969Jy asiBdGpcHiK7MHOangTiZ6BAmM2LwAtl14/xyhDSAtEaHF57ZBEnknHEj7MRa9/gAg9SWQ8j p74nuIf9auyL1zR3+L3x/pEAiRkfRypaal53OsnUg1sTRuhPmFXl6ioTlQrlob4XUQNr3A+g LkxYUNLDtWplMTW018Ok3Sl5r8cR75gfHBAfVGQmMGcyS3GgjuwucnInLbfQyEizJN8Hpp+T hk0JTVF7pvIZ5ArRu3xtupMuBk5f7urWv1kImbS1fLDoqGeaTv0tXZ/w==; From: AdrianF To: bitbake-devel@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 6/8] tinfoil: add a prepared task runner Date: Mon, 17 Aug 2026 00:14:45 +0200 Message-ID: <20260816221507.155861-7-adrian.freihofer@siemens.com> In-Reply-To: <20260816221507.155861-1-adrian.freihofer@siemens.com> References: <20260816221507.155861-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sun, 16 Aug 2026 22:15:21 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/bitbake-devel/message/19961 From: Adrian Freihofer Add run_prepared_task() to execute a single recipe task, and nothing else. No dependencies are resolved and no other task of the recipe is run, so everything the task consumes must already be in place. It uses BitBake's normal worker path, retaining the standard dispatch for shell and Python task bodies as well as fakeroot setup. devtool ide-sdk needs to rerun an already prepared BitBake task after a developer changes its source. A normal target build would create a runqueue and resolve dependencies again, and even "bitbake -b" would re-run the task's intra-recipe predecessors. That is wrong for this workflow: the developer intentionally wants to rerun only the prepared task against content the IDE has just produced. Expose the single-task worker path so devtool can reuse it instead of maintaining a separate task executor and pseudo-session setup. AI-Generated: Uses GitHub Copilot Signed-off-by: Adrian Freihofer --- lib/bb/tinfoil.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/bb/tinfoil.py b/lib/bb/tinfoil.py index 634d7796f..42385c879 100644 --- a/lib/bb/tinfoil.py +++ b/lib/bb/tinfoil.py @@ -826,20 +826,39 @@ class Tinfoil: else: return None - def build_file(self, buildfile, task, internal=True): + def build_file(self, buildfile, task, internal=True, taskonly=False): """ Runs the specified task for just a single recipe (i.e. no dependencies). This is equivalent to bitbake -b, except with the default internal=True no warning about dependencies will be produced, normal info messages from the runqueue will be silenced and BuildInit, BuildStarted and BuildCompleted events will not be fired. + With taskonly=True the recipe's own task ordering is dropped as well, so + only the requested task runs. """ - return self.run_command('buildFile', buildfile, task, internal) + return self.run_command('buildFile', buildfile, task, internal, taskonly) @wait_for def build_file_sync(self, *args): self.build_file(*args) + def run_prepared_task(self, recipe, task): + """Run *task* for one parsed recipe, and nothing else. + + No dependencies are resolved and no other task of the recipe is run, + so everything the task consumes must already be in place. The task + runs through the normal BitBake worker path, including fakeroot setup + and dispatch of shell or Python task bodies. + + The task is forced to execute rather than being skipped as up to date. + Returns False if the task failed. + """ + self.run_command('setConfig', 'force', True) + try: + return self.build_file_sync(self.get_recipe_file(recipe), task, True, True) + finally: + self.run_command('setConfig', 'force', False) + def build_targets(self, targets, task=None, handle_events=True, extra_events=None, event_callback=None): """ Builds the specified targets. This is equivalent to a normal invocation From patchwork Sun Aug 16 22:14:46 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95474 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6730EC5DF70 for ; Sun, 16 Aug 2026 22:15:19 +0000 (UTC) Received: from mta-65-225.siemens.flowmailer.net (mta-65-225.siemens.flowmailer.net [185.136.65.225]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.18660.1786918513787562090 for ; Sun, 16 Aug 2026 15:15:15 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=Rm21FPUu; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.225, mailfrom: fm-1329275-20260816221511cf92ef957c0002072e-rlrtpe@rts-flowmailer.siemens.com) Received: by mta-65-225.siemens.flowmailer.net with ESMTPSA id 20260816221511cf92ef957c0002072e for ; Mon, 17 Aug 2026 00:15:11 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=a0wK4tx14p+YFg+kNynKwjPGsq8o+Rx393o4g4x8gYo=; b=Rm21FPUuHg58/aci32F0gpJhUGe7gJu9aAyuOuYPeq+8EV9CNF/a0Wzy0eAiSVkJgo9t7o TS/DK3F+Bw6ryL5jfNKLOozjHhH8YpOg6qhFD0+gIRHOyyuy2CSewr/z6AYi8e8oPGtEJJ1/ R+lkhqkzVNCC+CKsIFMHI/WEqzbuTRJxBEbYM9etu9BUrx9FMqOl9kDLHhudwCmqMmLQkHxO 0K7BuXhCPUdizlSK+08Ah50Z8yIPjHxdcr81/XO+7rA/TnbhIHZm/XT0L3ledOCJ/LmUN1w8 XfgXwy1k+hMOzqgF6R3CG/wClRZvBPdAl1Ku3eDJwQ6wLrekLkfER2XQ==; From: AdrianF To: bitbake-devel@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 7/8] tests/cooker: add TinfoilTests for run_prepared_task Date: Mon, 17 Aug 2026 00:14:46 +0200 Message-ID: <20260816221507.155861-8-adrian.freihofer@siemens.com> In-Reply-To: <20260816221507.155861-1-adrian.freihofer@siemens.com> References: <20260816221507.155861-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sun, 16 Aug 2026 22:15:19 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/bitbake-devel/message/19965 From: Adrian Freihofer Add TinfoilTests, built on _BitbakeSubprocessTestCase, covering 'tinfoil: add a prepared task runner'. Each test spawns a subprocess to isolate tinfoil's server lifecycle. TestEquivHash is needed because the noop siggen's invalidate_task() removes the base stamp path instead of the task-specific one, making force=True a no-op otherwise. test_run_prepared_task_recipecache_self_heals additionally proves that run_prepared_task() (like "bitbake -b" before it) only wipes and repopulates the recipe cache for the one recipe it touches as a transient footprint: a subsequent normal, full parse restores the complete recipe set again. Lives in cooker.py rather than runqueue.py since it tests Tinfoil's Python API, not CLI-level runqueue behaviour. AI-Generated: Uses GitHub Copilot Signed-off-by: Adrian Freihofer --- lib/bb/tests/cooker.py | 244 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) diff --git a/lib/bb/tests/cooker.py b/lib/bb/tests/cooker.py index c32694cc5..db4104d40 100644 --- a/lib/bb/tests/cooker.py +++ b/lib/bb/tests/cooker.py @@ -63,6 +63,250 @@ class _BitbakeSubprocessTestCase(unittest.TestCase): self._shutdown(builddir) +class TinfoilTests(_BitbakeSubprocessTestCase): + """Tests for the Tinfoil API that require a running bitbake server.""" + + # Library directory containing bb.tinfoil + _bblib = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..')) + # runqueue-tests BBPATH (provides the simple a1/b1/... test recipes) + _runqueuetests = os.path.realpath(os.path.join(os.path.dirname(__file__), 'runqueue-tests')) + + failing_recipe = """\ +python do_install() { + bb.fatal("deliberate failure") +} +addtask install +""" + + # do_install genuinely depends on do_compile's output, unlike the + # a1 fixture recipe's dummy stamptask() tasks. + dependent_recipe = """\ +python do_compile() { + with open(d.expand("${TOPDIR}/compiled"), "w") as f: + f.write("compiled") +} +addtask compile + +python do_install() { + if not os.path.exists(d.expand("${TOPDIR}/compiled")): + bb.fatal("do_install ran without do_compile's output being present") +} +addtask install after do_compile +""" + + # Same contract as dependent_recipe, but with shell tasks, since a real + # recipe's do_install is usually shell (e.g. install/cp under pseudo) + # rather than a python function. + shell_dependent_recipe = """\ +do_compile() { + echo compiled > "${TOPDIR}/compiled" +} +addtask compile + +do_install() { + if [ ! -e "${TOPDIR}/compiled" ]; then + echo "do_install ran without do_compile's output being present" >&2 + exit 1 + fi +} +addtask install after do_compile +""" + + def _make_env(self, builddir, extra=None): + env = os.environ.copy() + env['PYTHONPATH'] = self._bblib + (':' + env['PYTHONPATH'] if 'PYTHONPATH' in env else '') + env['BBPATH'] = self._runqueuetests + env['BB_ENV_PASSTHROUGH_ADDITIONS'] = 'SSTATEVALID SLOWTASKS TOPDIR BB_HASHSERVE BB_SIGNATURE_HANDLER EXTRA_BBFILES' + env['SSTATEVALID'] = '' + env['SLOWTASKS'] = '' + env['TOPDIR'] = builddir + # TestEquivHash creates taint files so that force=True actually + # invalidates the task hash; the default noop siggen cannot do this. + env['BB_HASHSERVE'] = 'auto' + env['BB_SIGNATURE_HANDLER'] = 'TestEquivHash' + if extra: + env.update(extra) + return env + + def _run_script(self, builddir, script, extra=None): + """Run script in a subprocess to isolate tinfoil's server lifecycle.""" + return self._run_subprocess([sys.executable, '-c', script], + self._make_env(builddir, extra), builddir) + + def _read_tasklog(self, builddir, cleanup=True): + tasklog = os.path.join(builddir, 'task.log') + tasks = [] + if os.path.exists(tasklog): + with open(tasklog) as f: + tasks = [line.rstrip() for line in f] + if cleanup: + os.remove(tasklog) + return tasks + + def test_run_prepared_task(self): + """tinfoil.run_prepared_task() reruns one task without resolving deps. + + Uses do_install since that's the real devtool ide-sdk scenario: it + needs pseudo and so must run via bitbake, unlike the compile step which + the IDE invokes directly (e.g. via cmake/meson). + + Builds a1 completely so all stamps/hashes are valid, then calls + run_prepared_task('a1', 'install') through the Python API and verifies + that only do_install re-runs while its intra-recipe predecessors + (fetch, unpack, patch, prepare_recipe_sysroot, configure, compile) are + skipped. + """ + # The script runs inside a subprocess so that tinfoil's server + # lifecycle and environment modifications are isolated. + script = """ +import os, sys +import bb.tinfoil + +builddir = os.environ['TOPDIR'] +tasklog = os.path.join(builddir, 'task.log') + +with bb.tinfoil.Tinfoil() as tinfoil: + tinfoil.prepare(quiet=2) + # Full build so all stamps and hashes are valid. + tinfoil.build_targets(['a1']) + # Clear the log so only the run_prepared_task() entries are counted. + if os.path.exists(tasklog): + os.remove(tasklog) + # run_prepared_task() sets force=True (taint) and calls build_file_sync + # with the recipe file resolved via get_recipe_file(), bypassing the + # normal runqueue dependency resolver. + tinfoil.run_prepared_task('a1', 'install') +""" + with self._build_dir() as builddir: + self._run_script(builddir, script) + + tasks = self._read_tasklog(builddir) + self.assertEqual(tasks, ['a1:install'], + 'run_prepared_task should rerun only install, got: %s' % tasks) + + def test_run_prepared_task_unbuilt(self): + """run_prepared_task() does not pull any dependency task into the runqueue. + + buildFileInternal()'s taskonly=True clears task_deps['parents'] for + every task of the recipe, so do_install's intra-recipe predecessors + (fetch, unpack, patch, ...) are never added to the runqueue at all. + The recipe was never built here (no stamps exist for any of them), so + this is the case that would actually catch a regression: without + taskonly, those missing-stamp tasks would have to run to satisfy the + runqueue, and the tasklog assertion below would show more than just + 'a1:install'. + + Whether do_install itself then succeeds or fails is a separate matter + that does depend on the recipe: this fixture's do_install has no real + prerequisites, so it succeeds here; test_run_prepared_task_unbuilt_dependent_fails() + uses a recipe whose do_install does have one, and fails instead. + """ + script = """ +import bb.tinfoil + +with bb.tinfoil.Tinfoil() as tinfoil: + tinfoil.prepare(quiet=2) + assert tinfoil.run_prepared_task('a1', 'install') is True +""" + with self._build_dir() as builddir: + self._run_script(builddir, script) + + tasks = self._read_tasklog(builddir) + self.assertEqual(tasks, ['a1:install'], + 'run_prepared_task should run no dependency task, got: %s' % tasks) + + def test_run_prepared_task_unbuilt_dependent_fails(self): + """A task with a genuine dependency on a predecessor's output fails + when that predecessor never ran. + + Unlike test_run_prepared_task_unbuilt()'s fixture recipe, whose tasks + are dummy stamptask() calls with no real prerequisites, + dependent_recipe's do_install actually needs do_compile's output. + run_prepared_task() only skips bitbake's own dependency resolution; + it does not make the prerequisites appear, exactly as documented: + "everything the task consumes must already be in place". + """ + script = """ +import bb.tinfoil + +with bb.tinfoil.Tinfoil() as tinfoil: + tinfoil.prepare(quiet=2) + assert tinfoil.run_prepared_task('dependent', 'install') is False +""" + with tempfile.TemporaryDirectory(prefix='tinfoilrecipes') as recipes, \ + self._build_dir() as builddir: + with open(os.path.join(recipes, 'dependent.bb'), 'w') as f: + f.write(self.dependent_recipe) + self._run_script(builddir, script, + {'EXTRA_BBFILES': '%s/*.bb' % recipes}) + + def test_run_prepared_task_unbuilt_dependent_fails_shell(self): + """Same as test_run_prepared_task_unbuilt_dependent_fails(), but with + shell do_compile/do_install tasks instead of python ones, matching how + a real recipe's do_install is usually written. + """ + script = """ +import bb.tinfoil + +with bb.tinfoil.Tinfoil() as tinfoil: + tinfoil.prepare(quiet=2) + assert tinfoil.run_prepared_task('shelldependent', 'install') is False +""" + with tempfile.TemporaryDirectory(prefix='tinfoilrecipes') as recipes, \ + self._build_dir() as builddir: + with open(os.path.join(recipes, 'shelldependent.bb'), 'w') as f: + f.write(self.shell_dependent_recipe) + self._run_script(builddir, script, + {'EXTRA_BBFILES': '%s/*.bb' % recipes}) + + def test_run_prepared_task_failure(self): + """A failing task makes run_prepared_task() return False, not raise.""" + script = """ +import bb.tinfoil + +with bb.tinfoil.Tinfoil() as tinfoil: + tinfoil.prepare(quiet=2) + assert tinfoil.run_prepared_task('failer', 'install') is False +""" + with tempfile.TemporaryDirectory(prefix='tinfoilrecipes') as recipes, \ + self._build_dir() as builddir: + with open(os.path.join(recipes, 'failer.bb'), 'w') as f: + f.write(self.failing_recipe) + self._run_script(builddir, script, + {'EXTRA_BBFILES': '%s/*.bb' % recipes}) + + def test_run_prepared_task_recipecache_self_heals(self): + """A normal full parse after run_prepared_task() sees every recipe. + + run_prepared_task() -> buildFileInternal() -> parseConfiguration() + wipes and repopulates self.recipecaches[mc]/self.collections[mc] + for just the one recipe it builds - that has always been true of + "bitbake -b" too. Prove this is only a transient footprint: a + subsequent normal, full parse (as any non -b build would trigger) + must see the complete recipe set again, not just the one recipe + run_prepared_task() touched. + """ + script = """ +import bb.tinfoil + +with bb.tinfoil.Tinfoil() as tinfoil: + tinfoil.prepare(quiet=2) + tinfoil.parse_recipes() + recipes_before = tinfoil.run_command('getRecipes') + assert len(recipes_before) > 1, 'test fixture should have more than one recipe' + + tinfoil.run_prepared_task('a1', 'install') + + tinfoil.parse_recipes() + recipes_after = tinfoil.run_command('getRecipes') + assert len(recipes_after) == len(recipes_before), ( + 'recipe cache not fully restored after run_prepared_task(): ' + 'before=%d after=%d' % (len(recipes_before), len(recipes_after))) +""" + with self._build_dir() as builddir: + self._run_script(builddir, script) + + class CookerTest(unittest.TestCase): def setUp(self): # At least one variable needs to be set From patchwork Sun Aug 16 22:14:47 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95475 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id E79C7C5DF79 for ; Sun, 16 Aug 2026 22:15:20 +0000 (UTC) Received: from mta-65-227.siemens.flowmailer.net (mta-65-227.siemens.flowmailer.net [185.136.65.227]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.18663.1786918513890478162 for ; Sun, 16 Aug 2026 15:15:15 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=lWf6mjZU; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.227, mailfrom: fm-1329275-202608162215113ee3d26604000207de-j3cxi0@rts-flowmailer.siemens.com) Received: by mta-65-227.siemens.flowmailer.net with ESMTPSA id 202608162215113ee3d26604000207de for ; Mon, 17 Aug 2026 00:15:11 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=9WXvp6Q+Q1gwpxZLbwOYXlkLseb12r0UQTIYqM3gqh0=; b=lWf6mjZU0hoAUbt9wfsSVPlwwYBD1V/asfVgyGWs+KL+Yv8OBGBfz9zGxyXqYCTBiMp82p yDbx+sVUs+7TfAzafL8qSBHDRnFqjJQyTpHm7HQ9PPk0VtTlR+RvXEZcDheJGoBCRTGYS6cI T6bJ7QO10PBZ1QtmFs9B7ZrfQEVnUfgjXUFq14xJiqhlnBh2TCgZqHRSc8ZwBcJ5EkEjyXG/ 8zlGmz5uF1pMagDHpdncDbn+X25zWMilt2rnOnxNZ9ViJFqVrxhBvURhuzQe3crBTCqmODoN i9ELt4iu23od8ihIpomGbW935hr0wSKzpliWNHN1mYyYNh/CU55TSHMA==; From: AdrianF To: bitbake-devel@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 8/8] parse/ast: skip empty BBPATH segments in include_all Date: Mon, 17 Aug 2026 00:14:47 +0200 Message-ID: <20260816221507.155861-9-adrian.freihofer@siemens.com> In-Reply-To: <20260816221507.155861-1-adrian.freihofer@siemens.com> References: <20260816221507.155861-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sun, 16 Aug 2026 22:15:20 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/bitbake-devel/message/19962 From: Adrian Freihofer BBPATH can end up with an empty ":"-split segment when different layer.conf files mix the "${LAYERDIR}:" (prepend) and ":${LAYERDIR}" (append) idioms, e.g. openembedded-core's own meta/conf/layer.conf uses "BBPATH .= \":${LAYERDIR}\"" while every other layer.conf in a typical poky setup uses "BBPATH =. \"${LAYERDIR}:\"". Combined, this produces a literal "::" in the final value. IncludeAllNode.eval() iterates every BBPATH segment and calls os.path.join(path, s) to build the candidate file to include. For an empty segment, os.path.join("", s) returns s unchanged, i.e. a relative path instead of an absolute one. include_single_file() then takes its relative-path branch, which does its own independent search across the whole BBPATH and marks every path it tries (found or not) as a dependency via mark_dependency(), as a side effect of resolving that one (bogus) relative candidate. If that side search happens to try the real target file before this loop's own iteration for its actual BBPATH entry runs, check_dependency() reports it as already seen and include_single_file() logs a spurious "Duplicate inclusion" warning for it, even though the file is only ever included once. This is how e.g. oe-core's "include_all conf/distro/include/maintainers.inc" in defaultsetup.conf ends up warning about itself on every parse. Skip empty segments so an empty BBPATH entry cannot trigger this false-positive dependency marking. AI-Generated: Uses GitHub Copilot Signed-off-by: Adrian Freihofer --- lib/bb/parse/ast.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/bb/parse/ast.py b/lib/bb/parse/ast.py index a372b3534..866ab8ed1 100644 --- a/lib/bb/parse/ast.py +++ b/lib/bb/parse/ast.py @@ -56,6 +56,10 @@ class IncludeAllNode(AstNode): logger.debug2("CONF %s:%s: including %s", self.filename, self.lineno, s) for path in data.getVar("BBPATH").split(":"): + # Skip empty segments (e.g. from a stray "::" if some layer.conf + # uses ".= \":${LAYERDIR}\"" instead of "=. \"${LAYERDIR}:\""). + if not path: + continue bb.parse.ConfHandler.include(self.filename, os.path.join(path, s), self.lineno, data, False) class ExportNode(AstNode):