Skip to content

Commit d83c865

Browse files
authored
Add flag to refer to a sh_toolchain for process wrapper bootstrap shebangs (#2694)
This introduces the flag `--@rules_rust//rust/settings:experimental_use_sh_toolchain_for_bootstrap_process_wrapper` which can be used to embed the shell path from `@bazel_tools//tools/sh:toolchain_type` in the rustc bootstrap process wrapper.
1 parent 9c67294 commit d83c865

File tree

9 files changed

+202
-9
lines changed

9 files changed

+202
-9
lines changed

rust/settings/BUILD.bazel

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,10 @@ incompatible_flag(
104104
build_setting_default = True,
105105
issue = "https://github.com/bazelbuild/rules_rust/issues/2429",
106106
)
107+
108+
# A flag to control whether the shell path from a shell toolchain (`@bazel_tools//tools/sh:toolchain_type`)
109+
# is embedded into the bootstrap process wrapper for the `.sh` file.
110+
bool_flag(
111+
name = "experimental_use_sh_toolchain_for_bootstrap_process_wrapper",
112+
build_setting_default = False,
113+
)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
load("//rust:defs.bzl", "rust_test")
2+
load(":process_wrapper_bootstrap_test.bzl", "process_wrapper_bootstrap_test_suite")
3+
4+
rust_test(
5+
name = "bootstrap_process_wrapper_test",
6+
srcs = ["bootstrap_process_wrapper_test.rs"],
7+
data = ["//util/process_wrapper/private:process_wrapper.sh"],
8+
edition = "2021",
9+
deps = ["//tools/runfiles"],
10+
)
11+
12+
process_wrapper_bootstrap_test_suite(name = "process_wrapper_bootstrap_test_suite")
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//! Tests for the boostrap process wrapper
2+
3+
use std::fs::read_to_string;
4+
5+
use runfiles::Runfiles;
6+
7+
/// Test that the shell process wrapper starts with the expected shebang to
8+
/// avoid breaking the contract with the `bootstrap_process_wrapper` rule.
9+
#[test]
10+
fn test_shebang() {
11+
let rfiles = Runfiles::create().unwrap();
12+
13+
let script = runfiles::rlocation!(
14+
rfiles,
15+
"rules_rust/util/process_wrapper/private/process_wrapper.sh"
16+
);
17+
18+
let content = read_to_string(script).unwrap();
19+
assert!(
20+
content.starts_with("#!/usr/bin/env bash"),
21+
"The shell script does not start with the expected shebang."
22+
)
23+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""Starlark unit tests for the bootstrap process wrapper"""
2+
3+
load("@bazel_skylib//lib:unittest.bzl", "analysistest")
4+
load("//test/unit:common.bzl", "assert_action_mnemonic")
5+
6+
def _enable_sh_toolchain_test_impl(ctx):
7+
env = analysistest.begin(ctx)
8+
target = analysistest.target_under_test(env)
9+
10+
if ctx.attr.expected_ext == ".bat":
11+
assert_action_mnemonic(env, target.actions[0], "ExecutableSymlink")
12+
else:
13+
assert_action_mnemonic(env, target.actions[0], "TemplateExpand")
14+
15+
return analysistest.end(env)
16+
17+
_enable_sh_toolchain_test = analysistest.make(
18+
_enable_sh_toolchain_test_impl,
19+
config_settings = {
20+
str(Label("//rust/settings:experimental_use_sh_toolchain_for_bootstrap_process_wrapper")): True,
21+
},
22+
attrs = {
23+
"expected_ext": attr.string(
24+
doc = "The expected extension for the bootstrap script.",
25+
mandatory = True,
26+
values = [
27+
".bat",
28+
".sh",
29+
],
30+
),
31+
},
32+
)
33+
34+
def _disable_sh_toolchain_test_impl(ctx):
35+
env = analysistest.begin(ctx)
36+
target = analysistest.target_under_test(env)
37+
38+
assert_action_mnemonic(env, target.actions[0], "ExecutableSymlink")
39+
40+
return analysistest.end(env)
41+
42+
_disable_sh_toolchain_test = analysistest.make(
43+
_disable_sh_toolchain_test_impl,
44+
config_settings = {
45+
str(Label("//rust/settings:experimental_use_sh_toolchain_for_bootstrap_process_wrapper")): False,
46+
},
47+
)
48+
49+
def process_wrapper_bootstrap_test_suite(name, **kwargs):
50+
"""Entry-point macro called from the BUILD file.
51+
52+
Args:
53+
name (str): Name of the macro.
54+
**kwargs (dict): Additional keyword arguments.
55+
"""
56+
57+
_enable_sh_toolchain_test(
58+
name = "enable_sh_toolchain_test",
59+
target_under_test = Label("//util/process_wrapper:bootstrap_process_wrapper"),
60+
expected_ext = select({
61+
"@platforms//os:windows": ".bat",
62+
"//conditions:default": ".sh",
63+
}),
64+
)
65+
66+
_disable_sh_toolchain_test(
67+
name = "disable_sh_toolchain_test",
68+
target_under_test = Label("//util/process_wrapper:bootstrap_process_wrapper"),
69+
)
70+
71+
native.test_suite(
72+
name = name,
73+
tests = [
74+
":disable_sh_toolchain_test",
75+
":enable_sh_toolchain_test",
76+
],
77+
**kwargs
78+
)

util/process_wrapper/BUILD.bazel

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
load("@bazel_skylib//lib:selects.bzl", "selects")
2-
load("@bazel_skylib//rules:native_binary.bzl", "native_binary")
32
load("//rust:defs.bzl", "rust_test")
43

54
# buildifier: disable=bzl-visibility
65
load("//rust/private:rust.bzl", "rust_binary_without_process_wrapper")
6+
load("//util/process_wrapper/private:bootstrap_process_wrapper.bzl", "bootstrap_process_wrapper")
77

88
config_setting(
99
name = "compilation_mode_opt",
@@ -52,15 +52,11 @@ rust_test(
5252
edition = "2018",
5353
)
5454

55-
native_binary(
55+
bootstrap_process_wrapper(
5656
name = "bootstrap_process_wrapper",
57-
src = select({
58-
"@platforms//os:windows": "process_wrapper.bat",
59-
"//conditions:default": "process_wrapper.sh",
60-
}),
61-
out = select({
62-
"@platforms//os:windows": "process_wrapper.bat",
63-
"//conditions:default": "process_wrapper.sh",
57+
is_windows = select({
58+
"@platforms//os:windows": True,
59+
"//conditions:default": False,
6460
}),
6561
visibility = ["//visibility:public"],
6662
)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
exports_files([
2+
"process_wrapper.sh",
3+
"process_wrapper.bat",
4+
])
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
"""Bootstrap rustc process wrapper"""
2+
3+
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
4+
5+
def _bootstrap_process_wrapper_impl_unix(ctx):
6+
output = ctx.actions.declare_file("{}.sh".format(ctx.label.name))
7+
8+
setting = ctx.attr._use_sh_toolchain_for_bootstrap_process_wrapper[BuildSettingInfo].value
9+
sh_toolchain = ctx.toolchains["@bazel_tools//tools/sh:toolchain_type"]
10+
if setting and sh_toolchain:
11+
shebang = "#!{}".format(sh_toolchain.path)
12+
ctx.actions.expand_template(
13+
output = output,
14+
template = ctx.file._bash,
15+
substitutions = {
16+
# Replace the shebang with one constructed from the configured
17+
# shell toolchain.
18+
"#!/usr/bin/env bash": shebang,
19+
},
20+
)
21+
else:
22+
ctx.actions.symlink(
23+
output = output,
24+
target_file = ctx.file._bash,
25+
is_executable = True,
26+
)
27+
28+
return [DefaultInfo(
29+
files = depset([output]),
30+
executable = output,
31+
)]
32+
33+
def _bootstrap_process_wrapper_impl_windows(ctx):
34+
output = ctx.actions.declare_file("{}.bat".format(ctx.label.name))
35+
ctx.actions.symlink(
36+
output = output,
37+
target_file = ctx.file._batch,
38+
is_executable = True,
39+
)
40+
41+
return [DefaultInfo(
42+
files = depset([output]),
43+
executable = output,
44+
)]
45+
46+
def _bootstrap_process_wrapper_impl(ctx):
47+
if ctx.attr.is_windows:
48+
return _bootstrap_process_wrapper_impl_windows(ctx)
49+
return _bootstrap_process_wrapper_impl_unix(ctx)
50+
51+
bootstrap_process_wrapper = rule(
52+
doc = "A rule which produces a bootstrapping script for the rustc process wrapper.",
53+
implementation = _bootstrap_process_wrapper_impl,
54+
attrs = {
55+
"is_windows": attr.bool(
56+
doc = "Indicate whether or not the target platform is windows.",
57+
mandatory = True,
58+
),
59+
"_bash": attr.label(
60+
allow_single_file = True,
61+
default = Label("//util/process_wrapper/private:process_wrapper.sh"),
62+
),
63+
"_batch": attr.label(
64+
allow_single_file = True,
65+
default = Label("//util/process_wrapper/private:process_wrapper.bat"),
66+
),
67+
"_use_sh_toolchain_for_bootstrap_process_wrapper": attr.label(
68+
default = Label("//rust/settings:experimental_use_sh_toolchain_for_bootstrap_process_wrapper"),
69+
),
70+
},
71+
toolchains = [config_common.toolchain_type("@bazel_tools//tools/sh:toolchain_type", mandatory = False)],
72+
executable = True,
73+
)

0 commit comments

Comments
 (0)