diff --git a/src/cargo/core/compiler/context/compilation_files.rs b/src/cargo/core/compiler/context/compilation_files.rs index 1f8286d06..ae885d067 100644 --- a/src/cargo/core/compiler/context/compilation_files.rs +++ b/src/cargo/core/compiler/context/compilation_files.rs @@ -10,7 +10,7 @@ use log::info; use super::{BuildContext, CompileKind, Context, FileFlavor, Layout}; use crate::core::compiler::{CompileMode, CompileTarget, Unit}; -use crate::core::{TargetKind, Workspace}; +use crate::core::{Target, TargetKind, Workspace}; use crate::util::{self, CargoResult}; /// The `Metadata` is a hash used to make unique file names for each unit in a build. @@ -241,6 +241,36 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> { } } + /// Returns the path to the executable binary for the given bin target. + /// + /// This should only to be used when a `Unit` is not available. + pub fn bin_link_for_target( + &self, + target: &Target, + kind: CompileKind, + bcx: &BuildContext<'_, '_>, + ) -> CargoResult { + assert!(target.is_bin()); + let dest = self.layout(kind).dest(); + let info = bcx.info(kind); + let file_types = info + .file_types( + "bin", + FileFlavor::Normal, + &TargetKind::Bin, + kind.short_name(bcx), + )? + .expect("target must support `bin`"); + + let file_type = file_types + .iter() + .filter(|file_type| file_type.flavor == FileFlavor::Normal) + .next() + .expect("target must support `bin`"); + + Ok(dest.join(file_type.filename(target.name()))) + } + /// Returns the filenames that the given unit will generate. pub(super) fn outputs( &self, diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 5052d790d..b763f5b31 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -876,6 +876,23 @@ fn build_base_args<'a, 'cfg>( cmd.arg("-Zforce-unstable-if-unmarked") .env("RUSTC_BOOTSTRAP", "1"); } + + // Add `CARGO_BIN_` 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()) + { + let exe_path = cx + .files() + .bin_link_for_target(bin_target, unit.kind, cx.bcx)?; + let key = format!("CARGO_BIN_EXE_{}", bin_target.crate_name().to_uppercase()); + cmd.env(&key, exe_path); + } + } Ok(()) } diff --git a/src/doc/src/reference/environment-variables.md b/src/doc/src/reference/environment-variables.md index 44b018b85..c99f708e2 100644 --- a/src/doc/src/reference/environment-variables.md +++ b/src/doc/src/reference/environment-variables.md @@ -187,6 +187,15 @@ let version = env!("CARGO_PKG_VERSION"); * `OUT_DIR` — If the package has a build script, this is set to the folder where the build script should place its output. See below for more information. (Only set during compilation.) +* `CARGO_BIN_EXE_` — The absolute path to a binary target's executable. + This is only set when building an [integration test] or benchmark. This may + be used with the [`env` macro] to find the executable to run for testing + purposes. The `` is the name of the binary converted to uppercase, and + `-` converted to `_`. Binaries are automatically built when the test is + built, unless the binary has required features that are not enabled. + +[integration test]: manifest.md#integration-tests +[`env` macro]: ../../std/macro.env.html #### Dynamic library paths diff --git a/tests/testsuite/test.rs b/tests/testsuite/test.rs index a092002fc..c685b51ce 100644 --- a/tests/testsuite/test.rs +++ b/tests/testsuite/test.rs @@ -3995,3 +3995,50 @@ fn panic_abort_test_profile_inherits() { .with_status(0) .run(); } + +#[cargo_test] +fn bin_env_for_test() { + // Test for the `CARGO_BIN_` environment variables for tests. + // + // Note: The Unicode binary uses a `[[bin]]` definition because different + // filesystems normalize utf-8 in different ways. For example, HFS uses + // "gru\u{308}ßen" and APFS uses "gr\u{fc}ßen". Defining it in TOML forces + // one form to be used. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [[bin]] + name = 'grüßen' + path = 'src/bin/grussen.rs' + "#, + ) + .file("src/bin/foo.rs", "fn main() {}") + .file("src/bin/with-dash.rs", "fn main() {}") + .file("src/bin/grussen.rs", "fn main() {}") + .build(); + + let bin_path = |name| p.bin(name).to_string_lossy().replace("\\", "\\\\"); + p.change_file( + "tests/check_env.rs", + &r#" + #[test] + fn run_bins() { + assert_eq!(env!("CARGO_BIN_EXE_FOO"), ""); + assert_eq!(env!("CARGO_BIN_EXE_WITH_DASH"), ""); + assert_eq!(env!("CARGO_BIN_EXE_GRÜSSEN"), ""); + } + "# + .replace("", &bin_path("foo")) + .replace("", &bin_path("with-dash")) + .replace("", &bin_path("grüßen")), + ); + + p.cargo("test --test check_env").run(); + p.cargo("check --test check_env").run(); +}