Skip to content

Commit

Permalink
Merge pull request #429 from dtolnay/probe
Browse files Browse the repository at this point in the history
Test for the specific proc_macro_span API expected by proc-macro2
  • Loading branch information
dtolnay committed Dec 31, 2023
2 parents 784ae2e + a961bae commit 3db1977
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 50 deletions.
158 changes: 108 additions & 50 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<RustcVersion> {
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<u32> {
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 {
Expand Down
18 changes: 18 additions & 0 deletions build/probe.rs
Original file line number Diff line number Diff line change
@@ -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<Span> {
this.join(other)
}

pub fn subspan<R: RangeBounds<usize>>(this: &Literal, range: R) -> Option<Span> {
this.subspan(range)
}

0 comments on commit 3db1977

Please sign in to comment.