devtool: ide-sdk: add support for Rust recipes
v2 cover devtool: ide-sdk: add support for Rust recipes
Hello, this series introduces support for Rust ide-sdk VS Code configuration against the Yocto cross-compilation toolchain:
- editor symbol resolution and linting
- build task
- deploy task
- debugging
It introduces a cargo-example recipe following the model used for cmake and meson.
The generated configurations can be portable to standalone SDK installations by manually updating the paths.
C/C++ IntelliSense could provide navigation for native code compiled by Cargo build scripts, using the recipe compiler and sysroot. This first series defers it because no mixed-language fixture is available to validate that workflow.
BitBake generates the Cargo configuration consumed by ide-sdk. This series updates the parsed configuration and writes its supported TOML values without adding a new dependency.
I reproduced the selftest coverage expected from other recipe classes, including configuration analysis up to actual debugging symbol resolution.
I largely used AI generation to write this stack. After many iterations, I am finally happy with the code and commit history quality of this series. I also have a documentation commit ready for the manual if this gets approved.
Any feedback is appreciated. Hope this will help lots of Rust developers working with Yocto distros :).
Cheers,
Enguerrand de Ribaucourt
---
Changes in v2:
- Replace Cargo config in-place update with a simple TOML serializer.
- Add Cargo config parser coverage.
- Generate Cargo config for all IDE modes.
- Route ide=none Cargo debugging through LLDB and explain GDB limitations.
- Configure the rustc problems matcher.
- Map Rust sources with STAGING_LIBDIR_NATIVE.
- Discard C/C++ IntelliSense mixed-language feature.
- Fix bbappend always being modified on re-runs
- Fix '=' ordering in remap
- Fix install task duplication
- Link to v1: https://patch.msgid.link/20261026-devtool-ide-sdk-cargo-v1-0-d4504e124d07@savoirfairelinux.com
Thank you very much for the detailed review Adrian, it's been really helpful!
---
Enguerrand de Ribaucourt (18):
cargo-example: add minimal selftest fixture
devtool: ide-sdk: capture Cargo cross environment
devtool: ide-sdk: export recipe Cargo configuration oeqa/selftest: verify Cargo config serialization
devtool: ide-sdk: recommend Rust Analyzer for Cargo
devtool: ide-sdk: configure Rust Analyzer for Cargo
devtool: ide-sdk: generalize binary install task name
devtool: ide-sdk: extract install task definition
devtool: ide-sdk: order install after optional build task
devtool: ide-sdk: add Cargo build task
devtool: ide-sdk: enforce LLDB for Cargo
devtool: ide-sdk: remap Cargo sources for BitBake builds
devtool: ide-sdk: map Cargo workspace sources for LLDB
devtool: ide-sdk: map libstd-rs sources to Rust SDK oeqa/selftest: share source breakpoint lookup oeqa/selftest: run IDE tasks with configured environment oeqa/selftest: debug Cargo example over QEMU slirp oeqa/selftest: verify Cargo rebuild and redeploy
meta-selftest/recipes-test/rust/cargo-example.bb | 15 + .../recipes-test/rust/cargo-example/Cargo.lock | 5 + .../recipes-test/rust/cargo-example/Cargo.toml | 8 + .../recipes-test/rust/cargo-example/build.rs | 20 ++ .../recipes-test/rust/cargo-example/src/main.rs | 11 + meta/lib/oeqa/selftest/cases/devtool.py | 347 ++++++++++++++++++++- scripts/lib/devtool/ide_plugins/__init__.py | 1 + scripts/lib/devtool/ide_plugins/ide_code.py | 83 ++++- scripts/lib/devtool/ide_plugins/ide_none.py | 4 +- scripts/lib/devtool/ide_sdk.py | 249 +++++++++++++-- 10 files changed, 688 insertions(+), 55 deletions(-)
---
base-commit: 39b27e524f3c092e5018cbed23ddf1829390ae65
change-id: 20261022-devtool-ide-sdk-cargo-d003158ae2f1
Best regards,
Signature · 1 line
Enguerrand de Ribaucourt <[email protected]>
PATCH v2 1/18 cargo-example: add minimal selftest fixture
Add a minimal Cargo recipe to demonstrate the ide-sdk configuration for Rust in the selftests.
AI-Generated: Uses GitHub Copilot
--- meta-selftest/recipes-test/rust/cargo-example.bb | 15 +++++++++++++++ .../recipes-test/rust/cargo-example/Cargo.lock | 5 +++++ .../recipes-test/rust/cargo-example/Cargo.toml | 8 ++++++++ .../recipes-test/rust/cargo-example/build.rs | 20 ++++++++++++++++++++ .../recipes-test/rust/cargo-example/src/main.rs | 11 +++++++++++ 5 files changed, 59 insertions(+)
meta-selftest/recipes-test/rust/cargo-example.bb new file +15 -0
@@ -0,0 +1,15 @@ +SUMMARY = "Minimal Cargo application for devtool ide-sdk selftests" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" + +SRC_URI = "file://Cargo.toml \ + file://Cargo.lock \ + file://build.rs \ + file://src/main.rs \ + " + +S = "${UNPACKDIR}" + +inherit cargo + +CARGO_BUILD_FLAGS:append = " --features ide-sdk-selftest"
meta-selftest/recipes-test/rust/cargo-example/Cargo.lock new file +5 -0
@@ -0,0 +1,5 @@ +version = 3 + +[[package]] +name = "cargo-example" +version = "0.1.0"
meta-selftest/recipes-test/rust/cargo-example/Cargo.toml new file +8 -0
@@ -0,0 +1,8 @@ +[package] +name = "cargo-example" +version = "0.1.0" +edition = "2021" + +[features] +# Used only to verify that ide-sdk forwards recipe Cargo features to its task. +ide-sdk-selftest = []
meta-selftest/recipes-test/rust/cargo-example/build.rs new file +20 -0
@@ -0,0 +1,20 @@ +use std::env; + +// Fail the selftest build if Cargo does not receive BitBake's cross environment. +fn main () { +let target_cc = env::var ("CC" ).expect ("Cargo config should set target CC" ); +assert! (target_cc.ends_with ("/target-rust-cc" ),"unexpected CC: {target_cc}" ); + +let host_cc = env::var ("HOST_CC" ).expect ("Cargo config should set host CC" ); +assert! (host_cc.ends_with ("/build-rust-cc" ),"unexpected HOST_CC: {host_cc}" ); + +assert! ( + env::var_os ("RUST_TARGET_PATH" ).is_some (), +"Cargo config should set the OE Rust target path" , + ); +assert_eq! ( + env::var ("PKG_CONFIG_ALLOW_CROSS" ).as_deref (), +Ok ("1" ), +"Cargo config should allow cross pkg-config" , + ); +}
meta-selftest/recipes-test/rust/cargo-example/src/main.rs new file +11 -0
@@ -0,0 +1,11 @@ +fn fibonacci (n:u32 ) ->u32 { +match n { +0 =>0 , +1 =>1 , + _ =>fibonacci (n -1 ) +fibonacci (n -2 ), + } +} + +fn main () { +println! ("fibonacci={}" ,fibonacci (4 )); +}
Signature · 1 line
2.53.0
PATCH v2 2/18 devtool: ide-sdk: capture Cargo cross environment
In reply to Enguerrand de RibaucourtCapture Cargo target and host compiler settings, recipe Cargo state, and package configuration to generate IDE settings. Rust source paths use Cargo RUSTFLAGS and TARGET_DBGSRC_DIR because Rust does not use compiler DEBUG_PREFIX_MAP.
Mirror the -sys and pkg-config exports from cargo_common.bbclass so Cargo config matches OE's system-library behavior.
AI-Generated: Uses GitHub Copilot
--- scripts/lib/devtool/ide_plugins/__init__.py | 1 + scripts/lib/devtool/ide_sdk.py | 57 +++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+)
scripts/lib/devtool/ide_plugins/__init__.py +1 -0
@@ -33,6 +33,7 @@ class BuildTool(Enum): UNDEFINED = auto() CMAKE = auto() MESON = auto() + CARGO = auto() KERNEL_MODULE = auto() @property
scripts/lib/devtool/ide_sdk.py +57 -0
69 lines not on this page · raw message
Signature · 1 line
2.53.0
PATCH v2 3/18 devtool: ide-sdk: export recipe Cargo configuration
In reply to Enguerrand de RibaucourtExport recipe Cargo config to the devtool workspace so Cargo and rust-analyzer reuse target, linker, rustflags, and dependency settings instead of duplicating them in settings.json and tasks.json. Other IDE integrations can reuse the generated config too.
The original input is generated by cargo_common.bbclass. Updating it in-place would require a Python TOML writer dependency, so regenerate it with a bounded writer helper.
AI-Generated: Uses GitHub Copilot
---
Changes in v2:
- Replace in-place Cargo config updates with a helper that rewrites the full file.
- Generate Cargo config for all IDE modes.
--- scripts/lib/devtool/ide_sdk.py | 102 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+)
scripts/lib/devtool/ide_sdk.py +102 -0
@@ -22,6 +22,7 @@ from pathlib import Path import scriptutils import bb +from bb._vendor import tomli from devtool import exec_build_env_command, setup_tinfoil, check_workspace_recipe, DevtoolError, parse_recipe from devtool.standard import get_real_srctree from devtool.deploy import parse_packages_arg
@@ -787,6 +788,41 @@ class ExecutableBinary: return None +def _toml_dumps(config): + """Serialize TOML values and tables.""" + def format_value(value): + if isinstance(value, str): + return json.dumps(value) + if isinstance(value, bool): + return str(value).lower() + if isinstance(value, (int, float)): + return repr(value) + if isinstance(value, list): + return '[%s]' % ', '.join(format_value(item) for item in value) + if isinstance(value, dict): + return '{ %s }' % ', '.join( + '%s = %s' % (json.dumps(key), format_value(item)) + for key, item in value.items()) + raise TypeError('Unsupported TOML value: %s' % type(value).__name__) + + lines = [] + + def write_table(table, path=()): + if path: + if lines: + lines.append('') + lines.append('[%s]' % '.'.join(json.dumps(key) for key in path)) + for key, value in table.items(): + if not isinstance(value, dict): + lines.append('%s = %s' % (json.dumps(key), format_value(value))) + for key, value in table.items(): + if isinstance(value, dict): + write_table(value, path + (key,)) + + write_table(config) + return '\n'.join(lines) + ('\n' if lines else '') + + class RecipeModified: """Handling of recipes in the workspace created by devtool modify""" OE_INIT_BUILD_ENV = 'oe-init-build-env'
@@ -1122,6 +1158,70 @@ class RecipeModified: self.recipe_id = self.bpn + "-" + self.package_arch self.recipe_id_pretty = self.bpn + ": " + self.package_arch + @property + def cargo_config_path(self): + return os.path.join(self.ide_sdk_dir, 'cargo-config.toml') + + def update_cargo_config(self): + """Export the recipe Cargo config to the devtool workspace.""" + source_config = os.path.join(self.cargo_home, 'config.toml') + try: + with open(source_config, 'r') as config_file: + cargo_config = tomli.loads(config_file.read()) + except FileNotFoundError as exc: + raise DevtoolError( + 'Cargo config not found at %s; build the recipe first' % source_config) from exc + + if 'build' not in cargo_config: + cargo_config['build'] = {} + cargo_config['build']['target'] = self.cargo_target + + if 'target' not in cargo_config: + cargo_config['target'] = {} + target_config = cargo_config['target'] + if self.cargo_target not in target_config: + target_config[self.cargo_target] = {} + target_config = target_config[self.cargo_target] + target_config['linker'] = self.cargo_linker + target_config['rustflags'] = shlex.split(self.cargo_rustflags) + + if 'env' not in cargo_config: + cargo_config['env'] = {} + env_config = cargo_config['env'] + env_names = ( + 'CC', 'CXX', 'CFLAGS', 'CXXFLAGS', 'AR', + 'TARGET_CC', 'TARGET_CXX', 'TARGET_CFLAGS', 'TARGET_CXXFLAGS', 'TARGET_AR', + 'HOST_CC', 'HOST_CXX', 'HOST_CFLAGS', 'HOST_CXXFLAGS', 'HOST_AR', + 'RUST_TARGET_PATH', 'RUST_BACKTRACE', 'SOURCE_DATE_EPOCH', + 'PKG_CONFIG', 'PKG_CONFIG_FOR_BUILD', 'PKG_CONFIG_FOR_TARGET', + 'PKG_CONFIG_SYSROOT_DIR', 'PKG_CONFIG_PATH', 'PKG_CONFIG_LIBDIR', + 'LIBGIT2_NO_VENDOR', 'LIBSSH2_SYS_USE_PKG_CONFIG', + 'LIBSQLITE3_SYS_USE_PKG_CONFIG', 'OPENSSL_NO_VENDOR', + 'PKG_CONFIG_ALLOW_CROSS', 'PKG_CONFIG_ALL_DYNAMIC', + 'SYSTEM_DEPS_BUILD_INTERNAL', 'ZSTD_SYS_USE_PKG_CONFIG', + ) + for name in env_names: + value = self.cargo_env.get(name) + if value is not None: + env_config[name] = {'value': str(value), 'force': True} + + content = ( + '# Generated by devtool ide-sdk for the active build environment.\n' + '\n' + + _toml_dumps(cargo_config)) + + config_path = self.cargo_config_path + os.makedirs(os.path.dirname(config_path), exist_ok=True) + try: + with open(config_path, 'r') as config_file: + current = config_file.read() + except FileNotFoundError: + current = None + if current != content: + with open(config_path, 'w') as config_file: + config_file.write(content) + return config_path + @staticmethod def is_valid_shell_variable(var): """Skip strange shell variables like systemd
@@ -2019,6 +2119,8 @@ def ide_setup(args, config, basepath, workspace): image.extract_nfs_rootfs(nfs_export_base_dir, args.nfs, args.target) if DevtoolIdeMode.modified in modes: for recipe_modified in recipes_modified: + if recipe_modified.build_tool is BuildTool.CARGO: + recipe_modified.update_cargo_config() if recipe_modified.build_tool is BuildTool.CMAKE: recipe_modified.cmake_preset() if recipe_modified.build_tool is BuildTool.MESON:
Signature · 1 line
2.53.0
PATCH v2 4/18 oeqa/selftest: verify Cargo config serialization
In reply to Enguerrand de RibaucourtParse the config exported by devtool and round-trip the TOML writer's supported tables and values.
AI-Generated: Uses GitHub Copilot
---
Changes in v2:
- Commit added to verify the generated Cargo config.
--- meta/lib/oeqa/selftest/cases/devtool.py | 63 +++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+)
meta/lib/oeqa/selftest/cases/devtool.py +63 -0
@@ -5242,3 +5242,66 @@ class DevtoolIdeSdkMiscTests(DevtoolIdeSdkTests): with open(modified_config_file) as modified_config: modified_config_new = modified_config.read() self.assertEqual(modified_config_str, modified_config_new) + + +class DevtoolIdeSdkCargoConfigTests(DevtoolIdeSdkTests): + + def test_cargo_toml_serialization(self): + import importlib + import sys + + from bb._vendor import tomli + + config = { + 'paths': ['/work/crate', '/work/quoted "crate"\\dir'], + 'source': {'bitbake': {'directory': '/downloads/cargo'}}, + 'patch': { + 'https://example.com/repo': { + 'example': {'path': '/work/patched-crate'} + } + }, + 'env': { + 'CC': {'value': '/tool/cc --flag="quoted"\\path', 'force': True} + }, + 'target': { + 'x86_64-poky-linux': { + 'linker': '/tool/ld', + 'rustflags': ['--sysroot', '/sysroot path'], + } + }, + 'term': {'progress': {'when': 'always', 'width': 80}}, + } + + scripts_lib = os.path.join(get_bb_var('COREBASE'), 'scripts', 'lib') + sys.path.insert(0, scripts_lib) + oeqa_devtool = sys.modules.pop('devtool') + try: + ide_sdk = importlib.import_module('devtool.ide_sdk') + self.assertEqual(tomli.loads(ide_sdk._toml_dumps(config)), config) + finally: + for name in tuple(sys.modules): + if name == 'devtool' or name.startswith('devtool.'): + del sys.modules[name] + sys.modules['devtool'] = oeqa_devtool + sys.path.remove(scripts_lib) + + def test_cargo_config_is_generated_and_valid(self): + from bb._vendor import tomli + + recipe_name = 'cargo-example' + self._check_workspace() + self._write_bb_config() + self._devtool_ide_sdk_recipe(recipe_name, 'Cargo.toml', None) + runCmd('devtool ide-sdk %s oe-selftest-image --ide=code' % recipe_name, + output_log=self._cmd_logger) + + cargo_config = os.path.join( + self.workspacedir, 'ide-sdk', recipe_name, 'cargo-config.toml') + self.assertExists(cargo_config, 'Cargo config was not generated') + with open(cargo_config) as cargo_config_file: + config = tomli.loads(cargo_config_file.read()) + rust_target = get_bb_var('RUST_HOST_SYS', recipe_name) + self.assertEqual(config['build']['target'], rust_target) + self.assertEqual( + config['target'][rust_target]['linker'], + get_bb_var('CARGO_RUST_TARGET_CCLD', recipe_name))
Signature · 1 line
2.53.0
PATCH v2 5/18 devtool: ide-sdk: recommend Rust Analyzer for Cargo
In reply to Enguerrand de RibaucourtRecommend rust-analyzer for Cargo recipes and verify the extension appears in the generated VS Code recommendations file.
AI-Generated: Uses GitHub Copilot
--- meta/lib/oeqa/selftest/cases/devtool.py | 14 ++++++++++++++ scripts/lib/devtool/ide_plugins/ide_code.py | 2 ++ 2 files changed, 16 insertions(+)
meta/lib/oeqa/selftest/cases/devtool.py +14 -0
@@ -4339,6 +4339,20 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): self._test_code_and_none_qemu(slirp=True) +class DevtoolIdeSdkRustTests(DevtoolIdeSdkTests): + + def test_code_cargo_rust_analyzer_recommendation(self): + recipe_name = "cargo-example" + self._check_workspace() + self._write_bb_config() + tempdir = self._devtool_ide_sdk_recipe(recipe_name, "Cargo.toml", None) + runCmd('devtool ide-sdk %s oe-selftest-image --ide=code' % recipe_name, + output_log=self._cmd_logger) + + with open(os.path.join(tempdir, '.vscode', 'extensions.json')) as extensions_file: + extensions = json.load(extensions_file) + self.assertIn('rust-lang.rust-analyzer', extensions['recommendations']) + class DevtoolIdeSdkKernelTests(DevtoolIdeSdkTests): @OETestTag("runqemu")
scripts/lib/devtool/ide_plugins/ide_code.py +2 -0
@@ -303,6 +303,8 @@ class IdeVSCode(IdeBase): def vscode_extensions(self, modified_recipe): recommendations = [] + if modified_recipe.build_tool is BuildTool.CARGO: + recommendations.append("rust-lang.rust-analyzer") # clangd (matched to the recipe's real cross compiler via # compile_commands.json) replaces cpptools as the IntelliSense provider # when selected via IDE_SDK_INTELLISENSE (defaults to clangd for the
Signature · 1 line
2.53.0
PATCH v2 6/18 devtool: ide-sdk: configure Rust Analyzer for Cargo
In reply to Enguerrand de RibaucourtPoint Rust Analyzer at the generated Cargo config and target, while keeping its process environment limited to the recipe PATH. Verify the workspace config path and target in the Cargo selftest.
AI-Generated: Uses GitHub Copilot
---
Changes in v2:
- Use the Cargo config path generated during common recipe setup.
--- meta/lib/oeqa/selftest/cases/devtool.py | 12 +++++++++++- scripts/lib/devtool/ide_plugins/ide_code.py | 10 ++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-)
meta/lib/oeqa/selftest/cases/devtool.py +11 -1
@@ -4341,7 +4341,7 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): class DevtoolIdeSdkRustTests(DevtoolIdeSdkTests): - def test_code_cargo_rust_analyzer_recommendation(self): + def test_code_cargo_rust_analyzer_configuration(self): recipe_name = "cargo-example" self._check_workspace() self._write_bb_config()
@@ -4349,8 +4349,18 @@ class DevtoolIdeSdkRustTests(DevtoolIdeSdkTests): runCmd('devtool ide-sdk %s oe-selftest-image --ide=code' % recipe_name, output_log=self._cmd_logger) + with open(os.path.join(tempdir, '.vscode', 'settings.json')) as settings_file: + settings = json.load(settings_file) with open(os.path.join(tempdir, '.vscode', 'extensions.json')) as extensions_file: extensions = json.load(extensions_file) + cargo_config = os.path.join( + self.workspacedir, 'ide-sdk', recipe_name, 'cargo-config.toml') + self.assertExists(cargo_config, 'Cargo config was not generated') + self.assertEqual(settings['rust-analyzer.cargo.configPath'], cargo_config) + self.assertEqual( + settings['rust-analyzer.cargo.target'], + get_bb_var('RUST_HOST_SYS', recipe_name)) + self.assertNotIn('rust-analyzer.cargo.extraEnv', settings) self.assertIn('rust-lang.rust-analyzer', extensions['recommendations']) class DevtoolIdeSdkKernelTests(DevtoolIdeSdkTests):
scripts/lib/devtool/ide_plugins/ide_code.py +10 -0
@@ -228,6 +228,15 @@ class IdeVSCode(IdeBase): } ] + def __vscode_settings_cargo(self, settings_dict, modified_recipe): + if modified_recipe.build_tool is not BuildTool.CARGO: + return + settings_dict["rust-analyzer.server.extraEnv"] = { + "PATH": modified_recipe.path + } + settings_dict["rust-analyzer.cargo.target"] = modified_recipe.cargo_target + settings_dict["rust-analyzer.cargo.configPath"] = modified_recipe.cargo_config_path + def __vscode_settings_clangd(self, settings_dict, modified_recipe): if not (modified_recipe.ide_sdk_intellisense == 'clangd' and modified_recipe.build_tool.is_c_cpp): return
@@ -294,6 +303,7 @@ class IdeVSCode(IdeBase): self.__vscode_settings_cmake(settings_dict, modified_recipe) self.__vscode_settings_meson(settings_dict, modified_recipe) self.__vscode_settings_kernel_module(settings_dict, modified_recipe) + self.__vscode_settings_cargo(settings_dict, modified_recipe) self.__vscode_settings_clangd(settings_dict, modified_recipe) self.__vscode_settings_format(settings_dict, modified_recipe)
Signature · 1 line
2.53.0
PATCH v2 7/18 devtool: ide-sdk: generalize binary install task name
In reply to Enguerrand de RibaucourtRename the C++-specific generator to describe its install/deploy behavior. The upcoming Cargo task builds outside BitBake and will reuse this helper to install and deploy the resulting recipe artifacts.
AI-Generated: Uses GitHub Copilot
--- scripts/lib/devtool/ide_plugins/ide_code.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
scripts/lib/devtool/ide_plugins/ide_code.py +2 -2
@@ -661,7 +661,7 @@ class IdeVSCode(IdeBase): IdeBase.update_json_file( self.dot_code_dir(modified_recipe), launch_file, launch_dict) - def vscode_tasks_cpp(self, args, image_recipe, modified_recipe): + def vscode_tasks_binary(self, args, image_recipe, modified_recipe): run_install_deploy = modified_recipe.gen_install_deploy_script(args, image_recipe.nfs_deploy_dir) install_task_name = "install && deploy-target %s" % modified_recipe.recipe_id_pretty deploy_args = ["--target", resolve_deploy_target(args, image_recipe.nfs_deploy_dir)]
@@ -963,7 +963,7 @@ class IdeVSCode(IdeBase): def vscode_tasks(self, args, image_recipe, modified_recipe): if modified_recipe.build_tool.is_c_cpp: - self.vscode_tasks_cpp(args, image_recipe, modified_recipe) + self.vscode_tasks_binary(args, image_recipe, modified_recipe) elif modified_recipe.build_tool == BuildTool.KERNEL_MODULE: self.vscode_tasks_kernel_module(args, image_recipe, modified_recipe) else:
Signature · 1 line
2.53.0
PATCH v2 8/18 devtool: ide-sdk: extract install task definition
In reply to Enguerrand de RibaucourtExtract the install/deploy task object before adding optional dependencies. This preserves existing task output and provides a shared insertion point for Cargo build/deploy ordering.
AI-Generated: Uses GitHub Copilot
--- scripts/lib/devtool/ide_plugins/ide_code.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-)
scripts/lib/devtool/ide_plugins/ide_code.py +8 -9
@@ -669,17 +669,16 @@ class IdeVSCode(IdeBase): deploy_args += ["--port", args.port] for package in args.package or []: deploy_args += ["--package", package] + install_task = { + "label": install_task_name, + "type": "shell", + "command": run_install_deploy, + "args": deploy_args, + "problemMatcher": [] + } tasks_dict = { "version": "2.0.0", - "tasks": [ - { - "label": install_task_name, - "type": "shell", - "command": run_install_deploy, - "args": deploy_args, - "problemMatcher": [] - } - ] + "tasks": [install_task] } for cross_debug_config in self.cross_debug_configs: if cross_debug_config.modified_recipe is not modified_recipe:
Signature · 1 line
2.53.0
PATCH v2 9/18 devtool: ide-sdk: order install after optional build task
In reply to Enguerrand de RibaucourtLet binary task generation optionally insert a build task and make install/deploy depend on it. Cargo builds outside BitBake, so this ordering ensures its artifacts exist before do_install and deploy-target run.
AI-Generated: Uses GitHub Copilot
---
Changes in v2:
- Fix install-task duplication while adding build-before-install ordering.
--- scripts/lib/devtool/ide_plugins/ide_code.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
scripts/lib/devtool/ide_plugins/ide_code.py +7 -2
@@ -661,7 +661,7 @@ class IdeVSCode(IdeBase): IdeBase.update_json_file( self.dot_code_dir(modified_recipe), launch_file, launch_dict) - def vscode_tasks_binary(self, args, image_recipe, modified_recipe): + def vscode_tasks_binary(self, args, image_recipe, modified_recipe, build_task=None): run_install_deploy = modified_recipe.gen_install_deploy_script(args, image_recipe.nfs_deploy_dir) install_task_name = "install && deploy-target %s" % modified_recipe.recipe_id_pretty deploy_args = ["--target", resolve_deploy_target(args, image_recipe.nfs_deploy_dir)]
@@ -669,6 +669,7 @@ class IdeVSCode(IdeBase): deploy_args += ["--port", args.port] for package in args.package or []: deploy_args += ["--package", package] + tasks = [build_task] if build_task else [] install_task = { "label": install_task_name, "type": "shell",
@@ -676,9 +677,13 @@ class IdeVSCode(IdeBase): "args": deploy_args, "problemMatcher": [] } + if build_task: + install_task["dependsOn"] = [build_task["label"]] + install_task["dependsOrder"] = "sequence" + tasks.append(install_task) tasks_dict = { "version": "2.0.0", - "tasks": [install_task] + "tasks": tasks } for cross_debug_config in self.cross_debug_configs: if cross_debug_config.modified_recipe is not modified_recipe:
Signature · 1 line
2.53.0
PATCH v2 10/18 devtool: ide-sdk: add Cargo build task
In reply to Enguerrand de RibaucourtGenerate a Cargo build task that uses the exported recipe config and build flags. Run it in the recipe source tree with only the SDK PATH so Cargo uses the cross environment captured for the recipe.
AI-Generated: Uses GitHub Copilot
---
Changes in v2:
- Use the rustc problem matcher for Cargo build diagnostics.
--- meta/lib/oeqa/selftest/cases/devtool.py | 38 +++++++++++++++++++++++++++++ scripts/lib/devtool/ide_plugins/ide_code.py | 25 +++++++++++++++++++ 2 files changed, 63 insertions(+)
meta/lib/oeqa/selftest/cases/devtool.py +38 -0
@@ -4363,6 +4363,44 @@ class DevtoolIdeSdkRustTests(DevtoolIdeSdkTests): self.assertNotIn('rust-analyzer.cargo.extraEnv', settings) self.assertIn('rust-lang.rust-analyzer', extensions['recommendations']) + def test_code_cargo_build_task(self): + recipe_name = "cargo-example" + self._check_workspace() + self._write_bb_config() + tempdir = self._devtool_ide_sdk_recipe(recipe_name, "Cargo.toml", None) + runCmd('devtool ide-sdk %s oe-selftest-image --ide=code ' + '--target root@localhost --no-host-check' % recipe_name, + output_log=self._cmd_logger) + + with open(os.path.join(tempdir, '.vscode', 'tasks.json')) as tasks_file: + tasks = json.load(tasks_file)['tasks'] + build_task = next(task for task in tasks + if task['label'].startswith('cargo build ')) + cargo_config = os.path.join( + self.workspacedir, 'ide-sdk', recipe_name, 'cargo-config.toml') + self.assertEqual(build_task['command'], get_bb_var('CARGO', recipe_name)) + self.assertEqual( + build_task['args'][:3], ['--config', cargo_config, 'build']) + self.assertIn('--features', build_task['args']) + self.assertIn('ide-sdk-selftest', build_task['args']) + self.assertEqual(set(build_task['options']['env']), {'PATH'}) + self.assertEqual(build_task['problemMatcher'], '$rustc') + + build_env = os.environ.copy() + build_env.update(build_task['options']['env']) + result = subprocess.run( + [build_task['command']] + build_task['args'], + cwd=build_task['options']['cwd'], env=build_env, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + universal_newlines=True) + self.assertEqual(result.returncode, 0, result.stdout) + + install_task = next( + task for task in tasks + if task['label'].startswith('install && deploy-target ')) + self.assertEqual(install_task['dependsOn'], [build_task['label']]) + + class DevtoolIdeSdkKernelTests(DevtoolIdeSdkTests): @OETestTag("runqemu")
scripts/lib/devtool/ide_plugins/ide_code.py +25 -0
@@ -8,6 +8,7 @@ import json import logging import os +import shlex import shutil from devtool.ide_plugins import BuildTool, IdeBase, GdbCrossConfig, DebuggerServerModes, LldbServerConfig, get_devtool_deploy_opts, resolve_deploy_target
@@ -832,6 +833,28 @@ class IdeVSCode(IdeBase): IdeBase.update_json_file( self.dot_code_dir(modified_recipe), tasks_file, tasks_dict) + def vscode_tasks_cargo(self, args, image_recipe, modified_recipe): + build_task_name = "cargo build %s" % modified_recipe.recipe_id_pretty + cargo_config = modified_recipe.cargo_config_path + build_task = { + "label": build_task_name, + "type": "shell", + "command": modified_recipe.cargo, + "args": ["--config", cargo_config, "build"] + + shlex.split(modified_recipe.cargo_build_flags) + + shlex.split(modified_recipe.cargo_packageconfig_confargs), + "options": { + "cwd": modified_recipe.real_srctree, + "env": {"PATH": modified_recipe.path}, + }, + "group": { + "kind": "build", + "isDefault": True + }, + "problemMatcher": "$rustc" + } + self.vscode_tasks_binary(args, image_recipe, modified_recipe, build_task) + def vscode_tasks_fallback(self, args, image_recipe, modified_recipe): oe_init_dir = modified_recipe.oe_init_dir oe_init = ". %s %s > /dev/null && " % (modified_recipe.oe_init_build_env, modified_recipe.topdir)
@@ -970,6 +993,8 @@ class IdeVSCode(IdeBase): self.vscode_tasks_binary(args, image_recipe, modified_recipe) elif modified_recipe.build_tool == BuildTool.KERNEL_MODULE: self.vscode_tasks_kernel_module(args, image_recipe, modified_recipe) + elif modified_recipe.build_tool == BuildTool.CARGO: + self.vscode_tasks_cargo(args, image_recipe, modified_recipe) else: self.vscode_tasks_fallback(args, image_recipe, modified_recipe)
Signature · 1 line
2.53.0
PATCH v2 11/18 devtool: ide-sdk: enforce LLDB for Cargo
In reply to Enguerrand de RibaucourtRoute Cargo debugging through CodeLLDB and lldb-server because GDB/cppdbg does not resolve Rust function breakpoints in the VS Code Rust workflow.
Cargo debugging always uses LLDB, regardless of the recipe C/C++ toolchain. Key shared debugger instances by backend rather than TOOLCHAIN so Rust recipes use the correct server while ordinary GCC recipes retain GDB. Cargo C++ IntelliSense still uses the recipe compiler.
This follows VS Code's recommended Rust workflow:
https://code.visualstudio.com/docs/languages/rust#_debugging
AI-Generated: Uses GitHub Copilot
---
Changes in v2:
- Cover LLDB configuration when using --ide=none.
- Explain GDB's Rust function breakpoint limitations.
--- meta/lib/oeqa/selftest/cases/devtool.py | 35 +++++++++++++++++++++++++++ scripts/lib/devtool/ide_plugins/ide_code.py | 7 ++++-- scripts/lib/devtool/ide_plugins/ide_none.py | 4 ++-- scripts/lib/devtool/ide_sdk.py | 37 +++++++++++++++++++---------- 4 files changed, 66 insertions(+), 17 deletions(-)
meta/lib/oeqa/selftest/cases/devtool.py +35 -0
@@ -4363,6 +4363,41 @@ class DevtoolIdeSdkRustTests(DevtoolIdeSdkTests): self.assertNotIn('rust-analyzer.cargo.extraEnv', settings) self.assertIn('rust-lang.rust-analyzer', extensions['recommendations']) + def test_cargo_lldb_configuration(self): + recipe_name = "cargo-example" + self._check_workspace() + self._write_bb_config() + tempdir = self._devtool_ide_sdk_recipe(recipe_name, "Cargo.toml", None) + runCmd('devtool ide-sdk %s oe-selftest-image --ide=code --ide=none ' + '--target root@localhost --no-host-check' % recipe_name, + output_log=self._cmd_logger) + + with open(os.path.join(tempdir, '.vscode', 'extensions.json')) as extensions_file: + extensions = json.load(extensions_file) + with open(os.path.join(tempdir, '.vscode', 'launch.json')) as launch_file: + launch = json.load(launch_file)['configurations'][0] + with open(os.path.join(tempdir, '.vscode', 'tasks.json')) as tasks_file: + tasks = json.load(tasks_file)['tasks'] + + self.assertIn('vadimcn.vscode-lldb', extensions['recommendations']) + self.assertEqual(launch['type'], 'lldb') + + scripts_dir = self._workspace_scripts_dir(recipe_name) + scripts = os.listdir(scripts_dir) + self.assertTrue(any(name.startswith('lldb_server_') for name in scripts)) + self.assertFalse(any(name.startswith('gdbserver_') for name in scripts)) + self._verify_install_script_code(tempdir, recipe_name) + cargo_config = os.path.join( + self.workspacedir, 'ide-sdk', recipe_name, 'cargo-config.toml') + self.assertExists( + cargo_config, 'Cargo config was not generated for --ide=none') + target_dbgsrc_dir = get_bb_var('TARGET_DBGSRC_DIR', recipe_name) + self.assertEqual( + launch['sourceMap'][target_dbgsrc_dir], os.path.realpath(tempdir)) + server_task = next( + task for task in tasks if task['label'] == launch['preLaunchTask']) + self.assertIn('lldb-server platform --server', server_task['args'][-1]) + def test_code_cargo_build_task(self): recipe_name = "cargo-example" self._check_workspace()
scripts/lib/devtool/ide_plugins/ide_code.py +5 -2
@@ -315,7 +315,10 @@ class IdeVSCode(IdeBase): def vscode_extensions(self, modified_recipe): recommendations = [] if modified_recipe.build_tool is BuildTool.CARGO: - recommendations.append("rust-lang.rust-analyzer") + recommendations += [ + "rust-lang.rust-analyzer", + "vadimcn.vscode-lldb" + ] # clangd (matched to the recipe's real cross compiler via # compile_commands.json) replaces cpptools as the IntelliSense provider # when selected via IDE_SDK_INTELLISENSE (defaults to clangd for the
@@ -1003,7 +1006,7 @@ class IdeVSCode(IdeBase): self.vscode_extensions(modified_recipe) self.vscode_c_cpp_properties(modified_recipe, image_recipe) if args.target: - if modified_recipe.toolchain == 'clang': + if modified_recipe.uses_lldb: self.initialize_cross_debug_configs( image_recipe, modified_recipe, LldbServerConfigVSCode) else:
scripts/lib/devtool/ide_plugins/ide_none.py +2 -2
@@ -306,13 +306,13 @@ class IdeNone(IdeBase): - Execute the do_install task - Execute devtool deploy-target - - Generate a gdbinit file per executable + - Generate debugger configuration for each executable - Generate the oe-scripts sym-link """ script_path = modified_recipe.gen_install_deploy_script(args, image_recipe.nfs_deploy_dir) logger.info("Created: %s" % script_path) - if modified_recipe.toolchain == 'clang': + if modified_recipe.uses_lldb: self.initialize_cross_debug_configs( image_recipe, modified_recipe, LldbServerConfigNone) else:
scripts/lib/devtool/ide_sdk.py +24 -13
@@ -406,11 +406,11 @@ class RecipeImage: resulting bbappend content actually differs from what was on disk when initialize() ran, False if it is left exactly as it was. """ - wants_gdbserver = any( - r.wants_gdbserver and r.toolchain != 'clang' - for r in recipes_modified) wants_lldb_server = any( - r.wants_gdbserver and r.toolchain == 'clang' + r.wants_gdbserver and r.uses_lldb + for r in recipes_modified) + wants_gdbserver = any( + r.wants_gdbserver and not r.uses_lldb for r in recipes_modified) # Only add what the base configuration (e.g. local.conf) does not
@@ -1158,6 +1158,16 @@ class RecipeModified: self.recipe_id = self.bpn + "-" + self.package_arch self.recipe_id_pretty = self.bpn + ": " + self.package_arch + @property + def uses_lldb(self): + # VS Code's Rust debugging guide recommends CodeLLDB: + # https://code.visualstudio.com/docs/languages/rust#_debugging + # ide-sdk supports Rust debugging only through CodeLLDB; cppdbg/GDB + # does not resolve Rust function breakpoints from the VS Code UI because + # GDB does not fully understand Rust's LLVM-generated function layout + # and name mangling. + return self.toolchain == 'clang' or self.build_tool is BuildTool.CARGO + @property def cargo_config_path(self): return os.path.join(self.ide_sdk_dir, 'cargo-config.toml')
@@ -2048,20 +2058,21 @@ def ide_setup(args, config, basepath, workspace): bootstrap_tasks += recipe_modified.bootstrap_tasks recipes_modified.append(recipe_modified) - # Key by (arch, toolchain) so recipes with different toolchains - # targeting the same arch each get the right debugger. + # Key by (arch, debugger) so recipes sharing an architecture + # but using different debugger backends each get the right one. + debugger = 'lldb' if recipe_modified.uses_lldb else 'gdb' debugger_key = (recipe_modified.target_arch, - recipe_modified.toolchain or '') + debugger) if debugger_key not in debuggers: target_device = TargetDevice(args) - if recipe_modified.toolchain == 'clang': - debugger = RecipeLldbNative(args, target_device) + if recipe_modified.uses_lldb: + debugger_config = RecipeLldbNative(args, target_device) else: - debugger = RecipeGdbCross( + debugger_config = RecipeGdbCross( args, recipe_modified.target_arch, target_device) - debugger.initialize(config, workspace, tinfoil) - bootstrap_tasks += debugger.bootstrap_tasks - debuggers[debugger_key] = debugger + debugger_config.initialize(config, workspace, tinfoil) + bootstrap_tasks += debugger_config.bootstrap_tasks + debuggers[debugger_key] = debugger_config recipe_modified.debugger_cross = debuggers[debugger_key] finally:
Signature · 1 line
2.53.0
PATCH v2 12/18 devtool: ide-sdk: remap Cargo sources for BitBake builds
In reply to Enguerrand de RibaucourtWith devtool modify, Cargo sources can live outside WORKDIR, so the standard Rust remaps leave the workspace path in DWARF and package QA rejects the debug package. Append a workspace-specific remap to RUST_DEBUG_REMAP in a managed bbappend section so repeated ide-sdk runs restore and reparse it.
AI-Generated: Uses GitHub Copilot
---
Changes in v2:
- Revert bbappend comparison after stripping managed sections to avoid reparse when the ide-sdk section is not modified. I think there is a pre-existing bug with that logic however. An ide-sdk re-run leaves the stripped bbappend for clang and cargo classes. As a consequence, I simplified the Qemu selftest to no longer run ide-sdk twice.
--- scripts/lib/devtool/ide_sdk.py | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-)
scripts/lib/devtool/ide_sdk.py +25 -9
@@ -831,6 +831,7 @@ class RecipeModified: VALID_BASH_ENV_NAME_CHARS = re.compile(r"^[a-zA-Z0-9_]*$") MARKER = '# devtool ide-sdk: clangd toolchain support' + CARGO_REMAP_MARKER = '# devtool ide-sdk: Cargo source debug remap' # PROVIDES of recipes that are deployed via do_deploy (e.g. to # DEPLOY_DIR_IMAGE) rather than installed into the rootfs, so adding them
@@ -951,13 +952,14 @@ class RecipeModified: content = f.read() originals[name] = content stripped = cls._strip_marker_section(content, cls.MARKER) + stripped = cls._strip_marker_section(stripped, cls.CARGO_REMAP_MARKER) if stripped != content: with open(bbappend, 'w') as f: f.write(stripped) return originals def update_bbappend(self): - """Add DEPENDS on clang-native when clangd is used as only the IntelliSense engine""" + """Add workspace-only clang dependencies or Cargo debug path remaps.""" wants_clang_native = ( self.build_tool.is_c_cpp and self.ide_sdk_intellisense == 'clangd'
@@ -970,17 +972,27 @@ class RecipeModified: else: parsed_content = '' - if not wants_clang_native: + new_sections = [] + if wants_clang_native: + new_sections.append( + self.MARKER + '\nDEPENDS:append = " clang-native"\n') + if self.build_tool is BuildTool.CARGO: + new_sections.append( + self.CARGO_REMAP_MARKER + + '\nRUST_DEBUG_REMAP:append = " ' + '--remap-path-prefix=${S}=${TARGET_DBGSRC_DIR}"\n') + + if not new_sections: if self.MARKER in original_content: logger.info( "Removed clangd toolchain support from %s: no longer needed", self.bbappend) return False - new_section = self.MARKER + '\nDEPENDS:append = " clang-native"\n' new_content = parsed_content - if new_content and not new_content.endswith('\n'): - new_content += '\n' - new_content += new_section + for section in new_sections: + if new_content and not new_content.endswith('\n'): + new_content += '\n' + new_content += section if new_content == original_content: return False
@@ -989,9 +1001,13 @@ class RecipeModified: os.makedirs(appends_dir, exist_ok=True) with open(self.bbappend, 'w') as f: f.write(new_content) - logger.info( - "Updated %s: added DEPENDS on clang-native, needed to stage clangd " - "for IDE_SDK_INTELLISENSE=\"clangd\"", self.bbappend) + if wants_clang_native: + logger.info( + "Updated %s: added DEPENDS on clang-native, needed to stage clangd " + "for IDE_SDK_INTELLISENSE=\"clangd\"", self.bbappend) + if self.build_tool is BuildTool.CARGO: + logger.info( + "Updated %s: remapped Cargo external sources for debug info", self.bbappend) return True def initialize(self, config, workspace, tinfoil):
Signature · 1 line
2.53.0
Patchtest results for [PATCH v2 12/18] devtool: ide-sdk: remap Cargo sources for BitBake builds
Thank you for your submission. Patchtest identified one or more issues with the patch. Please see the log below for more information:
---
Testing patch /home/patchtest/share/mboxes/v2-12-18-devtool-ide-sdk-remap-Cargo-sources-for-BitBake-builds.patch
FAIL: test Signed-off-by presence: Mbox is missing Signed-off-by. Add it manually or with "git commit --amend -s" (test_mbox.TestMbox.test_signed_off_by_presence)
PASS: pretest pylint (test_python_pylint.PyLint.pretest_pylint)
PASS: test auh changelog truncation notice (test_mbox.TestMbox.test_auh_changelog_truncation_notice)
PASS: test author valid (test_mbox.TestMbox.test_author_valid)
PASS: test commit message presence (test_mbox.TestMbox.test_commit_message_presence)
PASS: test commit message user tags (test_mbox.TestMbox.test_commit_message_user_tags)
PASS: test max line length (test_metadata.TestMetadata.test_max_line_length)
PASS: test mbox format (test_mbox.TestMbox.test_mbox_format)
PASS: test non-AUH upgrade (test_mbox.TestMbox.test_non_auh_upgrade)
PASS: test pylint (test_python_pylint.PyLint.test_pylint)
PASS: test shortlog format (test_mbox.TestMbox.test_shortlog_format)
PASS: test shortlog length (test_mbox.TestMbox.test_shortlog_length)
PASS: test target mailing list (test_mbox.TestMbox.test_target_mailing_list)
SKIP: pretest src uri left files: No modified recipes, skipping pretest (test_metadata.TestMetadata.pretest_src_uri_left_files)
SKIP: test CVE check ignore: No modified recipes or older target branch, skipping test (test_metadata.TestMetadata.test_cve_check_ignore)
SKIP: test CVE tag format: No new source patches introduced (test_patch.TestPatch.test_cve_tag_format)
SKIP: test Signed-off-by presence: No new source patches introduced (test_patch.TestPatch.test_signed_off_by_presence)
SKIP: test Upstream-Status presence: No new source patches introduced (test_patch.TestPatch.test_upstream_status_presence_format)
SKIP: test bugzilla entry format: No bug ID found (test_mbox.TestMbox.test_bugzilla_entry_format)
SKIP: test lic files chksum modified not mentioned: No modified recipes, skipping test (test_metadata.TestMetadata.test_lic_files_chksum_modified_not_mentioned)
SKIP: test lic files chksum presence: No added recipes, skipping test (test_metadata.TestMetadata.test_lic_files_chksum_presence)
SKIP: test license presence: No added recipes, skipping test (test_metadata.TestMetadata.test_license_presence)
SKIP: test series merge on head: Merge test is disabled for now (test_mbox.TestMbox.test_series_merge_on_head)
SKIP: test src uri left files: No modified recipes, skipping test (test_metadata.TestMetadata.test_src_uri_left_files)
SKIP: test summary presence: No added recipes, skipping test (test_metadata.TestMetadata.test_summary_presence)
---
Please address the issues identified and
submit a new revision of the patch, or alternatively, reply to this email with an explanation of why the patch should be accepted. If you believe these results are due to an error in patchtest, please submit a bug at https://bugzilla.yoctoproject.org/ (use the 'Patchtest' category under 'Yocto Project Subprojects'). For more information on specific failures, see: https://wiki.yoctoproject.org/wiki/Patchtest. Thank you!
PATCH v2 13/18 devtool: ide-sdk: map Cargo workspace sources for LLDB
In reply to Enguerrand de RibaucourtCargo debug info remaps the modified source tree to TARGET_DBGSRC_DIR. Include this reverse map in CodeLLDB launch configs even when rootfs-dbg is disabled. Use the Cargo workspace as its source root while preserving the existing rootfs-debug mappings for other recipes.
AI-Generated: Uses GitHub Copilot
---
Changes in v2:
- Move source-map verification to the LLDB configuration test.
--- scripts/lib/devtool/ide_plugins/ide_code.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
scripts/lib/devtool/ide_plugins/ide_code.py +10 -3
@@ -555,15 +555,22 @@ class IdeVSCode(IdeBase): source_map = { "/usr/include": os.path.join(modified_recipe.recipe_sysroot, "usr", "include") } - if lldb_config.image_recipe.rootfs_dbg: + rootfs_dbg = lldb_config.image_recipe.rootfs_dbg + is_cargo = modified_recipe.build_tool is BuildTool.CARGO + if is_cargo or rootfs_dbg: + source_root = ( + modified_recipe.real_srctree + if is_cargo else "${workspaceFolder}") # 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}" + source_root + host_path[len(modified_recipe.real_srctree):]) - else: + elif rootfs_dbg: source_map[target_path] = host_path + + if rootfs_dbg: if "/usr/src/debug" in source_map: logger.error( 'Key "/usr/src/debug" already exists in source_map. '
Signature · 1 line
2.53.0
PATCH v2 14/18 devtool: ide-sdk: map libstd-rs sources to Rust SDK
In reply to Enguerrand de Ribaucourtlibstd-rs DWARF paths use a source prefix under work-shared, while rust-native stages the standard-library sources in the recipe native sysroot. Derive the target prefix from BitBake metadata and map it to the SDK source tree so LLDB runtime frames resolve without host Rust sources.
AI-Generated: Uses GitHub Copilot
---
Changes in v2:
- Use STAGING_LIBDIR_NATIVE for multilib-correct Rust source paths.
--- scripts/lib/devtool/ide_plugins/ide_code.py | 2 ++ scripts/lib/devtool/ide_sdk.py | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+)
scripts/lib/devtool/ide_plugins/ide_code.py +2 -0
@@ -557,6 +557,8 @@ class IdeVSCode(IdeBase): } rootfs_dbg = lldb_config.image_recipe.rootfs_dbg is_cargo = modified_recipe.build_tool is BuildTool.CARGO + if is_cargo: + source_map.update(modified_recipe.cargo_std_source_map) if is_cargo or rootfs_dbg: source_root = ( modified_recipe.real_srctree
scripts/lib/devtool/ide_sdk.py +19 -0
@@ -923,6 +923,7 @@ class RecipeModified: self.cargo_packageconfig_confargs = None self.cargo_rustflags = None self.cargo_env = None + self.cargo_std_source_map = {} # Populated after bitbake built all the recipes self._installed_binaries = None
@@ -1140,6 +1141,24 @@ class RecipeModified: 'SYSTEM_DEPS_BUILD_INTERNAL': 'never', 'ZSTD_SYS_USE_PKG_CONFIG': '1', }) + # libstd-rs DWARF paths map to sources staged by rust-native. + libstd_recipe_d = parse_recipe( + config, tinfoil, 'libstd-rs', appends=True, + filter_workspace=False) + if libstd_recipe_d: + libstd_rustsrc = libstd_recipe_d.getVar('RUSTSRC') + target_dbgsrc_dir = libstd_recipe_d.getVar('TARGET_DBGSRC_DIR') + libstd_tmpdir = libstd_recipe_d.getVar('TMPDIR') + if libstd_rustsrc and target_dbgsrc_dir and libstd_tmpdir: + shared_source_dir = os.path.join(libstd_tmpdir, 'work-shared') + target_source_root = os.path.join( + target_dbgsrc_dir, + os.path.relpath(libstd_rustsrc, shared_source_dir), + 'library') + sdk_source_root = os.path.join( + recipe_d.getVar('STAGING_LIBDIR_NATIVE'), + 'rustlib', 'src', 'rust', 'library') + self.cargo_std_source_map[target_source_root] = sdk_source_root self.build_tool = BuildTool.CARGO elif bb.data.inherits_class('module', recipe_d): self.build_tool = BuildTool.KERNEL_MODULE
Signature · 1 line
2.53.0
PATCH v2 15/18 oeqa/selftest: share source breakpoint lookup
In reply to Enguerrand de RibaucourtMove the stable source-line helper to the common ide-sdk test base so the Rust CodeLLDB integration can reuse it. Preserve the rationale for using marker-based lines instead of brittle hard-coded numbers.
AI-Generated: Uses GitHub Copilot
--- meta/lib/oeqa/selftest/cases/devtool.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-)
meta/lib/oeqa/selftest/cases/devtool.py +14 -14
@@ -3133,6 +3133,20 @@ class DevtoolIdeSdkTests(DevtoolBase): def _sources_workdir_dir(self, src_dir): return os.path.realpath(os.path.join(src_dir, 'oe-workdir')) + def _find_source_break_line(self, tempdir, source_file, marker): + """Find the 1-based line number of `marker` in tempdir/source_file + + Used to set a source-level (file:line) breakpoint at a stable, + self-documenting location instead of hard-coding a line number that + would silently go stale whenever the example source changes. + """ + source_path = os.path.join(tempdir, source_file) + with open(source_path) as f: + for lineno, line in enumerate(f, start=1): + if marker in line: + return lineno + self.fail("Could not find marker %r in %s" % (marker, source_path)) + def _find_multi_script_port(self, recipe_name, script_prefix, example_exe): """Discover the port assigned to a generated 'multi' mode debug-server script (gdbserver_*/lldb_server_*), since each --ide plugin allocates
@@ -4860,20 +4874,6 @@ class DevtoolIdeSdkClangTests(DevtoolIdeSdkTests): self.assertTrue(task, f"Configuration '{config_name}' preLaunchTask should not be empty") - def _find_source_break_line(self, tempdir, source_file, marker): - """Find the 1-based line number of `marker` in tempdir/source_file - - Used to set a source-level (file:line) breakpoint at a stable, - self-documenting location instead of hard-coding a line number that - would silently go stale whenever the example source changes. - """ - source_path = os.path.join(tempdir, source_file) - with open(source_path) as f: - for lineno, line in enumerate(f, start=1): - if marker in line: - return lineno - self.fail("Could not find marker %r in %s" % (marker, source_path)) - def _lldb_debug_cpp_example_batch_commands(self, tempdir): """Get a list of lldb --batch '-o' commands to debug the cpp-example-lib example
Signature · 1 line
2.53.0
PATCH v2 16/18 oeqa/selftest: run IDE tasks with configured environment
In reply to Enguerrand de RibaucourtApply each generated shell task's options.env and return its runCmd results. Cargo build tasks intentionally keep only the SDK PATH, and the following rebuild integration test needs both to execute and inspect that task.
AI-Generated: Uses GitHub Copilot
--- meta/lib/oeqa/selftest/cases/devtool.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
meta/lib/oeqa/selftest/cases/devtool.py +9 -2
@@ -3485,6 +3485,7 @@ class DevtoolIdeSdkTests(DevtoolBase): self.assertIn(task_label, task_by_label, "Task '%s' not found in tasks.json" % task_label) + task_results = [] ran = set() def run_task(label, execute_task):
@@ -3509,14 +3510,20 @@ class DevtoolIdeSdkTests(DevtoolBase): args = [args] task_cmd = [command] + [str(arg) for arg in args] - task_cwd = task.get('options', {}).get('cwd', tempdir) + task_options = task.get('options', {}) + task_cwd = task_options.get('cwd', tempdir) if isinstance(task_cwd, str): task_cwd = task_cwd.replace('${workspaceFolder}', tempdir) - runCmd(task_cmd, cwd=task_cwd, output_log=self._cmd_logger) + task_env = os.environ.copy() + task_env.update(task_options.get('env', {})) + task_results.append(runCmd( + task_cmd, cwd=task_cwd, env=task_env, + output_log=self._cmd_logger)) ran.add(label) run_task(task_label, run_main_task) + return task_results def _ide_sdk_package_opts(self): """Extra `devtool ide-sdk --package` options. Override to scope packages."""
Signature · 1 line
2.53.0
PATCH v2 17/18 oeqa/selftest: debug Cargo example over QEMU slirp
In reply to Enguerrand de RibaucourtExercise Cargo deploy and CodeLLDB launch against oe-selftest-image over slirp. Verify source breakpoints, Rust and runtime backtrace frames, and that the BitBake RUST_DEBUG_REMAP includes the devtool external source tree so package QA does not reject host paths in DWARF.
AI-Generated: Uses GitHub Copilot
---
Changes in v2:
- Use STAGING_LIBDIR_NATIVE to locate Rust standard-library sources in the SDK.
- Remove the second ide-sdk invocation and repeated-remap assertion while retaining the QEMU debugging and rebuild coverage.
--- meta/lib/oeqa/selftest/cases/devtool.py | 117 ++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+)
meta/lib/oeqa/selftest/cases/devtool.py +117 -0
@@ -4456,6 +4456,123 @@ class DevtoolIdeSdkRustTests(DevtoolIdeSdkTests): if task['label'].startswith('install && deploy-target ')) self.assertEqual(install_task['dependsOn'], [build_task['label']]) + @OETestTag("runqemu") + def test_code_cargo_qemu_slirp(self): + recipe_name = 'cargo-example' + testimage = 'oe-selftest-image' + self._check_workspace() + self._write_bb_config() + tempdir = self._devtool_ide_sdk_recipe( + recipe_name, 'Cargo.toml', testimage) + port_start = self._reserve_slirp_port_block() + port_opts = ' -G %d' % port_start + runCmd( + 'devtool ide-sdk %s %s --ide=code --target root@localhost ' + '--port 2222 --no-host-check%s' % ( + recipe_name, testimage, port_opts), + output_log=self._cmd_logger) + source_remap = '--remap-path-prefix=%s=%s' % ( + os.path.realpath(tempdir), + get_bb_var('TARGET_DBGSRC_DIR', recipe_name)) + rustflags = shlex.split(get_bb_var('RUSTFLAGS', recipe_name)) + self.assertIn( + source_remap, rustflags, + 'Cargo BitBake builds must remap the external source tree for DWARF') + self._verify_slirp_bbappend(testimage) + + with runqemu( + testimage, runqemuparams='nographic slirp snapshot') as qemu: + self.assertIsNotNone(qemu.port, 'No SSH port for the slirp target') + self._verify_rust_lldb_debugging(tempdir, expected_input=4) + + def _verify_rust_lldb_debugging(self, tempdir, expected_input): + with open(os.path.join(tempdir, '.vscode', 'launch.json')) as launch_file: + launch = json.load(launch_file)['configurations'][0] + with open(os.path.join(tempdir, '.vscode', 'tasks.json')) as tasks_file: + tasks = json.load(tasks_file)['tasks'] + + self.assertEqual(launch['type'], 'lldb') + target_dbgsrc_dir = get_bb_var('TARGET_DBGSRC_DIR', 'cargo-example') + self.assertEqual( + launch['sourceMap'][target_dbgsrc_dir], os.path.realpath(tempdir)) + self._run_vscode_task_with_dependencies( + tempdir, launch['preLaunchTask'], run_main_task=False) + server_task = next( + task for task in tasks if task['label'] == launch['preLaunchTask']) + self.assertEqual(server_task['command'], 'ssh') + ssh_args = [server_task['command']] + server_task['args'] + if ssh_args[-1].startswith('"') and ssh_args[-1].endswith('"'): + ssh_args[-1] = ssh_args[-1][1:-1].replace('\\$', '$') + runCmd(ssh_args, output_log=self._cmd_logger) + + source_line = self._find_source_break_line( + tempdir, 'src/main.rs', ' match n {') + println_line = self._find_source_break_line( + tempdir, 'src/main.rs', 'println!') + lldb_batch = [ + os.path.join( + get_bb_var('RECIPE_SYSROOT_NATIVE', 'lldb-native'), + 'usr', 'bin', 'lldb'), + '--batch', + ] + for command in launch['initCommands'] + launch['targetCreateCommands']: + lldb_batch += ['-o', command] + source_map_args = [] + for source, destination in launch.get('sourceMap', {}).items(): + source_map_args += [source, destination] + if source_map_args: + lldb_batch += [ + '-o', 'settings set target.source-map %s' % + ' '.join(source_map_args)] + lldb_batch += [ + '-o', 'breakpoint set --file main.rs --line %d' % source_line, + '-o', 'breakpoint set --file main.rs --line %d' % println_line, + '-o', 'breakpoint disable 2', + '-o', 'breakpoint list', + '-o', 'run', + '-o', 'frame variable n', + '-o', 'thread backtrace', + '-o', 'breakpoint disable 1', + '-o', 'breakpoint enable 2', + '-o', 'continue', + '-o', 'frame info', + '-o', 'frame select 1', + '-o', 'frame info', + '-o', 'frame select 3', + '-o', "image lookup -v -r -n 'std::rt::lang_start.*'", + '-o', 'thread backtrace', + '-o', 'exit', + ] + result = runCmd(lldb_batch, timeout=60, output_log=self._cmd_logger) + self.assertNotIn('(pending)', result.output) + self.assertIn('cargo_example::fibonacci', result.output) + self.assertIn('cargo_example::main', result.output) + self.assertIn('std::rt::lang_start', result.output) + self.assertIn('__libc_start', result.output) + self.assertRegex(result.output, r'n\s*=\s*%d' % expected_input) + libstd_rustsrc = get_bb_var('RUSTSRC', 'libstd-rs') + libstd_work_shared = os.path.join( + get_bb_var('TMPDIR', 'libstd-rs'), 'work-shared') + rust_std_target_root = os.path.join( + get_bb_var('TARGET_DBGSRC_DIR', 'libstd-rs'), + os.path.relpath(libstd_rustsrc, libstd_work_shared), + 'library') + rust_std_sdk_root = os.path.join( + get_bb_var('STAGING_LIBDIR_NATIVE', 'cargo-example'), + 'rustlib', 'src', 'rust', 'library') + rust_std_source = os.path.join( + rust_std_target_root, 'std', 'src', 'rt.rs') + self.assertEqual( + launch['sourceMap'][rust_std_target_root], rust_std_sdk_root) + self.assertIn( + rust_std_source, result.output, + 'Rust runtime backtrace did not resolve to the SDK debug path:\n%s' % + result.output) + self.assertNotIn(libstd_rustsrc, result.output) + self.assertTrue( + os.path.isfile(os.path.join(rust_std_sdk_root, 'std', 'src', 'rt.rs')), + 'Rust standard-library source is missing from the recipe SDK sysroot') + class DevtoolIdeSdkKernelTests(DevtoolIdeSdkTests):
Signature · 1 line
2.53.0
PATCH v2 18/18 oeqa/selftest: verify Cargo rebuild and redeploy
In reply to Enguerrand de RibaucourtAfter a source edit, run the ordered Cargo build/install/deploy tasks and verify the changed Fibonacci result. Use a helper to shift source lines so the breakpoint location can change without hard-coded line numbers; reuse the shared LLDB runner for both debug sessions.
AI-Generated: Uses GitHub Copilot
--- meta/lib/oeqa/selftest/cases/devtool.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+)
meta/lib/oeqa/selftest/cases/devtool.py +31 -0
@@ -3147,6 +3147,15 @@ class DevtoolIdeSdkTests(DevtoolBase): return lineno self.fail("Could not find marker %r in %s" % (marker, source_path)) + def _insert_source_line_shift(self, source_path, marker, line_count): + with open(source_path) as source_handle: + source = source_handle.read() + self.assertEqual(source.count(marker), 1) + inserted_lines = ''.join( + ' // line shift %d\n' % index for index in range(line_count)) + with open(source_path, 'w') as source_handle: + source_handle.write(source.replace(marker, inserted_lines + marker, 1)) + def _find_multi_script_port(self, recipe_name, script_prefix, example_exe): """Discover the port assigned to a generated 'multi' mode debug-server script (gdbserver_*/lldb_server_*), since each --ide plugin allocates
@@ -4485,6 +4494,28 @@ class DevtoolIdeSdkRustTests(DevtoolIdeSdkTests): self.assertIsNotNone(qemu.port, 'No SSH port for the slirp target') self._verify_rust_lldb_debugging(tempdir, expected_input=4) + main_rs = os.path.join(tempdir, 'src', 'main.rs') + self._insert_source_line_shift(main_rs, ' match n {', 4) + with open(main_rs) as source_file: + shifted_source = source_file.read() + self.assertEqual(shifted_source.count('fibonacci(4)'), 1) + with open(main_rs, 'w') as source_file: + source_file.write( + shifted_source.replace('fibonacci(4)', 'fibonacci(5)', 1)) + + with open(os.path.join(tempdir, '.vscode', 'tasks.json')) as tasks_file: + tasks = json.load(tasks_file)['tasks'] + install_task = next( + task for task in tasks + if task['label'].startswith('install && deploy-target ')) + task_results = self._run_vscode_task_with_dependencies( + tempdir, install_task['label']) + self.assertTrue( + any('Compiling cargo-example' in result.output + for result in task_results), + 'Cargo build task did not recompile the modified Rust source') + self._verify_rust_lldb_debugging(tempdir, expected_input=5) + def _verify_rust_lldb_debugging(self, tempdir, expected_input): with open(os.path.join(tempdir, '.vscode', 'launch.json')) as launch_file: launch = json.load(launch_file)['configurations'][0]
Signature · 1 line
2.53.0
Quoted 31 lines · Enguerrand de Ribaucourt via openembedded-core
Hello, this series introduces support for Rust ide-sdk VS Code configuration against the Yocto cross-compilation toolchain:
- editor symbol resolution and linting
- build task
- deploy task
- debuggingIt introduces a cargo-example recipe following the model used for cmake and meson.
The generated configurations can be portable to standalone SDK installations by manually updating the paths.
C/C++ IntelliSense could provide navigation for native code compiled by Cargo build scripts, using the recipe compiler and sysroot. This first series defers it because no mixed-language fixture is available to validate that workflow.
BitBake generates the Cargo configuration consumed by ide-sdk. This series updates the parsed configuration and writes its supported TOML values without adding a new dependency.
I reproduced the selftest coverage expected from other recipe classes, including configuration analysis up to actual debugging symbol resolution.
I largely used AI generation to write this stack. After many iterations, I am finally happy with the code and commit history quality of this series. I also have a documentation commit ready for the manual if this gets approved.
Quoted 8 lines
Any feedback is appreciated. Hope this will help lots of Rust developers working with Yocto distros :).
Cheers,
Enguerrand de RibaucourtSigned-off-by Enguerrand de Ribaucourt <[email protected]>---
Hi Enguerrand,
Thanks for the v2.
I can see one of the selftest is failing, but only in 1 of the 3 selftest builds. So it might be something not fully deterministic with lldb-server installation:
2026-11-10 10:42:27,794 - oe-selftest - INFO - devtool.DevtoolIdeSdkRustTests.test_code_cargo_qemu_slirp (subunit.RemotedTestCase)
2026-11-10 10:42:27,795 - oe-selftest - INFO - ... FAIL
...
2026-11-10 10:42:27,795 - oe-selftest - INFO - testtools.testresult.real._StringException: Traceback (most recent call last):
File "/srv/pokybuild/yocto-worker/oe-selftest-fedora/build/layers/openembedded-core/meta/lib/oeqa/selftest/cases/devtool.py", line 4503, in test_code_cargo_qemu_slirp
self._verify_rust_lldb_debugging(tempdir, expected_input=4)
File "/srv/pokybuild/yocto-worker/oe-selftest-fedora/build/layers/openembedded-core/meta/lib/oeqa/selftest/cases/devtool.py", line 4545, in _verify_rust_lldb_debugging
runCmd(ssh_args, output_log=self._cmd_logger)
File "/srv/pokybuild/yocto-worker/oe-selftest-fedora/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 '['ssh', '-p', '2222', '-o', 'UserKnownHostsFile=/dev/null', '-o', 'StrictHostKeyChecking=no', 'root@localhost', "/bin/sh -c 'grep -q :04F2 /proc/net/tcp /proc/net/tcp6 2>/dev/null && exit 0; mkdir -p /tmp/lldb_server_1266_usr-bin-cargo-example_multi; cd /tmp/lldb_server_1266_usr-bin-cargo-example_multi; /usr/bin/lldb-server platform --server --listen *:1266 --gdbserver-port 1267 > /tmp/lldb_server_1266_usr-bin-cargo-example_multi/lldb_server.log 2>&1 & _lldb_server_pid=$!; echo $_lldb_server_pid > /tmp/lldb_server_1266_usr-bin-cargo-example_multi/lldb_server.pid; _w=0; while ! grep -q :04F2 /proc/net/tcp /proc/net/tcp6 2>/dev/null; do kill -0 $_lldb_server_pid 2>/dev/null || { cat /tmp/lldb_server_1266_usr-bin-cargo-example_multi/lldb_server.log >&2; echo lldb-server exited before it started listening on port 1266 >&2; exit 1; }; _w=$((_w+1)); [ $_w -lt 600 ] || { kill $_lldb_server_pid 2>/dev/null; echo lldb-server did not start on port 1266 after $_w retries >&2; cat /tmp/lldb_server_1266_usr-bin-cargo-example_multi/lldb_server.log >&2; exit 1; }; sleep 0.1; done;'"]' returned non-zero exit status 1:
Warning: Permanently added '[localhost]:2222' (RSA) to the list of known hosts./bin/sh: line 0: /usr/bin/lldb-server: not found
lldb-server exited before it started listening on port 1266
https://autobuilder.yoctoproject.org/valkyrie/#/builders/48/builds/5043
Can you have a look at the issue?
I launched a rebuild of that build, out of curiosity. But in all cases, we should understand what happened here.
Thanks,
Mathieu
Signature · 3 lines
Mathieu Dubois-Briand, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
Am Di., 10. Nov. 2026 um 13:25 Uhr schrieb Mathieu Dubois-Briand via openembedded-core <[email protected]
Quoted 100 lines
:
Quoted 14 lines · Enguerrand de Ribaucourt via openembedded-core
Hello, this series introduces support for Rust ide-sdk VS Code configuration against the Yocto cross-compilation toolchain:
- editor symbol resolution and linting
- build task
- deploy task
- debuggingIt introduces a cargo-example recipe following the model used for cmake and meson.
The generated configurations can be portable to standalone SDK installations by manually updating the paths.
Quoted 6 lines
C/C++ IntelliSense could provide navigation for native code compiled by Cargo build scripts, using the recipe compiler and sysroot. This first series defers it because no mixed-language fixture is available to validate that workflow.
BitBake generates the Cargo configuration consumed by ide-sdk. This
series
Quoted 5 lines
updates the parsed configuration and writes its supported TOML values without adding a new dependency.
I reproduced the selftest coverage expected from other recipe classes, including configuration analysis up to actual debugging symbol
resolution.
Quoted 3 lines
I largely used AI generation to write this stack. After many iterations, I am finally happy with the code and commit history quality of this
series.
Quoted 10 lines
I also have a documentation commit ready for the manual if this gets approved.
Any feedback is appreciated. Hope this will help lots of Rust developers working with Yocto distros :).
Cheers,
Enguerrand de RibaucourtSigned-off-by Enguerrand de Ribaucourt <Quoted 1 line
---
Hi Enguerrand,
Thanks for the v2.
I can see one of the selftest is failing, but only in 1 of the 3 selftest builds. So it might be something not fully deterministic with lldb-server installation:
2026-11-10 10:42:27,794 - oe-selftest - INFO - devtool.DevtoolIdeSdkRustTests.test_code_cargo_qemu_slirp (subunit.RemotedTestCase) 2026-11-10 10:42:27,795 - oe-selftest - INFO - ... FAIL ... 2026-11-10 10:42:27,795 - oe-selftest - INFO -testtools.testresult.real._StringException: Traceback (most recent call
last):
File
"/srv/pokybuild/yocto-worker/oe-selftest-fedora/build/layers/openembedded-core/meta/lib/oeqa/selftest/cases/devtool.py", line 4503, intest_code_cargo_qemu_slirp
self._verify_rust_lldb_debugging(tempdir, expected_input=4)
File
"/srv/pokybuild/yocto-worker/oe-selftest-fedora/build/layers/openembedded-core/meta/lib/oeqa/selftest/cases/devtool.py", line 4545, in_verify_rust_lldb_debugging
runCmd(ssh_args, output_log=self._cmd_logger)
File
"/srv/pokybuild/yocto-worker/oe-selftest-fedora/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 '['ssh', '-p', '2222', '-o',
'UserKnownHostsFile=/dev/null', '-o', 'StrictHostKeyChecking=no',
'root@localhost', "/bin/sh-c 'grep -q :04F2/proc/net/tcp/proc/net/tcp6
2>/dev/null && exit 0; mkdir -p/tmp/lldb_server_1266_usr-bin-cargo-example_multi; cd/tmp/lldb_server_1266_usr-bin-cargo-example_multi;/usr/bin/lldb-server
platform --server --listen *:1266 --gdbserver-port 1267 >/tmp/lldb_server_1266_usr-bin-cargo-example_multi/lldb_server.log2>&1 &
_lldb_server_pid=$!; echo $_lldb_server_pid >/tmp/lldb_server_1266_usr-bin-cargo-example_multi/lldb_server.pid; _w=0;
while ! grep -q :04F2/proc/net/tcp/proc/net/tcp62>/dev/null; do kill -0
$_lldb_server_pid 2>/dev/null || { cat/tmp/lldb_server_1266_usr-bin-cargo-example_multi/lldb_server.log>&2; echo
lldb-server exited before it started listening on port 1266 >&2; exit 1; };
_w=$((_w+1)); [ $_w -lt 600 ] || { kill $_lldb_server_pid 2>/dev/null; echo
lldb-server did not start on port 1266 after $_w retries >&2; cat/tmp/lldb_server_1266_usr-bin-cargo-example_multi/lldb_server.log>&2; exit
1; }; sleep 0.1; done;'"]' returned non-zero exit status 1:
Warning: Permanently added '[localhost]:2222' (RSA) to the list of known
hosts./bin/sh: line 0:/usr/bin/lldb-server: not found
lldb-server exited before it started listening on port 1266
Quoted 7 lines
https://autobuilder.yoctoproject.org/valkyrie/#/builders/48/builds/5043
Can you have a look at the issue?
I launched a rebuild of that build, out of curiosity. But in all cases, we should understand what happened here.
This can be tricky. The explanation could be that a test
1. devtool ide-sdk adds the lldb-server to the image and builds it
2. The test
1. starts Qemu
2. starts lldb-server in Qemu which goes wrong because lldb-server is not in the image
That can happen if one test instance tries to connect to the wrong runqemu instance (e.g. one with gdb-server installed) respectively the port exposed on the build machine. There is a patch series which tries to fix that. But v1 was rejected because it used Qemu's human readable QMP API instead of the machine readable JSON API. v2 is still WIP
https://git.openembedded.org/openembedded-core-contrib/log/?h=adrianf/runqemu-use-qmp-for-port-alloc.
It requires adding some APIs to Qemu upstream. Not a trivial quick fix. I was also lowering the priority on that because it seemed like working.
Probably this issue is not directly introduced by this patch series. But the more tests we add, the more likely it becomes that two of them run in parallel and fail in that wy.
Regards,
Adrian
Quoted 20 lines
Thanks,
Mathieu--
Mathieu Dubois-Briand, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group. View/Reply Online (#249920):
https://lists.openembedded.org/g/openembedded-core/message/249920
Mute This Topic: https://lists.openembedded.org/mt/122173943/4454582
Group Owner: [email protected]
Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [ [email protected]]
-=-=-=-=-=-=-=-=-=-=-=-