Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for [env] section in .cargo/config.toml #9175

Merged
merged 4 commits into from
Feb 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/cargo/core/compiler/compilation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,16 @@ impl<'cfg> Compilation<'cfg> {
)
.env("CARGO_PKG_AUTHORS", &pkg.authors().join(":"))
.cwd(pkg.root());

if self.config.cli_unstable().configurable_env {
// Apply any environment variables from the config
for (key, value) in self.config.env_config()?.iter() {
if value.is_force() || cmd.get_env(&key).is_none() {
cmd.env(&key, value.resolve(&self.config));
}
}
}

Ok(cmd)
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/cargo/core/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ pub struct CliUnstable {
pub weak_dep_features: bool,
pub extra_link_arg: bool,
pub credential_process: bool,
pub configurable_env: bool,
}

const STABILIZED_COMPILE_PROGRESS: &str = "The progress bar is now always \
Expand Down Expand Up @@ -598,6 +599,7 @@ impl CliUnstable {
"doctest-xcompile" => self.doctest_xcompile = parse_empty(k, v)?,
"panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,
"jobserver-per-rustc" => self.jobserver_per_rustc = parse_empty(k, v)?,
"configurable-env" => self.configurable_env = parse_empty(k, v)?,
"features" => {
// For now this is still allowed (there are still some
// unstable options like "compare"). This should be removed at
Expand Down
57 changes: 57 additions & 0 deletions src/cargo/util/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@
//! translate from `ConfigValue` and environment variables to the caller's
//! desired type.

use std::borrow::Cow;
use std::cell::{RefCell, RefMut};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::{HashMap, HashSet};
use std::env;
use std::ffi::OsStr;
use std::fmt;
use std::fs::{self, File};
use std::io::prelude::*;
Expand Down Expand Up @@ -181,6 +183,7 @@ pub struct Config {
target_cfgs: LazyCell<Vec<(String, TargetCfgConfig)>>,
doc_extern_map: LazyCell<RustdocExternMap>,
progress_config: ProgressConfig,
env_config: LazyCell<EnvConfig>,
}

impl Config {
Expand Down Expand Up @@ -264,6 +267,7 @@ impl Config {
target_cfgs: LazyCell::new(),
doc_extern_map: LazyCell::new(),
progress_config: ProgressConfig::default(),
env_config: LazyCell::new(),
}
}

Expand Down Expand Up @@ -1244,6 +1248,11 @@ impl Config {
&self.progress_config
}

pub fn env_config(&self) -> CargoResult<&EnvConfig> {
self.env_config
.try_borrow_with(|| self.get::<EnvConfig>("env"))
}

/// This is used to validate the `term` table has valid syntax.
///
/// This is necessary because loading the term settings happens very
Expand Down Expand Up @@ -1953,6 +1962,54 @@ where
deserializer.deserialize_option(ProgressVisitor)
}

#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum EnvConfigValueInner {
Simple(String),
WithOptions {
value: String,
#[serde(default)]
force: bool,
#[serde(default)]
relative: bool,
},
}

#[derive(Debug, Deserialize)]
#[serde(transparent)]
pub struct EnvConfigValue {
inner: Value<EnvConfigValueInner>,
}

impl EnvConfigValue {
pub fn is_force(&self) -> bool {
match self.inner.val {
EnvConfigValueInner::Simple(_) => false,
EnvConfigValueInner::WithOptions { force, .. } => force,
}
}

pub fn resolve<'a>(&'a self, config: &Config) -> Cow<'a, OsStr> {
match self.inner.val {
EnvConfigValueInner::Simple(ref s) => Cow::Borrowed(OsStr::new(s.as_str())),
EnvConfigValueInner::WithOptions {
ref value,
relative,
..
} => {
if relative {
let p = self.inner.definition.root(config).join(&value);
Cow::Owned(p.into_os_string())
} else {
Cow::Borrowed(OsStr::new(value.as_str()))
}
}
}
}
}

pub type EnvConfig = HashMap<String, EnvConfigValue>;

/// A type to deserialize a list of strings from a toml file.
///
/// Supports deserializing either a whitespace-separated list of arguments in a
Expand Down
133 changes: 133 additions & 0 deletions tests/testsuite/cargo_env_config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//! Tests for `[env]` config.

use cargo_test_support::{basic_bin_manifest, project};

#[cargo_test]
fn env_basic() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest("foo"))
.file(
"src/main.rs",
r#"
use std::env;
fn main() {
println!( "compile-time:{}", env!("ENV_TEST_1233") );
println!( "run-time:{}", env::var("ENV_TEST_1233").unwrap());
}
"#,
)
.file(
".cargo/config",
r#"
[env]
ENV_TEST_1233 = "Hello"
"#,
)
.build();

p.cargo("run -Zconfigurable-env")
.masquerade_as_nightly_cargo()
.with_stdout_contains("compile-time:Hello")
.with_stdout_contains("run-time:Hello")
.run();
}

#[cargo_test]
fn env_invalid() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest("foo"))
.file(
"src/main.rs",
r#"
fn main() {
}
"#,
)
.file(
".cargo/config",
r#"
[env]
ENV_TEST_BOOL = false
"#,
)
.build();

p.cargo("build -Zconfigurable-env")
.masquerade_as_nightly_cargo()
.with_status(101)
.with_stderr_contains("[..]could not load config key `env.ENV_TEST_BOOL`")
.run();
}

#[cargo_test]
fn env_force() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest("foo"))
.file(
"src/main.rs",
r#"
use std::env;
fn main() {
println!( "ENV_TEST_FORCED:{}", env!("ENV_TEST_FORCED") );
println!( "ENV_TEST_UNFORCED:{}", env!("ENV_TEST_UNFORCED") );
println!( "ENV_TEST_UNFORCED_DEFAULT:{}", env!("ENV_TEST_UNFORCED_DEFAULT") );
}
"#,
)
.file(
".cargo/config",
r#"
[env]
ENV_TEST_UNFORCED_DEFAULT = "from-config"
ENV_TEST_UNFORCED = { value = "from-config", force = false }
ENV_TEST_FORCED = { value = "from-config", force = true }
"#,
)
.build();

p.cargo("run -Zconfigurable-env")
.masquerade_as_nightly_cargo()
.env("ENV_TEST_FORCED", "from-env")
.env("ENV_TEST_UNFORCED", "from-env")
.env("ENV_TEST_UNFORCED_DEFAULT", "from-env")
.with_stdout_contains("ENV_TEST_FORCED:from-config")
.with_stdout_contains("ENV_TEST_UNFORCED:from-env")
.with_stdout_contains("ENV_TEST_UNFORCED_DEFAULT:from-env")
.run();
}

#[cargo_test]
fn env_relative() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest("foo2"))
.file(
"src/main.rs",
r#"
use std::env;
use std::path::Path;
fn main() {
println!( "ENV_TEST_REGULAR:{}", env!("ENV_TEST_REGULAR") );
println!( "ENV_TEST_REGULAR_DEFAULT:{}", env!("ENV_TEST_REGULAR_DEFAULT") );
println!( "ENV_TEST_RELATIVE:{}", env!("ENV_TEST_RELATIVE") );

assert!( Path::new(env!("ENV_TEST_RELATIVE")).is_absolute() );
assert!( !Path::new(env!("ENV_TEST_REGULAR")).is_absolute() );
assert!( !Path::new(env!("ENV_TEST_REGULAR_DEFAULT")).is_absolute() );
}
"#,
)
.file(
".cargo/config",
r#"
[env]
ENV_TEST_REGULAR = { value = "Cargo.toml", relative = false }
ENV_TEST_REGULAR_DEFAULT = "Cargo.toml"
ENV_TEST_RELATIVE = { value = "Cargo.toml", relative = true }
"#,
)
.build();

p.cargo("run -Zconfigurable-env")
.masquerade_as_nightly_cargo()
.run();
}
1 change: 1 addition & 0 deletions tests/testsuite/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ mod build_script_extra_link_arg;
mod cache_messages;
mod cargo_alias_config;
mod cargo_command;
mod cargo_env_config;
mod cargo_features;
mod cargo_targets;
mod cfg;
Expand Down