Skip to content

Commit c11169e

Browse files
committed
Updated //util/launcher to be more commonly usable and updated rustdoc rules to use it
1 parent fe657ed commit c11169e

File tree

4 files changed

+217
-179
lines changed

4 files changed

+217
-179
lines changed

rust/private/rust.bzl

Lines changed: 12 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ load(
2020
"crate_name_from_attr",
2121
"dedent",
2222
"determine_output_hash",
23-
"expand_dict_value_locations",
2423
"find_toolchain",
2524
)
25+
load("//util/launcher:launcher.bzl", "create_launcher")
2626

2727
# TODO(marco): Separate each rule into its own file.
2828

@@ -304,88 +304,6 @@ def _rust_binary_impl(ctx):
304304
),
305305
)
306306

307-
def _create_test_launcher(ctx, toolchain, output, providers):
308-
"""Create a process wrapper to ensure runtime environment variables are defined for the test binary
309-
310-
Args:
311-
ctx (ctx): The rule's context object
312-
toolchain (rust_toolchain): The current rust toolchain
313-
output (File): The output File that will be produced, depends on crate type.
314-
providers (list): Providers from a rust compile action. See `rustc_compile_action`
315-
316-
Returns:
317-
list: A list of providers similar to `rustc_compile_action` but with modified default info
318-
"""
319-
320-
# TODO: It's unclear if the toolchain is in the same configuration as the `_launcher` attribute
321-
# This should be investigated but for now, we generally assume if the target environment is windows,
322-
# the execution environment is windows.
323-
if toolchain.os == "windows":
324-
launcher_filename = ctx.label.name + ".launcher.exe"
325-
else:
326-
launcher_filename = ctx.label.name + ".launcher"
327-
328-
launcher = ctx.actions.declare_file(launcher_filename)
329-
330-
# Because returned executables must be created from the same rule, the
331-
# launcher target is simply symlinked and exposed.
332-
ctx.actions.symlink(
333-
output = launcher,
334-
target_file = ctx.executable._launcher,
335-
is_executable = True,
336-
)
337-
338-
# Get data attribute
339-
data = getattr(ctx.attr, "data", [])
340-
341-
# Expand the environment variables and write them to a file
342-
environ_file = ctx.actions.declare_file(launcher_filename + ".launchfiles/env")
343-
environ = expand_dict_value_locations(
344-
ctx,
345-
getattr(ctx.attr, "env", {}),
346-
data,
347-
)
348-
349-
# Convert the environment variables into a list to be written into a file.
350-
environ_list = []
351-
for key, value in sorted(environ.items()):
352-
environ_list.extend([key, value])
353-
354-
ctx.actions.write(
355-
output = environ_file,
356-
content = "\n".join(environ_list),
357-
)
358-
359-
launcher_files = [environ_file]
360-
361-
# Replace the `DefaultInfo` provider in the returned list
362-
default_info = None
363-
for i in range(len(providers)):
364-
if type(providers[i]) == "DefaultInfo":
365-
default_info = providers[i]
366-
providers.pop(i)
367-
break
368-
369-
if not default_info:
370-
fail("No DefaultInfo provider returned from `rustc_compile_action`")
371-
372-
providers.extend([
373-
DefaultInfo(
374-
files = default_info.files,
375-
runfiles = default_info.default_runfiles.merge(
376-
# The output is now also considered a runfile
377-
ctx.runfiles(files = launcher_files + [output]),
378-
),
379-
executable = launcher,
380-
),
381-
OutputGroupInfo(
382-
launcher_files = depset(launcher_files),
383-
output = depset([output]),
384-
),
385-
])
386-
387-
return providers
388-
389307
def _rust_test_common(ctx, toolchain, output):
390308
"""Builds a Rust test binary.
391309
@@ -452,7 +370,17 @@ def _rust_test_common(ctx, toolchain, output):
452370
rust_flags = ["--test"] if ctx.attr.use_libtest_harness else ["--cfg", "test"],
453371
)
454372

455-
return _create_test_launcher(ctx, toolchain, output, providers)
373+
env = getattr(ctx.attr, "env", {})
374+
data = getattr(ctx.attr, "data", [])
375+
376+
return create_launcher(
377+
ctx = ctx,
378+
toolchain = toolchain,
379+
providers = providers,
380+
env = env,
381+
data = data,
382+
executable = output,
383+
)
456384

457385
def _rust_test_impl(ctx):
458386
"""The implementation of the `rust_test` rule

rust/private/rustdoc_test.bzl

Lines changed: 36 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# buildifier: disable=module-docstring
1616
load("//rust/private:common.bzl", "rust_common")
1717
load("//rust/private:utils.bzl", "find_toolchain", "get_lib_name", "get_preferred_artifact")
18+
load("//util/launcher:launcher.bzl", "create_launcher")
1819

1920
def _rust_doc_test_impl(ctx):
2021
"""The implementation for the `rust_doc_test` rule
@@ -41,11 +42,7 @@ def _rust_doc_test_impl(ctx):
4142

4243
# Construct rustdoc test command, which will be written to a shell script
4344
# to be executed to run the test.
44-
flags = _build_rustdoc_flags(dep_info, crate_info)
45-
if toolchain.os != "windows":
46-
rust_doc_test = _build_rustdoc_test_bash_script(ctx, toolchain, flags, crate_info)
47-
else:
48-
rust_doc_test = _build_rustdoc_test_batch_script(ctx, toolchain, flags, crate_info)
45+
flags = _build_rustdoc_flags(dep_info, crate_info, toolchain)
4946

5047
# The test script compiles the crate and runs it, so it needs both compile and runtime inputs.
5148
compile_inputs = depset(
@@ -61,13 +58,26 @@ def _rust_doc_test_impl(ctx):
6158
],
6259
)
6360

64-
return [DefaultInfo(
65-
runfiles = ctx.runfiles(
66-
files = compile_inputs.to_list(),
67-
collect_data = True,
68-
),
69-
executable = rust_doc_test,
70-
)]
61+
rustdoc = ctx.actions.declare_file(ctx.label.name + toolchain.binary_ext)
62+
ctx.actions.symlink(
63+
output = rustdoc,
64+
target_file = toolchain.rust_doc,
65+
is_executable = True,
66+
)
67+
68+
return create_launcher(
69+
ctx = ctx,
70+
args = [
71+
"--test",
72+
crate_info.root.path,
73+
"--crate-name={}".format(crate_info.name),
74+
] + flags,
75+
toolchain = toolchain,
76+
providers = [DefaultInfo(
77+
runfiles = ctx.runfiles(transitive_files = compile_inputs),
78+
)],
79+
executable = rustdoc,
80+
)
7181

7282
# TODO: Replace with bazel-skylib's `path.dirname`. This requires addressing some dependency issues or
7383
# generating docs will break.
@@ -82,12 +92,13 @@ def _dirname(path_str):
8292
"""
8393
return "/".join(path_str.split("/")[:-1])
8494

85-
def _build_rustdoc_flags(dep_info, crate_info):
95+
def _build_rustdoc_flags(dep_info, crate_info, toolchain):
8696
"""Constructs the rustdoc script used to test `crate`.
8797
8898
Args:
8999
dep_info (DepInfo): The DepInfo provider
90100
crate_info (CrateInfo): The CrateInfo provider
101+
toolchain (rust_toolchain): The curret `rust_toolchain`.
91102
92103
Returns:
93104
list: A list of rustdoc flags (str)
@@ -121,80 +132,6 @@ def _build_rustdoc_flags(dep_info, crate_info):
121132

122133
return link_search_flags + link_flags + edition_flags
123134

124-
_rustdoc_test_bash_script = """\
125-
#!/usr/bin/env bash
126-
127-
set -e;
128-
129-
{rust_doc} --test \\
130-
{crate_root} \\
131-
--crate-name={crate_name} \\
132-
{flags}
133-
"""
134-
135-
def _build_rustdoc_test_bash_script(ctx, toolchain, flags, crate_info):
136-
"""Generates a helper script for executing a rustdoc test for unix systems
137-
138-
Args:
139-
ctx (ctx): The `rust_doc_test` rule's context object
140-
toolchain (ToolchainInfo): A rustdoc toolchain
141-
flags (list): A list of rustdoc flags (str)
142-
crate_info (CrateInfo): The CrateInfo provider
143-
144-
Returns:
145-
File: An executable containing information for a rustdoc test
146-
"""
147-
rust_doc_test = ctx.actions.declare_file(
148-
ctx.label.name + ".sh",
149-
)
150-
ctx.actions.write(
151-
output = rust_doc_test,
152-
content = _rustdoc_test_bash_script.format(
153-
rust_doc = toolchain.rust_doc.short_path,
154-
crate_root = crate_info.root.path,
155-
crate_name = crate_info.name,
156-
# TODO: Should be possible to do this with ctx.actions.Args, but can't seem to get them as a str and into the template.
157-
flags = " \\\n ".join(flags),
158-
),
159-
is_executable = True,
160-
)
161-
return rust_doc_test
162-
163-
_rustdoc_test_batch_script = """\
164-
{rust_doc} --test ^
165-
{crate_root} ^
166-
--crate-name={crate_name} ^
167-
{flags}
168-
"""
169-
170-
def _build_rustdoc_test_batch_script(ctx, toolchain, flags, crate_info):
171-
"""Generates a helper script for executing a rustdoc test for windows systems
172-
173-
Args:
174-
ctx (ctx): The `rust_doc_test` rule's context object
175-
toolchain (ToolchainInfo): A rustdoc toolchain
176-
flags (list): A list of rustdoc flags (str)
177-
crate_info (CrateInfo): The CrateInfo provider
178-
179-
Returns:
180-
File: An executable containing information for a rustdoc test
181-
"""
182-
rust_doc_test = ctx.actions.declare_file(
183-
ctx.label.name + ".bat",
184-
)
185-
ctx.actions.write(
186-
output = rust_doc_test,
187-
content = _rustdoc_test_batch_script.format(
188-
rust_doc = toolchain.rust_doc.short_path.replace("/", "\\"),
189-
crate_root = crate_info.root.path,
190-
crate_name = crate_info.name,
191-
# TODO: Should be possible to do this with ctx.actions.Args, but can't seem to get them as a str and into the template.
192-
flags = " ^\n ".join(flags),
193-
),
194-
is_executable = True,
195-
)
196-
return rust_doc_test
197-
198135
rust_doc_test = rule(
199136
implementation = _rust_doc_test_impl,
200137
attrs = {
@@ -212,10 +149,20 @@ rust_doc_test = rule(
212149
doc = "__deprecated__: use `crate`",
213150
providers = [rust_common.crate_info],
214151
),
152+
"_launcher": attr.label(
153+
executable = True,
154+
default = Label("//util/launcher:launcher"),
155+
cfg = "exec",
156+
doc = (
157+
"A launcher executable for loading environment and argument files passed in via the " +
158+
"`env` attribute and ensuring the variables are set for the underlying test executable."
159+
),
160+
),
215161
},
216-
executable = True,
217162
test = True,
218-
toolchains = [str(Label("//rust:toolchain"))],
163+
toolchains = [
164+
str(Label("//rust:toolchain")),
165+
],
219166
incompatible_use_toolchain_transition = True,
220167
doc = """Runs Rust documentation tests.
221168

0 commit comments

Comments
 (0)