Skip to content

rustc: include proc-macro dep transitive rlibs in proc-macro link inputs#29

Closed
walter-zeromatter wants to merge 81 commits into
hermeticbuild:mainfrom
ZeromatterOSS:fix/proc-macro-dep-transitive-link-inputs
Closed

rustc: include proc-macro dep transitive rlibs in proc-macro link inputs#29
walter-zeromatter wants to merge 81 commits into
hermeticbuild:mainfrom
ZeromatterOSS:fix/proc-macro-dep-transitive-link-inputs

Conversation

@walter-zeromatter

@walter-zeromatter walter-zeromatter commented Jun 24, 2026

Copy link
Copy Markdown

Problem

collect_deps stops normal transitive_crate_outputs propagation at proc-macro dependency edges. That is important because ordinary downstream targets should not inherit implementation rlibs used only to build or load compile-time proc macros.

However, when the crate currently being compiled is itself a proc macro, rustc may need the crate-output closure of its direct proc-macro dependencies available in the sandbox. Under --remote_download_outputs=minimal, a CI worker only materializes files that are declared action inputs, so a hidden proc-macro dependency rlib can be remote-only unless it is declared on that proc-macro compile action.

The original CI failure was reported as a missing .rlib during proc-macro linking. Attempts to reproduce that exact linker-consumption path have been mixed, so this PR should be understood as a scoped Bazel action-input fix for proc-macro crate actions under minimal remote download, not as proof that every hidden proc-macro-chain .rlib is consumed by every downstream linker action.

Behavior

This PR preserves the important split:

  • Hidden proc-macro implementation crates do not become rustc --extern arguments.
  • Hidden proc-macro implementation rlibs do not leak into normal dependents, including bin -> lib_a -> proc_macro -> rlib_R.
  • Metadata and normal library actions still avoid proc-macro-only implementation rlibs.
  • Proc-macro crate actions get the crate-output closure from their direct proc-macro dependencies as declared action inputs.

Fix

Keep DepInfo.transitive_crate_outputs scoped to direct crate outputs plus crate outputs reachable through non-proc-macro dependency chains.

When collecting inputs for the main Rustc action of a proc-macro crate, merge in each direct proc-macro dependency's existing dep_info.transitive_crate_outputs. This avoids persistent provider pollution while declaring the extra inputs only on the proc-macro action that may need them.

Changes

  • rust/private/rustc.bzl: restore the proc-macro edge stop in collect_deps.
  • rust/private/rustc.bzl: add a scoped collect_inputs option used only by the main Rustc path for proc-macro crates.
  • rust/private/providers.bzl: document that transitive_crate_outputs does not recursively cross proc-macro dependency chains.
  • test/unit/proc_macro/leaks_deps/proc_macro_does_not_leak_deps.bzl: add analysis-test coverage for both sides:
    • a proc-macro crate receives a hidden proc-macro dependency rlib as an action input but not a rustc arg;
    • normal binaries and downstream library dependents do not receive proc-macro-only implementation rlibs.
  • test/unit/proc_macro/leaks_deps/cargo_artifact_closure.rs and test/unit/proc_macro/leaks_deps/lib/a_user.rs: add fixtures for the normal-dependent no-leak cases.

The earlier transitive_proc_macro_dep_outputs provider field and recursive proc-macro propagation have been removed.

Validation

Passed locally:

buildifier -mode=check rust/private/providers.bzl rust/private/rustc.bzl test/unit/proc_macro/leaks_deps/proc_macro_does_not_leak_deps.bzl
git diff --check
bazel test //test/unit/proc_macro:proc_macro_does_not_leak_deps_test_suite //test/unit/proc_macro:proc_macro_test_suite --nocache_test_results
bazel test //test/unit/proc_macro:proc_macro_does_not_leak_deps_test_suite --nocache_test_results --remote_download_outputs=minimal

Additional action-shape checks:

  • //test/unit/proc_macro:proc_macro_uses_proc_macro has libproc_macro_dep-*.rlib as a Rustc action input.
  • //test/unit/proc_macro:cargo_artifact_closure has no Rustc action input matching libproc_macro_dep.
  • //test/unit/proc_macro:a_user has no Rustc action input matching libb.

dzbarsky and others added 30 commits June 13, 2026 21:57
Add support for tier 3 targets bpfeb-unknown-none and bpfel-unknown-none
(see

https://github.com/rust-lang/rust/blob/f5e2df7/src/doc/rustc/src/platform-support.md?plain=1#L311-L312).

This is modeled after bazelbuild#3507
and
should probably be updated if/when
bazelbuild/platforms#131 is merged.

(please use rebase merge when landing this as the proper commit message
is in the commit, rather than the PR description)

/cc @avrabe
…hollow rlibs: the RustcMetadata action runs rustc to completion with -Zno-codegen, emitting a .rlib archive. This approach mirrors the one used by buck2 and avoids needing to kill rustc mid-output in order to produce metadata.

While not fixing problems with SVH mismatches when non-determinism, this does simplify the codepath and uses a production tested technique that doesn't have any of the dangers associated with killing the rustc process while it's still active.
Port the sharding wrapper feature from bazelbuild#3774 into the hermeticbuild fork. The implementation wraps rust_test executables when experimental_enable_sharding is set while keeping rustc_compile_action's existing provider-list API for internal and extension callers.

rust_test now scans the returned providers to replace DefaultInfo for the wrapper, so extensions such as prost and wasm-bindgen continue to consume rustc_compile_action without API churn.

Co-authored-by: Brian Duff <bduff@linkedin.com>

Co-authored-by: Codex <noreply@openai.com>
Rustc emits GNU-like Windows staticlibs as lib<crate>.a, but rules_rust was stripping the lib prefix for all Windows non-rlib library outputs. Keep the prefix for staticlib outputs when the target ABI is gnu or gnullvm so declared outputs match rustc.
dzbarsky and others added 18 commits June 13, 2026 22:03
bazelbuild#25)

rust_shared_library links the cc toolchain's C++ runtime
(libstdc++/libc++ and the unwinder) dynamically, so it becomes an
external DT_NEEDED on the produced shared library. When shipping a
self-contained .so to a consumer that uses a different C++ stdlib,
that dynamic dependency is undesirable.

Add a `cc_runtime_linkage` attribute on rust_shared_library
(dynamic|static, default dynamic so existing behavior is unchanged).
When static, the C++ runtime is linked via
cc_toolchain.static_runtime_lib so it is embedded in the shared
library and no libstdc++/libc++/libunwind DT_NEEDED is emitted.

get_cc_toolchain_runtime_libs gains a `linkage` parameter resolved
from the attribute. Non-shared crate types are unchanged.
Add documentation extraction targets for the public cargo and rust Starlark packages. Declare the bazel_features, selects, and cc_debug_helper_bzl dependencies required by those targets.
When building a proc-macro, rustc passes all transitively-reachable rlibs
to the linker (clang++). This includes rlibs that come through proc-macro
dependency chains, even though those rlibs are already embedded in their
parent proc-macro .so files.

Under --remote_download_outputs=minimal, Bazel only downloads declared
action inputs. The missing rlibs cause clang++ to fail with "no such file
or directory" because they weren't declared as action inputs.

Add a new `transitive_proc_macro_dep_outputs` field to DepInfo. Populate
it with the transitive_crate_outputs of proc-macro deps (the ones skipped
by the existing `if not is_proc_macro` guard). In collect_inputs, include
this depset in the action inputs when building a proc-macro target.

Copy link
Copy Markdown

Pushed follow-up 729e90b38 for the nested proc-macro case: proc-macro deps now carry their own transitive_proc_macro_dep_outputs, and there is a regression test for a two-hop proc-macro chain.

Validated with buildifier, targeted proc-macro/transitive-crate-output Bazel tests, bazel build //test/unit/proc_macro:nested_outer_macro, and git diff --check.

@walter-zeromatter walter-zeromatter marked this pull request as ready for review June 24, 2026 23:47
@dzbarsky

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 729e90b38c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rust/private/rustc.bzl Outdated
transitive_crate_outputs.append(dep_info.transitive_crate_outputs)
else:
transitive_proc_macro_dep_outputs.append(dep_info.transitive_crate_outputs)
transitive_proc_macro_dep_outputs.append(dep_info.transitive_proc_macro_dep_outputs)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve older DepInfo producers

DepInfo is part of the public rust_common API for custom rules, so existing providers constructed before this new field will leave transitive_proc_macro_dep_outputs unset. When such a proc-macro dependency is collected, this appends None into the depset(transitive=...) built below and analysis fails even if the dependency has no nested proc-macro outputs; treat a missing/None value as an empty depset before appending it.

Useful? React with 👍 / 👎.

@walter-zeromatter

Copy link
Copy Markdown
Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7aee4c9473

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rust/private/rustc.bzl Outdated
direct_crate_outputs.append(crate_info.output)
if not is_proc_macro:
transitive_crate_outputs.append(dep_info.transitive_crate_outputs)
transitive_crate_outputs.append(dep_info.transitive_crate_outputs)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep proc-macro-only rlibs out of normal dependents

For a chain like bin -> lib_a where lib_a only uses proc-macro M at compile time and M -> rlib_R, this line now stores rlib_R in lib_a's transitive_crate_outputs. Any downstream non-proc-macro target that depends on lib_a consumes that depset as action inputs, so hidden exec-configured macro implementation rlibs are built/downloaded and included in action keys even though they are not command-line deps and are not needed after lib_a was compiled. Track these proc-macro-closure outputs separately and merge them only for the proc-macro crate action that actually needs them.

Useful? React with 👍 / 👎.

@dzbarsky

dzbarsky commented Jul 9, 2026

Copy link
Copy Markdown
Member

Chatted offline - we think this is a bug in the remote executor

@dzbarsky dzbarsky closed this Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants