diff --git a/src/bin/cargo.rs b/src/bin/cargo.rs index 0d0171ad2e8..187db436fcb 100644 --- a/src/bin/cargo.rs +++ b/src/bin/cargo.rs @@ -320,7 +320,12 @@ fn execute_external_subcommand(config: &Config, cmd: &str, args: &[String]) -> C .into()) } }; - let err = match util::process(&command).args(&args[1..]).exec() { + + let cargo_exe = config.cargo_exe()?; + let err = match util::process(&command) + .env(cargo::CARGO_ENV, cargo_exe) + .args(&args[1..]) + .exec() { Ok(()) => return Ok(()), Err(e) => e, }; diff --git a/src/cargo/lib.rs b/src/cargo/lib.rs index 1d13a503323..019a7a19dff 100755 --- a/src/cargo/lib.rs +++ b/src/cargo/lib.rs @@ -41,6 +41,8 @@ use term::color::{BLACK}; pub use util::{CargoError, CargoResult, CliError, CliResult, human, Config, ChainError}; +pub const CARGO_ENV: &'static str = "CARGO"; + macro_rules! bail { ($($fmt:tt)*) => ( return Err(::util::human(&format_args!($($fmt)*))) diff --git a/src/cargo/ops/cargo_rustc/compilation.rs b/src/cargo/ops/cargo_rustc/compilation.rs index 60f62eada54..9f9b1c52ebb 100644 --- a/src/cargo/ops/cargo_rustc/compilation.rs +++ b/src/cargo/ops/cargo_rustc/compilation.rs @@ -143,6 +143,9 @@ impl<'cfg> Compilation<'cfg> { let metadata = pkg.manifest().metadata(); + let cargo_exe = self.config.cargo_exe()?; + cmd.env(::CARGO_ENV, cargo_exe); + cmd.env("CARGO_MANIFEST_DIR", pkg.root()) .env("CARGO_PKG_VERSION_MAJOR", &pkg.version().major.to_string()) .env("CARGO_PKG_VERSION_MINOR", &pkg.version().minor.to_string()) diff --git a/src/cargo/util/config.rs b/src/cargo/util/config.rs index 08af70451c8..a715306637c 100644 --- a/src/cargo/util/config.rs +++ b/src/cargo/util/config.rs @@ -28,6 +28,7 @@ pub struct Config { rustc: LazyCell, values: LazyCell>, cwd: PathBuf, + cargo_exe: LazyCell, rustdoc: LazyCell, extra_verbose: Cell, frozen: Cell, @@ -44,6 +45,7 @@ impl Config { rustc: LazyCell::new(), cwd: cwd, values: LazyCell::new(), + cargo_exe: LazyCell::new(), rustdoc: LazyCell::new(), extra_verbose: Cell::new(false), frozen: Cell::new(false), @@ -93,6 +95,15 @@ impl Config { self.rustc.get_or_try_init(|| Rustc::new(self.get_tool("rustc")?)) } + pub fn cargo_exe(&self) -> CargoResult<&Path> { + self.cargo_exe.get_or_try_init(|| + env::current_exe().and_then(|path| path.canonicalize()) + .chain_error(|| { + human("couldn't get the path to cargo executable") + }) + ).map(AsRef::as_ref) + } + pub fn values(&self) -> CargoResult<&HashMap> { self.values.get_or_try_init(|| self.load_values()) } diff --git a/src/doc/environment-variables.md b/src/doc/environment-variables.md index 7cb67b6c19e..97bdb38cb51 100644 --- a/src/doc/environment-variables.md +++ b/src/doc/environment-variables.md @@ -30,8 +30,9 @@ configuration values, as described in [that documentation][config-env] # Environment variables Cargo sets for crates -Cargo exposes these environment variables to your crate when it is compiled. To get the -value of any of these variables in a Rust program, do this: +Cargo exposes these environment variables to your crate when it is compiled. +Note that this applies for test binaries as well. +To get the value of any of these variables in a Rust program, do this: ``` let version = env!("CARGO_PKG_VERSION"); @@ -39,6 +40,7 @@ let version = env!("CARGO_PKG_VERSION"); `version` will now contain the value of `CARGO_PKG_VERSION`. +* `CARGO` - Path to the `cargo` binary performing the build. * `CARGO_MANIFEST_DIR` - The directory containing the manifest of your package. * `CARGO_PKG_VERSION` - The full version of your package. * `CARGO_PKG_VERSION_MAJOR` - The major version of your package. @@ -96,3 +98,10 @@ let out_dir = env::var("OUT_DIR").unwrap(); [links]: build-script.html#the-links-manifest-key [profile]: manifest.html#the-profile-sections [clang]:http://clang.llvm.org/docs/CrossCompilation.html#target-triple + +# Environment variables Cargo sets for 3rd party subcommands + +Cargo exposes this environment variable to 3rd party subcommands +(ie. programs named `cargo-foobar` placed in `$PATH`): + +* `CARGO` - Path to the `cargo` binary performing the build. diff --git a/tests/cargo.rs b/tests/cargo.rs index b8f87325ba4..43e76c6ec15 100644 --- a/tests/cargo.rs +++ b/tests/cargo.rs @@ -1,3 +1,4 @@ +extern crate cargo; extern crate cargotest; extern crate hamcrest; @@ -10,8 +11,8 @@ use std::str; use cargotest::cargo_process; use cargotest::support::paths::{self, CargoPathExt}; -use cargotest::support::{execs, project, ProjectBuilder}; -use hamcrest::{assert_that}; +use cargotest::support::{execs, project, ProjectBuilder, basic_bin_manifest}; +use hamcrest::{assert_that, existing_file}; #[cfg_attr(windows,allow(dead_code))] enum FakeKind<'a> { @@ -81,11 +82,11 @@ fn list_command_looks_at_path() { #[cfg(unix)] #[test] fn list_command_resolves_symlinks() { - use cargotest::support::cargo_dir; + use cargotest::support::cargo_exe; let proj = project("list-non-overlapping"); let proj = fake_file(proj, Path::new("path-test"), "cargo-2", - FakeKind::Symlink{target:&cargo_dir().join("cargo")}); + FakeKind::Symlink{target:&cargo_exe()}); let mut pr = cargo_process(); let mut path = path(); @@ -161,6 +162,37 @@ fn override_cargo_home() { assert!(contents.contains(r#"authors = ["foo "]"#)); } +#[test] +fn cargo_subcommand_env() { + use cargotest::support::cargo_exe; + + let src = format!(r#" + use std::env; + + fn main() {{ + println!("{{}}", env::var("{}").unwrap()); + }} + "#, cargo::CARGO_ENV); + + let p = project("cargo-envtest") + .file("Cargo.toml", &basic_bin_manifest("cargo-envtest")) + .file("src/main.rs", &src); + + let target_dir = p.target_debug_dir(); + + assert_that(p.cargo_process("build"), execs().with_status(0)); + assert_that(&p.bin("cargo-envtest"), existing_file()); + + let mut pr = cargo_process(); + let cargo = cargo_exe().canonicalize().unwrap(); + let mut path = path(); + path.push(target_dir); + let path = env::join_paths(path.iter()).unwrap(); + + assert_that(pr.arg("envtest").env("PATH", &path), + execs().with_status(0).with_stdout(cargo.to_str().unwrap())); +} + #[test] fn cargo_help() { assert_that(cargo_process(), diff --git a/tests/cargotest/lib.rs b/tests/cargotest/lib.rs index 11df25fdd13..c6a4dc4f48e 100644 --- a/tests/cargotest/lib.rs +++ b/tests/cargotest/lib.rs @@ -90,7 +90,7 @@ fn _process(t: &OsStr) -> cargo::util::ProcessBuilder { } pub fn cargo_process() -> cargo::util::ProcessBuilder { - process(&support::cargo_dir().join("cargo")) + process(&support::cargo_exe()) } pub fn sleep_ms(ms: u64) { diff --git a/tests/cargotest/support/mod.rs b/tests/cargotest/support/mod.rs index 88b5c396437..2e3e6d21c8c 100644 --- a/tests/cargotest/support/mod.rs +++ b/tests/cargotest/support/mod.rs @@ -165,7 +165,7 @@ impl ProjectBuilder { assert!(self.is_build.get(), "call `.build()` before calling `.cargo()`, \ or use `.cargo_process()`"); - let mut p = self.process(&cargo_dir().join("cargo")); + let mut p = self.process(&cargo_exe()); p.arg(cmd); return p; } @@ -317,6 +317,10 @@ pub fn cargo_dir() -> PathBuf { }) } +pub fn cargo_exe() -> PathBuf { + cargo_dir().join(format!("cargo{}", env::consts::EXE_SUFFIX)) +} + /// Returns an absolute path in the filesystem that `path` points to. The /// returned path does not contain any symlinks in its hierarchy. /* diff --git a/tests/init.rs b/tests/init.rs index c13d57d7340..47efa3b97c9 100644 --- a/tests/init.rs +++ b/tests/init.rs @@ -8,12 +8,12 @@ use std::io::prelude::*; use std::env; use cargo::util::ProcessBuilder; -use cargotest::support::{execs, paths, cargo_dir}; +use cargotest::support::{execs, paths, cargo_exe}; use hamcrest::{assert_that, existing_file, existing_dir, is_not}; use tempdir::TempDir; fn cargo_process(s: &str) -> ProcessBuilder { - let mut p = cargotest::process(&cargo_dir().join("cargo")); + let mut p = cargotest::process(&cargo_exe()); p.arg(s).cwd(&paths::root()).env("HOME", &paths::home()); p } diff --git a/tests/package.rs b/tests/package.rs index c40da6f5391..fab868061d5 100644 --- a/tests/package.rs +++ b/tests/package.rs @@ -11,7 +11,7 @@ use std::io::prelude::*; use std::path::{Path, PathBuf}; use cargotest::{cargo_process, process}; -use cargotest::support::{project, execs, paths, git, path2url, cargo_dir}; +use cargotest::support::{project, execs, paths, git, path2url, cargo_exe}; use flate2::read::GzDecoder; use hamcrest::{assert_that, existing_file, contains}; use tar::Archive; @@ -481,7 +481,7 @@ fn repackage_on_source_change() { "#).unwrap(); std::mem::drop(file); - let mut pro = process(&cargo_dir().join("cargo")); + let mut pro = process(&cargo_exe()); pro.arg("package").cwd(p.root()); // Check that cargo rebuilds the tarball diff --git a/tests/test.rs b/tests/test.rs index 6e3e5c4646c..2f0d3e6332e 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -7,7 +7,7 @@ use std::io::prelude::*; use std::str; use cargotest::{sleep_ms, is_nightly}; -use cargotest::support::{project, execs, basic_bin_manifest, basic_lib_manifest}; +use cargotest::support::{project, execs, basic_bin_manifest, basic_lib_manifest, cargo_exe}; use cargotest::support::paths::CargoPathExt; use cargotest::support::registry::Package; use hamcrest::{assert_that, existing_file, is_not}; @@ -2666,3 +2666,32 @@ fn doctest_and_registry() { assert_that(p.cargo_process("test").arg("--all").arg("-v"), execs().with_status(0)); } + +#[test] +fn cargo_test_env() { + let src = format!(r#" + #![crate_type = "rlib"] + + #[test] + fn env_test() {{ + use std::env; + println!("{{}}", env::var("{}").unwrap()); + }} + "#, cargo::CARGO_ENV); + + let p = project("env_test") + .file("Cargo.toml", &basic_lib_manifest("env_test")) + .file("src/lib.rs", &src); + + let mut pr = p.cargo_process("test"); + let cargo = cargo_exe().canonicalize().unwrap(); + assert_that(pr.args(&["--lib", "--", "--nocapture"]), + execs().with_status(0).with_stdout(format!(" +running 1 test +{} +test env_test ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured + +", cargo.to_str().unwrap()))); +}