From patchwork Mon Feb 23 21:06:34 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 81645 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 BCDF1EA4FC7 for ; Mon, 23 Feb 2026 21:08:14 +0000 (UTC) Received: from mta-64-225.siemens.flowmailer.net (mta-64-225.siemens.flowmailer.net [185.136.64.225]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.5320.1771880890339452630 for ; Mon, 23 Feb 2026 13:08:11 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=c5vTh662; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.225, mailfrom: fm-1329275-202602232108076374560e6000020733-2jqw_l@rts-flowmailer.siemens.com) Received: by mta-64-225.siemens.flowmailer.net with ESMTPSA id 202602232108076374560e6000020733 for ; Mon, 23 Feb 2026 22:08:07 +0100 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=D2lSxZXQVrdciUg/mjuWsnfkUHoZNn9RhSPtGyMnmbE=; b=c5vTh6623pvDp4yicN0c2FjnnlMzIVrt5+hmV2J8VyWXgup4iKMeBEldHPXjwS1as98VNC wbrUhw8eY/USk+uXOdLP99b2bL7lVV+XqU5dFrr/Yd4E/K8q1SMV5IHhBrcp9ahrDUU9ZK8I 73l6nYu3wK8BktzeJs+9Cv1FoiiY5WlTMHBNCcz/QNQVdBcIQLXIQPzfGL1MSbE6NIupD8lM WAuAixIZy4d7BNSpP8dwQRCJ+EkkrMEtW01rmoPiOFhDAm9Ay59gP7Tem513TYrTyNKkALNv BUVThS7esCf8Lv5riskarhVIxjT5IMmvpnCa1qNaba2pxuVUFo6eLiRg==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 1/7] oe-selftest: devtool: add compile step in ide-sdk tests Date: Mon, 23 Feb 2026 22:06:34 +0100 Message-ID: <20260223210748.1905502-2-adrian.freihofer@siemens.com> In-Reply-To: <20260223210748.1905502-1-adrian.freihofer@siemens.com> References: <20260223210748.1905502-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 ; Mon, 23 Feb 2026 21:08:14 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231693 From: Adrian Freihofer Add explicit compile step to the ide-sdk test workflow. The current implementation relies on calling bitbake -c install to perform the install step, which also triggers a build. But this will change when bitbake will support task execution without handling dependencies. To make the tests future-proof, add an explicit compile step after modifying the source code. This also improves the test coverage for meson based recipes, as the compile step is now explicitly tested. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 65 +++++++++++++++++++------ 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index d1209dd94e..f7f9deb6c7 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -2598,6 +2598,9 @@ class DevtoolIdeSdkTests(DevtoolBase): def _sources_scripts_dir(self, src_dir): return os.path.realpath(os.path.join(src_dir, 'oe-scripts')) + def _sources_workdir_dir(self, src_dir): + return os.path.realpath(os.path.join(src_dir, 'oe-workdir')) + def _workspace_gdbinit_dir(self, recipe_name): return os.path.realpath(os.path.join(self.builddir, 'workspace', 'ide-sdk', recipe_name, 'scripts', 'gdbinit')) @@ -2666,7 +2669,7 @@ class DevtoolIdeSdkTests(DevtoolBase): self._workspace_scripts_dir(recipe_name), i_and_d_script) self.assertExists(i_and_d_script_path) - def _devtool_ide_sdk_qemu(self, tempdir, qemu, recipe_name, example_exe): + def _devtool_ide_sdk_qemu(self, tempdir, qemu, recipe_name, example_exe, compile_cmd): """Verify deployment and execution in Qemu system work for one recipe. This function checks the entire SDK workflow: changing the code, recompiling @@ -2711,6 +2714,7 @@ class DevtoolIdeSdkTests(DevtoolBase): cpp_code = cpp_code.replace(DevtoolIdeSdkTests.MAGIC_STRING_ORIG, MAGIC_STRING_NEW) with open(cpp_example_lib_hpp, 'w') as file: file.write(cpp_code) + runCmd(compile_cmd, cwd=tempdir, output_log=self._cmd_logger) runCmd(install_deploy_cmd, cwd=tempdir, output_log=self._cmd_logger) # Verify the modified example prints the modified magic string @@ -2841,6 +2845,7 @@ class DevtoolIdeSdkTests(DevtoolBase): self.assertEqual(len(config_presets), 1) cmake_exe = config_presets[0]["cmakeExecutable"] preset_name = config_presets[0]["name"] + compile_cmd = '%s --build --preset %s' % (cmake_exe, preset_name) # Verify the wrapper for cmake native is available self.assertExists(cmake_exe) @@ -2850,28 +2855,59 @@ class DevtoolIdeSdkTests(DevtoolBase): self.assertIn(preset_name, result.output) # Verify cmake re-uses the o files compiled by bitbake - result = runCmd('%s --build --preset %s' % - (cmake_exe, preset_name), cwd=tempdir, output_log=self._cmd_logger) + result = runCmd(compile_cmd, cwd=tempdir, output_log=self._cmd_logger) self.assertIn("ninja: no work to do.", result.output) # Verify the unit tests work (in Qemu user mode) - result = runCmd('%s --build --preset %s --target test' % - (cmake_exe, preset_name), cwd=tempdir, output_log=self._cmd_logger) + result = runCmd('%s --target test' % compile_cmd, cwd=tempdir, output_log=self._cmd_logger) self.assertIn("100% tests passed", result.output) # Verify re-building and testing works again - result = runCmd('%s --build --preset %s --target clean' % - (cmake_exe, preset_name), cwd=tempdir, output_log=self._cmd_logger) + result = runCmd('%s --target clean' % compile_cmd, cwd=tempdir, output_log=self._cmd_logger) self.assertIn("Cleaning", result.output) - result = runCmd('%s --build --preset %s' % - (cmake_exe, preset_name), cwd=tempdir, output_log=self._cmd_logger) + result = runCmd(compile_cmd, cwd=tempdir, output_log=self._cmd_logger) self.assertIn("Building", result.output) self.assertIn("Linking", result.output) - result = runCmd('%s --build --preset %s --target test' % - (cmake_exe, preset_name), cwd=tempdir, output_log=self._cmd_logger) + result = runCmd('%s --target test' % compile_cmd, cwd=tempdir, output_log=self._cmd_logger) self.assertIn("Running tests...", result.output) self.assertIn("100% tests passed", result.output) + return compile_cmd + + def _verify_meson_build(self, tempdir, recipe_name): + """Verify meson works as expected + + Check if compiling works + Check if unit tests can be executed in qemu (not qemu-system) + """ + meson_exe = os.path.join(self._workspace_scripts_dir(recipe_name), "meson") + self.assertExists(meson_exe) + build_dir = os.path.join(self._sources_workdir_dir(tempdir), recipe_name + "-1.0") + compile_cmd = '%s compile -C %s' % (meson_exe, build_dir) + + # Verify meson re-uses the o files compiled by bitbake + result = runCmd(compile_cmd, cwd=tempdir, output_log=self._cmd_logger) + self.assertIn("ninja: no work to do.", result.output) + + # Verify the unit tests work (in Qemu user mode) + result = runCmd('%s test -C %s' % (meson_exe, build_dir), + cwd=tempdir, output_log=self._cmd_logger) + self.assertEqual(result.status, 0) + self.assertIn("Fail: 0", result.output) + + # Verify re-building and testing works again + result = runCmd('%s compile -C %s --clean' % (meson_exe, build_dir), + cwd=tempdir, output_log=self._cmd_logger) + self.assertIn("Cleaning...", result.output) + result = runCmd(compile_cmd, cwd=tempdir, output_log=self._cmd_logger) + self.assertIn("Linking target", result.output) + result = runCmd('%s test -C %s' % (meson_exe, build_dir), + cwd=tempdir, output_log=self._cmd_logger) + self.assertEqual(result.status, 0) + self.assertIn("Fail: 0", result.output) + + return compile_cmd + def _verify_service_running(self, qemu, service_name): """Helper to verify a service is running in Qemu""" status, output = qemu.run("pgrep %s" % service_name) @@ -2921,8 +2957,8 @@ class DevtoolIdeSdkTests(DevtoolBase): runCmd(bitbake_sdk_cmd, output_log=self._cmd_logger) self._gdb_cross() - self._verify_cmake_preset(tempdir) - self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe) + compile_cmd = self._verify_cmake_preset(tempdir) + self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe, compile_cmd) # Verify the oe-scripts sym-link is valid self.assertEqual(self._workspace_scripts_dir( @@ -2953,7 +2989,8 @@ class DevtoolIdeSdkTests(DevtoolBase): runCmd(bitbake_sdk_cmd, output_log=self._cmd_logger) self._gdb_cross() - self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe) + compile_cmd = self._verify_meson_build(tempdir, recipe_name) + self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe, compile_cmd) # Verify the oe-scripts sym-link is valid self.assertEqual(self._workspace_scripts_dir( From patchwork Mon Feb 23 21:06:35 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 81646 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 BCE3EEA4FD8 for ; Mon, 23 Feb 2026 21:08:14 +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.5321.1771880890339920881 for ; Mon, 23 Feb 2026 13:08:11 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=PSSfkDe7; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.228, mailfrom: fm-1329275-20260223210807cd09546a45000207c8-e4podj@rts-flowmailer.siemens.com) Received: by mta-64-228.siemens.flowmailer.net with ESMTPSA id 20260223210807cd09546a45000207c8 for ; Mon, 23 Feb 2026 22:08:07 +0100 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=Is2DPq64dpIsCbjgXl9MpW3MmlY5c/dMSHDSz8lKoMQ=; b=PSSfkDe7aEXT7ndZLE2YfnIKsBlbkCPynSHK48sc3VsyOUb1fWvvFHXBNAVxt/zWc1FeUQ 55ksmGwUE10Ac/WhoC1Mh40JWDeYyuwSnAQjWi4wpXPu2bmlEPpw50L632O0tTbdOSGSeasH pRxNH6DEkfumEQTpkSGI/Ai/GCeaQmlSQ+lYxVquOT77BfU5ITf0jFXl0sIEdJtWFSLwrmuK hZTnSk3lGri0jDTIO9WycnPAWvmtVvs/dlPotJjA21WVtWrm0K7Oju4ms8GB1CLwc7CfXmPo 22u61WfZtzSO1Ag0FupeZskUer3gBM35OMVfoi4vOwZOLZ4ilKf5Oiug==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 2/7] oe-selftest: devtool: improve test_devtool_ide_sdk_shared_sysroots Date: Mon, 23 Feb 2026 22:06:35 +0100 Message-ID: <20260223210748.1905502-3-adrian.freihofer@siemens.com> In-Reply-To: <20260223210748.1905502-1-adrian.freihofer@siemens.com> References: <20260223210748.1905502-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 ; Mon, 23 Feb 2026 21:08:14 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231694 From: Adrian Freihofer In shared-sysroot IDE SDK mode, the C++ CMake example test ran CMake commands but did not assert that configure/build completed successfully. This could hide failures and produce false positives. Capture command output from both steps and assert expected messages: - configure: "Build files have been written to: " - build: "Built target" This makes the test explicitly fail when CMake configure or build does not complete as expected. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index f7f9deb6c7..6e6bada147 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -3338,8 +3338,12 @@ class DevtoolIdeSdkTests(DevtoolBase): cmake_native = os.path.normpath(result_cmake.output.strip()) self.assertExists(cmake_native) - runCmdEnv('cmake %s' % cpp_example_src, cwd=tempdir_cmake, output_log=self._cmd_logger) - runCmdEnv('cmake --build %s' % tempdir_cmake, cwd=tempdir_cmake, output_log=self._cmd_logger) + result_cmake = runCmdEnv('cmake -S %s -B %s' % (cpp_example_src, tempdir_cmake), + cwd=tempdir_cmake, output_log=self._cmd_logger) + self.assertIn("Build files have been written to: %s" % tempdir_cmake, result_cmake.output) + result_cmake = runCmdEnv('cmake --build %s' % tempdir_cmake, + cwd=tempdir_cmake, output_log=self._cmd_logger) + self.assertIn("Built target", result_cmake.output) # Verify the printed note really referres to a cmake executable cmake_native_code = "" From patchwork Mon Feb 23 21:06:36 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 81643 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 ADDEFEC113F for ; Mon, 23 Feb 2026 21:08:14 +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.5270.1771880890345406642 for ; Mon, 23 Feb 2026 13:08:11 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=OdWR3c+j; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.227, mailfrom: fm-1329275-202602232108077b1fc73b1f000207ff-o0jqfw@rts-flowmailer.siemens.com) Received: by mta-65-227.siemens.flowmailer.net with ESMTPSA id 202602232108077b1fc73b1f000207ff for ; Mon, 23 Feb 2026 22:08:07 +0100 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=vDCF/ic7iMF0NGecHj1JFvSOYi2e0RcsvOFBIslwrV8=; b=OdWR3c+j36HV2PlaDrB13pWNew+PCREFpmMfmO+QWBphpa8vp0rhfcM5KlhiwzhCsEHnn3 Wq9e3QHNAOTiLXm3mIOV2SX25T3kgbeQESIDK1JMXtltYVDV9YxFT1HUHnfjlQ6shEcZ2Tok fDSctyzgAirz1ympA3e5CpJY5y1I1x0TtJUW6iEMX1xQFQlaHwQ1XjgnG736eR0qPfDyRnvi 7CgswORUXBHpyf8YuLu8qlGSXwKEOtBbQi9qEgRaeIv5NFg6xhjQ43erSJJU5NP20TpT65Ok lnsZgsHFGMon3m/cJnAKzxm96IoCb7JOUlouhtCN/nqRH/5ZLtQqcGRw==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 3/7] devtool: ide-sdk gate gdbserver warnings on recipe need Date: Mon, 23 Feb 2026 22:06:36 +0100 Message-ID: <20260223210748.1905502-4-adrian.freihofer@siemens.com> In-Reply-To: <20260223210748.1905502-1-adrian.freihofer@siemens.com> References: <20260223210748.1905502-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 ; Mon, 23 Feb 2026 21:08:14 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231697 From: Adrian Freihofer Add a `wants_gdbserver` attribute to modified recipes and evaluate it across the selected set. Only emit warnings about missing `gdbserver` and missing `image-combined-dbg` when at least one recipe actually requires remote debugging support. This avoids noisy, irrelevant warnings in setups that do not use gdbserver. Signed-off-by: Adrian Freihofer --- scripts/lib/devtool/ide_sdk.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/scripts/lib/devtool/ide_sdk.py b/scripts/lib/devtool/ide_sdk.py index ba225f20b9..d6cda4be9d 100755 --- a/scripts/lib/devtool/ide_sdk.py +++ b/scripts/lib/devtool/ide_sdk.py @@ -427,6 +427,11 @@ class RecipeModified: self.__oe_init_dir = None # main build tool used by this recipe self.build_tool = BuildTool.UNDEFINED + # Whether this recipe benefits from gdbserver and rootfs-dbg in the image. + self.wants_gdbserver = True + # Whether to warn when DEBUG_BUILD is not set. Kernel modules are built + # by the kernel's build system and DEBUG_BUILD does not influence them. + self.wants_debug_build = True # build_tool = cmake self.oecmake_generator = None self.cmake_cache_vars = None @@ -1156,12 +1161,14 @@ def ide_setup(args, config, basepath, workspace): exec_build_env_command( config.init_path, basepath, bb_cmd_late, watch=True) + wants_gdbserver = any( + r.wants_gdbserver for r in recipes_modified) for recipe_image in recipes_images: - if (recipe_image.gdbserver_missing): + if wants_gdbserver and recipe_image.gdbserver_missing: logger.warning( "gdbserver not installed in image %s. Remote debugging will not be available" % recipe_image) - if recipe_image.combine_dbg_image is False: + if wants_gdbserver and recipe_image.combine_dbg_image is False: logger.warning( 'IMAGE_CLASSES += "image-combined-dbg" is missing for image %s. Remote debugging will not find debug symbols from rootfs-dbg.' % recipe_image) @@ -1178,7 +1185,7 @@ def ide_setup(args, config, basepath, workspace): ide.setup_modified_recipe( args, recipe_image, recipe_modified) - if recipe_modified.debug_build != '1': + if recipe_modified.wants_debug_build and recipe_modified.debug_build != '1': logger.warn( 'Recipe %s is compiled with release build configuration. ' 'You might want to add DEBUG_BUILD = "1" to %s. ' From patchwork Mon Feb 23 21:06:37 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 81647 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 C9E71EEC281 for ; Mon, 23 Feb 2026 21:08:14 +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.5319.1771880890339280837 for ; Mon, 23 Feb 2026 13:08:11 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=HAPYBq1r; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.227, mailfrom: fm-1329275-202602232108071ddacbb7a400020779-soxxlo@rts-flowmailer.siemens.com) Received: by mta-64-227.siemens.flowmailer.net with ESMTPSA id 202602232108071ddacbb7a400020779 for ; Mon, 23 Feb 2026 22:08:07 +0100 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=GqWGOONnBUjjxjEvxnuh7z7lsLRjLH3CzUJT2AOYHAc=; b=HAPYBq1rJ4hEmn2NaAApkAscFYiZUWzj7LYbU5+nNeQZVJgKEpdotjwC7FRKMPT5I0xk2J UZ2nJUvSUIxVdP3fVdAnCgxc0QLktNbuEt9Jq1VrRcAv0S4tZKHsZpRY+JdsRKWDyTozs090 q/fN5UcjFR1sZcCLrnm8Wi2s+pqX/fDT1ylD+a+O6OMKfZVJckJFwhc69DnzJ6udpuJhzYL4 FKxTxO0kusF7Bi5nP2xD94DcmkbaLfAHadfb0f21rAoWZ+YuCg0e8a3oOZdNrr/p7+fIyMbN a45yTLDa7Vabp6VexCDur0Zf0qyCjBung3UBg7HMK4NnV+L/S5m3ATZw==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 4/7] module.bbclass: move environment setup to kernel_module.py Date: Mon, 23 Feb 2026 22:06:37 +0100 Message-ID: <20260223210748.1905502-5-adrian.freihofer@siemens.com> In-Reply-To: <20260223210748.1905502-1-adrian.freihofer@siemens.com> References: <20260223210748.1905502-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 ; Mon, 23 Feb 2026 21:08:14 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231692 From: Adrian Freihofer Refactor: move kernel module environment setup from do_devshell to kernel_module.py Extract the kernel module environment variable setup from do_devshell into oe.kernel_module.kernel_module_os_env(). This enables code reuse for future features such as devtool ide-sdk. Note: it would also be possible to e.g. bb.utils.py. But when every such a widely used utility function gets changed, bitbake needs to recompile a lot of code. Therefore it's probably better to put it into a separate file. It also is a very specific function, so oe.kernel_module seems to be a good place. Signed-off-by: Adrian Freihofer --- meta/classes-recipe/module.bbclass | 21 ++------------------- meta/lib/oe/kernel_module.py | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 19 deletions(-) create mode 100644 meta/lib/oe/kernel_module.py diff --git a/meta/classes-recipe/module.bbclass b/meta/classes-recipe/module.bbclass index 4948e995c5..ca2dcba43e 100644 --- a/meta/classes-recipe/module.bbclass +++ b/meta/classes-recipe/module.bbclass @@ -25,25 +25,8 @@ python do_package:prepend () { } python do_devshell:prepend () { - os.environ['CFLAGS'] = '' - os.environ['CPPFLAGS'] = '' - os.environ['CXXFLAGS'] = '' - os.environ['LDFLAGS'] = '' - - os.environ['KERNEL_PATH'] = d.getVar('STAGING_KERNEL_DIR') - os.environ['KERNEL_SRC'] = d.getVar('STAGING_KERNEL_DIR') - os.environ['KERNEL_VERSION'] = d.getVar('KERNEL_VERSION') - os.environ['CC'] = d.getVar('KERNEL_CC') - os.environ['LD'] = d.getVar('KERNEL_LD') - os.environ['AR'] = d.getVar('KERNEL_AR') - os.environ['OBJCOPY'] = d.getVar('KERNEL_OBJCOPY') - os.environ['STRIP'] = d.getVar('KERNEL_STRIP') - os.environ['O'] = d.getVar('STAGING_KERNEL_BUILDDIR') - kbuild_extra_symbols = d.getVar('KBUILD_EXTRA_SYMBOLS') - if kbuild_extra_symbols: - os.environ['KBUILD_EXTRA_SYMBOLS'] = kbuild_extra_symbols - else: - os.environ['KBUILD_EXTRA_SYMBOLS'] = '' + import oe.kernel_module + oe.kernel_module.kernel_module_os_env(d, os.environ) } module_do_compile() { diff --git a/meta/lib/oe/kernel_module.py b/meta/lib/oe/kernel_module.py new file mode 100644 index 0000000000..678f7de03b --- /dev/null +++ b/meta/lib/oe/kernel_module.py @@ -0,0 +1,22 @@ + +# Set up the environment for building kernel modules +def kernel_module_os_env(d, env_dict): + env_dict['CFLAGS'] = '' + env_dict['CPPFLAGS'] = '' + env_dict['CXXFLAGS'] = '' + env_dict['LDFLAGS'] = '' + + env_dict['KERNEL_PATH'] = d.getVar('STAGING_KERNEL_DIR') + env_dict['KERNEL_SRC'] = d.getVar('STAGING_KERNEL_DIR') + env_dict['KERNEL_VERSION'] = d.getVar('KERNEL_VERSION') + env_dict['CC'] = d.getVar('KERNEL_CC') + env_dict['LD'] = d.getVar('KERNEL_LD') + env_dict['AR'] = d.getVar('KERNEL_AR') + env_dict['OBJCOPY'] = d.getVar('KERNEL_OBJCOPY') + env_dict['STRIP'] = d.getVar('KERNEL_STRIP') + env_dict['O'] = d.getVar('STAGING_KERNEL_BUILDDIR') + kbuild_extra_symbols = d.getVar('KBUILD_EXTRA_SYMBOLS') + if kbuild_extra_symbols: + env_dict['KBUILD_EXTRA_SYMBOLS'] = kbuild_extra_symbols + else: + env_dict['KBUILD_EXTRA_SYMBOLS'] = '' From patchwork Mon Feb 23 21:06:38 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 81648 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 C9DF6EEC280 for ; Mon, 23 Feb 2026 21:08:14 +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.msgproc02-g2.5269.1771880890345232511 for ; Mon, 23 Feb 2026 13:08:11 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=gU95EMC4; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.228, mailfrom: fm-1329275-202602232108073c933d809e00020772-tnbweu@rts-flowmailer.siemens.com) Received: by mta-65-228.siemens.flowmailer.net with ESMTPSA id 202602232108073c933d809e00020772 for ; Mon, 23 Feb 2026 22:08:07 +0100 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=pRi//hZvV0Dfe3KJSLgK7jlXqFMe9XFeR3gn8CBHxXw=; b=gU95EMC4blYIDpaxFBClpC6QbTRYSHHymVHfOCNDzZV6jn3c7SwkpFknN7nQFsRh2zH36w VDnfD+U6IRcKvOtSWJGoMqz6PyHoyflbYQ6V2GTcf6zmTl+1KqG6/3bq2aPTtqrPX4JvRdZB qQuiafiei3zvuPMaC/kK94gYSFpmEXEZmrAJ6ACR9vYuo5B/vSrEi2gaTTywbulYigd3kbqH KOErpHNx+92Dp5+iLcuunf4WUCbPSIWpUdBpBboNKuAP1mzdhQy84rz56laQjmQ+5iYiiqKF OeLj5aphwyV5UTgN3d0CfJTBs+JQrMMyHJq7lVn0D3/uPMfs/r7EtmgA==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 5/7] devtool: ide-sdk: support kernel module development Date: Mon, 23 Feb 2026 22:06:38 +0100 Message-ID: <20260223210748.1905502-6-adrian.freihofer@siemens.com> In-Reply-To: <20260223210748.1905502-1-adrian.freihofer@siemens.com> References: <20260223210748.1905502-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 ; Mon, 23 Feb 2026 21:08:14 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231699 From: Adrian Freihofer This add very basic support for kernel module development with devtool ide-sdk. It exports the kernel build environment and sets up tasks for building and cleaning the module. But it does not yet support install, deploy, and debug tasks. It looks like possible to offer the same level of support as for CMake and Meson based projects, but that requires more work. Signed-off-by: Adrian Freihofer --- scripts/lib/devtool/ide_plugins/__init__.py | 7 + scripts/lib/devtool/ide_plugins/ide_code.py | 186 +++++++++++++++++--- scripts/lib/devtool/ide_sdk.py | 33 +++- 3 files changed, 199 insertions(+), 27 deletions(-) diff --git a/scripts/lib/devtool/ide_plugins/__init__.py b/scripts/lib/devtool/ide_plugins/__init__.py index 80dfc1e235..eaf88e78cd 100644 --- a/scripts/lib/devtool/ide_plugins/__init__.py +++ b/scripts/lib/devtool/ide_plugins/__init__.py @@ -19,6 +19,7 @@ class BuildTool(Enum): UNDEFINED = auto() CMAKE = auto() MESON = auto() + KERNEL_MODULE = auto() @property def is_c_ccp(self): @@ -28,6 +29,12 @@ class BuildTool(Enum): return True return False + @property + def is_c_cpp_kernel(self): + if self.is_c_ccp or self is BuildTool.KERNEL_MODULE: + return True + return False + class GdbServerModes(Enum): ONCE = auto() diff --git a/scripts/lib/devtool/ide_plugins/ide_code.py b/scripts/lib/devtool/ide_plugins/ide_code.py index c2ee9b91c6..84cf35b50f 100644 --- a/scripts/lib/devtool/ide_plugins/ide_code.py +++ b/scripts/lib/devtool/ide_plugins/ide_code.py @@ -149,6 +149,59 @@ class IdeVSCode(IdeBase): settings_dict["cmake.configureOnOpen"] = True settings_dict["cmake.sourceDirectory"] = modified_recipe.real_srctree + def __vscode_settings_kernel_module(self, settings_dict, modified_recipe): + if modified_recipe.build_tool is not BuildTool.KERNEL_MODULE: + return + + # Define kernel exclude patterns once + kernel_exclude_patterns = [ + "**/.*.cmd", + "**/.*.d", + "**/.*.S", + "**/.tmp*", + "**/*.tmp", + "**/*.o", + "**/*.a", + "**/*.builtin", + "**/*.order", + "**/*.orig", + "**/*.symvers", + "**/*.modinfo", + "**/*.map", + "*.cache/**" + ] + files_excludes_kernel = {pattern: True for pattern in kernel_exclude_patterns} + + settings_dict["files.exclude"].update(files_excludes_kernel) + settings_dict["files.watcherExclude"].update(files_excludes_kernel) + settings_dict["python.analysis.exclude"] += kernel_exclude_patterns + + # protect the kernel sources + settings_dict["files.readonlyInclude"][modified_recipe.staging_kernel_dir + '/**'] = True + + # Export the complete cross-build environment + settings_dict["terminal.integrated.env.linux"] = modified_recipe.exported_vars + + # and the make configuration + make_executable = os.path.join( + modified_recipe.recipe_sysroot_native, 'usr', 'bin', 'make') + settings_dict["makefile.configurations"] = [ + { + "name": ' '.join(modified_recipe.make_targets), + "makePath": make_executable, + "makeDirectory": modified_recipe.srctree, + "makefilePath": os.path.join(modified_recipe.srctree, "Makefile"), + "makeArgs": modified_recipe.extra_oemake + modified_recipe.make_targets + }, + { + "name": "clean", + "makePath": make_executable, + "makeDirectory": modified_recipe.srctree, + "makefilePath": os.path.join(modified_recipe.srctree, "Makefile"), + "makeArgs": modified_recipe.extra_oemake + ["clean"] + } + ] + def vscode_settings(self, modified_recipe, image_recipe): files_excludes = { "**/.git/**": True, @@ -176,35 +229,27 @@ class IdeVSCode(IdeBase): } self.__vscode_settings_cmake(settings_dict, modified_recipe) self.__vscode_settings_meson(settings_dict, modified_recipe) + self.__vscode_settings_kernel_module(settings_dict, modified_recipe) settings_file = 'settings.json' IdeBase.update_json_file( self.dot_code_dir(modified_recipe), settings_file, settings_dict) - def __vscode_extensions_cmake(self, modified_recipe, recommendations): - if modified_recipe.build_tool is not BuildTool.CMAKE: - return - recommendations += [ - "ms-vscode.cmake-tools", - "ms-vscode.cpptools", - "ms-vscode.cpptools-extension-pack", - "ms-vscode.cpptools-themes" - ] - - def __vscode_extensions_meson(self, modified_recipe, recommendations): - if modified_recipe.build_tool is not BuildTool.MESON: - return - recommendations += [ - 'mesonbuild.mesonbuild', - "ms-vscode.cpptools", - "ms-vscode.cpptools-extension-pack", - "ms-vscode.cpptools-themes" - ] - def vscode_extensions(self, modified_recipe): recommendations = [] - self.__vscode_extensions_cmake(modified_recipe, recommendations) - self.__vscode_extensions_meson(modified_recipe, recommendations) + if modified_recipe.build_tool.is_c_cpp_kernel: + recommendations += [ + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.cpptools-themes" + ] + if modified_recipe.build_tool is BuildTool.CMAKE: + recommendations.append("ms-vscode.cmake-tools") + if modified_recipe.build_tool is BuildTool.MESON: + recommendations.append("mesonbuild.mesonbuild") + if modified_recipe.build_tool is BuildTool.KERNEL_MODULE: + recommendations.append("ms-vscode.makefile-tools") + extensions_file = 'extensions.json' IdeBase.update_json_file( self.dot_code_dir(modified_recipe), extensions_file, {"recommendations": recommendations}) @@ -218,6 +263,15 @@ class IdeVSCode(IdeBase): elif modified_recipe.build_tool is BuildTool.MESON: properties_dict["configurationProvider"] = "mesonbuild.mesonbuild" properties_dict["compilerPath"] = os.path.join(modified_recipe.staging_bindir_toolchain, modified_recipe.cxx.split()[0]) + elif modified_recipe.build_tool is BuildTool.KERNEL_MODULE: + # Using e.g. configurationProvider = "ms-vscode.makefile-tools" was not successful + properties_dict["compilerPath"] = os.path.join(modified_recipe.staging_bindir_toolchain, modified_recipe.kernel_cc.split()[0]) + properties_dict["includePath"] = [ + "${workspaceFolder}/**", + os.path.join(modified_recipe.staging_kernel_dir, "include", "**") + ] + # https://www.kernel.org/doc/html/next/process/programming-language.html + properties_dict["cStandard"] = "gnu11" else: # no C/C++ build return @@ -314,8 +368,15 @@ class IdeVSCode(IdeBase): return launch_config - def vscode_launch(self, modified_recipe): - """GDB Launch configuration for binaries (elf files)""" + def vscode_launch(self, args, modified_recipe): + """GDB launch configurations for user-space binaries. + + Kernel modules are not debugged via gdbserver and have no launch entry. + Their deployment workflow is driven entirely by tasks.json: + install && deploy-target -> reload module (rmmod + insmod) -> verify module + These tasks can be triggered from the Tasks menu (Ctrl+Shift+P > Run Task) + or via the default build task (Ctrl+Shift+B for install && deploy-target). + """ configurations = [] for gdb_cross_config in self.gdb_cross_configs: @@ -411,6 +472,79 @@ class IdeVSCode(IdeBase): IdeBase.update_json_file( self.dot_code_dir(modified_recipe), tasks_file, tasks_dict) + @staticmethod + def _ssh_args(target_device, remote_cmd): + """Build a VS Code task 'args' list for an ssh command to the target.""" + args = list(target_device.extraoptions) + if target_device.ssh_port: + args += list(target_device.ssh_port) + args += [target_device.target, remote_cmd] + return args + + def vscode_tasks_kernel_module(self, args, modified_recipe): + """Generate tasks.json for kernel module recipes. + + Three tasks are generated and chained in sequence: + 1. install && deploy-target - run bitbake do_install and push the + freshly built .ko to the target via devtool deploy-target. + 2. reload module - single SSH call: rmmod (errors ignored) then insmod + using the path reported by find so depmod is not required. + 3. verify module - SSH call: lsmod | grep to confirm the new + module is loaded; output is visible in the task terminal. + + The tasks are linked via dependsOn / dependsOrder: sequence so that + running the verify task automatically executes the full chain. The + launch.json 'reload kernel module' entry uses preLaunchTask: verify, + providing a single F5 / click action for the complete reload cycle. + """ + td = modified_recipe.gdb_cross.target_device + ko_name = modified_recipe.bpn + '.ko' + # rmmod / lsmod use the kernel module name (- replaced by _ per kernel convention) + mod_name = modified_recipe.bpn.replace('-', '_') + install_task_name = "install && deploy-target %s" % modified_recipe.recipe_id_pretty + reload_task_name = "reload module %s" % modified_recipe.recipe_id_pretty + verify_task_name = "verify module %s" % modified_recipe.recipe_id_pretty + run_install_deploy = modified_recipe.gen_install_deploy_script(args) + tasks_dict = { + "version": "2.0.0", + "tasks": [ + { + "label": install_task_name, + "type": "shell", + "command": run_install_deploy, + "args": [ + "--target", + args.target + ], + "problemMatcher": [] + }, + { + "label": reload_task_name, + "type": "shell", + "command": td.ssh_sshexec, + "args": self._ssh_args( + td, + "rmmod %(mod)s 2>/dev/null; insmod $(find /lib/modules -name '%(ko)s' | head -n 1)" + % {"mod": mod_name, "ko": ko_name}), + "dependsOn": [install_task_name], + "dependsOrder": "sequence", + "problemMatcher": [] + }, + { + "label": verify_task_name, + "type": "shell", + "command": td.ssh_sshexec, + "args": self._ssh_args(td, "lsmod | grep %s" % mod_name), + "dependsOn": [reload_task_name], + "dependsOrder": "sequence", + "problemMatcher": [] + } + ] + } + tasks_file = 'tasks.json' + IdeBase.update_json_file( + self.dot_code_dir(modified_recipe), tasks_file, tasks_dict) + def vscode_tasks_fallback(self, args, modified_recipe): oe_init_dir = modified_recipe.oe_init_dir oe_init = ". %s %s > /dev/null && " % (modified_recipe.oe_init_build_env, modified_recipe.topdir) @@ -533,6 +667,8 @@ class IdeVSCode(IdeBase): def vscode_tasks(self, args, modified_recipe): if modified_recipe.build_tool.is_c_ccp: self.vscode_tasks_cpp(args, modified_recipe) + elif modified_recipe.build_tool == BuildTool.KERNEL_MODULE: + self.vscode_tasks_kernel_module(args, modified_recipe) else: self.vscode_tasks_fallback(args, modified_recipe) @@ -543,7 +679,7 @@ class IdeVSCode(IdeBase): if args.target: self.initialize_gdb_cross_configs( image_recipe, modified_recipe, GdbCrossConfigVSCode) - self.vscode_launch(modified_recipe) + self.vscode_launch(args, modified_recipe) self.vscode_tasks(args, modified_recipe) diff --git a/scripts/lib/devtool/ide_sdk.py b/scripts/lib/devtool/ide_sdk.py index d6cda4be9d..9bccd76f0c 100755 --- a/scripts/lib/devtool/ide_sdk.py +++ b/scripts/lib/devtool/ide_sdk.py @@ -24,6 +24,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.ide_plugins import BuildTool +from oe.kernel_module import kernel_module_os_env logger = logging.getLogger('devtool') @@ -441,6 +442,11 @@ class RecipeModified: self.mesonopts = None self.extra_oemeson = None self.meson_cross_file = None + # kernel module + self.make_targets = None + self.extra_oemake = None + self.kernel_cc = None + self.staging_kernel_dir = None # Populated after bitbake built all the recipes self._installed_binaries = None @@ -514,9 +520,32 @@ class RecipeModified: self.extra_oemeson = recipe_d.getVar('EXTRA_OEMESON') self.meson_cross_file = recipe_d.getVar('MESON_CROSS_FILE') self.build_tool = BuildTool.MESON + elif bb.data.inherits_class('module', recipe_d): + self.build_tool = BuildTool.KERNEL_MODULE + self.wants_gdbserver = False + self.wants_debug_build = False + make_targets = recipe_d.getVar('MAKE_TARGETS') + if make_targets: + self.make_targets = shlex.split(make_targets) + else: + self.make_targets = ["all"] + extra_oemake = recipe_d.getVar('EXTRA_OEMAKE') + if extra_oemake: + self.extra_oemake = shlex.split(extra_oemake) + else: + self.extra_oemake = [] + self.kernel_cc = recipe_d.getVar('KERNEL_CC') + self.staging_kernel_dir = recipe_d.getVar('STAGING_KERNEL_DIR') + # Export up the environment for building kernel modules + kernel_module_os_env(recipe_d, self.exported_vars) - self.reverse_debug_prefix_map = self._init_reverse_debug_prefix_map( - recipe_d.getVar('DEBUG_PREFIX_MAP')) + # For the kernel the KERNEL_CC variable contains the prefix-map arguments + if self.build_tool is BuildTool.KERNEL_MODULE: + self.reverse_debug_prefix_map = self._init_reverse_debug_prefix_map( + self.kernel_cc) + else: + self.reverse_debug_prefix_map = self._init_reverse_debug_prefix_map( + recipe_d.getVar('DEBUG_PREFIX_MAP')) # Recipe ID is the identifier for IDE config sections self.recipe_id = self.bpn + "-" + self.package_arch From patchwork Mon Feb 23 21:06:39 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 81644 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 AE95CEC112B for ; Mon, 23 Feb 2026 21:08:14 +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.5272.1771880890345762746 for ; Mon, 23 Feb 2026 13:08:11 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=Wx+ClTrq; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.225, mailfrom: fm-1329275-20260223210807a14af387970002079b-qtr4k1@rts-flowmailer.siemens.com) Received: by mta-65-225.siemens.flowmailer.net with ESMTPSA id 20260223210807a14af387970002079b for ; Mon, 23 Feb 2026 22:08:08 +0100 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=nBRvOTm+8GW0PCCmEmkK6+Ak1lvgiok7CAoYlvaMRfk=; b=Wx+ClTrqdxozITQmXVtRpmikJpQ3Q1XmhA/zFevCNUkj1ocE5hYMUxIxcPJlIRz+E0in9l sNydSw0fzz2gl/De2oyFseCcb8rX+N+8iHa5WH6F9qaEzHtDwys2opp+zMM2TpzqOzBUM2H6 qamFACiAWP95Bxv2FWs4IeXui0/CRdPsIx++XamObM8hkOcyVX9yGlRY8Uz9dVpClBKSlZQP oHybhgyIA5YDshDLcWTF5LuRv9ISG8yCJcsd0Wfel8R6CF7wCnZ+hme6oaTH54vIXjskfyco oeMii4vSUvd33OYC/D/seM7cHTlPcc7BbwUFJyql+UL8VrpN4awUJ+QQ==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 6/7] meta-selftest: add test kernel module recipe Date: Mon, 23 Feb 2026 22:06:39 +0100 Message-ID: <20260223210748.1905502-7-adrian.freihofer@siemens.com> In-Reply-To: <20260223210748.1905502-1-adrian.freihofer@siemens.com> References: <20260223210748.1905502-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 ; Mon, 23 Feb 2026 21:08:14 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231696 From: Adrian Freihofer Add a minimal out-of-tree kernel module and corresponding recipe under meta-selftest for devtool ide-sdk test coverage. This provides a simple module source and Makefile that can be built via the module class in selftest scenarios. Signed-off-by: Adrian Freihofer --- .../selftest-kmodule/files/Makefile | 16 ++++++ .../selftest-kmodule/files/selftest-kmodule.c | 56 +++++++++++++++++++ .../selftest-kmodule/selftest-kmodule.bb | 18 ++++++ 3 files changed, 90 insertions(+) create mode 100644 meta-selftest/recipes-test/selftest-kmodule/files/Makefile create mode 100644 meta-selftest/recipes-test/selftest-kmodule/files/selftest-kmodule.c create mode 100644 meta-selftest/recipes-test/selftest-kmodule/selftest-kmodule.bb diff --git a/meta-selftest/recipes-test/selftest-kmodule/files/Makefile b/meta-selftest/recipes-test/selftest-kmodule/files/Makefile new file mode 100644 index 0000000000..9a26462bfc --- /dev/null +++ b/meta-selftest/recipes-test/selftest-kmodule/files/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: MIT + +obj-m := selftest-kmodule.o + +SRC := $(shell pwd) + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(SRC) + +modules_install: + $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install + +clean: + rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c + rm -f Module.markers Module.symvers modules.order + rm -rf .tmp_versions Modules.symvers diff --git a/meta-selftest/recipes-test/selftest-kmodule/files/selftest-kmodule.c b/meta-selftest/recipes-test/selftest-kmodule/files/selftest-kmodule.c new file mode 100644 index 0000000000..a72e0a9786 --- /dev/null +++ b/meta-selftest/recipes-test/selftest-kmodule/files/selftest-kmodule.c @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * A simple test kernel module with sysfs interface for devtool ide-sdk testing + * + * Usage: + * cat /sys/kernel/selftest_kmodule/magic + * Hello from selftest-kmodule + */ + +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("OpenEmbedded Contributors"); +MODULE_DESCRIPTION("A simple test kernel module with sysfs interface for devtool ide-sdk testing"); + +/* Change this string to verify the modify/rebuild/redeploy workflow */ +#define SELFTEST_MAGIC_STRING "Hello from selftest-kmodule" + +static struct kobject *selftest_kobj; + +static ssize_t magic_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%s\n", SELFTEST_MAGIC_STRING); +} + +static struct kobj_attribute magic_attr = __ATTR_RO(magic); + +static int __init selftest_kmodule_init(void) +{ + int ret; + + selftest_kobj = kobject_create_and_add("selftest_kmodule", kernel_kobj); + if (!selftest_kobj) + return -ENOMEM; + + ret = sysfs_create_file(selftest_kobj, &magic_attr.attr); + if (ret) + kobject_put(selftest_kobj); + + pr_info("selftest-kmodule: loaded\n"); + return ret; +} + +static void __exit selftest_kmodule_exit(void) +{ + sysfs_remove_file(selftest_kobj, &magic_attr.attr); + kobject_put(selftest_kobj); + pr_info("selftest-kmodule: unloaded\n"); +} + +module_init(selftest_kmodule_init); +module_exit(selftest_kmodule_exit); diff --git a/meta-selftest/recipes-test/selftest-kmodule/selftest-kmodule.bb b/meta-selftest/recipes-test/selftest-kmodule/selftest-kmodule.bb new file mode 100644 index 0000000000..5b3f00c5a0 --- /dev/null +++ b/meta-selftest/recipes-test/selftest-kmodule/selftest-kmodule.bb @@ -0,0 +1,18 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +SUMMARY = "A simple kernel module for testing devtool ide-sdk" +LICENSE = "GPL-2.0-only" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6" + +inherit module + +SRC_URI = "\ + file://selftest-kmodule.c \ + file://Makefile \ +" + +S = "${UNPACKDIR}" From patchwork Mon Feb 23 21:06:40 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 81649 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 D73D1EEC282 for ; Mon, 23 Feb 2026 21:08:14 +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.5271.1771880890345474373 for ; Mon, 23 Feb 2026 13:08:11 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=DqvRXwpN; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.226, mailfrom: fm-1329275-202602232108081e72966ef000020792-nu2_kw@rts-flowmailer.siemens.com) Received: by mta-64-226.siemens.flowmailer.net with ESMTPSA id 202602232108081e72966ef000020792 for ; Mon, 23 Feb 2026 22:08:08 +0100 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=xjsDxSVfdkUJ6ZYmsUOeQjyVwNGDZIGBmJWXJgi55gM=; b=DqvRXwpNxNhFeuOmlpLHyOexmgEfcgBdTJS6Wndn2qgeL7r1tkxu97Pe5oQD9g5La3DNV7 VkWDyKs2o99nYzuckkafoz33jR6Et84vEVnA3HEObn/nQPhYxNSQpESYI1QzEmhsjHgKiz05 R40y9a2jaJhHCehzFbj/ix+qTK0C1cR21GTf6Z+SmkOXB5fH3R6OOR8x0WEsBxMJK1AK1J/5 SZ5H6HuHiKg6rDIJ0HYwUJ/XWDRJQzoRfjRa+wUCsgSTJf5w/9Cbza5MSq+fG86yXBdnbCQC AOC+2DZhCtCbN2kZI0vLkqGorW14NV2QbX95gyzvD9VYTPXccRcOQ6tg==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 7/7] oe-selftest: devtool: add ide-sdk test for kernel modules Date: Mon, 23 Feb 2026 22:06:40 +0100 Message-ID: <20260223210748.1905502-8-adrian.freihofer@siemens.com> In-Reply-To: <20260223210748.1905502-1-adrian.freihofer@siemens.com> References: <20260223210748.1905502-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 ; Mon, 23 Feb 2026 21:08:14 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231698 From: Adrian Freihofer Add a new selftest that validates `devtool ide-sdk --ide=code` output for a kernel module recipe. The test verifies: - generated makefile build/clean configurations - read-only kernel source mapping - exported cross-build terminal environment variables - kernel-specific file exclude patterns - extension recommendations for makefile/cpptools - `gnu11` and kernel include paths in c_cpp_properties.json Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 215 ++++++++++++++++++++ scripts/lib/devtool/ide_plugins/ide_code.py | 13 +- 2 files changed, 223 insertions(+), 5 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index 6e6bada147..8f5180d997 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -3305,6 +3305,221 @@ class DevtoolIdeSdkTests(DevtoolBase): self._verify_install_script_code(tempdir, recipe_name) self._gdb_cross() + @OETestTag("runqemu") + def test_devtool_ide_sdk_code_kernel_module(self): + """Verify a kernel module recipe works with ide=code mode + + Test flow: + 1. devtool modify — extract sources into a temporary directory + 2. devtool ide-sdk (no -t) — generate VSCode config files with the + default target; verify settings.json, extensions.json, + c_cpp_properties.json, and the install && deploy-target task + 3. Boot Qemu, then re-run devtool ide-sdk with -t and --skip-bitbake + to update the deploy scripts with the real target address + 4. Deploy the .ko and load it with insmod; read the initial magic + string from the sysfs attribute exposed by the module + 5. Modify the magic string in the source tree, rebuild with the make + command and environment taken from settings.json (mirroring what + VSCode's Makefile Tools extension would invoke), and redeploy + 6. Reload the module and verify the updated string appears in sysfs + """ + recipe_name = "selftest-kmodule" + build_file = "Makefile" + testimage = "oe-selftest-image" + + self._check_workspace() + self._check_runqemu_prerequisites() + + # Setup source tree with devtool modify + tempdir = tempfile.mkdtemp(prefix='devtoolqa') + self.track_for_cleanup(tempdir) + self.add_command_to_tearDown('bitbake -c clean %s' % recipe_name) + result = runCmd('devtool modify %s -x %s' % (recipe_name, tempdir), + output_log=self._cmd_logger) + self.assertExists(os.path.join(tempdir, build_file), + 'Extracted source could not be found') + self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), + 'Workspace directory not created') + matches = glob.glob(os.path.join( + self.workspacedir, 'appends', recipe_name + '.bbappend')) + self.assertTrue(matches, 'bbappend not created %s' % result.output) + + # Test devtool status + result = runCmd('devtool status', output_log=self._cmd_logger) + self.assertIn(recipe_name, result.output) + self.assertIn(tempdir, result.output) + + # Generate VSCode configuration with the default target address; this step + # does not require Qemu to be running and produces the settings/tasks files + # that we verify first before booting the image. + bitbake_sdk_cmd = 'devtool ide-sdk %s %s -c --ide=code' % (recipe_name, testimage) + runCmd(bitbake_sdk_cmd, output_log=self._cmd_logger) + + # Verify the install && deploy-target script and tasks.json entry exist + self._verify_install_script_code(tempdir, recipe_name) + + # --- Verify settings.json --- + with open(os.path.join(tempdir, '.vscode', 'settings.json')) as settings_j: + settings_d = json.load(settings_j) + + # Verify make configurations are generated (build + clean) + make_configs = settings_d.get('makefile.configurations', []) + self.assertTrue(len(make_configs) >= 2, + 'makefile.configurations should have at least two entries (build + clean)') + build_config = next((c for c in make_configs if c.get('name') != 'clean'), None) + self.assertIsNotNone(build_config, + 'A build configuration should be present in makefile.configurations') + clean_config = next((c for c in make_configs if c.get('name') == 'clean'), None) + self.assertIsNotNone(clean_config, + 'A clean configuration should be present in makefile.configurations') + + # Verify make executable is set and exists + make_exe = build_config.get('makePath', '') + self.assertTrue(make_exe.endswith('/make'), + 'makePath should point to a make binary: %s' % make_exe) + self.assertExists(make_exe) + + # Verify that the Makefile path points inside the source tree + self.assertEqual(build_config.get('makeDirectory'), tempdir, + 'makeDirectory should be the source tree') + self.assertEqual(build_config.get('makefilePath'), + os.path.join(tempdir, 'Makefile'), + 'makefilePath should point to the Makefile in the source tree') + + # Verify kernel sources are set read-only + readonly_includes = settings_d.get('files.readonlyInclude', {}) + self.assertTrue( + any(k for k in readonly_includes if 'staging_kernel' in k.lower() or 'linux' in k.lower()), + 'Kernel staging dir should be set read-only in files.readonlyInclude: %s' % readonly_includes) + + # Verify the cross-build environment is exported for the terminal + self.assertIn('terminal.integrated.env.linux', settings_d, + 'terminal.integrated.env.linux should be set for kernel modules') + terminal_env = settings_d['terminal.integrated.env.linux'] + self.assertIn('KERNEL_SRC', terminal_env, + 'KERNEL_SRC should be in the exported terminal environment') + self.assertIn('KERNEL_VERSION', terminal_env, + 'KERNEL_VERSION should be in the exported terminal environment') + self.assertIn('CC', terminal_env, + 'CC (kernel compiler) should be in the exported terminal environment') + + # Verify kernel-specific file exclude patterns are present + files_exclude = settings_d.get('files.exclude', {}) + self.assertIn('**/.*.cmd', files_exclude, + 'Kernel build artifacts (.*.cmd) should be excluded from view') + self.assertIn('**/*.o', files_exclude, + 'Kernel build artifacts (*.o) should be excluded from view') + + # --- Verify extensions.json --- + with open(os.path.join(tempdir, '.vscode', 'extensions.json')) as ext_j: + ext_d = json.load(ext_j) + recommendations = ext_d.get('recommendations', []) + self.assertIn('ms-vscode.makefile-tools', recommendations, + 'ms-vscode.makefile-tools should be recommended for kernel modules') + self.assertIn('ms-vscode.cpptools', recommendations, + 'ms-vscode.cpptools should be recommended for kernel modules') + # cmake-tools and mesonbuild should not be recommended for kernel modules + self.assertNotIn('ms-vscode.cmake-tools', recommendations, + 'ms-vscode.cmake-tools should not be recommended for kernel modules') + self.assertNotIn('mesonbuild.mesonbuild', recommendations, + 'mesonbuild.mesonbuild should not be recommended for kernel modules') + + # --- Verify c_cpp_properties.json --- + with open(os.path.join(tempdir, '.vscode', 'c_cpp_properties.json')) as props_j: + props_d = json.load(props_j) + configurations = props_d.get('configurations', []) + self.assertTrue(len(configurations) > 0, + 'c_cpp_properties.json should have at least one configuration') + # Kernel modules use gnu11 as the C standard + self.assertEqual(configurations[0].get('cStandard'), 'gnu11', + 'Kernel modules should use gnu11 C standard in c_cpp_properties.json') + # Kernel include paths should be present + include_path = configurations[0].get('includePath', []) + self.assertTrue( + any('kernel' in p.lower() for p in include_path), + 'Kernel include path should be present in c_cpp_properties.json: %s' % include_path) + + # Build the make environment and command from settings.json so the + # rebuild step below uses the exact same invocation that VSCode would + # use via the Makefile Tools extension. + make_args = build_config.get('makeArgs', []) + make_dir = build_config.get('makeDirectory', tempdir) + make_env = dict(os.environ) + make_env.update(terminal_env) + + recipe_id, _ = self._get_recipe_ids(recipe_name) + install_deploy_cmd = os.path.join( + self._workspace_scripts_dir(recipe_name), 'install_and_deploy_' + recipe_id) + + SYSFS_MAGIC = '/sys/kernel/selftest_kmodule/magic' + MODULE_NAME = 'selftest_kmodule' + MAGIC_STRING_ORIG = 'Hello from selftest-kmodule' + MAGIC_STRING_NEW = 'Goodbye from selftest-kmodule' + + deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') + self.add_command_to_tearDown('bitbake -c clean %s' % testimage) + self.add_command_to_tearDown('rm -f %s/%s*' % (deploy_dir_image, testimage)) + with runqemu(testimage, runqemuparams="nographic") as qemu: + # Re-run ide-sdk with the Qemu target address to update the + # install && deploy scripts; --skip-bitbake avoids a rebuild. + bitbake_sdk_cmd = ( + 'devtool ide-sdk %s %s -t root@%s -c --skip-bitbake --ide=code' % ( + recipe_name, testimage, qemu.ip)) + runCmd(bitbake_sdk_cmd, output_log=self._cmd_logger) + + # Deploy the initial .ko to the target + runCmd(install_deploy_cmd, output_log=self._cmd_logger) + + # Out-of-tree modules land in updates/ after modules_install; + # use insmod with the full path to avoid needing depmod -a. + status, output = qemu.run( + 'find /lib/modules -name "selftest-kmodule.ko" 2>/dev/null') + self.assertEqual(status, 0) + ko_path = output.strip() + self.assertTrue(ko_path.endswith('selftest-kmodule.ko'), + 'selftest-kmodule.ko not found on target: %s' % output) + + status, output = qemu.run('insmod %s' % ko_path) + self.assertEqual(status, 0, msg='insmod failed: %s' % output) + + # Verify the sysfs interface exposes the expected magic string + status, output = qemu.run('cat %s' % SYSFS_MAGIC) + self.assertEqual(status, 0, msg='reading sysfs magic failed: %s' % output) + self.assertIn(MAGIC_STRING_ORIG, output, + 'Initial magic string not found in sysfs: %s' % output) + + # Modify the magic string in the source tree + kmodule_c = os.path.join(tempdir, 'selftest-kmodule.c') + with open(kmodule_c) as f: + src = f.read() + self.assertIn(MAGIC_STRING_ORIG, src, + 'SELFTEST_MAGIC_STRING not found in source; cannot modify it') + with open(kmodule_c, 'w') as f: + f.write(src.replace(MAGIC_STRING_ORIG, MAGIC_STRING_NEW)) + + # Rebuild using the make command and environment from settings.json, + # mirroring what VSCode would invoke via the Makefile Tools extension. + runCmd([make_exe] + make_args, cwd=make_dir, env=make_env, + output_log=self._cmd_logger) + runCmd(install_deploy_cmd, output_log=self._cmd_logger) + + # Reload the updated module and verify the sysfs string changed + status, output = qemu.run('rmmod %s' % MODULE_NAME) + self.assertEqual(status, 0, msg='rmmod failed: %s' % output) + status, output = qemu.run( + 'find /lib/modules -name "selftest-kmodule.ko" 2>/dev/null') + self.assertEqual(status, 0) + ko_path = output.strip() + status, output = qemu.run('insmod %s' % ko_path) + self.assertEqual(status, 0, msg='insmod of modified module failed: %s' % output) + + status, output = qemu.run('cat %s' % SYSFS_MAGIC) + self.assertEqual(status, 0, msg='reading sysfs magic (modified) failed: %s' % output) + self.assertNotIn(MAGIC_STRING_ORIG, output, + 'Old magic string still present in sysfs after rebuild') + self.assertIn(MAGIC_STRING_NEW, output, + 'New magic string not found in sysfs after rebuild: %s' % output) + def test_devtool_ide_sdk_shared_sysroots(self): """Verify the shared sysroot SDK""" diff --git a/scripts/lib/devtool/ide_plugins/ide_code.py b/scripts/lib/devtool/ide_plugins/ide_code.py index 84cf35b50f..603d3cecf3 100644 --- a/scripts/lib/devtool/ide_plugins/ide_code.py +++ b/scripts/lib/devtool/ide_plugins/ide_code.py @@ -155,20 +155,23 @@ class IdeVSCode(IdeBase): # Define kernel exclude patterns once kernel_exclude_patterns = [ + "*.cache/**", + "*.ko", + "*.mod.c", + "*.mod", "**/.*.cmd", "**/.*.d", "**/.*.S", "**/.tmp*", - "**/*.tmp", - "**/*.o", "**/*.a", "**/*.builtin", + "**/*.map", + "**/*.modinfo", + "**/*.o", "**/*.order", "**/*.orig", "**/*.symvers", - "**/*.modinfo", - "**/*.map", - "*.cache/**" + "**/*.tmp" ] files_excludes_kernel = {pattern: True for pattern in kernel_exclude_patterns}