Skip to content

Commit

Permalink
Moved cargo_dep_env into it's own file. (#2823)
Browse files Browse the repository at this point in the history
I've also added some additional tests for `cargo_build_script`.
  • Loading branch information
UebelAndre authored Sep 3, 2024
1 parent 73b1594 commit 6450253
Show file tree
Hide file tree
Showing 10 changed files with 258 additions and 110 deletions.
8 changes: 4 additions & 4 deletions cargo/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ load(
_cargo_bootstrap_repository = "cargo_bootstrap_repository",
_cargo_env = "cargo_env",
)
load(
"//cargo/private:cargo_build_script.bzl",
_cargo_dep_env = "cargo_dep_env",
)
load(
"//cargo/private:cargo_build_script_wrapper.bzl",
_cargo_build_script = "cargo_build_script",
)
load(
"//cargo/private:cargo_dep_env.bzl",
_cargo_dep_env = "cargo_dep_env",
)

cargo_bootstrap_repository = _cargo_bootstrap_repository
cargo_env = _cargo_env
Expand Down
105 changes: 1 addition & 104 deletions cargo/private/cargo_build_script.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES")
load("//rust:defs.bzl", "rust_common")
load("//rust:rust_common.bzl", "BuildInfo", "DepInfo")
load("//rust:rust_common.bzl", "BuildInfo")

# buildifier: disable=bzl-visibility
load(
Expand Down Expand Up @@ -402,8 +402,6 @@ cargo_build_script = rule(
file of arguments to rustc: `@$(location //package:target)`.
"""),
),
# The source of truth will be the `cargo_build_script` macro until stardoc
# implements documentation inheritence. See https://github.com/bazelbuild/stardoc/issues/27
"script": attr.label(
doc = "The binary script to run, generally a `rust_binary` target.",
executable = True,
Expand Down Expand Up @@ -458,104 +456,3 @@ def name_to_pkg_name(name):
if name.endswith("_bs"):
return name[:-len("_bs")]
return name

def _cargo_dep_env_implementation(ctx):
empty_file = ctx.actions.declare_file(ctx.label.name + ".empty_file")
empty_dir = ctx.actions.declare_directory(ctx.label.name + ".empty_dir")
ctx.actions.write(
output = empty_file,
content = "",
)
ctx.actions.run(
outputs = [empty_dir],
executable = "true",
)

build_infos = []
out_dir = ctx.file.out_dir
if out_dir:
if not out_dir.is_directory:
fail("out_dir must be a directory artifact")

# BuildInfos in this list are collected up for all transitive cargo_build_script
# dependencies. This is important for any flags set in `dep_env` which reference this
# `out_dir`.
#
# TLDR: This BuildInfo propagates up build script dependencies.
build_infos.append(BuildInfo(
dep_env = empty_file,
flags = empty_file,
linker_flags = empty_file,
link_search_paths = empty_file,
out_dir = out_dir,
rustc_env = empty_file,
compile_data = depset([]),
))
return [
DefaultInfo(files = depset(ctx.files.src)),
# Parts of this BuildInfo is used when building all transitive dependencies
# (cargo_build_script and otherwise), alongside the DepInfo. This is how other rules
# identify this one as a valid dependency, but we don't otherwise have a use for it.
#
# TLDR: This BuildInfo propagates up normal (non build script) depenencies.
#
# In the future, we could consider setting rustc_env here, and also propagating dep_dir
# so files in it can be referenced there.
BuildInfo(
dep_env = empty_file,
flags = empty_file,
linker_flags = empty_file,
link_search_paths = empty_file,
out_dir = None,
rustc_env = empty_file,
compile_data = depset([]),
),
# Information here is used directly by dependencies, and it is an error to have more than
# one dependency which sets this. This is the main way to specify information from build
# scripts, which is what we're looking to do.
DepInfo(
dep_env = ctx.file.src,
direct_crates = depset(),
link_search_path_files = depset(),
transitive_build_infos = depset(direct = build_infos),
transitive_crate_outputs = depset(),
transitive_crates = depset(),
transitive_noncrates = depset(),
),
]

cargo_dep_env = rule(
implementation = _cargo_dep_env_implementation,
doc = (
"A rule for generating variables for dependent `cargo_build_script`s " +
"without a build script. This is useful for using Bazel rules instead " +
"of a build script, while also generating configuration information " +
"for build scripts which depend on this crate."
),
attrs = {
"out_dir": attr.label(
doc = dedent("""\
Folder containing additional inputs when building all direct dependencies.
This has the same effect as a `cargo_build_script` which prints
puts files into `$OUT_DIR`, but without requiring a build script.
"""),
allow_single_file = True,
mandatory = False,
),
"src": attr.label(
doc = dedent("""\
File containing additional environment variables to set for build scripts of direct dependencies.
This has the same effect as a `cargo_build_script` which prints
`cargo:VAR=VALUE` lines, but without requiring a build script.
This 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 (`\\\\`).
"""),
allow_single_file = True,
mandatory = True,
),
},
)
107 changes: 107 additions & 0 deletions cargo/private/cargo_dep_env.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
"""Rules for Cargo build scripts (`build.rs` files)"""

load("//rust:rust_common.bzl", "BuildInfo", "DepInfo")

# buildifier: disable=bzl-visibility
load("//rust/private:utils.bzl", "dedent")

def _cargo_dep_env_impl(ctx):
empty_file = ctx.actions.declare_file(ctx.label.name + ".empty_file")
empty_dir = ctx.actions.declare_directory(ctx.label.name + ".empty_dir")
ctx.actions.write(
output = empty_file,
content = "",
)
ctx.actions.run(
outputs = [empty_dir],
executable = "true",
)

build_infos = []
out_dir = ctx.file.out_dir
if out_dir:
if not out_dir.is_directory:
fail("out_dir must be a directory artifact")

# BuildInfos in this list are collected up for all transitive cargo_build_script
# dependencies. This is important for any flags set in `dep_env` which reference this
# `out_dir`.
#
# TLDR: This BuildInfo propagates up build script dependencies.
build_infos.append(BuildInfo(
dep_env = empty_file,
flags = empty_file,
linker_flags = empty_file,
link_search_paths = empty_file,
out_dir = out_dir,
rustc_env = empty_file,
compile_data = depset([]),
))
return [
DefaultInfo(files = depset(ctx.files.src)),
# Parts of this BuildInfo is used when building all transitive dependencies
# (cargo_build_script and otherwise), alongside the DepInfo. This is how other rules
# identify this one as a valid dependency, but we don't otherwise have a use for it.
#
# TLDR: This BuildInfo propagates up normal (non build script) depenencies.
#
# In the future, we could consider setting rustc_env here, and also propagating dep_dir
# so files in it can be referenced there.
BuildInfo(
dep_env = empty_file,
flags = empty_file,
linker_flags = empty_file,
link_search_paths = empty_file,
out_dir = None,
rustc_env = empty_file,
compile_data = depset([]),
),
# Information here is used directly by dependencies, and it is an error to have more than
# one dependency which sets this. This is the main way to specify information from build
# scripts, which is what we're looking to do.
DepInfo(
dep_env = ctx.file.src,
direct_crates = depset(),
link_search_path_files = depset(),
transitive_build_infos = depset(direct = build_infos),
transitive_crate_outputs = depset(),
transitive_crates = depset(),
transitive_noncrates = depset(),
),
]

cargo_dep_env = rule(
implementation = _cargo_dep_env_impl,
doc = (
"A rule for generating variables for dependent `cargo_build_script`s " +
"without a build script. This is useful for using Bazel rules instead " +
"of a build script, while also generating configuration information " +
"for build scripts which depend on this crate."
),
attrs = {
"out_dir": attr.label(
doc = dedent("""\
Folder containing additional inputs when building all direct dependencies.
This has the same effect as a `cargo_build_script` which prints
puts files into `$OUT_DIR`, but without requiring a build script.
"""),
allow_single_file = True,
mandatory = False,
),
"src": attr.label(
doc = dedent("""\
File containing additional environment variables to set for build scripts of direct dependencies.
This has the same effect as a `cargo_build_script` which prints
`cargo:VAR=VALUE` lines, but without requiring a build script.
This 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 (`\\\\`).
"""),
allow_single_file = True,
mandatory = True,
),
},
)
38 changes: 38 additions & 0 deletions test/cargo_build_script/location_expansion/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
load("@bazel_skylib//rules:write_file.bzl", "write_file")
load("//cargo:defs.bzl", "cargo_build_script")
load("//rust:defs.bzl", "rust_test")

write_file(
name = "target_data",
out = "target_data.txt",
content = ["la-li-lu-le-lo"],
)

write_file(
name = "exec_data",
out = "exec_data.txt",
content = ["la-li-lu-le-lo"],
)

cargo_build_script(
name = "build_rs",
srcs = ["build.rs"],
build_script_env = {
"DATA_EXECPATH": "$(execpath target_data.txt)",
"DATA_RLOCATIONPATH": "$(rlocationpath target_data.txt)",
"DATA_ROOTPATH": "$(rootpath target_data.txt)",
"TOOL_EXECPATH": "$(execpath exec_data.txt)",
"TOOL_RLOCATIONPATH": "$(rlocationpath exec_data.txt)",
"TOOL_ROOTPATH": "$(rootpath exec_data.txt)",
},
data = ["target_data.txt"],
edition = "2018",
tools = ["exec_data.txt"],
)

rust_test(
name = "test",
srcs = ["test.rs"],
edition = "2018",
deps = [":build_rs"],
)
26 changes: 26 additions & 0 deletions test/cargo_build_script/location_expansion/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
fn main() {
println!(
"cargo:rustc-env=DATA_ROOTPATH={}",
std::env::var("DATA_ROOTPATH").expect("Environment variable not set")
);
println!(
"cargo:rustc-env=DATA_EXECPATH={}",
std::env::var("DATA_EXECPATH").expect("Environment variable not set")
);
println!(
"cargo:rustc-env=DATA_RLOCATIONPATH={}",
std::env::var("DATA_RLOCATIONPATH").expect("Environment variable not set")
);
println!(
"cargo:rustc-env=TOOL_ROOTPATH={}",
std::env::var("TOOL_ROOTPATH").expect("Environment variable not set")
);
println!(
"cargo:rustc-env=TOOL_EXECPATH={}",
std::env::var("TOOL_EXECPATH").expect("Environment variable not set")
);
println!(
"cargo:rustc-env=TOOL_RLOCATIONPATH={}",
std::env::var("TOOL_RLOCATIONPATH").expect("Environment variable not set")
);
}
80 changes: 80 additions & 0 deletions test/cargo_build_script/location_expansion/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#[test]
pub fn test_data_rootpath() {
assert_eq!(
"test/cargo_build_script/location_expansion/target_data.txt",
env!("DATA_ROOTPATH")
);
}

#[test]
pub fn test_data_rlocation() {
assert_eq!(
"rules_rust/test/cargo_build_script/location_expansion/target_data.txt",
env!("DATA_RLOCATIONPATH")
);
}

#[test]
pub fn test_tool_rootpath() {
assert_eq!(
"test/cargo_build_script/location_expansion/exec_data.txt",
env!("TOOL_ROOTPATH")
);
}

#[test]
pub fn test_tool_rlocationpath() {
assert_eq!(
"rules_rust/test/cargo_build_script/location_expansion/exec_data.txt",
env!("TOOL_RLOCATIONPATH")
);
}

#[test]
pub fn test_execpath() {
// Replace `\` to ensure paths are consistent on Windows.`
let data_execpath = env!("DATA_EXECPATH").replace('\\', "/");
let tool_execpath = env!("TOOL_EXECPATH").replace('\\', "/");

let data_path = data_execpath
.split_at(
data_execpath
.find("/bazel-out/")
.unwrap_or_else(|| panic!("Failed to parse execroot from: {}", data_execpath)),
)
.1;
let tool_path = tool_execpath
.split_at(
tool_execpath
.find("/bazel-out/")
.unwrap_or_else(|| panic!("Failed to parse execroot from: {}", tool_execpath)),
)
.1;

let (data_cfg, data_short_path) = data_path.split_at(
data_path
.find("/bin/")
.unwrap_or_else(|| panic!("Failed to find bin in {}", data_path))
+ "/bin/".len(),
);
let (tool_cfg, tool_short_path) = tool_path.split_at(
tool_path
.find("/bin/")
.unwrap_or_else(|| panic!("Failed to find bin in {}", tool_path))
+ "/bin/".len(),
);

assert_ne!(
data_cfg, tool_cfg,
"Data and tools should not be from the same configuration."
);

assert_eq!(
data_short_path,
"test/cargo_build_script/location_expansion/target_data.txt"
);
assert_eq!(
tool_short_path,
"test/cargo_build_script/location_expansion/exec_data.txt"
);
}
Empty file.
Loading

0 comments on commit 6450253

Please sign in to comment.