Skip to content

Commit

Permalink
$(location ...) expansion and runtime data support for build scripts
Browse files Browse the repository at this point in the history
- rustc_env now expands $(rootpath //package:target), for passing
the location of files that are needed at compile time
- build_script_env now expands $(execpath //package:target), for
passing the location of files or tools that are needed at build
script runtime
- cargo_script_build() now passes the data argument to the build
script runner, so the data is available at runtime
  • Loading branch information
dae committed Oct 29, 2020
1 parent 224fe6a commit 0e0ee31
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 9 deletions.
39 changes: 33 additions & 6 deletions cargo/cargo_build_script.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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):
if env.startswith("$(execpath ") or env.startswith("$(location "):
# build script runner will replace with execroot
return "${pwd}/" + ctx.expand_location(env, data)
else:
return env

def _expand_locations(ctx):
"Expand $(execroot ...) 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.
Expand Down Expand Up @@ -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,
)

Expand Down Expand Up @@ -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"),
),
Expand All @@ -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
Expand Down Expand Up @@ -220,10 +238,16 @@ 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
build_script_env = {
"SOME_TOOL_OR_FILE": "$(execroot @tool//:binary)"
}
# Optional data/tool dependencies
data = ["@tool//:binary"],
)
rust_library(
Expand All @@ -245,13 +269,15 @@ 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(
name = name + "_script_",
crate_features = crate_features,
version = version,
deps = deps,
data = data,
**kwargs
)
_build_script_run(
Expand All @@ -262,4 +288,5 @@ def cargo_build_script(
version = version,
build_script_env = build_script_env,
deps = deps,
data = data,
)
11 changes: 10 additions & 1 deletion cargo/cargo_build_script_runner/bin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.starts_with("${pwd}") {
env::set_var(key, value.replacen("${pwd}", exec_root_str, 1));
}
}


let output = BuildScriptOutput::from_command(&mut command).map_err(|exit_code| {
format!(
"Build script process failed{}",
Expand Down
51 changes: 51 additions & 0 deletions examples/env_locations/BUILD
Original file line number Diff line number Diff line change
@@ -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",
],
)
17 changes: 17 additions & 0 deletions examples/env_locations/build.rs
Original file line number Diff line number Diff line change
@@ -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);
}
11 changes: 11 additions & 0 deletions examples/env_locations/main.rs
Original file line number Diff line number Diff line change
@@ -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);
}
1 change: 1 addition & 0 deletions examples/env_locations/source.file
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
source
1 change: 1 addition & 0 deletions rust/private/clippy.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions rust/private/rust.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
15 changes: 13 additions & 2 deletions rust/private/rustc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -400,7 +409,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:
Expand All @@ -418,6 +428,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
Expand Down Expand Up @@ -549,7 +560,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.
Expand Down

0 comments on commit 0e0ee31

Please sign in to comment.