Skip to content

Commit 1b19a46

Browse files
jgsogoUebelAndre
andauthoredSep 9, 2023
Handle files from external repos (fix breaking change in Bazel 3.7.0) (#2138)
Fixes #1780 In Bazel 3.7.0, the path returned by `build_file_path` changed (bazelbuild/bazel#12344), it no longer contains the `external/<repo>` prefix for external files. This breaks the expectations of this rule in the [`paths.relativize` function](https://github.com/bazelbuild/bazel-skylib/blob/main/docs/paths_doc.md#pathsrelativize) call. In this PR I prepend the `workspace_root` (`external/<repo>`) to `build_file_path` (`<path/in/the/repo>`), and use `path` (`external/<repo>/path/in/the/repo`) instead of `short_path` (`../<repo>/path/in/the/repo`). For files that are generated in different configurations I need to strip the `root` which contains the `bazel-out/darwin-fastbuild-ST-a1a0f4088294/bin/` that will never appear in the `package_root` path. ~Note.- I will probably need some help to add tests for this change~ I've tried to add one test --------- Co-authored-by: UebelAndre <github@uebelandre.com>
1 parent 1f9b63a commit 1b19a46

File tree

4 files changed

+109
-27
lines changed

4 files changed

+109
-27
lines changed
 

‎rust/private/rust.bzl

+33-27
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,35 @@ def get_edition(attr, toolchain, label):
127127
else:
128128
return toolchain.default_edition
129129

130+
def _symlink_for_non_generated_source(ctx, src_file, package_root):
131+
"""Creates and returns a symlink for non-generated source files.
132+
133+
This rule uses the full path to the source files and the rule directory to compute
134+
the relative paths. This is needed, instead of using `short_path`, because of non-generated
135+
source files in external repositories possibly returning relative paths depending on the
136+
current version of Bazel.
137+
138+
Args:
139+
ctx (struct): The current rule's context.
140+
src_file (File): The source file.
141+
package_root (File): The full path to the directory containing the current rule.
142+
143+
Returns:
144+
File: The created symlink if a non-generated file, or the file itself.
145+
"""
146+
147+
if src_file.is_source or src_file.root.path != ctx.bin_dir.path:
148+
src_short_path = paths.relativize(src_file.path, src_file.root.path)
149+
src_symlink = ctx.actions.declare_file(paths.relativize(src_short_path, package_root))
150+
ctx.actions.symlink(
151+
output = src_symlink,
152+
target_file = src_file,
153+
progress_message = "Creating symlink to source file: {}".format(src_file.path),
154+
)
155+
return src_symlink
156+
else:
157+
return src_file
158+
130159
def _transform_sources(ctx, srcs, crate_root):
131160
"""Creates symlinks of the source files if needed.
132161
@@ -151,36 +180,13 @@ def _transform_sources(ctx, srcs, crate_root):
151180
if not has_generated_sources:
152181
return srcs, crate_root
153182

154-
generated_sources = []
155-
183+
package_root = paths.dirname(paths.join(ctx.label.workspace_root, ctx.build_file_path))
184+
generated_sources = [_symlink_for_non_generated_source(ctx, src, package_root) for src in srcs if src != crate_root]
156185
generated_root = crate_root
157-
package_root = paths.dirname(ctx.build_file_path)
158-
159-
if crate_root and (crate_root.is_source or crate_root.root.path != ctx.bin_dir.path):
160-
generated_root = ctx.actions.declare_file(paths.relativize(crate_root.short_path, package_root))
161-
ctx.actions.symlink(
162-
output = generated_root,
163-
target_file = crate_root,
164-
progress_message = "Creating symlink to source file: {}".format(crate_root.path),
165-
)
166-
if generated_root:
186+
if crate_root:
187+
generated_root = _symlink_for_non_generated_source(ctx, crate_root, package_root)
167188
generated_sources.append(generated_root)
168189

169-
for src in srcs:
170-
# We took care of the crate root above.
171-
if src == crate_root:
172-
continue
173-
if src.is_source or src.root.path != ctx.bin_dir.path:
174-
src_symlink = ctx.actions.declare_file(paths.relativize(src.short_path, package_root))
175-
ctx.actions.symlink(
176-
output = src_symlink,
177-
target_file = src,
178-
progress_message = "Creating symlink to source file: {}".format(src.path),
179-
)
180-
generated_sources.append(src_symlink)
181-
else:
182-
generated_sources.append(src)
183-
184190
return generated_sources, generated_root
185191

186192
def _rust_library_impl(ctx):

‎test/deps.bzl

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
44
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
5+
load("//test/generated_inputs:external_repo.bzl", "generated_inputs_in_external_repo")
56
load("//test/load_arbitrary_tool:load_arbitrary_tool_test.bzl", "load_arbitrary_tool_test")
67
load("//test/unit/toolchain:toolchain_test_utils.bzl", "rules_rust_toolchain_test_target_json_repository")
78

@@ -28,6 +29,8 @@ def rules_rust_test_deps():
2829

2930
load_arbitrary_tool_test()
3031

32+
generated_inputs_in_external_repo()
33+
3134
maybe(
3235
http_archive,
3336
name = "libc",

‎test/generated_inputs/BUILD.bazel

+9
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ rust_library(
5757
crate_root = "lib.rs",
5858
edition = "2018",
5959
tags = ["norustfmt"],
60+
visibility = ["//visibility:public"],
6061
)
6162

6263
rust_library(
@@ -178,3 +179,11 @@ bool_flag(
178179
name = "change_cfg",
179180
build_setting_default = False,
180181
)
182+
183+
rust_test(
184+
name = "generated_inputs_external_repo_test",
185+
# This is regression testing a specific failure case for generated files _not_ in the root
186+
# of an external repository.
187+
crate = "@generated_inputs_in_external_repo//lib:generated_inputs_external_repo",
188+
edition = "2021",
189+
)
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""External repository for `generated_inputs` tests"""
2+
3+
_BUILD_FILE_CONTENT = """
4+
load("@rules_rust//rust:defs.bzl", "rust_library")
5+
load("@bazel_skylib//rules:write_file.bzl", "write_file")
6+
7+
write_file(
8+
name = "generate_src",
9+
out = "src.rs",
10+
content = ["pub fn forty_two() -> i32 { 42 }"],
11+
)
12+
13+
rust_library(
14+
name = "generated_inputs_external_repo",
15+
srcs = [
16+
"lib.rs",
17+
":generate_src",
18+
],
19+
edition = "2021",
20+
visibility = ["//visibility:public"],
21+
)
22+
"""
23+
24+
_LIB_RS_CONTENT = """
25+
mod src;
26+
27+
pub fn forty_two_from_generated_src() -> String {
28+
format!("{}", src::forty_two())
29+
}
30+
31+
#[cfg(test)]
32+
mod test {
33+
#[test]
34+
fn test_forty_two_as_string() {
35+
assert_eq!(super::forty_two_from_generated_src(), "42");
36+
}
37+
}
38+
"""
39+
40+
def _generated_inputs_in_external_repo_impl(repository_ctx):
41+
# Create repository files (not in the root directory)
42+
repo_path = repository_ctx.path("lib")
43+
repository_ctx.file(
44+
"{}/BUILD.bazel".format(repo_path),
45+
content = _BUILD_FILE_CONTENT,
46+
)
47+
repository_ctx.file(
48+
"{}/lib.rs".format(repo_path),
49+
content = _LIB_RS_CONTENT,
50+
)
51+
52+
_generated_inputs_in_external_repo = repository_rule(
53+
implementation = _generated_inputs_in_external_repo_impl,
54+
doc = (
55+
"A test repository rule providing a Rust library using generated sources"
56+
),
57+
)
58+
59+
def generated_inputs_in_external_repo():
60+
"""Define the a test repository with Rust library using generated sources"""
61+
62+
_generated_inputs_in_external_repo(
63+
name = "generated_inputs_in_external_repo",
64+
)

0 commit comments

Comments
 (0)
Please sign in to comment.