From a1d7429b90a6a306626d759d8cc6c8558c8f79cd Mon Sep 17 00:00:00 2001 From: CAD97 Date: Fri, 15 Apr 2022 13:05:48 -0500 Subject: [PATCH] Stateless feature probe --- src/lib.rs | 36 ++++++++++++++++++++++++++++++++++++ src/tests.rs | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index cbe393a..280a5b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -378,6 +378,42 @@ impl AutoCfg { emit(cfg); } } + + /// Tests whether the given features can be used. + /// + /// To ensure the features work as expected, it is highly recommended to + /// attempt to use the features. + /// + /// The test code is subject to change, but currently looks like: + /// + /// ```ignore + /// #![feature(FEATURES)] + /// CODE + /// ``` + pub fn probe_features(&self, features: &[&str], code: &str) -> bool { + use std::fmt::Write; + let probe = &mut String::new(); + write!(probe, "#![feature(").unwrap(); + for feature in features { + write!(probe, "{},", feature).unwrap(); + } + write!(probe, ")] {}", code).unwrap(); + self.probe(probe).unwrap_or(false) + } + + /// Emits a config value `has_FEATURE` if `probe_features` returns true. + pub fn emit_has_feature(&self, feature: &str, code: &str) { + if self.probe_features(&[feature], code) { + emit(&format!("has_{}", feature)); + } + } + + /// Emits the given `cfg` value if `probe_features` returns true. + pub fn emit_features_cfg(&self, features: &[&str], code: &str, cfg: &str) { + if self.probe_features(features, code) { + emit(cfg) + } + } } fn mangle(s: &str) -> String { diff --git a/src/tests.rs b/src/tests.rs index d3b1fbb..c4e7f62 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,6 +1,8 @@ use super::AutoCfg; use std::env; use std::path::Path; +use std::process::Command; +use std::str; impl AutoCfg { fn core_std(&self, path: &str) -> String { @@ -22,6 +24,32 @@ impl AutoCfg { None => Self::with_dir("target"), } } + + fn assert_nightly(&self, probe_result: bool) { + // Get rustc's verbose version + let output = Command::new(&self.rustc) + .args(&["--version", "--verbose"]) + .output() + .unwrap(); + if !output.status.success() { + panic!("could not execute rustc") + } + let output = str::from_utf8(&output.stdout).unwrap(); + + // Find the release line in the verbose version output. + let release = match output.lines().find(|line| line.starts_with("release: ")) { + Some(line) => &line["release: ".len()..], + None => panic!("could not find rustc release"), + }; + + // Check for nightly channel info, e.g. "-nightly", "-dev" + let nightly = match release.find('-') { + Some(i) => &release[i..] == "-nightly" || &release[i..] == "-dev", + None => false, + }; + + assert_eq!(nightly, probe_result); + } } #[test] @@ -133,6 +161,17 @@ fn probe_constant() { ac.assert_min(1, 39, ac.probe_constant(r#""test".len()"#)); } +#[test] +fn prope_feature() { + let ac = AutoCfg::for_test().unwrap(); + // an empty #![features()] has no effect + assert!(ac.probe_features(&[], "")); + // stabilized feature succeeds + ac.assert_nightly(ac.probe_features(&["rust1"], "")); + // fake feature fails + ac.assert_nightly(!ac.probe_features(&["RUSTC_DONT_MAKE_ME_A_LIAR"], "")); +} + #[test] fn dir_does_not_contain_target() { assert!(!super::dir_contains_target(