Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion rust/private/rustc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,27 @@ def _will_emit_object_file(emit):
def _remove_codegen_units(flag):
return None if flag.startswith("-Ccodegen-units") else flag

def _should_add_oso_prefix(toolchain):
"""Whether to add -oso_prefix to strip absolute paths from N_OSO entries.

On macOS, ld64 embeds absolute paths in N_OSO stab entries which breaks
build reproducibility. The -oso_prefix flag strips a prefix from these
entries.

Both Apple's ld64 and lld-macho support -oso_prefix. For indirect linker
drivers (cc/clang), the flag is passed as -Wl,-oso_prefix,<prefix>.

Args:
toolchain (rust_toolchain): The current Rust toolchain.

Returns:
bool: True if -oso_prefix should be added.
"""
if not toolchain.target_os.startswith(("mac", "darwin", "ios")):
return False

return True

def construct_arguments(
*,
ctx,
Expand Down Expand Up @@ -939,7 +960,9 @@ def construct_arguments(
include_link_flags (bool, optional): Whether to include flags like `-l` that instruct the linker to search for a library.
stamp (bool, optional): Whether or not workspace status stamping is enabled. For more details see
https://docs.bazel.build/versions/main/user-manual.html#flag--stamp
remap_path_prefix (str, optional): A value used to remap `${pwd}` to. If set to None, no prefix will be set.
remap_path_prefix (str, optional): A value used to remap `${pwd}`, `${exec_root}`, and `${output_base}` to.
If set to None, no remapping will be applied. On macOS, also adds `-oso_prefix` to strip absolute paths
from N_OSO linker entries.
use_json_output (bool): Have rustc emit json and process_wrapper parse json messages to output rendered output.
build_metadata (bool): Generate CLI arguments for building *only* .rmeta files. This requires use_json_output.
force_depend_on_objects (bool): Force using `.rlib` object files instead of metadata (`.rmeta`) files even if they are available.
Expand Down Expand Up @@ -989,6 +1012,8 @@ def construct_arguments(
# Since we cannot get the `exec_root` from starlark, we cheat a little and
# use `${pwd}` which resolves the `exec_root` at action execution time.
process_wrapper_flags.add("--subst", "pwd=${pwd}")
process_wrapper_flags.add("--subst", "exec_root=${exec_root}")
process_wrapper_flags.add("--subst", "output_base=${output_base}")

# If stamping is enabled, enable the functionality in the process wrapper
if stamp:
Expand Down Expand Up @@ -1076,6 +1101,8 @@ def construct_arguments(
# For determinism to help with build distribution and such
if remap_path_prefix != None:
rustc_flags.add("--remap-path-prefix=${{pwd}}={}".format(remap_path_prefix))
rustc_flags.add("--remap-path-prefix=${{exec_root}}={}".format(remap_path_prefix))
rustc_flags.add("--remap-path-prefix=${{output_base}}={}".format(remap_path_prefix))

emit_without_paths = []
for kind in emit:
Expand Down Expand Up @@ -1140,6 +1167,15 @@ def construct_arguments(
# Additional context: https://github.com/rust-lang/rust/pull/36574
rustc_flags.add_all(link_args, format_each = "--codegen=link-arg=%s")

if remap_path_prefix != None and _should_add_oso_prefix(
toolchain,
):
if ld_is_direct_driver:
rustc_flags.add("--codegen=link-arg=-oso_prefix")
rustc_flags.add("${pwd}/", format = "--codegen=link-arg=%s")
else:
rustc_flags.add("--codegen=link-arg=-Wl,-oso_prefix,${pwd}/")

_add_native_link_flags(
rustc_flags,
dep_info,
Expand Down
5 changes: 5 additions & 0 deletions test/unit/remap_path_prefix/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_test")
load(":debug_transition.bzl", "dbg_rust_binary")
load(":remap_path_prefix_test.bzl", "remap_path_prefix_test_suite")

rust_library(
name = "dep",
Expand Down Expand Up @@ -35,3 +36,7 @@ rust_test(
},
deps = ["//rust/runfiles"],
)

remap_path_prefix_test_suite(
name = "remap_path_prefix_test_suite",
)
4 changes: 2 additions & 2 deletions test/unit/remap_path_prefix/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ fn test_backtrace() {
let mut check_next = false;
for line in stderr.split('\n') {
if check_next {
if !line.contains("./test/unit/remap_path_prefix/panic_bin.rs:6:5") {
panic!("Expected line to contain ./test/unit/remap_path_prefix/panic_bin.rs:6:5 but was {}", line);
if !line.contains("test/unit/remap_path_prefix/panic_bin.rs:6:5") {
panic!("Expected line to contain test/unit/remap_path_prefix/panic_bin.rs:6:5 but was {}", line);
}
return;
}
Expand Down
110 changes: 110 additions & 0 deletions test/unit/remap_path_prefix/remap_path_prefix_test.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"""Analysis tests verifying remap_path_prefix flags."""

load("@bazel_skylib//lib:unittest.bzl", "analysistest")
load("@bazel_skylib//rules:write_file.bzl", "write_file")
load("//rust:defs.bzl", "rust_binary", "rust_library")
load(
"//test/unit:common.bzl",
"assert_action_mnemonic",
"assert_argv_contains",
"assert_list_contains_adjacent_elements",
)

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

action = target.actions[0]
assert_action_mnemonic(env, action, "Rustc")

assert_argv_contains(env, action, "--remap-path-prefix=${pwd}=.")
assert_argv_contains(env, action, "--remap-path-prefix=${exec_root}=.")
assert_argv_contains(env, action, "--remap-path-prefix=${output_base}=.")

return analysistest.end(env)

_remap_path_prefix_test = analysistest.make(_remap_path_prefix_test_impl)

def _subst_flags_test_impl(ctx):
"""Verify that process wrapper --subst flags are present."""
env = analysistest.begin(ctx)
target = analysistest.target_under_test(env)

action = target.actions[0]
assert_action_mnemonic(env, action, "Rustc")

assert_list_contains_adjacent_elements(env, action.argv, ["--subst", "pwd=${pwd}"])
assert_list_contains_adjacent_elements(env, action.argv, ["--subst", "exec_root=${exec_root}"])
assert_list_contains_adjacent_elements(env, action.argv, ["--subst", "output_base=${output_base}"])

return analysistest.end(env)

_subst_flags_test = analysistest.make(_subst_flags_test_impl)

def remap_path_prefix_test_suite(name):
"""Entry-point macro called from the BUILD file.

Args:
name (str): The name of the test suite.
"""
write_file(
name = "remap_lib_src",
out = "remap_lib.rs",
content = [
"pub fn hello() {}",
"",
],
)

rust_library(
name = "remap_lib",
srcs = [":remap_lib.rs"],
edition = "2021",
)

write_file(
name = "remap_bin_src",
out = "remap_bin.rs",
content = [
"fn main() {}",
"",
],
)

rust_binary(
name = "remap_bin",
srcs = [":remap_bin.rs"],
edition = "2021",
)

_remap_path_prefix_test(
name = "remap_path_prefix_lib_test",
target_under_test = ":remap_lib",
)

_remap_path_prefix_test(
name = "remap_path_prefix_bin_test",
target_under_test = ":remap_bin",
)

_subst_flags_test(
name = "subst_flags_lib_test",
target_under_test = ":remap_lib",
)

_subst_flags_test(
name = "subst_flags_bin_test",
target_under_test = ":remap_bin",
)

tests = [
":remap_path_prefix_lib_test",
":remap_path_prefix_bin_test",
":subst_flags_lib_test",
":subst_flags_bin_test",
]

native.test_suite(
name = name,
tests = tests,
)
34 changes: 6 additions & 28 deletions util/process_wrapper/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,32 +1,7 @@
load("@bazel_skylib//lib:selects.bzl", "selects")

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

config_setting(
name = "compilation_mode_opt",
values = {"compilation_mode": "opt"},
)

selects.config_setting_group(
name = "opt_linux",
match_all = [
":compilation_mode_opt",
"@platforms//os:linux",
],
visibility = ["@rules_rust_tinyjson//:__pkg__"],
)

selects.config_setting_group(
name = "opt_macos",
match_all = [
":compilation_mode_opt",
"@platforms//os:macos",
],
visibility = ["@rules_rust_tinyjson//:__pkg__"],
)

rust_binary_without_process_wrapper(
name = "process_wrapper",
srcs = glob(["*.rs"]),
Expand All @@ -37,10 +12,13 @@ rust_binary_without_process_wrapper(
edition = "2018",
# To ensure the process wrapper is produced deterministically
# debug info, which is known to sometimes have host specific
# paths embedded in this section, is stripped out.
# paths embedded in this section, is stripped out. On macOS,
# full symbol stripping is needed to also remove N_OSO stab
# entries whose timestamps vary between builds.
rustc_flags = select({
":opt_linux": ["-Cstrip=debuginfo"],
":opt_macos": ["-Cstrip=debuginfo"],
"@platforms//os:linux": ["-Cstrip=debuginfo"],
"@platforms//os:macos": ["-Cstrip=symbols"],
"@platforms//os:windows": ["-Cstrip=debuginfo"],
"//conditions:default": [],
}),
visibility = ["//visibility:public"],
Expand Down
5 changes: 3 additions & 2 deletions util/process_wrapper/BUILD.tinyjson.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ rust_library_without_process_wrapper(
# debug info, which is known to sometimes have host specific
# paths embedded in this section, is stripped out.
rustc_flags = select({
"@rules_rust//util/process_wrapper:opt_linux": ["-Cstrip=debuginfo"],
"@rules_rust//util/process_wrapper:opt_macos": ["-Cstrip=debuginfo"],
"@platforms//os:linux": ["-Cstrip=debuginfo"],
"@platforms//os:macos": ["-Cstrip=debuginfo"],
"@platforms//os:windows": ["-Cstrip=debuginfo"],
"//conditions:default": [],
}),
visibility = ["@rules_rust//util/process_wrapper:__pkg__"],
Expand Down
32 changes: 32 additions & 0 deletions util/process_wrapper/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,34 @@ pub(crate) fn options() -> Result<Options, OptionError> {
.to_str()
.ok_or_else(|| OptionError::Generic("current directory not utf-8".to_owned()))?
.to_owned();
let output_base = {
let external = std::path::Path::new(&current_dir).join("external");
match std::fs::canonicalize(external) {
Ok(canonical) => canonical
.parent()
.and_then(|p| p.to_str())
.unwrap_or(&current_dir)
.to_owned(),
Err(_) => match std::fs::canonicalize(&current_dir) {
Ok(canonical) => canonical
.parent()
.and_then(|p| p.parent())
.and_then(|p| p.to_str())
.unwrap_or(&current_dir)
.to_owned(),
Err(_) => current_dir.clone(),
},
}
};

let exec_root = {
let workspace_name = std::path::Path::new(&current_dir)
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("_main");
format!("{}/execroot/{}", output_base, workspace_name)
};

let subst_mappings = subst_mapping_raw
.unwrap_or_default()
.into_iter()
Expand All @@ -146,6 +174,10 @@ pub(crate) fn options() -> Result<Options, OptionError> {
})?;
let v = if val == "${pwd}" {
current_dir.as_str()
} else if val == "${output_base}" {
output_base.as_str()
} else if val == "${exec_root}" {
exec_root.as_str()
} else {
val
}
Expand Down
9 changes: 9 additions & 0 deletions util/process_wrapper/private/process_wrapper.bat
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ SET command=%*
:: Resolve the `${pwd}` placeholders
SET command=!command:${pwd}=%CD%!

:: Resolve the `${output_base}` and `${exec_root}` placeholders.
:: The external directory is a junction/symlink to output_base\external.
:: This mirrors the logic in options.rs used by the real process wrapper.
FOR /F "delims=" %%i IN ('cd external\.. ^& cd') DO SET output_base=%%i
FOR %%i IN ("%CD%") DO SET workspace_name=%%~nxi
SET exec_root=!output_base!\execroot\!workspace_name!
SET command=!command:${output_base}=%output_base%!
SET command=!command:${exec_root}=%exec_root%!

:: Strip out the leading `--` argument.
SET command=!command:~3!

Expand Down
30 changes: 28 additions & 2 deletions util/process_wrapper/private/process_wrapper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,39 @@ set -eu
# Skip the first argument which is expected to be `--`
shift

# Derive output_base and exec_root so we can expand their placeholders
# in rustc flags (e.g. --remap-path-prefix, -oso_prefix). This mirrors
# the logic in options.rs used by the real process wrapper.
phys_pwd=$(cd -P . && pwd)
if [ -d "external" ]; then
output_base=$(cd -P external/.. && pwd)
else
output_base="${phys_pwd%/*}"
output_base="${output_base%/*}"
fi
workspace_name="${phys_pwd##*/}"
exec_root="${output_base}/execroot/${workspace_name}"

for arg in "$@"; do
case "$arg" in
*'${pwd}'*)
# Split on '${pwd}' and rejoin with the actual PWD value
prefix="${arg%%\$\{pwd\}*}"
suffix="${arg#*\$\{pwd\}}"
arg="${prefix}${PWD}${suffix}"
arg="${prefix}${phys_pwd}${suffix}"
;;
esac
case "$arg" in
*'${output_base}'*)
prefix="${arg%%\$\{output_base\}*}"
suffix="${arg#*\$\{output_base\}}"
arg="${prefix}${output_base}${suffix}"
;;
esac
case "$arg" in
*'${exec_root}'*)
prefix="${arg%%\$\{exec_root\}*}"
suffix="${arg#*\$\{exec_root\}}"
arg="${prefix}${exec_root}${suffix}"
;;
esac
set -- "$@" "$arg"
Expand Down
Loading