1 #![allow(unknown_lints)]
2 #![allow(unexpected_cfgs)]
3
4 use std::env;
5 use std::ffi::OsString;
6 use std::fs;
7 use std::io::ErrorKind;
8 use std::iter;
9 use std::path::Path;
10 use std::process::{self, Command, Stdio};
11 use std::str;
12
main()13 fn main() {
14 let rustc = rustc_minor_version().unwrap_or(u32::MAX);
15
16 if rustc >= 80 {
17 println!("cargo:rustc-check-cfg=cfg(fuzzing)");
18 println!("cargo:rustc-check-cfg=cfg(no_is_available)");
19 println!("cargo:rustc-check-cfg=cfg(no_literal_byte_character)");
20 println!("cargo:rustc-check-cfg=cfg(no_literal_c_string)");
21 println!("cargo:rustc-check-cfg=cfg(no_source_text)");
22 println!("cargo:rustc-check-cfg=cfg(proc_macro_span)");
23 println!("cargo:rustc-check-cfg=cfg(procmacro2_backtrace)");
24 println!("cargo:rustc-check-cfg=cfg(procmacro2_nightly_testing)");
25 println!("cargo:rustc-check-cfg=cfg(procmacro2_semver_exempt)");
26 println!("cargo:rustc-check-cfg=cfg(randomize_layout)");
27 println!("cargo:rustc-check-cfg=cfg(span_locations)");
28 println!("cargo:rustc-check-cfg=cfg(super_unstable)");
29 println!("cargo:rustc-check-cfg=cfg(wrap_proc_macro)");
30 }
31
32 let docs_rs = env::var_os("DOCS_RS").is_some();
33 let semver_exempt = cfg!(procmacro2_semver_exempt) || docs_rs;
34 if semver_exempt {
35 // https://github.com/dtolnay/proc-macro2/issues/147
36 println!("cargo:rustc-cfg=procmacro2_semver_exempt");
37 }
38
39 if semver_exempt || cfg!(feature = "span-locations") {
40 // Provide methods Span::start and Span::end which give the line/column
41 // location of a token. This is behind a cfg because tracking location
42 // inside spans is a performance hit.
43 println!("cargo:rustc-cfg=span_locations");
44 }
45
46 if rustc < 57 {
47 // Do not use proc_macro::is_available() to detect whether the proc
48 // macro API is available vs needs to be polyfilled. Instead, use the
49 // proc macro API unconditionally and catch the panic that occurs if it
50 // isn't available.
51 println!("cargo:rustc-cfg=no_is_available");
52 }
53
54 if rustc < 66 {
55 // Do not call libproc_macro's Span::source_text. Always return None.
56 println!("cargo:rustc-cfg=no_source_text");
57 }
58
59 if rustc < 79 {
60 // Do not call Literal::byte_character nor Literal::c_string. They can
61 // be emulated by way of Literal::from_str.
62 println!("cargo:rustc-cfg=no_literal_byte_character");
63 println!("cargo:rustc-cfg=no_literal_c_string");
64 }
65
66 if !cfg!(feature = "proc-macro") {
67 println!("cargo:rerun-if-changed=build.rs");
68 return;
69 }
70
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 {
109 // Wrap types from libproc_macro rather than polyfilling the whole API.
110 // Enabled as long as procmacro2_semver_exempt is not set, because we
111 // can't emulate the unstable API without emulating everything else.
112 // Also enabled unconditionally on nightly, in which case the
113 // procmacro2_semver_exempt surface area is implemented by using the
114 // nightly-only proc_macro API.
115 println!("cargo:rustc-cfg=wrap_proc_macro");
116 }
117
118 if proc_macro_span {
119 // Enable non-dummy behavior of Span::start and Span::end methods which
120 // requires an unstable compiler feature. Enabled when building with
121 // nightly, unless `-Z allow-feature` in RUSTFLAGS disallows unstable
122 // features.
123 println!("cargo:rustc-cfg=proc_macro_span");
124 }
125
126 if semver_exempt && proc_macro_span {
127 // Implement the semver exempt API in terms of the nightly-only
128 // proc_macro API.
129 println!("cargo:rustc-cfg=super_unstable");
130 }
131
132 if consider_rustc_bootstrap {
133 println!("cargo:rerun-if-env-changed=RUSTC_BOOTSTRAP");
134 }
135 }
136
compile_probe(rustc_bootstrap: bool) -> bool137 fn compile_probe(rustc_bootstrap: bool) -> bool {
138 if env::var_os("RUSTC_STAGE").is_some() {
139 // We are running inside rustc bootstrap. This is a highly non-standard
140 // environment with issues such as:
141 //
142 // https://github.com/rust-lang/cargo/issues/11138
143 // https://github.com/rust-lang/rust/issues/114839
144 //
145 // Let's just not use nightly features here.
146 return false;
147 }
148
149 let rustc = cargo_env_var("RUSTC");
150 let out_dir = cargo_env_var("OUT_DIR");
151 let out_subdir = Path::new(&out_dir).join("probe");
152 let probefile = Path::new("build").join("probe.rs");
153
154 if let Err(err) = fs::create_dir(&out_subdir) {
155 if err.kind() != ErrorKind::AlreadyExists {
156 eprintln!("Failed to create {}: {}", out_subdir.display(), err);
157 process::exit(1);
158 }
159 }
160
161 let rustc_wrapper = env::var_os("RUSTC_WRAPPER").filter(|wrapper| !wrapper.is_empty());
162 let rustc_workspace_wrapper =
163 env::var_os("RUSTC_WORKSPACE_WRAPPER").filter(|wrapper| !wrapper.is_empty());
164 let mut rustc = rustc_wrapper
165 .into_iter()
166 .chain(rustc_workspace_wrapper)
167 .chain(iter::once(rustc));
168 let mut cmd = Command::new(rustc.next().unwrap());
169 cmd.args(rustc);
170
171 if !rustc_bootstrap {
172 cmd.env_remove("RUSTC_BOOTSTRAP");
173 }
174
175 cmd.stderr(Stdio::null())
176 .arg("--edition=2021")
177 .arg("--crate-name=proc_macro2")
178 .arg("--crate-type=lib")
179 .arg("--cap-lints=allow")
180 .arg("--emit=dep-info,metadata")
181 .arg("--out-dir")
182 .arg(&out_subdir)
183 .arg(probefile);
184
185 if let Some(target) = env::var_os("TARGET") {
186 cmd.arg("--target").arg(target);
187 }
188
189 // If Cargo wants to set RUSTFLAGS, use that.
190 if let Ok(rustflags) = env::var("CARGO_ENCODED_RUSTFLAGS") {
191 if !rustflags.is_empty() {
192 for arg in rustflags.split('\x1f') {
193 cmd.arg(arg);
194 }
195 }
196 }
197
198 let success = match cmd.status() {
199 Ok(status) => status.success(),
200 Err(_) => false,
201 };
202
203 // Clean up to avoid leaving nondeterministic absolute paths in the dep-info
204 // file in OUT_DIR, which causes nonreproducible builds in build systems
205 // that treat the entire OUT_DIR as an artifact.
206 if let Err(err) = fs::remove_dir_all(&out_subdir) {
207 if err.kind() != ErrorKind::NotFound {
208 eprintln!("Failed to clean up {}: {}", out_subdir.display(), err);
209 process::exit(1);
210 }
211 }
212
213 success
214 }
215
rustc_minor_version() -> Option<u32>216 fn rustc_minor_version() -> Option<u32> {
217 let rustc = cargo_env_var("RUSTC");
218 let output = Command::new(rustc).arg("--version").output().ok()?;
219 let version = str::from_utf8(&output.stdout).ok()?;
220 let mut pieces = version.split('.');
221 if pieces.next() != Some("rustc 1") {
222 return None;
223 }
224 pieces.next()?.parse().ok()
225 }
226
cargo_env_var(key: &str) -> OsString227 fn cargo_env_var(key: &str) -> OsString {
228 env::var_os(key).unwrap_or_else(|| {
229 eprintln!(
230 "Environment variable ${} is not set during execution of build script",
231 key,
232 );
233 process::exit(1);
234 })
235 }
236