Skip to content

Commit 8e94585

Browse files
authored
Merge branch 'main' into path-mapping-support
2 parents d47646a + 0d36c00 commit 8e94585

14 files changed

Lines changed: 285 additions & 29 deletions

File tree

cargo/private/cargo_build_script.bzl

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -273,17 +273,14 @@ def _rlocationpath(file, workspace_name):
273273
def _create_runfiles_dir(ctx, script, data_runfiles, retain_list):
274274
"""Create a runfiles directory to represent `CARGO_MANIFEST_DIR`.
275275
276+
Merges runfiles from both the script binary and the data runfiles target,
277+
filtering out the fake executable from the data runfiles.
278+
276279
Due to the inability to forcibly generate runfiles directories for use as inputs
277280
to actions, this function creates a custom runfiles directory that can more
278281
consistently be relied upon as an input. For more details see:
279282
https://github.com/bazelbuild/bazel/issues/15486
280283
281-
Merges runfiles from both the script binary and the data runfiles target,
282-
filtering out the fake executable from the data runfiles.
283-
284-
If runfiles directories can ever be more directly treated as an input this function
285-
can be retired.
286-
287284
Args:
288285
ctx (ctx): The rule's context object
289286
script (Target): The build script binary target.
@@ -534,7 +531,6 @@ def _cargo_build_script_impl(ctx):
534531

535532
tools = depset(
536533
direct = [
537-
script,
538534
ctx.executable._cargo_build_script_runner,
539535
] + fallback_tools + ([toolchain.target_json] if toolchain.target_json else []),
540536
transitive = script_data + toolchain_tools,
@@ -620,7 +616,10 @@ def _cargo_build_script_impl(ctx):
620616
dep_env_out,
621617
runfiles_dir,
622618
] + extra_output,
623-
tools = tools,
619+
tools = [
620+
ctx.attr.script[DefaultInfo].files_to_run,
621+
tools,
622+
],
624623
inputs = depset(build_script_inputs, transitive = [runfiles_inputs]),
625624
mnemonic = "CargoBuildScriptRun",
626625
progress_message = "Running Cargo build script {}".format(pkg_name),

cargo/private/cargo_build_script_runner/bin.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ fn run_buildrs() -> Result<(), String> {
8787

8888
let working_directory = resolve_rundir(&rundir, &exec_root, &manifest_dir)?;
8989

90-
let mut command = Command::new(exec_root.join(progname));
90+
let script_path = exec_root.join(&progname);
91+
let mut command = Command::new(&script_path);
9192
command
9293
.current_dir(&working_directory)
9394
.envs(target_env_vars)
@@ -96,6 +97,15 @@ fn run_buildrs() -> Result<(), String> {
9697
.env("RUSTC", rustc)
9798
.env("RUST_BACKTRACE", "full");
9899

100+
// The script binary may have a `<script>.runfiles/` tree or a
101+
// `<script>.runfiles_manifest` file materialized next to it by Bazel
102+
// (because the script was passed as a `FilesToRunProvider`). Expose
103+
// whichever exists so the runfiles library can locate the script's
104+
// runfiles (tools). Data files of `cargo_build_script` are intentionally
105+
// NOT in this tree — they must be looked up relative to
106+
// `CARGO_MANIFEST_DIR`.
107+
set_script_runfiles_env(&script_path, &mut command);
108+
99109
for dep_env_path in input_dep_env_paths.iter() {
100110
if let Ok(contents) = read_to_string(dep_env_path) {
101111
for line in contents.split('\n') {
@@ -313,6 +323,39 @@ fn should_symlink_exec_root() -> bool {
313323
.unwrap_or(false)
314324
}
315325

326+
/// Locate the runfiles materialized for `script_path` and expose them to the
327+
/// build script via the appropriate env var.
328+
///
329+
/// Bazel materializes runfiles for a `FilesToRunProvider` tool either as a
330+
/// `<script>.runfiles/` directory tree, a `<script>.runfiles_manifest` file,
331+
/// or both. Both are checked; if neither is present, no env var is set and
332+
/// the runfiles library falls back to its own heuristics (e.g. argv[0]).
333+
///
334+
/// `RUNFILES_MANIFEST_FILE` inherited from the parent process is cleared so
335+
/// it doesn't shadow what we're exposing.
336+
fn set_script_runfiles_env(script_path: &Path, command: &mut Command) {
337+
command.env_remove("RUNFILES_MANIFEST_FILE");
338+
command.env_remove("RUNFILES_DIR");
339+
340+
let Some(file_name) = script_path.file_name() else {
341+
return;
342+
};
343+
344+
let mut runfiles_dir_name = file_name.to_owned();
345+
runfiles_dir_name.push(".runfiles");
346+
let runfiles_dir = script_path.with_file_name(&runfiles_dir_name);
347+
if runfiles_dir.is_dir() {
348+
command.env("RUNFILES_DIR", &runfiles_dir);
349+
}
350+
351+
let mut runfiles_manifest_name = file_name.to_owned();
352+
runfiles_manifest_name.push(".runfiles_manifest");
353+
let runfiles_manifest = script_path.with_file_name(&runfiles_manifest_name);
354+
if runfiles_manifest.is_file() {
355+
command.env("RUNFILES_MANIFEST_FILE", &runfiles_manifest);
356+
}
357+
}
358+
316359
/// Create a symlink from `link` to `original` if `link` doesn't already exist.
317360
fn symlink_if_not_exists(original: &Path, link: &Path) -> Result<(), String> {
318361
symlink(original, link)

cargo/private/cargo_build_script_runner/cargo_manifest_dir.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ impl RunfilesMaker {
139139
.next()
140140
.unwrap_or_else(|| panic!("Not enough arguments provided."))
141141
.split(',')
142+
.filter(|s| !s.is_empty())
142143
.map(|s| s.to_owned())
143144
.collect::<BTreeSet<String>>();
144145
let runfiles = args
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
load("@bazel_skylib//rules:write_file.bzl", "write_file")
2+
load("//cargo:defs.bzl", "cargo_build_script")
3+
load("//rust:defs.bzl", "rust_test")
4+
5+
# A "tool" file that becomes `data` on the rust_binary backing the build
6+
# script. The build script should be able to locate it via the runfiles
7+
# library (RUNFILES_DIR).
8+
write_file(
9+
name = "tool_txt",
10+
out = "tool.txt",
11+
content = ["this is a tool, locatable via the runfiles library"],
12+
)
13+
14+
# A "data" file that lives in CARGO_MANIFEST_DIR. The build script must NOT
15+
# be able to locate it via the runfiles library; it should be looked up
16+
# relative to CARGO_MANIFEST_DIR like a normal source file.
17+
write_file(
18+
name = "data_txt",
19+
out = "data.txt",
20+
content = ["this is data, locatable only via CARGO_MANIFEST_DIR"],
21+
)
22+
23+
cargo_build_script(
24+
name = "build_rs",
25+
srcs = ["build.rs"],
26+
build_script_env = {
27+
"DATA_RLOCATION": "$(rlocationpath :data.txt)",
28+
"TOOL_RLOCATION": "$(rlocationpath :tool.txt)",
29+
},
30+
data = [":data.txt"],
31+
edition = "2021",
32+
tools = [":tool.txt"],
33+
deps = ["//rust/runfiles"],
34+
)
35+
36+
rust_test(
37+
name = "test",
38+
srcs = ["test.rs"],
39+
edition = "2021",
40+
deps = [":build_rs"],
41+
)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//! A Cargo build script binary used in unit tests for the Bazel
2+
//! `cargo_build_script` rule.
3+
//!
4+
//! Asserts that:
5+
//! 1. Tools (which are `data` of the underlying `rust_binary`) are locatable
6+
//! via the runfiles library (RUNFILES_DIR).
7+
//! 2. Data (which is `data` of `cargo_build_script`) is NOT locatable via
8+
//! the runfiles library.
9+
//! 3. Data IS locatable relative to `CARGO_MANIFEST_DIR`, like a normal
10+
//! source file alongside `build.rs`.
11+
12+
fn main() {
13+
let r = runfiles::Runfiles::create().expect(
14+
"Build scripts should be able to construct a Runfiles object — \
15+
RUNFILES_DIR or RUNFILES_MANIFEST_FILE must be exposed by the build script runner",
16+
);
17+
18+
let tool_rlocation =
19+
std::env::var("TOOL_RLOCATION").expect("TOOL_RLOCATION env var should be set");
20+
let tool_path = r.rlocation(&tool_rlocation).unwrap_or_else(|| {
21+
panic!(
22+
"Tool must be locatable via the runfiles library (rlocation: {})",
23+
tool_rlocation,
24+
)
25+
});
26+
assert!(
27+
tool_path.exists(),
28+
"Tool must exist at the path returned by the runfiles library: {}",
29+
tool_path.display(),
30+
);
31+
32+
let data_rlocation =
33+
std::env::var("DATA_RLOCATION").expect("DATA_RLOCATION env var should be set");
34+
let data_via_runfiles = r.rlocation(&data_rlocation);
35+
if let Some(path) = data_via_runfiles {
36+
assert!(
37+
!path.exists(),
38+
"Data must NOT be locatable via the runfiles library, but found it at {} (rlocation: {})",
39+
path.display(),
40+
data_rlocation,
41+
);
42+
}
43+
44+
let manifest_dir =
45+
std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR should be set");
46+
let data_in_manifest = std::path::Path::new(&manifest_dir).join("data.txt");
47+
assert!(
48+
data_in_manifest.exists(),
49+
"Data must be locatable relative to CARGO_MANIFEST_DIR at {}",
50+
data_in_manifest.display(),
51+
);
52+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//! Triggers execution of the `build.rs` build script. The build script
2+
//! itself contains the assertions; if any fail the action fails and this
3+
//! test fails to build.
4+
5+
#[test]
6+
fn build_script_ran() {}

rust/private/rustc.bzl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,7 @@ def collect_inputs(
651651
stamp = False,
652652
force_depend_on_objects = False,
653653
experimental_use_cc_common_link = False,
654+
include_linker_inputs = False,
654655
include_link_flags = True):
655656
"""Gather's the inputs and required input information for a rustc action
656657
@@ -673,6 +674,7 @@ def collect_inputs(
673674
metadata, even for libraries. This is used in rustdoc tests.
674675
experimental_use_cc_common_link (bool, optional): Whether rules_rust uses cc_common.link to link
675676
rust binaries.
677+
include_linker_inputs (bool, optional): Whether to include linker inputs in transitive dependencies.
676678
include_link_flags (bool, optional): Whether to include flags like `-l` that instruct the linker to search for a library.
677679
678680
Returns:
@@ -713,7 +715,7 @@ def collect_inputs(
713715
# flattened on each transitive rust_library dependency.
714716
libs_from_linker_inputs = []
715717
ambiguous_libs = {}
716-
if crate_info.type not in ("lib", "rlib"):
718+
if crate_info.type not in ("lib", "rlib") or include_linker_inputs:
717719
linker_inputs = dep_info.transitive_noncrates.to_list()
718720
ambiguous_libs = _disambiguate_libs(ctx.actions, toolchain, crate_info, dep_info, use_pic)
719721
libs_from_linker_inputs = _collect_libs_from_linker_inputs(linker_inputs, use_pic) + [
@@ -2634,8 +2636,6 @@ def _add_native_link_flags(
26342636
use_direct_link_driver (bool): Whether the linker is a direct driver (e.g. `ld`, `wasm-ld`) vs a wrapper (e.g. `clang`, `gcc`).
26352637
include_link_flags (bool, optional): Whether to include flags like `-l` that instruct the linker to search for a library.
26362638
"""
2637-
if crate_type in ["lib", "rlib"]:
2638-
return
26392639

26402640
use_pic = should_use_pic(
26412641
cc_toolchain = cc_toolchain,

rust/private/rustdoc.bzl

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,18 @@
1515
"""Rules for generating documentation with `rustdoc` for Bazel built crates"""
1616

1717
load("//rust/private:common.bzl", "rust_common")
18+
load("//rust/private:pic_utils.bzl", "should_use_pic")
1819
load("//rust/private:providers.bzl", "LintsInfo")
1920
load("//rust/private:rustc.bzl", "collect_deps", "collect_inputs", "construct_arguments")
20-
load("//rust/private:utils.bzl", "dedent", "find_cc_toolchain", "find_toolchain")
21+
load(
22+
"//rust/private:utils.bzl",
23+
"dedent",
24+
"find_cc_toolchain",
25+
"find_toolchain",
26+
"get_lib_name_default",
27+
"get_lib_name_for_windows",
28+
"get_preferred_artifact",
29+
)
2130

2231
def _strip_crate_info_output(crate_info):
2332
"""Set the CrateInfo.output to None for a given CrateInfo provider.
@@ -122,6 +131,7 @@ def rustdoc_compile_action(
122131
build_info = build_info,
123132
lint_files = lint_files,
124133
force_depend_on_objects = force_depend_on_objects,
134+
include_linker_inputs = is_test or force_depend_on_objects,
125135
include_link_flags = False,
126136
)
127137

@@ -131,6 +141,32 @@ def rustdoc_compile_action(
131141
# arguments expecting to do so.
132142
rustdoc_crate_info = _strip_crate_info_output(crate_info)
133143

144+
# rustdoc does not understand linker flags like -lstatic that
145+
# `include_link_flags` generates. So we manually build flags that only apply
146+
# to rustdoc.
147+
if is_test or force_depend_on_objects:
148+
compilation_mode = ctx.var["COMPILATION_MODE"]
149+
use_pic = should_use_pic(
150+
cc_toolchain = cc_toolchain,
151+
feature_configuration = feature_configuration,
152+
crate_type = crate_info.type,
153+
compilation_mode = compilation_mode,
154+
toolchain = toolchain,
155+
)
156+
for_windows = toolchain.target_abi == "msvc"
157+
get_lib_name = get_lib_name_for_windows if for_windows else get_lib_name_default
158+
for dep in dep_info.transitive_noncrates.to_list():
159+
for lib in dep.libraries:
160+
if not (lib.static_library or lib.pic_static_library):
161+
continue
162+
arg = get_lib_name(get_preferred_artifact(lib, use_pic))
163+
if not for_windows:
164+
arg = "-l" + arg
165+
if type(rustdoc_flags) == "Args":
166+
rustdoc_flags.add("-Clink-arg=%s" % arg)
167+
else:
168+
rustdoc_flags.append("-Clink-arg=%s" % arg)
169+
134170
args, env = construct_arguments(
135171
ctx = ctx,
136172
attr = ctx.attr,

rust/private/rustdoc_test.bzl

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ load("//rust/private:providers.bzl", "CrateInfo")
2020
load("//rust/private:rustdoc.bzl", "rustdoc_compile_action")
2121
load("//rust/private:utils.bzl", "dedent", "find_toolchain", "transform_deps")
2222

23+
def _collect_library_roots(roots, deps):
24+
for dep in deps.to_list():
25+
for lib in dep.libraries:
26+
for artifact in [lib.static_library, lib.pic_static_library]:
27+
if artifact:
28+
roots.append(artifact.root.path)
29+
for input in dep.additional_inputs:
30+
roots.append(input.root.path)
31+
2332
def _construct_writer_arguments(ctx, test_runner, opt_test_params, action, crate_info):
2433
"""Construct arguments and environment variables specific to `rustdoc_test_writer`.
2534
@@ -64,29 +73,23 @@ def _construct_writer_arguments(ctx, test_runner, opt_test_params, action, crate
6473

6574
# Collect and dedupe all of the file roots in a list before appending
6675
# them to args to prevent generating a large amount of identical args
67-
roots = []
68-
root = crate_info.output.root.path
69-
if not root in roots:
70-
roots.append(root)
76+
roots = [crate_info.output.root.path]
7177
for dep in crate_info.deps.to_list() + crate_info.proc_macro_deps.to_list():
7278
dep_crate_info = getattr(dep, "crate_info", None)
7379
dep_dep_info = getattr(dep, "dep_info", None)
80+
dep_cc_info = getattr(dep, "cc_info", None)
7481
if dep_crate_info:
75-
root = dep_crate_info.output.root.path
76-
if not root in roots:
77-
roots.append(root)
82+
roots.append(dep_crate_info.output.root.path)
7883
if dep_dep_info:
7984
for direct_dep in dep_dep_info.direct_crates.to_list():
80-
root = direct_dep.dep.output.root.path
81-
if not root in roots:
82-
roots.append(root)
85+
roots.append(direct_dep.dep.output.root.path)
8386
for transitive_dep in dep_dep_info.transitive_crates.to_list():
84-
root = transitive_dep.output.root.path
85-
if not root in roots:
86-
roots.append(root)
87+
roots.append(transitive_dep.output.root.path)
88+
_collect_library_roots(roots, dep_dep_info.transitive_noncrates)
89+
if dep_cc_info:
90+
_collect_library_roots(roots, dep_cc_info.linking_context.linker_inputs)
8791

88-
for root in roots:
89-
writer_args.add("--strip_substring={}/".format(root))
92+
writer_args.add_all(roots, format_each = "--strip_substring=%s/", uniquify = True)
9093

9194
# Indicate that the rustdoc_test args are over.
9295
writer_args.add("--")

test/unit/rustdoc/rustdoc.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include "test/unit/rustdoc/rustdoc.h"
2+
3+
int rustdoc_native_dep() { return 42; }

0 commit comments

Comments
 (0)