diff --git a/docs/defs.md b/docs/defs.md
index 3d0136a60c..5f5a0df19a 100644
--- a/docs/defs.md
+++ b/docs/defs.md
@@ -550,7 +550,7 @@ Run the test with `bazel build //hello_lib:hello_lib_test`.
| data | List of files used by this rule at compile time and runtime.
If including data at compile time with include_str!() and similar, prefer compile_data
over data
, to prevent the data also being included in the runfiles. | List of labels | optional | [] |
| deps | List of other libraries to be linked to this library target.
These can be either other rust_library
targets or cc_library
targets if linking a native library. | List of labels | optional | [] |
| edition | The rust edition to use for this crate. Defaults to the edition specified in the rust_toolchain. | String | optional | "" |
-| env | Specifies additional environment variables to set when the test is executed by bazel test. Values are subject to $(execpath)
and ["Make variable"](https://docs.bazel.build/versions/master/be/make-variables.html) substitution. | Dictionary: String -> String | optional | {} |
+| env | Specifies additional environment variables to set when the test is executed by bazel test. Values are subject to $(rootpath)
, $(execpath)
, location, and ["Make variable"](https://docs.bazel.build/versions/master/be/make-variables.html) substitution.
Execpath returns absolute path, and in order to be able to construct the absolute path we need to wrap the test binary in a launcher. Using a launcher comes with complications, such as more complicated debugger attachment. | Dictionary: String -> String | optional | {} |
| proc_macro_deps | List of rust_library
targets with kind proc-macro
used to help build this library target. | List of labels | optional | [] |
| rustc_env | Dictionary of additional "key": "value"
environment variables to set for rustc.
rust_test()/rust_binary() rules can use $(rootpath //package:target) to pass in the location of a generated file or external tool. Cargo build scripts that wish to expand locations should use cargo_build_script()'s build_script_env argument instead, as build scripts are run in a different environment - see cargo_build_script()'s documentation for more. | Dictionary: String -> String | optional | {} |
| rustc_env_files | Files containing additional environment variables to set for rustc.
These files should contain a single variable per line, of format NAME=value
, and newlines may be included in a value by ending a line with a trailing back-slash (\
).
The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged. | List of labels | optional | [] |
diff --git a/docs/flatten.md b/docs/flatten.md
index ffeb8fd92f..159965433c 100644
--- a/docs/flatten.md
+++ b/docs/flatten.md
@@ -1094,7 +1094,7 @@ Run the test with `bazel build //hello_lib:hello_lib_test`.
| data | List of files used by this rule at compile time and runtime.
If including data at compile time with include_str!() and similar, prefer compile_data
over data
, to prevent the data also being included in the runfiles. | List of labels | optional | [] |
| deps | List of other libraries to be linked to this library target.
These can be either other rust_library
targets or cc_library
targets if linking a native library. | List of labels | optional | [] |
| edition | The rust edition to use for this crate. Defaults to the edition specified in the rust_toolchain. | String | optional | "" |
-| env | Specifies additional environment variables to set when the test is executed by bazel test. Values are subject to $(execpath)
and ["Make variable"](https://docs.bazel.build/versions/master/be/make-variables.html) substitution. | Dictionary: String -> String | optional | {} |
+| env | Specifies additional environment variables to set when the test is executed by bazel test. Values are subject to $(rootpath)
, $(execpath)
, location, and ["Make variable"](https://docs.bazel.build/versions/master/be/make-variables.html) substitution.
Execpath returns absolute path, and in order to be able to construct the absolute path we need to wrap the test binary in a launcher. Using a launcher comes with complications, such as more complicated debugger attachment. | Dictionary: String -> String | optional | {} |
| proc_macro_deps | List of rust_library
targets with kind proc-macro
used to help build this library target. | List of labels | optional | [] |
| rustc_env | Dictionary of additional "key": "value"
environment variables to set for rustc.
rust_test()/rust_binary() rules can use $(rootpath //package:target) to pass in the location of a generated file or external tool. Cargo build scripts that wish to expand locations should use cargo_build_script()'s build_script_env argument instead, as build scripts are run in a different environment - see cargo_build_script()'s documentation for more. | Dictionary: String -> String | optional | {} |
| rustc_env_files | Files containing additional environment variables to set for rustc.
These files should contain a single variable per line, of format NAME=value
, and newlines may be included in a value by ending a line with a trailing back-slash (\
).
The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged. | List of labels | optional | [] |
diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl
index f1d24ef55d..89d95e6c7c 100644
--- a/rust/private/rust.bzl
+++ b/rust/private/rust.bzl
@@ -306,13 +306,14 @@ def _rust_binary_impl(ctx):
),
)
-def _create_test_launcher(ctx, toolchain, output, providers):
+def _create_test_launcher(ctx, toolchain, output, env, providers):
"""Create a process wrapper to ensure runtime environment variables are defined for the test binary
Args:
ctx (ctx): The rule's context object
toolchain (rust_toolchain): The current rust toolchain
output (File): The output File that will be produced, depends on crate type.
+ env (dict): Dict of environment variables
providers (list): Providers from a rust compile action. See `rustc_compile_action`
Returns:
@@ -337,20 +338,12 @@ def _create_test_launcher(ctx, toolchain, output, providers):
is_executable = True,
)
- # Get data attribute
- data = getattr(ctx.attr, "data", [])
-
# Expand the environment variables and write them to a file
environ_file = ctx.actions.declare_file(launcher_filename + ".launchfiles/env")
- environ = expand_dict_value_locations(
- ctx,
- getattr(ctx.attr, "env", {}),
- data,
- )
# Convert the environment variables into a list to be written into a file.
environ_list = []
- for key, value in sorted(environ.items()):
+ for key, value in sorted(env.items()):
environ_list.extend([key, value])
ctx.actions.write(
@@ -454,8 +447,21 @@ def _rust_test_common(ctx, toolchain, output):
crate_info = crate_info,
rust_flags = ["--test"] if ctx.attr.use_libtest_harness else ["--cfg", "test"],
)
+ data = getattr(ctx.attr, "data", [])
+
+ env = expand_dict_value_locations(
+ ctx,
+ getattr(ctx.attr, "env", {}),
+ data,
+ )
+ providers.append(testing.TestEnvironment(env))
- return _create_test_launcher(ctx, toolchain, output, providers)
+ if any(["{pwd}" in v for v in env.values()]):
+ # Some of the environment variables require expanding {pwd} placeholder at runtime,
+ # we need a launcher for that.
+ return _create_test_launcher(ctx, toolchain, output, env, providers)
+ else:
+ return providers
def _rust_test_impl(ctx):
"""The implementation of the `rust_test` rule
@@ -630,8 +636,12 @@ _rust_test_attrs = {
mandatory = False,
doc = dedent("""\
Specifies additional environment variables to set when the test is executed by bazel test.
- Values are subject to `$(execpath)` and
+ Values are subject to `$(rootpath)`, `$(execpath)`, location, and
["Make variable"](https://docs.bazel.build/versions/master/be/make-variables.html) substitution.
+
+ Execpath returns absolute path, and in order to be able to construct the absolute path we
+ need to wrap the test binary in a launcher. Using a launcher comes with complications, such as
+ more complicated debugger attachment.
"""),
),
"use_libtest_harness": attr.bool(
diff --git a/test/test_env/BUILD.bazel b/test/test_env/BUILD.bazel
index ba7ba26075..f3f34ff5b8 100644
--- a/test/test_env/BUILD.bazel
+++ b/test/test_env/BUILD.bazel
@@ -26,6 +26,5 @@ rust_test(
env = {
"FERRIS_SAYS": "Hello fellow Rustaceans!",
"HELLO_WORLD_BIN_ROOTPATH": "$(rootpath :hello-world)",
- "HELLO_WORLD_SRC_EXECPATH": "$(execpath :hello_world_main)",
},
)
diff --git a/test/test_env/tests/run.rs b/test/test_env/tests/run.rs
index 0ee58ecf92..1c78412ee5 100644
--- a/test/test_env/tests/run.rs
+++ b/test/test_env/tests/run.rs
@@ -26,10 +26,4 @@ fn run() {
);
assert!(!hello_world_bin.is_absolute());
assert!(hello_world_bin.exists());
-
- // Ensure `execpath` expanded variables map to real files and have absolute paths
- let hello_world_src =
- std::path::PathBuf::from(std::env::var("HELLO_WORLD_SRC_EXECPATH").unwrap());
- assert!(hello_world_src.is_absolute());
- assert!(hello_world_src.exists());
}
diff --git a/test/test_env_launcher/BUILD.bazel b/test/test_env_launcher/BUILD.bazel
new file mode 100644
index 0000000000..ba7ba26075
--- /dev/null
+++ b/test/test_env_launcher/BUILD.bazel
@@ -0,0 +1,31 @@
+load(
+ "//rust:rust.bzl",
+ "rust_binary",
+ "rust_test",
+)
+
+rust_binary(
+ name = "hello-world",
+ srcs = ["src/main.rs"],
+ edition = "2018",
+)
+
+filegroup(
+ name = "hello_world_main",
+ srcs = ["src/main.rs"],
+)
+
+rust_test(
+ name = "test",
+ srcs = ["tests/run.rs"],
+ data = [
+ ":hello-world",
+ ":hello_world_main",
+ ],
+ edition = "2018",
+ env = {
+ "FERRIS_SAYS": "Hello fellow Rustaceans!",
+ "HELLO_WORLD_BIN_ROOTPATH": "$(rootpath :hello-world)",
+ "HELLO_WORLD_SRC_EXECPATH": "$(execpath :hello_world_main)",
+ },
+)
diff --git a/test/test_env_launcher/src/main.rs b/test/test_env_launcher/src/main.rs
new file mode 100644
index 0000000000..5bf256ea97
--- /dev/null
+++ b/test/test_env_launcher/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello world");
+}
diff --git a/test/test_env_launcher/tests/run.rs b/test/test_env_launcher/tests/run.rs
new file mode 100644
index 0000000000..01903cd2aa
--- /dev/null
+++ b/test/test_env_launcher/tests/run.rs
@@ -0,0 +1,35 @@
+#[test]
+fn run() {
+ let path = env!("CARGO_BIN_EXE_hello-world");
+ let output = std::process::Command::new(path)
+ .output()
+ .expect("Failed to run process");
+ assert_eq!(&b"Hello world\n"[..], output.stdout.as_slice());
+
+ // Test the `env` attribute of `rust_test` at run time
+ assert_eq!(
+ std::env::var("FERRIS_SAYS").unwrap(),
+ "Hello fellow Rustaceans!"
+ );
+
+ // Test the behavior of `rootpath` and that a binary can be found relative to current_dir
+ let hello_world_bin =
+ std::path::PathBuf::from(std::env::var_os("HELLO_WORLD_BIN_ROOTPATH").unwrap());
+
+ assert_eq!(
+ hello_world_bin.as_path(),
+ std::path::Path::new(if std::env::consts::OS == "windows" {
+ "test/test_env_launcher/hello-world.exe"
+ } else {
+ "test/test_env_launcher/hello-world"
+ })
+ );
+ assert!(!hello_world_bin.is_absolute());
+ assert!(hello_world_bin.exists());
+
+ // Ensure `execpath` expanded variables map to real files and have absolute paths
+ let hello_world_src =
+ std::path::PathBuf::from(std::env::var("HELLO_WORLD_SRC_EXECPATH").unwrap());
+ assert!(hello_world_src.is_absolute());
+ assert!(hello_world_src.exists());
+}