From patchwork Thu Sep 17 18:37:53 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 98562 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 738BCC982DD for ; Thu, 17 Sep 2026 18:38:48 +0000 (UTC) Received: from mta-65-228.siemens.flowmailer.net (mta-65-228.siemens.flowmailer.net [185.136.65.228]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.621.1789670320924562694 for ; Thu, 17 Sep 2026 11:38:41 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm1 header.b=gBHZAZPe; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.228, mailfrom: fm-1329275-20260917183837ec83644e9e000207b8-1rgbpa@rts-flowmailer.siemens.com) Received: by mta-65-228.siemens.flowmailer.net with ESMTPSA id 20260917183837ec83644e9e000207b8 for ; Thu, 17 Sep 2026 20:38:38 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; 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=S9cWwnc4DAHQzAhdNpQs9IHDgMxOCfrYjfv3MuRO0zc=; b=gBHZAZPeT8qCyILFfjXkH6769VbufuVRH6JZesdqnvCpG7BVS6v62BHpiOo+XjxcLQiZgV eMJq/wQfzI8/PxSsDlisN1pICkaaAnBKfeKPCiUHp0z4XgCZWFQK7O/HUOk4rGKBAHZrb2Qc Ra3TOZpXG58fMeAMyqc4SoyJk+hTSXwZ2jAKR/QSNcWHQI5oDONtcG6l/J6o/n8EdviQaGFY XrOzX438HemzVyrOnJnkKPBjey4T2RxuyGM+l4MKjAKymTokNjerjO2NutWYF1nin751nru0 I1qiXNjKCA3ogbHl6zL4H2MIu9ZES2oMp6owrVznQB3HN8LDcz30Bw6Q==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 1/7] devtool: ide-sdk: allow --ide to be given multiple times, add "all" Date: Thu, 17 Sep 2026 20:37:53 +0200 Message-ID: <20260917183832.998361-2-adrian.freihofer@siemens.com> In-Reply-To: <20260917183832.998361-1-adrian.freihofer@siemens.com> References: <20260917183832.998361-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 ; Thu, 17 Sep 2026 18:38:48 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/246082 From: Adrian Freihofer Replace the comma-separated --ide value (--ide code,none) with the --package/--file-glob style: --ide can now be given multiple times (-i code -i none) to set up several IDEs in one invocation. Also add an "all" choice that expands to every registered IDE plugin, so all supported configurations can be generated without listing them individually. Updated oe-selftest to use repeated --ide instead of the comma-separated form. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 10 +++++----- scripts/lib/devtool/ide_sdk.py | 25 ++++++++++--------------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index 84e020cd3d..0cede11d67 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -3565,7 +3565,7 @@ class DevtoolIdeSdkTests(DevtoolBase): self._verify_nfs_launch_json(tempdir, nfs_rootfs) def _test_devtool_ide_sdk_code_and_none_qemu(self, nfs=False, slirp=False): - """Verify devtool ide-sdk --ide=code,none for cmake, meson and the target toolchain. + """Verify devtool ide-sdk --ide=code --ide=none for cmake, meson and the target toolchain. Generating the VS Code (ide=code) and generic (ide=none) IDE configurations from a SINGLE devtool ide-sdk invocation and check @@ -3607,7 +3607,7 @@ class DevtoolIdeSdkTests(DevtoolBase): self._meson_recipe_name, "meson.build", testimage) package_opts = self._ide_sdk_package_opts() nfs_opts = ' --nfs=%s' % nfs_export if nfs else '' - runCmd('devtool ide-sdk %s -c --ide=code,none %s%s' % (testimage, package_opts, nfs_opts), + runCmd('devtool ide-sdk %s -c --ide=code --ide=none %s%s' % (testimage, package_opts, nfs_opts), output_log=self._cmd_logger) if slirp: @@ -3649,7 +3649,7 @@ class DevtoolIdeSdkTests(DevtoolBase): # not known at the time of the initial ide-sdk invocation. # --skip-bitbake also skips the NFS rootfs (re-)extraction, which # would otherwise wipe the directory the target has mounted. - bitbake_sdk_cmd = 'devtool ide-sdk %s %s --skip-bitbake --ide=code,none %s%s' % ( + bitbake_sdk_cmd = 'devtool ide-sdk %s %s --skip-bitbake --ide=code --ide=none %s%s' % ( testimage, target_options, package_opts, nfs_opts) runCmd(bitbake_sdk_cmd, output_log=self._cmd_logger) @@ -4231,7 +4231,7 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): @OETestTag("runqemu") def test_devtool_ide_sdk_code_and_none_qemu(self): - """Verify devtool ide-sdk --ide=code,none for cmake-example/meson-example with GCC and GDB.""" + """Verify devtool ide-sdk --ide=code --ide=none for cmake-example/meson-example with GCC and GDB.""" self._test_devtool_ide_sdk_code_and_none_qemu() @OETestTag("runqemu") @@ -5051,7 +5051,7 @@ class DevtoolIdeSdkClangTests(DevtoolIdeSdkTests): @OETestTag("runqemu") def test_devtool_ide_sdk_code_and_none_qemu(self): - """Verify devtool ide-sdk --ide=code,none for cmake/meson-example-clang with Clang and LLDB. + """Verify devtool ide-sdk --ide=code --ide=none for cmake/meson-example-clang with Clang and LLDB. See DevtoolIdeSdkTests._test_devtool_ide_sdk_code_and_none_qemu for the shared workflow. Clang-specific here: the generated configs diff --git a/scripts/lib/devtool/ide_sdk.py b/scripts/lib/devtool/ide_sdk.py index 8f2e768a62..20b03b2d40 100755 --- a/scripts/lib/devtool/ide_sdk.py +++ b/scripts/lib/devtool/ide_sdk.py @@ -1714,7 +1714,10 @@ def ide_setup(args, config, basepath, workspace): config, args.recipenames) orig_recipe_bbappend_contents = RecipeModified.strip_bbappend_sections( config, args.recipenames) - ides = [ide_plugins[name]() for name in args.ide] + ide_names = args.ide or [list(ide_plugins.keys())[0]] + if 'all' in ide_names: + ide_names = list(ide_plugins.keys()) + ides = [ide_plugins[name]() for name in ide_names] tinfoil = setup_tinfoil(config_only=False, basepath=basepath) try: # define mode depending on recipes which need to be processed @@ -2014,21 +2017,13 @@ def register_commands(subparsers, context): ' To use this tool-chain the environment-* file found in the deploy..image folder needs to be sourced into a shell.\n' ' In case of VSCode and cmake the tool-chain is also exposed as a cmake-kit') default_ide = list(ide_plugins.keys())[0] - - def ide_list(value): - """argparse type: comma separated list of IDE plugin names""" - names = value.split(',') - for name in names: - if name not in ide_plugins: - raise ValueError( - "invalid choice: %r (choose from %s)" % ( - name, ', '.join(sorted(ide_plugins.keys())))) - return names + ide_choices = list(ide_plugins.keys()) + ['all'] parser_ide_sdk.add_argument( - '-i', '--ide', type=ide_list, default=[default_ide], - metavar='{%s}' % ','.join(ide_plugins.keys()), - help='Comma separated list of IDEs to setup the configuration for ' - '(choices: %s, default: %s)' % (', '.join(ide_plugins.keys()), default_ide)) + '-i', '--ide', action='append', choices=ide_choices, + metavar='{%s}' % ','.join(ide_choices), + help='IDE to setup the configuration for. May be specified multiple times ' + 'to set up more than one IDE. "all" sets up every supported IDE ' + '(choices: %s, default: %s)' % (', '.join(ide_choices), default_ide)) parser_ide_sdk.add_argument( '-t', '--target', default='root@192.168.7.2', help='Live target machine running an ssh server: user@hostname.') From patchwork Thu Sep 17 18:37:54 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 98560 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 CCA9FC982D0 for ; Thu, 17 Sep 2026 18:38:47 +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.641.1789670320498030187 for ; Thu, 17 Sep 2026 11:38:42 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm1 header.b=kC+Ciklf; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.227, mailfrom: fm-1329275-202609171838388c8ec9cf850002073b-wmbivh@rts-flowmailer.siemens.com) Received: by mta-64-227.siemens.flowmailer.net with ESMTPSA id 202609171838388c8ec9cf850002073b for ; Thu, 17 Sep 2026 20:38:38 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; 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=DRuH3dLDFEe1I1uA4yUjsBBSeZaif4vhGMIfVNpqFiU=; b=kC+Ciklf3cIA4orQh20qOQwW/dY5HtRdLpVajceNTMMa5e18okjqxxxqZtah7BoE2Z+srE TjTDH/bffOld2MQDRlNfYlmgSEyb01LijIMjgpvxAq3WsVtQgx3/lDcfCoV4U+q3lK0h/Vhz iJQ8xmg8/vMGzAOpg5aegakpEGSW8FGzQ2g/y+M/IzOFRhZjir2qO8EoHfmYCCAPLwwer3zR EmEgMV/d7CJ97jhQo7yTrXfqQc6VXqV3Ca0BPd1O2q9f7TGcb5qCfTBbUV6opOd44al2bn6U e8huQutlhVKehX9dHzXw3Gy9o/tnHZf0ZC6mRMM2g5uIP1rDJA6sYcOA==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 2/7] devtool: ide-sdk: add real hardware NFS export helper Date: Thu, 17 Sep 2026 20:37:54 +0200 Message-ID: <20260917183832.998361-3-adrian.freihofer@siemens.com> In-Reply-To: <20260917183832.998361-1-adrian.freihofer@siemens.com> References: <20260917183832.998361-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 ; Thu, 17 Sep 2026 18:38:47 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/246085 From: Adrian Freihofer Generate a helper for NFS-enabled IDE SDK images that exports the rootfs with runqemu-export-rootfs and serves the deploy directory over HTTP. This allows real hardware targets to boot the generated rootfs and retrieve the kernel from the deploy directory over HTTP. For some use cases like u-boot with wget command built-in this helper is expected to cover the typical scenario. For other use cases the generated script at least provides a starting point and can be adapted as needed for other scenarios. Signed-off-by: Adrian Freihofer --- scripts/lib/devtool/ide_sdk.py | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/scripts/lib/devtool/ide_sdk.py b/scripts/lib/devtool/ide_sdk.py index 20b03b2d40..a2b15ce440 100755 --- a/scripts/lib/devtool/ide_sdk.py +++ b/scripts/lib/devtool/ide_sdk.py @@ -360,6 +360,33 @@ class RecipeImage: os.chmod(helper, os.stat(helper).st_mode | stat.S_IEXEC) return helper + def nfs_hw_helper(self, nfs_export_base_dir, nfs): + """Create a helper that exports the selected rootfs and the kernel to a target. + + The rootfs is exported over NFS with runqemu-export-rootfs + The deploy directory (fitImage/kernel) is served over plain + HTTP, e.g. for a U-Boot 'wget' in recovery/netboot mode. + """ + export_dir = os.path.join(nfs_export_base_dir, self.pn) + rootfs_dir = self.nfs_rootfs_dir(nfs_export_base_dir, nfs) + + helper = os.path.join(export_dir, 'export-hw-' + nfs) + with open(helper, 'w') as helper_file: + helper_file.write('#!/bin/sh\n') + helper_file.write('set -e\n') + helper_file.write( + 'runqemu-export-rootfs start %s\n' % shlex.quote(rootfs_dir)) + helper_file.write( + 'trap \'runqemu-export-rootfs stop %s\' EXIT INT TERM\n' % + shlex.quote(rootfs_dir)) + helper_file.write('cd %s\n' % shlex.quote(self.deploy_dir_image)) + helper_file.write( + 'echo "Serving %s over HTTP on port ${PORT:-8080}"\n' % + self.deploy_dir_image) + helper_file.write('python3 -m http.server "${PORT:-8080}"\n') + os.chmod(helper, os.stat(helper).st_mode | stat.S_IEXEC) + return helper + def update_image_bbappend(self, recipes_modified, nfs=None): """Write debug settings for modified-mode recipes into the image bbappend. @@ -489,6 +516,14 @@ class RecipeImage: 'Pass any additional runqemu options to this helper.', helper, opts) + hw_helper = self.nfs_hw_helper(nfs_export_base_dir, nfs) + logger.info( + 'With the build environment sourced, export the rootfs (NFS) and ' + 'the kernel (HTTP) for a real hardware target instead of QEMU:\n' + ' %s\n' + 'Set PORT to override the HTTP port (default: 8080).', + hw_helper) + def update_qb_slirp_opt(self): """Update QB_SLIRP_OPT in the image bbappend From patchwork Thu Sep 17 18:37:55 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 98563 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 3BF39C982DB for ; Thu, 17 Sep 2026 18:38:48 +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.msgproc01-g2.619.1789670320767513171 for ; Thu, 17 Sep 2026 11:38:41 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm1 header.b=KEeoNUkR; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.225, mailfrom: fm-1329275-20260917183838e6f74f38ae000207a1-_mgqbc@rts-flowmailer.siemens.com) Received: by mta-65-225.siemens.flowmailer.net with ESMTPSA id 20260917183838e6f74f38ae000207a1 for ; Thu, 17 Sep 2026 20:38:38 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; 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=ptuunu9Ekxp3uKtgAOW8NMhTLyzXG9DBvccZfd3MFMA=; b=KEeoNUkR1u38Zih2czXdl0OlwCL7uASSkJpg5p1g0Tqkwc7fF6sp/N4tRH/6ozVUCg2UW4 CjflUSuFFiNwbeKXRwIK6i/vwaq8C3PbtkYlzZjNaEk8bjoO+vtCPfvR+8GZozlmMG23We2j SrRWdyGNb4RLEAuj3EBInVgpHV3SQuOuLc7xo6ySPYYXc+Y3wgg1QJGeUG3W/50+TCuDMNi5 cHF8bTIH/fCIPBPMFU6rfOsXI3/T5CowEAwavpA9tW7CboxLLDeRYlGt7FwTtKuaydmUTcm8 uIqQ8foovo8IEaK0zMY9+/a+Fm6IHvWVV5YAgPpUvxG5tQEjLcfoYVrA==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 3/7] oe-selftest: devtool ide-sdk shorter test names Date: Thu, 17 Sep 2026 20:37:55 +0200 Message-ID: <20260917183832.998361-4-adrian.freihofer@siemens.com> In-Reply-To: <20260917183832.998361-1-adrian.freihofer@siemens.com> References: <20260917183832.998361-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 ; Thu, 17 Sep 2026 18:38:48 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/246081 From: Adrian Freihofer No functional changes, only test name shortening. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 54 ++++++++++++------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index 0cede11d67..535ffed9f0 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -3547,7 +3547,7 @@ class DevtoolIdeSdkTests(DevtoolBase): """Verify a recipe's binary was extracted into the NFS debug rootfs.""" self.assertExists(os.path.join(nfs_rootfs, 'usr', 'bin', example_exe)) - def _test_devtool_ide_sdk_code_nfs_debug_rootfs(self): + def _test_code_nfs_debug_rootfs(self): """Verify ide-sdk extracts an NFS debug rootfs for VS Code debugging.""" recipe_name = self._cmake_recipe_name build_file = "CMakeLists.txt" @@ -3564,7 +3564,7 @@ class DevtoolIdeSdkTests(DevtoolBase): self._verify_nfs_binary_deployed(nfs_rootfs, recipe_name) self._verify_nfs_launch_json(tempdir, nfs_rootfs) - def _test_devtool_ide_sdk_code_and_none_qemu(self, nfs=False, slirp=False): + def _test_code_and_none_qemu(self, nfs=False, slirp=False): """Verify devtool ide-sdk --ide=code --ide=none for cmake, meson and the target toolchain. Generating the VS Code (ide=code) and generic (ide=none) IDE @@ -4225,27 +4225,27 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): os.path.join(nfs_rootfs, 'usr', 'src', 'debug'), configuration['sourceFileMap']['/usr/src/debug']) - def test_devtool_ide_sdk_code_nfs_debug_rootfs(self): + def test_code_nfs_debug_rootfs(self): """Verify ide-sdk extracts an NFS debug rootfs for VS Code debugging.""" - self._test_devtool_ide_sdk_code_nfs_debug_rootfs() + self._test_code_nfs_debug_rootfs() @OETestTag("runqemu") - def test_devtool_ide_sdk_code_and_none_qemu(self): + def test_code_and_none_qemu(self): """Verify devtool ide-sdk --ide=code --ide=none for cmake-example/meson-example with GCC and GDB.""" - self._test_devtool_ide_sdk_code_and_none_qemu() + self._test_code_and_none_qemu() @OETestTag("runqemu") - def test_devtool_ide_sdk_none_nfs_qemu(self): + def test_code_and_none_nfs_qemu(self): """Verify the full ide=code,none workflow through an NFS-root QEMU target.""" - self._test_devtool_ide_sdk_code_and_none_qemu(nfs=True) + self._test_code_and_none_qemu(nfs=True) @OETestTag("runqemu") - def test_devtool_ide_sdk_none_nfs_qemu_slirp(self): + def test_code_and_none_nfs_qemu_slirp(self): """Verify the full ide=code,none workflow through an NFS-root slirp target.""" - self._test_devtool_ide_sdk_code_and_none_qemu(nfs=True, slirp=True) + self._test_code_and_none_qemu(nfs=True, slirp=True) @OETestTag("runqemu") - def test_devtool_ide_sdk_none_qemu_slirp(self): + def test_code_and_none_qemu_slirp(self): """Verify devtool ide-sdk works with runqemu slirp networking. Slirp mode uses SSH port forwarding (default: localhost:2222 -> guest:22). @@ -4255,13 +4255,13 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): - runqemu boots the image in slirp mode. - devtool deploy-target reaches the target via localhost:2222. """ - self._test_devtool_ide_sdk_code_and_none_qemu(slirp=True) + self._test_code_and_none_qemu(slirp=True) class DevtoolIdeSdkKernelTests(DevtoolIdeSdkTests): @OETestTag("runqemu") - def test_devtool_ide_sdk_code_kernel_module(self): + def test_code_kernel_module(self): """Verify a kernel module recipe works with ide=code mode Test flow: @@ -4477,7 +4477,7 @@ class DevtoolIdeSdkKernelTests(DevtoolIdeSdkTests): class DevtoolIdeSdkSharedTests(DevtoolIdeSdkTests): - def test_devtool_ide_sdk_shared_sysroots(self): + def test_shared_sysroots(self): """Verify the shared sysroot SDK""" # Handle the workspace (which is not needed by this test case) @@ -5045,15 +5045,15 @@ class DevtoolIdeSdkClangTests(DevtoolIdeSdkTests): lldb_server_pid, pid_file)) self.assertEqual(status, 0) - def test_devtool_ide_sdk_code_nfs_debug_rootfs(self): + def test_sdk_code_nfs_debug_rootfs(self): """Verify ide-sdk extracts an NFS debug rootfs for VS Code (CodeLLDB) debugging.""" - self._test_devtool_ide_sdk_code_nfs_debug_rootfs() + self._test_code_nfs_debug_rootfs() @OETestTag("runqemu") - def test_devtool_ide_sdk_code_and_none_qemu(self): + def test_code_and_none_qemu(self): """Verify devtool ide-sdk --ide=code --ide=none for cmake/meson-example-clang with Clang and LLDB. - See DevtoolIdeSdkTests._test_devtool_ide_sdk_code_and_none_qemu for + See DevtoolIdeSdkTests._test_code_and_none_qemu for the shared workflow. Clang-specific here: the generated configs select lldb-native/lldb-server instead of gdb-cross/gdbserver (extensions.json recommends vadimcn.vscode-lldb, launch.json uses @@ -5066,31 +5066,31 @@ class DevtoolIdeSdkClangTests(DevtoolIdeSdkTests): -fdebug-prefix-map/-ffile-prefix-map underflow breaking source-level breakpoint resolution in CodeLLDB. """ - self._test_devtool_ide_sdk_code_and_none_qemu() + self._test_code_and_none_qemu() @OETestTag("runqemu") - def test_devtool_ide_sdk_none_nfs_qemu(self): + def test_sdk_none_nfs_qemu(self): """Verify the full ide=code,none workflow through an NFS-root QEMU target.""" - self._test_devtool_ide_sdk_code_and_none_qemu(nfs=True) + self._test_code_and_none_qemu(nfs=True) @OETestTag("runqemu") - def test_devtool_ide_sdk_none_nfs_qemu_slirp(self): + def test_sdk_none_nfs_qemu_slirp(self): """Verify the full ide=code,none workflow through an NFS-root slirp target.""" - self._test_devtool_ide_sdk_code_and_none_qemu(nfs=True, slirp=True) + self._test_code_and_none_qemu(nfs=True, slirp=True) @OETestTag("runqemu") - def test_devtool_ide_sdk_none_qemu_slirp(self): + def test_sdk_none_qemu_slirp(self): """Verify devtool ide-sdk works with runqemu slirp networking. Slirp mode uses SSH port forwarding (default: localhost:2222 -> guest:22). """ - self._test_devtool_ide_sdk_code_and_none_qemu(slirp=True) + self._test_code_and_none_qemu(slirp=True) class DevtoolIdeSdkMiscTests(DevtoolIdeSdkTests): - def test_devtool_ide_sdk_plugins(self): - """Test that devtool ide-sdk can use plugins from other layers.""" + def test_plugins(self): + """Test that plugins from other layers can be used by devtool ide-sdk.""" # We need a workspace layer and a modified recipe (but no image) modified_recipe_name = "meson-example" From patchwork Thu Sep 17 18:37:56 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 98559 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 DAAECC982D9 for ; Thu, 17 Sep 2026 18:38:47 +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.642.1789670320606720005 for ; Thu, 17 Sep 2026 11:38:42 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm1 header.b=nk7eXvBx; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.226, mailfrom: fm-1329275-20260917183838d3ce1a3a04000207f9-g_2z_y@rts-flowmailer.siemens.com) Received: by mta-64-226.siemens.flowmailer.net with ESMTPSA id 20260917183838d3ce1a3a04000207f9 for ; Thu, 17 Sep 2026 20:38:38 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; 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=lbSR9TIPNgfw0SK54GXx83a56iROlharzJesCQQfL0Q=; b=nk7eXvBxTr44X99LNQHIus3mp8Y04xoYkAQggrY0YKSoM4MLLv0lrjMg8XGDGdsl0HbQNu D3AhorDU9pInrdw6CRW+w9AytAlN/lALEViO/Ig0ReLyJUTWMtdl/vcdHP8DRhKKWpa+5rwG ypV67v3WccueedgICqogfwjFGqgPKZP1tVeHbtd1AWglGIhnaBa1IS3aanybqC7COUe0nmNF jpeCeEfYcuda95jouf/JmylZN2kfSGBv82UXufXKxv9yUFpec826ro8mSpQo5uBcsNHdWkeq Fn26wazzi3vkXY9rFQuhjxwPaPgsuaApHY4ONYoJsRl+mzwyJvgTjFIQ==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 4/7] oe-selftest: devtool ide-sdk: enable qemu -d Date: Thu, 17 Sep 2026 20:37:56 +0200 Message-ID: <20260917183832.998361-5-adrian.freihofer@siemens.com> In-Reply-To: <20260917183832.998361-1-adrian.freihofer@siemens.com> References: <20260917183832.998361-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 ; Thu, 17 Sep 2026 18:38:47 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/246086 From: Adrian Freihofer Pass "-d" to runqemu for the ide-sdk selftests when oe-selftest's own logger is at DEBUG level, to get qemu's verbose logging for free when diagnosing a failure (e.g. slirp hostfwd port allocation) instead of having to hardcode it on for every run. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index 535ffed9f0..490a1d0bf0 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -3620,7 +3620,8 @@ class DevtoolIdeSdkTests(DevtoolBase): self._verify_nfs_launch_json(tempdir_cmake, nfs_rootfs) self._verify_nfs_launch_json(tempdir_meson, nfs_rootfs) - launch_cmd = '%s nographic' % shlex.quote(runqemu_helper) + qemu_debug_opt = ' -d' if self.logger.isEnabledFor(logging.DEBUG) else '' + launch_cmd = '%s nographic%s' % (shlex.quote(runqemu_helper), qemu_debug_opt) runqemuparams = '' if slirp: launch_cmd += ' slirp' @@ -3629,7 +3630,8 @@ class DevtoolIdeSdkTests(DevtoolBase): runqemuparams = 'slirp' qemu_cm = runqemu(testimage, runqemuparams=runqemuparams, launch_cmd=launch_cmd) else: - runqemuparams = "nographic slirp" if slirp else "nographic" + qemu_debug_opt = ' -d' if self.logger.isEnabledFor(logging.DEBUG) else '' + runqemuparams = ("nographic slirp" if slirp else "nographic") + qemu_debug_opt qemu_cm = runqemu(testimage, runqemuparams=runqemuparams) with qemu_cm as qemu: From patchwork Thu Sep 17 18:37:57 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 98564 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 9E454C982DC for ; Thu, 17 Sep 2026 18:38:48 +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.msgproc02-g2.645.1789670320798710367 for ; Thu, 17 Sep 2026 11:38:42 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm1 header.b=ile/DUfK; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.228, mailfrom: fm-1329275-20260917183838bf6ae70dcd000207d3-fx4fkf@rts-flowmailer.siemens.com) Received: by mta-64-228.siemens.flowmailer.net with ESMTPSA id 20260917183838bf6ae70dcd000207d3 for ; Thu, 17 Sep 2026 20:38:38 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; 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=aKORRr+PV+R5cSLBs76r8OK0l63cNGpEyx1Jbut/iPM=; b=ile/DUfKrBI/MJeAZtGo89IPTI0+j5enzHOsh9gW1gIhxJ0tPiRw0ZYCubwrZrcHFqlU7A shlY/T3k/DwEIIapFLlp7gjLM0Br12Uoizp/dvD7A7/PFMzJDpT6kQNvR1f59/36cyIRAFz9 1VB+O9amM4GGAhW8kUlLxypn2RHq/iv4xNkYz+tNkaX2NlzREW52MoiZlLRRPGFDPD+MT+3E W0n0eEya/y3eC5/NMiCuGPvWiIkt4hLq7j5p5f9Uf/PL1NBq5n9Ka8SVx0NV+9f1CVKT4c5W ou1oRnBz4355hgc/8KtQCzIpooofsTQzCtLfGkg/n2986C32XT9IXV/g==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 5/7] devtool: ide-sdk: move the loopback target helpers to ide_plugins Date: Thu, 17 Sep 2026 20:37:57 +0200 Message-ID: <20260917183832.998361-6-adrian.freihofer@siemens.com> In-Reply-To: <20260917183832.998361-1-adrian.freihofer@siemens.com> References: <20260917183832.998361-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 ; Thu, 17 Sep 2026 18:38:48 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/246083 From: Adrian Freihofer The debugger configuration classes in ide_plugins need to know whether the target loops back to the local machine, which is where the slirp port forwarding they have to deal with exists. They cannot import ide_sdk for it, since ide_sdk imports them. No functional change. Signed-off-by: Adrian Freihofer --- scripts/lib/devtool/ide_plugins/__init__.py | 14 ++++++++++++++ scripts/lib/devtool/ide_sdk.py | 16 +--------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/scripts/lib/devtool/ide_plugins/__init__.py b/scripts/lib/devtool/ide_plugins/__init__.py index 333ec9bac8..2cdb2e504a 100644 --- a/scripts/lib/devtool/ide_plugins/__init__.py +++ b/scripts/lib/devtool/ide_plugins/__init__.py @@ -15,6 +15,20 @@ from devtool import DevtoolError logger = logging.getLogger('devtool') +# Hosts a ssh target is considered to loop back to the local machine, e.g. a +# QEMU instance reached through slirp/hostfwd port forwarding (root@localhost) +# which has an ephemeral ssh host key that changes on every boot. +LOOPBACK_HOSTS = ('localhost', '127.0.0.1', '::1') + + +def target_host(target): + return target.split('@')[-1] + + +def is_loopback_target(target): + return target_host(target) in LOOPBACK_HOSTS + + class BuildTool(Enum): UNDEFINED = auto() CMAKE = auto() diff --git a/scripts/lib/devtool/ide_sdk.py b/scripts/lib/devtool/ide_sdk.py index a2b15ce440..a987c37f06 100755 --- a/scripts/lib/devtool/ide_sdk.py +++ b/scripts/lib/devtool/ide_sdk.py @@ -25,7 +25,7 @@ import bb from devtool import exec_build_env_command, setup_tinfoil, check_workspace_recipe, DevtoolError, parse_recipe from devtool.standard import get_real_srctree from devtool.deploy import parse_packages_arg -from devtool.ide_plugins import BuildTool, DebuggerCrossConfig +from devtool.ide_plugins import BuildTool, DebuggerCrossConfig, LOOPBACK_HOSTS, is_loopback_target from oe.kernel_module import kernel_module_os_env from pseudo_rootfs_utils import PseudoRootfsError, extract_sdk_rootfs, pseudo_state_dir @@ -48,20 +48,6 @@ class DevtoolIdeMode(Enum): shared = 'shared' -# Hosts a ssh target is considered to loop back to the local machine, e.g. a -# QEMU instance reached through slirp/hostfwd port forwarding (root@localhost) -# which has an ephemeral ssh host key that changes on every boot. -LOOPBACK_HOSTS = ('localhost', '127.0.0.1', '::1') - - -def target_host(target): - return target.split('@')[-1] - - -def is_loopback_target(target): - return target_host(target) in LOOPBACK_HOSTS - - class TargetDevice: """SSH remote login parameters""" From patchwork Thu Sep 17 18:37:58 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 98558 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 B7C68C88E72 for ; Thu, 17 Sep 2026 18:38:47 +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.643.1789670320666797561 for ; Thu, 17 Sep 2026 11:38:42 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm1 header.b=uPXY5LiC; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.226, mailfrom: fm-1329275-2026091718383833499c2651000207c1-qw3ab3@rts-flowmailer.siemens.com) Received: by mta-64-226.siemens.flowmailer.net with ESMTPSA id 2026091718383833499c2651000207c1 for ; Thu, 17 Sep 2026 20:38:38 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; 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=VP4NgDBWALOEON4xvldUfJqEPsCexCSkAAc58hB85Vk=; b=uPXY5LiCOnNdlQq/Uu/ljGrsG53nrgeqhS5lXxpHvpPWbFOlXA8DJA0719MmMs7TCkzjKd 2Vo+2F9jyarEbguzPidsarDfBdqbprMkWRdUy5xAGOgZXDmuz2MTGZ2c69GdsxodF3jQDvqC ynXHXbqr244dCc29ywHogtFPeW4nFb126ZDDkvYeX4Z6PgskN/BU+dwXYGvdjs0F6Qz6bOxl LNADEOyA/1YuG5uclF2dGST7NF/Gcf9s+cNVZfp+ZdK1Jsx9Q2ISHaP/+KT5FA0jkJoXnIuV CQxhFrt8gm+Aiu2ODVM1VhOgqTvwSnAov9tKNV9fy1jwH4jaUu2IeqFQ==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 6/7] devtool: ide-sdk: honour --gdbserver-port-start for all debug servers Date: Thu, 17 Sep 2026 20:37:58 +0200 Message-ID: <20260917183832.998361-7-adrian.freihofer@siemens.com> In-Reply-To: <20260917183832.998361-1-adrian.freihofer@siemens.com> References: <20260917183832.998361-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 ; Thu, 17 Sep 2026 18:38:47 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/246084 From: Adrian Freihofer The value was stored in RecipeGdbCross.gdbserver_port_next, which nothing ever read. The ports actually handed out come from the class wide counter DebuggerCrossConfig._port_next, which is shared by the gdbserver and lldb-server configurations and always started at its hardcoded 1234, so -G had no effect at all. Seed that counter instead, which also makes the option a way to move all debug servers of one invocation out of the way of another one running concurrently on the same host. Signed-off-by: Adrian Freihofer --- scripts/lib/devtool/ide_sdk.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/lib/devtool/ide_sdk.py b/scripts/lib/devtool/ide_sdk.py index a987c37f06..e1a9d1774e 100755 --- a/scripts/lib/devtool/ide_sdk.py +++ b/scripts/lib/devtool/ide_sdk.py @@ -123,7 +123,6 @@ class RecipeGdbCross(RecipeNative): super().__init__('gdb-cross-' + target_arch, target_arch) self.target_device = target_device self.gdb = None - self.gdbserver_port_next = int(args.gdbserver_port_start) self.config_db = {} def __find_gdbserver(self, config, tinfoil): @@ -1856,6 +1855,7 @@ def ide_setup(args, config, basepath, workspace): if args.mode == DevtoolIdeMode.modified: logger.info("Setting up workspaces for modified recipe: %s" % str(recipes_modified_names)) + DebuggerCrossConfig._port_next = int(args.gdbserver_port_start) debuggers = {} for recipe_name in recipes_modified_names: recipe_modified = RecipeModified( @@ -2049,7 +2049,9 @@ def register_commands(subparsers, context): '-t', '--target', default='root@192.168.7.2', help='Live target machine running an ssh server: user@hostname.') parser_ide_sdk.add_argument( - '-G', '--gdbserver-port-start', default="1234", help='port where gdbserver is listening.') + '-G', '--gdbserver-port-start', default="1234", + help='First port used by the debug servers on the target. Each debug ' + 'configuration consumes one port, counting up from here.') parser_ide_sdk.add_argument( '-c', '--no-host-check', help='Disable ssh host key checking', action='store_true') parser_ide_sdk.add_argument( From patchwork Thu Sep 17 18:37:59 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 98561 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 1A174C982DA for ; Thu, 17 Sep 2026 18:38:48 +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.644.1789670320670501724 for ; Thu, 17 Sep 2026 11:38:42 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm1 header.b=hxHA9oKH; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.227, mailfrom: fm-1329275-20260917183838b9bb64f11e00020763-7eloik@rts-flowmailer.siemens.com) Received: by mta-64-227.siemens.flowmailer.net with ESMTPSA id 20260917183838b9bb64f11e00020763 for ; Thu, 17 Sep 2026 20:38:38 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; 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=goaqqBNm5D0ir7dqkBQRzoVfAJxxIbl9oNOv+n2VvFU=; b=hxHA9oKHKg2CPzfNISnQzfsi1ZwzJzGFOIIZyp0me3ZGSPhTD11wPoiPAAryqEcICN76sK 72AHUD604R+Pj6ru+jYtCqmjcQditZlHCwsQXV5a1/c0X6EnikzWeJHf21KGI++81H1Y86mM 7KyYPyojWSSw2dVFTb8XORqvELL8CE4E9gJf4AH8Mvs/iqz5tsp1Vy5pqYdLOvMOaBmFU76q bm5EjBIEVNHxWQ7TYcR0MFZSkpv4OeQKxlaRe64jaffPDHMdsquDtnNvtC1GdMxxGseZ2a39 zdgL3x+6qk6x1SfK1CRiWwQ4/GumWmdFU0Vs8i6K5snMHWDcuVse4wqA==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 7/7] oe-selftest: devtool ide-sdk: make slirp tests port collision proof Date: Thu, 17 Sep 2026 20:37:59 +0200 Message-ID: <20260917183832.998361-8-adrian.freihofer@siemens.com> In-Reply-To: <20260917183832.998361-1-adrian.freihofer@siemens.com> References: <20260917183832.998361-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 ; Thu, 17 Sep 2026 18:38:48 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/246087 From: Adrian Freihofer The hostfwd=tcp: entries in QB_SLIRP_OPT are only a wishlist for the debug server ports: runqemu moves forward to the next free host port whenever the wished-for one is already taken, e.g. by a parallel oe-selftest worker's qemu. Only the ssh port can be recovered afterwards. The debug server ones silently pointed the debugger at nothing instead. lldb-server's spawned gdbserver cannot cope with a moved port at all: its client is told the target port to reconnect to and there is no hook to tell it about a remapped host one. If the test allocates a pool of host ports that is free right beforehand and holds it for the debug servers, runqemu is expected to grant every wish in it 1:1, since nothing else can take one of them in between. Reserve such a block and pass it to the first devtool ide-sdk invocation, which bakes the ports into QB_SLIRP_OPT before the image is built. The debug server ports are therefore already final at that point, with no remapping left to resolve. devtool ide-sdk still has to be re-run once qemu is up regardless, and needs the same block passed again: not because of these ports, but because it is the only invocation that knows the live qemu IP and the ssh port, which is not covered by the reservation and can still be moved by runqemu's own port picking, e.g. against a second worker's default 2222. The reservation keeps its own lock directory rather than runqemu's, since holding runqemu's locks is exactly what would make it consider the reserved ports taken and move the forwards away again. Having the Linux kernel itself arbitrate the ports, by requesting them from qemu dynamically via QMP once it is running, would be a more robust fix and remove the need for this pool altogether, but that is a bigger refactoring of the runqemu/qemu integration than warranted here. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 64 +++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index 490a1d0bf0..5bb191ff47 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -5,9 +5,11 @@ # import errno +import fcntl import os import re import shutil +import socket import subprocess import tempfile import threading @@ -3066,6 +3068,12 @@ class RunCmdBackground: class DevtoolIdeSdkTests(DevtoolBase): MAGIC_STRING_ORIG = "Magic: 123456789" + SLIRP_PORT_BLOCK_SIZE = 32 + # runqemu locks /tmp/qemu-port-locks itself while picking the slirp host + # ports, so the reservation below needs a lock directory of its own: + # holding runqemu's locks would make it consider the reserved ports taken + # and move the forwards away again. + SLIRP_PORT_LOCK_DIR = '/tmp/oe-selftest-ide-sdk-port-locks' def setUp(self): super().setUp() @@ -3520,6 +3528,50 @@ class DevtoolIdeSdkTests(DevtoolBase): self.assertIn('hostfwd=tcp:127.0.0.1:2222-:22', bbappend_content, 'SSH slirp port forward missing from QB_SLIRP_OPT') + def _reserve_slirp_port_block(self): + """Reserve a block of host ports for this test's debug servers and return its first port. + + runqemu moves a QB_SLIRP_OPT forward to the next host port whenever the + wished-for one is already taken, and lldb-server's spawned gdbserver + has no way to tell its client about a remapped host port, so the wishes + have to be granted 1:1. Every parallel oe-selftest worker otherwise + starts from the same default port, so claim a block that is free right + now and hold it for the rest of the test. + """ + os.makedirs(self.SLIRP_PORT_LOCK_DIR, exist_ok=True) + for start in range(1234, 20000, self.SLIRP_PORT_BLOCK_SIZE): + block = range(start, start + self.SLIRP_PORT_BLOCK_SIZE) + # runqemu forwards these for ssh/telnet, handing one of them to a + # debug server as well would collide. + if 2222 in block or 2323 in block: + continue + locks = [] + for port in block: + lock = open(os.path.join(self.SLIRP_PORT_LOCK_DIR, '%d.lock' % port), 'w') + try: + fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) + except OSError: + lock.close() + break + locks.append(lock) + # The lock only coordinates with other ide-sdk selftests, so + # confirm nothing unrelated is holding the port either. + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as probe: + try: + probe.bind(('127.0.0.1', port)) + except OSError: + break + else: + for lock in locks: + self.addCleanup(lock.close) + self.logger.debug('Reserved slirp host ports %d-%d', + start, start + self.SLIRP_PORT_BLOCK_SIZE - 1) + return start + for lock in locks: + lock.close() + self.fail('Could not reserve %d consecutive free host ports' + % self.SLIRP_PORT_BLOCK_SIZE) + def _verify_nfs_debug_rootfs(self, testimage, nfs): """Verify the NFS debug rootfs was extracted and its runqemu launch helper generated.""" nfs_rootfs = os.path.join(self.workspacedir, 'nfs-exports', testimage, nfs) @@ -3607,7 +3659,13 @@ class DevtoolIdeSdkTests(DevtoolBase): self._meson_recipe_name, "meson.build", testimage) package_opts = self._ide_sdk_package_opts() nfs_opts = ' --nfs=%s' % nfs_export if nfs else '' - runCmd('devtool ide-sdk %s -c --ide=code --ide=none %s%s' % (testimage, package_opts, nfs_opts), + # runqemu only grants a QB_SLIRP_OPT wish while the host port is free, + # and lldb-server's spawned gdbserver needs host == target, so give the + # debug servers a block no parallel worker uses (see + # _reserve_slirp_port_block); both invocations must agree on it. + port_opts = ' -G %d' % self._reserve_slirp_port_block() if slirp else '' + runCmd('devtool ide-sdk %s -c --ide=code --ide=none %s%s%s' % ( + testimage, package_opts, nfs_opts, port_opts), output_log=self._cmd_logger) if slirp: @@ -3651,8 +3709,8 @@ class DevtoolIdeSdkTests(DevtoolBase): # not known at the time of the initial ide-sdk invocation. # --skip-bitbake also skips the NFS rootfs (re-)extraction, which # would otherwise wipe the directory the target has mounted. - bitbake_sdk_cmd = 'devtool ide-sdk %s %s --skip-bitbake --ide=code --ide=none %s%s' % ( - testimage, target_options, package_opts, nfs_opts) + bitbake_sdk_cmd = 'devtool ide-sdk %s %s --skip-bitbake --ide=code --ide=none %s%s%s' % ( + testimage, target_options, package_opts, nfs_opts, port_opts) runCmd(bitbake_sdk_cmd, output_log=self._cmd_logger) # Verify the debugger is available and functional on host