summaryrefslogtreecommitdiff
path: root/components/rust/examples/modules/SConscript
blob: dffacb839daeda36e7433f7a31ed7f59d8c4cbc1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
import os
import sys
import subprocess
from building import *

cwd = GetCurrentDir()

RUSTC_FLAGS = {
    "linker": "-C linker=ld.lld",
    "panic": "-C panic=abort",
}

CARGO_CMD = {
    "base": "cargo build",
    "build_std": "-Z build-std=core,alloc,panic_abort",
    "target_flag": "--target",
    "target_arch": "%s",  
    "release_profile": "--release",
    "debug_profile": "",  # No additional flag for debug mode
}

tools_dir = os.path.join(cwd, '..', '..', 'tools')
sys.path.insert(0, tools_dir)

from build_support import detect_rust_target, ensure_rust_target_installed, clean_rust_build

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))


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


def should_build_module(module_dir: str) -> bool:
    if _has('RT_RUST_BUILD_ALL_EXAMPLES'):
        return True

    config = MODULE_CONFIG_MAP.get(os.path.basename(module_dir))
    return bool(config and _has(config))


def detect_target_for_dynamic_modules():
    """
    Detect the appropriate Rust target for dynamic modules.
    For dynamic modules, we need Linux targets instead of bare-metal targets.
    """
    if detect_rust_target is not None:
        try:
            import rtconfig
            bare_metal_target = detect_rust_target(_has, rtconfig)
            
            if bare_metal_target:
                if "riscv64" in bare_metal_target:
                    return "riscv64gc-unknown-linux-gnu"
                elif "riscv32" in bare_metal_target:
                    return "riscv32gc-unknown-linux-gnu"
                elif "aarch64" in bare_metal_target:
                    return "aarch64-unknown-linux-gnu"
                elif "arm" in bare_metal_target or "thumb" in bare_metal_target:
                    return "armv7-unknown-linux-gnueabihf"
        except Exception as e:
            print(f"Error: Target detection failed: {e}")
            raise RuntimeError(f"Failed to detect Rust target for dynamic modules: {e}")
    
    print("Error: Unable to detect appropriate Rust target for dynamic modules")
    raise RuntimeError("Target detection failed - no valid target found")

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 [], [], ""

    import toml

    with open(cargo_toml_path, 'r') as f:
        cargo_config = toml.load(f)
    
    module_name = cargo_config['package']['name']

    # Detect target automatically based on the current configuration
    target = detect_target_for_dynamic_modules()
    print(f"Building Rust module '{module_name}' for target: {target}")
    
    # Detect debug mode from rtconfig (same as main rust/SConscript)
    debug = bool(_has('RUST_DEBUG_BUILD'))
    build_mode = "debug" if debug else "release"
    print(f"Building in {build_mode} mode")
    
    # Use global RUSTFLAGS configuration for dynamic modules
    rustflags = " ".join(RUSTC_FLAGS.values())
    
    # Verify that the target is installed
    if ensure_rust_target_installed is not None:
        if not ensure_rust_target_installed(target):
            print(f"Error: Rust target '{target}' is not installed")
            print(f"Please install it with: rustup target add {target}")
            return [], [], ""
    else:
        print(f"Warning: Cannot verify if target '{target}' is installed")
    
    # Set up build environment
    env = os.environ.copy()
    env['RUSTFLAGS'] = rustflags
    env['CARGO_TARGET_DIR'] = build_root
    
    # Build the module with configurable parameters using dictionary configuration
    build_cmd = [
        CARGO_CMD["base"].split()[0],  # 'cargo'
        CARGO_CMD["base"].split()[1],  # 'build'
        CARGO_CMD["target_flag"],      # '--target'
        target,                        # actual target architecture
    ]
    
    # Add profile flag based on debug mode
    profile_flag = CARGO_CMD["debug_profile"] if debug else CARGO_CMD["release_profile"]
    if profile_flag:
        build_cmd.append(profile_flag)
    
    # Add build-std flag if specified
    if CARGO_CMD["build_std"]:
        build_std_parts = CARGO_CMD["build_std"].split()
        build_cmd.extend(build_std_parts)  
    
    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:
        return [], [], ""

# Check dependencies
if not _has('RT_USING_MODULE') and not GetOption('clean'):
    Return([])

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

build_root = os.path.join(Dir('#').abspath, "build", "rust_modules")

# Handle clean operation
if GetOption('clean'):
    if clean_rust_build is not None:
        modules_build_dir = clean_rust_build(Dir('#').abspath, "rust_modules")
        if os.path.exists(modules_build_dir):
            print(f'Registering {modules_build_dir} for cleanup')
            Clean('.', modules_build_dir)
        else:
            print('No rust_modules build artifacts to clean')
    else:
        print('Warning: clean_rust_build function not available')
else:
    # Build all Rust modules in subdirectories
    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'))
            and should_build_module(item_path)
        ):
            result = build_rust_module(item_path, build_root)
            if result[0]:
                modules_built.extend(result[0])

    if modules_built:
        print(f"Successfully built {len(modules_built)} Rust dynamic module(s): {', '.join(modules_built)}")

group = DefineGroup(
    'rust_modules',
    [],
    depend=['RT_USING_RUST']
)

Return('group')