Skip to content

Commit a506b97

Browse files
committed
Rustdoc rules now share a code path with Rustc rules for more consistency
1 parent 0b82149 commit a506b97

File tree

7 files changed

+560
-237
lines changed

7 files changed

+560
-237
lines changed

.bazelci/presubmit.yml

-16
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ default_windows_targets: &default_windows_targets
1515
- "-//test/proto/..."
1616
- "-//tools/rust_analyzer/..."
1717
- "-//test/rustfmt/..."
18-
# rust_doc_test targets are currently broken on windows
19-
# see: https://github.com/bazelbuild/rules_rust/issues/887
20-
- "-//test/chained_direct_deps:mod3_doc_test"
2118
tasks:
2219
ubuntu2004:
2320
build_targets: *default_linux_targets
@@ -33,8 +30,6 @@ tasks:
3330
- "//..."
3431
- "//test/..."
3532
- "-//test/conflicting_deps:conflicting_deps_test"
36-
# rust_doc_test is likely not fully sandboxed
37-
- "-//test/chained_direct_deps:mod3_doc_test"
3833
macos:
3934
build_targets: *default_macos_targets
4035
test_targets: *default_macos_targets
@@ -64,8 +59,6 @@ tasks:
6459
- "..."
6560
- "//test/..."
6661
- "-//test/conflicting_deps:conflicting_deps_test"
67-
# rust_doc_test is likely not fully sandboxed
68-
- "-//test/chained_direct_deps:mod3_doc_test"
6962
build_flags: *aspects_flags
7063
rbe_ubuntu1604_rolling_with_aspects:
7164
name: RBE Rolling Bazel Version With Aspects
@@ -79,8 +72,6 @@ tasks:
7972
- "..."
8073
- "//test/..."
8174
- "-//test/conflicting_deps:conflicting_deps_test"
82-
# rust_doc_test is likely not fully sandboxed
83-
- "-//test/chained_direct_deps:mod3_doc_test"
8475
build_flags: *aspects_flags
8576
soft_fail: yes
8677
bazel: "rolling"
@@ -209,9 +200,6 @@ tasks:
209200
- "//..."
210201
# TODO: This requires an updated `rules_foreign_cc`
211202
- "-//sys/..."
212-
# rust_doc_test is likely not fully sandboxed
213-
- "-//fibonacci:fibonacci_doc_test"
214-
- "-//hello_lib:hello_lib_doc_test"
215203
# See https://github.com/bazelbuild/bazel/issues/9987
216204
- "-//ffi/rust_calling_c:matrix_dylib_test"
217205
# The bindgen rules currently do not work on RBE
@@ -255,10 +243,6 @@ tasks:
255243
- "-//proto/..."
256244
# The wasm rules do not work on windows
257245
- "-//wasm/..."
258-
# rust_doc_test targets are currently broken on windows
259-
# see: https://github.com/bazelbuild/rules_rust/issues/887
260-
- "-//hello_lib:hello_lib_doc_test"
261-
- "-//fibonacci:fibonacci_doc_test"
262246
build_targets: *windows_targets
263247
test_targets: *windows_targets
264248
crate_universe_examples_ubuntu2004:

rust/private/rustc.bzl

+14-5
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,9 @@ def construct_arguments(
460460
build_flags_files,
461461
emit = ["dep-info", "link"],
462462
force_all_deps_direct = False,
463-
stamp = False):
463+
force_link = False,
464+
stamp = False,
465+
remap_path_prefix = "."):
464466
"""Builds an Args object containing common rustc flags
465467
466468
Args:
@@ -482,8 +484,10 @@ def construct_arguments(
482484
emit (list): Values for the --emit flag to rustc.
483485
force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
484486
to the commandline as opposed to -L.
487+
force_link (bool, optional): Whether to add link flags to the command regardless of `emit`.
485488
stamp (bool, optional): Whether or not workspace status stamping is enabled. For more details see
486489
https://docs.bazel.build/versions/main/user-manual.html#flag--stamp
490+
remap_path_prefix (str, optional): A value used to remap `${pwd}` to. If set to a falsey value, no prefix will be set.
487491
488492
Returns:
489493
tuple: A tuple of the following items
@@ -576,9 +580,11 @@ def construct_arguments(
576580
rustc_flags.add("--codegen=debuginfo=" + compilation_mode.debug_info)
577581

578582
# For determinism to help with build distribution and such
579-
rustc_flags.add("--remap-path-prefix=${pwd}=.")
583+
if remap_path_prefix:
584+
rustc_flags.add("--remap-path-prefix=${{pwd}}={}".format(remap_path_prefix))
580585

581-
rustc_flags.add("--emit=" + ",".join(emit_with_paths))
586+
if emit:
587+
rustc_flags.add("--emit=" + ",".join(emit_with_paths))
582588
rustc_flags.add("--color=always")
583589
rustc_flags.add("--target=" + toolchain.target_flag_value)
584590
if hasattr(attr, "crate_features"):
@@ -604,11 +610,14 @@ def construct_arguments(
604610
add_edition_flags(rustc_flags, crate_info)
605611

606612
# Link!
607-
if "link" in emit:
613+
if "link" in emit or force_link:
608614
# Rust's built-in linker can handle linking wasm files. We don't want to attempt to use the cc
609615
# linker since it won't understand.
610616
if toolchain.target_arch != "wasm32":
611-
rpaths = _compute_rpaths(toolchain, output_dir, dep_info)
617+
if output_dir:
618+
rpaths = _compute_rpaths(toolchain, output_dir, dep_info)
619+
else:
620+
rpaths = depset([])
612621
ld, link_args, link_env = get_linker_and_args(ctx, attr, cc_toolchain, feature_configuration, rpaths)
613622
env.update(link_env)
614623
rustc_flags.add("--codegen=linker=" + ld)

rust/private/rustdoc.bzl

+173-57
Original file line numberDiff line numberDiff line change
@@ -15,84 +15,125 @@
1515
"""Rules for generating documentation with `rustdoc` for Bazel built crates"""
1616

1717
load("//rust/private:common.bzl", "rust_common")
18-
load("//rust/private:rustc.bzl", "add_crate_link_flags", "add_edition_flags")
19-
load("//rust/private:utils.bzl", "dedent", "find_toolchain")
18+
load("//rust/private:rustc.bzl", "collect_deps", "collect_inputs", "construct_arguments")
19+
load("//rust/private:utils.bzl", "dedent", "find_cc_toolchain", "find_toolchain")
2020

21-
def _rust_doc_impl(ctx):
22-
"""The implementation of the `rust_doc` rule
21+
def _strip_crate_info_output(crate_info):
22+
"""Set the CrateInfo.output to None for a given CrateInfo provider.
2323
2424
Args:
25-
ctx (ctx): The rule's context object
25+
crate_info (CrateInfo): A provider
26+
27+
Returns:
28+
CrateInfo: A modified CrateInfo provider
2629
"""
30+
return rust_common.create_crate_info(
31+
name = crate_info.name,
32+
type = crate_info.type,
33+
root = crate_info.root,
34+
srcs = crate_info.srcs,
35+
deps = crate_info.deps,
36+
proc_macro_deps = crate_info.proc_macro_deps,
37+
aliases = crate_info.aliases,
38+
# This crate info should have no output
39+
output = None,
40+
edition = crate_info.edition,
41+
rustc_env = crate_info.rustc_env,
42+
is_test = crate_info.is_test,
43+
compile_data = crate_info.compile_data,
44+
)
2745

28-
if ctx.attr.crate and ctx.attr.dep:
29-
fail("{} should only use the `crate` attribute. `dep` is deprecated".format(
30-
ctx.label,
31-
))
46+
def rustdoc_compile_action(
47+
ctx,
48+
toolchain,
49+
crate_info,
50+
output = None,
51+
rustdoc_flags = []):
52+
"""Create a struct of information needed for a `rustdoc` compile action based on crate passed to the rustdoc rule.
3253
33-
crate = ctx.attr.crate or ctx.attr.dep
34-
if not crate:
35-
fail("{} is missing the `crate` attribute".format(ctx.label))
54+
Args:
55+
ctx (ctx): The rule's context object.
56+
toolchain (rust_toolchain): The currently configured `rust_toolchain`.
57+
crate_info (CrateInfo): The provider of the crate passed to a rustdoc rule.
58+
output (File, optional): An optional output a `rustdoc` action is intended to produce.
59+
rustdoc_flags (list, optional): A list of `rustdoc` specific flags.
3660
37-
crate_info = crate[rust_common.crate_info]
38-
dep_info = crate[rust_common.dep_info]
61+
Returns:
62+
struct: A struct of some `ctx.actions.run` arguments.
63+
"""
3964

40-
toolchain = find_toolchain(ctx)
65+
# If an output was provided, ensure it's used in rustdoc arguments
66+
if output:
67+
rustdoc_flags = [
68+
"--output",
69+
output.path,
70+
] + rustdoc_flags
4171

42-
rustdoc_inputs = depset(
43-
[c.output for c in dep_info.transitive_crates.to_list()] +
44-
[toolchain.rust_doc],
45-
transitive = [
46-
crate_info.srcs,
47-
toolchain.rustc_lib.files,
48-
toolchain.rust_lib.files,
49-
],
72+
cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
73+
74+
dep_info, build_info, linkstamps = collect_deps(
75+
label = ctx.label,
76+
deps = crate_info.deps,
77+
proc_macro_deps = crate_info.proc_macro_deps,
78+
aliases = crate_info.aliases,
5079
)
5180

52-
output_dir = ctx.actions.declare_directory(ctx.label.name)
53-
args = ctx.actions.args()
54-
args.add(crate_info.root.path)
55-
args.add("--crate-name", crate_info.name)
56-
args.add("--crate-type", crate_info.type)
57-
if crate_info.type == "proc-macro":
58-
args.add("--extern")
59-
args.add("proc_macro")
60-
args.add("--output", output_dir.path)
61-
add_edition_flags(args, crate_info)
62-
63-
# nb. rustdoc can't do anything with native link flags; we must omit them.
64-
add_crate_link_flags(args, dep_info)
65-
66-
args.add_all(ctx.files.markdown_css, before_each = "--markdown-css")
67-
if ctx.file.html_in_header:
68-
args.add("--html-in-header", ctx.file.html_in_header)
69-
if ctx.file.html_before_content:
70-
args.add("--html-before-content", ctx.file.html_before_content)
71-
if ctx.file.html_after_content:
72-
args.add("--html-after-content", ctx.file.html_after_content)
81+
compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs = collect_inputs(
82+
ctx = ctx,
83+
file = ctx.file,
84+
files = ctx.files,
85+
linkstamps = linkstamps,
86+
toolchain = toolchain,
87+
cc_toolchain = cc_toolchain,
88+
feature_configuration = feature_configuration,
89+
crate_info = crate_info,
90+
dep_info = dep_info,
91+
build_info = build_info,
92+
)
7393

74-
ctx.actions.run(
75-
executable = toolchain.rust_doc,
76-
inputs = rustdoc_inputs,
77-
outputs = [output_dir],
78-
arguments = [args],
79-
mnemonic = "Rustdoc",
80-
progress_message = "Generating rustdoc for {} ({} files)".format(
81-
crate_info.name,
82-
len(crate_info.srcs.to_list()),
83-
),
94+
# Since this crate is not actually producing the output described by the
95+
# given CrateInfo, this attribute needs to be stripped to allow the rest
96+
# of the rustc functionality in `construct_arguments` to avoid generating
97+
# arguments expecting to do so.
98+
rustdoc_crate_info = _strip_crate_info_output(crate_info)
99+
100+
args, env = construct_arguments(
101+
ctx = ctx,
102+
attr = ctx.attr,
103+
file = ctx.file,
104+
toolchain = toolchain,
105+
tool_path = toolchain.rust_doc.path,
106+
cc_toolchain = cc_toolchain,
107+
feature_configuration = feature_configuration,
108+
crate_info = rustdoc_crate_info,
109+
dep_info = dep_info,
110+
linkstamp_outs = linkstamp_outs,
111+
output_hash = None,
112+
rust_flags = rustdoc_flags,
113+
out_dir = out_dir,
114+
build_env_files = build_env_files,
115+
build_flags_files = build_flags_files,
116+
emit = [],
117+
remap_path_prefix = None,
118+
force_link = True,
84119
)
85120

86-
# This rule does nothing without a single-file output, though the directory should've sufficed.
87-
_zip_action(ctx, output_dir, ctx.outputs.rust_doc_zip)
121+
return struct(
122+
executable = ctx.executable._process_wrapper,
123+
inputs = depset([crate_info.output], transitive = [compile_inputs]),
124+
env = env,
125+
arguments = args.all,
126+
tools = [toolchain.rust_doc],
127+
)
88128

89-
def _zip_action(ctx, input_dir, output_zip):
129+
def _zip_action(ctx, input_dir, output_zip, crate_label):
90130
"""Creates an archive of the generated documentation from `rustdoc`
91131
92132
Args:
93133
ctx (ctx): The `rust_doc` rule's context object
94134
input_dir (File): A directory containing the outputs from rustdoc
95135
output_zip (File): The location of the output archive containing generated documentation
136+
crate_label (Label): The label of the crate docs are being generated for.
96137
"""
97138
args = ctx.actions.args()
98139
args.add(ctx.executable._zipper)
@@ -104,9 +145,70 @@ def _zip_action(ctx, input_dir, output_zip):
104145
inputs = [input_dir],
105146
outputs = [output_zip],
106147
arguments = [args],
148+
mnemonic = "RustdocZip",
149+
progress_message = "Creating RustdocZip for {}".format(crate_label),
107150
tools = [ctx.executable._zipper],
108151
)
109152

153+
def _rust_doc_impl(ctx):
154+
"""The implementation of the `rust_doc` rule
155+
156+
Args:
157+
ctx (ctx): The rule's context object
158+
"""
159+
160+
if ctx.attr.crate and ctx.attr.dep:
161+
fail("{} should only use the `crate` attribute. `dep` is deprecated".format(
162+
ctx.label,
163+
))
164+
165+
crate = ctx.attr.crate or ctx.attr.dep
166+
if not crate:
167+
fail("{} is missing the `crate` attribute".format(ctx.label))
168+
169+
crate_info = crate[rust_common.crate_info]
170+
dep_info = crate[rust_common.dep_info]
171+
172+
output_dir = ctx.actions.declare_directory("{}.rustdoc".format(ctx.label.name))
173+
174+
# Add the current crate as an extern for the compile action
175+
rustdoc_flags = [
176+
"--extern",
177+
"{}={}".format(crate_info.name, crate_info.output.path),
178+
]
179+
180+
action = rustdoc_compile_action(
181+
ctx = ctx,
182+
toolchain = find_toolchain(ctx),
183+
crate_info = crate_info,
184+
output = output_dir,
185+
rustdoc_flags = rustdoc_flags,
186+
)
187+
188+
ctx.actions.run(
189+
mnemonic = "Rustdoc",
190+
progress_message = "Generating Rustdoc for {}".format(crate.label),
191+
outputs = [output_dir],
192+
executable = action.executable,
193+
inputs = action.inputs,
194+
env = action.env,
195+
arguments = action.arguments,
196+
tools = action.tools,
197+
)
198+
199+
# This rule does nothing without a single-file output, though the directory should've sufficed.
200+
_zip_action(ctx, output_dir, ctx.outputs.rust_doc_zip, crate.label)
201+
202+
return [
203+
DefaultInfo(
204+
files = depset([ctx.outputs.rust_doc_zip]),
205+
),
206+
OutputGroupInfo(
207+
rustdoc_dir = depset([output_dir]),
208+
rustdoc_zip = depset([ctx.outputs.rust_doc_zip]),
209+
),
210+
]
211+
110212
rust_doc = rule(
111213
doc = dedent("""\
112214
Generates code documentation.
@@ -179,17 +281,31 @@ rust_doc = rule(
179281
doc = "CSS files to include via `<link>` in a rendered Markdown file.",
180282
allow_files = [".css"],
181283
),
284+
"_cc_toolchain": attr.label(
285+
doc = "In order to use find_cpp_toolchain, you must define the '_cc_toolchain' attribute on your rule or aspect.",
286+
default = "@bazel_tools//tools/cpp:current_cc_toolchain",
287+
),
182288
"_dir_zipper": attr.label(
289+
doc = "A tool that orchestrates the creation of zip archives for rustdoc outputs.",
183290
default = Label("//util/dir_zipper"),
184291
cfg = "exec",
185292
executable = True,
186293
),
294+
"_process_wrapper": attr.label(
295+
doc = "A process wrapper for running rustdoc on all platforms",
296+
default = Label("@rules_rust//util/process_wrapper"),
297+
executable = True,
298+
allow_single_file = True,
299+
cfg = "exec",
300+
),
187301
"_zipper": attr.label(
302+
doc = "A Bazel provided tool for creating archives",
188303
default = Label("@bazel_tools//tools/zip:zipper"),
189304
cfg = "exec",
190305
executable = True,
191306
),
192307
},
308+
fragments = ["cpp"],
193309
outputs = {
194310
"rust_doc_zip": "%{name}.zip",
195311
},

0 commit comments

Comments
 (0)