Skip to content

Commit ab9438f

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

File tree

2 files changed

+51
-15
lines changed

2 files changed

+51
-15
lines changed

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

Lines changed: 48 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,38 @@ 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| str::to_string(s)),
811+
None => {
812+
if cfg!(windows) {
813+
let env_config_insensitive = env_config_insensitive.get_or_init(|| {
814+
env_config
815+
.iter()
816+
.map(|(k, v)| (k.to_uppercase().clone(), v.clone()))
817+
.collect()
818+
});
819+
let val = env_config_insensitive
820+
.get(&key.to_uppercase())
821+
.and_then(|s| s.to_str().map(|s| str::to_string(s)));
822+
823+
val
824+
} else {
825+
None
826+
}
827+
}
828+
} {
829+
Some(val)
830+
} else {
831+
env::var(key).ok()
832+
};
807833
LocalFingerprint::RerunIfEnvChanged { var, val }
808834
}
809835

@@ -1573,7 +1599,7 @@ fn calculate_run_custom_build(
15731599
// the build script this means we'll be watching files and env vars.
15741600
// Otherwise if we haven't previously executed it we'll just start watching
15751601
// the whole crate.
1576-
let (gen_local, overridden) = build_script_local_fingerprints(build_runner, unit);
1602+
let (gen_local, overridden) = build_script_local_fingerprints(build_runner, unit)?;
15771603
let deps = &build_runner.build_explicit_deps[unit];
15781604
let local = (gen_local)(
15791605
deps,
@@ -1667,7 +1693,7 @@ See https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-change
16671693
fn build_script_local_fingerprints(
16681694
build_runner: &mut BuildRunner<'_, '_>,
16691695
unit: &Unit,
1670-
) -> (
1696+
) -> CargoResult<(
16711697
Box<
16721698
dyn FnOnce(
16731699
&BuildDeps,
@@ -1676,20 +1702,20 @@ fn build_script_local_fingerprints(
16761702
+ Send,
16771703
>,
16781704
bool,
1679-
) {
1705+
)> {
16801706
assert!(unit.mode.is_run_custom_build());
16811707
// First up, if this build script is entirely overridden, then we just
16821708
// return the hash of what we overrode it with. This is the easy case!
16831709
if let Some(fingerprint) = build_script_override_fingerprint(build_runner, unit) {
16841710
debug!("override local fingerprints deps {}", unit.pkg);
1685-
return (
1711+
return Ok((
16861712
Box::new(
16871713
move |_: &BuildDeps, _: Option<&dyn Fn() -> CargoResult<String>>| {
16881714
Ok(Some(vec![fingerprint]))
16891715
},
16901716
),
16911717
true, // this is an overridden build script
1692-
);
1718+
));
16931719
}
16941720

16951721
// ... Otherwise this is a "real" build script and we need to return a real
@@ -1701,6 +1727,7 @@ fn build_script_local_fingerprints(
17011727
// obvious.
17021728
let pkg_root = unit.pkg.root().to_path_buf();
17031729
let target_dir = target_root(build_runner);
1730+
let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
17041731
let calculate =
17051732
move |deps: &BuildDeps, pkg_fingerprint: Option<&dyn Fn() -> CargoResult<String>>| {
17061733
if deps.rerun_if_changed.is_empty() && deps.rerun_if_env_changed.is_empty() {
@@ -1730,11 +1757,16 @@ fn build_script_local_fingerprints(
17301757
// Ok so now we're in "new mode" where we can have files listed as
17311758
// dependencies as well as env vars listed as dependencies. Process
17321759
// them all here.
1733-
Ok(Some(local_fingerprints_deps(deps, &target_dir, &pkg_root)))
1760+
Ok(Some(local_fingerprints_deps(
1761+
deps,
1762+
&target_dir,
1763+
&pkg_root,
1764+
&env_config,
1765+
)))
17341766
};
17351767

17361768
// Note that `false` == "not overridden"
1737-
(Box::new(calculate), false)
1769+
Ok((Box::new(calculate), false))
17381770
}
17391771

17401772
/// Create a [`LocalFingerprint`] for an overridden build script.
@@ -1765,6 +1797,7 @@ fn local_fingerprints_deps(
17651797
deps: &BuildDeps,
17661798
target_root: &Path,
17671799
pkg_root: &Path,
1800+
env_config: &Arc<HashMap<String, OsString>>,
17681801
) -> Vec<LocalFingerprint> {
17691802
debug!("new local fingerprints deps {:?}", pkg_root);
17701803
let mut local = Vec::new();
@@ -1785,11 +1818,11 @@ fn local_fingerprints_deps(
17851818
.collect();
17861819
local.push(LocalFingerprint::RerunIfChanged { output, paths });
17871820
}
1788-
1821+
let env_config_insensitive = Arc::new(OnceCell::new());
17891822
local.extend(
17901823
deps.rerun_if_env_changed
17911824
.iter()
1792-
.map(LocalFingerprint::from_env),
1825+
.map(|s| LocalFingerprint::from_env(s, env_config, &env_config_insensitive)),
17931826
);
17941827

17951828
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)