Skip to content

Commit 3129515

Browse files
committed
fix(env): dont track envs set by cargo in dep-info file
This patch is quite hacky since the "set-by-Cargo" env vars are hardcoded. However, not all environment variables set by Cargo are statically known. To prevent the actual environment variables applied to rustc invocation get out of sync from what we hard-code, a debug time assertion is put to help discover missing environment variables earlier.
1 parent 08b7746 commit 3129515

File tree

5 files changed

+109
-3
lines changed

5 files changed

+109
-3
lines changed

crates/cargo-util/src/paths.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ pub fn join_paths<T: AsRef<OsStr>>(paths: &[T], env: &str) -> Result<OsString> {
3535

3636
/// Returns the name of the environment variable used for searching for
3737
/// dynamic libraries.
38-
pub fn dylib_path_envvar() -> &'static str {
38+
pub const fn dylib_path_envvar() -> &'static str {
3939
if cfg!(windows) {
4040
"PATH"
4141
} else if cfg!(target_os = "macos") {

src/cargo/core/compiler/compilation.rs

+95
Original file line numberDiff line numberDiff line change
@@ -527,3 +527,98 @@ fn target_linker(bcx: &BuildContext<'_, '_>, kind: CompileKind) -> CargoResult<O
527527
}
528528
Ok(matching_linker.map(|(_k, linker)| linker.val.clone().resolve_program(bcx.gctx)))
529529
}
530+
531+
/// This tracks environment variables Cargo sets to rustc when building a crate.
532+
///
533+
/// This only inclues variables with statically known keys.
534+
/// For environment variable keys that vary like `CARG_BIN_EXE_<name>` or
535+
/// `-Zbindeps` related env vars, we compare only their prefixes to determine
536+
/// if they are internal.
537+
/// See [`is_env_set_by_cargo`] and
538+
/// <https://doc.rust-lang.org/nightly/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>.
539+
const ENV_VARS_SET_FOR_CRATES: [&str; 23] = [
540+
crate::CARGO_ENV,
541+
"CARGO_MANIFEST_DIR",
542+
"CARGO_MANIFEST_PATH",
543+
"CARGO_PKG_VERSION",
544+
"CARGO_PKG_VERSION_MAJOR",
545+
"CARGO_PKG_VERSION_MINOR",
546+
"CARGO_PKG_VERSION_PATCH",
547+
"CARGO_PKG_VERSION_PRE",
548+
"CARGO_PKG_AUTHORS",
549+
"CARGO_PKG_NAME",
550+
"CARGO_PKG_DESCRIPTION",
551+
"CARGO_PKG_HOMEPAGE",
552+
"CARGO_PKG_REPOSITORY",
553+
"CARGO_PKG_LICENSE",
554+
"CARGO_PKG_LICENSE_FILE",
555+
"CARGO_PKG_RUST_VERSION",
556+
"CARGO_PKG_README",
557+
"CARGO_CRATE_NAME",
558+
"CARGO_BIN_NAME",
559+
"OUT_DIR",
560+
"CARGO_PRIMARY_PACKAGE",
561+
"CARGO_TARGET_TMPDIR",
562+
paths::dylib_path_envvar(),
563+
];
564+
/// Asserts if the given env vars are controlled by Cargo.
565+
///
566+
/// This is only for reminding Cargo developer to keep newly added environment
567+
/// variables in sync with [`ENV_VARS_SET_FOR_CRATES`].
568+
#[cfg(debug_assertions)]
569+
pub(crate) fn assert_only_envs_set_by_cargo<'a>(
570+
keys: impl Iterator<Item = impl AsRef<str>>,
571+
env_config: &HashMap<String, OsString>,
572+
) {
573+
for key in keys {
574+
let key = key.as_ref();
575+
// When running Cargo's test suite,
576+
// we're fine if it is from the `[env]` table
577+
if env_config.contains_key(key) {
578+
continue;
579+
}
580+
assert!(
581+
is_env_set_by_cargo(key),
582+
"`{key}` is not tracked as an environment variable set by Cargo\n\
583+
Add it to `ENV_VARS_SET_FOR_CRATES` if you intend to introduce a new one"
584+
);
585+
}
586+
}
587+
588+
/// True if the given env var is controlled or set by Cargo.
589+
/// See [`ENV_VARS_SET_FOR_CRATES`].
590+
pub(crate) fn is_env_set_by_cargo(key: &str) -> bool {
591+
ENV_VARS_SET_FOR_CRATES.contains(&key)
592+
|| key.starts_with("CARGO_BIN_EXE_")
593+
|| key.starts_with("__CARGO") // internal/test-only envs
594+
|| key == "RUSTC_BOOTSTRAP" // for -Zbuild-std
595+
|| is_artifact_dep_env_vars(key)
596+
}
597+
598+
/// Whether an env var is set because of `-Zbindeps`.
599+
fn is_artifact_dep_env_vars(key: &str) -> bool {
600+
let Some(key) = key.strip_prefix("CARGO_") else {
601+
return false;
602+
};
603+
let Some(key) = key
604+
.strip_prefix("BIN_")
605+
.or_else(|| key.strip_prefix("CDYLIB_"))
606+
.or_else(|| key.strip_prefix("STATICLIB_"))
607+
else {
608+
return false;
609+
};
610+
key.starts_with("FILE_") || key.starts_with("DIR_")
611+
}
612+
613+
#[cfg(test)]
614+
mod tests {
615+
use std::collections::HashSet;
616+
617+
use super::ENV_VARS_SET_FOR_CRATES;
618+
619+
#[test]
620+
fn ensure_env_vars_set_for_crates_unique() {
621+
let set: HashSet<&str> = HashSet::from_iter(ENV_VARS_SET_FOR_CRATES);
622+
assert_eq!(ENV_VARS_SET_FOR_CRATES.len(), set.len());
623+
}
624+
}

src/cargo/core/compiler/fingerprint/dep_info.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use cargo_util::paths;
1919
use cargo_util::ProcessBuilder;
2020
use cargo_util::Sha256;
2121

22+
use crate::core::compiler::is_env_set_by_cargo;
2223
use crate::CargoResult;
2324
use crate::CARGO_ENV;
2425

@@ -334,7 +335,13 @@ pub fn translate_dep_info(
334335
//
335336
// For cargo#13280, We trace env vars that are defined in the `[env]` config table.
336337
on_disk_info.env.retain(|(key, _)| {
337-
env_config.contains_key(key) || !rustc_cmd.get_envs().contains_key(key) || key == CARGO_ENV
338+
if env_config.contains_key(key) && !is_env_set_by_cargo(key) {
339+
return true;
340+
}
341+
if !rustc_cmd.get_envs().contains_key(key) {
342+
return true;
343+
}
344+
key == CARGO_ENV
338345
});
339346

340347
let serialize_path = |file| {

src/cargo/core/compiler/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ pub use self::build_context::{
7474
};
7575
use self::build_plan::BuildPlan;
7676
pub use self::build_runner::{BuildRunner, Metadata, UnitHash};
77+
pub(crate) use self::compilation::is_env_set_by_cargo;
7778
pub use self::compilation::{Compilation, Doctest, UnitOutput};
7879
pub use self::compile_kind::{CompileKind, CompileTarget};
7980
pub use self::crate_type::CrateType;
@@ -331,6 +332,10 @@ fn rustc(
331332
output_options.show_diagnostics = false;
332333
}
333334
let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
335+
336+
#[cfg(debug_assertions)]
337+
compilation::assert_only_envs_set_by_cargo(rustc.get_envs().keys(), &env_config);
338+
334339
return Ok(Work::new(move |state| {
335340
// Artifacts are in a different location than typical units,
336341
// hence we must assure the crate- and target-dependent

tests/testsuite/cargo_env_config.rs

-1
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,6 @@ foo
490490
491491
"#]])
492492
.with_stderr_data(str![[r#"
493-
[COMPILING] foo v0.5.0 ([ROOT]/foo)
494493
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
495494
[RUNNING] `target/debug/foo[EXE]`
496495

0 commit comments

Comments
 (0)