Skip to content

Commit ec5fd40

Browse files
authored
Fix issues causing the Rustc process wrapper to be built non-determ… (#2216)
This change aims to ensure the process wrapper is built determinisitically, thus reducing churn in cases where multiple developers share a bazel remote cache. The shared cache is notable as the binaries can be deterministically produced for a single user for a single checkout as some host information is embedded in the compiled binaries. The host information is stored in the `debug-info` section of the executable. This can be resolved by stripping it. The other issue of determinism is `rust_binary_without_process_wrapper` does not resolve `${pwd}` like the process wrapper does, thus stripping sandbox paths from rustc outputs. To solve for this a small bash and batch script was added to account for this behavior. The intent of these scripts is to use __no__ host executables and instead rely on pure bash and batch to accomplish this and keep any actions which use this as hermetic as possible. The following tables were produced on macOS. Note that `rules_rust_2` is simply another checkout of `rules_rust` on the same commit Before the changes in this PR: | path | rules_rust sha256 | rules_rust_2 sha256 | | --- | --- | --- | | bazel-out/darwin_arm64-opt-exec-2B5CBBC6/bin/external/rules_rust_tinyjson/libtinyjson-4031717389.rlib | 60db194b3e98b67cc0702c6b6c48c5bc8fcf7d723f3ece6a7a24a53888158c7e | 3eac0b443ba160e3a1bde3b023f4e953bb9fc9722e430b5ded6dbb723bc2b532 | | bazel-out/darwin_arm64-opt-exec-2B5CBBC6/bin/util/process_wrapper/process_wrapper | 32e7840602c977b76bba0bc39da768d35db3e7a17dbf96e455727be2b2f0151f | 0d248279bbc9b17be5914b41a66759f939ef4da77f8a40a5ce6fa3bf339648ad | After the changes: | path | rules_rust sha256 | rules_rust_2 sha256 | | --- | --- | --- | | bazel-out/darwin_arm64-opt-exec-2B5CBBC6/bin/external/rules_rust_tinyjson/libtinyjson-4031717389.rlib | 07432f5d207da854266ccde212243c67c29907f81a7619434ce9f608b1658d96 | 07432f5d207da854266ccde212243c67c29907f81a7619434ce9f608b1658d96 | | bazel-out/darwin_arm64-opt-exec-2B5CBBC6/bin/util/process_wrapper/process_wrapper | cbf17392338aabfc942d975f95a49a67b731c5e597a7d27e3d9cf4d4a06b8f2c | cbf17392338aabfc942d975f95a49a67b731c5e597a7d27e3d9cf4d4a06b8f2c | closes #2092
1 parent 1557205 commit ec5fd40

File tree

6 files changed

+115
-3
lines changed

6 files changed

+115
-3
lines changed

rust/private/rust.bzl

+7
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,13 @@ def _common_attrs_for_binary_without_process_wrapper(attrs):
10601060
cfg = "exec",
10611061
)
10621062

1063+
new_attr["_bootstrap_process_wrapper"] = attr.label(
1064+
default = Label("//util/process_wrapper:bootstrap_process_wrapper"),
1065+
executable = True,
1066+
allow_single_file = True,
1067+
cfg = "exec",
1068+
)
1069+
10631070
# fix stamp = 0
10641071
new_attr["stamp"] = attr.int(
10651072
doc = dedent("""\

rust/private/rustc.bzl

+5-3
Original file line numberDiff line numberDiff line change
@@ -1300,16 +1300,16 @@ def rustc_compile_action(
13001300
),
13011301
toolchain = "@rules_rust//rust:toolchain_type",
13021302
)
1303-
else:
1303+
elif hasattr(ctx.executable, "_bootstrap_process_wrapper"):
13041304
# Run without process_wrapper
13051305
if build_env_files or build_flags_files or stamp or build_metadata:
13061306
fail("build_env_files, build_flags_files, stamp, build_metadata are not supported when building without process_wrapper")
13071307
ctx.actions.run(
1308-
executable = toolchain.rustc,
1308+
executable = ctx.executable._bootstrap_process_wrapper,
13091309
inputs = compile_inputs,
13101310
outputs = action_outputs,
13111311
env = env,
1312-
arguments = [args.rustc_flags],
1312+
arguments = [args.rustc_path, args.rustc_flags],
13131313
mnemonic = "Rustc",
13141314
progress_message = "Compiling Rust (without process_wrapper) {} {}{} ({} files)".format(
13151315
crate_info.type,
@@ -1319,6 +1319,8 @@ def rustc_compile_action(
13191319
),
13201320
toolchain = "@rules_rust//rust:toolchain_type",
13211321
)
1322+
else:
1323+
fail("No process wrapper was defined for {}".format(ctx.label))
13221324

13231325
if experimental_use_cc_common_link:
13241326
# Wrap the main `.o` file into a compilation output suitable for

util/process_wrapper/BUILD.bazel

+46
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,45 @@
1+
load("@bazel_skylib//lib:selects.bzl", "selects")
2+
load("@bazel_skylib//rules:native_binary.bzl", "native_binary")
13
load("//rust:defs.bzl", "rust_test")
24

35
# buildifier: disable=bzl-visibility
46
load("//rust/private:rust.bzl", "rust_binary_without_process_wrapper")
57

8+
config_setting(
9+
name = "compilation_mode_opt",
10+
values = {"compilation_mode": "opt"},
11+
)
12+
13+
selects.config_setting_group(
14+
name = "opt_linux",
15+
match_all = [
16+
":compilation_mode_opt",
17+
"@platforms//os:linux",
18+
],
19+
visibility = ["@rules_rust_tinyjson//:__pkg__"],
20+
)
21+
22+
selects.config_setting_group(
23+
name = "opt_macos",
24+
match_all = [
25+
":compilation_mode_opt",
26+
"@platforms//os:macos",
27+
],
28+
visibility = ["@rules_rust_tinyjson//:__pkg__"],
29+
)
30+
631
rust_binary_without_process_wrapper(
732
name = "process_wrapper",
833
srcs = glob(["*.rs"]),
934
edition = "2018",
35+
# To ensure the process wrapper is produced deterministically
36+
# debug info, which is known to sometimes have host specific
37+
# paths embedded in this section, is stripped out.
38+
rustc_flags = select({
39+
":opt_linux": ["-Cstrip=debuginfo"],
40+
":opt_macos": ["-Cstrip=debuginfo"],
41+
"//conditions:default": [],
42+
}),
1043
visibility = ["//visibility:public"],
1144
deps = [
1245
"@rules_rust_tinyjson//:tinyjson",
@@ -18,3 +51,16 @@ rust_test(
1851
crate = ":process_wrapper",
1952
edition = "2018",
2053
)
54+
55+
native_binary(
56+
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",
64+
}),
65+
visibility = ["//visibility:public"],
66+
)

util/process_wrapper/BUILD.tinyjson.bazel

+8
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,13 @@ rust_library_without_process_wrapper(
55
name = "tinyjson",
66
srcs = glob(["src/*.rs"]),
77
edition = "2018",
8+
# To ensure the process wrapper is produced deterministically
9+
# debug info, which is known to sometimes have host specific
10+
# paths embedded in this section, is stripped out.
11+
rustc_flags = select({
12+
"@rules_rust//util/process_wrapper:opt_linux": ["-Cstrip=debuginfo"],
13+
"@rules_rust//util/process_wrapper:opt_macos": ["-Cstrip=debuginfo"],
14+
"//conditions:default": [],
15+
}),
816
visibility = ["@rules_rust//util/process_wrapper:__pkg__"],
917
)
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
@ECHO OFF
2+
SETLOCAL enabledelayedexpansion
3+
4+
SET command=%*
5+
6+
:: Resolve the `${pwd}` placeholders
7+
SET command=!command:${pwd}=%CD%!
8+
9+
:: Strip out the leading `--` argument.
10+
SET command=!command:~3!
11+
12+
:: Find the rustc.exe argument and sanitize it's path
13+
for %%A in (%*) do (
14+
SET arg=%%~A
15+
if "!arg:~-9!"=="rustc.exe" (
16+
SET sanitized=!arg:/=\!
17+
18+
SET command=!sanitized! !command:%%~A=!
19+
goto :break
20+
)
21+
)
22+
23+
:break
24+
25+
%command%
26+
27+
:: Capture the exit code of rustc.exe
28+
SET exit_code=!errorlevel!
29+
30+
:: Exit with the same exit code
31+
EXIT /b %exit_code%
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
# Skip the first argument which is expected to be `--`
6+
shift
7+
8+
args=()
9+
10+
for arg in "$@"; do
11+
# Check if the argument contains "${PWD}" and replace it with the actual value of PWD
12+
if [[ "${arg}" == *'${pwd}'* ]]; then
13+
arg="${arg//\$\{pwd\}/$PWD}"
14+
fi
15+
args+=("${arg}")
16+
done
17+
18+
exec "${args[@]}"

0 commit comments

Comments
 (0)