use std::{collections::BTreeSet, env, fs, path::Path};
use crate::{CRATE_NAME_FILE, FEATURES_FILE};
#[allow(clippy::needless_doctest_main)]
pub fn init_prebindgen_out_dir() {
env::var("OUT_DIR").expect(
"OUT_DIR environment variable not set. This function should be called from build.rs.",
);
let crate_name = env::var("CARGO_PKG_NAME").expect("CARGO_PKG_NAME environment variable not set. This function should be called from build.rs.");
let prebindgen_dir = get_prebindgen_out_dir();
if prebindgen_dir.exists() {
for entry in fs::read_dir(&prebindgen_dir).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if path.is_file() {
fs::remove_file(&path).unwrap_or_else(|e| {
panic!("Failed to delete {}: {}", path.display(), e);
});
}
}
} else {
fs::create_dir_all(&prebindgen_dir).unwrap_or_else(|e| {
panic!(
"Failed to create prebindgen directory {}: {}",
prebindgen_dir.display(),
e
);
});
}
let crate_name_path = prebindgen_dir.join(CRATE_NAME_FILE);
fs::write(&crate_name_path, &crate_name).unwrap_or_else(|e| {
panic!(
"Failed to write crate name to {}: {}",
crate_name_path.display(),
e
);
});
let features = get_enabled_features();
let features_path = prebindgen_dir.join(FEATURES_FILE);
let mut feature_contents = String::new();
for feature in &features {
feature_contents += feature;
feature_contents.push('\n');
}
fs::write(&features_path, feature_contents).unwrap_or_else(|e| {
panic!(
"Failed to write features to {}: {}",
features_path.display(),
e
);
});
println!(
"cargo:rustc-env=PREBINDGEN_FEATURES={}",
features
.into_iter()
.map(|f| format!("{}/{}", crate_name, f))
.collect::<Vec<_>>()
.join(" ")
);
}
pub fn get_all_features() -> BTreeSet<String> {
env::var("OUT_DIR").expect(
"OUT_DIR environment variable not set. This function should be called from build.rs.",
);
let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect(
"CARGO_MANIFEST_DIR environment variable not set. This function should be called from build.rs.",
);
let manifest_path = Path::new(&manifest_dir).join("Cargo.toml");
let manifest_content = fs::read_to_string(&manifest_path).unwrap_or_else(|e| {
panic!(
"Failed to read Cargo.toml at {}: {}",
manifest_path.display(),
e
)
});
let doc: toml::Value = toml::from_str(&manifest_content).unwrap_or_else(|e| {
panic!(
"Failed to parse Cargo.toml at {} as TOML: {}",
manifest_path.display(),
e
)
});
let mut set = BTreeSet::new();
if let Some(features_tbl) = doc.get("features").and_then(|v| v.as_table()) {
for key in features_tbl.keys() {
if key != "default" {
set.insert(key.to_string());
}
}
}
set
}
pub fn is_feature_enabled(feature: &str) -> bool {
env::var("OUT_DIR").expect(
"OUT_DIR environment variable not set. This function should be called from build.rs.",
);
let env_key = format!(
"CARGO_FEATURE_{}",
feature
.to_ascii_uppercase()
.chars()
.map(|c| if c == '-' { '_' } else { c })
.collect::<String>()
);
env::var_os(env_key).is_some()
}
pub fn get_enabled_features() -> BTreeSet<String> {
get_all_features()
.into_iter()
.filter(|f| is_feature_enabled(f))
.collect()
}
const PREBINDGEN_DIR: &str = "prebindgen";
pub fn get_prebindgen_out_dir() -> std::path::PathBuf {
let out_dir = std::env::var("OUT_DIR")
.expect("OUT_DIR environment variable not set. Check if build.rs for the crate exitsts");
std::path::Path::new(&out_dir).join(PREBINDGEN_DIR)
}
#[macro_export]
macro_rules! trace {
($($arg:tt)*) => {
println!("cargo:warning=[{}:{}] {}",
file!(),
line!(),
format!($($arg)*)
);
};
}