Skip to content

Commit 4742e82

Browse files
committed
Auto merge of #9175 - wickerwaka:env-section, r=alexcrichton
Add support for [env] section in .cargo/config.toml This adds support for an `[env]` section in the config.toml files. Environment variables set in this section will be applied to the environment of any processes executed by cargo. This is implemented to follow the recommendations in #8839 (comment) Variables have optional `force` and `relative` flags. `force` means the variable can override an existing environment variable. `relative` means the variable represents a path relative to the location of the directory that contains the `.cargo/` directory that contains the `config.toml` file. A relative variable will have an absolute path prepended to it before setting it in the environment. ``` [env] FOOBAR = "Apple" PATH_TO_SOME_TOOL = { value = "bin/tool", relative = true } USERNAME = { value = "test_user", force = true } ``` Fixes #4121
2 parents 7442c14 + 05acf73 commit 4742e82

File tree

5 files changed

+203
-0
lines changed

5 files changed

+203
-0
lines changed

src/cargo/core/compiler/compilation.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,16 @@ impl<'cfg> Compilation<'cfg> {
339339
)
340340
.env("CARGO_PKG_AUTHORS", &pkg.authors().join(":"))
341341
.cwd(pkg.root());
342+
343+
if self.config.cli_unstable().configurable_env {
344+
// Apply any environment variables from the config
345+
for (key, value) in self.config.env_config()?.iter() {
346+
if value.is_force() || cmd.get_env(&key).is_none() {
347+
cmd.env(&key, value.resolve(&self.config));
348+
}
349+
}
350+
}
351+
342352
Ok(cmd)
343353
}
344354
}

src/cargo/core/features.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ pub struct CliUnstable {
444444
pub weak_dep_features: bool,
445445
pub extra_link_arg: bool,
446446
pub credential_process: bool,
447+
pub configurable_env: bool,
447448
}
448449

449450
const STABILIZED_COMPILE_PROGRESS: &str = "The progress bar is now always \
@@ -598,6 +599,7 @@ impl CliUnstable {
598599
"doctest-xcompile" => self.doctest_xcompile = parse_empty(k, v)?,
599600
"panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,
600601
"jobserver-per-rustc" => self.jobserver_per_rustc = parse_empty(k, v)?,
602+
"configurable-env" => self.configurable_env = parse_empty(k, v)?,
601603
"features" => {
602604
// For now this is still allowed (there are still some
603605
// unstable options like "compare"). This should be removed at

src/cargo/util/config/mod.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@
4949
//! translate from `ConfigValue` and environment variables to the caller's
5050
//! desired type.
5151
52+
use std::borrow::Cow;
5253
use std::cell::{RefCell, RefMut};
5354
use std::collections::hash_map::Entry::{Occupied, Vacant};
5455
use std::collections::{HashMap, HashSet};
5556
use std::env;
57+
use std::ffi::OsStr;
5658
use std::fmt;
5759
use std::fs::{self, File};
5860
use std::io::prelude::*;
@@ -181,6 +183,7 @@ pub struct Config {
181183
target_cfgs: LazyCell<Vec<(String, TargetCfgConfig)>>,
182184
doc_extern_map: LazyCell<RustdocExternMap>,
183185
progress_config: ProgressConfig,
186+
env_config: LazyCell<EnvConfig>,
184187
}
185188

186189
impl Config {
@@ -264,6 +267,7 @@ impl Config {
264267
target_cfgs: LazyCell::new(),
265268
doc_extern_map: LazyCell::new(),
266269
progress_config: ProgressConfig::default(),
270+
env_config: LazyCell::new(),
267271
}
268272
}
269273

@@ -1244,6 +1248,11 @@ impl Config {
12441248
&self.progress_config
12451249
}
12461250

1251+
pub fn env_config(&self) -> CargoResult<&EnvConfig> {
1252+
self.env_config
1253+
.try_borrow_with(|| self.get::<EnvConfig>("env"))
1254+
}
1255+
12471256
/// This is used to validate the `term` table has valid syntax.
12481257
///
12491258
/// This is necessary because loading the term settings happens very
@@ -1953,6 +1962,54 @@ where
19531962
deserializer.deserialize_option(ProgressVisitor)
19541963
}
19551964

1965+
#[derive(Debug, Deserialize)]
1966+
#[serde(untagged)]
1967+
enum EnvConfigValueInner {
1968+
Simple(String),
1969+
WithOptions {
1970+
value: String,
1971+
#[serde(default)]
1972+
force: bool,
1973+
#[serde(default)]
1974+
relative: bool,
1975+
},
1976+
}
1977+
1978+
#[derive(Debug, Deserialize)]
1979+
#[serde(transparent)]
1980+
pub struct EnvConfigValue {
1981+
inner: Value<EnvConfigValueInner>,
1982+
}
1983+
1984+
impl EnvConfigValue {
1985+
pub fn is_force(&self) -> bool {
1986+
match self.inner.val {
1987+
EnvConfigValueInner::Simple(_) => false,
1988+
EnvConfigValueInner::WithOptions { force, .. } => force,
1989+
}
1990+
}
1991+
1992+
pub fn resolve<'a>(&'a self, config: &Config) -> Cow<'a, OsStr> {
1993+
match self.inner.val {
1994+
EnvConfigValueInner::Simple(ref s) => Cow::Borrowed(OsStr::new(s.as_str())),
1995+
EnvConfigValueInner::WithOptions {
1996+
ref value,
1997+
relative,
1998+
..
1999+
} => {
2000+
if relative {
2001+
let p = self.inner.definition.root(config).join(&value);
2002+
Cow::Owned(p.into_os_string())
2003+
} else {
2004+
Cow::Borrowed(OsStr::new(value.as_str()))
2005+
}
2006+
}
2007+
}
2008+
}
2009+
}
2010+
2011+
pub type EnvConfig = HashMap<String, EnvConfigValue>;
2012+
19562013
/// A type to deserialize a list of strings from a toml file.
19572014
///
19582015
/// Supports deserializing either a whitespace-separated list of arguments in a

tests/testsuite/cargo_env_config.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
//! Tests for `[env]` config.
2+
3+
use cargo_test_support::{basic_bin_manifest, project};
4+
5+
#[cargo_test]
6+
fn env_basic() {
7+
let p = project()
8+
.file("Cargo.toml", &basic_bin_manifest("foo"))
9+
.file(
10+
"src/main.rs",
11+
r#"
12+
use std::env;
13+
fn main() {
14+
println!( "compile-time:{}", env!("ENV_TEST_1233") );
15+
println!( "run-time:{}", env::var("ENV_TEST_1233").unwrap());
16+
}
17+
"#,
18+
)
19+
.file(
20+
".cargo/config",
21+
r#"
22+
[env]
23+
ENV_TEST_1233 = "Hello"
24+
"#,
25+
)
26+
.build();
27+
28+
p.cargo("run -Zconfigurable-env")
29+
.masquerade_as_nightly_cargo()
30+
.with_stdout_contains("compile-time:Hello")
31+
.with_stdout_contains("run-time:Hello")
32+
.run();
33+
}
34+
35+
#[cargo_test]
36+
fn env_invalid() {
37+
let p = project()
38+
.file("Cargo.toml", &basic_bin_manifest("foo"))
39+
.file(
40+
"src/main.rs",
41+
r#"
42+
fn main() {
43+
}
44+
"#,
45+
)
46+
.file(
47+
".cargo/config",
48+
r#"
49+
[env]
50+
ENV_TEST_BOOL = false
51+
"#,
52+
)
53+
.build();
54+
55+
p.cargo("build -Zconfigurable-env")
56+
.masquerade_as_nightly_cargo()
57+
.with_status(101)
58+
.with_stderr_contains("[..]could not load config key `env.ENV_TEST_BOOL`")
59+
.run();
60+
}
61+
62+
#[cargo_test]
63+
fn env_force() {
64+
let p = project()
65+
.file("Cargo.toml", &basic_bin_manifest("foo"))
66+
.file(
67+
"src/main.rs",
68+
r#"
69+
use std::env;
70+
fn main() {
71+
println!( "ENV_TEST_FORCED:{}", env!("ENV_TEST_FORCED") );
72+
println!( "ENV_TEST_UNFORCED:{}", env!("ENV_TEST_UNFORCED") );
73+
println!( "ENV_TEST_UNFORCED_DEFAULT:{}", env!("ENV_TEST_UNFORCED_DEFAULT") );
74+
}
75+
"#,
76+
)
77+
.file(
78+
".cargo/config",
79+
r#"
80+
[env]
81+
ENV_TEST_UNFORCED_DEFAULT = "from-config"
82+
ENV_TEST_UNFORCED = { value = "from-config", force = false }
83+
ENV_TEST_FORCED = { value = "from-config", force = true }
84+
"#,
85+
)
86+
.build();
87+
88+
p.cargo("run -Zconfigurable-env")
89+
.masquerade_as_nightly_cargo()
90+
.env("ENV_TEST_FORCED", "from-env")
91+
.env("ENV_TEST_UNFORCED", "from-env")
92+
.env("ENV_TEST_UNFORCED_DEFAULT", "from-env")
93+
.with_stdout_contains("ENV_TEST_FORCED:from-config")
94+
.with_stdout_contains("ENV_TEST_UNFORCED:from-env")
95+
.with_stdout_contains("ENV_TEST_UNFORCED_DEFAULT:from-env")
96+
.run();
97+
}
98+
99+
#[cargo_test]
100+
fn env_relative() {
101+
let p = project()
102+
.file("Cargo.toml", &basic_bin_manifest("foo2"))
103+
.file(
104+
"src/main.rs",
105+
r#"
106+
use std::env;
107+
use std::path::Path;
108+
fn main() {
109+
println!( "ENV_TEST_REGULAR:{}", env!("ENV_TEST_REGULAR") );
110+
println!( "ENV_TEST_REGULAR_DEFAULT:{}", env!("ENV_TEST_REGULAR_DEFAULT") );
111+
println!( "ENV_TEST_RELATIVE:{}", env!("ENV_TEST_RELATIVE") );
112+
113+
assert!( Path::new(env!("ENV_TEST_RELATIVE")).is_absolute() );
114+
assert!( !Path::new(env!("ENV_TEST_REGULAR")).is_absolute() );
115+
assert!( !Path::new(env!("ENV_TEST_REGULAR_DEFAULT")).is_absolute() );
116+
}
117+
"#,
118+
)
119+
.file(
120+
".cargo/config",
121+
r#"
122+
[env]
123+
ENV_TEST_REGULAR = { value = "Cargo.toml", relative = false }
124+
ENV_TEST_REGULAR_DEFAULT = "Cargo.toml"
125+
ENV_TEST_RELATIVE = { value = "Cargo.toml", relative = true }
126+
"#,
127+
)
128+
.build();
129+
130+
p.cargo("run -Zconfigurable-env")
131+
.masquerade_as_nightly_cargo()
132+
.run();
133+
}

tests/testsuite/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ mod build_script_extra_link_arg;
2424
mod cache_messages;
2525
mod cargo_alias_config;
2626
mod cargo_command;
27+
mod cargo_env_config;
2728
mod cargo_features;
2829
mod cargo_targets;
2930
mod cfg;

0 commit comments

Comments
 (0)