Skip to content

Commit 3db1977

Browse files
authored
Merge pull request #429 from dtolnay/probe
Test for the specific proc_macro_span API expected by proc-macro2
2 parents 784ae2e + a961bae commit 3db1977

File tree

2 files changed

+126
-50
lines changed

2 files changed

+126
-50
lines changed

build.rs

+108-50
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,13 @@
3636

3737
use std::env;
3838
use std::ffi::OsString;
39-
use std::process::{self, Command};
39+
use std::path::Path;
40+
use std::process::{self, Command, Stdio};
4041
use std::str;
4142
use std::u32;
4243

4344
fn main() {
44-
println!("cargo:rerun-if-changed=build.rs");
45-
46-
let version = rustc_version().unwrap_or(RustcVersion {
47-
minor: u32::MAX,
48-
nightly: false,
49-
});
45+
let rustc = rustc_minor_version().unwrap_or(u32::MAX);
5046

5147
let docs_rs = env::var_os("DOCS_RS").is_some();
5248
let semver_exempt = cfg!(procmacro2_semver_exempt) || docs_rs;
@@ -59,78 +55,140 @@ fn main() {
5955
println!("cargo:rustc-cfg=span_locations");
6056
}
6157

62-
if version.minor < 57 {
58+
if rustc < 57 {
6359
println!("cargo:rustc-cfg=no_is_available");
6460
}
6561

66-
if version.minor < 66 {
62+
if rustc < 66 {
6763
println!("cargo:rustc-cfg=no_source_text");
6864
}
6965

7066
if !cfg!(feature = "proc-macro") {
67+
println!("cargo:rerun-if-changed=build.rs");
7168
return;
7269
}
7370

74-
if version.nightly || !semver_exempt {
71+
println!("cargo:rerun-if-changed=build/probe.rs");
72+
73+
let proc_macro_span;
74+
let consider_rustc_bootstrap;
75+
if compile_probe(false) {
76+
// This is a nightly or dev compiler, so it supports unstable features
77+
// regardless of RUSTC_BOOTSTRAP. No need to rerun build script if
78+
// RUSTC_BOOTSTRAP is changed.
79+
proc_macro_span = true;
80+
consider_rustc_bootstrap = false;
81+
} else if let Some(rustc_bootstrap) = env::var_os("RUSTC_BOOTSTRAP") {
82+
if compile_probe(true) {
83+
// This is a stable or beta compiler for which the user has set
84+
// RUSTC_BOOTSTRAP to turn on unstable features. Rerun build script
85+
// if they change it.
86+
proc_macro_span = true;
87+
consider_rustc_bootstrap = true;
88+
} else if rustc_bootstrap == "1" {
89+
// This compiler does not support the proc macro Span API in the
90+
// form that proc-macro2 expects. No need to pay attention to
91+
// RUSTC_BOOTSTRAP.
92+
proc_macro_span = false;
93+
consider_rustc_bootstrap = false;
94+
} else {
95+
// This is a stable or beta compiler for which RUSTC_BOOTSTRAP is
96+
// set to restrict the use of unstable features by this crate.
97+
proc_macro_span = false;
98+
consider_rustc_bootstrap = true;
99+
}
100+
} else {
101+
// Without RUSTC_BOOTSTRAP, this compiler does not support the proc
102+
// macro Span API in the form that proc-macro2 expects, but try again if
103+
// the user turns on unstable features.
104+
proc_macro_span = false;
105+
consider_rustc_bootstrap = true;
106+
}
107+
108+
if proc_macro_span || !semver_exempt {
75109
println!("cargo:rustc-cfg=wrap_proc_macro");
76110
}
77111

78-
if version.nightly && feature_allowed("proc_macro_span") {
112+
if proc_macro_span {
79113
println!("cargo:rustc-cfg=proc_macro_span");
80114
}
81115

82-
if semver_exempt && version.nightly {
116+
if semver_exempt && proc_macro_span {
83117
println!("cargo:rustc-cfg=super_unstable");
84118
}
85-
}
86119

87-
struct RustcVersion {
88-
minor: u32,
89-
nightly: bool,
120+
if consider_rustc_bootstrap {
121+
println!("cargo:rerun-if-env-changed=RUSTC_BOOTSTRAP");
122+
}
90123
}
91124

92-
fn rustc_version() -> Option<RustcVersion> {
93-
let rustc = cargo_env_var("RUSTC");
94-
let output = Command::new(rustc).arg("--version").output().ok()?;
95-
let version = str::from_utf8(&output.stdout).ok()?;
96-
let nightly = version.contains("nightly") || version.contains("dev");
97-
let mut pieces = version.split('.');
98-
if pieces.next() != Some("rustc 1") {
99-
return None;
125+
fn compile_probe(rustc_bootstrap: bool) -> bool {
126+
if env::var_os("RUSTC_STAGE").is_some() {
127+
// We are running inside rustc bootstrap. This is a highly non-standard
128+
// environment with issues such as:
129+
//
130+
// https://github.com/rust-lang/cargo/issues/11138
131+
// https://github.com/rust-lang/rust/issues/114839
132+
//
133+
// Let's just not use nightly features here.
134+
return false;
100135
}
101-
let minor = pieces.next()?.parse().ok()?;
102-
Some(RustcVersion { minor, nightly })
103-
}
104136

105-
fn feature_allowed(feature: &str) -> bool {
106-
// Recognized formats:
107-
//
108-
// -Z allow-features=feature1,feature2
109-
//
110-
// -Zallow-features=feature1,feature2
111-
112-
let flags_var;
113-
let flags_var_string;
114-
let flags = if let Some(encoded_rustflags) = env::var_os("CARGO_ENCODED_RUSTFLAGS") {
115-
flags_var = encoded_rustflags;
116-
flags_var_string = flags_var.to_string_lossy();
117-
flags_var_string.split('\x1f')
137+
let rustc = cargo_env_var("RUSTC");
138+
let out_dir = cargo_env_var("OUT_DIR");
139+
let probefile = Path::new("build").join("probe.rs");
140+
141+
// Make sure to pick up Cargo rustc configuration.
142+
let mut cmd = if let Some(wrapper) = env::var_os("RUSTC_WRAPPER") {
143+
let mut cmd = Command::new(wrapper);
144+
// The wrapper's first argument is supposed to be the path to rustc.
145+
cmd.arg(rustc);
146+
cmd
118147
} else {
119-
return true;
148+
Command::new(rustc)
120149
};
121150

122-
for mut flag in flags {
123-
if flag.starts_with("-Z") {
124-
flag = &flag["-Z".len()..];
125-
}
126-
if flag.starts_with("allow-features=") {
127-
flag = &flag["allow-features=".len()..];
128-
return flag.split(',').any(|allowed| allowed == feature);
151+
if !rustc_bootstrap {
152+
cmd.env_remove("RUSTC_BOOTSTRAP");
153+
}
154+
155+
cmd.stderr(Stdio::null())
156+
.arg("--edition=2021")
157+
.arg("--crate-name=proc_macro2")
158+
.arg("--crate-type=lib")
159+
.arg("--emit=metadata")
160+
.arg("--out-dir")
161+
.arg(out_dir)
162+
.arg(probefile);
163+
164+
if let Some(target) = env::var_os("TARGET") {
165+
cmd.arg("--target").arg(target);
166+
}
167+
168+
// If Cargo wants to set RUSTFLAGS, use that.
169+
if let Ok(rustflags) = env::var("CARGO_ENCODED_RUSTFLAGS") {
170+
if !rustflags.is_empty() {
171+
for arg in rustflags.split('\x1f') {
172+
cmd.arg(arg);
173+
}
129174
}
130175
}
131176

132-
// No allow-features= flag, allowed by default.
133-
true
177+
match cmd.status() {
178+
Ok(status) => status.success(),
179+
Err(_) => false,
180+
}
181+
}
182+
183+
fn rustc_minor_version() -> Option<u32> {
184+
let rustc = cargo_env_var("RUSTC");
185+
let output = Command::new(rustc).arg("--version").output().ok()?;
186+
let version = str::from_utf8(&output.stdout).ok()?;
187+
let mut pieces = version.split('.');
188+
if pieces.next() != Some("rustc 1") {
189+
return None;
190+
}
191+
pieces.next()?.parse().ok()
134192
}
135193

136194
fn cargo_env_var(key: &str) -> OsString {

build/probe.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// This code exercises the surface area that we expect of Span's unstable API.
2+
// If the current toolchain is able to compile it, then proc-macro2 is able to
3+
// offer these APIs too.
4+
5+
#![feature(proc_macro_span)]
6+
7+
extern crate proc_macro;
8+
9+
use core::ops::RangeBounds;
10+
use proc_macro::{Literal, Span};
11+
12+
pub fn join(this: &Span, other: Span) -> Option<Span> {
13+
this.join(other)
14+
}
15+
16+
pub fn subspan<R: RangeBounds<usize>>(this: &Literal, range: R) -> Option<Span> {
17+
this.subspan(range)
18+
}

0 commit comments

Comments
 (0)