//! Target detection //! //! This build script translates the target for which `radium` is being compiled //! into a set of directives that the crate can use to control which atomic //! symbols it attempts to name. //! //! The compiler maintains its store of target information here: //! //! //! That module is not easily extracted into something that can be loaded here, //! so we are replicating it through string matching on the target name until //! we are able to uniquely identify targets through `cfg` checks. //! //! Use `rustc --print target-list` to enumerate the full list of targets //! available, and `rustc --print cfg` (optionally with `-Z unstable-options`) //! to see what `cfg` values are produced for a target. //! //! The missing `cfg` checks required for conditional compilation, rather than a //! build script, are: //! //! - [`accessible`](https://github.com/rust-lang/rust/issues/64797) //! - [`target_feature`](https://github.com/rust-lang/rust/issues/69098) //! - [`target_has_atomic`](https://github.com/rust-lang/rust/issues/32976) //! //! Once any of these becomes usable on the stable series, we can switch to a //! set of `cfg` checks instead of a build script. use std::{env, error::Error}; /// Collection of flags indicating whether the target processor supports atomic /// instructions for a certain width. #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] struct Atomics { /// Target supports 8-bit atomics has_8: bool, /// Target supports 16-bit atomics has_16: bool, /// Target supports 32-bit atomics has_32: bool, /// Target supports 64-bit atomics has_64: bool, /// Target supports word-width atomics has_ptr: bool, } impl Atomics { const ALL: Self = Self { has_8: true, has_16: true, has_32: true, has_64: true, has_ptr: true, }; const NONE: Self = Self { has_8: false, has_16: false, has_32: false, has_64: false, has_ptr: false, }; } fn main() -> Result<(), Box> { let mut atomics = Atomics::ALL; let target = env::var("TARGET")?; // Add new target strings here with their atomic availability. #[allow(clippy::match_single_binding, clippy::single_match)] match &*target { "arm-linux-androideabi" => atomics.has_64 = false, _ => {} } // If for some reason splitting fails, use the whole target string. let tgt_arch = target.split("-").next().unwrap_or(&target); // Additionally, the `cfg!(target_arch)` value may be of use for some // targets. Note that it does **not** carry distinguishing information in // all cases! `armv5te` and `armv7` targets are both // `cfg!(target_arch = "arm")`. let env_arch = env::var("CARGO_CFG_TARGET_ARCH")?; // Add new architecture sections here with their atomic availability. #[allow(clippy::match_single_binding, clippy::single_match)] match tgt_arch { "armv5te" | "mips" | "mipsel" | "powerpc" | "riscv32imac" | "thumbv7em" | "thumbv7m" | "thumbv8m.base" | "thumbv8m.main" | "armebv7r" | "armv7r" => atomics.has_64 = false, // These ARMv7 targets have 32-bit pointers and 64-bit atomics. "armv7" | "armv7a" | "armv7s" => atomics.has_64 = true, // "riscv32imc-unknown-none-elf" and "riscv32imac-unknown-none-elf" are // both `target_arch = "riscv32", and have no stable `cfg`-discoverable // distinction. As such, the non-atomic RISC-V targets must be // discovered here. "riscv32i" | "riscv32imc" | "thumbv6m" => atomics = Atomics::NONE, _ => {} } #[allow(clippy::match_single_binding, clippy::single_match)] match &*env_arch { "avr" => atomics = Atomics::NONE, _ => {} } if atomics.has_8 { println!("cargo:rustc-cfg=radium_atomic_8"); } if atomics.has_16 { println!("cargo:rustc-cfg=radium_atomic_16"); } if atomics.has_32 { println!("cargo:rustc-cfg=radium_atomic_32"); } if atomics.has_64 { println!("cargo:rustc-cfg=radium_atomic_64"); } if atomics.has_ptr { println!("cargo:rustc-cfg=radium_atomic_ptr"); } Ok(()) }