public inbox for openembedded-core@lists.openembedded.org
 help / color / mirror / Atom feed
* [PATCH 0/9] devtool: ide-sdk clang/LLDB support and minor fixes
@ 2026-03-18 22:36 AdrianF
  2026-03-18 22:36 ` [PATCH 1/9] oe-selftest: devtool: use stat for reading user/group names in ide-sdk tests AdrianF
                   ` (8 more replies)
  0 siblings, 9 replies; 13+ messages in thread
From: AdrianF @ 2026-03-18 22:36 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

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

This series adds clang/LLDB support to devtool ide-sdk and contains
some bug fixes accumulated before the release.

Bug fixes and small improvements:

- Various selftest robustness fixes: use stat(1) instead of ls for
  user/group name lookups, switch to assertRegex, fix conf-file
  ownership with static UIDs/GIDs, add a GDB test that sets a
  breakpoint after std::vector construction.
- ide-sdk: use TOOLCHAIN instead of TCOVERRIDE to detect the active
  toolchain.
- ide-sdk debugger back-end: fix a typo (is_c_ccp -> is_c_cpp) and
  cleanly separate the debugger configuration from the IDE-specific
  launch/task file generation to simplify adding further back-ends.

Clang and LLDB support:

- ide-sdk: add LLDB (CodeLLDB) remote debugging support for the clang
  toolchain, including lldb-server target configuration, RecipeLldbNative
  host wrapper, source-map handling and VSCode launch.json generation.
- meta-selftest: refactor cmake-example and meson-example into shared
  .inc files and add clang variants (cmake-example-clang,
  meson-example-clang) to exercise the new toolchain path.
- oe-selftest: add end-to-end tests for devtool ide-sdk with the clang
  toolchain, including a full remote debugging session where the binary
  is compiled with clang and debugged with LLDB.

Adrian Freihofer (9):
  oe-selftest: devtool: use stat for reading user/group names in ide-sdk
    tests
  oe-selftest: devtool: GDB breakpoint after std::vector is constructed
  oe-selftest: devtool: use assertRegex to match test output for meson
  oe-selftest/cpp-example: fix conf file ownership with static UIDs/GIDs
  devtool: ide-sdk: use TOOLCHAIN not TCOVERRIDE
  devtool: ide-sdk debugger back-end abstraction
  devtool: ide-sdk add LLDB support for clang toolchain
  meta-selftest: refactor cpp examples into .inc files and add clang
    variants
  oe-selftest: devtool ide-sdk: add clang/LLDB test

 meta-selftest/files/static-group              |   2 +
 meta-selftest/files/static-passwd             |   2 +
 .../recipes-test/cpp/cmake-example-clang.bb   |  13 +
 .../recipes-test/cpp/cmake-example.bb         |  20 +-
 .../recipes-test/cpp/cmake-example.inc        |  29 ++
 .../recipes-test/cpp/cmake-example/run-ptest  |  10 -
 .../recipes-test/cpp/cpp-example.inc          |  26 +-
 .../recipes-test/cpp/files/CMakeLists.txt     |  35 +--
 .../recipes-test/cpp/files/meson.build        |  17 +-
 .../recipes-test/cpp/files/meson.options      |   6 +
 .../cpp/{meson-example => files}/run-ptest    |   4 +-
 .../recipes-test/cpp/meson-example-clang.bb   |  13 +
 .../recipes-test/cpp/meson-example.bb         |  24 +-
 .../recipes-test/cpp/meson-example.inc        |  36 +++
 meta/lib/oeqa/selftest/cases/devtool.py       | 229 +++++++++++++++-
 scripts/lib/devtool/ide_plugins/__init__.py   | 258 ++++++++++++------
 scripts/lib/devtool/ide_plugins/ide_code.py   | 146 ++++++++--
 scripts/lib/devtool/ide_plugins/ide_none.py   |  26 +-
 scripts/lib/devtool/ide_sdk.py                |  86 +++++-
 19 files changed, 754 insertions(+), 228 deletions(-)
 create mode 100644 meta-selftest/recipes-test/cpp/cmake-example-clang.bb
 create mode 100644 meta-selftest/recipes-test/cpp/cmake-example.inc
 delete mode 100644 meta-selftest/recipes-test/cpp/cmake-example/run-ptest
 rename meta-selftest/recipes-test/cpp/{meson-example => files}/run-ptest (51%)
 create mode 100644 meta-selftest/recipes-test/cpp/meson-example-clang.bb
 create mode 100644 meta-selftest/recipes-test/cpp/meson-example.inc

-- 
2.53.0



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

* [PATCH 1/9] oe-selftest: devtool: use stat for reading user/group names in ide-sdk tests
  2026-03-18 22:36 [PATCH 0/9] devtool: ide-sdk clang/LLDB support and minor fixes AdrianF
@ 2026-03-18 22:36 ` AdrianF
  2026-03-18 22:36 ` [PATCH 2/9] oe-selftest: devtool: GDB breakpoint after std::vector is constructed AdrianF
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: AdrianF @ 2026-03-18 22:36 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

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

On some systems, ls truncates long user and group names, which causes the
ownership check to fail. For example:

AssertionError: Regex didn't match:
  '^-.+ cmake-example cmake-example .+ /etc/cmake\\-example\\.conf$' not found in
  '-rw-r--r--    1 cmake-ex cmake-ex        83 Mar  9  2018 /etc/cmake-example.conf'

Use "stat -c '%U %G'" instead, which always returns the full user and group
names regardless of terminal width or system configuration.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>

  # Please enter the commit message for your changes. Lines starting
  # with '#' will be ignored, and an empty message aborts the commit.
  #
  # Date:      Mon Mar 2 23:32:13 2026 +0100
  #
  # interactive rebase in progress; onto c1fb515f2a
  # Last commands done (3 commands done):
  #    pick 08cf9a6fc0 # Revert "devtool: ide-sdk deploy-target without bitbake"
  #    reword 2ab466cf23 # oe-selftest: devtool: use stat for reading user names in ide-sdk tests
  # Next commands to do (3 remaining commands):
  #    reword 81562bd21a # oe-selftest/cpp-example: fix conf file ownership with static UIDs/GIDs
  #    reword 1f6fd56a04 # oe-selftest: devtool: GDB breakpoint after std::vector is constructed
  # You are currently editing a commit while rebasing branch 'adrianf/ide-sdk-improvements' on 'c1fb515f2a'.
  #
  # Changes to be committed:
  #	modified:   meta/lib/oeqa/selftest/cases/devtool.py
  #
  # ------------------------ >8 ------------------------
  # Do not modify or remove the line above.
  # Everything below it will be ignored.
  diff --git c/meta/lib/oeqa/selftest/cases/devtool.py i/meta/lib/oeqa/selftest/cases/devtool.py
  index 5f25c4803b..9d8ffcc786 100644
  --- c/meta/lib/oeqa/selftest/cases/devtool.py
  +++ i/meta/lib/oeqa/selftest/cases/devtool.py
  @@ -2934,11 +2934,14 @@ class DevtoolIdeSdkTests(DevtoolBase):

       def _verify_conf_file(self, qemu, conf_file, owner, group):
           """Helper to verify a configuration file is owned by the proper user and group"""
  -        ls_cmd = "ls -l %s" % conf_file
  -        status, output = qemu.run(ls_cmd)
  -        self.assertEqual(status, 0, msg="Failed to ls %s: %s" % (conf_file, output))
  -        self.assertRegex(output, rf"^-.+ {owner} {group} .+ {re.escape(conf_file)}$",
  -                         msg="%s not owned by %s:%s: %s" % (conf_file, owner, group, output))
  +        stat_cmd = "stat -c '%%U %%G' %s" % conf_file
  +        status, output = qemu.run(stat_cmd)
  +        self.assertEqual(status, 0, msg="Failed to stat %s: %s" % (conf_file, output))
  +        actual_owner, actual_group = output.strip().split()
  +        self.assertEqual(actual_owner, owner,
  +                         msg="%s not owned by user %s: got %s" % (conf_file, owner, actual_owner))
  +        self.assertEqual(actual_group, group,
  +                         msg="%s not owned by group %s: got %s" % (conf_file, group, actual_group))

       @OETestTag("runqemu")
       def test_devtool_ide_sdk_none_qemu(self):

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/lib/oeqa/selftest/cases/devtool.py | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py
index 5f25c4803b..9d8ffcc786 100644
--- a/meta/lib/oeqa/selftest/cases/devtool.py
+++ b/meta/lib/oeqa/selftest/cases/devtool.py
@@ -2934,11 +2934,14 @@ class DevtoolIdeSdkTests(DevtoolBase):
 
     def _verify_conf_file(self, qemu, conf_file, owner, group):
         """Helper to verify a configuration file is owned by the proper user and group"""
-        ls_cmd = "ls -l %s" % conf_file
-        status, output = qemu.run(ls_cmd)
-        self.assertEqual(status, 0, msg="Failed to ls %s: %s" % (conf_file, output))
-        self.assertRegex(output, rf"^-.+ {owner} {group} .+ {re.escape(conf_file)}$",
-                         msg="%s not owned by %s:%s: %s" % (conf_file, owner, group, output))
+        stat_cmd = "stat -c '%%U %%G' %s" % conf_file
+        status, output = qemu.run(stat_cmd)
+        self.assertEqual(status, 0, msg="Failed to stat %s: %s" % (conf_file, output))
+        actual_owner, actual_group = output.strip().split()
+        self.assertEqual(actual_owner, owner,
+                         msg="%s not owned by user %s: got %s" % (conf_file, owner, actual_owner))
+        self.assertEqual(actual_group, group,
+                         msg="%s not owned by group %s: got %s" % (conf_file, group, actual_group))
 
     @OETestTag("runqemu")
     def test_devtool_ide_sdk_none_qemu(self):
-- 
2.53.0



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

* [PATCH 2/9] oe-selftest: devtool: GDB breakpoint after std::vector is constructed
  2026-03-18 22:36 [PATCH 0/9] devtool: ide-sdk clang/LLDB support and minor fixes AdrianF
  2026-03-18 22:36 ` [PATCH 1/9] oe-selftest: devtool: use stat for reading user/group names in ide-sdk tests AdrianF
@ 2026-03-18 22:36 ` AdrianF
  2026-03-18 22:36 ` [PATCH 3/9] oe-selftest: devtool: use assertRegex to match test output for meson AdrianF
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: AdrianF @ 2026-03-18 22:36 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

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

Change the GDB breakpoint from line 55 to 56 in cpp-example.cpp so that
the std::vector constructor has already executed when GDB stops. This
ensures that inspecting the vector with GDB works as intended also with
older GDB versions (e.g. on scarthgap).

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/lib/oeqa/selftest/cases/devtool.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py
index 9d8ffcc786..297dda7457 100644
--- a/meta/lib/oeqa/selftest/cases/devtool.py
+++ b/meta/lib/oeqa/selftest/cases/devtool.py
@@ -2772,7 +2772,9 @@ class DevtoolIdeSdkTests(DevtoolBase):
 
         # check if resolving std::vector works with python scripts
         gdb_batch_cmd += " -ex 'list cpp-example.cpp:55,55'"
-        gdb_batch_cmd += " -ex 'break cpp-example.cpp:55'"
+        # Break on line 56 (the std::cout after the declaration) so the vector
+        # constructor on line 55 has already run when GDB stops.
+        gdb_batch_cmd += " -ex 'break cpp-example.cpp:56'"
         gdb_batch_cmd += " -ex 'continue'"
         gdb_batch_cmd += " -ex 'print numbers'"
         gdb_batch_cmd += " -ex 'continue'"
-- 
2.53.0



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

* [PATCH 3/9] oe-selftest: devtool: use assertRegex to match test output for meson
  2026-03-18 22:36 [PATCH 0/9] devtool: ide-sdk clang/LLDB support and minor fixes AdrianF
  2026-03-18 22:36 ` [PATCH 1/9] oe-selftest: devtool: use stat for reading user/group names in ide-sdk tests AdrianF
  2026-03-18 22:36 ` [PATCH 2/9] oe-selftest: devtool: GDB breakpoint after std::vector is constructed AdrianF
@ 2026-03-18 22:36 ` AdrianF
  2026-03-18 22:36 ` [PATCH 4/9] oe-selftest/cpp-example: fix conf file ownership with static UIDs/GIDs AdrianF
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: AdrianF @ 2026-03-18 22:36 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

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

Replace strict string matching with assertRegex to allow for flexible
whitespace in the "Fail: 0" output from meson tests. This improves test
robustness against formatting changes.
This issue was discovered with scarthgap.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/lib/oeqa/selftest/cases/devtool.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py
index 297dda7457..dc83d406fd 100644
--- a/meta/lib/oeqa/selftest/cases/devtool.py
+++ b/meta/lib/oeqa/selftest/cases/devtool.py
@@ -2911,7 +2911,7 @@ class DevtoolIdeSdkTests(DevtoolBase):
         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)
+        self.assertRegex(result.output, r"Fail:\s+0")
 
         # Verify re-building and testing works again
         result = runCmd('%s compile -C %s --clean' % (meson_exe, build_dir),
@@ -2922,7 +2922,7 @@ class DevtoolIdeSdkTests(DevtoolBase):
         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)
+        self.assertRegex(result.output, r"Fail:\s+0")
 
         return compile_cmd
 
-- 
2.53.0



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

* [PATCH 4/9] oe-selftest/cpp-example: fix conf file ownership with static UIDs/GIDs
  2026-03-18 22:36 [PATCH 0/9] devtool: ide-sdk clang/LLDB support and minor fixes AdrianF
                   ` (2 preceding siblings ...)
  2026-03-18 22:36 ` [PATCH 3/9] oe-selftest: devtool: use assertRegex to match test output for meson AdrianF
@ 2026-03-18 22:36 ` AdrianF
  2026-03-18 22:36 ` [PATCH 5/9] devtool: ide-sdk: use TOOLCHAIN not TCOVERRIDE AdrianF
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: AdrianF @ 2026-03-18 22:36 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

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

test_devtool_ide_sdk_none_qemu builds an image containing both
cmake-example and meson-example, starts a QEMU instance, then uses
devtool ide-sdk + devtool deploy-target to rebuild and redeploy each
recipe in turn. The test verifies that /etc/<recipe>.conf is owned by
the matching user both before and after each deploy cycle.

The test was failing with:

  /etc/meson-example.conf not owned by user meson-example: got cmake-example

Root cause: both recipes call

  install -m 0644 -o ${BPN} -g ${BPN} ... ${D}${sysconfdir}/${BPN}.conf

During do_install, pseudo resolves ${BPN} to a UID by looking up
/etc/passwd in the recipe's own isolated RECIPE_SYSROOT. Since the
sysroots are independent, both cmake-example and meson-example each
see themselves as the first --system user and get the same UID (e.g.
100). Both ${D} trees therefore contain files with UID 100. In the
final rootfs cmake-example is allocated UID 100 and meson-example UID
101. Files packaged for meson-example still carry UID 100, so stat
reports them as owned by cmake-example.

A pkg_postinst chown would fix the rootfs, but devtool deploy-target
is a plain tar pipe over SSH with no package-manager involvement - it
never runs pkg_postinst. Whatever UID is embedded in ${D} is what
lands on the target. Not sure how this could be fixed with dynamic UIDs.

A clean solution is to make every recipe sysroot and the final image
agree on the same UIDs from the start, i.e. static IDs.

Fix:
- Enable USERADDEXTENSION = "useradd-staticids" in _write_bb_config so
  the test builds with static IDs for the duration of the test.
- Add cmake-example (UID/GID 533) and meson-example (UID/GID 534) to
  meta-selftest/files/static-passwd and static-group.
- Expand the comment in cpp-example.inc's do_install to document the
  static-ID requirement so future readers understand why the -o/-g
  flags work correctly only under useradd-staticids.
- Fix a copy-paste error in the in-test comment (said
  "meson-example.conf ... cmake-example user" for the cmake block).

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta-selftest/files/static-group               |  2 ++
 meta-selftest/files/static-passwd              |  2 ++
 meta-selftest/recipes-test/cpp/cpp-example.inc |  6 +++++-
 meta/lib/oeqa/selftest/cases/devtool.py        | 12 +++++++++++-
 4 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/meta-selftest/files/static-group b/meta-selftest/files/static-group
index 3fca4aa5c9..adf436f310 100644
--- a/meta-selftest/files/static-group
+++ b/meta-selftest/files/static-group
@@ -28,4 +28,6 @@ ptest:x:529:
 xuser:x:530:
 seat:x:531:
 audio:x:532:
+cmake-example:x:533:
+meson-example:x:534:
 nogroup:x:65534:
diff --git a/meta-selftest/files/static-passwd b/meta-selftest/files/static-passwd
index cc6c5acd5c..8d9f149b9f 100644
--- a/meta-selftest/files/static-passwd
+++ b/meta-selftest/files/static-passwd
@@ -19,3 +19,5 @@ _apt:x:523:523::/:/bin/nologin
 weston:x:525:525::/:/bin/nologin
 ptest:x:529:529::/:/bin/nologin
 xuser:x:530:530::/:/bin/nologin
+cmake-example:x:533:533::/var/lib/cmake-example:/bin/false
+meson-example:x:534:534::/var/lib/meson-example:/bin/false
diff --git a/meta-selftest/recipes-test/cpp/cpp-example.inc b/meta-selftest/recipes-test/cpp/cpp-example.inc
index 2653f45e90..0671824d1c 100644
--- a/meta-selftest/recipes-test/cpp/cpp-example.inc
+++ b/meta-selftest/recipes-test/cpp/cpp-example.inc
@@ -41,7 +41,11 @@ USERADD_PARAM:${PN} = "--system --home /var/lib/${BPN} --no-create-home --shell
 EX_BINARY_NAME ?= "${BPN}"
 
 do_install:append() {
-    # Install configuration file owned by unprivileged user
+    # Install configuration file owned by the recipe's unprivileged user.
+    # Note: this requires static UIDs/GIDs (USERADDEXTENSION = "useradd-staticids")
+    # so that the UID embedded by pseudo during do_install matches the UID assigned
+    # in the final image. devtool deploy-target is a raw file copy and does not run
+    # pkg_postinst, so the UID in ${D} must already be correct.
     install -d ${D}${sysconfdir}
     install -m 0644 -g ${BPN} -o ${BPN} ${S}/cpp-example.conf ${D}${sysconfdir}/${BPN}.conf
     sed -i -e 's|@BINARY_NAME@|${BPN}|g' ${D}${sysconfdir}/${BPN}.conf
diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py
index dc83d406fd..6c6f22a667 100644
--- a/meta/lib/oeqa/selftest/cases/devtool.py
+++ b/meta/lib/oeqa/selftest/cases/devtool.py
@@ -2598,6 +2598,16 @@ class DevtoolIdeSdkTests(DevtoolBase):
             'IMAGE_INSTALL:append = " gdbserver %s"' % ' '.join(
                 [r + '-ptest' for r in recipe_names]),
             'DISTRO_FEATURES:append = " ptest"'
+            # Static UIDs/GIDs are required so that files installed via
+            # "install -o ${BPN}" in do_install embed the same UID that gets
+            # assigned in the final image. Without this, each recipe's isolated
+            # sysroot allocates UIDs independently (both start at the first free
+            # system UID), so files end up with colliding UIDs in the image.
+            # devtool deploy-target is a raw file copy and does not run
+            # pkg_postinst, so ownership must be correct already in ${D}.
+            'USERADDEXTENSION = "useradd-staticids"',
+            'USERADD_UID_TABLES += "files/static-passwd"',
+            'USERADD_GID_TABLES += "files/static-group"',
         ]
         self.write_config("\n".join(conf_lines))
 
@@ -2985,7 +2995,7 @@ class DevtoolIdeSdkTests(DevtoolBase):
             self.assertEqual(self._workspace_scripts_dir(
                 recipe_name), self._sources_scripts_dir(tempdir))
 
-            # Verify /etc/meson-example.conf is still owned by the cmake-example user
+            # Verify /etc/cmake-example.conf is still owned by the cmake-example user
             # after the install and deploy scripts updated the file
             self._verify_conf_file(qemu, conf_file, example_exe, example_exe)
 
-- 
2.53.0



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

* [PATCH 5/9] devtool: ide-sdk: use TOOLCHAIN not TCOVERRIDE
  2026-03-18 22:36 [PATCH 0/9] devtool: ide-sdk clang/LLDB support and minor fixes AdrianF
                   ` (3 preceding siblings ...)
  2026-03-18 22:36 ` [PATCH 4/9] oe-selftest/cpp-example: fix conf file ownership with static UIDs/GIDs AdrianF
@ 2026-03-18 22:36 ` AdrianF
  2026-03-18 22:36 ` [PATCH 6/9] devtool: ide-sdk debugger back-end abstraction AdrianF
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: AdrianF @ 2026-03-18 22:36 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

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

Looks like TOOLCHAIN is the correct variable to determine the toolchain
used by a recipe, not TCOVERRIDE.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 scripts/lib/devtool/ide_sdk.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/scripts/lib/devtool/ide_sdk.py b/scripts/lib/devtool/ide_sdk.py
index 9bccd76f0c..07f5552758 100755
--- a/scripts/lib/devtool/ide_sdk.py
+++ b/scripts/lib/devtool/ide_sdk.py
@@ -416,7 +416,7 @@ class RecipeModified:
         self.staging_incdir = None
         self.strip_cmd = None
         self.target_arch = None
-        self.tcoverride = None
+        self.toolchain = None
         self.topdir = None
         self.workdir = None
         # Service management
@@ -502,7 +502,7 @@ class RecipeModified:
             recipe_d.getVar('STAGING_INCDIR'))
         self.strip_cmd = recipe_d.getVar('STRIP')
         self.target_arch = recipe_d.getVar('TARGET_ARCH')
-        self.tcoverride = recipe_d.getVar('TCOVERRIDE')
+        self.toolchain = recipe_d.getVar('TOOLCHAIN')
         self.topdir = recipe_d.getVar('TOPDIR')
         self.workdir = os.path.realpath(recipe_d.getVar('WORKDIR'))
 
@@ -673,7 +673,7 @@ class RecipeModified:
     @property
     def gdb_pretty_print_scripts(self):
         if self._gdb_pretty_print_scripts is None:
-            if self.tcoverride == "toolchain-gcc":
+            if self.toolchain == "gcc":
                 gcc_python_helpers_pattern = os.path.join(self.recipe_sysroot, "usr", "share", "gcc-*", "python")
                 gcc_python_helpers_dirs = glob.glob(gcc_python_helpers_pattern)
                 if gcc_python_helpers_dirs:
-- 
2.53.0



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

* [PATCH 6/9] devtool: ide-sdk debugger back-end abstraction
  2026-03-18 22:36 [PATCH 0/9] devtool: ide-sdk clang/LLDB support and minor fixes AdrianF
                   ` (4 preceding siblings ...)
  2026-03-18 22:36 ` [PATCH 5/9] devtool: ide-sdk: use TOOLCHAIN not TCOVERRIDE AdrianF
@ 2026-03-18 22:36 ` AdrianF
  2026-03-18 22:36 ` [PATCH 7/9] devtool: ide-sdk add LLDB support for clang toolchain AdrianF
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: AdrianF @ 2026-03-18 22:36 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

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

This is a refactoring of the devtool ide-sdk support for remote
debugging with gdbserver. The main goal is to cleanly separate the
generation of the host-side debugger configuration (gdbinit, wrapper
scripts) from the IDE-specific launch/task config generation, and to
provide a common interface for supporting multiple debug server
back-ends (gdbserver, lldb-server) in the future.

Also fix a typo in the GDB configuration generator where the property
was named "is_c_ccp" instead of "is_c_cpp".

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 scripts/lib/devtool/ide_plugins/__init__.py | 195 +++++++++++---------
 scripts/lib/devtool/ide_plugins/ide_code.py |  38 ++--
 scripts/lib/devtool/ide_plugins/ide_none.py |  26 +--
 scripts/lib/devtool/ide_sdk.py              |  21 ++-
 4 files changed, 154 insertions(+), 126 deletions(-)

diff --git a/scripts/lib/devtool/ide_plugins/__init__.py b/scripts/lib/devtool/ide_plugins/__init__.py
index eaf88e78cd..8c41afc640 100644
--- a/scripts/lib/devtool/ide_plugins/__init__.py
+++ b/scripts/lib/devtool/ide_plugins/__init__.py
@@ -22,7 +22,7 @@ class BuildTool(Enum):
     KERNEL_MODULE = auto()
 
     @property
-    def is_c_ccp(self):
+    def is_c_cpp(self):
         if self is BuildTool.CMAKE:
             return True
         if self is BuildTool.MESON:
@@ -31,7 +31,7 @@ class BuildTool(Enum):
 
     @property
     def is_c_cpp_kernel(self):
-        if self.is_c_ccp or self is BuildTool.KERNEL_MODULE:
+        if self.is_c_cpp or self is BuildTool.KERNEL_MODULE:
             return True
         return False
 
@@ -42,104 +42,48 @@ class GdbServerModes(Enum):
     MULTI = auto()
 
 
-class GdbCrossConfig:
-    """Base class defining the GDB configuration generator interface
+class DebuggerCrossConfig:
+    """Base class defining the cross-debugger configuration generator interface.
 
-    Generate a GDB configuration for a binary on the target device.
+    Manages the per-binary port assignment, script paths, and SSH argument
+    construction that are common to all debugger back-ends (GDB, LLDB).
+    Concrete subclasses provide the back-end-specific remote start/kill commands.
     """
-    _gdbserver_port_next = 1234
-    _gdb_cross_configs = {}
+    _port_next = 1234
+    _configs = {}
 
-    def __init__(self, image_recipe, modified_recipe, binary, gdbserver_default_mode):
+    def __init__(self, image_recipe, modified_recipe, binary, default_mode):
         self.image_recipe = image_recipe
         self.modified_recipe = modified_recipe
         self.gdb_cross = modified_recipe.gdb_cross
         self.binary = binary
-        self.gdbserver_default_mode = gdbserver_default_mode
+        self.default_mode = default_mode
         self.binary_pretty = self.binary.binary_path.replace(os.sep, '-').lstrip('-')
-        self.gdbserver_port = GdbCrossConfig._gdbserver_port_next
-        GdbCrossConfig._gdbserver_port_next += 1
-        self.id_pretty = "%d_%s" % (self.gdbserver_port, self.binary_pretty)
+        self.port = DebuggerCrossConfig._port_next
+        DebuggerCrossConfig._port_next += 1
+        self.id_pretty = "%d_%s" % (self.port, self.binary_pretty)
 
-        # Track all generated gdbserver configs to avoid duplicates
-        if self.id_pretty in GdbCrossConfig._gdb_cross_configs:
+        if self.id_pretty in DebuggerCrossConfig._configs:
             raise DevtoolError(
-                "gdbserver config for binary %s is already generated" % binary)
-        GdbCrossConfig._gdb_cross_configs[self.id_pretty] = self
+                "debugger config for binary %s is already generated" % binary)
+        DebuggerCrossConfig._configs[self.id_pretty] = self
 
-    def id_pretty_mode(self, gdbserver_mode):
-        return "%s_%s" % (self.id_pretty, gdbserver_mode.name.lower())
+    def id_pretty_mode(self, mode):
+        return "%s_%s" % (self.id_pretty, mode.name.lower())
 
-    # GDB and gdbserver script on the host
+    # Host-side script paths
     @property
     def script_dir(self):
         return self.modified_recipe.ide_sdk_scripts_dir
 
-    @property
-    def gdbinit_dir(self):
-        return os.path.join(self.script_dir, 'gdbinit')
+    def server_script_file(self, mode):
+        return 'gdbserver_' + self.id_pretty_mode(mode)
 
-    def gdbserver_script_file(self, gdbserver_mode):
-        return 'gdbserver_' + self.id_pretty_mode(gdbserver_mode)
+    def server_script(self, mode):
+        return os.path.join(self.script_dir, self.server_script_file(mode))
 
-    def gdbserver_script(self, gdbserver_mode):
-        return os.path.join(self.script_dir, self.gdbserver_script_file(gdbserver_mode))
-
-    @property
-    def gdbinit(self):
-        return os.path.join(
-            self.gdbinit_dir, 'gdbinit_' + self.id_pretty)
-
-    @property
-    def gdb_script(self):
-        return os.path.join(
-            self.script_dir, 'gdb_' + self.id_pretty)
-
-    # gdbserver files on the target
-    def gdbserver_tmp_dir(self, gdbserver_mode):
-        return os.path.join('/tmp', 'gdbserver_%s' % self.id_pretty_mode(gdbserver_mode))
-
-    def gdbserver_pid_file(self, gdbserver_mode):
-        return os.path.join(self.gdbserver_tmp_dir(gdbserver_mode), 'gdbserver.pid')
-
-    def gdbserver_log_file(self, gdbserver_mode):
-        return os.path.join(self.gdbserver_tmp_dir(gdbserver_mode), 'gdbserver.log')
-
-    def _target_gdbserver_start_cmd(self, gdbserver_mode):
-        """Get the ssh command to start gdbserver on the target device
-
-        returns something like:
-          "\"/bin/sh -c '/usr/bin/gdbserver --once :1234 /usr/bin/cmake-example'\""
-        or for multi mode:
-          "\"/bin/sh -c 'if [ \"$1\" = \"stop\" ]; then ... else ... fi'\""
-        """
-        if gdbserver_mode == GdbServerModes.ONCE:
-            gdbserver_cmd_start = "%s --once :%s %s" % (
-                self.gdb_cross.gdbserver_path, self.gdbserver_port, self.binary.binary_path)
-        elif gdbserver_mode == GdbServerModes.ATTACH:
-            pid_command = self.binary.pid_command
-            if pid_command:
-                gdbserver_cmd_start = "%s --attach :%s \\$(%s)" % (
-                    self.gdb_cross.gdbserver_path,
-                    self.gdbserver_port,
-                    pid_command)
-            else:
-                raise DevtoolError("Cannot use gdbserver attach mode for binary %s. No PID found." % self.binary.binary_path)
-        elif gdbserver_mode == GdbServerModes.MULTI:
-            gdbserver_cmd_start = "test -f %s && exit 0; " % self.gdbserver_pid_file(gdbserver_mode)
-            gdbserver_cmd_start += "mkdir -p %s; " % self.gdbserver_tmp_dir(gdbserver_mode)
-            gdbserver_cmd_start += "%s --multi :%s > %s 2>&1 & " % (
-                self.gdb_cross.gdbserver_path, self.gdbserver_port, self.gdbserver_log_file(gdbserver_mode))
-            gdbserver_cmd_start += "echo \\$! > %s;" % self.gdbserver_pid_file(gdbserver_mode)
-        else:
-            raise DevtoolError("Unsupported gdbserver mode: %s" % gdbserver_mode)
-        return "\"/bin/sh -c '" + gdbserver_cmd_start + "'\""
-
-    def _target_gdbserver_kill_cmd(self):
-        """Get the ssh command to kill gdbserver on the target device"""
-        return "\"kill \\$(pgrep -o -f 'gdbserver --attach :%s') 2>/dev/null || true\"" % self.gdbserver_port
-
-    def _target_ssh_gdbserver_args(self):
+    # SSH argument helpers
+    def _target_ssh_args(self):
         ssh_args = []
         if self.gdb_cross.target_device.ssh_port:
             ssh_args += ["-p", self.gdb_cross.target_device.ssh_port]
@@ -149,17 +93,94 @@ class GdbCrossConfig:
             ssh_args.append(self.gdb_cross.target_device.target)
         return ssh_args
 
-    def gdbserver_modes(self):
-        """Get the list of gdbserver modes for which scripts are generated"""
-        modes = [self.gdbserver_default_mode]
-        if self.binary.runs_as_service and self.gdbserver_default_mode != GdbServerModes.ATTACH:
+    def server_modes(self):
+        """List of debug-server modes for which scripts are generated."""
+        modes = [self.default_mode]
+        if self.binary.runs_as_service and self.default_mode != GdbServerModes.ATTACH:
             modes.append(GdbServerModes.ATTACH)
         return modes
 
     def initialize(self):
-        """Interface function to initialize the gdb config generation"""
+        """Called after construction to generate any required config files."""
         pass
 
+    # Abstract — subclasses must implement
+    def _target_start_cmd(self, mode):
+        raise NotImplementedError
+
+    def _target_kill_cmd(self):
+        raise NotImplementedError
+
+
+class GdbCrossConfig(DebuggerCrossConfig):
+    """GDB-specific cross-debugging configuration.
+
+    Manages gdbserver on the target and gdb-cross on the host.  Provides
+    gdbinit / gdb wrapper scripts used by ide=none as well as the
+    target-side tmp/pid/log paths consumed by the gdbserver start command.
+    """
+
+    def __init__(self, image_recipe, modified_recipe, binary,
+                 default_mode=GdbServerModes.MULTI):
+        super().__init__(image_recipe, modified_recipe, binary,
+                         default_mode)
+
+    # GDB-specific host paths
+    @property
+    def gdbinit_dir(self):
+        return os.path.join(self.script_dir, 'gdbinit')
+
+    @property
+    def gdbinit(self):
+        return os.path.join(self.gdbinit_dir, 'gdbinit_' + self.id_pretty)
+
+    @property
+    def gdb_script(self):
+        return os.path.join(self.script_dir, 'gdb_' + self.id_pretty)
+
+    # gdbserver files on the target
+    def _gdbserver_tmp_dir(self, mode):
+        return os.path.join('/tmp', 'gdbserver_%s' % self.id_pretty_mode(mode))
+
+    def _gdbserver_pid_file(self, mode):
+        return os.path.join(self._gdbserver_tmp_dir(mode), 'gdbserver.pid')
+
+    def _gdbserver_log_file(self, mode):
+        return os.path.join(self._gdbserver_tmp_dir(mode), 'gdbserver.log')
+
+    def _target_start_cmd(self, gdbserver_mode):
+        """SSH command to start gdbserver on the target device.
+
+        Returns something like:
+          "\"/bin/sh -c '/usr/bin/gdbserver --once :1234 /usr/bin/cmake-example'\""
+        """
+        if gdbserver_mode == GdbServerModes.ONCE:
+            gdbserver_cmd_start = "%s --once :%s %s" % (
+                self.gdb_cross.gdbserver_path, self.port, self.binary.binary_path)
+        elif gdbserver_mode == GdbServerModes.ATTACH:
+            pid_command = self.binary.pid_command
+            if pid_command:
+                gdbserver_cmd_start = "%s --attach :%s \\$(%s)" % (
+                    self.gdb_cross.gdbserver_path,
+                    self.port,
+                    pid_command)
+            else:
+                raise DevtoolError("Cannot use gdbserver attach mode for binary %s. No PID found." % self.binary.binary_path)
+        elif gdbserver_mode == GdbServerModes.MULTI:
+            gdbserver_cmd_start = "test -f %s && exit 0; " % self._gdbserver_pid_file(gdbserver_mode)
+            gdbserver_cmd_start += "mkdir -p %s; " % self._gdbserver_tmp_dir(gdbserver_mode)
+            gdbserver_cmd_start += "%s --multi :%s > %s 2>&1 & " % (
+                self.gdb_cross.gdbserver_path, self.port, self._gdbserver_log_file(gdbserver_mode))
+            gdbserver_cmd_start += "echo \\$! > %s;" % self._gdbserver_pid_file(gdbserver_mode)
+        else:
+            raise DevtoolError("Unsupported gdbserver mode: %s" % gdbserver_mode)
+        return "\"/bin/sh -c '" + gdbserver_cmd_start + "'\""
+
+    def _target_kill_cmd(self):
+        """SSH command to kill gdbserver on the target device."""
+        return "\"kill \\$(pgrep -o -f 'gdbserver --attach :%s') 2>/dev/null || true\"" % self.port
+
+
 
 
 class IdeBase:
diff --git a/scripts/lib/devtool/ide_plugins/ide_code.py b/scripts/lib/devtool/ide_plugins/ide_code.py
index 603d3cecf3..7fe5a40eb1 100644
--- a/scripts/lib/devtool/ide_plugins/ide_code.py
+++ b/scripts/lib/devtool/ide_plugins/ide_code.py
@@ -9,27 +9,27 @@ import json
 import logging
 import os
 import shutil
-from devtool.ide_plugins import BuildTool, IdeBase, GdbCrossConfig, GdbServerModes, get_devtool_deploy_opts
+from devtool.ide_plugins import BuildTool, IdeBase, DebuggerCrossConfig, GdbCrossConfig, GdbServerModes, get_devtool_deploy_opts
 
 logger = logging.getLogger('devtool')
 
 
 class GdbCrossConfigVSCode(GdbCrossConfig):
     def __init__(self, image_recipe, modified_recipe, binary,
-                 gdbserver_default_mode=GdbServerModes.ONCE):
+                 default_mode=GdbServerModes.ONCE):
         super().__init__(image_recipe, modified_recipe, binary,
-                         gdbserver_default_mode)
+                         default_mode)
 
-    def target_ssh_gdbserver_start_args(self, gdbserver_mode=None):
+    def target_ssh_gdbserver_start_args(self, mode=None):
         """Get the ssh command arguments to start gdbserver on the target device
 
         returns something like:
           ['-p', '2222', 'root@target', '"/bin/sh -c \'/usr/bin/gdbserver --once :1234 /usr/bin/cmake-example\'"']
         """
-        if gdbserver_mode is None:
-            gdbserver_mode = self.gdbserver_default_mode
-        return self._target_ssh_gdbserver_args() + [
-            self._target_gdbserver_start_cmd(gdbserver_mode)
+        if mode is None:
+            mode = self.default_mode
+        return self._target_ssh_args() + [
+            self._target_start_cmd(mode)
         ]
 
     def target_ssh_gdbserver_kill_args(self):
@@ -38,13 +38,10 @@ class GdbCrossConfigVSCode(GdbCrossConfig):
         returns something like:
           ['-p', '2222', 'root@target', '"kill $(pgrep -o -f \'gdbserver --attach :1234\') 2>/dev/null || true"']
         """
-        return self._target_ssh_gdbserver_args() + [
-            self._target_gdbserver_kill_cmd()
+        return self._target_ssh_args() + [
+            self._target_kill_cmd()
         ]
 
-    def initialize(self):
-        pass
-
 
 class IdeVSCode(IdeBase):
     """Manage IDE configurations for VSCode
@@ -289,6 +286,11 @@ class IdeVSCode(IdeBase):
             self.dot_code_dir(modified_recipe), prop_file, properties_dicts)
 
     def vscode_launch_bin_dbg(self, gdb_cross_config, gdbserver_mode):
+        """Dispatch to the GDB launch config generator."""
+        return self._vscode_launch_bin_dbg_gdb(gdb_cross_config, gdbserver_mode)
+
+    def _vscode_launch_bin_dbg_gdb(self, gdb_cross_config, gdbserver_mode):
+        """Generate a cppdbg (GDB) launch configuration entry for launch.json."""
         modified_recipe = gdb_cross_config.modified_recipe
 
         launch_config = {
@@ -303,7 +305,7 @@ class IdeVSCode(IdeBase):
             "MIMode": "gdb",
             "preLaunchTask": gdb_cross_config.id_pretty_mode(gdbserver_mode),
             "miDebuggerPath": modified_recipe.gdb_cross.gdb,
-            "miDebuggerServerAddress": "%s:%d" % (modified_recipe.gdb_cross.host, gdb_cross_config.gdbserver_port)
+            "miDebuggerServerAddress": "%s:%d" % (modified_recipe.gdb_cross.host, gdb_cross_config.port)
         }
 
         # Search for header files in recipe-sysroot.
@@ -384,7 +386,7 @@ class IdeVSCode(IdeBase):
         configurations = []
         for gdb_cross_config in self.gdb_cross_configs:
             if gdb_cross_config.modified_recipe is modified_recipe:
-                for gdbserver_mode in gdb_cross_config.gdbserver_modes():
+                for gdbserver_mode in gdb_cross_config.server_modes():
                     configurations.append(self.vscode_launch_bin_dbg(gdb_cross_config, gdbserver_mode))
         launch_dict = {
             "version": "0.2.0",
@@ -415,7 +417,7 @@ class IdeVSCode(IdeBase):
         for gdb_cross_config in self.gdb_cross_configs:
             if gdb_cross_config.modified_recipe is not modified_recipe:
                 continue
-            for gdbserver_mode in gdb_cross_config.gdbserver_modes():
+            for gdbserver_mode in gdb_cross_config.server_modes():
                 new_task = {
                     "label": gdb_cross_config.id_pretty_mode(gdbserver_mode),
                     "type": "shell",
@@ -633,7 +635,7 @@ class IdeVSCode(IdeBase):
             for gdb_cross_config in self.gdb_cross_configs:
                 if gdb_cross_config.modified_recipe is not modified_recipe:
                     continue
-                for gdbserver_mode in gdb_cross_config.gdbserver_modes():
+                for gdbserver_mode in gdb_cross_config.server_modes():
                     new_task = {
                         "label": gdb_cross_config.id_pretty(gdbserver_mode),
                         "type": "shell",
@@ -668,7 +670,7 @@ class IdeVSCode(IdeBase):
             self.dot_code_dir(modified_recipe), tasks_file, tasks_dict)
 
     def vscode_tasks(self, args, modified_recipe):
-        if modified_recipe.build_tool.is_c_ccp:
+        if modified_recipe.build_tool.is_c_cpp:
             self.vscode_tasks_cpp(args, modified_recipe)
         elif modified_recipe.build_tool == BuildTool.KERNEL_MODULE:
             self.vscode_tasks_kernel_module(args, modified_recipe)
diff --git a/scripts/lib/devtool/ide_plugins/ide_none.py b/scripts/lib/devtool/ide_plugins/ide_none.py
index ed96afa33c..781e832ee8 100644
--- a/scripts/lib/devtool/ide_plugins/ide_none.py
+++ b/scripts/lib/devtool/ide_plugins/ide_none.py
@@ -16,17 +16,17 @@ logger = logging.getLogger('devtool')
 
 class GdbCrossConfigNone(GdbCrossConfig):
     def __init__(self, image_recipe, modified_recipe, binary,
-                 gdbserver_default_mode=GdbServerModes.MULTI):
+                 default_mode=GdbServerModes.MULTI):
         super().__init__(image_recipe, modified_recipe, binary,
-                         gdbserver_default_mode)
+                         default_mode)
 
     def _target_gdbserver_stop_cmd(self, gdbserver_mode):
         """Kill a gdbserver process"""
         # This is the usual behavior: gdbserver is stopped on demand
         if gdbserver_mode == GdbServerModes.MULTI:
             gdbserver_cmd_stop = "test -f %s && kill \\$(cat %s);" % (
-                self.gdbserver_pid_file(gdbserver_mode), self.gdbserver_pid_file(gdbserver_mode))
-            gdbserver_cmd_stop += " rm -rf %s" % self.gdbserver_tmp_dir(gdbserver_mode)
+                self._gdbserver_pid_file(gdbserver_mode), self._gdbserver_pid_file(gdbserver_mode))
+            gdbserver_cmd_stop += " rm -rf %s" % self._gdbserver_tmp_dir(gdbserver_mode)
         # This is unexpected since gdbserver should terminate after each debug session
         # Just kill all gdbserver instances to keep it simple
         else:
@@ -36,11 +36,11 @@ class GdbCrossConfigNone(GdbCrossConfig):
     def _gen_gdbserver_start_script(self, gdbserver_mode=None):
         """Generate a shell script starting the gdbserver on the remote device via ssh"""
         if gdbserver_mode is None:
-            gdbserver_mode = self.gdbserver_default_mode
-        gdbserver_cmd_start = self._target_gdbserver_start_cmd(gdbserver_mode)
+            gdbserver_mode = self.default_mode
+        gdbserver_cmd_start = self._target_start_cmd(gdbserver_mode)
         gdbserver_cmd_stop = self._target_gdbserver_stop_cmd(gdbserver_mode)
         remote_ssh = "%s %s" % (self.gdb_cross.target_device.ssh_sshexec,
-                                " ".join(self._target_ssh_gdbserver_args()))
+                                " ".join(self._target_ssh_args()))
         gdbserver_cmd = ['#!/bin/sh']
         gdbserver_cmd.append('if [ "$1" = "stop" ]; then')
         gdbserver_cmd.append('  shift')
@@ -48,19 +48,19 @@ class GdbCrossConfigNone(GdbCrossConfig):
         gdbserver_cmd.append('else')
         gdbserver_cmd.append("  %s %s" % (remote_ssh, gdbserver_cmd_start))
         gdbserver_cmd.append('fi')
-        GdbCrossConfigNone.write_file(self.gdbserver_script(gdbserver_mode), gdbserver_cmd, True)
+        GdbCrossConfigNone.write_file(self.server_script(gdbserver_mode), gdbserver_cmd, True)
 
     def _gen_gdbinit_config(self, gdbserver_mode=None):
         """Generate a gdbinit file for this binary and the corresponding gdbserver configuration"""
         if gdbserver_mode is None:
-            gdbserver_mode = self.gdbserver_default_mode
+            gdbserver_mode = self.default_mode
         gdbinit_lines = ['# This file is generated by devtool ide-sdk']
         if gdbserver_mode == GdbServerModes.MULTI:
-            target_help = '#   gdbserver --multi :%d' % self.gdbserver_port
+            target_help = '#   gdbserver --multi :%d' % self.port
             remote_cmd = 'target extended-remote'
         else:
             target_help = '#   gdbserver :%d %s' % (
-                self.gdbserver_port, self.binary)
+                self.port, self.binary)
             remote_cmd = 'target remote'
         gdbinit_lines.append('# On the remote target:')
         gdbinit_lines.append(target_help)
@@ -111,7 +111,7 @@ class GdbCrossConfigNone(GdbCrossConfig):
             gdbinit_lines.append("end" + os.linesep)
 
         gdbinit_lines.append(
-            '%s %s:%d' % (remote_cmd, self.gdb_cross.host, self.gdbserver_port))
+            '%s %s:%d' % (remote_cmd, self.gdb_cross.host, self.port))
         gdbinit_lines.append('set remote exec-file ' + self.binary.binary_path)
         gdbinit_lines.append('run ' + self.binary.binary_path)
 
@@ -127,7 +127,7 @@ class GdbCrossConfigNone(GdbCrossConfig):
 
     def initialize(self):
         self._gen_gdbserver_start_script()
-        if self.binary.runs_as_service and self.gdbserver_default_mode != GdbServerModes.ATTACH:
+        if self.binary.runs_as_service and self.default_mode != GdbServerModes.ATTACH:
             self._gen_gdbserver_start_script(GdbServerModes.ATTACH)
         self._gen_gdbinit_config()
         self._gen_gdb_start_script()
diff --git a/scripts/lib/devtool/ide_sdk.py b/scripts/lib/devtool/ide_sdk.py
index 07f5552758..76cbccf618 100755
--- a/scripts/lib/devtool/ide_sdk.py
+++ b/scripts/lib/devtool/ide_sdk.py
@@ -1159,21 +1159,25 @@ def ide_setup(args, config, basepath, workspace):
         if args.mode == DevtoolIdeMode.modified:
             logger.info("Setting up workspaces for modified recipe: %s" %
                         str(recipes_modified_names))
-            gdbs_cross = {}
+            debuggers = {}
             for recipe_name in recipes_modified_names:
                 recipe_modified = RecipeModified(recipe_name)
                 recipe_modified.initialize(config, workspace, tinfoil)
                 bootstrap_tasks += recipe_modified.bootstrap_tasks
                 recipes_modified.append(recipe_modified)
 
-                if recipe_modified.target_arch not in gdbs_cross:
+                # Key by (arch, toolchain) so recipes with different toolchains
+                # targeting the same arch each get the right debugger.
+                debugger_key = (recipe_modified.target_arch,
+                                recipe_modified.toolchain or '')
+                if debugger_key not in debuggers:
                     target_device = TargetDevice(args)
-                    gdb_cross = RecipeGdbCross(
+                    debugger = RecipeGdbCross(
                         args, recipe_modified.target_arch, target_device)
-                    gdb_cross.initialize(config, workspace, tinfoil)
-                    bootstrap_tasks += gdb_cross.bootstrap_tasks
-                    gdbs_cross[recipe_modified.target_arch] = gdb_cross
-                recipe_modified.gdb_cross = gdbs_cross[recipe_modified.target_arch]
+                    debugger.initialize(config, workspace, tinfoil)
+                    bootstrap_tasks += debugger.bootstrap_tasks
+                    debuggers[debugger_key] = debugger
+                recipe_modified.gdb_cross = debuggers[debugger_key]
 
     finally:
         tinfoil.shutdown()
@@ -1191,7 +1195,8 @@ def ide_setup(args, config, basepath, workspace):
                 config.init_path, basepath, bb_cmd_late, watch=True)
 
     wants_gdbserver = any(
-        r.wants_gdbserver for r in recipes_modified)
+        r.wants_gdbserver and r.toolchain == 'gcc'
+        for r in recipes_modified)
     for recipe_image in recipes_images:
         if wants_gdbserver and recipe_image.gdbserver_missing:
             logger.warning(
-- 
2.53.0



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

* [PATCH 7/9] devtool: ide-sdk add LLDB support for clang toolchain
  2026-03-18 22:36 [PATCH 0/9] devtool: ide-sdk clang/LLDB support and minor fixes AdrianF
                   ` (5 preceding siblings ...)
  2026-03-18 22:36 ` [PATCH 6/9] devtool: ide-sdk debugger back-end abstraction AdrianF
@ 2026-03-18 22:36 ` AdrianF
  2026-03-18 22:36 ` [PATCH 8/9] meta-selftest: refactor cpp examples into .inc files and add clang variants AdrianF
  2026-03-18 22:36 ` [PATCH 9/9] oe-selftest: devtool ide-sdk: add clang/LLDB test AdrianF
  8 siblings, 0 replies; 13+ messages in thread
From: AdrianF @ 2026-03-18 22:36 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

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

Add support for LLDB (CodeLLDB) remote debugging in VSCode when using
the clang toolchain. This includes:

- New LldbServerConfig class for configuring lldb-server on the target
- LldbServerConfigVSCode for VSCode-specific LLDB configuration
- RecipeLldbNative to handle lldb-native (architecture-agnostic) on the
  host
- CodeLLDB VSCode extension recommendation for clang toolchain
- Launch configuration generator for LLDB debugging
- Proper handling of source maps and debug symbol paths for LLDB

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 scripts/lib/devtool/ide_plugins/__init__.py |  63 +++++++++++
 scripts/lib/devtool/ide_plugins/ide_code.py | 114 +++++++++++++++++++-
 scripts/lib/devtool/ide_sdk.py              |  61 ++++++++++-
 3 files changed, 229 insertions(+), 9 deletions(-)

diff --git a/scripts/lib/devtool/ide_plugins/__init__.py b/scripts/lib/devtool/ide_plugins/__init__.py
index 8c41afc640..d7d06567ee 100644
--- a/scripts/lib/devtool/ide_plugins/__init__.py
+++ b/scripts/lib/devtool/ide_plugins/__init__.py
@@ -181,6 +181,69 @@ class GdbCrossConfig(DebuggerCrossConfig):
         return "\"kill \\$(pgrep -o -f 'gdbserver --attach :%s') 2>/dev/null || true\"" % self.port
 
 
+class LldbServerConfig(DebuggerCrossConfig):
+    """Configure lldb-server (platform mode) on the target for CodeLLDB remote debugging.
+
+    Unlike gdbserver, lldb-server platform mode is architecture-agnostic on the host
+    side: a single lldb-native binary handles all target architectures via the
+    LLDB platform protocol that CodeLLDB speaks natively.
+
+    The ATTACH mode is not supported because lldb-server platform does not take a
+    PID argument; attaching is done client-side via 'process attach'.
+    """
+
+    def __init__(self, image_recipe, modified_recipe, binary,
+                 default_mode=GdbServerModes.MULTI):
+        super().__init__(image_recipe, modified_recipe, binary,
+                         default_mode)
+
+    def _lldb_server_tmp_dir(self, mode):
+        return os.path.join('/tmp', 'lldb_server_%s' % self.id_pretty_mode(mode))
+
+    def _lldb_server_pid_file(self, mode):
+        return os.path.join(self._lldb_server_tmp_dir(mode), 'lldb_server.pid')
+
+    def _lldb_server_log_file(self, mode):
+        return os.path.join(self._lldb_server_tmp_dir(mode), 'lldb_server.log')
+
+    def _target_start_cmd(self, mode):
+        """SSH command to start lldb-server in platform mode on the target."""
+        lldb_server = self.gdb_cross.gdbserver_path
+        # Use '*:<port>' so lldb-server binds on all interfaces (0.0.0.0), not
+        # just loopback.  The bare ':<port>' form only binds to 127.0.0.1 in
+        # lldb-server 21.x and the remote lldb client connects from the host.
+        # Start from /tmp because lldb-server creates temp files in its cwd and
+        # the SSH default cwd (/home/root) may not exist on a minimal image.
+        if mode == GdbServerModes.ONCE:
+            cmd = "cd /tmp && %s platform --one-shot --server --listen *:%s" % (
+                lldb_server, self.port)
+        elif mode == GdbServerModes.MULTI:
+            pid_file = self._lldb_server_pid_file(mode)
+            tmp_dir = self._lldb_server_tmp_dir(mode)
+            log_file = self._lldb_server_log_file(mode)
+            cmd = "test -f %s && exit 0; " % pid_file
+            cmd += "mkdir -p %s; " % tmp_dir
+            cmd += "cd %s; " % tmp_dir
+            cmd += "%s platform --server --listen *:%s > %s 2>&1 & " % (
+                lldb_server, self.port, log_file)
+            cmd += "echo \\$! > %s;" % pid_file
+        else:
+            raise DevtoolError(
+                "lldb-server does not support mode %s "
+                "(ATTACH is handled client-side with 'process attach')" % mode)
+        return "\"/bin/sh -c '" + cmd + "'\""
+
+    def _target_kill_cmd(self):
+        """SSH command to stop a MULTI-mode lldb-server on the target."""
+        pid_file = self._lldb_server_pid_file(GdbServerModes.MULTI)
+        tmp_dir = self._lldb_server_tmp_dir(GdbServerModes.MULTI)
+        cmd = ("test -f %(pf)s && kill \\$(cat %(pf)s) 2>/dev/null; rm -rf %(td)s"
+               % {'pf': pid_file, 'td': tmp_dir})
+        return "\"/bin/sh -c '" + cmd + "'\""
+
+    def server_modes(self):
+        """ATTACH mode is not applicable for lldb-server platform."""
+        return [self.default_mode]
 
 
 class IdeBase:
diff --git a/scripts/lib/devtool/ide_plugins/ide_code.py b/scripts/lib/devtool/ide_plugins/ide_code.py
index 7fe5a40eb1..1b8434ab1c 100644
--- a/scripts/lib/devtool/ide_plugins/ide_code.py
+++ b/scripts/lib/devtool/ide_plugins/ide_code.py
@@ -9,7 +9,7 @@ import json
 import logging
 import os
 import shutil
-from devtool.ide_plugins import BuildTool, IdeBase, DebuggerCrossConfig, GdbCrossConfig, GdbServerModes, get_devtool_deploy_opts
+from devtool.ide_plugins import BuildTool, IdeBase, DebuggerCrossConfig, GdbCrossConfig, GdbServerModes, LldbServerConfig, get_devtool_deploy_opts
 
 logger = logging.getLogger('devtool')
 
@@ -43,6 +43,28 @@ class GdbCrossConfigVSCode(GdbCrossConfig):
         ]
 
 
+class LldbServerConfigVSCode(LldbServerConfig):
+    """VSCode-specific lldb-server configuration for CodeLLDB remote debugging."""
+
+    def __init__(self, image_recipe, modified_recipe, binary,
+                 default_mode=GdbServerModes.MULTI):
+        super().__init__(image_recipe, modified_recipe, binary,
+                         default_mode)
+
+    def target_ssh_gdbserver_start_args(self, mode=None):
+        """SSH argument list to start lldb-server on the target"""
+        if mode is None:
+            mode = self.default_mode
+        return self._target_ssh_args() + [
+            self._target_start_cmd(mode)
+        ]
+
+    def target_ssh_gdbserver_kill_args(self):
+        """SSH argument list to stop a running MULTI-mode lldb-server"""
+        return self._target_ssh_args() + [
+            self._target_kill_cmd()
+        ]
+
 class IdeVSCode(IdeBase):
     """Manage IDE configurations for VSCode
 
@@ -243,6 +265,10 @@ class IdeVSCode(IdeBase):
                 "ms-vscode.cpptools-extension-pack",
                 "ms-vscode.cpptools-themes"
             ]
+        # For clang toolchain, CodeLLDB provides native LLDB debugging in VSCode
+        if (modified_recipe.toolchain == 'clang'
+                and modified_recipe.build_tool.is_c_cpp):
+            recommendations.append("vadimcn.vscode-lldb")
         if modified_recipe.build_tool is BuildTool.CMAKE:
             recommendations.append("ms-vscode.cmake-tools")
         if modified_recipe.build_tool is BuildTool.MESON:
@@ -286,7 +312,9 @@ class IdeVSCode(IdeBase):
             self.dot_code_dir(modified_recipe), prop_file, properties_dicts)
 
     def vscode_launch_bin_dbg(self, gdb_cross_config, gdbserver_mode):
-        """Dispatch to the GDB launch config generator."""
+        """Dispatch to the GDB or LLDB launch config generator."""
+        if isinstance(gdb_cross_config, LldbServerConfig):
+            return self._vscode_launch_bin_dbg_lldb(gdb_cross_config, gdbserver_mode)
         return self._vscode_launch_bin_dbg_gdb(gdb_cross_config, gdbserver_mode)
 
     def _vscode_launch_bin_dbg_gdb(self, gdb_cross_config, gdbserver_mode):
@@ -373,6 +401,80 @@ class IdeVSCode(IdeBase):
 
         return launch_config
 
+    def _vscode_launch_bin_dbg_lldb(self, lldb_config, gdbserver_mode):
+        """Generate a CodeLLDB (type: lldb) launch configuration entry for launch.json.
+
+        CodeLLDB connects to lldb-server via the LLDB platform protocol.  The
+        initCommands select the remote platform and open the connection before
+        the process is launched, so CodeLLDB can inspect and control it.
+        """
+        modified_recipe = lldb_config.modified_recipe
+        gdb_cross = modified_recipe.gdb_cross
+
+        init_commands = [
+            "platform select remote-linux",
+            "platform connect connect://%s:%d" % (gdb_cross.host, lldb_config.port),
+            # Clear the default step-avoid-regexp so std:: and other library
+            # namespaces are not silently skipped on step-in. (default is "std::" in LLDB 15+)
+            "settings set target.process.thread.step-avoid-regexp \"\"",
+        ]
+        # Point LLDB at the installed files in ${D} so it resolves shared
+        # library paths automatically (equivalent of GDB's 'set sysroot').
+        # target.sysroot is not a valid LLDB setting; use the module search
+        # path substitution instead, which requires a target to exist and
+        # therefore must run in preRunCommands, not initCommands.
+        pre_run_commands = [
+            "target modules search-paths add / %s" % modified_recipe.d,
+        ]
+
+        # Search for header files in recipe-sysroot (same as GDB sourceFileMap).
+        source_map = {
+            "/usr/include": os.path.join(modified_recipe.recipe_sysroot, "usr", "include")
+        }
+        if lldb_config.image_recipe.rootfs_dbg:
+            # Map build-time paths back to the workspace source tree.
+            for target_path, host_path in modified_recipe.reverse_debug_prefix_map.items():
+                if host_path.startswith(modified_recipe.real_srctree):
+                    source_map[target_path] = (
+                        "${workspaceFolder}"
+                        + host_path[len(modified_recipe.real_srctree):])
+                else:
+                    source_map[target_path] = host_path
+            if "/usr/src/debug" in source_map:
+                logger.error(
+                    'Key "/usr/src/debug" already exists in source_map. '
+                    'Something with DEBUG_PREFIX_MAP looks unexpected and finding '
+                    'sources in the rootfs-dbg will not work as expected.')
+            else:
+                source_map["/usr/src/debug"] = os.path.join(
+                    lldb_config.image_recipe.rootfs_dbg, "usr", "src", "debug")
+
+            # Point LLDB at the .debug directories in rootfs-dbg.
+            debug_search_paths = " ".join(
+                modified_recipe.solib_search_path(lldb_config.image_recipe))
+            init_commands.append(
+                "settings set target.debug-file-search-paths %s" % debug_search_paths)
+        else:
+            logger.warning(
+                "Cannot setup debug symbols configuration for LLDB. "
+                "IMAGE_GEN_DEBUGFS is not enabled.")
+
+        launch_config = {
+            "name": lldb_config.id_pretty_mode(gdbserver_mode),
+            "type": "lldb",
+            "request": "launch",
+            "program": lldb_config.binary.binary_host_path,
+            "stopOnEntry": False,
+            "cwd": "/tmp",
+            "preLaunchTask": lldb_config.id_pretty_mode(gdbserver_mode),
+            "initCommands": init_commands,
+            "preRunCommands": pre_run_commands,
+        }
+        if source_map:
+            launch_config["sourceMap"] = source_map
+
+        return launch_config
+
     def vscode_launch(self, args, modified_recipe):
         """GDB launch configurations for user-space binaries.
 
@@ -682,8 +784,12 @@ class IdeVSCode(IdeBase):
         self.vscode_extensions(modified_recipe)
         self.vscode_c_cpp_properties(modified_recipe)
         if args.target:
-            self.initialize_gdb_cross_configs(
-                image_recipe, modified_recipe, GdbCrossConfigVSCode)
+            if modified_recipe.toolchain == 'clang':
+                self.initialize_gdb_cross_configs(
+                    image_recipe, modified_recipe, LldbServerConfigVSCode)
+            else:
+                self.initialize_gdb_cross_configs(
+                    image_recipe, modified_recipe, GdbCrossConfigVSCode)
             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 76cbccf618..7c461e6b0e 100755
--- a/scripts/lib/devtool/ide_sdk.py
+++ b/scripts/lib/devtool/ide_sdk.py
@@ -137,6 +137,45 @@ class RecipeGdbCross(RecipeNative):
         return self.target_device.host
 
 
+class RecipeLldbNative(RecipeNative):
+    """Handle lldb on the host and lldb-server on the target device.
+
+    Unlike GDB which requires a per-architecture gdb-cross-<arch> binary, LLDB
+    is architecture-agnostic: a single lldb-native installation can debug any
+    target architecture via the LLDB platform protocol.
+
+    On the target side, lldb-server (the ${PN}-server sub-package from the lldb
+    recipe) provides the platform server that CodeLLDB connects to.
+    """
+
+    def __init__(self, args, target_device):
+        super().__init__('lldb-native')
+        self.target_device = target_device
+        self._lldb = None
+        self._lldb_server_path = None
+
+    def __find_lldb_server(self, config, tinfoil):
+        """Absolute path of lldb-server on the target (from the lldb recipe)."""
+        recipe_d_lldb = parse_recipe(
+            config, tinfoil, 'lldb', appends=True, filter_workspace=False)
+        if not recipe_d_lldb:
+            raise DevtoolError("Parsing lldb recipe failed")
+        return os.path.join(recipe_d_lldb.getVar('bindir'), 'lldb-server')
+
+    def initialize(self, config, workspace, tinfoil):
+        super()._initialize(config, workspace, tinfoil)
+        self._lldb = os.path.join(self.staging_bindir_native, 'lldb')
+        self._lldb_server_path = self.__find_lldb_server(config, tinfoil)
+
+    @property
+    def gdbserver_path(self):
+        return self._lldb_server_path
+
+    @property
+    def host(self):
+        return self.target_device.host
+
+
 class RecipeImage:
     """Handle some image recipe related properties
 
@@ -169,8 +208,9 @@ class RecipeImage:
         if image_d.getVar('IMAGE_GEN_DEBUGFS') == "1":
             self.__rootfs_dbg = os.path.join(workdir, 'rootfs-dbg')
 
-        self.gdbserver_missing = 'gdbserver' not in image_d.getVar(
-            'IMAGE_INSTALL') and 'tools-debug' not in image_d.getVar('IMAGE_FEATURES')
+        package_install = image_d.getVar('PACKAGE_INSTALL').split()
+        self.gdbserver_missing = 'gdbserver' not in package_install
+        self.lldb_server_missing = 'lldb-server' not in package_install
 
     @property
     def debug_support(self):
@@ -1172,8 +1212,11 @@ def ide_setup(args, config, basepath, workspace):
                                 recipe_modified.toolchain or '')
                 if debugger_key not in debuggers:
                     target_device = TargetDevice(args)
-                    debugger = RecipeGdbCross(
-                        args, recipe_modified.target_arch, target_device)
+                    if recipe_modified.toolchain == 'clang':
+                        debugger = RecipeLldbNative(args, target_device)
+                    else:
+                        debugger = RecipeGdbCross(
+                            args, recipe_modified.target_arch, target_device)
                     debugger.initialize(config, workspace, tinfoil)
                     bootstrap_tasks += debugger.bootstrap_tasks
                     debuggers[debugger_key] = debugger
@@ -1197,12 +1240,20 @@ def ide_setup(args, config, basepath, workspace):
     wants_gdbserver = any(
         r.wants_gdbserver and r.toolchain == 'gcc'
         for r in recipes_modified)
+    wants_lldb_server = any(
+        r.wants_gdbserver and r.toolchain == 'clang'
+        for r in recipes_modified)
     for recipe_image in recipes_images:
         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 wants_lldb_server and recipe_image.lldb_server_missing:
+            logger.warning(
+                "lldb-server not installed in image %s. "
+                "Remote debugging with LLDB (CodeLLDB) will not be available. "
+                "Add 'lldb-server' to IMAGE_INSTALL." % recipe_image)
 
-        if wants_gdbserver and recipe_image.combine_dbg_image is False:
+        if (wants_gdbserver or wants_lldb_server) 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)
 
-- 
2.53.0



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

* [PATCH 8/9] meta-selftest: refactor cpp examples into .inc files and add clang variants
  2026-03-18 22:36 [PATCH 0/9] devtool: ide-sdk clang/LLDB support and minor fixes AdrianF
                   ` (6 preceding siblings ...)
  2026-03-18 22:36 ` [PATCH 7/9] devtool: ide-sdk add LLDB support for clang toolchain AdrianF
@ 2026-03-18 22:36 ` AdrianF
  2026-03-18 22:36 ` [PATCH 9/9] oe-selftest: devtool ide-sdk: add clang/LLDB test AdrianF
  8 siblings, 0 replies; 13+ messages in thread
From: AdrianF @ 2026-03-18 22:36 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

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

Refactor cmake-example.bb and meson-example.bb to extract common
build logic into separate .inc files. Add clang variants of both
examples to enable testing with alternative toolchains.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 .../recipes-test/cpp/cmake-example-clang.bb   | 13 +++++++
 .../recipes-test/cpp/cmake-example.bb         | 20 ++---------
 .../recipes-test/cpp/cmake-example.inc        | 29 +++++++++++++++
 .../recipes-test/cpp/cmake-example/run-ptest  | 10 ------
 .../recipes-test/cpp/cpp-example.inc          | 20 +++++++----
 .../recipes-test/cpp/files/CMakeLists.txt     | 35 +++++++++---------
 .../recipes-test/cpp/files/meson.build        | 17 +++++----
 .../recipes-test/cpp/files/meson.options      |  6 ++++
 .../cpp/{meson-example => files}/run-ptest    |  4 +--
 .../recipes-test/cpp/meson-example-clang.bb   | 13 +++++++
 .../recipes-test/cpp/meson-example.bb         | 24 ++-----------
 .../recipes-test/cpp/meson-example.inc        | 36 +++++++++++++++++++
 12 files changed, 147 insertions(+), 80 deletions(-)
 create mode 100644 meta-selftest/recipes-test/cpp/cmake-example-clang.bb
 create mode 100644 meta-selftest/recipes-test/cpp/cmake-example.inc
 delete mode 100644 meta-selftest/recipes-test/cpp/cmake-example/run-ptest
 rename meta-selftest/recipes-test/cpp/{meson-example => files}/run-ptest (51%)
 create mode 100644 meta-selftest/recipes-test/cpp/meson-example-clang.bb
 create mode 100644 meta-selftest/recipes-test/cpp/meson-example.inc

diff --git a/meta-selftest/recipes-test/cpp/cmake-example-clang.bb b/meta-selftest/recipes-test/cpp/cmake-example-clang.bb
new file mode 100644
index 0000000000..bf20b94e14
--- /dev/null
+++ b/meta-selftest/recipes-test/cpp/cmake-example-clang.bb
@@ -0,0 +1,13 @@
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+SUMMARY = "A C++ example compiled with cmake and clang."
+
+require cmake-example.inc
+
+TOOLCHAIN = "clang"
+EX_BINARY_NAME = "${BPN}"
+EX_SERVICE_USER = "cmake-example"
diff --git a/meta-selftest/recipes-test/cpp/cmake-example.bb b/meta-selftest/recipes-test/cpp/cmake-example.bb
index aecfcf780a..19d056fdd8 100644
--- a/meta-selftest/recipes-test/cpp/cmake-example.bb
+++ b/meta-selftest/recipes-test/cpp/cmake-example.bb
@@ -4,22 +4,8 @@
 # SPDX-License-Identifier: MIT
 #
 
-SUMMARY = "A C++ example compiled with cmake."
+SUMMARY = "A C++ example compiled with cmake and GCC."
 
-require cpp-example.inc
+require cmake-example.inc
 
-SRC_URI += "file://CMakeLists.txt"
-
-inherit cmake-qemu
-
-PACKAGECONFIG[failing_test] = "-DFAILING_TEST=ON"
-
-FILES:${PN}-ptest += "${bindir}/test-cmake-example"
-
-do_run_tests () {
-    bbnote ${DESTDIR:+DESTDIR=${DESTDIR} }${CMAKE_VERBOSE} cmake --build '${B}' --target test -- ${EXTRA_OECMAKE_BUILD}
-    eval ${DESTDIR:+DESTDIR=${DESTDIR} }${CMAKE_VERBOSE} cmake --build '${B}' --target test -- ${EXTRA_OECMAKE_BUILD}
-}
-do_run_tests[doc] = "Run cmake --target=test using qemu-user"
-
-addtask do_run_tests after do_compile
+TOOLCHAIN = "gcc"
diff --git a/meta-selftest/recipes-test/cpp/cmake-example.inc b/meta-selftest/recipes-test/cpp/cmake-example.inc
new file mode 100644
index 0000000000..eb023d389a
--- /dev/null
+++ b/meta-selftest/recipes-test/cpp/cmake-example.inc
@@ -0,0 +1,29 @@
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+require cpp-example.inc
+
+SRC_URI += "file://CMakeLists.txt"
+
+inherit cmake-qemu
+
+PACKAGECONFIG[failing_test] = "-DFAILING_TEST=ON"
+
+# Support installing all recipe variants in parallel
+EXTRA_OECMAKE += "\
+    -DBINARY_NAME=${EX_BINARY_NAME} \
+    -DTEST_BINARY_NAME=${EX_TEST_BINARY_NAME} \
+"
+
+FILES:${PN}-ptest += "${bindir}/${EX_TEST_BINARY_NAME}"
+
+do_run_tests () {
+    bbnote ${DESTDIR:+DESTDIR=${DESTDIR} }${CMAKE_VERBOSE} cmake --build '${B}' --target test -- ${EXTRA_OECMAKE_BUILD}
+    eval ${DESTDIR:+DESTDIR=${DESTDIR} }${CMAKE_VERBOSE} cmake --build '${B}' --target test -- ${EXTRA_OECMAKE_BUILD}
+}
+do_run_tests[doc] = "Run cmake --target=test using qemu-user"
+
+addtask do_run_tests after do_compile
diff --git a/meta-selftest/recipes-test/cpp/cmake-example/run-ptest b/meta-selftest/recipes-test/cpp/cmake-example/run-ptest
deleted file mode 100644
index 94b620a198..0000000000
--- a/meta-selftest/recipes-test/cpp/cmake-example/run-ptest
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-#
-# Copyright OpenEmbedded Contributors
-#
-# SPDX-License-Identifier: MIT
-#
-
-test-cmake-example
-
-# Note: run-ptests exits with exit value from test-cmake-example
diff --git a/meta-selftest/recipes-test/cpp/cpp-example.inc b/meta-selftest/recipes-test/cpp/cpp-example.inc
index 0671824d1c..0070d17201 100644
--- a/meta-selftest/recipes-test/cpp/cpp-example.inc
+++ b/meta-selftest/recipes-test/cpp/cpp-example.inc
@@ -35,10 +35,16 @@ INITSCRIPT_PARAMS = "defaults 99"
 
 # Create cpp-example user and group
 USERADD_PACKAGES = "${PN}"
-GROUPADD_PARAM:${PN} = "--system ${BPN}"
-USERADD_PARAM:${PN} = "--system --home /var/lib/${BPN} --no-create-home --shell /bin/false --gid ${BPN} ${BPN}"
+GROUPADD_PARAM:${PN} = "--system ${EX_SERVICE_USER}"
+USERADD_PARAM:${PN} = "--system --home /var/lib/${EX_SERVICE_USER} --no-create-home --shell /bin/false --gid ${EX_SERVICE_USER} ${EX_SERVICE_USER}"
 
+EX_SERVICE_USER ?= "${BPN}"
 EX_BINARY_NAME ?= "${BPN}"
+EX_TEST_BINARY_NAME ?= "test-${EX_BINARY_NAME}"
+
+do_install_ptest() {
+    sed -i -e 's|@TEST_BINARY_NAME@|${EX_TEST_BINARY_NAME}|g' ${D}${PTEST_PATH}/run-ptest
+}
 
 do_install:append() {
     # Install configuration file owned by the recipe's unprivileged user.
@@ -47,7 +53,7 @@ do_install:append() {
     # in the final image. devtool deploy-target is a raw file copy and does not run
     # pkg_postinst, so the UID in ${D} must already be correct.
     install -d ${D}${sysconfdir}
-    install -m 0644 -g ${BPN} -o ${BPN} ${S}/cpp-example.conf ${D}${sysconfdir}/${BPN}.conf
+    install -m 0644 -g ${EX_SERVICE_USER} -o ${EX_SERVICE_USER} ${S}/cpp-example.conf ${D}${sysconfdir}/${BPN}.conf
     sed -i -e 's|@BINARY_NAME@|${BPN}|g' ${D}${sysconfdir}/${BPN}.conf
 
     # Install service files or init scripts and substitute placeholders in service files
@@ -57,8 +63,8 @@ do_install:append() {
         sed -i \
             -e 's|@BINDIR@|${bindir}|g' \
             -e 's|@BINARY_NAME@|${EX_BINARY_NAME}|g' \
-            -e 's|@USER@|${BPN}|g' \
-            -e 's|@GROUP@|${BPN}|g' \
+            -e 's|@USER@|${EX_SERVICE_USER}|g' \
+            -e 's|@GROUP@|${EX_SERVICE_USER}|g' \
             ${D}${systemd_system_unitdir}/${BPN}.service
     else
         install -d ${D}${sysconfdir}/init.d
@@ -66,8 +72,8 @@ do_install:append() {
         sed -i \
             -e 's|@BINDIR@|${bindir}|g' \
             -e 's|@BINARY_NAME@|${EX_BINARY_NAME}|g' \
-            -e 's|@USER@|${BPN}|g' \
-            -e 's|@GROUP@|${BPN}|g' \
+            -e 's|@USER@|${EX_SERVICE_USER}|g' \
+            -e 's|@GROUP@|${EX_SERVICE_USER}|g' \
             ${D}${sysconfdir}/init.d/${BPN}
     fi
 }
diff --git a/meta-selftest/recipes-test/cpp/files/CMakeLists.txt b/meta-selftest/recipes-test/cpp/files/CMakeLists.txt
index e363f31af2..8802839702 100644
--- a/meta-selftest/recipes-test/cpp/files/CMakeLists.txt
+++ b/meta-selftest/recipes-test/cpp/files/CMakeLists.txt
@@ -14,6 +14,9 @@ project(cmake-example
 option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
 option(FAILING_TEST "Compile a failing unit test to test the test infrastructure" OFF)
 
+set(BINARY_NAME "cmake-example" CACHE STRING "Name of the installed executable and library prefix")
+set(TEST_BINARY_NAME "test-cmake-example" CACHE STRING "Name of the installed test executable")
+
 set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED On)
 set(CMAKE_CXX_EXTENSIONS Off)
@@ -21,7 +24,7 @@ set(CMAKE_CXX_EXTENSIONS Off)
 include(GNUInstallDirs)
 
 # Define the config file path as a constant
-set(CPP_EXAMPLE_CONFIG_PATH "${CMAKE_INSTALL_FULL_SYSCONFDIR}/cmake-example.conf")
+set(CPP_EXAMPLE_CONFIG_PATH "${CMAKE_INSTALL_FULL_SYSCONFDIR}/${BINARY_NAME}.conf")
 
 # Generate config.h from config.h.in
 configure_file(config.h.in config.h @ONLY)
@@ -30,44 +33,44 @@ configure_file(config.h.in config.h @ONLY)
 find_package(json-c)
 
 # A simple library linking json-c library found by pkgconfig
-add_library(cmake-example-lib cpp-example-lib.cpp cpp-example-lib.hpp)
-set_target_properties(cmake-example-lib PROPERTIES
+add_library(${BINARY_NAME}-lib cpp-example-lib.cpp cpp-example-lib.hpp)
+set_target_properties(${BINARY_NAME}-lib PROPERTIES
     VERSION ${PROJECT_VERSION}
     SOVERSION ${PROJECT_VERSION_MAJOR}
 )
 
 # Add the build directory to include path for config.h
-target_include_directories(cmake-example-lib PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+target_include_directories(${BINARY_NAME}-lib PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
 
-target_link_libraries(cmake-example-lib PRIVATE json-c::json-c)
+target_link_libraries(${BINARY_NAME}-lib PRIVATE json-c::json-c)
 
-install(TARGETS cmake-example-lib
+install(TARGETS ${BINARY_NAME}-lib
     INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
     ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
     LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
 )
 
 # A simple executable linking the library
-add_executable(cmake-example cpp-example.cpp)
-target_include_directories(cmake-example PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
-target_link_libraries(cmake-example PRIVATE cmake-example-lib)
+add_executable(${BINARY_NAME} cpp-example.cpp)
+target_include_directories(${BINARY_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+target_link_libraries(${BINARY_NAME} PRIVATE ${BINARY_NAME}-lib)
 
-install(TARGETS cmake-example
+install(TARGETS ${BINARY_NAME}
     RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
 )
 
 # A simple test executable for testing the library
-add_executable(test-cmake-example test-cpp-example.cpp)
-target_include_directories(test-cmake-example PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
-target_link_libraries(test-cmake-example PRIVATE cmake-example-lib)
+add_executable(${TEST_BINARY_NAME} test-cpp-example.cpp)
+target_include_directories(${TEST_BINARY_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+target_link_libraries(${TEST_BINARY_NAME} PRIVATE ${BINARY_NAME}-lib)
 
 if (FAILING_TEST)
-    target_compile_definitions(test-cmake-example PRIVATE FAIL_COMPARISON_STR="foo")
+    target_compile_definitions(${TEST_BINARY_NAME} PRIVATE FAIL_COMPARISON_STR="foo")
 endif(FAILING_TEST)
 
-install(TARGETS test-cmake-example
+install(TARGETS ${TEST_BINARY_NAME}
     RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
 )
 
 include(CTest)
-add_test(NAME test-cmake-example COMMAND test-cmake-example)
+add_test(NAME ${TEST_BINARY_NAME} COMMAND ${TEST_BINARY_NAME})
diff --git a/meta-selftest/recipes-test/cpp/files/meson.build b/meta-selftest/recipes-test/cpp/files/meson.build
index 53248c4380..3cb4669dfa 100644
--- a/meta-selftest/recipes-test/cpp/files/meson.build
+++ b/meta-selftest/recipes-test/cpp/files/meson.build
@@ -16,8 +16,11 @@ if get_option('FAILING_TEST').enabled()
     add_project_arguments('-DFAIL_COMPARISON_STR=foo', language: 'cpp')
 endif
 
+binary_name = get_option('BINARY_NAME')
+test_binary_name = get_option('TEST_BINARY_NAME')
+
 # Generate config.h from config.h.in
-config_path = get_option('sysconfdir') / 'meson-example.conf'
+config_path = get_option('sysconfdir') / get_option('CONFIG_FILE_NAME')
 conf_data = configuration_data()
 conf_data.set('CPP_EXAMPLE_CONFIG_PATH', config_path)
 configure_file(input : 'config.h.in',
@@ -27,7 +30,7 @@ configure_file(input : 'config.h.in',
 # Include the build directory for config.h
 inc_dir = include_directories('.')
 
-mesonexlib = shared_library('mesonexlib',
+exlib = shared_library(binary_name + 'lib',
     'cpp-example-lib.cpp', 'cpp-example-lib.hpp',
     version: meson.project_version(),
     soversion: meson.project_version().split('.')[0],
@@ -36,18 +39,18 @@ mesonexlib = shared_library('mesonexlib',
     install : true
     )
 
-executable('mesonex',
+executable(binary_name,
     'cpp-example.cpp',
-    link_with : mesonexlib,
+    link_with : exlib,
     include_directories : inc_dir,
     install : true
     )
 
-test_mesonex = executable('test-mesonex',
+test_exe = executable(test_binary_name,
     'test-cpp-example.cpp',
-    link_with : mesonexlib,
+    link_with : exlib,
     include_directories : inc_dir,
     install : true
 )
 
-test('meson example test', test_mesonex)
+test('meson example test', test_exe)
diff --git a/meta-selftest/recipes-test/cpp/files/meson.options b/meta-selftest/recipes-test/cpp/files/meson.options
index 58a0bf9e61..374e346197 100644
--- a/meta-selftest/recipes-test/cpp/files/meson.options
+++ b/meta-selftest/recipes-test/cpp/files/meson.options
@@ -1,3 +1,9 @@
 
 option('FAILING_TEST', type : 'feature', value : 'disabled',
     description : 'Compile a failing unit test to test the test infrastructure')
+option('BINARY_NAME', type : 'string', value : 'mesonex',
+    description : 'Name of the installed executable')
+option('TEST_BINARY_NAME', type : 'string', value : 'test-mesonex',
+    description : 'Name of the installed test executable')
+option('CONFIG_FILE_NAME', type : 'string', value : 'meson-example.conf',
+    description : 'Configuration file name in sysconfdir')
diff --git a/meta-selftest/recipes-test/cpp/meson-example/run-ptest b/meta-selftest/recipes-test/cpp/files/run-ptest
similarity index 51%
rename from meta-selftest/recipes-test/cpp/meson-example/run-ptest
rename to meta-selftest/recipes-test/cpp/files/run-ptest
index b1804f0096..62c24db04f 100644
--- a/meta-selftest/recipes-test/cpp/meson-example/run-ptest
+++ b/meta-selftest/recipes-test/cpp/files/run-ptest
@@ -5,6 +5,6 @@
 # SPDX-License-Identifier: MIT
 #
 
-test-mesonex
+@TEST_BINARY_NAME@
 
-# Note: run-ptests exits with exit value from test-mesonex
+# Note: run-ptest exits with exit value from @TEST_BINARY_NAME@
diff --git a/meta-selftest/recipes-test/cpp/meson-example-clang.bb b/meta-selftest/recipes-test/cpp/meson-example-clang.bb
new file mode 100644
index 0000000000..341ade21f9
--- /dev/null
+++ b/meta-selftest/recipes-test/cpp/meson-example-clang.bb
@@ -0,0 +1,13 @@
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+SUMMARY = "A C++ example compiled with meson and clang."
+
+require meson-example.inc
+
+TOOLCHAIN = "clang"
+EX_BINARY_NAME = "mesonex-clang"
+EX_SERVICE_USER = "meson-example"
diff --git a/meta-selftest/recipes-test/cpp/meson-example.bb b/meta-selftest/recipes-test/cpp/meson-example.bb
index da0ea18376..b2335f5c26 100644
--- a/meta-selftest/recipes-test/cpp/meson-example.bb
+++ b/meta-selftest/recipes-test/cpp/meson-example.bb
@@ -4,26 +4,8 @@
 # SPDX-License-Identifier: MIT
 #
 
-SUMMARY = "A C++ example compiled with meson."
+SUMMARY = "A C++ example compiled with meson and GCC."
 
-require cpp-example.inc
+require meson-example.inc
 
-SRC_URI += "\
-    file://meson.build \
-    file://meson.options \
-"
-
-inherit pkgconfig meson
-
-PACKAGECONFIG[failing_test] = "-DFAILING_TEST=enabled"
-
-FILES:${PN}-ptest += "${bindir}/test-mesonex"
-
-do_run_tests () {
-    meson test -C "${B}" --no-rebuild
-}
-do_run_tests[doc] = "Run meson test using qemu-user"
-
-addtask do_run_tests after do_compile
-
-EX_BINARY_NAME = "mesonex"
+TOOLCHAIN = "gcc"
diff --git a/meta-selftest/recipes-test/cpp/meson-example.inc b/meta-selftest/recipes-test/cpp/meson-example.inc
new file mode 100644
index 0000000000..2937be27f8
--- /dev/null
+++ b/meta-selftest/recipes-test/cpp/meson-example.inc
@@ -0,0 +1,36 @@
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+SUMMARY = "A C++ example compiled with meson."
+
+require cpp-example.inc
+
+SRC_URI += "\
+    file://meson.build \
+    file://meson.options \
+"
+
+inherit pkgconfig meson
+
+PACKAGECONFIG[failing_test] = "-DFAILING_TEST=enabled"
+
+# Support installing all recipes variants in parallel
+EXTRA_OEMESON += "\
+    -DBINARY_NAME=${EX_BINARY_NAME} \
+    -DTEST_BINARY_NAME=${EX_TEST_BINARY_NAME} \
+    -DCONFIG_FILE_NAME=${BPN}.conf \
+"
+
+FILES:${PN}-ptest += "${bindir}/${EX_TEST_BINARY_NAME}"
+
+do_run_tests () {
+    meson test -C "${B}" --no-rebuild
+}
+do_run_tests[doc] = "Run meson test using qemu-user"
+
+addtask do_run_tests after do_compile
+
+EX_BINARY_NAME = "mesonex"
-- 
2.53.0



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

* [PATCH 9/9] oe-selftest: devtool ide-sdk: add clang/LLDB test
  2026-03-18 22:36 [PATCH 0/9] devtool: ide-sdk clang/LLDB support and minor fixes AdrianF
                   ` (7 preceding siblings ...)
  2026-03-18 22:36 ` [PATCH 8/9] meta-selftest: refactor cpp examples into .inc files and add clang variants AdrianF
@ 2026-03-18 22:36 ` AdrianF
  2026-03-20  7:12   ` [OE-core] " Mathieu Dubois-Briand
  8 siblings, 1 reply; 13+ messages in thread
From: AdrianF @ 2026-03-18 22:36 UTC (permalink / raw)
  To: openembedded-core; +Cc: Adrian Freihofer

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

Add test_devtool_ide_sdk_code_cmake_clang to verify the full devtool
ide-sdk workflow for a cmake recipe built with clang.  Unlike the gcc
variant the clang recipe uses lldb-server for remote debugging and
CodeLLDB (vadimcn.vscode-lldb) as the VS Code debug adapter.

The test covers:
- devtool modify + devtool ide-sdk with ide=code
- cmake preset compilation and CTest execution (same as the gcc test)
- extensions.json recommends vadimcn.vscode-lldb
- launch.json uses "type": "lldb" (CodeLLDB) instead of "type": "cppdbg"
- End-to-end lldb --batch remote debugging session via lldb-server
  platform mode running on qemu

Supporting changes:
- _write_bb_config: accept optional extra_packages parameter so the
  clang test can add lldb-server to IMAGE_INSTALL
- _verify_launch_json_lldb: new helper that validates the CodeLLDB
  launch.json structure (type, initCommands, program, cwd, preLaunchTask)
- _lldb_server_debugging_once: new helper that reads the preLaunchTask
  SSH command from tasks.json, starts lldb-server on the target, and
  runs lldb --batch to verify a breakpoint at main is hit
- _verify_service_running: use pgrep with exact regex (^name$) for exact
  process name matching; without that, pgrep would also match
  cmake-example-clang (truncated to 'cmake-example-c' in
  /proc/pid/comm) when checking for cmake-example, returning two PIDs
  and failing the isdigit() assertion

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/lib/oeqa/selftest/cases/devtool.py | 196 +++++++++++++++++++++++-
 1 file changed, 192 insertions(+), 4 deletions(-)

diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py
index 6c6f22a667..2cd03e68d6 100644
--- a/meta/lib/oeqa/selftest/cases/devtool.py
+++ b/meta/lib/oeqa/selftest/cases/devtool.py
@@ -2590,13 +2590,15 @@ class DevtoolIdeSdkTests(DevtoolBase):
         if self.logger.isEnabledFor(logging.DEBUG):
             self._cmd_logger = self.logger
 
-    def _write_bb_config(self, recipe_names):
+    def _write_bb_config(self, recipe_names, extra_packages=None):
         """Helper to write the bitbake local.conf file"""
+        image_install = 'gdbserver ' + ' '.join([r + '-ptest' for r in recipe_names])
+        if extra_packages:
+            image_install += ' ' + ' '.join(extra_packages)
         conf_lines = [
             'IMAGE_CLASSES += "image-combined-dbg"',
             'IMAGE_GEN_DEBUGFS = "1"',
-            'IMAGE_INSTALL:append = " gdbserver %s"' % ' '.join(
-                [r + '-ptest' for r in recipe_names]),
+            'IMAGE_INSTALL:append = " %s"' % image_install,
             'DISTRO_FEATURES:append = " ptest"'
             # Static UIDs/GIDs are required so that files installed via
             # "install -o ${BPN}" in do_install embed the same UID that gets
@@ -2938,7 +2940,9 @@ class DevtoolIdeSdkTests(DevtoolBase):
 
     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)
+        # Use anchored regex (^name$) instead of pgrep -x because the target
+        # may have busybox pgrep which does not support the -x flag.
+        status, output = qemu.run("pgrep '^%s$'" % service_name)
         self.assertEqual(status, 0, msg="%s service not running: %s" %
                          (service_name, output))
         self.assertTrue(output.strip().isdigit(),
@@ -3612,6 +3616,190 @@ class DevtoolIdeSdkTests(DevtoolBase):
         runCmdEnv('meson setup %s' % tempdir_meson, cwd=cpp_example_src, output_log=self._cmd_logger)
         runCmdEnv('meson compile', cwd=tempdir_meson, output_log=self._cmd_logger)
 
+    def _verify_launch_json_lldb(self, tempdir):
+        """Verify the launch.json file contains valid CodeLLDB (type: lldb) configurations."""
+        launch_json_path = os.path.join(tempdir, '.vscode', 'launch.json')
+        self.assertTrue(os.path.exists(launch_json_path), "launch.json file should exist")
+
+        with open(launch_json_path) as launch_j:
+            launch_d = json.load(launch_j)
+
+        self.assertIn("configurations", launch_d)
+        configurations = launch_d["configurations"]
+        self.assertGreater(len(configurations), 0,
+                           "Should have at least one debug configuration")
+
+        for config in configurations:
+            config_name = config.get("name", "Unknown")
+            # CodeLLDB configs use "type": "lldb", not "type": "cppdbg"
+            self.assertEqual(config["type"], "lldb",
+                             f"Configuration '{config_name}' should use lldb type (CodeLLDB)")
+            self.assertNotIn("MIMode", config,
+                             f"Configuration '{config_name}' should not have MIMode (CodeLLDB)")
+            self.assertNotIn("miDebuggerPath", config,
+                             f"Configuration '{config_name}' should not have miDebuggerPath")
+            self.assertEqual(config["request"], "launch",
+                             f"Configuration '{config_name}' should be launch type")
+            self.assertEqual(config["cwd"], "/tmp",
+                             f"Configuration '{config_name}' cwd should be /tmp (writable on target)")
+
+            # Verify initCommands contain the platform connect sequence
+            init_commands = config.get("initCommands", [])
+            self.assertTrue(any("platform select remote-linux" in cmd
+                                for cmd in init_commands),
+                            f"Configuration '{config_name}' should select remote-linux platform")
+            self.assertTrue(any("platform connect" in cmd for cmd in init_commands),
+                            f"Configuration '{config_name}' should connect to remote platform")
+
+            # Verify program path points into the image directory
+            program = config.get("program", "")
+            self.assertTrue(program.startswith("/"),
+                            f"Configuration '{config_name}' program should be an absolute path")
+            self.assertIn("/image/", program,
+                          f"Configuration '{config_name}' program should be in image directory")
+
+            # Verify preLaunchTask referencing the lldb-server start task
+            task = config.get("preLaunchTask", "")
+            self.assertTrue(task,
+                            f"Configuration '{config_name}' preLaunchTask should not be empty")
+
+    def _lldb_server_debugging_once(self, tempdir, qemu, recipe_name, magic_string):
+        """Verify lldb-server (platform mode) + lldb batch debugging works end-to-end.
+
+        Reads the preLaunchTask SSH command from tasks.json to start lldb-server
+        on the target, then runs lldb --batch to perform a minimal debugging
+        session and checks that the expected magic string is visible.
+        """
+        sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
+
+        with open(os.path.join(tempdir, '.vscode', 'launch.json')) as f:
+            launch_d = json.load(f)
+        with open(os.path.join(tempdir, '.vscode', 'tasks.json')) as f:
+            tasks_d = json.load(f)
+
+        # Find the first *_once or *_multi config
+        lldb_config = next(
+            (c for c in launch_d["configurations"]
+             if "_once" in c["name"] or "_multi" in c["name"]), None)
+        self.assertIsNotNone(lldb_config, "Should have at least one lldb debug configuration")
+
+        prelaunch_task_name = lldb_config["preLaunchTask"]
+        prelaunch_task = next(
+            (t for t in tasks_d["tasks"] if t["label"] == prelaunch_task_name), None)
+        self.assertIsNotNone(prelaunch_task,
+                             "preLaunchTask '%s' not found in tasks.json" % prelaunch_task_name)
+
+        # Extract the SSH command and start lldb-server on the target
+        task_command = prelaunch_task["command"]
+        task_args = prelaunch_task["args"]
+        self.assertEqual(task_command, "ssh",
+                         "preLaunchTask should use ssh to start lldb-server")
+        ssh_cmd = [task_command] + task_args
+        if ssh_cmd[-1].startswith('"') and ssh_cmd[-1].endswith('"'):
+            ssh_cmd[-1] = ssh_cmd[-1][1:-1]
+
+        # Extract connection details from initCommands
+        init_commands = lldb_config["initCommands"]
+        connect_cmd = next((c for c in init_commands if "platform connect" in c), None)
+        self.assertIsNotNone(connect_cmd, "initCommands should contain a platform connect command")
+
+        # Find lldb binary from lldb-native sysroot
+        lldb_native_sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'lldb-native')
+        lldb_binary = os.path.join(lldb_native_sysroot, 'usr', 'bin', 'lldb')
+        self.assertExists(lldb_binary, "lldb binary should exist in lldb-native sysroot")
+
+        with RunCmdBackground(ssh_cmd, output_log=self._cmd_logger):
+            time.sleep(1)
+
+            # Verify lldb-server is running on the target
+            r = runCmd('ssh %s root@%s ps' % (sshargs, qemu.ip),
+                       output_log=self._cmd_logger)
+            self.assertIn("lldb-server", r.output,
+                          "lldb-server should be running on target")
+
+            # Run lldb --batch: connect to platform, set a breakpoint on main, run
+            program = lldb_config["program"]
+            source_map = lldb_config.get("sourceMap", {})
+
+            pre_run_commands = lldb_config.get("preRunCommands", [])
+
+            lldb_batch = [lldb_binary, "--batch"]
+            for cmd in init_commands:
+                lldb_batch += ["-o", cmd]
+            lldb_batch += ["-o", "target create %s" % program]
+            for k, v in source_map.items():
+                v_resolved = v.replace("${workspaceFolder}", tempdir)
+                lldb_batch += ["-o", "settings set target.source-map %s %s" % (k, v_resolved)]
+            for cmd in pre_run_commands:
+                lldb_batch += ["-o", cmd]
+            lldb_batch += [
+                "-o", "b main",
+                "-o", "run",
+                "-o", "continue",
+                "-o", "exit"
+            ]
+            r = runCmd(lldb_batch, output_log=self._cmd_logger)
+            self.assertEqual(r.status, 0, "lldb batch session failed: %s" % r.output)
+            self.assertIn("stop reason = breakpoint", r.output,
+                          "lldb should have stopped at main breakpoint")
+
+    @OETestTag("runqemu")
+    def test_devtool_ide_sdk_code_cmake_clang(self):
+        """Verify a cmake recipe built with clang works with ide=code (CodeLLDB debugging).
+
+        This test uses the cmake-example-clang recipe which is a cmake-example variant
+        built with clang. It installs a separate binary (cmake-example-clang) so all four
+        recipe variants (cmake/meson x gcc/clang) can be installed in the same image
+        without conflicts. It is configured to use lldb-server for debugging instead of
+        gdbserver. The test flow is similar to test_devtool_ide_sdk_code_cmake but with
+        additional checks related to lldb:
+        - devtool ide-sdk selects lldb-native / lldb-server instead of gdb-cross
+        - launch.json uses "type": "lldb" (CodeLLDB) instead of "type": "cppdbg"
+        - extensions.json recommends vadimcn.vscode-lldb
+        - A basic lldb --batch remote debugging session succeeds against the
+          lldb-server platform running on the Qemu target
+        """
+        recipe_name = "cmake-example-clang"
+        build_file = "CMakeLists.txt"
+        testimage = "oe-selftest-image"
+
+        self._check_workspace()
+        self._write_bb_config([recipe_name], extra_packages=['lldb-server'])
+
+        self._check_runqemu_prerequisites()
+        bitbake(testimage)
+        with runqemu(testimage, runqemuparams="nographic") as qemu:
+            tempdir = self._devtool_ide_sdk_recipe(recipe_name, build_file, testimage)
+            bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@%s -c --ide=code' % (
+                recipe_name, testimage, qemu.ip)
+            runCmd(bitbake_sdk_cmd, output_log=self._cmd_logger)
+
+            # Verify the cmake preset still works (build system unchanged)
+            compile_cmd = self._verify_cmake_preset(tempdir)
+
+            # Verify the install && deploy-target task script exists
+            self._verify_install_script_code(tempdir, recipe_name)
+
+            # Verify extensions.json recommends CodeLLDB instead of / alongside cpptools
+            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('vadimcn.vscode-lldb', recommendations,
+                          'vadimcn.vscode-lldb should be recommended for clang recipes')
+
+            # Verify launch.json uses CodeLLDB format
+            self._verify_launch_json_lldb(tempdir)
+
+            # Verify deployment and lldb batch remote debugging work end-to-end
+            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)
+            runCmd(install_deploy_cmd, output_log=self._cmd_logger)
+
+            self._lldb_server_debugging_once(tempdir, qemu, recipe_name,
+                                             DevtoolIdeSdkTests.MAGIC_STRING_ORIG)
+
     def test_devtool_ide_sdk_plugins(self):
         """Test that devtool ide-sdk can use plugins from other layers."""
 
-- 
2.53.0



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

* Re: [OE-core] [PATCH 9/9] oe-selftest: devtool ide-sdk: add clang/LLDB test
  2026-03-18 22:36 ` [PATCH 9/9] oe-selftest: devtool ide-sdk: add clang/LLDB test AdrianF
@ 2026-03-20  7:12   ` Mathieu Dubois-Briand
  2026-03-20  9:11     ` adrian.freihofer
  0 siblings, 1 reply; 13+ messages in thread
From: Mathieu Dubois-Briand @ 2026-03-20  7:12 UTC (permalink / raw)
  To: adrian.freihofer, openembedded-core

On Wed Mar 18, 2026 at 11:36 PM CET, Adrian Freihofer via lists.openembedded.org wrote:
> From: Adrian Freihofer <adrian.freihofer@siemens.com>
>
> Add test_devtool_ide_sdk_code_cmake_clang to verify the full devtool
> ide-sdk workflow for a cmake recipe built with clang.  Unlike the gcc
> variant the clang recipe uses lldb-server for remote debugging and
> CodeLLDB (vadimcn.vscode-lldb) as the VS Code debug adapter.
>
> The test covers:
> - devtool modify + devtool ide-sdk with ide=code
> - cmake preset compilation and CTest execution (same as the gcc test)
> - extensions.json recommends vadimcn.vscode-lldb
> - launch.json uses "type": "lldb" (CodeLLDB) instead of "type": "cppdbg"
> - End-to-end lldb --batch remote debugging session via lldb-server
>   platform mode running on qemu
>
> Supporting changes:
> - _write_bb_config: accept optional extra_packages parameter so the
>   clang test can add lldb-server to IMAGE_INSTALL
> - _verify_launch_json_lldb: new helper that validates the CodeLLDB
>   launch.json structure (type, initCommands, program, cwd, preLaunchTask)
> - _lldb_server_debugging_once: new helper that reads the preLaunchTask
>   SSH command from tasks.json, starts lldb-server on the target, and
>   runs lldb --batch to verify a breakpoint at main is hit
> - _verify_service_running: use pgrep with exact regex (^name$) for exact
>   process name matching; without that, pgrep would also match
>   cmake-example-clang (truncated to 'cmake-example-c' in
>   /proc/pid/comm) when checking for cmake-example, returning two PIDs
>   and failing the isdigit() assertion
>
> Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
> ---

Hi Adrian,

Thanks for your patch.

It looks like the added test_devtool_ide_sdk_code_cmake_clang test is
failing on the autobuilder:

2026-03-19 19:34:48,650 - oe-selftest - INFO - devtool.DevtoolIdeSdkTests.test_devtool_ide_sdk_code_cmake_clang (subunit.RemotedTestCase)
2026-03-19 19:34:48,651 - oe-selftest - INFO -  ... FAIL
...
2026-03-19 19:34:48,651 - oe-selftest - INFO - 11: 3/39 214/681 (495.87s) (0 failed) (devtool.DevtoolIdeSdkTests.test_devtool_ide_sdk_code_cmake_clang)
2026-03-19 19:34:48,651 - oe-selftest - INFO - testtools.testresult.real._StringException: Traceback (most recent call last):
  File "/srv/pokybuild/yocto-worker/oe-selftest-armhost/build/layers/openembedded-core/meta/lib/oeqa/selftest/cases/devtool.py", line 3862, in test_devtool_ide_sdk_code_cmake_clang
    runCmd(install_deploy_cmd, output_log=self._cmd_logger)
  File "/srv/pokybuild/yocto-worker/oe-selftest-armhost/build/layers/openembedded-core/meta/lib/oeqa/utils/commands.py", line 214, in runCmd
    raise AssertionError("Command '%s' returned non-zero exit status %d:\n%s" % (command, result.status, exc_output))
AssertionError: Command '/srv/pokybuild/yocto-worker/oe-selftest-armhost/build/build-st-273901/workspace/ide-sdk/cmake-example-clang/scripts/install_and_deploy_cmake-example-clang-cortexa57' returned non-zero exit status 1:

https://autobuilder.yoctoproject.org/valkyrie/#/builders/23/builds/3579
https://autobuilder.yoctoproject.org/valkyrie/#/builders/35/builds/3474
https://autobuilder.yoctoproject.org/valkyrie/#/builders/48/builds/3360

Can you have a look at the issue?

Thanks,
Mathieu

-- 
Mathieu Dubois-Briand, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com



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

* Re: [OE-core] [PATCH 9/9] oe-selftest: devtool ide-sdk: add clang/LLDB test
  2026-03-20  7:12   ` [OE-core] " Mathieu Dubois-Briand
@ 2026-03-20  9:11     ` adrian.freihofer
  2026-03-20 10:18       ` Mathieu Dubois-Briand
  0 siblings, 1 reply; 13+ messages in thread
From: adrian.freihofer @ 2026-03-20  9:11 UTC (permalink / raw)
  To: mathieu.dubois-briand, adrian.freihofer, openembedded-core

On Fri, 2026-03-20 at 08:12 +0100, Mathieu Dubois-Briand via
lists.openembedded.org wrote:
> On Wed Mar 18, 2026 at 11:36 PM CET, Adrian Freihofer via
> lists.openembedded.org wrote:
> > From: Adrian Freihofer <adrian.freihofer@siemens.com>
> > 
> > Add test_devtool_ide_sdk_code_cmake_clang to verify the full
> > devtool
> > ide-sdk workflow for a cmake recipe built with clang.  Unlike the
> > gcc
> > variant the clang recipe uses lldb-server for remote debugging and
> > CodeLLDB (vadimcn.vscode-lldb) as the VS Code debug adapter.
> > 
> > The test covers:
> > - devtool modify + devtool ide-sdk with ide=code
> > - cmake preset compilation and CTest execution (same as the gcc
> > test)
> > - extensions.json recommends vadimcn.vscode-lldb
> > - launch.json uses "type": "lldb" (CodeLLDB) instead of "type":
> > "cppdbg"
> > - End-to-end lldb --batch remote debugging session via lldb-server
> >   platform mode running on qemu
> > 
> > Supporting changes:
> > - _write_bb_config: accept optional extra_packages parameter so the
> >   clang test can add lldb-server to IMAGE_INSTALL
> > - _verify_launch_json_lldb: new helper that validates the CodeLLDB
> >   launch.json structure (type, initCommands, program, cwd,
> > preLaunchTask)
> > - _lldb_server_debugging_once: new helper that reads the
> > preLaunchTask
> >   SSH command from tasks.json, starts lldb-server on the target,
> > and
> >   runs lldb --batch to verify a breakpoint at main is hit
> > - _verify_service_running: use pgrep with exact regex (^name$) for
> > exact
> >   process name matching; without that, pgrep would also match
> >   cmake-example-clang (truncated to 'cmake-example-c' in
> >   /proc/pid/comm) when checking for cmake-example, returning two
> > PIDs
> >   and failing the isdigit() assertion
> > 
> > Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
> > ---
> 
> Hi Adrian,
> 
> Thanks for your patch.
> 
> It looks like the added test_devtool_ide_sdk_code_cmake_clang test is
> failing on the autobuilder:
> 
> 2026-03-19 19:34:48,650 - oe-selftest - INFO -
> devtool.DevtoolIdeSdkTests.test_devtool_ide_sdk_code_cmake_clang
> (subunit.RemotedTestCase)
> 2026-03-19 19:34:48,651 - oe-selftest - INFO -  ... FAIL
> ...
> 2026-03-19 19:34:48,651 - oe-selftest - INFO - 11: 3/39 214/681
> (495.87s) (0 failed)
> (devtool.DevtoolIdeSdkTests.test_devtool_ide_sdk_code_cmake_clang)
> 2026-03-19 19:34:48,651 - oe-selftest - INFO -
> testtools.testresult.real._StringException: Traceback (most recent
> call last):
>   File "/srv/pokybuild/yocto-worker/oe-selftest-
> armhost/build/layers/openembedded-
> core/meta/lib/oeqa/selftest/cases/devtool.py", line 3862, in
> test_devtool_ide_sdk_code_cmake_clang
>     runCmd(install_deploy_cmd, output_log=self._cmd_logger)
>   File "/srv/pokybuild/yocto-worker/oe-selftest-
> armhost/build/layers/openembedded-
> core/meta/lib/oeqa/utils/commands.py", line 214, in runCmd
>     raise AssertionError("Command '%s' returned non-zero exit status
> %d:\n%s" % (command, result.status, exc_output))
> AssertionError: Command '/srv/pokybuild/yocto-worker/oe-selftest-
> armhost/build/build-st-273901/workspace/ide-sdk/cmake-example-
> clang/scripts/install_and_deploy_cmake-example-clang-cortexa57'
> returned non-zero exit status 1:
> 
> https://autobuilder.yoctoproject.org/valkyrie/#/builders/23/builds/3579
> https://autobuilder.yoctoproject.org/valkyrie/#/builders/35/builds/3474
> https://autobuilder.yoctoproject.org/valkyrie/#/builders/48/builds/3360
> 
> Can you have a look at the issue?
> 

Thank you for the feedback.

Looks like the summary is: Pseudo crashes on ARM hosts (but not on x86-
64 hosts).

Is it possible to get this file
/srv/pokybuild/yocto-worker/oe-selftest-armhost/build/build-st-
273901/tmp/work/cortexa57-poky-linux/cmake-example-
clang/1.0/pseudo//pseudo.log ?


Relevant section from the logs is:

Summary: There was 1 WARNING message.
abort()ing pseudo client by server request. See
https://wiki.yoctoproject.org/wiki/Pseudo_Abort for more details on
this.
Check logfile: /srv/pokybuild/yocto-worker/oe-selftest-
armhost/build/build-st-273901/tmp/work/cortexa57-poky-linux/cmake-
example-clang/1.0/pseudo//pseudo.log
Aborted (core dumped)
tar: Unexpected EOF in archive
tar: Unexpected EOF in archive
tar: Error is not recoverable: exiting now
Traceback (most recent call last):
  File "/srv/pokybuild/yocto-worker/oe-selftest-armhost/build/build-st-
273901/workspace/ide-sdk/cmake-example-
clang/scripts/deploy_target_cmake-example-clang-cortexa57", line 19, in
<module>
    deploy_no_d("/srv/pokybuild/yocto-worker/oe-selftest-
armhost/build/build-st-273901/tmp/work/cortexa57-poky-linux/cmake-
example-clang/1.0/image", "/srv/pokybuild/yocto-worker/oe-selftest-
armhost/build/build-st-273901/tmp/work/cortexa57-poky-linux/cmake-
example-clang/1.0", "/srv/pokybuild/yocto-worker/oe-selftest-
armhost/build/build-st-273901/tmp/sysroots-uninative/aarch64-
linux/usr/bin:/tmp/devtoolqalpx2fc25/core-
copy/scripts:/srv/pokybuild/yocto-worker/oe-selftest-
armhost/build/build-st-273901/tmp/work/cortexa57-poky-linux/cmake-
example-clang/1.0/recipe-sysroot-native/usr/bin/aarch64-poky-
linux:/srv/pokybuild/yocto-worker/oe-selftest-armhost/build/build-st-
273901/tmp/work/cortexa57-poky-linux/cmake-example-clang/1.0/recipe-
sysroot/usr/bin/crossscripts:/srv/pokybuild/yocto-worker/oe-selftest-
armhost/build/build-st-273901/tmp/work/cortexa57-poky-linux/cmake-
example-clang/1.0/recipe-sysroot-native/usr/sbin:/srv/pokybuild/yocto-
worker/oe-selftest-armhost/build/build-st-273901/tmp/work/cortexa57-
poky-linux/cmake-example-clang/1.0/recipe-sysroot-
native/usr/bin:/srv/pokybuild/yocto-worker/oe-selftest-
armhost/build/build-st-273901/tmp/work/cortexa57-poky-linux/cmake-
example-clang/1.0/recipe-sysroot-native/sbin:/srv/pokybuild/yocto-
worker/oe-selftest-armhost/build/build-st-273901/tmp/work/cortexa57-
poky-linux/cmake-example-clang/1.0/recipe-sysroot-
native/bin:/srv/pokybuild/yocto-worker/oe-selftest-
armhost/build/layers/bitbake/bin:/srv/pokybuild/yocto-worker/oe-
selftest-armhost/build/build-st-273901/tmp/hosttools", "aarch64-poky-
linux-llvm-strip", "/usr/lib", "/lib", 16, "/srv/pokybuild/yocto-
worker/oe-selftest-armhost/build/build-st-273901/tmp/sysroots-
components/aarch64/pseudo-native/usr/bin/pseudo",
"PSEUDO_PREFIX=/srv/pokybuild/yocto-worker/oe-selftest-
armhost/build/build-st-273901/tmp/sysroots-components/aarch64/pseudo-
native/usr PSEUDO_LOCALSTATEDIR=/srv/pokybuild/yocto-worker/oe-
selftest-armhost/build/build-st-273901/tmp/work/cortexa57-poky-
linux/cmake-example-clang/1.0/pseudo/
PSEUDO_PASSWD=/srv/pokybuild/yocto-worker/oe-selftest-
armhost/build/build-st-273901/tmp/work/cortexa57-poky-linux/cmake-
example-clang/1.0/recipe-sysroot:/srv/pokybuild/yocto-worker/oe-
selftest-armhost/build/build-st-273901/tmp/sysroots-
components/aarch64/pseudo-native PSEUDO_NOSYMLINKEXP=1
PSEUDO_INCLUDE_PATHS=/proc,/srv/pokybuild/yocto-worker/oe-selftest-
armhost/build/build-st-273901/tmp/work/cortexa57-poky-linux/cmake-
example-clang/1.0/image,/srv/pokybuild/yocto-worker/oe-selftest-
armhost/build/build-st-273901/tmp/work/cortexa57-poky-linux/cmake-
example-clang/1.0/package,/srv/pokybuild/yocto-worker/oe-selftest-
armhost/build/build-st-273901/tmp/work/cortexa57-poky-linux/cmake-
example-clang/1.0/rootfs,/srv/pokybuild/yocto-worker/oe-selftest-
armhost/build/build-st-273901/tmp/work/cortexa57-poky-linux/cmake-
example-clang/1.0/sstate-build-package/,/srv/pokybuild/yocto-worker/oe-
selftest-armhost/build/build-st-273901/tmp/work/cortexa57-poky-
linux/cmake-example-clang/1.0/sstate-install-
package/,/srv/pokybuild/yocto-worker/oe-selftest-armhost/build/build-
st-273901/tmp/work/cortexa57-poky-linux/cmake-example-
clang/1.0/pkgdata,/srv/pokybuild/yocto-worker/oe-selftest-
armhost/build/build-st-273901/tmp/work/cortexa57-poky-linux/cmake-
example-clang/1.0/minidebuginfo,/srv/pokybuild/yocto-worker/oe-
selftest-armhost/build/build-st-273901/tmp/work/cortexa57-poky-
linux/cmake-example-clang/1.0/devtool-deploy-target-stripped
PSEUDO_DISABLED=0", filtered_args)
  File "/srv/pokybuild/yocto-worker/oe-selftest-
armhost/build/layers/openembedded-core/scripts/lib/devtool/deploy.py",
line 274, in deploy_no_d
    raise DevtoolError('Deploy failed - rerun with -s to get a complete
'
devtool.DevtoolError: Deploy failed - rerun with -s to get a complete
error message


Thanks,
Adrian



> Thanks,
> Mathieu
> 
> 
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#233580):
> https://lists.openembedded.org/g/openembedded-core/message/233580
> Mute This Topic: https://lists.openembedded.org/mt/118391915/4454582
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe:
> https://lists.openembedded.org/g/openembedded-core/unsub [
> adrian.freihofer@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-


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

* Re: [OE-core] [PATCH 9/9] oe-selftest: devtool ide-sdk: add clang/LLDB test
  2026-03-20  9:11     ` adrian.freihofer
@ 2026-03-20 10:18       ` Mathieu Dubois-Briand
  0 siblings, 0 replies; 13+ messages in thread
From: Mathieu Dubois-Briand @ 2026-03-20 10:18 UTC (permalink / raw)
  To: adrian.freihofer, adrian.freihofer, openembedded-core

On Fri Mar 20, 2026 at 10:11 AM CET, Adrian Freihofer via lists.openembedded.org wrote:
> On Fri, 2026-03-20 at 08:12 +0100, Mathieu Dubois-Briand via
>> 
>> https://autobuilder.yoctoproject.org/valkyrie/#/builders/23/builds/3579
>> https://autobuilder.yoctoproject.org/valkyrie/#/builders/35/builds/3474
>> https://autobuilder.yoctoproject.org/valkyrie/#/builders/48/builds/3360
>> 
>> Can you have a look at the issue?
>> 
>
> Thank you for the feedback.
>
> Looks like the summary is: Pseudo crashes on ARM hosts (but not on x86-
> 64 hosts).
>

Hi Adrian,

Just to be sure: the second and third links above point to two x86-64
builds.

Thanks,
Mathieu

-- 
Mathieu Dubois-Briand, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com



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

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

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-18 22:36 [PATCH 0/9] devtool: ide-sdk clang/LLDB support and minor fixes AdrianF
2026-03-18 22:36 ` [PATCH 1/9] oe-selftest: devtool: use stat for reading user/group names in ide-sdk tests AdrianF
2026-03-18 22:36 ` [PATCH 2/9] oe-selftest: devtool: GDB breakpoint after std::vector is constructed AdrianF
2026-03-18 22:36 ` [PATCH 3/9] oe-selftest: devtool: use assertRegex to match test output for meson AdrianF
2026-03-18 22:36 ` [PATCH 4/9] oe-selftest/cpp-example: fix conf file ownership with static UIDs/GIDs AdrianF
2026-03-18 22:36 ` [PATCH 5/9] devtool: ide-sdk: use TOOLCHAIN not TCOVERRIDE AdrianF
2026-03-18 22:36 ` [PATCH 6/9] devtool: ide-sdk debugger back-end abstraction AdrianF
2026-03-18 22:36 ` [PATCH 7/9] devtool: ide-sdk add LLDB support for clang toolchain AdrianF
2026-03-18 22:36 ` [PATCH 8/9] meta-selftest: refactor cpp examples into .inc files and add clang variants AdrianF
2026-03-18 22:36 ` [PATCH 9/9] oe-selftest: devtool ide-sdk: add clang/LLDB test AdrianF
2026-03-20  7:12   ` [OE-core] " Mathieu Dubois-Briand
2026-03-20  9:11     ` adrian.freihofer
2026-03-20 10:18       ` Mathieu Dubois-Briand

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