use std::env;
use std::ffi::OsString;
use std::io::Write;
use std::path::PathBuf;
use std::process::{Child, Command, Stdio};
use std::sync::atomic::{AtomicUsize, Ordering};
const RT_CHECK_ENV: &str = "DUNGEON_CELL_RUNTIME_CHECKS";
const NO_CONST_TYPE_NAME: &str = "DUNGEON_CELL_NO_CONST_TYPE_NAME";
fn main() {
assert!(probe(""));
let const_type_name_override =
std::env::var_os(NO_CONST_TYPE_NAME).is_some();
let const_assert_override = env::var_os(RT_CHECK_ENV).is_some();
if !const_type_name_override
&& probe("pub const PROBE: () = ((), core::any::type_name::<()>()).0;")
{
emit("has_const_type_name");
} else if !const_type_name_override
&& probe(
r#"
#![feature(const_type_name)]
pub const PROBE: () = ((), core::any::type_name::<()>()).0;
"#,
)
{
emit("has_feature_const_type_name");
emit("has_const_type_name");
}
if !const_assert_override {
emit("use_const_assert");
}
rerun_env(RT_CHECK_ENV);
rerun_env(NO_CONST_TYPE_NAME);
rerun_path(file!());
}
pub fn rerun_path(path: &str) {
println!("cargo:rerun-if-changed={}", path);
}
pub fn rerun_env(var: &str) {
println!("cargo:rerun-if-env-changed={}", var);
}
fn emit(cfg: &str) {
println!("cargo:rustc-cfg={}", cfg);
}
fn rustc_path() -> PathBuf {
env::var_os("RUSTC")
.unwrap_or_else(|| "rustc".into())
.into()
}
fn out_dir() -> PathBuf {
env::var_os("OUT_DIR").unwrap().into()
}
fn target() -> Option<OsString> {
env::var_os("TARGET")
}
fn rustflags() -> Vec<String> {
let flags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap_or("".to_owned());
if flags.is_empty() {
Vec::new()
} else {
flags.split('\x1f').map(str::to_string).collect()
}
}
static ID: AtomicUsize = AtomicUsize::new(0);
fn probe<T: AsRef<[u8]>>(code: T) -> bool {
let mut child = new_rustc_child();
let mut stdin = child.stdin.take().expect("rustc stdin");
stdin
.write_all(b"#![no_std]\n#![allow(warnings)]\n")
.unwrap();
stdin.write_all(code.as_ref()).unwrap();
drop(stdin);
let status = child.wait().unwrap();
status.success()
}
fn new_rustc_child() -> Child {
let id = ID.fetch_add(1, Ordering::Relaxed);
let mut command = Command::new(rustc_path());
command
.arg("--crate-name")
.arg(format!("probe{}", id))
.arg("--crate-type=lib")
.arg("--out-dir")
.arg(out_dir())
.arg("--emit=llvm-ir");
if let Some(target) = target() {
command.arg("--target").arg(target);
}
command.args(rustflags());
command.arg("-").stdin(Stdio::piped());
command.stdout(Stdio::null()).stderr(Stdio::null());
command.spawn().unwrap()
}