use std::{env, path::PathBuf}; static SOURCES: &[&str] = &[ "barrier.c", "cond.c", "event.c", "list.c", "mutex.c", "notify.c", "queue.c", "rt.c", "rwlock.c", "sem.c", "sleep.c", "timer.c", ]; struct Arch<'a> { name: &'a str, sources: &'a [&'a str], flags: &'a [&'a str], defs: &'a [(&'a str, Option<&'a str>)], } static SIGNAL_ARCH: Arch = Arch { name: "signal", sources: &["signal.c", "syscall.S"], flags: &["-pthread"], defs: &[("_GNU_SOURCE", None)], }; static ARM_ARCH: Arch = Arch { name: "arm", sources: &["arm.c", "syscall.S"], flags: &["-fshort-enums"], // arm-none-eabi-gcc and rustc use this by default, but clang/bindgen don't defs: &[], }; static RISCV_ARCH: Arch = Arch { name: "riscv", sources: &["riscv.c", "syscall.S"], flags: &["-march=rv32imac_zicsr"], defs: &[], }; struct ArmArFeature<'a> { name: &'a str, path: &'a str, arch_patterns: &'a [&'a str], } static ARM_AR_FEATURES: &[ArmArFeature] = &[ ArmArFeature { name: "HERCULES", path: "hercules", arch_patterns: &["armebv7r", "armv7r"], }, ArmArFeature { name: "SITARA_VIM", path: "sitara/vim", arch_patterns: &["armv7r"], }, ArmArFeature { name: "SITARA_INTC", path: "sitara/intc", arch_patterns: &["armv7a"], }, ]; static ARM_AR_PATTERNS: &[&str] = &["armv7r", "armebv7r", "armv8r", "armv7a"]; static ARM_MPU_ARCH_PATTERNS: &[&str] = &[ "thumbv6m", "thumbv7m", "thumbv7em", "thumbv8m.base", "thumbv8m.main", "armv7r", "armebv7r", "armv8r", ]; fn main() { let root_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); let target = env::var("TARGET").unwrap(); let rt_cflags: Vec = env::var("RT_CFLAGS").map_or(vec![], |s| s.split(' ').map(String::from).collect()); println!("cargo:rerun-if-env-changed=RT_CFLAGS"); let arch = if target_arch == "arm" && target_os == "none" { &ARM_ARCH } else if target_arch == "riscv32" && target_os == "none" { &RISCV_ARCH } else if ["linux", "macos"].contains(&target_os.as_str()) && ["aarch64", "x86_64"].contains(&target_arch.as_str()) { &SIGNAL_ARCH } else { panic!("Unsupported (arch, os) combination: ({target_arch}, {target_os})") }; let src_dir = root_dir.join("src"); let include_dir = root_dir.join("include"); let arch_dir = root_dir.join("arch").join(arch.name); let arch_include_dir = arch_dir.join("include"); let mut cc = cc::Build::new(); cc.files(SOURCES.iter().map(|s| src_dir.join(s))); cc.files(arch.sources.iter().map(|s| arch_dir.join(s))); cc.include(&include_dir); cc.include(arch_dir.join("include")); cc.flag("-std=c17"); for flag in arch.flags { cc.flag(flag); } for &(name, val) in arch.defs { cc.define(name, val); } for flag in rt_cflags.iter() { cc.flag(flag); } let mut feature_defs: Vec<&'static str> = Vec::new(); if env::var("CARGO_FEATURE_TASK_MPU").is_ok() { assert!( (target_arch == "arm" || target_arch == "riscv32") && target_os == "none", "The task-mpu feature is not supported on {target}.", ); feature_defs.push("RT_MPU_TASK_REGIONS_ENABLE"); } for def in feature_defs.iter() { cc.define(def, None); } if arch.name == "arm" { cc.flag("-flto"); cc.flag("-ffat-lto-objects"); if target.contains("armeb") { cc.compiler("armeb-none-eabi-gcc"); cc.archiver("armeb-none-eabi-gcc-ar"); } else { cc.compiler("arm-none-eabi-gcc"); cc.archiver("arm-none-eabi-gcc-ar"); } let mut arm_ar_feature_already_set = false; for feat in ARM_AR_FEATURES { if env::var(format!("CARGO_FEATURE_{}", feat.name)).is_ok() { // Prevent multiple arm/ar features from being enabled. assert!(!arm_ar_feature_already_set); // Check that the architecture matches what the feature expects. assert!(feat.arch_patterns.iter().any(|pat| target.contains(pat))); cc.include(arch_dir.join("ar").join(feat.path)); arm_ar_feature_already_set = true; } } if ARM_AR_PATTERNS.iter().any(|pat| target.contains(pat)) { /* On A/R-profile, we must avoid using VFP registers in rt itself, so the lazy FP * context tracking works. If Neon is enabled for example, the compiler may attempt to * use it for list manipulation, which will be an undefined instruction exception if the * active task doesn't have the FPU enabled in CPACR. */ cc.flag("-mgeneral-regs-only"); } if ARM_MPU_ARCH_PATTERNS.iter().any(|pat| target.contains(pat)) { // Enable the MPU config if supported for the target architecture. println!("cargo:rustc-cfg=mpu"); if target.contains("v6m") { println!("cargo:rustc-cfg=mpu_armv6m"); } else if target.contains("v7r") { println!("cargo:rustc-cfg=mpu_armv7r"); } else if target.contains("v8m") { println!("cargo:rustc-cfg=mpu_armv8m"); } else if target.contains("v8r") { println!("cargo:rustc-cfg=mpu_armv8r"); } else { // v7m or v7em println!("cargo:rustc-cfg=mpu_armv7m"); } } else { // Otherwise, ensure that the task-mpu feature is not enabled. assert!( env::var("CARGO_FEATURE_TASK_MPU").is_err(), "The task-mpu feature is not supported on {target}." ); } } else if arch.name == "riscv" { cc.flag("-flto"); cc.flag("-ffat-lto-objects"); println!("cargo:rustc-cfg=mpu"); println!("cargo:rustc-cfg=mpu_riscv"); if env::var("CARGO_FEATURE_RISCV_ESP32").is_ok() { cc.compiler("riscv32-esp-elf-gcc"); cc.archiver("riscv32-esp-elf-gcc-ar"); cc.include(arch_dir.join("esp32")); } } println!("cargo:rustc-check-cfg=cfg(mpu)"); println!("cargo:rustc-check-cfg=cfg(mpu_armv6m)"); println!("cargo:rustc-check-cfg=cfg(mpu_armv7m)"); println!("cargo:rustc-check-cfg=cfg(mpu_armv8m)"); println!("cargo:rustc-check-cfg=cfg(mpu_armv7r)"); println!("cargo:rustc-check-cfg=cfg(mpu_armv8r)"); println!("cargo:rustc-check-cfg=cfg(mpu_riscv)"); cc.compile("rt"); let wrapper = "rust/wrapper.h"; println!("cargo:rerun-if-changed={}", wrapper); for d in [&src_dir, &include_dir, &arch_dir] { println!("cargo:rerun-if-changed={}", d.display()); } let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); let arch_def_flags = arch.defs.iter().map(|(m, ov)| match ov { Some(v) => format!("-D{m}={v}"), None => format!("-D{m}"), }); let feature_def_flags = feature_defs.iter().map(|m| format!("-D{m}")); bindgen::builder() .header(wrapper) .clang_arg(format!("-I{}", include_dir.display())) .clang_arg(format!("-I{}", arch_include_dir.display())) .clang_args(arch_def_flags) .clang_args(arch.flags) .clang_args(rt_cflags) .clang_args(feature_def_flags) .disable_nested_struct_naming() .use_core() .default_enum_style(bindgen::EnumVariation::NewType { is_bitfield: false, is_global: false, }) .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .generate() .expect("Failed to generate bindings.") .write_to_file(out_dir.join("bindings.rs")) .expect("Failed to write bindings."); }