1 use std::env::var; 2 use std::io::Write; 3 main()4fn main() { 5 // I/O safety is stabilized in Rust 1.63. 6 if has_io_safety() { 7 use_feature("io_safety_is_in_std") 8 } 9 10 // Work around 11 // https://github.com/rust-lang/rust/issues/103306. 12 use_feature_or_nothing("wasi_ext"); 13 14 // Rust 1.56 and earlier don't support panic in const fn. 15 if has_panic_in_const_fn() { 16 use_feature("panic_in_const_fn") 17 } 18 19 // Don't rerun this on changes other than build.rs, as we only depend on 20 // the rustc version. 21 println!("cargo:rerun-if-changed=build.rs"); 22 } 23 use_feature_or_nothing(feature: &str)24fn use_feature_or_nothing(feature: &str) { 25 if has_feature(feature) { 26 use_feature(feature); 27 } 28 } 29 use_feature(feature: &str)30fn use_feature(feature: &str) { 31 println!("cargo:rustc-cfg={}", feature); 32 } 33 34 /// Test whether the rustc at `var("RUSTC")` supports the given feature. has_feature(feature: &str) -> bool35fn has_feature(feature: &str) -> bool { 36 can_compile(&format!( 37 "#![allow(stable_features)]\n#![feature({})]", 38 feature 39 )) 40 } 41 42 /// Test whether the rustc at `var("RUSTC")` can compile the given code. can_compile<T: AsRef<str>>(test: T) -> bool43fn can_compile<T: AsRef<str>>(test: T) -> bool { 44 use std::process::Stdio; 45 46 let out_dir = var("OUT_DIR").unwrap(); 47 let rustc = var("RUSTC").unwrap(); 48 let target = var("TARGET").unwrap(); 49 50 let mut cmd = if let Ok(wrapper) = var("CARGO_RUSTC_WRAPPER") { 51 let mut cmd = std::process::Command::new(wrapper); 52 // The wrapper's first argument is supposed to be the path to rustc. 53 cmd.arg(rustc); 54 cmd 55 } else { 56 std::process::Command::new(rustc) 57 }; 58 59 cmd.arg("--crate-type=rlib") // Don't require `main`. 60 .arg("--emit=metadata") // Do as little as possible but still parse. 61 .arg("--target") 62 .arg(target) 63 .arg("--out-dir") 64 .arg(out_dir); // Put the output somewhere inconsequential. 65 66 // If Cargo wants to set RUSTFLAGS, use that. 67 if let Ok(rustflags) = var("CARGO_ENCODED_RUSTFLAGS") { 68 if !rustflags.is_empty() { 69 for arg in rustflags.split('\x1f') { 70 cmd.arg(arg); 71 } 72 } 73 } 74 75 let mut child = cmd 76 .arg("-") // Read from stdin. 77 .stdin(Stdio::piped()) // Stdin is a pipe. 78 .stderr(Stdio::null()) // Errors from feature detection aren't interesting and can be confusing. 79 .spawn() 80 .unwrap(); 81 82 writeln!(child.stdin.take().unwrap(), "{}", test.as_ref()).unwrap(); 83 84 child.wait().unwrap().success() 85 } 86 87 /// Test whether the rustc at `var("RUSTC")` supports panic in `const fn`. has_panic_in_const_fn() -> bool88fn has_panic_in_const_fn() -> bool { 89 can_compile("const fn foo() {{ panic!() }}") 90 } 91 92 /// Test whether the rustc at `var("RUSTC")` supports the I/O safety feature. has_io_safety() -> bool93fn has_io_safety() -> bool { 94 can_compile( 95 "\ 96 #[cfg(unix)]\n\ 97 use std::os::unix::io::OwnedFd as Owned;\n\ 98 #[cfg(target_os = \"wasi\")]\n\ 99 use std::os::wasi::io::OwnedFd as Owned;\n\ 100 #[cfg(windows)]\n\ 101 use std::os::windows::io::OwnedHandle as Owned;\n\ 102 \n\ 103 pub type Success = Owned;\n\ 104 ", 105 ) 106 } 107