Skip to content

Commit f03698b

Browse files
committed
Auto merge of #8629 - EmbarkStudios:master, r=ehuss
Fix bug with PathAndArg config values This fixes an issue I noticed when trying to specify a target runner via the [`CARGO_TARGET_{triplet}_RUNNER`](https://doc.rust-lang.org/cargo/reference/config.html#targettriplerunner) environment variable, expecting it to override the value in our `.cargo/config.toml` file, which was giving quite strange errors until I figured out that cargo was actually merging the config file's values with the environment's values. This change adds a small hack to use and `UnmergedStringList` from `PathAndArgs` instead of just plain `StringList`, which uses the type in the deserializer to determine if `Config::get_list_or_string` should merge the values from the config file(s) with the environment as it was doing before, or else only use the environment to the exclusion of the config file(s) if the key was set in the environment. I also added a test for this to confirm both the bug and the fix.
2 parents e0dfa08 + ddc7090 commit f03698b

File tree

5 files changed

+66
-9
lines changed

5 files changed

+66
-9
lines changed

src/cargo/core/compiler/compilation.rs

+1
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ fn target_runner(
346346

347347
// try target.{}.runner
348348
let key = format!("target.{}.runner", target);
349+
349350
if let Some(v) = bcx.config.get::<Option<config::PathAndArgs>>(&key)? {
350351
let path = v.path.resolve_program(bcx.config);
351352
return Ok(Some((path, v.args)));

src/cargo/util/config/de.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ impl<'de, 'config> de::Deserializer<'de> for Deserializer<'config> {
5757
(None, Some(_)) => true,
5858
_ => false,
5959
};
60+
6061
if use_env {
6162
// Future note: If you ever need to deserialize a non-self describing
6263
// map type, this should implement a starts_with check (similar to how
@@ -187,13 +188,17 @@ impl<'de, 'config> de::Deserializer<'de> for Deserializer<'config> {
187188
where
188189
V: de::Visitor<'de>,
189190
{
190-
if name == "StringList" {
191-
let vals = self.config.get_list_or_string(&self.key)?;
192-
let vals: Vec<String> = vals.into_iter().map(|vd| vd.0).collect();
193-
visitor.visit_newtype_struct(vals.into_deserializer())
191+
let merge = if name == "StringList" {
192+
true
193+
} else if name == "UnmergedStringList" {
194+
false
194195
} else {
195-
visitor.visit_newtype_struct(self)
196-
}
196+
return visitor.visit_newtype_struct(self);
197+
};
198+
199+
let vals = self.config.get_list_or_string(&self.key, merge)?;
200+
let vals: Vec<String> = vals.into_iter().map(|vd| vd.0).collect();
201+
visitor.visit_newtype_struct(vals.into_deserializer())
197202
}
198203

199204
fn deserialize_enum<V>(

src/cargo/util/config/mod.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -587,8 +587,21 @@ impl Config {
587587
}
588588

589589
/// Helper for StringList type to get something that is a string or list.
590-
fn get_list_or_string(&self, key: &ConfigKey) -> CargoResult<Vec<(String, Definition)>> {
590+
fn get_list_or_string(
591+
&self,
592+
key: &ConfigKey,
593+
merge: bool,
594+
) -> CargoResult<Vec<(String, Definition)>> {
591595
let mut res = Vec::new();
596+
597+
if !merge {
598+
self.get_env_list(key, &mut res)?;
599+
600+
if !res.is_empty() {
601+
return Ok(res);
602+
}
603+
}
604+
592605
match self.get_cv(key)? {
593606
Some(CV::List(val, _def)) => res.extend(val),
594607
Some(CV::String(val, def)) => {
@@ -602,6 +615,7 @@ impl Config {
602615
}
603616

604617
self.get_env_list(key, &mut res)?;
618+
605619
Ok(res)
606620
}
607621

@@ -1766,6 +1780,14 @@ impl StringList {
17661780
}
17671781
}
17681782

1783+
/// StringList automatically merges config values with environment values,
1784+
/// this instead follows the precedence rules, so that eg. a string list found
1785+
/// in the environment will be used instead of one in a config file.
1786+
///
1787+
/// This is currently only used by `PathAndArgs`
1788+
#[derive(Debug, Deserialize)]
1789+
pub struct UnmergedStringList(Vec<String>);
1790+
17691791
#[macro_export]
17701792
macro_rules! __shell_print {
17711793
($config:expr, $which:ident, $newline:literal, $($arg:tt)*) => ({

src/cargo/util/config/path.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{Config, StringList, Value};
1+
use super::{Config, UnmergedStringList, Value};
22
use serde::{de::Error, Deserialize};
33
use std::path::PathBuf;
44

@@ -55,7 +55,7 @@ impl<'de> serde::Deserialize<'de> for PathAndArgs {
5555
where
5656
D: serde::Deserializer<'de>,
5757
{
58-
let vsl = Value::<StringList>::deserialize(deserializer)?;
58+
let vsl = Value::<UnmergedStringList>::deserialize(deserializer)?;
5959
let mut strings = vsl.val.0;
6060
if strings.is_empty() {
6161
return Err(D::Error::invalid_length(0, &"at least one element"));

tests/testsuite/tool_paths.rs

+29
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,35 @@ Caused by:
288288
.run();
289289
}
290290

291+
#[cargo_test]
292+
fn custom_runner_env_overrides_config() {
293+
let target = rustc_host();
294+
let p = project()
295+
.file("src/main.rs", "fn main() {}")
296+
.file(
297+
".cargo/config.toml",
298+
&format!(
299+
r#"
300+
[target.{}]
301+
runner = "should-not-run -r"
302+
"#,
303+
target
304+
),
305+
)
306+
.build();
307+
308+
let key = format!(
309+
"CARGO_TARGET_{}_RUNNER",
310+
target.to_uppercase().replace('-', "_")
311+
);
312+
313+
p.cargo("run")
314+
.env(&key, "should-run --foo")
315+
.with_status(101)
316+
.with_stderr_contains("[RUNNING] `should-run --foo target/debug/foo[EXE]`")
317+
.run();
318+
}
319+
291320
#[cargo_test]
292321
#[cfg(unix)] // Assumes `true` is in PATH.
293322
fn custom_runner_env_true() {

0 commit comments

Comments
 (0)