Skip to content

Commit cbb2164

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 a7745fb commit cbb2164

File tree

5 files changed

+109
-3
lines changed

5 files changed

+109
-3
lines changed

crates/cargo-util/src/paths.rs

Lines changed: 1 addition & 1 deletion
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

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

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

Lines changed: 8 additions & 1 deletion
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

Lines changed: 5 additions & 0 deletions
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};
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;
@@ -334,6 +335,10 @@ fn rustc(
334335
output_options.show_diagnostics = false;
335336
}
336337
let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
338+
339+
#[cfg(debug_assertions)]
340+
compilation::assert_only_envs_set_by_cargo(rustc.get_envs().keys(), &env_config);
341+
337342
return Ok(Work::new(move |state| {
338343
// Artifacts are in a different location than typical units,
339344
// hence we must assure the crate- and target-dependent

tests/testsuite/cargo_env_config.rs

Lines changed: 0 additions & 1 deletion
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)