3636
3737use std:: env;
3838use std:: ffi:: OsString ;
39- use std:: process:: { self , Command } ;
39+ use std:: path:: Path ;
40+ use std:: process:: { self , Command , Stdio } ;
4041use std:: str;
4142use std:: u32;
4243
4344fn 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
136194fn cargo_env_var ( key : & str ) -> OsString {
0 commit comments