From 98334622bd1d082c645440cbd513a0f38c4f53ae Mon Sep 17 00:00:00 2001 From: Randolf Jung Date: Wed, 23 Sep 2026 22:26:41 -0700 Subject: [PATCH] rust: avoid duplicate build script native link flags When a binary depends directly on its package library and build script, the library already owns the native library flags. Keep those flags for libraries and binaries without a direct library. Assisted-by: OpenAI Codex --- .../duplicate_link_flags/BUILD.bazel | 3 + .../duplicate_link_flags/build.rs | 3 + .../duplicate_link_flags_test.bzl | 79 +++++++++++++++++++ .../duplicate_link_flags/lib.rs | 3 + .../duplicate_link_flags/main.rs | 1 + rust/private/rustc.bzl | 13 ++- 6 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 cargo/tests/cargo_build_script/duplicate_link_flags/BUILD.bazel create mode 100644 cargo/tests/cargo_build_script/duplicate_link_flags/build.rs create mode 100644 cargo/tests/cargo_build_script/duplicate_link_flags/duplicate_link_flags_test.bzl create mode 100644 cargo/tests/cargo_build_script/duplicate_link_flags/lib.rs create mode 100644 cargo/tests/cargo_build_script/duplicate_link_flags/main.rs diff --git a/cargo/tests/cargo_build_script/duplicate_link_flags/BUILD.bazel b/cargo/tests/cargo_build_script/duplicate_link_flags/BUILD.bazel new file mode 100644 index 0000000000..848d2704f2 --- /dev/null +++ b/cargo/tests/cargo_build_script/duplicate_link_flags/BUILD.bazel @@ -0,0 +1,3 @@ +load(":duplicate_link_flags_test.bzl", "duplicate_link_flags_test_suite") + +duplicate_link_flags_test_suite(name = "duplicate_link_flags_test_suite") diff --git a/cargo/tests/cargo_build_script/duplicate_link_flags/build.rs b/cargo/tests/cargo_build_script/duplicate_link_flags/build.rs new file mode 100644 index 0000000000..4233161ec8 --- /dev/null +++ b/cargo/tests/cargo_build_script/duplicate_link_flags/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rustc-link-lib=static=example"); +} diff --git a/cargo/tests/cargo_build_script/duplicate_link_flags/duplicate_link_flags_test.bzl b/cargo/tests/cargo_build_script/duplicate_link_flags/duplicate_link_flags_test.bzl new file mode 100644 index 0000000000..80bddb01c2 --- /dev/null +++ b/cargo/tests/cargo_build_script/duplicate_link_flags/duplicate_link_flags_test.bzl @@ -0,0 +1,79 @@ +"""Check when a package build script's native link flags reach a Rustc action.""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load("//cargo:defs.bzl", "cargo_build_script") +load("//rust:defs.bzl", "rust_binary", "rust_library") + +def _link_flags_test_impl(ctx): + env = analysistest.begin(ctx) + target = analysistest.target_under_test(env) + rustc_actions = [action for action in target.actions if action.mnemonic == "Rustc"] + asserts.equals(env, 1, len(rustc_actions)) + + if rustc_actions: + argv = rustc_actions[0].argv + link_flags = [ + argv[i + 1] + for i in range(len(argv) - 1) + if argv[i] == "--arg-file" and argv[i + 1].endswith("shared_script.linkflags") + ] + asserts.equals(env, ctx.attr.expected_count, len(link_flags)) + + return analysistest.end(env) + +_link_flags_test = analysistest.make( + _link_flags_test_impl, + attrs = {"expected_count": attr.int(mandatory = True)}, +) + +def duplicate_link_flags_test_suite(name): + """Verify a direct library owns its package build script's native flags.""" + cargo_build_script( + name = "shared_script", + srcs = ["build.rs"], + ) + + rust_library( + name = "lib", + srcs = ["lib.rs"], + deps = [":shared_script"], + ) + + rust_binary( + name = "bin_with_lib", + srcs = ["main.rs"], + deps = [":lib", ":shared_script"], + ) + + rust_binary( + name = "bin_without_lib", + srcs = ["main.rs"], + deps = [":shared_script"], + ) + + _link_flags_test( + name = "lib_link_flags_test", + target_under_test = ":lib", + expected_count = 1, + ) + + _link_flags_test( + name = "bin_with_lib_link_flags_test", + target_under_test = ":bin_with_lib", + expected_count = 0, + ) + + _link_flags_test( + name = "bin_without_lib_link_flags_test", + target_under_test = ":bin_without_lib", + expected_count = 1, + ) + + native.test_suite( + name = name, + tests = [ + ":lib_link_flags_test", + ":bin_with_lib_link_flags_test", + ":bin_without_lib_link_flags_test", + ], + ) diff --git a/cargo/tests/cargo_build_script/duplicate_link_flags/lib.rs b/cargo/tests/cargo_build_script/duplicate_link_flags/lib.rs new file mode 100644 index 0000000000..094e370a02 --- /dev/null +++ b/cargo/tests/cargo_build_script/duplicate_link_flags/lib.rs @@ -0,0 +1,3 @@ +pub fn value() -> i32 { + 1 +} diff --git a/cargo/tests/cargo_build_script/duplicate_link_flags/main.rs b/cargo/tests/cargo_build_script/duplicate_link_flags/main.rs new file mode 100644 index 0000000000..f328e4d9d0 --- /dev/null +++ b/cargo/tests/cargo_build_script/duplicate_link_flags/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 1099e35098..4c9f1eb456 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -2576,7 +2576,7 @@ def _process_build_scripts( build_env_file = build_info.rustc_env if build_info.flags: build_flags_files.append(build_info.flags) - if build_info.linker_flags and include_link_flags: + if build_info.linker_flags and include_link_flags and not _build_script_linked_by_library(build_info, dep_info): build_flags_files.append(build_info.linker_flags) direct_inputs.append(build_info.linker_flags) @@ -2600,6 +2600,17 @@ def _process_build_scripts( depset(build_flags_files, transitive = [dep_info.link_search_path_files]), ) +def _build_script_linked_by_library(build_info, dep_info): + # Cargo applies rustc-link-lib to the package library when one exists. + # Binaries that depend on that library already link its native archives. + for crate in dep_info.direct_crates.to_list(): + if crate.dep.type not in ("lib", "rlib", "dylib") or crate.dep.is_test: + continue + for dep in crate.dep.deps.to_list(): + if dep.build_info == build_info and dep.build_info.linker_flags == build_info.linker_flags: + return True + return False + def _compute_rpaths(toolchain, output_dir, dep_info, use_pic, link_std_dylib, output_file = None, workspace_name = ""): """Determine the artifact's rpaths relative to the bazel root for runtime linking of shared libraries.