public inbox for openembedded-core@lists.openembedded.org
 help / color / mirror / Atom feed
* [PATCH 0/7] devtool ide-sdk: test improvements and basic kernel module development support
@ 2026-02-23 21:06 AdrianF
  2026-02-23 21:06 ` [PATCH 1/7] oe-selftest: devtool: add compile step in ide-sdk tests AdrianF
                   ` (6 more replies)
  0 siblings, 7 replies; 9+ messages in thread
From: AdrianF @ 2026-02-23 21:06 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

- Some improvements and fixes for the existing devtool ide-sdk tests
- Intial support for building and deploying kernel modules with devtool ide-sdk,
  including a new selftest-kmodule recipe and test cases

Adrian Freihofer (7):
  oe-selftest: devtool: add compile step in ide-sdk tests
  oe-selftest: devtool: improve test_devtool_ide_sdk_shared_sysroots
  devtool: ide-sdk gate gdbserver warnings on recipe need
  module.bbclass: move environment setup to kernel_module.py
  devtool: ide-sdk: support kernel module development
  meta-selftest: add test kernel module recipe
  oe-selftest: devtool: add ide-sdk test for kernel modules

 .../selftest-kmodule/files/Makefile           |  16 +
 .../selftest-kmodule/files/selftest-kmodule.c |  56 ++++
 .../selftest-kmodule/selftest-kmodule.bb      |  18 ++
 meta/classes-recipe/module.bbclass            |  21 +-
 meta/lib/oe/kernel_module.py                  |  22 ++
 meta/lib/oeqa/selftest/cases/devtool.py       | 288 +++++++++++++++++-
 scripts/lib/devtool/ide_plugins/__init__.py   |   7 +
 scripts/lib/devtool/ide_plugins/ide_code.py   | 189 ++++++++++--
 scripts/lib/devtool/ide_sdk.py                |  46 ++-
 9 files changed, 598 insertions(+), 65 deletions(-)
 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
 create mode 100644 meta/lib/oe/kernel_module.py

-- 
2.53.0



^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/7] oe-selftest: devtool: add compile step in ide-sdk tests
  2026-02-23 21:06 [PATCH 0/7] devtool ide-sdk: test improvements and basic kernel module development support AdrianF
@ 2026-02-23 21:06 ` AdrianF
  2026-02-23 21:06 ` [PATCH 2/7] oe-selftest: devtool: improve test_devtool_ide_sdk_shared_sysroots AdrianF
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: AdrianF @ 2026-02-23 21:06 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

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 <adrian.freihofer@siemens.com>
---
 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(
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 2/7] oe-selftest: devtool: improve test_devtool_ide_sdk_shared_sysroots
  2026-02-23 21:06 [PATCH 0/7] devtool ide-sdk: test improvements and basic kernel module development support AdrianF
  2026-02-23 21:06 ` [PATCH 1/7] oe-selftest: devtool: add compile step in ide-sdk tests AdrianF
@ 2026-02-23 21:06 ` AdrianF
  2026-02-23 21:06 ` [PATCH 3/7] devtool: ide-sdk gate gdbserver warnings on recipe need AdrianF
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: AdrianF @ 2026-02-23 21:06 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

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: <builddir>"
- build: "Built target"

This makes the test explicitly fail when CMake configure or build does not
complete as expected.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 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 = ""
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 3/7] devtool: ide-sdk gate gdbserver warnings on recipe need
  2026-02-23 21:06 [PATCH 0/7] devtool ide-sdk: test improvements and basic kernel module development support AdrianF
  2026-02-23 21:06 ` [PATCH 1/7] oe-selftest: devtool: add compile step in ide-sdk tests AdrianF
  2026-02-23 21:06 ` [PATCH 2/7] oe-selftest: devtool: improve test_devtool_ide_sdk_shared_sysroots AdrianF
@ 2026-02-23 21:06 ` AdrianF
  2026-02-23 21:06 ` [PATCH 4/7] module.bbclass: move environment setup to kernel_module.py AdrianF
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: AdrianF @ 2026-02-23 21:06 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

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 <adrian.freihofer@siemens.com>
---
 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. '
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 4/7] module.bbclass: move environment setup to kernel_module.py
  2026-02-23 21:06 [PATCH 0/7] devtool ide-sdk: test improvements and basic kernel module development support AdrianF
                   ` (2 preceding siblings ...)
  2026-02-23 21:06 ` [PATCH 3/7] devtool: ide-sdk gate gdbserver warnings on recipe need AdrianF
@ 2026-02-23 21:06 ` AdrianF
  2026-03-05 10:46   ` [OE-core] " Antonin Godard
  2026-02-23 21:06 ` [PATCH 5/7] devtool: ide-sdk: support kernel module development AdrianF
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 9+ messages in thread
From: AdrianF @ 2026-02-23 21:06 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

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 <adrian.freihofer@siemens.com>
---
 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'] = ''
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 5/7] devtool: ide-sdk: support kernel module development
  2026-02-23 21:06 [PATCH 0/7] devtool ide-sdk: test improvements and basic kernel module development support AdrianF
                   ` (3 preceding siblings ...)
  2026-02-23 21:06 ` [PATCH 4/7] module.bbclass: move environment setup to kernel_module.py AdrianF
@ 2026-02-23 21:06 ` AdrianF
  2026-02-23 21:06 ` [PATCH 6/7] meta-selftest: add test kernel module recipe AdrianF
  2026-02-23 21:06 ` [PATCH 7/7] oe-selftest: devtool: add ide-sdk test for kernel modules AdrianF
  6 siblings, 0 replies; 9+ messages in thread
From: AdrianF @ 2026-02-23 21:06 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

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 <adrian.freihofer@siemens.com>
---
 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 <module> 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
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 6/7] meta-selftest: add test kernel module recipe
  2026-02-23 21:06 [PATCH 0/7] devtool ide-sdk: test improvements and basic kernel module development support AdrianF
                   ` (4 preceding siblings ...)
  2026-02-23 21:06 ` [PATCH 5/7] devtool: ide-sdk: support kernel module development AdrianF
@ 2026-02-23 21:06 ` AdrianF
  2026-02-23 21:06 ` [PATCH 7/7] oe-selftest: devtool: add ide-sdk test for kernel modules AdrianF
  6 siblings, 0 replies; 9+ messages in thread
From: AdrianF @ 2026-02-23 21:06 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

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 <adrian.freihofer@siemens.com>
---
 .../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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+
+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}"
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 7/7] oe-selftest: devtool: add ide-sdk test for kernel modules
  2026-02-23 21:06 [PATCH 0/7] devtool ide-sdk: test improvements and basic kernel module development support AdrianF
                   ` (5 preceding siblings ...)
  2026-02-23 21:06 ` [PATCH 6/7] meta-selftest: add test kernel module recipe AdrianF
@ 2026-02-23 21:06 ` AdrianF
  6 siblings, 0 replies; 9+ messages in thread
From: AdrianF @ 2026-02-23 21:06 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

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 <adrian.freihofer@siemens.com>
---
 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}
 
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [OE-core] [PATCH 4/7] module.bbclass: move environment setup to kernel_module.py
  2026-02-23 21:06 ` [PATCH 4/7] module.bbclass: move environment setup to kernel_module.py AdrianF
@ 2026-03-05 10:46   ` Antonin Godard
  0 siblings, 0 replies; 9+ messages in thread
From: Antonin Godard @ 2026-03-05 10:46 UTC (permalink / raw)
  To: adrian.freihofer, openembedded-core

Hi,

On Mon Feb 23, 2026 at 10:06 PM CET, Adrian Freihofer via lists.openembedded.org wrote:
[...]
> 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'] = ''

This is missing an SPDX identifier, which should be something like:

#
# Copyright OpenEmbedded Contributors
#
# SPDX-License-Identifier: GPL-2.0-only
#

from looking at the other files.

During the patch review call we said it was fine to merge without it for now but
could you follow-up with a patch adding it?

Thanks,
Antonin


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-03-05 10:46 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-23 21:06 [PATCH 0/7] devtool ide-sdk: test improvements and basic kernel module development support AdrianF
2026-02-23 21:06 ` [PATCH 1/7] oe-selftest: devtool: add compile step in ide-sdk tests AdrianF
2026-02-23 21:06 ` [PATCH 2/7] oe-selftest: devtool: improve test_devtool_ide_sdk_shared_sysroots AdrianF
2026-02-23 21:06 ` [PATCH 3/7] devtool: ide-sdk gate gdbserver warnings on recipe need AdrianF
2026-02-23 21:06 ` [PATCH 4/7] module.bbclass: move environment setup to kernel_module.py AdrianF
2026-03-05 10:46   ` [OE-core] " Antonin Godard
2026-02-23 21:06 ` [PATCH 5/7] devtool: ide-sdk: support kernel module development AdrianF
2026-02-23 21:06 ` [PATCH 6/7] meta-selftest: add test kernel module recipe AdrianF
2026-02-23 21:06 ` [PATCH 7/7] oe-selftest: devtool: add ide-sdk test for kernel modules AdrianF

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox