Skip to content

Commit a1d7429

Browse files
committed
Stateless feature probe
1 parent 2dc4819 commit a1d7429

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-0
lines changed

src/lib.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,42 @@ impl AutoCfg {
378378
emit(cfg);
379379
}
380380
}
381+
382+
/// Tests whether the given features can be used.
383+
///
384+
/// To ensure the features work as expected, it is highly recommended to
385+
/// attempt to use the features.
386+
///
387+
/// The test code is subject to change, but currently looks like:
388+
///
389+
/// ```ignore
390+
/// #![feature(FEATURES)]
391+
/// CODE
392+
/// ```
393+
pub fn probe_features(&self, features: &[&str], code: &str) -> bool {
394+
use std::fmt::Write;
395+
let probe = &mut String::new();
396+
write!(probe, "#![feature(").unwrap();
397+
for feature in features {
398+
write!(probe, "{},", feature).unwrap();
399+
}
400+
write!(probe, ")] {}", code).unwrap();
401+
self.probe(probe).unwrap_or(false)
402+
}
403+
404+
/// Emits a config value `has_FEATURE` if `probe_features` returns true.
405+
pub fn emit_has_feature(&self, feature: &str, code: &str) {
406+
if self.probe_features(&[feature], code) {
407+
emit(&format!("has_{}", feature));
408+
}
409+
}
410+
411+
/// Emits the given `cfg` value if `probe_features` returns true.
412+
pub fn emit_features_cfg(&self, features: &[&str], code: &str, cfg: &str) {
413+
if self.probe_features(features, code) {
414+
emit(cfg)
415+
}
416+
}
381417
}
382418

383419
fn mangle(s: &str) -> String {

src/tests.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use super::AutoCfg;
22
use std::env;
33
use std::path::Path;
4+
use std::process::Command;
5+
use std::str;
46

57
impl AutoCfg {
68
fn core_std(&self, path: &str) -> String {
@@ -22,6 +24,32 @@ impl AutoCfg {
2224
None => Self::with_dir("target"),
2325
}
2426
}
27+
28+
fn assert_nightly(&self, probe_result: bool) {
29+
// Get rustc's verbose version
30+
let output = Command::new(&self.rustc)
31+
.args(&["--version", "--verbose"])
32+
.output()
33+
.unwrap();
34+
if !output.status.success() {
35+
panic!("could not execute rustc")
36+
}
37+
let output = str::from_utf8(&output.stdout).unwrap();
38+
39+
// Find the release line in the verbose version output.
40+
let release = match output.lines().find(|line| line.starts_with("release: ")) {
41+
Some(line) => &line["release: ".len()..],
42+
None => panic!("could not find rustc release"),
43+
};
44+
45+
// Check for nightly channel info, e.g. "-nightly", "-dev"
46+
let nightly = match release.find('-') {
47+
Some(i) => &release[i..] == "-nightly" || &release[i..] == "-dev",
48+
None => false,
49+
};
50+
51+
assert_eq!(nightly, probe_result);
52+
}
2553
}
2654

2755
#[test]
@@ -133,6 +161,17 @@ fn probe_constant() {
133161
ac.assert_min(1, 39, ac.probe_constant(r#""test".len()"#));
134162
}
135163

164+
#[test]
165+
fn prope_feature() {
166+
let ac = AutoCfg::for_test().unwrap();
167+
// an empty #![features()] has no effect
168+
assert!(ac.probe_features(&[], ""));
169+
// stabilized feature succeeds
170+
ac.assert_nightly(ac.probe_features(&["rust1"], ""));
171+
// fake feature fails
172+
ac.assert_nightly(!ac.probe_features(&["RUSTC_DONT_MAKE_ME_A_LIAR"], ""));
173+
}
174+
136175
#[test]
137176
fn dir_does_not_contain_target() {
138177
assert!(!super::dir_contains_target(

0 commit comments

Comments
 (0)