diff --git a/src/cargo/core/compiler/artifact.rs b/src/cargo/core/compiler/artifact.rs index 42d979eed1b..d6b9fc105b3 100644 --- a/src/cargo/core/compiler/artifact.rs +++ b/src/cargo/core/compiler/artifact.rs @@ -12,9 +12,40 @@ use std::ffi::OsString; /// if artifacts are present. pub fn get_env( build_runner: &BuildRunner<'_, '_>, + unit: &Unit, dependencies: &[UnitDep], ) -> CargoResult> { let mut env = HashMap::new(); + + // Add `CARGO_BIN_EXE_` environment variables for building tests. + // + // These aren't built for `cargo check`, so can't use `dependencies` + if unit.target.is_test() || unit.target.is_bench() { + for bin_target in unit + .pkg + .manifest() + .targets() + .iter() + .filter(|target| target.is_bin()) + { + let name = bin_target + .binary_filename() + .unwrap_or_else(|| bin_target.name().to_string()); + + // For `cargo check` builds we do not uplift the CARGO_BIN_EXE_ artifacts to the + // artifact-dir. We do not want to provide a path to a non-existent binary but we still + // need to provide *something* so `env!("CARGO_BIN_EXE_...")` macros will compile. + let exe_path = build_runner + .files() + .bin_link_for_target(bin_target, unit.kind, build_runner.bcx)? + .map(|path| path.as_os_str().to_os_string()) + .unwrap_or_else(|| OsString::from(format!("placeholder:{name}"))); + + let key = format!("CARGO_BIN_EXE_{name}"); + env.insert(key, exe_path); + } + } + for unit_dep in dependencies.iter().filter(|d| d.unit.artifact.is_true()) { for artifact_path in build_runner .outputs(&unit_dep.unit)? diff --git a/src/cargo/core/compiler/build_runner/mod.rs b/src/cargo/core/compiler/build_runner/mod.rs index 35a7aff8f07..caa10f0af4a 100644 --- a/src/cargo/core/compiler/build_runner/mod.rs +++ b/src/cargo/core/compiler/build_runner/mod.rs @@ -284,7 +284,7 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> { unstable_opts, linker: self.compilation.target_linker(unit.kind).clone(), script_metas, - env: artifact::get_env(&self, self.unit_deps(unit))?, + env: artifact::get_env(&self, unit, self.unit_deps(unit))?, }); } @@ -321,17 +321,17 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> { if unit.mode == CompileMode::Test { self.compilation .tests - .push(self.unit_output(unit, &output.path)); + .push(self.unit_output(unit, &output.path)?); } else if unit.target.is_executable() { self.compilation .binaries - .push(self.unit_output(unit, bindst)); + .push(self.unit_output(unit, bindst)?); } else if unit.target.is_cdylib() && !self.compilation.cdylibs.iter().any(|uo| uo.unit == *unit) { self.compilation .cdylibs - .push(self.unit_output(unit, bindst)); + .push(self.unit_output(unit, bindst)?); } } Ok(()) @@ -561,13 +561,15 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> { /// Returns a [`UnitOutput`] which represents some information about the /// output of a unit. - pub fn unit_output(&self, unit: &Unit, path: &Path) -> UnitOutput { + pub fn unit_output(&self, unit: &Unit, path: &Path) -> CargoResult { let script_metas = self.find_build_script_metadatas(unit); - UnitOutput { + let env = artifact::get_env(&self, unit, self.unit_deps(unit))?; + Ok(UnitOutput { unit: unit.clone(), path: path.to_path_buf(), script_metas, - } + env, + }) } /// Check if any output file name collision happens. diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index dff2d00ba80..24431fe979b 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -53,7 +53,6 @@ pub struct Doctest { } /// Information about the output of a unit. -#[derive(Ord, PartialOrd, Eq, PartialEq)] pub struct UnitOutput { /// The unit that generated this output. pub unit: Unit, @@ -63,6 +62,9 @@ pub struct UnitOutput { /// /// This is used for indexing [`Compilation::extra_env`]. pub script_metas: Option>, + + /// Environment variables to set in the unit's process. + pub env: HashMap, } /// A structure returning the result of a compilation. diff --git a/src/cargo/core/compiler/custom_build.rs b/src/cargo/core/compiler/custom_build.rs index d05201d6c5b..851b5101891 100644 --- a/src/cargo/core/compiler/custom_build.rs +++ b/src/cargo/core/compiler/custom_build.rs @@ -377,7 +377,7 @@ fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResul .inherit_jobserver(&build_runner.jobserver); // Find all artifact dependencies and make their file and containing directory discoverable using environment variables. - for (var, value) in artifact::get_env(build_runner, dependencies)? { + for (var, value) in artifact::get_env(build_runner, unit, dependencies)? { cmd.env(&var, value); } diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 7695a526f42..1dbc52bd5bc 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -1478,31 +1478,6 @@ fn build_base_args( .env("RUSTC_BOOTSTRAP", "1"); } - // Add `CARGO_BIN_EXE_` environment variables for building tests. - if unit.target.is_test() || unit.target.is_bench() { - for bin_target in unit - .pkg - .manifest() - .targets() - .iter() - .filter(|target| target.is_bin()) - { - // For `cargo check` builds we do not uplift the CARGO_BIN_EXE_ artifacts to the - // artifact-dir. We do not want to provide a path to a non-existent binary but we still - // need to provide *something* so `env!("CARGO_BIN_EXE_...")` macros will compile. - let exe_path = build_runner - .files() - .bin_link_for_target(bin_target, unit.kind, build_runner.bcx)? - .map(|path| path.as_os_str().to_os_string()) - .unwrap_or_else(|| OsString::from(format!("placeholder:{}", bin_target.name()))); - - let name = bin_target - .binary_filename() - .unwrap_or(bin_target.name().to_string()); - let key = format!("CARGO_BIN_EXE_{}", name); - cmd.env(&key, exe_path); - } - } Ok(()) } @@ -1806,7 +1781,7 @@ fn build_deps_args( cmd.arg(arg); } - for (var, env) in artifact::get_env(build_runner, deps)? { + for (var, env) in artifact::get_env(build_runner, unit, deps)? { cmd.env(&var, env); } diff --git a/src/cargo/ops/cargo_run.rs b/src/cargo/ops/cargo_run.rs index b7a56a8a0b0..240865ec5e5 100644 --- a/src/cargo/ops/cargo_run.rs +++ b/src/cargo/ops/cargo_run.rs @@ -97,6 +97,7 @@ pub fn run( unit, path, script_metas, + env: _env, } = &compile.binaries[0]; let exe = match path.strip_prefix(gctx.cwd()) { Ok(path) if path.file_name() == Some(path.as_os_str()) => Path::new(".").join(path), diff --git a/src/cargo/ops/cargo_test.rs b/src/cargo/ops/cargo_test.rs index 0e3e309c3b6..9ea6231ef56 100644 --- a/src/cargo/ops/cargo_test.rs +++ b/src/cargo/ops/cargo_test.rs @@ -8,6 +8,7 @@ use crate::util::errors::CargoResult; use crate::util::{CliError, CliResult, GlobalContext, add_path_args}; use anyhow::format_err; use cargo_util::{ProcessBuilder, ProcessError}; +use std::collections::HashMap; use std::ffi::OsString; use std::fmt::Write; use std::path::{Path, PathBuf}; @@ -103,7 +104,7 @@ pub fn run_benches(ws: &Workspace<'_>, options: &TestOptions, args: &[&str]) -> fn compile_tests<'a>(ws: &Workspace<'a>, options: &TestOptions) -> CargoResult> { let mut compilation = ops::compile(ws, &options.compile_opts)?; - compilation.tests.sort(); + compilation.tests.sort_by_key(|u| u.unit.clone()); Ok(compilation) } @@ -126,6 +127,7 @@ fn run_unit_tests( unit, path, script_metas, + env, } in compilation.tests.iter() { let (exe_display, mut cmd) = cmd_builds( @@ -134,6 +136,7 @@ fn run_unit_tests( unit, path, script_metas.as_ref(), + env, test_args, compilation, "unittests", @@ -287,6 +290,7 @@ fn display_no_run_information( unit, path, script_metas, + env, } in compilation.tests.iter() { let (exe_display, cmd) = cmd_builds( @@ -295,6 +299,7 @@ fn display_no_run_information( unit, path, script_metas.as_ref(), + env, test_args, compilation, exec_type, @@ -319,6 +324,7 @@ fn cmd_builds( unit: &Unit, path: &PathBuf, script_metas: Option<&Vec>, + env: &HashMap, test_args: &[&str], compilation: &Compilation<'_>, exec_type: &str, @@ -348,6 +354,9 @@ fn cmd_builds( if unit.target.harness() && gctx.shell().verbosity() == Verbosity::Quiet { cmd.arg("--quiet"); } + for (key, val) in env.iter() { + cmd.env(key, val); + } Ok((exe_display, cmd)) } diff --git a/src/doc/man/generated_txt/cargo-bench.txt b/src/doc/man/generated_txt/cargo-bench.txt index 8be0ed97f72..f1fa449452a 100644 --- a/src/doc/man/generated_txt/cargo-bench.txt +++ b/src/doc/man/generated_txt/cargo-bench.txt @@ -138,8 +138,9 @@ OPTIONS test to execute the binary to exercise and test its behavior. The CARGO_BIN_EXE_ environment variable - is set when the integration test is built so that it can use the env - macro to locate the + is set when the integration test is built and run so that it can use the + env macro or the var + function to locate the executable. Passing target selection flags will benchmark only the specified diff --git a/src/doc/man/generated_txt/cargo-build.txt b/src/doc/man/generated_txt/cargo-build.txt index 57983b8c109..acce0baf363 100644 --- a/src/doc/man/generated_txt/cargo-build.txt +++ b/src/doc/man/generated_txt/cargo-build.txt @@ -56,8 +56,9 @@ OPTIONS execute the binary to exercise and test its behavior. The CARGO_BIN_EXE_ environment variable - is set when the integration test is built so that it can use the env - macro to locate the + is set when the integration test is built and run so that it can use the + env macro or the var + function to locate the executable. Passing target selection flags will build only the specified targets. diff --git a/src/doc/man/generated_txt/cargo-rustc.txt b/src/doc/man/generated_txt/cargo-rustc.txt index bdb03f6eda2..6f7dad5b0a1 100644 --- a/src/doc/man/generated_txt/cargo-rustc.txt +++ b/src/doc/man/generated_txt/cargo-rustc.txt @@ -47,8 +47,9 @@ OPTIONS execute the binary to exercise and test its behavior. The CARGO_BIN_EXE_ environment variable - is set when the integration test is built so that it can use the env - macro to locate the + is set when the integration test is built and run so that it can use the + env macro or the var + function to locate the executable. Passing target selection flags will build only the specified targets. diff --git a/src/doc/man/generated_txt/cargo-test.txt b/src/doc/man/generated_txt/cargo-test.txt index a40ffe1c010..ae3a6b66b45 100644 --- a/src/doc/man/generated_txt/cargo-test.txt +++ b/src/doc/man/generated_txt/cargo-test.txt @@ -157,8 +157,9 @@ OPTIONS execute the binary to exercise and test its behavior. The CARGO_BIN_EXE_ environment variable - is set when the integration test is built so that it can use the env - macro to locate the + is set when the integration test is built and run so that it can use the + env macro or the var + function to locate the executable. Passing target selection flags will test only the specified targets. diff --git a/src/doc/man/includes/options-targets-bin-auto-built.md b/src/doc/man/includes/options-targets-bin-auto-built.md index c2234ab79f9..57146427b3e 100644 --- a/src/doc/man/includes/options-targets-bin-auto-built.md +++ b/src/doc/man/includes/options-targets-bin-auto-built.md @@ -3,6 +3,7 @@ benchmark being selected to {{lower actionverb}}. This allows an integration test to execute the binary to exercise and test its behavior. The `CARGO_BIN_EXE_` [environment variable](../reference/environment-variables.html#environment-variables-cargo-sets-for-crates) -is set when the integration test is built so that it can use the -[`env` macro](https://doc.rust-lang.org/std/macro.env.html) to locate the +is set when the integration test is built and run so that it can use the +[`env` macro](https://doc.rust-lang.org/std/macro.env.html) or the +[`var` function](https://doc.rust-lang.org/std/env/fn.var.html) to locate the executable. diff --git a/src/doc/src/commands/cargo-bench.md b/src/doc/src/commands/cargo-bench.md index cab06a0b74c..63eedeb5e68 100644 --- a/src/doc/src/commands/cargo-bench.md +++ b/src/doc/src/commands/cargo-bench.md @@ -157,8 +157,9 @@ benchmark being selected to benchmark. This allows an integration test to execute the binary to exercise and test its behavior. The `CARGO_BIN_EXE_` [environment variable](../reference/environment-variables.html#environment-variables-cargo-sets-for-crates) -is set when the integration test is built so that it can use the -[`env` macro](https://doc.rust-lang.org/std/macro.env.html) to locate the +is set when the integration test is built and run so that it can use the +[`env` macro](https://doc.rust-lang.org/std/macro.env.html) or the +[`var` function](https://doc.rust-lang.org/std/env/fn.var.html) to locate the executable. Passing target selection flags will benchmark only the specified diff --git a/src/doc/src/commands/cargo-build.md b/src/doc/src/commands/cargo-build.md index f47254d7b1d..83eee7cab04 100644 --- a/src/doc/src/commands/cargo-build.md +++ b/src/doc/src/commands/cargo-build.md @@ -70,8 +70,9 @@ benchmark being selected to build. This allows an integration test to execute the binary to exercise and test its behavior. The `CARGO_BIN_EXE_` [environment variable](../reference/environment-variables.html#environment-variables-cargo-sets-for-crates) -is set when the integration test is built so that it can use the -[`env` macro](https://doc.rust-lang.org/std/macro.env.html) to locate the +is set when the integration test is built and run so that it can use the +[`env` macro](https://doc.rust-lang.org/std/macro.env.html) or the +[`var` function](https://doc.rust-lang.org/std/env/fn.var.html) to locate the executable. Passing target selection flags will build only the specified diff --git a/src/doc/src/commands/cargo-rustc.md b/src/doc/src/commands/cargo-rustc.md index 6c81459a264..c009704f885 100644 --- a/src/doc/src/commands/cargo-rustc.md +++ b/src/doc/src/commands/cargo-rustc.md @@ -56,8 +56,9 @@ benchmark being selected to build. This allows an integration test to execute the binary to exercise and test its behavior. The `CARGO_BIN_EXE_` [environment variable](../reference/environment-variables.html#environment-variables-cargo-sets-for-crates) -is set when the integration test is built so that it can use the -[`env` macro](https://doc.rust-lang.org/std/macro.env.html) to locate the +is set when the integration test is built and run so that it can use the +[`env` macro](https://doc.rust-lang.org/std/macro.env.html) or the +[`var` function](https://doc.rust-lang.org/std/env/fn.var.html) to locate the executable. Passing target selection flags will build only the specified diff --git a/src/doc/src/commands/cargo-test.md b/src/doc/src/commands/cargo-test.md index bcf2d31bf04..881f9230dd5 100644 --- a/src/doc/src/commands/cargo-test.md +++ b/src/doc/src/commands/cargo-test.md @@ -170,8 +170,9 @@ benchmark being selected to test. This allows an integration test to execute the binary to exercise and test its behavior. The `CARGO_BIN_EXE_` [environment variable](../reference/environment-variables.html#environment-variables-cargo-sets-for-crates) -is set when the integration test is built so that it can use the -[`env` macro](https://doc.rust-lang.org/std/macro.env.html) to locate the +is set when the integration test is built and run so that it can use the +[`env` macro](https://doc.rust-lang.org/std/macro.env.html) or the +[`var` function](https://doc.rust-lang.org/std/env/fn.var.html) to locate the executable. Passing target selection flags will test only the specified diff --git a/src/doc/src/reference/cargo-targets.md b/src/doc/src/reference/cargo-targets.md index 160988ca9f0..47021a1ce3b 100644 --- a/src/doc/src/reference/cargo-targets.md +++ b/src/doc/src/reference/cargo-targets.md @@ -134,11 +134,12 @@ annotated functions and run them in parallel. You can pass module names to Binary targets are automatically built if there is an integration test. This allows an integration test to execute the binary to exercise and test its behavior. The `CARGO_BIN_EXE_` [environment variable] is set when the -integration test is built so that it can use the [`env` macro] to locate the -executable. +integration test is built and run so that it can use the [`env` macro] or [`var` function] +to locate the executable. [environment variable]: environment-variables.md#environment-variables-cargo-sets-for-crates [`env` macro]: ../../std/macro.env.html +[`var` function]: ../../std/env/fn.var.html ## Benchmarks diff --git a/src/doc/src/reference/environment-variables.md b/src/doc/src/reference/environment-variables.md index 31764dd0ce1..e2da513d490 100644 --- a/src/doc/src/reference/environment-variables.md +++ b/src/doc/src/reference/environment-variables.md @@ -430,6 +430,23 @@ let out_dir = env::var("OUT_DIR").unwrap(); [`debug`]: profiles.md#debug [`opt-level`]: profiles.md#opt-level +## Environment variables Cargo sets for `cargo test` + +Cargo sets several environment variables when tests are run. +You can retrieve the values when the tests are run: + +```rust,ignore +use std::env; +let out_dir = env::var("CARGO_BIN_EXE_foo").unwrap(); +``` + +* `CARGO_BIN_EXE_` --- The absolute path to a binary target's executable. + This is only set when running an [integration test] or benchmark. + The `` is the name of the binary target, exactly as-is. For + example, `CARGO_BIN_EXE_my-program` for a binary named `my-program`. + Binaries are automatically built when the test is built, unless the binary + has required features that are not enabled. + ## Environment variables Cargo sets for 3rd party subcommands Cargo exposes this environment variable to 3rd party subcommands diff --git a/src/etc/man/cargo-bench.1 b/src/etc/man/cargo-bench.1 index 2b72a53a295..c447bb058e9 100644 --- a/src/etc/man/cargo-bench.1 +++ b/src/etc/man/cargo-bench.1 @@ -161,8 +161,9 @@ benchmark being selected to benchmark. This allows an integration test to execute the binary to exercise and test its behavior. The \fBCARGO_BIN_EXE_\fR \fIenvironment variable\fR -is set when the integration test is built so that it can use the -\fI\f(BIenv\fI macro\fR to locate the +is set when the integration test is built and run so that it can use the +\fI\f(BIenv\fI macro\fR or the +\fI\f(BIvar\fI function\fR to locate the executable. .sp Passing target selection flags will benchmark only the specified diff --git a/src/etc/man/cargo-build.1 b/src/etc/man/cargo-build.1 index 38bb7172c75..a256bcb6d42 100644 --- a/src/etc/man/cargo-build.1 +++ b/src/etc/man/cargo-build.1 @@ -60,8 +60,9 @@ benchmark being selected to build. This allows an integration test to execute the binary to exercise and test its behavior. The \fBCARGO_BIN_EXE_\fR \fIenvironment variable\fR -is set when the integration test is built so that it can use the -\fI\f(BIenv\fI macro\fR to locate the +is set when the integration test is built and run so that it can use the +\fI\f(BIenv\fI macro\fR or the +\fI\f(BIvar\fI function\fR to locate the executable. .sp Passing target selection flags will build only the specified diff --git a/src/etc/man/cargo-rustc.1 b/src/etc/man/cargo-rustc.1 index 5c43e3bdc48..6bcb3a5a4bb 100644 --- a/src/etc/man/cargo-rustc.1 +++ b/src/etc/man/cargo-rustc.1 @@ -46,8 +46,9 @@ benchmark being selected to build. This allows an integration test to execute the binary to exercise and test its behavior. The \fBCARGO_BIN_EXE_\fR \fIenvironment variable\fR -is set when the integration test is built so that it can use the -\fI\f(BIenv\fI macro\fR to locate the +is set when the integration test is built and run so that it can use the +\fI\f(BIenv\fI macro\fR or the +\fI\f(BIvar\fI function\fR to locate the executable. .sp Passing target selection flags will build only the specified diff --git a/src/etc/man/cargo-test.1 b/src/etc/man/cargo-test.1 index 58dd1fb6c5a..acc4b27b2b1 100644 --- a/src/etc/man/cargo-test.1 +++ b/src/etc/man/cargo-test.1 @@ -175,8 +175,9 @@ benchmark being selected to test. This allows an integration test to execute the binary to exercise and test its behavior. The \fBCARGO_BIN_EXE_\fR \fIenvironment variable\fR -is set when the integration test is built so that it can use the -\fI\f(BIenv\fI macro\fR to locate the +is set when the integration test is built and run so that it can use the +\fI\f(BIenv\fI macro\fR or the +\fI\f(BIvar\fI function\fR to locate the executable. .sp Passing target selection flags will test only the specified diff --git a/tests/testsuite/test.rs b/tests/testsuite/test.rs index dacff7631b7..77c244d6169 100644 --- a/tests/testsuite/test.rs +++ b/tests/testsuite/test.rs @@ -5205,6 +5205,29 @@ fn bin_env_for_test() { .file("src/bin/foo.rs", "fn main() {}") .file("src/bin/with-dash.rs", "fn main() {}") .file("src/bin/grussen.rs", "fn main() {}") + .file( + "src/lib.rs", + r#" + //! ``` + //! assert_eq!(option_env!("CARGO_BIN_EXE_foo"), None); + //! assert_eq!(option_env!("CARGO_BIN_EXE_with-dash"), None); + //! assert_eq!(option_env!("CARGO_BIN_EXE_grüßen"), None); + //! assert_eq!(std::env::var("CARGO_BIN_EXE_foo").ok(), None); + //! assert_eq!(std::env::var("CARGO_BIN_EXE_with-dash").ok(), None); + //! assert_eq!(std::env::var("CARGO_BIN_EXE_grüßen").ok(), None); + //! ``` + + #[test] + fn no_bins() { + assert_eq!(option_env!("CARGO_BIN_EXE_foo"), None); + assert_eq!(option_env!("CARGO_BIN_EXE_with-dash"), None); + assert_eq!(option_env!("CARGO_BIN_EXE_grüßen"), None); + assert_eq!(std::env::var("CARGO_BIN_EXE_foo").ok(), None); + assert_eq!(std::env::var("CARGO_BIN_EXE_with-dash").ok(), None); + assert_eq!(std::env::var("CARGO_BIN_EXE_grüßen").ok(), None); + } +"#, + ) .build(); let bin_path = |name| p.bin(name).to_string_lossy().replace("\\", "\\\\"); @@ -5216,6 +5239,9 @@ fn bin_env_for_test() { assert_eq!(env!("CARGO_BIN_EXE_foo"), ""); assert_eq!(env!("CARGO_BIN_EXE_with-dash"), ""); assert_eq!(env!("CARGO_BIN_EXE_grüßen"), ""); + assert_eq!(std::env::var("CARGO_BIN_EXE_foo").ok().as_deref(), Some("")); + assert_eq!(std::env::var("CARGO_BIN_EXE_with-dash").ok().as_deref(), Some("")); + assert_eq!(std::env::var("CARGO_BIN_EXE_grüßen").ok().as_deref(), Some("")); } "# .replace("", &bin_path("foo")) @@ -5223,8 +5249,8 @@ fn bin_env_for_test() { .replace("", &bin_path("grüßen")), ); - p.cargo("test --test check_env").run(); - p.cargo("check --test check_env").run(); + p.cargo("test").run(); + p.cargo("check --all-targets").run(); } #[cargo_test]