Skip to content

Commit

Permalink
Add flag to refer to a sh_toolchain for process wrapper bootstrap s…
Browse files Browse the repository at this point in the history
…hebangs (#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.
  • Loading branch information
UebelAndre authored Jun 13, 2024
1 parent 9c67294 commit d83c865
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 9 deletions.
7 changes: 7 additions & 0 deletions rust/settings/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,10 @@ incompatible_flag(
build_setting_default = True,
issue = "https://github.com/bazelbuild/rules_rust/issues/2429",
)

# A flag to control whether the shell path from a shell toolchain (`@bazel_tools//tools/sh:toolchain_type`)
# is embedded into the bootstrap process wrapper for the `.sh` file.
bool_flag(
name = "experimental_use_sh_toolchain_for_bootstrap_process_wrapper",
build_setting_default = False,
)
12 changes: 12 additions & 0 deletions test/process_wrapper_bootstrap/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
load("//rust:defs.bzl", "rust_test")
load(":process_wrapper_bootstrap_test.bzl", "process_wrapper_bootstrap_test_suite")

rust_test(
name = "bootstrap_process_wrapper_test",
srcs = ["bootstrap_process_wrapper_test.rs"],
data = ["//util/process_wrapper/private:process_wrapper.sh"],
edition = "2021",
deps = ["//tools/runfiles"],
)

process_wrapper_bootstrap_test_suite(name = "process_wrapper_bootstrap_test_suite")
23 changes: 23 additions & 0 deletions test/process_wrapper_bootstrap/bootstrap_process_wrapper_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//! Tests for the boostrap process wrapper
use std::fs::read_to_string;

use runfiles::Runfiles;

/// Test that the shell process wrapper starts with the expected shebang to
/// avoid breaking the contract with the `bootstrap_process_wrapper` rule.
#[test]
fn test_shebang() {
let rfiles = Runfiles::create().unwrap();

let script = runfiles::rlocation!(
rfiles,
"rules_rust/util/process_wrapper/private/process_wrapper.sh"
);

let content = read_to_string(script).unwrap();
assert!(
content.starts_with("#!/usr/bin/env bash"),
"The shell script does not start with the expected shebang."
)
}
78 changes: 78 additions & 0 deletions test/process_wrapper_bootstrap/process_wrapper_bootstrap_test.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""Starlark unit tests for the bootstrap process wrapper"""

load("@bazel_skylib//lib:unittest.bzl", "analysistest")
load("//test/unit:common.bzl", "assert_action_mnemonic")

def _enable_sh_toolchain_test_impl(ctx):
env = analysistest.begin(ctx)
target = analysistest.target_under_test(env)

if ctx.attr.expected_ext == ".bat":
assert_action_mnemonic(env, target.actions[0], "ExecutableSymlink")
else:
assert_action_mnemonic(env, target.actions[0], "TemplateExpand")

return analysistest.end(env)

_enable_sh_toolchain_test = analysistest.make(
_enable_sh_toolchain_test_impl,
config_settings = {
str(Label("//rust/settings:experimental_use_sh_toolchain_for_bootstrap_process_wrapper")): True,
},
attrs = {
"expected_ext": attr.string(
doc = "The expected extension for the bootstrap script.",
mandatory = True,
values = [
".bat",
".sh",
],
),
},
)

def _disable_sh_toolchain_test_impl(ctx):
env = analysistest.begin(ctx)
target = analysistest.target_under_test(env)

assert_action_mnemonic(env, target.actions[0], "ExecutableSymlink")

return analysistest.end(env)

_disable_sh_toolchain_test = analysistest.make(
_disable_sh_toolchain_test_impl,
config_settings = {
str(Label("//rust/settings:experimental_use_sh_toolchain_for_bootstrap_process_wrapper")): False,
},
)

def process_wrapper_bootstrap_test_suite(name, **kwargs):
"""Entry-point macro called from the BUILD file.
Args:
name (str): Name of the macro.
**kwargs (dict): Additional keyword arguments.
"""

_enable_sh_toolchain_test(
name = "enable_sh_toolchain_test",
target_under_test = Label("//util/process_wrapper:bootstrap_process_wrapper"),
expected_ext = select({
"@platforms//os:windows": ".bat",
"//conditions:default": ".sh",
}),
)

_disable_sh_toolchain_test(
name = "disable_sh_toolchain_test",
target_under_test = Label("//util/process_wrapper:bootstrap_process_wrapper"),
)

native.test_suite(
name = name,
tests = [
":disable_sh_toolchain_test",
":enable_sh_toolchain_test",
],
**kwargs
)
14 changes: 5 additions & 9 deletions util/process_wrapper/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
load("@bazel_skylib//lib:selects.bzl", "selects")
load("@bazel_skylib//rules:native_binary.bzl", "native_binary")
load("//rust:defs.bzl", "rust_test")

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

config_setting(
name = "compilation_mode_opt",
Expand Down Expand Up @@ -52,15 +52,11 @@ rust_test(
edition = "2018",
)

native_binary(
bootstrap_process_wrapper(
name = "bootstrap_process_wrapper",
src = select({
"@platforms//os:windows": "process_wrapper.bat",
"//conditions:default": "process_wrapper.sh",
}),
out = select({
"@platforms//os:windows": "process_wrapper.bat",
"//conditions:default": "process_wrapper.sh",
is_windows = select({
"@platforms//os:windows": True,
"//conditions:default": False,
}),
visibility = ["//visibility:public"],
)
4 changes: 4 additions & 0 deletions util/process_wrapper/private/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
exports_files([
"process_wrapper.sh",
"process_wrapper.bat",
])
73 changes: 73 additions & 0 deletions util/process_wrapper/private/bootstrap_process_wrapper.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""Bootstrap rustc process wrapper"""

load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")

def _bootstrap_process_wrapper_impl_unix(ctx):
output = ctx.actions.declare_file("{}.sh".format(ctx.label.name))

setting = ctx.attr._use_sh_toolchain_for_bootstrap_process_wrapper[BuildSettingInfo].value
sh_toolchain = ctx.toolchains["@bazel_tools//tools/sh:toolchain_type"]
if setting and sh_toolchain:
shebang = "#!{}".format(sh_toolchain.path)
ctx.actions.expand_template(
output = output,
template = ctx.file._bash,
substitutions = {
# Replace the shebang with one constructed from the configured
# shell toolchain.
"#!/usr/bin/env bash": shebang,
},
)
else:
ctx.actions.symlink(
output = output,
target_file = ctx.file._bash,
is_executable = True,
)

return [DefaultInfo(
files = depset([output]),
executable = output,
)]

def _bootstrap_process_wrapper_impl_windows(ctx):
output = ctx.actions.declare_file("{}.bat".format(ctx.label.name))
ctx.actions.symlink(
output = output,
target_file = ctx.file._batch,
is_executable = True,
)

return [DefaultInfo(
files = depset([output]),
executable = output,
)]

def _bootstrap_process_wrapper_impl(ctx):
if ctx.attr.is_windows:
return _bootstrap_process_wrapper_impl_windows(ctx)
return _bootstrap_process_wrapper_impl_unix(ctx)

bootstrap_process_wrapper = rule(
doc = "A rule which produces a bootstrapping script for the rustc process wrapper.",
implementation = _bootstrap_process_wrapper_impl,
attrs = {
"is_windows": attr.bool(
doc = "Indicate whether or not the target platform is windows.",
mandatory = True,
),
"_bash": attr.label(
allow_single_file = True,
default = Label("//util/process_wrapper/private:process_wrapper.sh"),
),
"_batch": attr.label(
allow_single_file = True,
default = Label("//util/process_wrapper/private:process_wrapper.bat"),
),
"_use_sh_toolchain_for_bootstrap_process_wrapper": attr.label(
default = Label("//rust/settings:experimental_use_sh_toolchain_for_bootstrap_process_wrapper"),
),
},
toolchains = [config_common.toolchain_type("@bazel_tools//tools/sh:toolchain_type", mandatory = False)],
executable = True,
)
File renamed without changes.
File renamed without changes.

0 comments on commit d83c865

Please sign in to comment.