Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 51 additions & 19 deletions components/rust/core/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ from build_support import (
verify_rust_toolchain,
ensure_rust_target_installed,
cargo_build_staticlib,
get_staticlib_link_name,
clean_rust_build,
)
def _has(sym: str) -> bool:
Expand All @@ -20,10 +21,27 @@ def _has(sym: str) -> bool:
return bool(GetDepend(sym))


# Source files – MSH command glue
src = ['rust_cmd.c']
group = []
if not _has('RT_RUST_CORE'):
Return('group')


def get_staticlib_link_name_from_artifact(lib_path):
"""Derive the link name from the actual staticlib artifact file name."""
artifact_name = os.path.basename(os.fspath(lib_path))
if artifact_name.startswith("lib") and artifact_name.endswith(".a"):
return artifact_name[3:-2]
return None


# Source files – MSH command glue.
# rust_cmd.c references rust_init(), which is only provided by the Rust core
# static library. It must only enter the build when that library is actually
# produced; otherwise the C link stage fails with 'undefined reference to
# rust_init' instead of failing/skipping cleanly at the Rust build step.
LIBS = []
LIBPATH = []
include_rust_cmd = False

if GetOption('clean'):
# Register Rust artifacts for cleaning
Expand All @@ -33,9 +51,12 @@ if GetOption('clean'):
Clean('.', rust_build_dir)
else:
print('No rust build artifacts to clean')
# Keep rust_cmd.c in the group during clean so its object is cleaned too.
include_rust_cmd = True
else:
if verify_rust_toolchain():
import rtconfig
rust_build_dir = clean_rust_build(Dir('#').abspath)

target = detect_rust_target(_has, rtconfig)
if not target:
Expand All @@ -44,28 +65,39 @@ else:
print(f'Detected Rust target: {target}')

# Optional hint if target missing
ensure_rust_target_installed(target)

# Build mode and features
debug = bool(_has('RUST_DEBUG_BUILD'))
features = collect_features(_has)
if not ensure_rust_target_installed(target):
print('Warning: Rust target is not installed; skipping Rust library build')
else:
# Build mode and features
debug = bool(_has('RUST_DEBUG_BUILD'))
features = collect_features(_has)

rustflags = make_rustflags(rtconfig, target)
rust_lib = cargo_build_staticlib(
rust_dir=cwd, target=target, features=features, debug=debug, rustflags=rustflags
)
rustflags = make_rustflags(rtconfig, target)
rust_lib = cargo_build_staticlib(
rust_dir=cwd, target=target, features=features, debug=debug, rustflags=rustflags, build_root=rust_build_dir
)

if rust_lib:
LIBS = ['rt_rust']
LIBPATH = [os.path.dirname(rust_lib)]
print('Rust library linked successfully')
else:
print('Warning: Failed to build Rust library')
if rust_lib:
# Derive the link name from the actual artifact so it always
# matches the file that was built. Only fall back to
# re-parsing Cargo.toml when the artifact name does not
# follow the lib<name>.a convention.
link_lib_name = get_staticlib_link_name_from_artifact(rust_lib)
if not link_lib_name:
link_lib_name = get_staticlib_link_name(cwd)
LIBS = [link_lib_name]
LIBPATH = [os.path.dirname(rust_lib)]
include_rust_cmd = True
print('Rust library linked successfully')
else:
print('Warning: Failed to build Rust library')
else:
print('Warning: Rust toolchain not found')
print('Please install Rust from https://rustup.rs')

# Define component group for SCons
group = DefineGroup('rust', src, depend=['RT_USING_RUST'], LIBS=LIBS, LIBPATH=LIBPATH)
# Only define the component group (with rust_cmd.c) when the Rust core static
# library was actually produced, so its rust_init() reference always resolves.
if include_rust_cmd:
group = DefineGroup('rust', ['rust_cmd.c'], depend=['RT_USING_RUST', 'RT_RUST_CORE'], LIBS=LIBS, LIBPATH=LIBPATH)

Return('group')
4 changes: 2 additions & 2 deletions components/rust/examples/application/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def load_extended_feature_configs():

group = []

if not _has('RT_RUST_BUILD_APPLICATIONS'):
if not (_has('RT_RUST_BUILD_APPLICATIONS') or _has('RT_RUST_BUILD_ALL_EXAMPLES')):
Return('group')

# Load extended feature configurations
Expand Down Expand Up @@ -67,4 +67,4 @@ group = DefineGroup(
LINKFLAGS=LINKFLAGS
)

Return('group')
Return('group')
5 changes: 2 additions & 3 deletions components/rust/examples/component/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,11 @@ def _has(sym: str) -> bool:
return bool(GetDepend(sym))


# Early return if Rust or log component is not enabled
if not _has('RT_USING_RUST'):
group = []
Return('group')

if not _has('RUST_LOG_COMPONENT'):
if not (_has('RT_RUST_BUILD_COMPONENTS') or _has('RT_RUST_BUILD_ALL_EXAMPLES')):
group = []
Return('group')

Expand Down Expand Up @@ -68,4 +67,4 @@ group = DefineGroup(
LINKFLAGS=LINKFLAGS
)

Return('group')
Return('group')
92 changes: 77 additions & 15 deletions components/rust/examples/modules/SConscript
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import os
import sys
import subprocess
import toml
from building import *

cwd = GetCurrentDir()
Expand All @@ -25,13 +24,27 @@ sys.path.insert(0, tools_dir)

from build_support import detect_rust_target, ensure_rust_target_installed, clean_rust_build

MODULE_CONFIG_MAP = {
'example_lib': 'RT_RUST_MODULE_SIMPLE_MODULE',
}

def _has(sym: str) -> bool:
"""Helper function to check if a configuration symbol is enabled"""
try:
return bool(GetDepend([sym]))
except Exception:
return bool(GetDepend(sym))

def should_build_module(module_dir):
if _has('RT_RUST_BUILD_ALL_EXAMPLES'):
return True

config = MODULE_CONFIG_MAP.get(os.path.basename(module_dir))
if config:
return _has(config)

return True

def detect_target_for_dynamic_modules():
"""
Detect the appropriate Rust target for dynamic modules.
Expand All @@ -58,19 +71,43 @@ def detect_target_for_dynamic_modules():
print("Error: Unable to detect appropriate Rust target for dynamic modules")
raise RuntimeError("Target detection failed - no valid target found")

def _decode_cargo_output(output):
"""Decode cargo stdout/stderr which may be bytes or str."""
if output is None:
return ""
if isinstance(output, bytes):
return output.decode(errors="replace")
return str(output)

def build_rust_module(module_dir, build_root):
"""Build a Rust dynamic module with automatic target detection"""
cargo_toml_path = os.path.join(module_dir, 'Cargo.toml')
if not os.path.exists(cargo_toml_path):
return [], [], ""

try:
import toml
except ImportError:
print(f"Warning: Failed to parse {cargo_toml_path}: missing toml module")
return [], [], ""

with open(cargo_toml_path, 'r') as f:
cargo_config = toml.load(f)

module_name = cargo_config['package']['name']
try:
with open(cargo_toml_path, 'r') as f:
cargo_config = toml.load(f)
module_name = cargo_config['package']['name']
lib_name = cargo_config.get('lib', {}).get('name') or module_name
link_lib_name = lib_name.replace('-', '_')
except Exception as e:
print(f"Warning: Failed to parse module metadata from {cargo_toml_path}: {e}")
return [], [], ""

# Detect target automatically based on the current configuration
target = detect_target_for_dynamic_modules()
try:
target = detect_target_for_dynamic_modules()
except RuntimeError as e:
print(f"Warning: Failed to detect Rust target for module '{module_name}': {e}")
return [], [], ""

print(f"Building Rust module '{module_name}' for target: {target}")

# Detect debug mode from rtconfig (same as main rust/SConscript)
Expand All @@ -92,7 +129,14 @@ def build_rust_module(module_dir, build_root):

# Set up build environment
env = os.environ.copy()
env['RUSTFLAGS'] = rustflags
previous_rustflags = env.get('RUSTFLAGS', '').strip()
new_rustflags = rustflags.strip() if rustflags else ''
if previous_rustflags and new_rustflags:
env['RUSTFLAGS'] = f'{previous_rustflags} {new_rustflags}'
elif new_rustflags:
env['RUSTFLAGS'] = new_rustflags
elif previous_rustflags:
env['RUSTFLAGS'] = previous_rustflags
env['CARGO_TARGET_DIR'] = build_root

# Build the module with configurable parameters using dictionary configuration
Expand All @@ -116,12 +160,30 @@ def build_rust_module(module_dir, build_root):
try:
subprocess.run(build_cmd, cwd=module_dir, env=env, check=True, capture_output=True)
lib_dir = os.path.join(build_root, target, build_mode)
return [module_name], [lib_dir], ""
except subprocess.CalledProcessError:
artifact_name = f"lib{link_lib_name}.so"
artifact_path = os.path.join(lib_dir, artifact_name)
if not os.path.isfile(artifact_path) or os.path.getsize(artifact_path) == 0:
print(f"Warning: Rust module artifact not found, is not a file, or is empty: {artifact_path}")
return [], [], ""
return [link_lib_name], [lib_dir], ""
except FileNotFoundError:
print("Error: cargo executable not found. Please install Rust/Cargo and ensure it is in PATH.")
return [], [], ""
except subprocess.CalledProcessError as e:
print(f"Error: Failed to build Rust module: {e}")
stdout = _decode_cargo_output(getattr(e, "stdout", None) or getattr(e, "output", None)).strip()
stderr = _decode_cargo_output(getattr(e, "stderr", None)).strip()
if stdout:
print(stdout)
if stderr:
print(stderr)
return [], [], ""

# Check dependencies
if not _has('RT_RUST_BUILD_MODULES'):
if not _has('RT_USING_MODULE'):
Return([])

if not (_has('RT_RUST_BUILD_MODULES') or _has('RT_RUST_BUILD_ALL_EXAMPLES')):
Return([])

build_root = os.path.join(Dir('#').abspath, "build", "rust_modules")
Expand All @@ -142,7 +204,7 @@ else:
modules_built = []
for item in os.listdir(cwd):
item_path = os.path.join(cwd, item)
if os.path.isdir(item_path) and os.path.exists(os.path.join(item_path, 'Cargo.toml')):
if os.path.isdir(item_path) and os.path.exists(os.path.join(item_path, 'Cargo.toml')) and should_build_module(item_path):
result = build_rust_module(item_path, build_root)
if result[0]:
modules_built.extend(result[0])
Expand All @@ -151,9 +213,9 @@ else:
print(f"Successfully built {len(modules_built)} Rust dynamic module(s): {', '.join(modules_built)}")

group = DefineGroup(
'rust_modules',
[],
depend=['RT_RUST_BUILD_MODULES']
'rust_modules',
[],
depend=['RT_USING_MODULE']
)

Return('group')
Return('group')
2 changes: 1 addition & 1 deletion components/rust/examples/modules/example_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ edition = "2024"
crate-type = ["cdylib"]

[dependencies]
rt-rust = { path = "../../.." }
rt-rust = { path = "../../../core" }
Loading
Loading