11a0216d1Sopenharmony_ciuse std::env::var;
21a0216d1Sopenharmony_ciuse std::io::Write;
31a0216d1Sopenharmony_ci
41a0216d1Sopenharmony_cifn main() {
51a0216d1Sopenharmony_ci    // I/O safety is stabilized in Rust 1.63.
61a0216d1Sopenharmony_ci    if has_io_safety() {
71a0216d1Sopenharmony_ci        use_feature("io_safety_is_in_std")
81a0216d1Sopenharmony_ci    }
91a0216d1Sopenharmony_ci
101a0216d1Sopenharmony_ci    // Work around
111a0216d1Sopenharmony_ci    // https://github.com/rust-lang/rust/issues/103306.
121a0216d1Sopenharmony_ci    use_feature_or_nothing("wasi_ext");
131a0216d1Sopenharmony_ci
141a0216d1Sopenharmony_ci    // Rust 1.56 and earlier don't support panic in const fn.
151a0216d1Sopenharmony_ci    if has_panic_in_const_fn() {
161a0216d1Sopenharmony_ci        use_feature("panic_in_const_fn")
171a0216d1Sopenharmony_ci    }
181a0216d1Sopenharmony_ci
191a0216d1Sopenharmony_ci    // Don't rerun this on changes other than build.rs, as we only depend on
201a0216d1Sopenharmony_ci    // the rustc version.
211a0216d1Sopenharmony_ci    println!("cargo:rerun-if-changed=build.rs");
221a0216d1Sopenharmony_ci}
231a0216d1Sopenharmony_ci
241a0216d1Sopenharmony_cifn use_feature_or_nothing(feature: &str) {
251a0216d1Sopenharmony_ci    if has_feature(feature) {
261a0216d1Sopenharmony_ci        use_feature(feature);
271a0216d1Sopenharmony_ci    }
281a0216d1Sopenharmony_ci}
291a0216d1Sopenharmony_ci
301a0216d1Sopenharmony_cifn use_feature(feature: &str) {
311a0216d1Sopenharmony_ci    println!("cargo:rustc-cfg={}", feature);
321a0216d1Sopenharmony_ci}
331a0216d1Sopenharmony_ci
341a0216d1Sopenharmony_ci/// Test whether the rustc at `var("RUSTC")` supports the given feature.
351a0216d1Sopenharmony_cifn has_feature(feature: &str) -> bool {
361a0216d1Sopenharmony_ci    can_compile(&format!(
371a0216d1Sopenharmony_ci        "#![allow(stable_features)]\n#![feature({})]",
381a0216d1Sopenharmony_ci        feature
391a0216d1Sopenharmony_ci    ))
401a0216d1Sopenharmony_ci}
411a0216d1Sopenharmony_ci
421a0216d1Sopenharmony_ci/// Test whether the rustc at `var("RUSTC")` can compile the given code.
431a0216d1Sopenharmony_cifn can_compile<T: AsRef<str>>(test: T) -> bool {
441a0216d1Sopenharmony_ci    use std::process::Stdio;
451a0216d1Sopenharmony_ci
461a0216d1Sopenharmony_ci    let out_dir = var("OUT_DIR").unwrap();
471a0216d1Sopenharmony_ci    let rustc = var("RUSTC").unwrap();
481a0216d1Sopenharmony_ci    let target = var("TARGET").unwrap();
491a0216d1Sopenharmony_ci
501a0216d1Sopenharmony_ci    let mut cmd = if let Ok(wrapper) = var("CARGO_RUSTC_WRAPPER") {
511a0216d1Sopenharmony_ci        let mut cmd = std::process::Command::new(wrapper);
521a0216d1Sopenharmony_ci        // The wrapper's first argument is supposed to be the path to rustc.
531a0216d1Sopenharmony_ci        cmd.arg(rustc);
541a0216d1Sopenharmony_ci        cmd
551a0216d1Sopenharmony_ci    } else {
561a0216d1Sopenharmony_ci        std::process::Command::new(rustc)
571a0216d1Sopenharmony_ci    };
581a0216d1Sopenharmony_ci
591a0216d1Sopenharmony_ci    cmd.arg("--crate-type=rlib") // Don't require `main`.
601a0216d1Sopenharmony_ci        .arg("--emit=metadata") // Do as little as possible but still parse.
611a0216d1Sopenharmony_ci        .arg("--target")
621a0216d1Sopenharmony_ci        .arg(target)
631a0216d1Sopenharmony_ci        .arg("--out-dir")
641a0216d1Sopenharmony_ci        .arg(out_dir); // Put the output somewhere inconsequential.
651a0216d1Sopenharmony_ci
661a0216d1Sopenharmony_ci    // If Cargo wants to set RUSTFLAGS, use that.
671a0216d1Sopenharmony_ci    if let Ok(rustflags) = var("CARGO_ENCODED_RUSTFLAGS") {
681a0216d1Sopenharmony_ci        if !rustflags.is_empty() {
691a0216d1Sopenharmony_ci            for arg in rustflags.split('\x1f') {
701a0216d1Sopenharmony_ci                cmd.arg(arg);
711a0216d1Sopenharmony_ci            }
721a0216d1Sopenharmony_ci        }
731a0216d1Sopenharmony_ci    }
741a0216d1Sopenharmony_ci
751a0216d1Sopenharmony_ci    let mut child = cmd
761a0216d1Sopenharmony_ci        .arg("-") // Read from stdin.
771a0216d1Sopenharmony_ci        .stdin(Stdio::piped()) // Stdin is a pipe.
781a0216d1Sopenharmony_ci        .stderr(Stdio::null()) // Errors from feature detection aren't interesting and can be confusing.
791a0216d1Sopenharmony_ci        .spawn()
801a0216d1Sopenharmony_ci        .unwrap();
811a0216d1Sopenharmony_ci
821a0216d1Sopenharmony_ci    writeln!(child.stdin.take().unwrap(), "{}", test.as_ref()).unwrap();
831a0216d1Sopenharmony_ci
841a0216d1Sopenharmony_ci    child.wait().unwrap().success()
851a0216d1Sopenharmony_ci}
861a0216d1Sopenharmony_ci
871a0216d1Sopenharmony_ci/// Test whether the rustc at `var("RUSTC")` supports panic in `const fn`.
881a0216d1Sopenharmony_cifn has_panic_in_const_fn() -> bool {
891a0216d1Sopenharmony_ci    can_compile("const fn foo() {{ panic!() }}")
901a0216d1Sopenharmony_ci}
911a0216d1Sopenharmony_ci
921a0216d1Sopenharmony_ci/// Test whether the rustc at `var("RUSTC")` supports the I/O safety feature.
931a0216d1Sopenharmony_cifn has_io_safety() -> bool {
941a0216d1Sopenharmony_ci    can_compile(
951a0216d1Sopenharmony_ci        "\
961a0216d1Sopenharmony_ci    #[cfg(unix)]\n\
971a0216d1Sopenharmony_ci    use std::os::unix::io::OwnedFd as Owned;\n\
981a0216d1Sopenharmony_ci    #[cfg(target_os = \"wasi\")]\n\
991a0216d1Sopenharmony_ci    use std::os::wasi::io::OwnedFd as Owned;\n\
1001a0216d1Sopenharmony_ci    #[cfg(windows)]\n\
1011a0216d1Sopenharmony_ci    use std::os::windows::io::OwnedHandle as Owned;\n\
1021a0216d1Sopenharmony_ci    \n\
1031a0216d1Sopenharmony_ci    pub type Success = Owned;\n\
1041a0216d1Sopenharmony_ci    ",
1051a0216d1Sopenharmony_ci    )
1061a0216d1Sopenharmony_ci}
107