diff --git a/cargo/cargo_build_script.bzl b/cargo/cargo_build_script.bzl index 9877422d60..9a54b14b16 100644 --- a/cargo/cargo_build_script.bzl +++ b/cargo/cargo_build_script.bzl @@ -4,6 +4,19 @@ load("@io_bazel_rules_rust//rust:private/rustc.bzl", "BuildInfo", "DepInfo", "ge load("@io_bazel_rules_rust//rust:private/utils.bzl", "find_toolchain") load("@io_bazel_rules_rust//rust:rust.bzl", "rust_binary") +def _expand_location(ctx, env, data): + for directive in "$(execpath ", "$(location ": + if directive in env: + # build script runner will expand pwd to execroot for us + env = env.replace(directive, "${pwd}/" + directive) + return ctx.expand_location(env, data) + +def _expand_locations(ctx): + "Expand $(execpath ...) references in user-provided env vars." + env = ctx.attr.build_script_env + data = getattr(ctx.attr, "data", []) + return dict([(k, _expand_location(ctx, v, data)) for (k, v) in env.items()]) + def _build_script_impl(ctx): """The implementation for the `_build_script_run` rule. @@ -88,14 +101,14 @@ def _build_script_impl(ctx): for f in ctx.attr.crate_features: env["CARGO_FEATURE_" + f.upper().replace("-", "_")] = "1" - env.update(ctx.attr.build_script_env) + env.update(_expand_locations(ctx)) tools = depset( direct = [ script, ctx.executable._cargo_build_script_runner, toolchain.rustc, - ], + ] + ctx.files.data, transitive = toolchain_tools, ) @@ -164,6 +177,10 @@ _build_script_run = rule( "build_script_env": attr.string_dict( doc = "Environment variables for build scripts.", ), + "data": attr.label_list( + doc = "Data or tools required by the build script.", + allow_files = True, + ), "_cc_toolchain": attr.label( default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), ), @@ -188,6 +205,7 @@ def cargo_build_script( version = None, deps = [], build_script_env = {}, + data = [], **kwargs): """Compile and execute a rust build script to generate build attributes @@ -220,10 +238,19 @@ def cargo_build_script( cargo_build_script( name = "build_script", srcs = ["build.rs"], - # Data are shipped during execution. - data = ["src/lib.rs"], - # Environment variables passed during build.rs execution - build_script_env = {"CARGO_PKG_VERSION": "0.1.2"}, + # Optional environment variables passed during build.rs compilation + rustc_env = { + "CARGO_PKG_VERSION": "0.1.2", + }, + # Optional environment variables passed during build.rs execution. + # Note that as the build script's working directory is not execroot, + # execpath/location will return an absolute path, instead of a relative + # one. + build_script_env = { + "SOME_TOOL_OR_FILE": "$(execpath @tool//:binary)" + } + # Optional data/tool dependencies + data = ["@tool//:binary"], ) rust_library( @@ -245,6 +272,7 @@ def cargo_build_script( version (str, optional): The semantic version (semver) of the crate. deps (list, optional): The dependencies of the crate defined by `crate_name`. build_script_env (dict, optional): Environment variables for build scripts. + data (list, optional): Files or tools needed by the build script. **kwargs: Forwards to the underlying `rust_binary` rule. """ rust_binary( @@ -252,6 +280,7 @@ def cargo_build_script( crate_features = crate_features, version = version, deps = deps, + data = data, **kwargs ) _build_script_run( @@ -262,4 +291,5 @@ def cargo_build_script( version = version, build_script_env = build_script_env, deps = deps, + data = data, ) diff --git a/cargo/cargo_build_script_runner/bin.rs b/cargo/cargo_build_script_runner/bin.rs index 629bccad5f..be5593d9c0 100644 --- a/cargo/cargo_build_script_runner/bin.rs +++ b/cargo/cargo_build_script_runner/bin.rs @@ -48,7 +48,7 @@ fn main() -> Result<(), String> { let mut command = Command::new(exec_root.join(&progname)); command - .current_dir(manifest_dir.clone()) + .current_dir(&manifest_dir) .envs(target_env_vars) .env("OUT_DIR", out_dir_abs) .env("CARGO_MANIFEST_DIR", manifest_dir) @@ -92,6 +92,15 @@ fn main() -> Result<(), String> { } } + // replace env vars with a ${pwd} prefix with the exec_root + for (key, value) in env::vars() { + let exec_root_str = exec_root.to_str().expect("exec_root not in utf8"); + if value.contains("${pwd}") { + env::set_var(key, value.replace("${pwd}", exec_root_str)); + } + } + + let output = BuildScriptOutput::from_command(&mut command).map_err(|exit_code| { format!( "Build script process failed{}", diff --git a/docs/cargo_build_script.md b/docs/cargo_build_script.md index 51477960fc..f210c7c2f6 100644 --- a/docs/cargo_build_script.md +++ b/docs/cargo_build_script.md @@ -6,7 +6,7 @@ ## cargo_build_script
-cargo_build_script(name, crate_name, crate_features, version, deps, build_script_env, kwargs) +cargo_build_script(name, crate_name, crate_features, version, deps, build_script_env, data, kwargs)Compile and execute a rust build script to generate build attributes @@ -40,10 +40,19 @@ load("@io_bazel_rules_rust//cargo:cargo_build_script.bzl", "cargo_build_script") cargo_build_script( name = "build_script", srcs = ["build.rs"], - # Data are shipped during execution. - data = ["src/lib.rs"], - # Environment variables passed during build.rs execution - build_script_env = {"CARGO_PKG_VERSION": "0.1.2"}, + # Optional environment variables passed during build.rs compilation + rustc_env = { + "CARGO_PKG_VERSION": "0.1.2", + }, + # Optional environment variables passed during build.rs execution. + # Note that as the build script's working directory is not execroot, + # execpath/location will return an absolute path, instead of a relative + # one. + build_script_env = { + "SOME_TOOL_OR_FILE": "$(execpath @tool//:binary)" + } + # Optional data/tool dependencies + data = ["@tool//:binary"], ) rust_library( @@ -69,6 +78,7 @@ The `hello_lib` target will be build with the flags and the environment variable | version | The semantic version (semver) of the crate. |
None
|
| deps | The dependencies of the crate defined by crate_name
. | []
|
| build_script_env | Environment variables for build scripts. | {}
|
+| data | Files or tools needed by the build script. | []
|
| kwargs | Forwards to the underlying rust_binary
rule. | none |
diff --git a/docs/flatten.md b/docs/flatten.md
index 17c41d6bc2..9f7909ce18 100644
--- a/docs/flatten.md
+++ b/docs/flatten.md
@@ -122,7 +122,7 @@ Run the benchmark test using: `bazel run //fibonacci:fibonacci_bench`.
| edition | The rust edition to use for this crate. Defaults to the edition specified in the rust_toolchain. | String | optional | "" |
| out_dir_tar | __Deprecated__, do not use, see [#cargo_build_script] instead. | Label | optional | None |
| 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. | Dictionary: String -> String | optional | {} |
+| rustc_env | Dictionary of additional "key": "value"
environment variables to set for rustc.rustc
. | List of strings | optional | [] |
| srcs | List of Rust .rs
source files used to build the library.srcs
contains more than one file, then there must be a file either named lib.rs
. Otherwise, crate_root
must be set to the source file that is the root of the crate to be passed to rustc to build this crate. | List of labels | optional | [] |
| version | A version to inject in the cargo environment variable. | String | optional | "0.0.0" |
@@ -239,7 +239,7 @@ Hello world
| out_binary | - | Boolean | optional | False |
| out_dir_tar | __Deprecated__, do not use, see [#cargo_build_script] instead. | Label | optional | None |
| 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. | Dictionary: String -> String | optional | {} |
+| rustc_env | Dictionary of additional "key": "value"
environment variables to set for rustc.rustc
. | List of strings | optional | [] |
| srcs | List of Rust .rs
source files used to build the library.srcs
contains more than one file, then there must be a file either named lib.rs
. Otherwise, crate_root
must be set to the source file that is the root of the crate to be passed to rustc to build this crate. | List of labels | optional | [] |
| version | A version to inject in the cargo environment variable. | String | optional | "0.0.0" |
@@ -535,7 +535,7 @@ INFO: Elapsed time: 1.245s, Critical Path: 1.01s
| edition | The rust edition to use for this crate. Defaults to the edition specified in the rust_toolchain. | String | optional | "" |
| out_dir_tar | __Deprecated__, do not use, see [#cargo_build_script] instead. | Label | optional | None |
| 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. | Dictionary: String -> String | optional | {} |
+| rustc_env | Dictionary of additional "key": "value"
environment variables to set for rustc.rustc
. | List of strings | optional | [] |
| srcs | List of Rust .rs
source files used to build the library.srcs
contains more than one file, then there must be a file either named lib.rs
. Otherwise, crate_root
must be set to the source file that is the root of the crate to be passed to rustc to build this crate. | List of labels | optional | [] |
| version | A version to inject in the cargo environment variable. | String | optional | "0.0.0" |
@@ -792,7 +792,7 @@ Run the test with `bazel build //hello_lib:hello_lib_test`.
| edition | The rust edition to use for this crate. Defaults to the edition specified in the rust_toolchain. | String | optional | "" |
| out_dir_tar | __Deprecated__, do not use, see [#cargo_build_script] instead. | Label | optional | None |
| 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. | Dictionary: String -> String | optional | {} |
+| rustc_env | Dictionary of additional "key": "value"
environment variables to set for rustc.rustc
. | List of strings | optional | [] |
| srcs | List of Rust .rs
source files used to build the library.srcs
contains more than one file, then there must be a file either named lib.rs
. Otherwise, crate_root
must be set to the source file that is the root of the crate to be passed to rustc to build this crate. | List of labels | optional | [] |
| version | A version to inject in the cargo environment variable. | String | optional | "0.0.0" |
@@ -963,7 +963,7 @@ The tools required for the `rust_wasm_bindgen` rule.
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| name | A unique name for this target. | Name | required | |
-| bindgen | The label of a bindgen
executable. | Label | optional | None |
+| bindgen | The label of a wasm-bindgen
executable. | Label | optional | None |
@@ -971,7 +971,7 @@ The tools required for the `rust_wasm_bindgen` rule.
## cargo_build_script
-cargo_build_script(name, crate_name, crate_features, version, deps, build_script_env, kwargs) +cargo_build_script(name, crate_name, crate_features, version, deps, build_script_env, data, kwargs)Compile and execute a rust build script to generate build attributes @@ -1005,10 +1005,19 @@ load("@io_bazel_rules_rust//cargo:cargo_build_script.bzl", "cargo_build_script") cargo_build_script( name = "build_script", srcs = ["build.rs"], - # Data are shipped during execution. - data = ["src/lib.rs"], - # Environment variables passed during build.rs execution - build_script_env = {"CARGO_PKG_VERSION": "0.1.2"}, + # Optional environment variables passed during build.rs compilation + rustc_env = { + "CARGO_PKG_VERSION": "0.1.2", + }, + # Optional environment variables passed during build.rs execution. + # Note that as the build script's working directory is not execroot, + # execpath/location will return an absolute path, instead of a relative + # one. + build_script_env = { + "SOME_TOOL_OR_FILE": "$(execpath @tool//:binary)" + } + # Optional data/tool dependencies + data = ["@tool//:binary"], ) rust_library( @@ -1034,6 +1043,7 @@ The `hello_lib` target will be build with the flags and the environment variable | version | The semantic version (semver) of the crate. |
None
|
| deps | The dependencies of the crate defined by crate_name
. | []
|
| build_script_env | Environment variables for build scripts. | {}
|
+| data | Files or tools needed by the build script. | []
|
| kwargs | Forwards to the underlying rust_binary
rule. | none |
diff --git a/docs/rust.md b/docs/rust.md
index d85c2fe970..405af98da1 100644
--- a/docs/rust.md
+++ b/docs/rust.md
@@ -103,7 +103,7 @@ Run the benchmark test using: `bazel run //fibonacci:fibonacci_bench`.
| edition | The rust edition to use for this crate. Defaults to the edition specified in the rust_toolchain. | String | optional | "" |
| out_dir_tar | __Deprecated__, do not use, see [#cargo_build_script] instead. | Label | optional | None |
| 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. | Dictionary: String -> String | optional | {} |
+| rustc_env | Dictionary of additional "key": "value"
environment variables to set for rustc.rustc
. | List of strings | optional | [] |
| srcs | List of Rust .rs
source files used to build the library.srcs
contains more than one file, then there must be a file either named lib.rs
. Otherwise, crate_root
must be set to the source file that is the root of the crate to be passed to rustc to build this crate. | List of labels | optional | [] |
| version | A version to inject in the cargo environment variable. | String | optional | "0.0.0" |
@@ -220,7 +220,7 @@ Hello world
| out_binary | - | Boolean | optional | False |
| out_dir_tar | __Deprecated__, do not use, see [#cargo_build_script] instead. | Label | optional | None |
| 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. | Dictionary: String -> String | optional | {} |
+| rustc_env | Dictionary of additional "key": "value"
environment variables to set for rustc.rustc
. | List of strings | optional | [] |
| srcs | List of Rust .rs
source files used to build the library.srcs
contains more than one file, then there must be a file either named lib.rs
. Otherwise, crate_root
must be set to the source file that is the root of the crate to be passed to rustc to build this crate. | List of labels | optional | [] |
| version | A version to inject in the cargo environment variable. | String | optional | "0.0.0" |
@@ -314,7 +314,7 @@ INFO: Elapsed time: 1.245s, Critical Path: 1.01s
| edition | The rust edition to use for this crate. Defaults to the edition specified in the rust_toolchain. | String | optional | "" |
| out_dir_tar | __Deprecated__, do not use, see [#cargo_build_script] instead. | Label | optional | None |
| 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. | Dictionary: String -> String | optional | {} |
+| rustc_env | Dictionary of additional "key": "value"
environment variables to set for rustc.rustc
. | List of strings | optional | [] |
| srcs | List of Rust .rs
source files used to build the library.srcs
contains more than one file, then there must be a file either named lib.rs
. Otherwise, crate_root
must be set to the source file that is the root of the crate to be passed to rustc to build this crate. | List of labels | optional | [] |
| version | A version to inject in the cargo environment variable. | String | optional | "0.0.0" |
@@ -473,7 +473,7 @@ Run the test with `bazel build //hello_lib:hello_lib_test`.
| edition | The rust edition to use for this crate. Defaults to the edition specified in the rust_toolchain. | String | optional | "" |
| out_dir_tar | __Deprecated__, do not use, see [#cargo_build_script] instead. | Label | optional | None |
| 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. | Dictionary: String -> String | optional | {} |
+| rustc_env | Dictionary of additional "key": "value"
environment variables to set for rustc.rustc
. | List of strings | optional | [] |
| srcs | List of Rust .rs
source files used to build the library.srcs
contains more than one file, then there must be a file either named lib.rs
. Otherwise, crate_root
must be set to the source file that is the root of the crate to be passed to rustc to build this crate. | List of labels | optional | [] |
| version | A version to inject in the cargo environment variable. | String | optional | "0.0.0" |
diff --git a/docs/rust_wasm_bindgen.md b/docs/rust_wasm_bindgen.md
index 7713f6f299..58e1daff24 100644
--- a/docs/rust_wasm_bindgen.md
+++ b/docs/rust_wasm_bindgen.md
@@ -38,6 +38,6 @@ The tools required for the `rust_wasm_bindgen` rule.
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| name | A unique name for this target. | Name | required | |
-| bindgen | The label of a bindgen
executable. | Label | optional | None |
+| bindgen | The label of a wasm-bindgen
executable. | Label | optional | None |
diff --git a/examples/env_locations/BUILD b/examples/env_locations/BUILD
new file mode 100644
index 0000000000..b82f60c69e
--- /dev/null
+++ b/examples/env_locations/BUILD
@@ -0,0 +1,51 @@
+load(
+ "@io_bazel_rules_rust//rust:rust.bzl",
+ "rust_test",
+)
+load("@io_bazel_rules_rust//cargo:cargo_build_script.bzl", "cargo_build_script")
+
+# generate a file
+genrule(
+ name = "data_generator",
+ outs = ["generated.data"],
+ cmd = "echo hello > $@",
+)
+
+_data = [
+ # we should be able to read non-generated source/data files
+ "source.file",
+ # and generated files as well
+ "generated.data",
+ # we should also be able to access external binaries
+ # such as protoc.
+ "@com_google_protobuf//:protoc",
+]
+
+cargo_build_script(
+ name = "build",
+ srcs = ["build.rs"],
+ build_script_env = {
+ # both execpath and location should work
+ "SOURCE_FILE": "$(execpath source.file)",
+ "GENERATED_DATA": "$(location generated.data)",
+ "SOME_TOOL": "$(execpath @com_google_protobuf//:protoc)",
+ },
+ data = _data,
+)
+
+rust_test(
+ name = "test",
+ srcs = [
+ "main.rs",
+ ],
+ data = _data,
+ edition = "2018",
+ rustc_env = {
+ "SOURCE_FILE": "$(rootpath source.file)",
+ "GENERATED_DATA": "$(rootpath generated.data)",
+ "SOME_TOOL": "$(rootpath @com_google_protobuf//:protoc)",
+ },
+ deps = [
+ ":build",
+ ],
+)
diff --git a/examples/env_locations/build.rs b/examples/env_locations/build.rs
new file mode 100644
index 0000000000..dfc023d3b7
--- /dev/null
+++ b/examples/env_locations/build.rs
@@ -0,0 +1,17 @@
+use std::{fs,env};
+
+fn main() {
+ // our source file should be readable
+ let path = env::var("SOURCE_FILE").unwrap();
+ let generated_data = fs::read_to_string(&path).unwrap();
+ assert_eq!(generated_data, "source\n");
+
+ // our generated data file should be readable
+ let path = env::var("GENERATED_DATA").unwrap();
+ let generated_data = fs::read_to_string(&path).unwrap();
+ assert_eq!(generated_data, "hello\n");
+
+ // and we should be able to read (and thus execute) our tool
+ let path = env::var("SOME_TOOL").unwrap();
+ assert_eq!(fs::read(&path).unwrap().is_empty(), false);
+}
diff --git a/examples/env_locations/main.rs b/examples/env_locations/main.rs
new file mode 100644
index 0000000000..4905472f7e
--- /dev/null
+++ b/examples/env_locations/main.rs
@@ -0,0 +1,11 @@
+#[test]
+fn test() {
+ // our source file should be readable
+ let source_file = std::fs::read_to_string(env!("SOURCE_FILE")).unwrap();
+ assert_eq!(source_file, "source\n");
+ // our generated data file should be readable
+ let generated_data = std::fs::read_to_string(env!("GENERATED_DATA")).unwrap();
+ assert_eq!(generated_data, "hello\n");
+ // and we should be able to read (and thus execute) our tool
+ assert_eq!(std::fs::read(env!("SOME_TOOL")).unwrap().is_empty(), false);
+}
diff --git a/examples/env_locations/source.file b/examples/env_locations/source.file
new file mode 100644
index 0000000000..5a18cd2fbf
--- /dev/null
+++ b/examples/env_locations/source.file
@@ -0,0 +1 @@
+source
diff --git a/rust/private/clippy.bzl b/rust/private/clippy.bzl
index f01cc6b4ae..610aeea4ae 100644
--- a/rust/private/clippy.bzl
+++ b/rust/private/clippy.bzl
@@ -92,6 +92,7 @@ def _clippy_aspect_impl(target, ctx):
build_env_file = build_env_file,
build_flags_files = build_flags_files,
maker_path = clippy_marker.path,
+ aspect = True,
)
# Deny the default-on clippy warning levels.
diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl
index dfa2fc3a3e..718e5c574e 100644
--- a/rust/private/rust.bzl
+++ b/rust/private/rust.bzl
@@ -417,6 +417,12 @@ _rust_common_attrs = {
"rustc_env": attr.string_dict(
doc = _tidy("""
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.
"""),
),
"crate_features": attr.string_list(
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index 55b99355e2..b2a53ca310 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -311,6 +311,15 @@ def get_linker_and_args(ctx, cc_toolchain, feature_configuration, rpaths):
return ld, link_args, link_env
+def _expand_locations(ctx, env, aspect):
+ "Expand $(rootpath ...) references in user-provided env vars."
+ if aspect:
+ data = getattr(ctx.rule.attr, "data", [])
+ else:
+ data = getattr(ctx.attr, "data", [])
+
+ return dict([(k, ctx.expand_location(v, data)) for (k, v) in env.items()])
+
def _process_build_scripts(
ctx,
file,
@@ -415,7 +424,8 @@ def construct_arguments(
out_dir,
build_env_file,
build_flags_files,
- maker_path = None):
+ maker_path = None,
+ aspect = False):
"""Builds an Args object containing common rustc flags
Args:
@@ -433,6 +443,7 @@ def construct_arguments(
build_env_file (str): The output file of a `cargo_build_script` action containing rustc environment variables
build_flags_files (list): The output files of a `cargo_build_script` actions containing rustc build flags
maker_path (File): An optional clippy marker file
+ aspect (bool): True if called in an aspect context.
Returns:
tuple: A tuple of the following items
@@ -559,7 +570,7 @@ def construct_arguments(
env["CARGO_BIN_EXE_" + dep_crate_info.output.basename] = dep_crate_info.output.short_path
# Update environment with user provided variables.
- env.update(crate_info.rustc_env)
+ env.update(_expand_locations(ctx, crate_info.rustc_env, aspect))
# This empty value satisfies Clippy, which otherwise complains about the
# sysroot being undefined.