Files
rt-thread/components/rust/tools/build_component.py
2025-12-08 18:34:25 +08:00

200 lines
6.5 KiB
Python

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