mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-02-06 14:01:39 +00:00
233 lines
8.3 KiB
Python
233 lines
8.3 KiB
Python
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 |