use std::path::PathBuf;
use crate::ident::{is_ascii_ident, is_crate_name, is_feature_name};
use crate::output::rerun_if_env_changed;
const ENV: RerunIfEnvChanged<ProcessEnv> = RerunIfEnvChanged::new();
trait Env {
fn get(&self, key: &str) -> Option<std::ffi::OsString>;
fn is_present(&self, key: &str) -> bool;
}
struct ProcessEnv;
impl Env for ProcessEnv {
fn get(&self, key: &str) -> Option<std::ffi::OsString> {
std::env::var_os(key)
}
fn is_present(&self, key: &str) -> bool {
self.get(key).is_some()
}
}
struct RerunIfEnvChanged<E: Env>(E);
impl RerunIfEnvChanged<ProcessEnv> {
const fn new() -> Self {
Self(ProcessEnv)
}
}
impl<E: Env> Env for RerunIfEnvChanged<E> {
#[track_caller]
fn get(&self, key: &str) -> Option<std::ffi::OsString> {
rerun_if_env_changed(key);
self.0.get(key)
}
#[track_caller]
fn is_present(&self, key: &str) -> bool {
self.get(key).is_some()
}
}
#[track_caller]
pub fn cargo() -> PathBuf {
to_path(var_or_panic("CARGO"))
}
#[track_caller]
pub fn cargo_manifest_dir() -> PathBuf {
to_path(var_or_panic("CARGO_MANIFEST_DIR"))
}
#[track_caller]
pub fn cargo_manifest_path() -> PathBuf {
ENV.get("CARGO_MANIFEST_PATH")
.map(to_path)
.unwrap_or_else(|| {
let mut path = cargo_manifest_dir();
path.push("Cargo.toml");
path
})
}
#[track_caller]
pub fn cargo_manifest_links() -> Option<String> {
ENV.get("CARGO_MANIFEST_LINKS").map(to_string)
}
#[track_caller]
pub fn cargo_makeflags() -> Option<String> {
ENV.get("CARGO_MAKEFLAGS").map(to_string)
}
#[track_caller]
pub fn cargo_feature(name: &str) -> bool {
if !is_feature_name(name) {
panic!("invalid feature name {name:?}")
}
let name = name.to_uppercase().replace('-', "_");
let key = format!("CARGO_FEATURE_{name}");
ENV.is_present(&key)
}
#[track_caller]
pub fn cargo_cfg(cfg: &str) -> Option<Vec<String>> {
let var = cargo_cfg_var(cfg);
ENV.get(&var).map(|v| to_strings(v, ','))
}
#[track_caller]
fn cargo_cfg_var(cfg: &str) -> String {
if !is_ascii_ident(cfg) {
panic!("invalid configuration option {cfg:?}")
}
let cfg = cfg.to_uppercase().replace('-', "_");
let key = format!("CARGO_CFG_{cfg}");
key
}
pub use self::cfg::*;
mod cfg {
use super::*;
#[doc = requires_msrv!("1.85")]
#[track_caller]
pub fn cargo_cfg_feature() -> Vec<String> {
to_strings(var_or_panic(&cargo_cfg_var("feature")), ',')
}
#[cfg(any())]
#[track_caller]
pub fn cargo_cfg_clippy() -> bool {
ENV.is_present("CARGO_CFG_CLIPPY")
}
#[track_caller]
pub fn cargo_cfg_debug_assertions() -> bool {
ENV.is_present("CARGO_CFG_DEBUG_ASSERTIONS")
}
#[cfg(any())]
#[track_caller]
pub fn cargo_cfg_doc() -> bool {
ENV.is_present("CARGO_CFG_DOC")
}
#[cfg(any())]
#[track_caller]
pub fn cargo_cfg_docsrs() -> bool {
ENV.is_present("CARGO_CFG_DOCSRS")
}
#[cfg(any())]
#[track_caller]
pub fn cargo_cfg_doctest() -> bool {
ENV.is_present("CARGO_CFG_DOCTEST")
}
#[doc = unstable!(fmt_dbg, 129709)]
#[cfg(feature = "unstable")]
#[track_caller]
pub fn cargo_cfg_fmt_debug() -> String {
to_string(var_or_panic("CARGO_CFG_FMT_DEBUG"))
}
#[cfg(any())]
#[track_caller]
pub fn cargo_cfg_miri() -> bool {
ENV.is_present("CARGO_CFG_MIRI")
}
#[doc = unstable!(cfg_overflow_checks, 111466)]
#[cfg(feature = "unstable")]
#[track_caller]
pub fn cargo_cfg_overflow_checks() -> bool {
ENV.is_present("CARGO_CFG_OVERFLOW_CHECKS")
}
#[track_caller]
pub fn cargo_cfg_panic() -> String {
to_string(var_or_panic("CARGO_CFG_PANIC"))
}
#[track_caller]
pub fn cargo_cfg_proc_macro() -> bool {
ENV.is_present("CARGO_CFG_PROC_MACRO")
}
#[doc = unstable!(cfg_relocation_model, 114929)]
#[cfg(feature = "unstable")]
#[track_caller]
pub fn cargo_cfg_relocation_model() -> String {
to_string(var_or_panic("CARGO_CFG_RELOCATION_MODEL"))
}
#[cfg(any())]
#[track_caller]
pub fn cargo_cfg_rustfmt() -> bool {
ENV.is_present("CARGO_CFG_RUSTFMT")
}
#[doc = unstable!(cfg_sanitize, 39699)]
#[cfg(feature = "unstable")]
#[track_caller]
pub fn cargo_cfg_sanitize() -> Option<Vec<String>> {
ENV.get("CARGO_CFG_SANITIZE").map(|v| to_strings(v, ','))
}
#[doc = unstable!(cfg_sanitizer_cfi, 89653)]
#[cfg(feature = "unstable")]
#[track_caller]
pub fn cargo_cfg_sanitizer_cfi_generalize_pointers() -> bool {
ENV.is_present("CARGO_CFG_SANITIZER_CFI_GENERALIZE_POINTERS")
}
#[doc = unstable!(cfg_sanitizer_cfi, 89653)]
#[cfg(feature = "unstable")]
#[track_caller]
pub fn cargo_cfg_sanitizer_cfi_normalize_integers() -> bool {
ENV.is_present("CARGO_CFG_SANITIZER_CFI_NORMALIZE_INTEGERS")
}
#[track_caller]
pub fn cargo_cfg_target_abi() -> Option<String> {
to_opt(var_or_panic("CARGO_CFG_TARGET_ABI")).map(to_string)
}
#[track_caller]
pub fn cargo_cfg_target_arch() -> String {
to_string(var_or_panic("CARGO_CFG_TARGET_ARCH"))
}
#[track_caller]
pub fn cargo_cfg_target_endian() -> String {
to_string(var_or_panic("CARGO_CFG_TARGET_ENDIAN"))
}
#[track_caller]
pub fn cargo_cfg_target_env() -> String {
to_string(var_or_panic("CARGO_CFG_TARGET_ENV"))
}
#[track_caller]
pub fn cargo_target_family() -> Vec<String> {
to_strings(var_or_panic(&cargo_cfg_var("target_family")), ',')
}
#[track_caller]
pub fn cargo_cfg_target_feature() -> Vec<String> {
to_strings(var_or_panic(&cargo_cfg_var("target_feature")), ',')
}
#[track_caller]
pub fn cargo_cfg_target_has_atomic() -> Vec<String> {
to_strings(var_or_panic(&cargo_cfg_var("target_has_atomic")), ',')
}
#[doc = unstable!(cfg_target_has_atomic_equal_alignment, 93822)]
#[cfg(feature = "unstable")]
#[track_caller]
pub fn cargo_cfg_target_has_atomic_equal_alignment() -> Vec<String> {
to_strings(
var_or_panic(&cargo_cfg_var("target_has_atomic_equal_alignment")),
',',
)
}
#[doc = unstable!(cfg_target_has_atomic_load_store, 94039)]
#[cfg(feature = "unstable")]
#[track_caller]
pub fn cargo_cfg_target_has_atomic_load_store() -> Vec<String> {
to_strings(
var_or_panic(&cargo_cfg_var("target_has_atomic_load_store")),
',',
)
}
#[track_caller]
pub fn cargo_cfg_target_os() -> String {
to_string(var_or_panic("CARGO_CFG_TARGET_OS"))
}
#[track_caller]
pub fn cargo_cfg_target_pointer_width() -> u32 {
to_parsed(var_or_panic("CARGO_CFG_TARGET_POINTER_WIDTH"))
}
#[doc = unstable!(cfg_target_thread_local, 29594)]
#[cfg(feature = "unstable")]
#[track_caller]
pub fn cargo_cfg_target_thread_local() -> bool {
ENV.is_present("CARGO_CFG_TARGET_THREAD_LOCAL")
}
#[track_caller]
pub fn cargo_cfg_target_vendor() -> String {
to_string(var_or_panic("CARGO_CFG_TARGET_VENDOR"))
}
#[cfg(any())]
#[track_caller]
pub fn cargo_cfg_test() -> bool {
ENV.is_present("CARGO_CFG_TEST")
}
#[doc = unstable!(cfg_ub_checks, 123499)]
#[cfg(feature = "unstable")]
#[track_caller]
pub fn cargo_cfg_ub_checks() -> bool {
ENV.is_present("CARGO_CFG_UB_CHECKS")
}
#[track_caller]
pub fn cargo_cfg_unix() -> bool {
ENV.is_present("CARGO_CFG_UNIX")
}
#[track_caller]
pub fn cargo_cfg_windows() -> bool {
ENV.is_present("CARGO_CFG_WINDOWS")
}
}
#[track_caller]
pub fn out_dir() -> PathBuf {
to_path(var_or_panic("OUT_DIR"))
}
#[track_caller]
pub fn target() -> String {
to_string(var_or_panic("TARGET"))
}
#[track_caller]
pub fn host() -> String {
to_string(var_or_panic("HOST"))
}
#[track_caller]
pub fn num_jobs() -> u32 {
to_parsed(var_or_panic("NUM_JOBS"))
}
#[track_caller]
pub fn opt_level() -> String {
to_string(var_or_panic("OPT_LEVEL"))
}
#[track_caller]
pub fn debug() -> String {
to_string(var_or_panic("DEBUG"))
}
#[track_caller]
pub fn profile() -> String {
to_string(var_or_panic("PROFILE"))
}
#[track_caller]
pub fn dep_metadata(name: &str, key: &str) -> Option<String> {
if !is_crate_name(name) {
panic!("invalid dependency name {name:?}")
}
if !is_ascii_ident(key) {
panic!("invalid metadata key {key:?}")
}
let name = name.to_uppercase().replace('-', "_");
let key = key.to_uppercase().replace('-', "_");
let key = format!("DEP_{name}_{key}");
ENV.get(&key).map(to_string)
}
#[track_caller]
pub fn rustc() -> PathBuf {
to_path(var_or_panic("RUSTC"))
}
#[track_caller]
pub fn rustdoc() -> PathBuf {
to_path(var_or_panic("RUSTDOC"))
}
#[track_caller]
pub fn rustc_wrapper() -> Option<PathBuf> {
ENV.get("RUSTC_WRAPPER").map(to_path)
}
#[track_caller]
pub fn rustc_workspace_wrapper() -> Option<PathBuf> {
ENV.get("RUSTC_WORKSPACE_WRAPPER").map(to_path)
}
#[track_caller]
pub fn rustc_linker() -> Option<PathBuf> {
ENV.get("RUSTC_LINKER").map(to_path)
}
#[track_caller]
pub fn cargo_encoded_rustflags() -> Vec<String> {
to_strings(var_or_panic("CARGO_ENCODED_RUSTFLAGS"), '\x1f')
}
#[track_caller]
pub fn cargo_pkg_version() -> String {
to_string(var_or_panic("CARGO_PKG_VERSION"))
}
#[track_caller]
pub fn cargo_pkg_version_major() -> u64 {
to_parsed(var_or_panic("CARGO_PKG_VERSION_MAJOR"))
}
#[track_caller]
pub fn cargo_pkg_version_minor() -> u64 {
to_parsed(var_or_panic("CARGO_PKG_VERSION_MINOR"))
}
#[track_caller]
pub fn cargo_pkg_version_patch() -> u64 {
to_parsed(var_or_panic("CARGO_PKG_VERSION_PATCH"))
}
#[track_caller]
pub fn cargo_pkg_version_pre() -> Option<String> {
to_opt(var_or_panic("CARGO_PKG_VERSION_PRE")).map(to_string)
}
#[track_caller]
pub fn cargo_pkg_authors() -> Vec<String> {
to_strings(var_or_panic("CARGO_PKG_AUTHORS"), ':')
}
#[track_caller]
pub fn cargo_pkg_name() -> String {
to_string(var_or_panic("CARGO_PKG_NAME"))
}
#[track_caller]
pub fn cargo_pkg_description() -> Option<String> {
to_opt(var_or_panic("CARGO_PKG_DESCRIPTION")).map(to_string)
}
#[track_caller]
pub fn cargo_pkg_homepage() -> Option<String> {
to_opt(var_or_panic("CARGO_PKG_HOMEPAGE")).map(to_string)
}
#[track_caller]
pub fn cargo_pkg_repository() -> Option<String> {
to_opt(var_or_panic("CARGO_PKG_REPOSITORY")).map(to_string)
}
#[track_caller]
pub fn cargo_pkg_license() -> Option<String> {
to_opt(var_or_panic("CARGO_PKG_LICENSE")).map(to_string)
}
#[track_caller]
pub fn cargo_pkg_license_file() -> Option<PathBuf> {
to_opt(var_or_panic("CARGO_PKG_LICENSE_FILE")).map(to_path)
}
#[track_caller]
pub fn cargo_pkg_rust_version() -> Option<String> {
to_opt(var_or_panic("CARGO_PKG_RUST_VERSION")).map(to_string)
}
#[track_caller]
pub fn cargo_pkg_readme() -> Option<PathBuf> {
to_opt(var_or_panic("CARGO_PKG_README")).map(to_path)
}
#[track_caller]
fn var_or_panic(key: &str) -> std::ffi::OsString {
ENV.get(key)
.unwrap_or_else(|| panic!("cargo environment variable `{key}` is missing"))
}
fn to_path(value: std::ffi::OsString) -> PathBuf {
PathBuf::from(value)
}
#[track_caller]
fn to_string(value: std::ffi::OsString) -> String {
match value.into_string() {
Ok(s) => s,
Err(value) => {
let err = std::str::from_utf8(value.as_encoded_bytes()).unwrap_err();
panic!("{err}")
}
}
}
fn to_opt(value: std::ffi::OsString) -> Option<std::ffi::OsString> {
(!value.is_empty()).then_some(value)
}
#[track_caller]
fn to_strings(value: std::ffi::OsString, sep: char) -> Vec<String> {
if value.is_empty() {
return Vec::new();
}
let value = to_string(value);
value.split(sep).map(str::to_owned).collect()
}
#[track_caller]
fn to_parsed<T>(value: std::ffi::OsString) -> T
where
T: std::str::FromStr,
T::Err: std::fmt::Display,
{
let value = to_string(value);
match value.parse() {
Ok(s) => s,
Err(err) => {
panic!("{err}")
}
}
}