Skip to content

Commit 03a5f11

Browse files
authored
fix: env table config can't trigger rebuild with rerun-if-env-changed. (#14756)
### What does this PR try to resolve? As #10358 said, `When a build.rs script emits cargo:rerun-if-env-changed, it is not re-run when the value of the specified variable is changed via the env configuration.` Fixes #10358 ### How should we test and review this PR? Add test bofore fixing to reflect the issue, the next commit fixed it. ### Additional information The PR is a sucessor of #14058, so the previous dicussion can be refer to it.
2 parents 61d587f + 76ffbe0 commit 03a5f11

File tree

2 files changed

+100
-14
lines changed

2 files changed

+100
-14
lines changed

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

+31-14
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ mod dirty_reason;
374374

375375
use std::collections::hash_map::{Entry, HashMap};
376376
use std::env;
377+
use std::ffi::OsString;
377378
use std::fs;
378379
use std::fs::File;
379380
use std::hash::{self, Hash, Hasher};
@@ -509,7 +510,7 @@ pub fn prepare_target(
509510
// thunk we can invoke on a foreign thread to calculate this.
510511
let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
511512
let metadata = build_runner.get_run_build_script_metadata(unit);
512-
let (gen_local, _overridden) = build_script_local_fingerprints(build_runner, unit);
513+
let (gen_local, _overridden) = build_script_local_fingerprints(build_runner, unit)?;
513514
let output_path = build_runner.build_explicit_deps[unit]
514515
.build_script_output
515516
.clone();
@@ -801,15 +802,24 @@ pub enum StaleItem {
801802

802803
impl LocalFingerprint {
803804
/// Read the environment variable of the given env `key`, and creates a new
804-
/// [`LocalFingerprint::RerunIfEnvChanged`] for it.
805+
/// [`LocalFingerprint::RerunIfEnvChanged`] for it. The `env_config` is used firstly
806+
/// to check if the env var is set in the config system as some envs need to be overridden.
807+
/// If not, it will fallback to `std::env::var`.
805808
///
806-
// TODO: This is allowed at this moment. Should figure out if it makes
807-
// sense if permitting to read env from the config system.
809+
// TODO: `std::env::var` is allowed at this moment. Should figure out
810+
// if it makes sense if permitting to read env from the env snapshot.
808811
#[allow(clippy::disallowed_methods)]
809-
fn from_env<K: AsRef<str>>(key: K) -> LocalFingerprint {
812+
fn from_env<K: AsRef<str>>(
813+
key: K,
814+
env_config: &Arc<HashMap<String, OsString>>,
815+
) -> LocalFingerprint {
810816
let key = key.as_ref();
811817
let var = key.to_owned();
812-
let val = env::var(key).ok();
818+
let val = if let Some(val) = env_config.get(key) {
819+
val.to_str().map(ToOwned::to_owned)
820+
} else {
821+
env::var(key).ok()
822+
};
813823
LocalFingerprint::RerunIfEnvChanged { var, val }
814824
}
815825

@@ -1577,7 +1587,7 @@ fn calculate_run_custom_build(
15771587
// the build script this means we'll be watching files and env vars.
15781588
// Otherwise if we haven't previously executed it we'll just start watching
15791589
// the whole crate.
1580-
let (gen_local, overridden) = build_script_local_fingerprints(build_runner, unit);
1590+
let (gen_local, overridden) = build_script_local_fingerprints(build_runner, unit)?;
15811591
let deps = &build_runner.build_explicit_deps[unit];
15821592
let local = (gen_local)(
15831593
deps,
@@ -1671,7 +1681,7 @@ See https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-change
16711681
fn build_script_local_fingerprints(
16721682
build_runner: &mut BuildRunner<'_, '_>,
16731683
unit: &Unit,
1674-
) -> (
1684+
) -> CargoResult<(
16751685
Box<
16761686
dyn FnOnce(
16771687
&BuildDeps,
@@ -1680,20 +1690,20 @@ fn build_script_local_fingerprints(
16801690
+ Send,
16811691
>,
16821692
bool,
1683-
) {
1693+
)> {
16841694
assert!(unit.mode.is_run_custom_build());
16851695
// First up, if this build script is entirely overridden, then we just
16861696
// return the hash of what we overrode it with. This is the easy case!
16871697
if let Some(fingerprint) = build_script_override_fingerprint(build_runner, unit) {
16881698
debug!("override local fingerprints deps {}", unit.pkg);
1689-
return (
1699+
return Ok((
16901700
Box::new(
16911701
move |_: &BuildDeps, _: Option<&dyn Fn() -> CargoResult<String>>| {
16921702
Ok(Some(vec![fingerprint]))
16931703
},
16941704
),
16951705
true, // this is an overridden build script
1696-
);
1706+
));
16971707
}
16981708

16991709
// ... Otherwise this is a "real" build script and we need to return a real
@@ -1705,6 +1715,7 @@ fn build_script_local_fingerprints(
17051715
// obvious.
17061716
let pkg_root = unit.pkg.root().to_path_buf();
17071717
let target_dir = target_root(build_runner);
1718+
let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
17081719
let calculate =
17091720
move |deps: &BuildDeps, pkg_fingerprint: Option<&dyn Fn() -> CargoResult<String>>| {
17101721
if deps.rerun_if_changed.is_empty() && deps.rerun_if_env_changed.is_empty() {
@@ -1734,11 +1745,16 @@ fn build_script_local_fingerprints(
17341745
// Ok so now we're in "new mode" where we can have files listed as
17351746
// dependencies as well as env vars listed as dependencies. Process
17361747
// them all here.
1737-
Ok(Some(local_fingerprints_deps(deps, &target_dir, &pkg_root)))
1748+
Ok(Some(local_fingerprints_deps(
1749+
deps,
1750+
&target_dir,
1751+
&pkg_root,
1752+
&env_config,
1753+
)))
17381754
};
17391755

17401756
// Note that `false` == "not overridden"
1741-
(Box::new(calculate), false)
1757+
Ok((Box::new(calculate), false))
17421758
}
17431759

17441760
/// Create a [`LocalFingerprint`] for an overridden build script.
@@ -1769,6 +1785,7 @@ fn local_fingerprints_deps(
17691785
deps: &BuildDeps,
17701786
target_root: &Path,
17711787
pkg_root: &Path,
1788+
env_config: &Arc<HashMap<String, OsString>>,
17721789
) -> Vec<LocalFingerprint> {
17731790
debug!("new local fingerprints deps {:?}", pkg_root);
17741791
let mut local = Vec::new();
@@ -1793,7 +1810,7 @@ fn local_fingerprints_deps(
17931810
local.extend(
17941811
deps.rerun_if_env_changed
17951812
.iter()
1796-
.map(LocalFingerprint::from_env),
1813+
.map(|s| LocalFingerprint::from_env(s, env_config)),
17971814
);
17981815

17991816
local

tests/testsuite/build_script_env.rs

+69
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,72 @@ fn rustc_cfg_with_and_without_value() {
383383
);
384384
check.run();
385385
}
386+
387+
#[cargo_test]
388+
fn rerun_if_env_is_exsited_config() {
389+
let p = project()
390+
.file("src/main.rs", "fn main() {}")
391+
.file(
392+
"build.rs",
393+
r#"
394+
fn main() {
395+
println!("cargo::rerun-if-env-changed=FOO");
396+
}
397+
"#,
398+
)
399+
.file(
400+
".cargo/config.toml",
401+
r#"
402+
[env]
403+
FOO = "foo"
404+
"#,
405+
)
406+
.build();
407+
408+
p.cargo("check")
409+
.with_stderr_data(str![[r#"
410+
[COMPILING] foo v0.0.1 ([ROOT]/foo)
411+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
412+
413+
"#]])
414+
.run();
415+
416+
p.cargo(r#"check --config 'env.FOO="bar"'"#)
417+
.with_stderr_data(str![[r#"
418+
[COMPILING] foo v0.0.1 ([ROOT]/foo)
419+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
420+
421+
"#]])
422+
.run();
423+
}
424+
425+
#[cargo_test]
426+
fn rerun_if_env_newly_added_in_config() {
427+
let p = project()
428+
.file("src/main.rs", "fn main() {}")
429+
.file(
430+
"build.rs",
431+
r#"
432+
fn main() {
433+
println!("cargo::rerun-if-env-changed=FOO");
434+
}
435+
"#,
436+
)
437+
.build();
438+
439+
p.cargo("check")
440+
.with_stderr_data(str![[r#"
441+
[COMPILING] foo v0.0.1 ([ROOT]/foo)
442+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
443+
444+
"#]])
445+
.run();
446+
447+
p.cargo(r#"check --config 'env.FOO="foo"'"#)
448+
.with_stderr_data(str![[r#"
449+
[COMPILING] foo v0.0.1 ([ROOT]/foo)
450+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
451+
452+
"#]])
453+
.run();
454+
}

0 commit comments

Comments
 (0)