From a961baeb81c828eaee06c0ed08836d377b072dec Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 30 Dec 2023 16:40:01 -0800 Subject: [PATCH] Test for the specific proc_macro_span API expected by proc-macro2 --- build.rs | 158 +++++++++++++++++++++++++++++++++---------------- build/probe.rs | 18 ++++++ 2 files changed, 126 insertions(+), 50 deletions(-) create mode 100644 build/probe.rs diff --git a/build.rs b/build.rs index 35991ed..512502d 100644 --- a/build.rs +++ b/build.rs @@ -36,17 +36,13 @@ use std::env; use std::ffi::OsString; -use std::process::{self, Command}; +use std::path::Path; +use std::process::{self, Command, Stdio}; use std::str; use std::u32; fn main() { - println!("cargo:rerun-if-changed=build.rs"); - - let version = rustc_version().unwrap_or(RustcVersion { - minor: u32::MAX, - nightly: false, - }); + let rustc = rustc_minor_version().unwrap_or(u32::MAX); let docs_rs = env::var_os("DOCS_RS").is_some(); let semver_exempt = cfg!(procmacro2_semver_exempt) || docs_rs; @@ -59,78 +55,140 @@ fn main() { println!("cargo:rustc-cfg=span_locations"); } - if version.minor < 57 { + if rustc < 57 { println!("cargo:rustc-cfg=no_is_available"); } - if version.minor < 66 { + if rustc < 66 { println!("cargo:rustc-cfg=no_source_text"); } if !cfg!(feature = "proc-macro") { + println!("cargo:rerun-if-changed=build.rs"); return; } - if version.nightly || !semver_exempt { + println!("cargo:rerun-if-changed=build/probe.rs"); + + let proc_macro_span; + let consider_rustc_bootstrap; + if compile_probe(false) { + // This is a nightly or dev compiler, so it supports unstable features + // regardless of RUSTC_BOOTSTRAP. No need to rerun build script if + // RUSTC_BOOTSTRAP is changed. + proc_macro_span = true; + consider_rustc_bootstrap = false; + } else if let Some(rustc_bootstrap) = env::var_os("RUSTC_BOOTSTRAP") { + if compile_probe(true) { + // This is a stable or beta compiler for which the user has set + // RUSTC_BOOTSTRAP to turn on unstable features. Rerun build script + // if they change it. + proc_macro_span = true; + consider_rustc_bootstrap = true; + } else if rustc_bootstrap == "1" { + // This compiler does not support the proc macro Span API in the + // form that proc-macro2 expects. No need to pay attention to + // RUSTC_BOOTSTRAP. + proc_macro_span = false; + consider_rustc_bootstrap = false; + } else { + // This is a stable or beta compiler for which RUSTC_BOOTSTRAP is + // set to restrict the use of unstable features by this crate. + proc_macro_span = false; + consider_rustc_bootstrap = true; + } + } else { + // Without RUSTC_BOOTSTRAP, this compiler does not support the proc + // macro Span API in the form that proc-macro2 expects, but try again if + // the user turns on unstable features. + proc_macro_span = false; + consider_rustc_bootstrap = true; + } + + if proc_macro_span || !semver_exempt { println!("cargo:rustc-cfg=wrap_proc_macro"); } - if version.nightly && feature_allowed("proc_macro_span") { + if proc_macro_span { println!("cargo:rustc-cfg=proc_macro_span"); } - if semver_exempt && version.nightly { + if semver_exempt && proc_macro_span { println!("cargo:rustc-cfg=super_unstable"); } -} -struct RustcVersion { - minor: u32, - nightly: bool, + if consider_rustc_bootstrap { + println!("cargo:rerun-if-env-changed=RUSTC_BOOTSTRAP"); + } } -fn rustc_version() -> Option { - let rustc = cargo_env_var("RUSTC"); - let output = Command::new(rustc).arg("--version").output().ok()?; - let version = str::from_utf8(&output.stdout).ok()?; - let nightly = version.contains("nightly") || version.contains("dev"); - let mut pieces = version.split('.'); - if pieces.next() != Some("rustc 1") { - return None; +fn compile_probe(rustc_bootstrap: bool) -> bool { + if env::var_os("RUSTC_STAGE").is_some() { + // We are running inside rustc bootstrap. This is a highly non-standard + // environment with issues such as: + // + // https://github.com/rust-lang/cargo/issues/11138 + // https://github.com/rust-lang/rust/issues/114839 + // + // Let's just not use nightly features here. + return false; } - let minor = pieces.next()?.parse().ok()?; - Some(RustcVersion { minor, nightly }) -} -fn feature_allowed(feature: &str) -> bool { - // Recognized formats: - // - // -Z allow-features=feature1,feature2 - // - // -Zallow-features=feature1,feature2 - - let flags_var; - let flags_var_string; - let flags = if let Some(encoded_rustflags) = env::var_os("CARGO_ENCODED_RUSTFLAGS") { - flags_var = encoded_rustflags; - flags_var_string = flags_var.to_string_lossy(); - flags_var_string.split('\x1f') + let rustc = cargo_env_var("RUSTC"); + let out_dir = cargo_env_var("OUT_DIR"); + let probefile = Path::new("build").join("probe.rs"); + + // Make sure to pick up Cargo rustc configuration. + let mut cmd = if let Some(wrapper) = env::var_os("RUSTC_WRAPPER") { + let mut cmd = Command::new(wrapper); + // The wrapper's first argument is supposed to be the path to rustc. + cmd.arg(rustc); + cmd } else { - return true; + Command::new(rustc) }; - for mut flag in flags { - if flag.starts_with("-Z") { - flag = &flag["-Z".len()..]; - } - if flag.starts_with("allow-features=") { - flag = &flag["allow-features=".len()..]; - return flag.split(',').any(|allowed| allowed == feature); + if !rustc_bootstrap { + cmd.env_remove("RUSTC_BOOTSTRAP"); + } + + cmd.stderr(Stdio::null()) + .arg("--edition=2021") + .arg("--crate-name=proc_macro2") + .arg("--crate-type=lib") + .arg("--emit=metadata") + .arg("--out-dir") + .arg(out_dir) + .arg(probefile); + + if let Some(target) = env::var_os("TARGET") { + cmd.arg("--target").arg(target); + } + + // If Cargo wants to set RUSTFLAGS, use that. + if let Ok(rustflags) = env::var("CARGO_ENCODED_RUSTFLAGS") { + if !rustflags.is_empty() { + for arg in rustflags.split('\x1f') { + cmd.arg(arg); + } } } - // No allow-features= flag, allowed by default. - true + match cmd.status() { + Ok(status) => status.success(), + Err(_) => false, + } +} + +fn rustc_minor_version() -> Option { + let rustc = cargo_env_var("RUSTC"); + let output = Command::new(rustc).arg("--version").output().ok()?; + let version = str::from_utf8(&output.stdout).ok()?; + let mut pieces = version.split('.'); + if pieces.next() != Some("rustc 1") { + return None; + } + pieces.next()?.parse().ok() } fn cargo_env_var(key: &str) -> OsString { diff --git a/build/probe.rs b/build/probe.rs new file mode 100644 index 0000000..759bd10 --- /dev/null +++ b/build/probe.rs @@ -0,0 +1,18 @@ +// This code exercises the surface area that we expect of Span's unstable API. +// If the current toolchain is able to compile it, then proc-macro2 is able to +// offer these APIs too. + +#![feature(proc_macro_span)] + +extern crate proc_macro; + +use core::ops::RangeBounds; +use proc_macro::{Literal, Span}; + +pub fn join(this: &Span, other: Span) -> Option { + this.join(other) +} + +pub fn subspan>(this: &Literal, range: R) -> Option { + this.subspan(range) +}