summaryrefslogtreecommitdiff
path: root/components/rust/tools
diff options
context:
space:
mode:
authorzhang san <[email protected]>2025-12-08 18:34:25 +0800
committerGitHub <[email protected]>2025-12-08 18:34:25 +0800
commit69980f8b9d29b38dbd4ddf6373b4b45f3a138409 (patch)
tree21dd9a8ce80e3d30192e9ffa56ea39a199bae309 /components/rust/tools
parentcd1d47b87c5d47e131730e5506eb3ad77d357653 (diff)
Adding Rust Language Support for RT-Thread #10910
Diffstat (limited to 'components/rust/tools')
-rw-r--r--components/rust/tools/__init__.py1
-rw-r--r--components/rust/tools/build_component.py200
-rw-r--r--components/rust/tools/build_support.py233
-rw-r--r--components/rust/tools/build_usrapp.py392
-rw-r--r--components/rust/tools/feature_config_component.py30
-rw-r--r--components/rust/tools/feature_config_examples.py26
6 files changed, 882 insertions, 0 deletions
diff --git a/components/rust/tools/__init__.py b/components/rust/tools/__init__.py
new file mode 100644
index 0000000000..cdce003940
--- /dev/null
+++ b/components/rust/tools/__init__.py
@@ -0,0 +1 @@
+# User application build tools package \ No newline at end of file
diff --git a/components/rust/tools/build_component.py b/components/rust/tools/build_component.py
new file mode 100644
index 0000000000..8b604f68b8
--- /dev/null
+++ b/components/rust/tools/build_component.py
@@ -0,0 +1,200 @@
+import os
+import subprocess
+
+
+# Configuration to feature mapping table for components
+# This table defines which RT-Thread configurations should enable which component features
+# All feature configurations are now defined in feature_config_component.py
+CONFIG_COMPONENT_FEATURE_MAP = {}
+
+
+class ComponentBuildError(Exception):
+ pass
+
+
+def check_component_dependencies(component_dir, required_dependencies):
+ """
+ Check if a component has the required dependencies
+
+ Args:
+ component_dir: Component directory path
+ required_dependencies: List of dependency names to check
+
+ Returns:
+ bool: True if all required dependencies are present
+ """
+ if not component_dir or not required_dependencies:
+ return True
+
+ cargo_toml_path = os.path.join(component_dir, 'Cargo.toml')
+ if not os.path.exists(cargo_toml_path):
+ return False
+
+ try:
+ import toml
+ with open(cargo_toml_path, 'r') as f:
+ cargo_data = toml.load(f)
+
+ dependencies = cargo_data.get('dependencies', {})
+
+ # Check if all required dependencies are present
+ for dep in required_dependencies:
+ if dep not in dependencies:
+ return False
+
+ return True
+
+ except Exception as e:
+ print(f"Warning: Failed to parse {cargo_toml_path}: {e}")
+ return False
+
+
+def collect_component_features(has_func, component_dir=None):
+ """
+ Collect component features based on RT-Thread configuration using extensible mapping table
+
+ Args:
+ has_func: Function to check if a configuration is enabled
+ component_dir: Component directory to check dependencies (optional)
+
+ Returns:
+ list: List of features to enable
+ """
+ features = []
+
+ # Iterate through all configured mappings
+ for config_name, config_info in CONFIG_COMPONENT_FEATURE_MAP.items():
+ # Check if this RT-Thread configuration is enabled
+ if has_func(config_name):
+ feature_name = config_info['feature']
+ required_deps = config_info.get('dependencies', [])
+
+ # If component_dir is provided, check dependencies
+ if component_dir:
+ if check_component_dependencies(component_dir, required_deps):
+ features.append(feature_name)
+ print(f"Enabling component feature '{feature_name}' for {config_name} in {os.path.basename(component_dir)}")
+ else:
+ # If no component_dir provided, enable for all (backward compatibility)
+ features.append(feature_name)
+ print(f"Enabling component feature '{feature_name}' for {config_name}")
+
+ return features
+
+def cargo_build_component_staticlib(rust_dir, target, features, debug, rustflags=None, build_root=None):
+ """
+ Build a Rust component as a static library using Cargo.
+
+ Args:
+ rust_dir: Directory containing the Rust component
+ target: Rust target architecture
+ features: List of features to enable
+ debug: Whether this is a debug build
+ rustflags: Additional Rust compilation flags
+ build_root: Build root directory (if not provided, will raise error)
+
+ Returns:
+ str: Path to the built library file, or None if build failed
+ """
+ if not build_root:
+ raise ComponentBuildError("build_root parameter is required")
+
+ build_root = os.path.abspath(build_root)
+ os.makedirs(build_root, exist_ok=True)
+
+ env = os.environ.copy()
+ if rustflags:
+ prev = env.get("RUSTFLAGS", "").strip()
+ env["RUSTFLAGS"] = (prev + " " + rustflags).strip() if prev else rustflags
+ env["CARGO_TARGET_DIR"] = build_root
+
+ cmd = [
+ "cargo", "build",
+ "--target", target,
+ "--manifest-path", os.path.join(rust_dir, "Cargo.toml")
+ ]
+
+ if not debug:
+ cmd.insert(2, "--release")
+
+ if features:
+ cmd += ["--no-default-features", "--features", ",".join(features)]
+
+ print("Building example component log (cargo)…")
+ res = subprocess.run(cmd, cwd=rust_dir, env=env, capture_output=True, text=True)
+
+ if res.returncode != 0:
+ print("Warning: Example component build failed")
+ if res.stderr:
+ print(res.stderr)
+ return None
+
+ mode = "debug" if debug else "release"
+
+ # Try target-specific path first, then fallback to direct path
+ lib_path = os.path.join(build_root, target, mode, "libem_component_registry.a")
+ if os.path.exists(lib_path):
+ print("Example component log built successfully")
+ return lib_path
+
+ print("Warning: Library not found at expected location")
+ print(f"Expected: {lib_path}")
+ return None
+
+
+def build_example_component(cwd, has_func, rtconfig, build_root=None):
+ """
+ Build the example component.
+
+ Args:
+ cwd: Current working directory (component directory)
+ has_func: Function to check if a configuration is enabled
+ rtconfig: RT-Thread configuration module
+ build_root: Optional build root directory
+
+ Returns:
+ tuple: (LIBS, LIBPATH, LINKFLAGS) for SCons
+ """
+ LIBS = []
+ LIBPATH = []
+ LINKFLAGS = ""
+
+ # Import build support functions
+ import sys
+ sys.path.append(os.path.join(cwd, '../rust/tools'))
+ from build_support import (
+ detect_rust_target,
+ make_rustflags,
+ )
+
+ target = detect_rust_target(has_func, rtconfig)
+
+ # Build mode and features
+ debug = bool(has_func('RUST_DEBUG_BUILD'))
+
+ # Build the component registry
+ registry_dir = os.path.join(cwd, 'component_registry')
+ features = collect_component_features(has_func, registry_dir)
+
+ rustflags = make_rustflags(rtconfig, target)
+
+ rust_lib = cargo_build_component_staticlib(
+ rust_dir=registry_dir,
+ target=target,
+ features=features,
+ debug=debug,
+ rustflags=rustflags,
+ build_root=build_root
+ )
+
+ if rust_lib:
+ LIBS = ['em_component_registry']
+ LIBPATH = [os.path.dirname(rust_lib)]
+ # Add LINKFLAGS to ensure component is linked into final binary
+ LINKFLAGS += " -Wl,--whole-archive -lem_component_registry -Wl,--no-whole-archive"
+ LINKFLAGS += " -Wl,--allow-multiple-definition"
+ print('Example component registry library linked successfully')
+ else:
+ print('Warning: Failed to build example component registry library')
+
+ return LIBS, LIBPATH, LINKFLAGS \ No newline at end of file
diff --git a/components/rust/tools/build_support.py b/components/rust/tools/build_support.py
new file mode 100644
index 0000000000..eb05fc1f29
--- /dev/null
+++ b/components/rust/tools/build_support.py
@@ -0,0 +1,233 @@
+import os
+import subprocess
+
+
+def _parse_cflags(cflags: str):
+ info = {
+ "march": None,
+ "mabi": None,
+ "rv_bits": None, # 32 or 64
+ "has_f": False,
+ "has_d": False,
+ }
+
+ if not cflags:
+ return info
+
+ parts = cflags.split()
+ for flag in parts:
+ if flag.startswith("-march="):
+ info["march"] = flag.split("=", 1)[1]
+ if "rv32" in info["march"]:
+ info["rv_bits"] = 32
+ elif "rv64" in info["march"]:
+ info["rv_bits"] = 64
+ # crude feature detection
+ m = info["march"]
+ if m:
+ info["has_f"] = ("f" in m)
+ info["has_d"] = ("d" in m)
+ elif flag.startswith("-mabi="):
+ info["mabi"] = flag.split("=", 1)[1]
+ if info["mabi"] in ("ilp32d", "ilp32f", "lp64d", "lp64f"):
+ # floating-point ABI implies FPU availability
+ info["has_f"] = True
+ info["has_d"] = info["mabi"].endswith("d")
+
+ return info
+
+
+def detect_rust_target(has, rtconfig):
+ """
+ Decide the Rust target triple based on RT-Thread Kconfig and rtconfig.*.
+ `has` is a callable: has("SYMBOL") -> bool
+ """
+ # ARM Cortex-M
+ if has("ARCH_ARM"):
+ # FPU hints from flags/macros
+ cflags = getattr(rtconfig, "CFLAGS", "")
+ hard_float = "-mfloat-abi=hard" in cflags or has("ARCH_ARM_FPU") or has("ARCH_FPU_VFP")
+
+ if has("ARCH_ARM_CORTEX_M3"):
+ return "thumbv7m-none-eabi"
+ if has("ARCH_ARM_CORTEX_M4") or has("ARCH_ARM_CORTEX_M7"):
+ return "thumbv7em-none-eabihf" if hard_float else "thumbv7em-none-eabi"
+ if has("ARCH_ARM_CORTEX_M33"):
+ # v8m.main
+ return "thumbv8m.main-none-eabi"
+ if has("ARCH_ARM_CORTEX_A"):
+ return "armv7a-none-eabi"
+
+ # AArch64
+ if has("ARCH_AARCH64") or has("ARCH_ARMV8") or has("ARCH_ARM64"):
+ if has("ARCH_CPU_FLOAT_ABI_SOFT"):
+ return "aarch64-unknown-none-softfloat"
+ return "aarch64-unknown-none"
+
+ # RISC-V
+ if has("ARCH_RISCV32") or has("ARCH_RISCV64"):
+ cflags = getattr(rtconfig, "CFLAGS", "")
+ info = _parse_cflags(cflags)
+
+ # fallback to Kconfig hint if march not present
+ rv_bits = info["rv_bits"] or (32 if has("ARCH_RISCV32") else 64)
+
+ # ABI must carry f/d to actually use hard-float calling convention
+ abi = info["mabi"] or ""
+ abi_has_fp = abi.endswith("f") or abi.endswith("d")
+
+ if rv_bits == 32:
+ # Only pick *f* target when ABI uses hard-float; otherwise use soft-float even if core has F/D
+ return "riscv32imafc-unknown-none-elf" if abi_has_fp else "riscv32imac-unknown-none-elf"
+ else:
+ # rv64: prefer gc (includes fd) only when ABI uses hard-float
+ return "riscv64gc-unknown-none-elf" if abi_has_fp else "riscv64imac-unknown-none-elf"
+
+ # Fallback by ARCH string or CFLAGS
+ arch = getattr(rtconfig, "ARCH", None)
+ if arch:
+ arch_l = arch.lower()
+ if "aarch64" in arch_l:
+ return "aarch64-unknown-none"
+ if "arm" == arch_l or "armv7" in arch_l:
+ return "armv7a-none-eabi"
+ if "riscv32" in arch_l:
+ return "riscv32imac-unknown-none-elf"
+ if "riscv64" in arch_l or "risc-v" in arch_l:
+ # Many BSPs use "risc-v" token; assume 64-bit for virt64
+ return "riscv64imac-unknown-none-elf"
+
+ # Parse CFLAGS for hints
+ cflags = getattr(rtconfig, "CFLAGS", "")
+ if "-mcpu=cortex-m3" in cflags:
+ return "thumbv7m-none-eabi"
+ if "-mcpu=cortex-m4" in cflags or "-mcpu=cortex-m7" in cflags:
+ if "-mfpu=" in cflags and "-mfloat-abi=hard" in cflags:
+ return "thumbv7em-none-eabihf"
+ return "thumbv7em-none-eabi"
+ if "-march=rv32" in cflags:
+ march_val = None
+ mabi_val = None
+ for flag in cflags.split():
+ if flag.startswith("-march="):
+ march_val = flag[len("-march="):]
+ elif flag.startswith("-mabi="):
+ mabi_val = flag[len("-mabi="):]
+ has_f_or_d = False
+ if march_val and any(x in march_val for x in ("f", "d")):
+ has_f_or_d = True
+ if mabi_val and any(x in mabi_val for x in ("f", "d")):
+ has_f_or_d = True
+ return "riscv32imafc-unknown-none-elf" if has_f_or_d else "riscv32imac-unknown-none-elf"
+ if "-march=rv64" in cflags:
+ march_val = None
+ mabi_val = None
+ for flag in cflags.split():
+ if flag.startswith("-march="):
+ march_val = flag[len("-march="):]
+ elif flag.startswith("-mabi="):
+ mabi_val = flag[len("-mabi="):]
+ has_f_or_d = False
+ if mabi_val and (("lp64d" in mabi_val) or ("lp64f" in mabi_val)):
+ has_f_or_d = True
+ if march_val and any(x in march_val for x in ("f", "d")):
+ has_f_or_d = True
+ if mabi_val and any(x in mabi_val for x in ("f", "d")):
+ has_f_or_d = True
+ if has_f_or_d:
+ return "riscv64gc-unknown-none-elf"
+ return "riscv64imac-unknown-none-elf"
+
+ return None
+
+
+def make_rustflags(rtconfig, target: str):
+ rustflags = [
+ "-C", "opt-level=z",
+ "-C", "panic=abort",
+ "-C", "relocation-model=static",
+ ]
+
+ if "riscv" in target:
+ rustflags += [
+ "-C", "code-model=medium",
+ "-C", "link-dead-code",
+ ]
+ # propagate march/mabi for consistency (use link-arg for staticlib builds – harmless)
+ cflags = getattr(rtconfig, "CFLAGS", "")
+ for flag in cflags.split():
+ if flag.startswith("-march=") or flag.startswith("-mabi="):
+ rustflags += ["-C", f"link-arg={flag}"]
+
+ if "thumb" in target or "aarch64" in target:
+ rustflags += ["-C", "link-arg=-nostartfiles"]
+
+ return " ".join(rustflags)
+
+
+def collect_features(has):
+ feats = []
+ if has("RT_USING_SMP"):
+ feats.append("smp")
+ return feats
+
+
+def verify_rust_toolchain():
+ try:
+ r1 = subprocess.run(["rustc", "--version"], capture_output=True, text=True)
+ r2 = subprocess.run(["cargo", "--version"], capture_output=True, text=True)
+ return r1.returncode == 0 and r2.returncode == 0
+ except Exception:
+ return False
+
+
+def ensure_rust_target_installed(target: str):
+ try:
+ result = subprocess.run(["rustup", "target", "list", "--installed"], capture_output=True, text=True)
+ if result.returncode == 0 and target in result.stdout:
+ return True
+ print(f"Rust target '{target}' is not installed.")
+ print(f"Please install it with: rustup target add {target}")
+ except Exception:
+ print("Warning: Failed to check rustup target list (rustup missing?)")
+ return False
+
+
+def cargo_build_staticlib(rust_dir: str, target: str, features, debug: bool, rustflags: str = None):
+ build_root = os.path.join((os.path.abspath(os.path.join(rust_dir, os.pardir, os.pardir))), "build", "rust")
+ target_dir = os.path.join(build_root, "target")
+ os.makedirs(build_root, exist_ok=True)
+
+ env = os.environ.copy()
+ if rustflags:
+ prev = env.get("RUSTFLAGS", "").strip()
+ env["RUSTFLAGS"] = (prev + " " + rustflags).strip() if prev else rustflags
+ env["CARGO_TARGET_DIR"] = target_dir
+
+ cmd = ["cargo", "build", "--target", target, "--manifest-path", os.path.join(rust_dir, "Cargo.toml")]
+ if not debug:
+ cmd.insert(2, "--release")
+ if features:
+ cmd += ["--no-default-features", "--features", ",".join(features)]
+
+ print("Building Rust component (cargo)…")
+ res = subprocess.run(cmd, cwd=rust_dir, env=env, capture_output=True, text=True)
+ if res.returncode != 0:
+ print("Warning: Rust build failed")
+ if res.stderr:
+ print(res.stderr)
+ return None
+
+ mode = "debug" if debug else "release"
+ lib_path = os.path.join(target_dir, target, mode, "librt_rust.a")
+ if os.path.exists(lib_path):
+ print("Rust component built successfully")
+ return lib_path
+ print("Warning: Library not found at expected location")
+ return None
+
+
+def clean_rust_build(bsp_root: str, artifact_type: str = "rust"):
+ """Return the build directory path for SCons Clean operation"""
+ build_dir = os.path.join(bsp_root, "build", artifact_type)
+ return build_dir \ No newline at end of file
diff --git a/components/rust/tools/build_usrapp.py b/components/rust/tools/build_usrapp.py
new file mode 100644
index 0000000000..9d97a60ffe
--- /dev/null
+++ b/components/rust/tools/build_usrapp.py
@@ -0,0 +1,392 @@
+import os
+import subprocess
+import toml
+import shutil
+
+
+# Configuration to feature mapping table
+# This table defines which RT-Thread configurations should enable which Rust features
+# All feature configurations are now defined in feature_config_examples.py
+CONFIG_FEATURE_MAP = {}
+
+# Application directory to Kconfig mapping table
+# This table defines which Kconfig options control which application directories
+APP_CONFIG_MAP = {
+ 'fs': 'RT_RUST_EXAMPLE_FS',
+ 'loadlib': 'RT_RUST_EXAMPLE_LOADLIB',
+ 'mutex': 'RT_RUST_EXAMPLE_MUTEX',
+ 'param': 'RT_RUST_EXAMPLE_PARAM',
+ 'queue': 'RT_RUST_EXAMPLE_QUEUE',
+ 'semaphore': 'RT_RUST_EXAMPLE_SEMAPHORE',
+ 'thread': 'RT_RUST_EXAMPLE_THREAD'
+}
+
+
+def should_build_app(app_dir, has_func):
+ """
+ Check if an application should be built based on Kconfig configuration
+
+ Args:
+ app_dir: Application directory path
+ has_func: Function to check if a configuration is enabled
+
+ Returns:
+ bool: True if the application should be built
+ """
+ # Get the application name from the directory
+ app_name = os.path.basename(app_dir)
+
+ # Check if there's a specific Kconfig option for this app
+ if app_name in APP_CONFIG_MAP:
+ config_option = APP_CONFIG_MAP[app_name]
+ return has_func(config_option)
+
+ # If no specific config found, check if applications are enabled in general
+ return has_func('RT_RUST_BUILD_APPLICATIONS')
+
+
+def check_app_dependencies(app_dir, required_dependencies):
+ """
+ Check if an application has the required dependencies
+
+ Args:
+ app_dir: Application directory path
+ required_dependencies: List of dependency names to check
+
+ Returns:
+ bool: True if all required dependencies are present
+ """
+ if not app_dir or not required_dependencies:
+ return True
+
+ cargo_toml_path = os.path.join(app_dir, 'Cargo.toml')
+ if not os.path.exists(cargo_toml_path):
+ return False
+
+ try:
+ with open(cargo_toml_path, 'r') as f:
+ cargo_data = toml.load(f)
+
+ dependencies = cargo_data.get('dependencies', {})
+
+ # Check if all required dependencies are present
+ for dep in required_dependencies:
+ if dep not in dependencies:
+ return False
+
+ return True
+
+ except Exception as e:
+ print(f"Warning: Failed to parse {cargo_toml_path}: {e}")
+ return False
+
+
+def collect_features(has_func, app_dir=None):
+ """
+ Collect Rust features based on RT-Thread configuration using extensible mapping table
+
+ Args:
+ has_func: Function to check if a configuration is enabled
+ app_dir: Application directory to check dependencies (optional)
+
+ Returns:
+ list: List of features to enable
+ """
+ features = []
+
+ # Iterate through all configured mappings
+ for config_name, config_info in CONFIG_FEATURE_MAP.items():
+ # Check if this RT-Thread configuration is enabled
+ if has_func(config_name):
+ feature_name = config_info['feature']
+ required_deps = config_info.get('dependencies', [])
+
+ # If app_dir is provided, check dependencies
+ if app_dir:
+ if check_app_dependencies(app_dir, required_deps):
+ features.append(feature_name)
+ print(f"Enabling feature '{feature_name}' for {config_name} in {os.path.basename(app_dir)}")
+ else:
+ # If no app_dir provided, enable for all (backward compatibility)
+ features.append(feature_name)
+ print(f"Enabling feature '{feature_name}' for {config_name}")
+
+ return features
+
+
+
+
+
+class UserAppBuildError(Exception):
+ """User application build error exception"""
+ pass
+
+
+def parse_cargo_toml(cargo_toml_path):
+ """
+ Parse Cargo.toml file to extract library name and library type
+
+ Args:
+ cargo_toml_path: Path to Cargo.toml file
+
+ Returns:
+ tuple: (lib_name, is_staticlib)
+ """
+ try:
+ with open(cargo_toml_path, 'r') as f:
+ cargo_data = toml.load(f)
+
+ package_name = cargo_data.get('package', {}).get('name')
+ if not package_name:
+ raise UserAppBuildError(f"No package name found in {cargo_toml_path}")
+
+ lib_config = cargo_data.get('lib', {})
+ crate_type = lib_config.get('crate-type', [])
+ is_staticlib = 'staticlib' in crate_type
+
+ # Use lib name if specified, otherwise use package name
+ lib_name = lib_config.get('name', package_name)
+
+ return lib_name, is_staticlib
+
+ except Exception as e:
+ raise UserAppBuildError(f"Failed to parse {cargo_toml_path}: {e}")
+
+
+def discover_user_apps(base_dir):
+ """
+ Discover all user application directories
+
+ Args:
+ base_dir: Base directory path
+
+ Returns:
+ list: List of directories containing Cargo.toml
+ """
+ user_apps = []
+
+ for root, dirs, files in os.walk(base_dir):
+ if 'Cargo.toml' in files:
+ if 'target' in root or 'build' in root:
+ continue
+ user_apps.append(root)
+
+ return user_apps
+
+
+def build_user_app(app_dir, target, debug, rustflags, build_root, features=None):
+ """
+ Build a single user application
+
+ Args:
+ app_dir: Application directory
+ target: Rust target architecture
+ debug: Whether this is a debug build
+ rustflags: Rust compilation flags
+ build_root: Build root directory
+ features: List of features to enable
+
+ Returns:
+ tuple: (success, lib_name, lib_path)
+ """
+ try:
+ cargo_toml_path = os.path.join(app_dir, 'Cargo.toml')
+ lib_name, is_staticlib = parse_cargo_toml(cargo_toml_path)
+
+ if not is_staticlib:
+ return False, None, None
+
+ env = os.environ.copy()
+ env['RUSTFLAGS'] = rustflags
+ env['CARGO_TARGET_DIR'] = build_root
+
+ cmd = ['cargo', 'build', '--target', target]
+ if not debug:
+ cmd.append('--release')
+
+ # Add features if specified
+ if features:
+ cmd.extend(['--features', ','.join(features)])
+
+ print(f"Building example user app {lib_name} (cargo)…")
+ result = subprocess.run(cmd, cwd=app_dir, env=env,
+ capture_output=True, text=True)
+
+ if result.returncode != 0:
+ print(f"Failed to build user app in {app_dir}")
+ print(f"Command: {' '.join(cmd)}")
+ print(f"Return code: {result.returncode}")
+ print(f"STDOUT: {result.stdout}")
+ print(f"STDERR: {result.stderr}")
+ return False, None, None
+
+ lib_file = find_library_file(build_root, target, lib_name, debug)
+ if lib_file:
+ # Return the library name for linking
+ return True, lib_name, lib_file
+ else:
+ print(f"Library file not found for lib {lib_name}")
+ return False, None, None
+
+ except Exception as e:
+ print(f"Exception occurred while building user app in {app_dir}: {e}")
+ return False, None, None
+
+
+def find_library_file(build_root, target, lib_name, debug):
+ """
+ Find the generated library file
+
+ Args:
+ build_root: Build root directory
+ target: Rust target architecture
+ lib_name: Library name
+ debug: Whether this is a debug build
+
+ Returns:
+ str: Library file path, or None if not found
+ """
+ profile = "debug" if debug else "release"
+
+ possible_names = [
+ f"lib{lib_name}.a",
+ f"lib{lib_name.replace('-', '_')}.a"
+ ]
+
+ search_paths = [
+ os.path.join(build_root, target, profile),
+ os.path.join(build_root, target, profile, "deps")
+ ]
+
+ for search_path in search_paths:
+ if not os.path.exists(search_path):
+ continue
+
+ for name in possible_names:
+ lib_path = os.path.join(search_path, name)
+ if os.path.exists(lib_path):
+ return lib_path
+
+ return None
+
+
+def build_all_user_apps(base_dir, target, debug, rustflags, build_root, has_func):
+ """
+ Build all user applications
+
+ Args:
+ base_dir: User applications base directory
+ target: Rust target architecture
+ debug: Whether this is a debug build
+ rustflags: Rust compilation flags
+ build_root: Build root directory
+ has_func: Function to check if a configuration is enabled
+
+ Returns:
+ tuple: (LIBS, LIBPATH, success_count, total_count)
+ """
+ LIBS = []
+ LIBPATH = []
+ success_count = 0
+
+ user_apps = discover_user_apps(base_dir)
+ total_count = len(user_apps)
+
+ for app_dir in user_apps:
+ # Check if this application should be built based on Kconfig
+ if not should_build_app(app_dir, has_func):
+ app_name = os.path.basename(app_dir)
+ print(f"Skipping {app_name} (disabled in Kconfig)")
+ continue
+
+ # Collect features for this specific app
+ features = collect_features(has_func, app_dir)
+ success, lib_name, lib_path = build_user_app(app_dir, target, debug, rustflags, build_root, features)
+
+ if success and lib_path:
+ app_name = os.path.basename(app_dir)
+ print(f"Example user app {app_name} built successfully")
+ LIBS.append(lib_name)
+ lib_dir = os.path.dirname(lib_path)
+ if lib_dir not in LIBPATH:
+ LIBPATH.append(lib_dir)
+ success_count += 1
+
+ return LIBS, LIBPATH, success_count, total_count
+
+
+def generate_linkflags(LIBS, LIBPATH):
+ """
+ Generate link flags
+
+ Args:
+ LIBS: List of library names
+ LIBPATH: List of library paths
+
+ Returns:
+ str: Link flags string
+ """
+ if not LIBS or not LIBPATH:
+ return ""
+
+ linkflags = f" -L{LIBPATH[0]} -Wl,--whole-archive"
+ for lib in LIBS:
+ linkflags += f" -l{lib}"
+ linkflags += " -Wl,--no-whole-archive -Wl,--allow-multiple-definition"
+
+ return linkflags
+
+
+def clean_user_apps_build(build_root):
+ """
+ Clean user applications build artifacts
+
+ Args:
+ build_root: Build root directory
+ """
+ if os.path.exists(build_root):
+ shutil.rmtree(build_root)
+
+
+def build_example_usrapp(cwd, has_func, rtconfig, build_root=None):
+ """
+ Build the example user applications.
+
+ Args:
+ cwd: Current working directory (usrapp directory)
+ has_func: Function to check if a configuration is enabled
+ rtconfig: RT-Thread configuration module
+ build_root: Optional build root directory
+
+ Returns:
+ tuple: (LIBS, LIBPATH, LINKFLAGS) for SCons
+ """
+ LIBS = []
+ LIBPATH = []
+ LINKFLAGS = ""
+
+ try:
+ # Import build support functions
+ import sys
+ sys.path.append(os.path.join(cwd, '../rust/tools'))
+ import build_support as rust_build_support
+
+ target = rust_build_support.detect_rust_target(has_func, rtconfig)
+ debug = bool(has_func('RUST_DEBUG_BUILD'))
+ rustflags = rust_build_support.make_rustflags(rtconfig, target)
+ LIBS, LIBPATH, success_count, total_count = build_all_user_apps(
+ cwd, target, debug, rustflags, build_root, has_func
+ )
+
+ if success_count == 0 and total_count > 0:
+ print(f'Warning: Failed to build all {total_count} user applications')
+ elif success_count > 0:
+ LINKFLAGS = generate_linkflags(LIBS, LIBPATH)
+ print(f'Example user apps linked successfully')
+
+ except UserAppBuildError as e:
+ print(f'Error: {e}')
+ except Exception as e:
+ print(f'Unexpected error during user apps build: {e}')
+
+ return LIBS, LIBPATH, LINKFLAGS \ No newline at end of file
diff --git a/components/rust/tools/feature_config_component.py b/components/rust/tools/feature_config_component.py
new file mode 100644
index 0000000000..221d529abe
--- /dev/null
+++ b/components/rust/tools/feature_config_component.py
@@ -0,0 +1,30 @@
+import sys
+import os
+from building import *
+cwd = GetCurrentDir()
+sys.path.append(os.path.join(cwd, 'tools'))
+from build_component import CONFIG_COMPONENT_FEATURE_MAP
+
+def setup_all_component_features():
+ """
+ Setup all component feature mappings.
+ This function configures which RT-Thread configurations should enable which component features.
+ """
+
+ CONFIG_COMPONENT_FEATURE_MAP.update({
+ 'RUST_LOG_COMPONENT': {
+ 'feature': 'enable-log',
+ 'dependencies': ['em_component_log'],
+ 'description': 'Enable Rust logging component integration'
+ },
+ })
+
+ print("All component feature mappings have been configured!")
+
+
+if __name__ == "__main__":
+ setup_all_component_features()
+ print(f"Total component configurations: {len(CONFIG_COMPONENT_FEATURE_MAP)}")
+ print("Available component configurations:")
+ for config, info in CONFIG_COMPONENT_FEATURE_MAP.items():
+ print(f" {config}: {info['feature']} - {info['description']}") \ No newline at end of file
diff --git a/components/rust/tools/feature_config_examples.py b/components/rust/tools/feature_config_examples.py
new file mode 100644
index 0000000000..1e4befa604
--- /dev/null
+++ b/components/rust/tools/feature_config_examples.py
@@ -0,0 +1,26 @@
+import sys
+import os
+from building import *
+cwd = GetCurrentDir()
+sys.path.append(os.path.join(cwd, 'tools'))
+from build_usrapp import CONFIG_FEATURE_MAP
+
+def setup_all_example_features():
+
+ CONFIG_FEATURE_MAP.update({
+ 'RUST_LOG_COMPONENT': {
+ 'feature': 'enable-log',
+ 'dependencies': ['em_component_log'],
+ 'description': 'Enable Rust logging component integration'
+ },
+ })
+
+ print("All example feature mappings have been configured!")
+
+
+if __name__ == "__main__":
+ setup_all_example_features()
+ print(f"Total configurations: {len(CONFIG_FEATURE_MAP)}")
+ print("Available configurations:")
+ for config, info in CONFIG_FEATURE_MAP.items():
+ print(f" {config}: {info['feature']} - {info['description']}") \ No newline at end of file