Skip to content

Commit 0a52c09

Browse files
committed
fix: envs in config can trigger rebuild by custom build script with rerun-if-env-changed.
1 parent 475f6dc commit 0a52c09

File tree

2 files changed

+49
-15
lines changed

2 files changed

+49
-15
lines changed

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

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,10 @@
362362
mod dep_info;
363363
mod dirty_reason;
364364

365+
use std::cell::OnceCell;
365366
use std::collections::hash_map::{Entry, HashMap};
366367
use std::env;
368+
use std::ffi::OsString;
367369
use std::fs;
368370
use std::fs::File;
369371
use std::hash::{self, Hash, Hasher};
@@ -499,7 +501,7 @@ pub fn prepare_target(
499501
// thunk we can invoke on a foreign thread to calculate this.
500502
let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
501503
let metadata = build_runner.get_run_build_script_metadata(unit);
502-
let (gen_local, _overridden) = build_script_local_fingerprints(build_runner, unit);
504+
let (gen_local, _overridden) = build_script_local_fingerprints(build_runner, unit)?;
503505
let output_path = build_runner.build_explicit_deps[unit]
504506
.build_script_output
505507
.clone();
@@ -796,14 +798,36 @@ pub enum StaleItem {
796798
impl LocalFingerprint {
797799
/// Read the environment variable of the given env `key`, and creates a new
798800
/// [`LocalFingerprint::RerunIfEnvChanged`] for it.
799-
///
800-
// TODO: This is allowed at this moment. Should figure out if it makes
801-
// sense if permitting to read env from the config system.
802801
#[allow(clippy::disallowed_methods)]
803-
fn from_env<K: AsRef<str>>(key: K) -> LocalFingerprint {
802+
fn from_env<K: AsRef<str>>(
803+
key: K,
804+
env_config: &Arc<HashMap<String, OsString>>,
805+
env_config_insensitive: &Arc<OnceCell<HashMap<String, OsString>>>,
806+
) -> LocalFingerprint {
804807
let key = key.as_ref();
805808
let var = key.to_owned();
806-
let val = env::var(key).ok();
809+
let val = if let Some(val) = match env_config.get(key) {
810+
Some(value) => value.to_str().map(|s| s.to_string()),
811+
None => {
812+
if cfg!(windows) {
813+
env_config_insensitive
814+
.get_or_init(|| {
815+
env_config
816+
.iter()
817+
.map(|(k, v)| (k.to_uppercase().clone(), v.clone()))
818+
.collect::<HashMap<String, OsString>>()
819+
})
820+
.get(&key.to_uppercase())
821+
.and_then(|s| s.to_str().map(|s| s.to_string()))
822+
} else {
823+
None
824+
}
825+
}
826+
} {
827+
Some(val)
828+
} else {
829+
env::var(key).ok()
830+
};
807831
LocalFingerprint::RerunIfEnvChanged { var, val }
808832
}
809833

@@ -1573,7 +1597,7 @@ fn calculate_run_custom_build(
15731597
// the build script this means we'll be watching files and env vars.
15741598
// Otherwise if we haven't previously executed it we'll just start watching
15751599
// the whole crate.
1576-
let (gen_local, overridden) = build_script_local_fingerprints(build_runner, unit);
1600+
let (gen_local, overridden) = build_script_local_fingerprints(build_runner, unit)?;
15771601
let deps = &build_runner.build_explicit_deps[unit];
15781602
let local = (gen_local)(
15791603
deps,
@@ -1667,7 +1691,7 @@ See https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-change
16671691
fn build_script_local_fingerprints(
16681692
build_runner: &mut BuildRunner<'_, '_>,
16691693
unit: &Unit,
1670-
) -> (
1694+
) -> CargoResult<(
16711695
Box<
16721696
dyn FnOnce(
16731697
&BuildDeps,
@@ -1676,20 +1700,20 @@ fn build_script_local_fingerprints(
16761700
+ Send,
16771701
>,
16781702
bool,
1679-
) {
1703+
)> {
16801704
assert!(unit.mode.is_run_custom_build());
16811705
// First up, if this build script is entirely overridden, then we just
16821706
// return the hash of what we overrode it with. This is the easy case!
16831707
if let Some(fingerprint) = build_script_override_fingerprint(build_runner, unit) {
16841708
debug!("override local fingerprints deps {}", unit.pkg);
1685-
return (
1709+
return Ok((
16861710
Box::new(
16871711
move |_: &BuildDeps, _: Option<&dyn Fn() -> CargoResult<String>>| {
16881712
Ok(Some(vec![fingerprint]))
16891713
},
16901714
),
16911715
true, // this is an overridden build script
1692-
);
1716+
));
16931717
}
16941718

16951719
// ... Otherwise this is a "real" build script and we need to return a real
@@ -1701,6 +1725,7 @@ fn build_script_local_fingerprints(
17011725
// obvious.
17021726
let pkg_root = unit.pkg.root().to_path_buf();
17031727
let target_dir = target_root(build_runner);
1728+
let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
17041729
let calculate =
17051730
move |deps: &BuildDeps, pkg_fingerprint: Option<&dyn Fn() -> CargoResult<String>>| {
17061731
if deps.rerun_if_changed.is_empty() && deps.rerun_if_env_changed.is_empty() {
@@ -1730,11 +1755,16 @@ fn build_script_local_fingerprints(
17301755
// Ok so now we're in "new mode" where we can have files listed as
17311756
// dependencies as well as env vars listed as dependencies. Process
17321757
// them all here.
1733-
Ok(Some(local_fingerprints_deps(deps, &target_dir, &pkg_root)))
1758+
Ok(Some(local_fingerprints_deps(
1759+
deps,
1760+
&target_dir,
1761+
&pkg_root,
1762+
&env_config,
1763+
)))
17341764
};
17351765

17361766
// Note that `false` == "not overridden"
1737-
(Box::new(calculate), false)
1767+
Ok((Box::new(calculate), false))
17381768
}
17391769

17401770
/// Create a [`LocalFingerprint`] for an overridden build script.
@@ -1765,6 +1795,7 @@ fn local_fingerprints_deps(
17651795
deps: &BuildDeps,
17661796
target_root: &Path,
17671797
pkg_root: &Path,
1798+
env_config: &Arc<HashMap<String, OsString>>,
17681799
) -> Vec<LocalFingerprint> {
17691800
debug!("new local fingerprints deps {:?}", pkg_root);
17701801
let mut local = Vec::new();
@@ -1785,11 +1816,11 @@ fn local_fingerprints_deps(
17851816
.collect();
17861817
local.push(LocalFingerprint::RerunIfChanged { output, paths });
17871818
}
1788-
1819+
let env_config_insensitive = Arc::new(OnceCell::new());
17891820
local.extend(
17901821
deps.rerun_if_env_changed
17911822
.iter()
1792-
.map(LocalFingerprint::from_env),
1823+
.map(|s| LocalFingerprint::from_env(s, env_config, &env_config_insensitive)),
17931824
);
17941825

17951826
local

tests/testsuite/build_script_env.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,7 @@ fn env_config_rerun_if_changed() {
422422
);
423423
p.cargo("check")
424424
.with_stderr_data(str![[r#"
425+
[COMPILING] foo v0.0.1 ([ROOT]/foo)
425426
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
426427
427428
"#]])
@@ -473,6 +474,7 @@ fn insensitive_env_config_rerun_if_changed() {
473474
);
474475
p.cargo("check")
475476
.with_stderr_data(str![[r#"
477+
[COMPILING] foo v0.0.1 ([ROOT]/foo)
476478
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
477479
478480
"#]])
@@ -516,6 +518,7 @@ fn env_config_newly_added_rerun() {
516518
);
517519
p.cargo("check")
518520
.with_stderr_data(str![[r#"
521+
[COMPILING] foo v0.0.1 ([ROOT]/foo)
519522
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
520523
521524
"#]])

0 commit comments

Comments
 (0)