1f1555e47Sopenharmony_ci//! A Rust library for build scripts to automatically configure code based on 2f1555e47Sopenharmony_ci//! compiler support. Code snippets are dynamically tested to see if the `rustc` 3f1555e47Sopenharmony_ci//! will accept them, rather than hard-coding specific version support. 4f1555e47Sopenharmony_ci//! 5f1555e47Sopenharmony_ci//! 6f1555e47Sopenharmony_ci//! ## Usage 7f1555e47Sopenharmony_ci//! 8f1555e47Sopenharmony_ci//! Add this to your `Cargo.toml`: 9f1555e47Sopenharmony_ci//! 10f1555e47Sopenharmony_ci//! ```toml 11f1555e47Sopenharmony_ci//! [build-dependencies] 12f1555e47Sopenharmony_ci//! autocfg = "1" 13f1555e47Sopenharmony_ci//! ``` 14f1555e47Sopenharmony_ci//! 15f1555e47Sopenharmony_ci//! Then use it in your `build.rs` script to detect compiler features. For 16f1555e47Sopenharmony_ci//! example, to test for 128-bit integer support, it might look like: 17f1555e47Sopenharmony_ci//! 18f1555e47Sopenharmony_ci//! ```rust 19f1555e47Sopenharmony_ci//! extern crate autocfg; 20f1555e47Sopenharmony_ci//! 21f1555e47Sopenharmony_ci//! fn main() { 22f1555e47Sopenharmony_ci//! # // Normally, cargo will set `OUT_DIR` for build scripts. 23f1555e47Sopenharmony_ci//! # std::env::set_var("OUT_DIR", "target"); 24f1555e47Sopenharmony_ci//! let ac = autocfg::new(); 25f1555e47Sopenharmony_ci//! ac.emit_has_type("i128"); 26f1555e47Sopenharmony_ci//! 27f1555e47Sopenharmony_ci//! // (optional) We don't need to rerun for anything external. 28f1555e47Sopenharmony_ci//! autocfg::rerun_path("build.rs"); 29f1555e47Sopenharmony_ci//! } 30f1555e47Sopenharmony_ci//! ``` 31f1555e47Sopenharmony_ci//! 32f1555e47Sopenharmony_ci//! If the type test succeeds, this will write a `cargo:rustc-cfg=has_i128` line 33f1555e47Sopenharmony_ci//! for Cargo, which translates to Rust arguments `--cfg has_i128`. Then in the 34f1555e47Sopenharmony_ci//! rest of your Rust code, you can add `#[cfg(has_i128)]` conditions on code that 35f1555e47Sopenharmony_ci//! should only be used when the compiler supports it. 36f1555e47Sopenharmony_ci//! 37f1555e47Sopenharmony_ci//! ## Caution 38f1555e47Sopenharmony_ci//! 39f1555e47Sopenharmony_ci//! Many of the probing methods of `AutoCfg` document the particular template they 40f1555e47Sopenharmony_ci//! use, **subject to change**. The inputs are not validated to make sure they are 41f1555e47Sopenharmony_ci//! semantically correct for their expected use, so it's _possible_ to escape and 42f1555e47Sopenharmony_ci//! inject something unintended. However, such abuse is unsupported and will not 43f1555e47Sopenharmony_ci//! be considered when making changes to the templates. 44f1555e47Sopenharmony_ci 45f1555e47Sopenharmony_ci#![deny(missing_debug_implementations)] 46f1555e47Sopenharmony_ci#![deny(missing_docs)] 47f1555e47Sopenharmony_ci// allow future warnings that can't be fixed while keeping 1.0 compatibility 48f1555e47Sopenharmony_ci#![allow(unknown_lints)] 49f1555e47Sopenharmony_ci#![allow(bare_trait_objects)] 50f1555e47Sopenharmony_ci#![allow(ellipsis_inclusive_range_patterns)] 51f1555e47Sopenharmony_ci 52f1555e47Sopenharmony_ci/// Local macro to avoid `std::try!`, deprecated in Rust 1.39. 53f1555e47Sopenharmony_cimacro_rules! try { 54f1555e47Sopenharmony_ci ($result:expr) => { 55f1555e47Sopenharmony_ci match $result { 56f1555e47Sopenharmony_ci Ok(value) => value, 57f1555e47Sopenharmony_ci Err(error) => return Err(error), 58f1555e47Sopenharmony_ci } 59f1555e47Sopenharmony_ci }; 60f1555e47Sopenharmony_ci} 61f1555e47Sopenharmony_ci 62f1555e47Sopenharmony_ciuse std::env; 63f1555e47Sopenharmony_ciuse std::ffi::OsString; 64f1555e47Sopenharmony_ciuse std::fs; 65f1555e47Sopenharmony_ciuse std::io::{stderr, Write}; 66f1555e47Sopenharmony_ciuse std::path::{Path, PathBuf}; 67f1555e47Sopenharmony_ciuse std::process::{Command, Stdio}; 68f1555e47Sopenharmony_ci#[allow(deprecated)] 69f1555e47Sopenharmony_ciuse std::sync::atomic::ATOMIC_USIZE_INIT; 70f1555e47Sopenharmony_ciuse std::sync::atomic::{AtomicUsize, Ordering}; 71f1555e47Sopenharmony_ci 72f1555e47Sopenharmony_cimod error; 73f1555e47Sopenharmony_cipub use error::Error; 74f1555e47Sopenharmony_ci 75f1555e47Sopenharmony_cimod version; 76f1555e47Sopenharmony_ciuse version::Version; 77f1555e47Sopenharmony_ci 78f1555e47Sopenharmony_ci#[cfg(test)] 79f1555e47Sopenharmony_cimod tests; 80f1555e47Sopenharmony_ci 81f1555e47Sopenharmony_ci/// Helper to detect compiler features for `cfg` output in build scripts. 82f1555e47Sopenharmony_ci#[derive(Clone, Debug)] 83f1555e47Sopenharmony_cipub struct AutoCfg { 84f1555e47Sopenharmony_ci out_dir: PathBuf, 85f1555e47Sopenharmony_ci rustc: PathBuf, 86f1555e47Sopenharmony_ci rustc_version: Version, 87f1555e47Sopenharmony_ci target: Option<OsString>, 88f1555e47Sopenharmony_ci no_std: bool, 89f1555e47Sopenharmony_ci rustflags: Vec<String>, 90f1555e47Sopenharmony_ci} 91f1555e47Sopenharmony_ci 92f1555e47Sopenharmony_ci/// Writes a config flag for rustc on standard out. 93f1555e47Sopenharmony_ci/// 94f1555e47Sopenharmony_ci/// This looks like: `cargo:rustc-cfg=CFG` 95f1555e47Sopenharmony_ci/// 96f1555e47Sopenharmony_ci/// Cargo will use this in arguments to rustc, like `--cfg CFG`. 97f1555e47Sopenharmony_cipub fn emit(cfg: &str) { 98f1555e47Sopenharmony_ci println!("cargo:rustc-cfg={}", cfg); 99f1555e47Sopenharmony_ci} 100f1555e47Sopenharmony_ci 101f1555e47Sopenharmony_ci/// Writes a line telling Cargo to rerun the build script if `path` changes. 102f1555e47Sopenharmony_ci/// 103f1555e47Sopenharmony_ci/// This looks like: `cargo:rerun-if-changed=PATH` 104f1555e47Sopenharmony_ci/// 105f1555e47Sopenharmony_ci/// This requires at least cargo 0.7.0, corresponding to rustc 1.6.0. Earlier 106f1555e47Sopenharmony_ci/// versions of cargo will simply ignore the directive. 107f1555e47Sopenharmony_cipub fn rerun_path(path: &str) { 108f1555e47Sopenharmony_ci println!("cargo:rerun-if-changed={}", path); 109f1555e47Sopenharmony_ci} 110f1555e47Sopenharmony_ci 111f1555e47Sopenharmony_ci/// Writes a line telling Cargo to rerun the build script if the environment 112f1555e47Sopenharmony_ci/// variable `var` changes. 113f1555e47Sopenharmony_ci/// 114f1555e47Sopenharmony_ci/// This looks like: `cargo:rerun-if-env-changed=VAR` 115f1555e47Sopenharmony_ci/// 116f1555e47Sopenharmony_ci/// This requires at least cargo 0.21.0, corresponding to rustc 1.20.0. Earlier 117f1555e47Sopenharmony_ci/// versions of cargo will simply ignore the directive. 118f1555e47Sopenharmony_cipub fn rerun_env(var: &str) { 119f1555e47Sopenharmony_ci println!("cargo:rerun-if-env-changed={}", var); 120f1555e47Sopenharmony_ci} 121f1555e47Sopenharmony_ci 122f1555e47Sopenharmony_ci/// Create a new `AutoCfg` instance. 123f1555e47Sopenharmony_ci/// 124f1555e47Sopenharmony_ci/// # Panics 125f1555e47Sopenharmony_ci/// 126f1555e47Sopenharmony_ci/// Panics if `AutoCfg::new()` returns an error. 127f1555e47Sopenharmony_cipub fn new() -> AutoCfg { 128f1555e47Sopenharmony_ci AutoCfg::new().unwrap() 129f1555e47Sopenharmony_ci} 130f1555e47Sopenharmony_ci 131f1555e47Sopenharmony_ciimpl AutoCfg { 132f1555e47Sopenharmony_ci /// Create a new `AutoCfg` instance. 133f1555e47Sopenharmony_ci /// 134f1555e47Sopenharmony_ci /// # Common errors 135f1555e47Sopenharmony_ci /// 136f1555e47Sopenharmony_ci /// - `rustc` can't be executed, from `RUSTC` or in the `PATH`. 137f1555e47Sopenharmony_ci /// - The version output from `rustc` can't be parsed. 138f1555e47Sopenharmony_ci /// - `OUT_DIR` is not set in the environment, or is not a writable directory. 139f1555e47Sopenharmony_ci /// 140f1555e47Sopenharmony_ci pub fn new() -> Result<Self, Error> { 141f1555e47Sopenharmony_ci match env::var_os("OUT_DIR") { 142f1555e47Sopenharmony_ci Some(d) => Self::with_dir(d), 143f1555e47Sopenharmony_ci None => Err(error::from_str("no OUT_DIR specified!")), 144f1555e47Sopenharmony_ci } 145f1555e47Sopenharmony_ci } 146f1555e47Sopenharmony_ci 147f1555e47Sopenharmony_ci /// Create a new `AutoCfg` instance with the specified output directory. 148f1555e47Sopenharmony_ci /// 149f1555e47Sopenharmony_ci /// # Common errors 150f1555e47Sopenharmony_ci /// 151f1555e47Sopenharmony_ci /// - `rustc` can't be executed, from `RUSTC` or in the `PATH`. 152f1555e47Sopenharmony_ci /// - The version output from `rustc` can't be parsed. 153f1555e47Sopenharmony_ci /// - `dir` is not a writable directory. 154f1555e47Sopenharmony_ci /// 155f1555e47Sopenharmony_ci pub fn with_dir<T: Into<PathBuf>>(dir: T) -> Result<Self, Error> { 156f1555e47Sopenharmony_ci let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into()); 157f1555e47Sopenharmony_ci let rustc: PathBuf = rustc.into(); 158f1555e47Sopenharmony_ci let rustc_version = try!(Version::from_rustc(&rustc)); 159f1555e47Sopenharmony_ci 160f1555e47Sopenharmony_ci let target = env::var_os("TARGET"); 161f1555e47Sopenharmony_ci 162f1555e47Sopenharmony_ci // Sanity check the output directory 163f1555e47Sopenharmony_ci let dir = dir.into(); 164f1555e47Sopenharmony_ci let meta = try!(fs::metadata(&dir).map_err(error::from_io)); 165f1555e47Sopenharmony_ci if !meta.is_dir() || meta.permissions().readonly() { 166f1555e47Sopenharmony_ci return Err(error::from_str("output path is not a writable directory")); 167f1555e47Sopenharmony_ci } 168f1555e47Sopenharmony_ci 169f1555e47Sopenharmony_ci let mut ac = AutoCfg { 170f1555e47Sopenharmony_ci rustflags: rustflags(&target, &dir), 171f1555e47Sopenharmony_ci out_dir: dir, 172f1555e47Sopenharmony_ci rustc: rustc, 173f1555e47Sopenharmony_ci rustc_version: rustc_version, 174f1555e47Sopenharmony_ci target: target, 175f1555e47Sopenharmony_ci no_std: false, 176f1555e47Sopenharmony_ci }; 177f1555e47Sopenharmony_ci 178f1555e47Sopenharmony_ci // Sanity check with and without `std`. 179f1555e47Sopenharmony_ci if !ac.probe("").unwrap_or(false) { 180f1555e47Sopenharmony_ci ac.no_std = true; 181f1555e47Sopenharmony_ci if !ac.probe("").unwrap_or(false) { 182f1555e47Sopenharmony_ci // Neither worked, so assume nothing... 183f1555e47Sopenharmony_ci ac.no_std = false; 184f1555e47Sopenharmony_ci let warning = b"warning: autocfg could not probe for `std`\n"; 185f1555e47Sopenharmony_ci stderr().write_all(warning).ok(); 186f1555e47Sopenharmony_ci } 187f1555e47Sopenharmony_ci } 188f1555e47Sopenharmony_ci Ok(ac) 189f1555e47Sopenharmony_ci } 190f1555e47Sopenharmony_ci 191f1555e47Sopenharmony_ci /// Test whether the current `rustc` reports a version greater than 192f1555e47Sopenharmony_ci /// or equal to "`major`.`minor`". 193f1555e47Sopenharmony_ci pub fn probe_rustc_version(&self, major: usize, minor: usize) -> bool { 194f1555e47Sopenharmony_ci self.rustc_version >= Version::new(major, minor, 0) 195f1555e47Sopenharmony_ci } 196f1555e47Sopenharmony_ci 197f1555e47Sopenharmony_ci /// Sets a `cfg` value of the form `rustc_major_minor`, like `rustc_1_29`, 198f1555e47Sopenharmony_ci /// if the current `rustc` is at least that version. 199f1555e47Sopenharmony_ci pub fn emit_rustc_version(&self, major: usize, minor: usize) { 200f1555e47Sopenharmony_ci if self.probe_rustc_version(major, minor) { 201f1555e47Sopenharmony_ci emit(&format!("rustc_{}_{}", major, minor)); 202f1555e47Sopenharmony_ci } 203f1555e47Sopenharmony_ci } 204f1555e47Sopenharmony_ci 205f1555e47Sopenharmony_ci fn probe<T: AsRef<[u8]>>(&self, code: T) -> Result<bool, Error> { 206f1555e47Sopenharmony_ci #[allow(deprecated)] 207f1555e47Sopenharmony_ci static ID: AtomicUsize = ATOMIC_USIZE_INIT; 208f1555e47Sopenharmony_ci 209f1555e47Sopenharmony_ci let id = ID.fetch_add(1, Ordering::Relaxed); 210f1555e47Sopenharmony_ci let mut command = Command::new(&self.rustc); 211f1555e47Sopenharmony_ci command 212f1555e47Sopenharmony_ci .arg("--crate-name") 213f1555e47Sopenharmony_ci .arg(format!("probe{}", id)) 214f1555e47Sopenharmony_ci .arg("--crate-type=lib") 215f1555e47Sopenharmony_ci .arg("--out-dir") 216f1555e47Sopenharmony_ci .arg(&self.out_dir) 217f1555e47Sopenharmony_ci .arg("--emit=llvm-ir"); 218f1555e47Sopenharmony_ci 219f1555e47Sopenharmony_ci if let Some(target) = self.target.as_ref() { 220f1555e47Sopenharmony_ci command.arg("--target").arg(target); 221f1555e47Sopenharmony_ci } 222f1555e47Sopenharmony_ci 223f1555e47Sopenharmony_ci command.args(&self.rustflags); 224f1555e47Sopenharmony_ci 225f1555e47Sopenharmony_ci command.arg("-").stdin(Stdio::piped()); 226f1555e47Sopenharmony_ci let mut child = try!(command.spawn().map_err(error::from_io)); 227f1555e47Sopenharmony_ci let mut stdin = child.stdin.take().expect("rustc stdin"); 228f1555e47Sopenharmony_ci 229f1555e47Sopenharmony_ci if self.no_std { 230f1555e47Sopenharmony_ci try!(stdin.write_all(b"#![no_std]\n").map_err(error::from_io)); 231f1555e47Sopenharmony_ci } 232f1555e47Sopenharmony_ci try!(stdin.write_all(code.as_ref()).map_err(error::from_io)); 233f1555e47Sopenharmony_ci drop(stdin); 234f1555e47Sopenharmony_ci 235f1555e47Sopenharmony_ci let status = try!(child.wait().map_err(error::from_io)); 236f1555e47Sopenharmony_ci Ok(status.success()) 237f1555e47Sopenharmony_ci } 238f1555e47Sopenharmony_ci 239f1555e47Sopenharmony_ci /// Tests whether the given sysroot crate can be used. 240f1555e47Sopenharmony_ci /// 241f1555e47Sopenharmony_ci /// The test code is subject to change, but currently looks like: 242f1555e47Sopenharmony_ci /// 243f1555e47Sopenharmony_ci /// ```ignore 244f1555e47Sopenharmony_ci /// extern crate CRATE as probe; 245f1555e47Sopenharmony_ci /// ``` 246f1555e47Sopenharmony_ci pub fn probe_sysroot_crate(&self, name: &str) -> bool { 247f1555e47Sopenharmony_ci self.probe(format!("extern crate {} as probe;", name)) // `as _` wasn't stabilized until Rust 1.33 248f1555e47Sopenharmony_ci .unwrap_or(false) 249f1555e47Sopenharmony_ci } 250f1555e47Sopenharmony_ci 251f1555e47Sopenharmony_ci /// Emits a config value `has_CRATE` if `probe_sysroot_crate` returns true. 252f1555e47Sopenharmony_ci pub fn emit_sysroot_crate(&self, name: &str) { 253f1555e47Sopenharmony_ci if self.probe_sysroot_crate(name) { 254f1555e47Sopenharmony_ci emit(&format!("has_{}", mangle(name))); 255f1555e47Sopenharmony_ci } 256f1555e47Sopenharmony_ci } 257f1555e47Sopenharmony_ci 258f1555e47Sopenharmony_ci /// Tests whether the given path can be used. 259f1555e47Sopenharmony_ci /// 260f1555e47Sopenharmony_ci /// The test code is subject to change, but currently looks like: 261f1555e47Sopenharmony_ci /// 262f1555e47Sopenharmony_ci /// ```ignore 263f1555e47Sopenharmony_ci /// pub use PATH; 264f1555e47Sopenharmony_ci /// ``` 265f1555e47Sopenharmony_ci pub fn probe_path(&self, path: &str) -> bool { 266f1555e47Sopenharmony_ci self.probe(format!("pub use {};", path)).unwrap_or(false) 267f1555e47Sopenharmony_ci } 268f1555e47Sopenharmony_ci 269f1555e47Sopenharmony_ci /// Emits a config value `has_PATH` if `probe_path` returns true. 270f1555e47Sopenharmony_ci /// 271f1555e47Sopenharmony_ci /// Any non-identifier characters in the `path` will be replaced with 272f1555e47Sopenharmony_ci /// `_` in the generated config value. 273f1555e47Sopenharmony_ci pub fn emit_has_path(&self, path: &str) { 274f1555e47Sopenharmony_ci if self.probe_path(path) { 275f1555e47Sopenharmony_ci emit(&format!("has_{}", mangle(path))); 276f1555e47Sopenharmony_ci } 277f1555e47Sopenharmony_ci } 278f1555e47Sopenharmony_ci 279f1555e47Sopenharmony_ci /// Emits the given `cfg` value if `probe_path` returns true. 280f1555e47Sopenharmony_ci pub fn emit_path_cfg(&self, path: &str, cfg: &str) { 281f1555e47Sopenharmony_ci if self.probe_path(path) { 282f1555e47Sopenharmony_ci emit(cfg); 283f1555e47Sopenharmony_ci } 284f1555e47Sopenharmony_ci } 285f1555e47Sopenharmony_ci 286f1555e47Sopenharmony_ci /// Tests whether the given trait can be used. 287f1555e47Sopenharmony_ci /// 288f1555e47Sopenharmony_ci /// The test code is subject to change, but currently looks like: 289f1555e47Sopenharmony_ci /// 290f1555e47Sopenharmony_ci /// ```ignore 291f1555e47Sopenharmony_ci /// pub trait Probe: TRAIT + Sized {} 292f1555e47Sopenharmony_ci /// ``` 293f1555e47Sopenharmony_ci pub fn probe_trait(&self, name: &str) -> bool { 294f1555e47Sopenharmony_ci self.probe(format!("pub trait Probe: {} + Sized {{}}", name)) 295f1555e47Sopenharmony_ci .unwrap_or(false) 296f1555e47Sopenharmony_ci } 297f1555e47Sopenharmony_ci 298f1555e47Sopenharmony_ci /// Emits a config value `has_TRAIT` if `probe_trait` returns true. 299f1555e47Sopenharmony_ci /// 300f1555e47Sopenharmony_ci /// Any non-identifier characters in the trait `name` will be replaced with 301f1555e47Sopenharmony_ci /// `_` in the generated config value. 302f1555e47Sopenharmony_ci pub fn emit_has_trait(&self, name: &str) { 303f1555e47Sopenharmony_ci if self.probe_trait(name) { 304f1555e47Sopenharmony_ci emit(&format!("has_{}", mangle(name))); 305f1555e47Sopenharmony_ci } 306f1555e47Sopenharmony_ci } 307f1555e47Sopenharmony_ci 308f1555e47Sopenharmony_ci /// Emits the given `cfg` value if `probe_trait` returns true. 309f1555e47Sopenharmony_ci pub fn emit_trait_cfg(&self, name: &str, cfg: &str) { 310f1555e47Sopenharmony_ci if self.probe_trait(name) { 311f1555e47Sopenharmony_ci emit(cfg); 312f1555e47Sopenharmony_ci } 313f1555e47Sopenharmony_ci } 314f1555e47Sopenharmony_ci 315f1555e47Sopenharmony_ci /// Tests whether the given type can be used. 316f1555e47Sopenharmony_ci /// 317f1555e47Sopenharmony_ci /// The test code is subject to change, but currently looks like: 318f1555e47Sopenharmony_ci /// 319f1555e47Sopenharmony_ci /// ```ignore 320f1555e47Sopenharmony_ci /// pub type Probe = TYPE; 321f1555e47Sopenharmony_ci /// ``` 322f1555e47Sopenharmony_ci pub fn probe_type(&self, name: &str) -> bool { 323f1555e47Sopenharmony_ci self.probe(format!("pub type Probe = {};", name)) 324f1555e47Sopenharmony_ci .unwrap_or(false) 325f1555e47Sopenharmony_ci } 326f1555e47Sopenharmony_ci 327f1555e47Sopenharmony_ci /// Emits a config value `has_TYPE` if `probe_type` returns true. 328f1555e47Sopenharmony_ci /// 329f1555e47Sopenharmony_ci /// Any non-identifier characters in the type `name` will be replaced with 330f1555e47Sopenharmony_ci /// `_` in the generated config value. 331f1555e47Sopenharmony_ci pub fn emit_has_type(&self, name: &str) { 332f1555e47Sopenharmony_ci if self.probe_type(name) { 333f1555e47Sopenharmony_ci emit(&format!("has_{}", mangle(name))); 334f1555e47Sopenharmony_ci } 335f1555e47Sopenharmony_ci } 336f1555e47Sopenharmony_ci 337f1555e47Sopenharmony_ci /// Emits the given `cfg` value if `probe_type` returns true. 338f1555e47Sopenharmony_ci pub fn emit_type_cfg(&self, name: &str, cfg: &str) { 339f1555e47Sopenharmony_ci if self.probe_type(name) { 340f1555e47Sopenharmony_ci emit(cfg); 341f1555e47Sopenharmony_ci } 342f1555e47Sopenharmony_ci } 343f1555e47Sopenharmony_ci 344f1555e47Sopenharmony_ci /// Tests whether the given expression can be used. 345f1555e47Sopenharmony_ci /// 346f1555e47Sopenharmony_ci /// The test code is subject to change, but currently looks like: 347f1555e47Sopenharmony_ci /// 348f1555e47Sopenharmony_ci /// ```ignore 349f1555e47Sopenharmony_ci /// pub fn probe() { let _ = EXPR; } 350f1555e47Sopenharmony_ci /// ``` 351f1555e47Sopenharmony_ci pub fn probe_expression(&self, expr: &str) -> bool { 352f1555e47Sopenharmony_ci self.probe(format!("pub fn probe() {{ let _ = {}; }}", expr)) 353f1555e47Sopenharmony_ci .unwrap_or(false) 354f1555e47Sopenharmony_ci } 355f1555e47Sopenharmony_ci 356f1555e47Sopenharmony_ci /// Emits the given `cfg` value if `probe_expression` returns true. 357f1555e47Sopenharmony_ci pub fn emit_expression_cfg(&self, expr: &str, cfg: &str) { 358f1555e47Sopenharmony_ci if self.probe_expression(expr) { 359f1555e47Sopenharmony_ci emit(cfg); 360f1555e47Sopenharmony_ci } 361f1555e47Sopenharmony_ci } 362f1555e47Sopenharmony_ci 363f1555e47Sopenharmony_ci /// Tests whether the given constant expression can be used. 364f1555e47Sopenharmony_ci /// 365f1555e47Sopenharmony_ci /// The test code is subject to change, but currently looks like: 366f1555e47Sopenharmony_ci /// 367f1555e47Sopenharmony_ci /// ```ignore 368f1555e47Sopenharmony_ci /// pub const PROBE: () = ((), EXPR).0; 369f1555e47Sopenharmony_ci /// ``` 370f1555e47Sopenharmony_ci pub fn probe_constant(&self, expr: &str) -> bool { 371f1555e47Sopenharmony_ci self.probe(format!("pub const PROBE: () = ((), {}).0;", expr)) 372f1555e47Sopenharmony_ci .unwrap_or(false) 373f1555e47Sopenharmony_ci } 374f1555e47Sopenharmony_ci 375f1555e47Sopenharmony_ci /// Emits the given `cfg` value if `probe_constant` returns true. 376f1555e47Sopenharmony_ci pub fn emit_constant_cfg(&self, expr: &str, cfg: &str) { 377f1555e47Sopenharmony_ci if self.probe_constant(expr) { 378f1555e47Sopenharmony_ci emit(cfg); 379f1555e47Sopenharmony_ci } 380f1555e47Sopenharmony_ci } 381f1555e47Sopenharmony_ci} 382f1555e47Sopenharmony_ci 383f1555e47Sopenharmony_cifn mangle(s: &str) -> String { 384f1555e47Sopenharmony_ci s.chars() 385f1555e47Sopenharmony_ci .map(|c| match c { 386f1555e47Sopenharmony_ci 'A'...'Z' | 'a'...'z' | '0'...'9' => c, 387f1555e47Sopenharmony_ci _ => '_', 388f1555e47Sopenharmony_ci }) 389f1555e47Sopenharmony_ci .collect() 390f1555e47Sopenharmony_ci} 391f1555e47Sopenharmony_ci 392f1555e47Sopenharmony_cifn dir_contains_target( 393f1555e47Sopenharmony_ci target: &Option<OsString>, 394f1555e47Sopenharmony_ci dir: &Path, 395f1555e47Sopenharmony_ci cargo_target_dir: Option<OsString>, 396f1555e47Sopenharmony_ci) -> bool { 397f1555e47Sopenharmony_ci target 398f1555e47Sopenharmony_ci .as_ref() 399f1555e47Sopenharmony_ci .and_then(|target| { 400f1555e47Sopenharmony_ci dir.to_str().and_then(|dir| { 401f1555e47Sopenharmony_ci let mut cargo_target_dir = cargo_target_dir 402f1555e47Sopenharmony_ci .map(PathBuf::from) 403f1555e47Sopenharmony_ci .unwrap_or_else(|| PathBuf::from("target")); 404f1555e47Sopenharmony_ci cargo_target_dir.push(target); 405f1555e47Sopenharmony_ci 406f1555e47Sopenharmony_ci cargo_target_dir 407f1555e47Sopenharmony_ci .to_str() 408f1555e47Sopenharmony_ci .map(|cargo_target_dir| dir.contains(&cargo_target_dir)) 409f1555e47Sopenharmony_ci }) 410f1555e47Sopenharmony_ci }) 411f1555e47Sopenharmony_ci .unwrap_or(false) 412f1555e47Sopenharmony_ci} 413f1555e47Sopenharmony_ci 414f1555e47Sopenharmony_cifn rustflags(target: &Option<OsString>, dir: &Path) -> Vec<String> { 415f1555e47Sopenharmony_ci // Starting with rust-lang/cargo#9601, shipped in Rust 1.55, Cargo always sets 416f1555e47Sopenharmony_ci // CARGO_ENCODED_RUSTFLAGS for any host/target build script invocation. This 417f1555e47Sopenharmony_ci // includes any source of flags, whether from the environment, toml config, or 418f1555e47Sopenharmony_ci // whatever may come in the future. The value is either an empty string, or a 419f1555e47Sopenharmony_ci // list of arguments separated by the ASCII unit separator (US), 0x1f. 420f1555e47Sopenharmony_ci if let Ok(a) = env::var("CARGO_ENCODED_RUSTFLAGS") { 421f1555e47Sopenharmony_ci return if a.is_empty() { 422f1555e47Sopenharmony_ci Vec::new() 423f1555e47Sopenharmony_ci } else { 424f1555e47Sopenharmony_ci a.split('\x1f').map(str::to_string).collect() 425f1555e47Sopenharmony_ci }; 426f1555e47Sopenharmony_ci } 427f1555e47Sopenharmony_ci 428f1555e47Sopenharmony_ci // Otherwise, we have to take a more heuristic approach, and we don't 429f1555e47Sopenharmony_ci // support values from toml config at all. 430f1555e47Sopenharmony_ci // 431f1555e47Sopenharmony_ci // Cargo only applies RUSTFLAGS for building TARGET artifact in 432f1555e47Sopenharmony_ci // cross-compilation environment. Sadly, we don't have a way to detect 433f1555e47Sopenharmony_ci // when we're building HOST artifact in a cross-compilation environment, 434f1555e47Sopenharmony_ci // so for now we only apply RUSTFLAGS when cross-compiling an artifact. 435f1555e47Sopenharmony_ci // 436f1555e47Sopenharmony_ci // See https://github.com/cuviper/autocfg/pull/10#issuecomment-527575030. 437f1555e47Sopenharmony_ci if *target != env::var_os("HOST") 438f1555e47Sopenharmony_ci || dir_contains_target(target, dir, env::var_os("CARGO_TARGET_DIR")) 439f1555e47Sopenharmony_ci { 440f1555e47Sopenharmony_ci if let Ok(rustflags) = env::var("RUSTFLAGS") { 441f1555e47Sopenharmony_ci // This is meant to match how cargo handles the RUSTFLAGS environment variable. 442f1555e47Sopenharmony_ci // See https://github.com/rust-lang/cargo/blob/69aea5b6f69add7c51cca939a79644080c0b0ba0/src/cargo/core/compiler/build_context/target_info.rs#L434-L441 443f1555e47Sopenharmony_ci return rustflags 444f1555e47Sopenharmony_ci .split(' ') 445f1555e47Sopenharmony_ci .map(str::trim) 446f1555e47Sopenharmony_ci .filter(|s| !s.is_empty()) 447f1555e47Sopenharmony_ci .map(str::to_string) 448f1555e47Sopenharmony_ci .collect(); 449f1555e47Sopenharmony_ci } 450f1555e47Sopenharmony_ci } 451f1555e47Sopenharmony_ci 452f1555e47Sopenharmony_ci Vec::new() 453f1555e47Sopenharmony_ci} 454