build_support.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import os
  2. import subprocess
  3. def _parse_cflags(cflags: str):
  4. info = {
  5. "march": None,
  6. "mabi": None,
  7. "rv_bits": None, # 32 or 64
  8. "has_f": False,
  9. "has_d": False,
  10. }
  11. if not cflags:
  12. return info
  13. parts = cflags.split()
  14. for flag in parts:
  15. if flag.startswith("-march="):
  16. info["march"] = flag.split("=", 1)[1]
  17. if "rv32" in info["march"]:
  18. info["rv_bits"] = 32
  19. elif "rv64" in info["march"]:
  20. info["rv_bits"] = 64
  21. # crude feature detection
  22. m = info["march"]
  23. if m:
  24. info["has_f"] = ("f" in m)
  25. info["has_d"] = ("d" in m)
  26. elif flag.startswith("-mabi="):
  27. info["mabi"] = flag.split("=", 1)[1]
  28. if info["mabi"] in ("ilp32d", "ilp32f", "lp64d", "lp64f"):
  29. # floating-point ABI implies FPU availability
  30. info["has_f"] = True
  31. info["has_d"] = info["mabi"].endswith("d")
  32. return info
  33. def detect_rust_target(has, rtconfig):
  34. """
  35. Decide the Rust target triple based on RT-Thread Kconfig and rtconfig.*.
  36. `has` is a callable: has("SYMBOL") -> bool
  37. """
  38. # ARM Cortex-M
  39. if has("ARCH_ARM"):
  40. # FPU hints from flags/macros
  41. cflags = getattr(rtconfig, "CFLAGS", "")
  42. hard_float = "-mfloat-abi=hard" in cflags or has("ARCH_ARM_FPU") or has("ARCH_FPU_VFP")
  43. if has("ARCH_ARM_CORTEX_M3"):
  44. return "thumbv7m-none-eabi"
  45. if has("ARCH_ARM_CORTEX_M4") or has("ARCH_ARM_CORTEX_M7"):
  46. return "thumbv7em-none-eabihf" if hard_float else "thumbv7em-none-eabi"
  47. if has("ARCH_ARM_CORTEX_M33"):
  48. # v8m.main
  49. return "thumbv8m.main-none-eabi"
  50. if has("ARCH_ARM_CORTEX_A"):
  51. return "armv7a-none-eabi"
  52. # AArch64
  53. if has("ARCH_AARCH64") or has("ARCH_ARMV8") or has("ARCH_ARM64"):
  54. if has("ARCH_CPU_FLOAT_ABI_SOFT"):
  55. return "aarch64-unknown-none-softfloat"
  56. return "aarch64-unknown-none"
  57. # RISC-V
  58. if has("ARCH_RISCV32") or has("ARCH_RISCV64"):
  59. cflags = getattr(rtconfig, "CFLAGS", "")
  60. info = _parse_cflags(cflags)
  61. # fallback to Kconfig hint if march not present
  62. rv_bits = info["rv_bits"] or (32 if has("ARCH_RISCV32") else 64)
  63. # ABI must carry f/d to actually use hard-float calling convention
  64. abi = info["mabi"] or ""
  65. abi_has_fp = abi.endswith("f") or abi.endswith("d")
  66. if rv_bits == 32:
  67. # Only pick *f* target when ABI uses hard-float; otherwise use soft-float even if core has F/D
  68. return "riscv32imafc-unknown-none-elf" if abi_has_fp else "riscv32imac-unknown-none-elf"
  69. else:
  70. # rv64: prefer gc (includes fd) only when ABI uses hard-float
  71. return "riscv64gc-unknown-none-elf" if abi_has_fp else "riscv64imac-unknown-none-elf"
  72. # Fallback by ARCH string or CFLAGS
  73. arch = getattr(rtconfig, "ARCH", None)
  74. if arch:
  75. arch_l = arch.lower()
  76. if "aarch64" in arch_l:
  77. return "aarch64-unknown-none"
  78. if "arm" == arch_l or "armv7" in arch_l:
  79. return "armv7a-none-eabi"
  80. if "riscv32" in arch_l:
  81. return "riscv32imac-unknown-none-elf"
  82. if "riscv64" in arch_l or "risc-v" in arch_l:
  83. # Many BSPs use "risc-v" token; assume 64-bit for virt64
  84. return "riscv64imac-unknown-none-elf"
  85. # Parse CFLAGS for hints
  86. cflags = getattr(rtconfig, "CFLAGS", "")
  87. if "-mcpu=cortex-m3" in cflags:
  88. return "thumbv7m-none-eabi"
  89. if "-mcpu=cortex-m4" in cflags or "-mcpu=cortex-m7" in cflags:
  90. if "-mfpu=" in cflags and "-mfloat-abi=hard" in cflags:
  91. return "thumbv7em-none-eabihf"
  92. return "thumbv7em-none-eabi"
  93. if "-march=rv32" in cflags:
  94. march_val = None
  95. mabi_val = None
  96. for flag in cflags.split():
  97. if flag.startswith("-march="):
  98. march_val = flag[len("-march="):]
  99. elif flag.startswith("-mabi="):
  100. mabi_val = flag[len("-mabi="):]
  101. has_f_or_d = False
  102. if march_val and any(x in march_val for x in ("f", "d")):
  103. has_f_or_d = True
  104. if mabi_val and any(x in mabi_val for x in ("f", "d")):
  105. has_f_or_d = True
  106. return "riscv32imafc-unknown-none-elf" if has_f_or_d else "riscv32imac-unknown-none-elf"
  107. if "-march=rv64" in cflags:
  108. march_val = None
  109. mabi_val = None
  110. for flag in cflags.split():
  111. if flag.startswith("-march="):
  112. march_val = flag[len("-march="):]
  113. elif flag.startswith("-mabi="):
  114. mabi_val = flag[len("-mabi="):]
  115. has_f_or_d = False
  116. if mabi_val and (("lp64d" in mabi_val) or ("lp64f" in mabi_val)):
  117. has_f_or_d = True
  118. if march_val and any(x in march_val for x in ("f", "d")):
  119. has_f_or_d = True
  120. if mabi_val and any(x in mabi_val for x in ("f", "d")):
  121. has_f_or_d = True
  122. if has_f_or_d:
  123. return "riscv64gc-unknown-none-elf"
  124. return "riscv64imac-unknown-none-elf"
  125. return None
  126. def make_rustflags(rtconfig, target: str):
  127. rustflags = [
  128. "-C", "opt-level=z",
  129. "-C", "panic=abort",
  130. "-C", "relocation-model=static",
  131. ]
  132. if "riscv" in target:
  133. rustflags += [
  134. "-C", "code-model=medium",
  135. "-C", "link-dead-code",
  136. ]
  137. # propagate march/mabi for consistency (use link-arg for staticlib builds – harmless)
  138. cflags = getattr(rtconfig, "CFLAGS", "")
  139. for flag in cflags.split():
  140. if flag.startswith("-march=") or flag.startswith("-mabi="):
  141. rustflags += ["-C", f"link-arg={flag}"]
  142. if "thumb" in target or "aarch64" in target:
  143. rustflags += ["-C", "link-arg=-nostartfiles"]
  144. return " ".join(rustflags)
  145. def collect_features(has):
  146. feats = []
  147. if has("RT_USING_SMP"):
  148. feats.append("smp")
  149. return feats
  150. def verify_rust_toolchain():
  151. try:
  152. r1 = subprocess.run(["rustc", "--version"], capture_output=True, text=True)
  153. r2 = subprocess.run(["cargo", "--version"], capture_output=True, text=True)
  154. return r1.returncode == 0 and r2.returncode == 0
  155. except Exception:
  156. return False
  157. def ensure_rust_target_installed(target: str):
  158. try:
  159. result = subprocess.run(["rustup", "target", "list", "--installed"], capture_output=True, text=True)
  160. if result.returncode == 0 and target in result.stdout:
  161. return True
  162. print(f"Rust target '{target}' is not installed.")
  163. print(f"Please install it with: rustup target add {target}")
  164. except Exception:
  165. print("Warning: Failed to check rustup target list (rustup missing?)")
  166. return False
  167. def cargo_build_staticlib(rust_dir: str, target: str, features, debug: bool, rustflags: str = None):
  168. build_root = os.path.join((os.path.abspath(os.path.join(rust_dir, os.pardir, os.pardir))), "build", "rust")
  169. target_dir = os.path.join(build_root, "target")
  170. os.makedirs(build_root, exist_ok=True)
  171. env = os.environ.copy()
  172. if rustflags:
  173. prev = env.get("RUSTFLAGS", "").strip()
  174. env["RUSTFLAGS"] = (prev + " " + rustflags).strip() if prev else rustflags
  175. env["CARGO_TARGET_DIR"] = target_dir
  176. cmd = ["cargo", "build", "--target", target, "--manifest-path", os.path.join(rust_dir, "Cargo.toml")]
  177. if not debug:
  178. cmd.insert(2, "--release")
  179. if features:
  180. cmd += ["--no-default-features", "--features", ",".join(features)]
  181. print("Building Rust component (cargo)…")
  182. res = subprocess.run(cmd, cwd=rust_dir, env=env, capture_output=True, text=True)
  183. if res.returncode != 0:
  184. print("Warning: Rust build failed")
  185. if res.stderr:
  186. print(res.stderr)
  187. return None
  188. mode = "debug" if debug else "release"
  189. lib_path = os.path.join(target_dir, target, mode, "librt_rust.a")
  190. if os.path.exists(lib_path):
  191. print("Rust component built successfully")
  192. return lib_path
  193. print("Warning: Library not found at expected location")
  194. return None
  195. def clean_rust_build(bsp_root: str, artifact_type: str = "rust"):
  196. """Return the build directory path for SCons Clean operation"""
  197. build_dir = os.path.join(bsp_root, "build", artifact_type)
  198. return build_dir