Skip to content

Commit 45a4e98

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 4f16ebf commit 45a4e98

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

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)