diff mbox series

devtool: sdk-install: re-assemble the shared sysroot every time

Message ID 20260830183444.4025831-1-twoerner@gmail.com
State New
Headers show
Series devtool: sdk-install: re-assemble the shared sysroot every time | expand

Commit Message

Trevor Woerner Aug. 30, 2026, 6:34 p.m. UTC
sdk-install runs the build-sysroots tasks only when it found something
to install, so a recipe already staged in the components directory is
never assembled into the shared sysroot: sdk-install reports it as
installed while none of its commands are on PATH. devtool build leaves
that state, running do_populate_sysroot and nothing more. Both tasks are
nostamp with cleandirs, so running them unconditionally is idempotent.

The esdk selftest installs a native recipe, discards the assembled
sysroot and asks for it again, requiring the command to stay inside the
eSDK.

[YOCTO #11138]

AI-Generated: codex/claude-opus 5 (xhigh)
Signed-off-by: Trevor Woerner <twoerner@gmail.com>
---
 meta/lib/oeqa/selftest/cases/esdk.py | 44 ++++++++++++++++++++++++++--
 scripts/lib/devtool/sdk.py           | 10 +++----
 2 files changed, 47 insertions(+), 7 deletions(-)
diff mbox series

Patch

diff --git a/meta/lib/oeqa/selftest/cases/esdk.py b/meta/lib/oeqa/selftest/cases/esdk.py
index 7a5fe00a0823..8536c9b56e8f 100644
--- a/meta/lib/oeqa/selftest/cases/esdk.py
+++ b/meta/lib/oeqa/selftest/cases/esdk.py
@@ -33,7 +33,7 @@  class oeSDKExtSelfTest(OESelftestTestCase):
         if not 'shell' in options:
             options['shell'] = True
 
-        runCmd("cd %s; unset BBPATH; unset BUILDDIR; . %s; %s" % (tmpdir_eSDKQA, env_eSDK, cmd), **options)
+        return runCmd("cd %s; unset BBPATH; unset BUILDDIR; . %s; %s" % (tmpdir_eSDKQA, env_eSDK, cmd), **options)
 
     @staticmethod
     def generate_eSDK(image):
@@ -111,8 +111,48 @@  SSTATE_MIRRORS =  "file://.* file://%s/PATH"
         cmd = "devtool sdk-install %s " % pn_sstate
         oeSDKExtSelfTest.run_esdk_cmd(self.env_eSDK, self.tmpdir_eSDKQA, cmd)
 
+    def test_sdk_install_assembles_the_shared_sysroot(self):
+        """
+        A recipe's commands are only on PATH once the shared sysroot has
+        been assembled from the components directory, and sdk-install
+        assembles it only when it had something to install. Install a
+        recipe, discard the assembled sysroot, and ask for the recipe
+        again: it is already staged, so there is nothing to install, and
+        the commands have to be reachable regardless.
+
+        The recipe has to be a native one. An eSDK builds PATH from the
+        native sysroot alone, so a target recipe's commands are not
+        reachable however the sysroot was assembled.
+        """
+        recipe = 'bc-native'
+        command = 'bc'
+        bitbake(recipe)
+        install = "devtool sdk-install %s" % recipe
+        oeSDKExtSelfTest.run_esdk_cmd(self.env_eSDK, self.tmpdir_eSDKQA, install)
+
+        # the state devtool build leaves behind: staged, not assembled
+        oeSDKExtSelfTest.run_esdk_cmd(self.env_eSDK, self.tmpdir_eSDKQA,
+                                      "bitbake build-sysroots -c clean")
+        oeSDKExtSelfTest.run_esdk_cmd(self.env_eSDK, self.tmpdir_eSDKQA, install)
+
+        # The eSDK environment appends the caller's PATH, and bc is not a
+        # host tool oe requires, so an unassembled sysroot resolves either
+        # to the build machine's copy or to nothing at all. Neither is a
+        # command error, so ask without raising and report which happened.
+        result = oeSDKExtSelfTest.run_esdk_cmd(self.env_eSDK, self.tmpdir_eSDKQA,
+                                               "command -v %s" % command,
+                                               ignore_status=True)
+        # sourcing the eSDK environment prints a banner, and run_esdk_cmd
+        # merges stderr into the output, so the answer is the last line
+        found = ""
+        if result.status == 0:
+            found = result.output.strip().splitlines()[-1].strip()
+        self.assertTrue(found.startswith(self.tmpdir_eSDKQA),
+                        "sdk-install left %s out of the eSDK's shared sysroot; "
+                        "%s resolved to %s"
+                        % (command, command, found or "nothing"))
+
     def test_image_generation_binary_feeds(self):
         image = 'core-image-minimal'
         cmd = "devtool build-image %s" % image
         oeSDKExtSelfTest.run_esdk_cmd(self.env_eSDK, self.tmpdir_eSDKQA, cmd)
-
diff --git a/scripts/lib/devtool/sdk.py b/scripts/lib/devtool/sdk.py
index 7adb3de806b7..555bc82969a4 100644
--- a/scripts/lib/devtool/sdk.py
+++ b/scripts/lib/devtool/sdk.py
@@ -300,11 +300,11 @@  def sdk_install(args, config, basepath, workspace):
         if failed:
             return 2
 
-        try:
-            exec_build_env_command(config.init_path, basepath, 'bitbake build-sysroots -c build_native_sysroot', watch=True)
-            exec_build_env_command(config.init_path, basepath, 'bitbake build-sysroots -c build_target_sysroot', watch=True)
-        except bb.process.ExecutionError as e:
-            raise DevtoolError('Failed to bitbake build-sysroots:\n%s' % (str(e)))
+    try:
+        exec_build_env_command(config.init_path, basepath, 'bitbake build-sysroots -c build_native_sysroot', watch=True)
+        exec_build_env_command(config.init_path, basepath, 'bitbake build-sysroots -c build_target_sysroot', watch=True)
+    except bb.process.ExecutionError as e:
+        raise DevtoolError('Failed to bitbake build-sysroots:\n%s' % (str(e)))
 
 
 def register_commands(subparsers, context):