Skip to content

Commit

Permalink
Read configuration from environment variables
Browse files Browse the repository at this point in the history
This commit adds a more principled system to rationalize what ends up being a
configuration value versus an environment variable. This problem is solved by
just saying that they're one and the same! Similar to Bundler, this commit
supports overriding the `foo.bar` configuration value with the `CARGO_FOO_BAR`
environment variable.

Currently this is used as part of the `get_string` and `get_i64` methods on
`Config`. This means, for example, that the following environment variables can
now be used to configure Cargo:

* CARGO_BUILD_JOBS
* CARGO_HTTP_TIMEOUT
* CARGO_HTTP_PROXY

Currently it's not supported to encode a list in an environment variable, so for
example `CARGO_PATHS` would not be read when reading the global `paths`
configuration value.

cc rust-lang#2362
cc rust-lang#2395 -- intended to close this in tandem with rust-lang#2397
  • Loading branch information
alexcrichton committed Feb 19, 2016
1 parent 56db20d commit a40440c
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 4 deletions.
33 changes: 31 additions & 2 deletions src/cargo/util/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ use std::fs::{self, File};
use std::io::prelude::*;
use std::mem;
use std::path::{Path, PathBuf};
use std::str::FromStr;

use rustc_serialize::{Encodable,Encoder};
use toml;
use core::shell::{Verbosity, ColorConfig};
use core::{MultiShell, Package};
use util::{CargoResult, ChainError, Rustc, internal, human, paths};
use util::{CargoResult, CargoError, ChainError, Rustc, internal, human, paths};

use util::toml as cargo_toml;

Expand Down Expand Up @@ -148,7 +149,29 @@ impl Config {
Ok(Some(val.clone()))
}

fn get_env<V: FromStr>(&self, key: &str) -> CargoResult<Option<Value<V>>>
where Box<CargoError>: From<V::Err>
{
let key = key.replace(".", "_")
.replace("-", "_")
.chars()
.flat_map(|c| c.to_uppercase())
.collect::<String>();
match env::var(&format!("CARGO_{}", key)) {
Ok(value) => {
Ok(Some(Value {
val: try!(value.parse()),
definition: Definition::Environment,
}))
}
Err(..) => Ok(None),
}
}

pub fn get_string(&self, key: &str) -> CargoResult<Option<Value<String>>> {
if let Some(v) = try!(self.get_env(key)) {
return Ok(Some(v))
}
match try!(self.get(key)) {
Some(CV::String(i, path)) => {
Ok(Some(Value {
Expand Down Expand Up @@ -209,6 +232,9 @@ impl Config {
}

pub fn get_i64(&self, key: &str) -> CargoResult<Option<Value<i64>>> {
if let Some(v) = try!(self.get_env(key)) {
return Ok(Some(v))
}
match try!(self.get(key)) {
Some(CV::Integer(i, path)) => {
Ok(Some(Value {
Expand Down Expand Up @@ -311,6 +337,7 @@ pub struct Value<T> {

pub enum Definition {
Path(PathBuf),
Environment,
}

impl fmt::Debug for ConfigValue {
Expand Down Expand Up @@ -496,9 +523,10 @@ impl ConfigValue {
}

impl Definition {
pub fn root<'a>(&'a self, _config: &'a Config) -> &'a Path {
pub fn root<'a>(&'a self, config: &'a Config) -> &'a Path {
match *self {
Definition::Path(ref p) => p.parent().unwrap().parent().unwrap(),
Definition::Environment => config.cwd(),
}
}
}
Expand All @@ -507,6 +535,7 @@ impl fmt::Display for Definition {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Definition::Path(ref p) => p.display().fmt(f),
Definition::Environment => "the environment".fmt(f),
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/cargo/util/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ use std::error::Error;
use std::ffi;
use std::fmt;
use std::io;
use std::num;
use std::process::{Output, ExitStatus};
use std::str;
use std::string;

use curl;
use git2;
Expand Down Expand Up @@ -308,6 +310,13 @@ from_error! {
toml::DecodeError,
ffi::NulError,
term::Error,
num::ParseIntError,
}

impl From<string::ParseError> for Box<CargoError> {
fn from(t: string::ParseError) -> Box<CargoError> {
match t {}
}
}

impl<E: CargoError> From<Human<E>> for Box<CargoError> {
Expand All @@ -328,6 +337,7 @@ impl CargoError for toml::DecodeError {}
impl CargoError for url::ParseError {}
impl CargoError for ffi::NulError {}
impl CargoError for term::Error {}
impl CargoError for num::ParseIntError {}

// =============================================================================
// Construction helpers
Expand Down
12 changes: 10 additions & 2 deletions src/doc/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,16 @@ target-dir = "target" # path of where to place all generated artifacts

# Environment Variables

Cargo recognizes a few global [environment variables][env] to configure itself.
Settings specified via config files take precedence over those specified via
Cargo can also be configured through environment variables in addition to the
TOML syntax above. For each configuration key above of the form `foo.bar` the
environment variable `CARGO_FOO_BAR` can also be used to define the value. For
example the `build.jobs` key can also be defined by `CARGO_BUILD_JOBS`.

Environment variables will take precedent over TOML configuration, and currently
only integer, boolean, and string keys are supported to be defined by
environment variables.

In addition to the system above, Cargo recognizes a few other specific
[environment variables][env].

[env]: environment-variables.html
26 changes: 26 additions & 0 deletions tests/test_cargo_config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use support::{project, execs};
use hamcrest::assert_that;

fn setup() {
}

test!(read_env_vars_for_config {
let p = project("foo")
.file("Cargo.toml", r#"
[package]
name = "foo"
authors = []
version = "0.0.0"
build = "build.rs"
"#)
.file("src/lib.rs", "")
.file("build.rs", r#"
use std::env;
fn main() {
assert_eq!(env::var("NUM_JOBS").unwrap(), "100");
}
"#);

assert_that(p.cargo_process("build").env("CARGO_BUILD_JOBS", "100"),
execs().with_status(0));
});
1 change: 1 addition & 0 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ mod test_cargo_rustdoc;
mod test_cargo_search;
mod test_cargo_test;
mod test_cargo_tool_paths;
mod test_cargo_config;
mod test_cargo_verify_project;
mod test_cargo_version;
mod test_shell;
Expand Down

0 comments on commit a40440c

Please sign in to comment.