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