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
4 changes: 2 additions & 2 deletions extensions/prost/private/prost.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def _compile_rust(
prefix = "lib",
name = crate_name,
lib_hash = output_hash,
extension = ".rmeta",
extension = "_meta.rlib",
)

lib = ctx.actions.declare_file(lib_name)
Expand All @@ -193,7 +193,7 @@ def _compile_rust(
prefix = "lib",
name = crate_name,
lib_hash = output_hash,
extension = ".rmeta",
extension = "_meta.rlib",
)
rmeta = ctx.actions.declare_file(rmeta_name)
rustc_rmeta_output = generate_output_diagnostics(ctx, rmeta)
Expand Down
2 changes: 1 addition & 1 deletion rust/private/clippy.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def rust_clippy_action(ctx, clippy_executable, process_wrapper, crate_info, conf
out_dir = out_dir,
build_env_files = build_env_files,
build_flags_files = build_flags_files,
emit = ["dep-info", "metadata"],
emit = ["metadata"],
skip_expanding_rustc_env = True,
use_json_output = bool(clippy_diagnostics_file),
error_format = error_format,
Expand Down
14 changes: 5 additions & 9 deletions rust/private/rust.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -180,15 +180,11 @@ def _rust_library_common(ctx, crate_type):
crate_type,
disable_pipelining = getattr(ctx.attr, "disable_pipelining", False),
):
# The hollow rlib uses .rlib extension (not .rmeta) so rustc reads it as an
# rlib archive containing lib.rmeta with optimized MIR. It is placed in a
# "_hollow/" subdirectory so the full rlib and hollow rlib never appear in the
# same -Ldependency= search directory (which would cause E0463).
rust_metadata = ctx.actions.declare_file(
"_hollow/" + rust_lib_name[:-len(".rlib")] + "-hollow.rlib",
paths.replace_extension(rust_lib_name, "_meta.rlib"),
sibling = rust_lib,
)
rustc_rmeta_output = generate_output_diagnostics(ctx, rust_metadata)

metadata_supports_pipelining = (
can_use_metadata_for_pipelining(toolchain, crate_type) and
not ctx.attr.disable_pipelining
Expand Down Expand Up @@ -258,7 +254,7 @@ def _rust_binary_impl(ctx):
rustc_rmeta_output = None
if can_build_metadata(toolchain, ctx, ctx.attr.crate_type):
rust_metadata = ctx.actions.declare_file(
paths.replace_extension("lib" + crate_name, ".rmeta"),
paths.replace_extension("lib" + crate_name, "_meta.rlib"),
sibling = output,
)
rustc_rmeta_output = generate_output_diagnostics(ctx, rust_metadata)
Expand Down Expand Up @@ -360,7 +356,7 @@ def _rust_test_impl(ctx):
rustc_rmeta_output = None
if can_build_metadata(toolchain, ctx, crate_type):
rust_metadata = ctx.actions.declare_file(
paths.replace_extension("lib" + crate_name, ".rmeta"),
paths.replace_extension("lib" + crate_name, "_meta.rlib"),
sibling = output,
)
rustc_rmeta_output = generate_output_diagnostics(ctx, rust_metadata)
Expand Down Expand Up @@ -430,7 +426,7 @@ def _rust_test_impl(ctx):
rustc_rmeta_output = None
if can_build_metadata(toolchain, ctx, crate_type):
rust_metadata = ctx.actions.declare_file(
paths.replace_extension("lib" + crate_name, ".rmeta"),
paths.replace_extension("lib" + crate_name, "_meta.rlib"),
sibling = output,
)
rustc_rmeta_output = generate_output_diagnostics(ctx, rust_metadata)
Expand Down
152 changes: 25 additions & 127 deletions rust/private/rustc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@ def _disambiguate_libs(actions, toolchain, crate_info, dep_info, use_pic):
visited_libs[name] = artifact
return ambiguous_libs

def _depend_on_metadata(crate_info, force_depend_on_objects, experimental_use_cc_common_link = False):
def _depend_on_metadata(crate_info, force_depend_on_objects):
"""Determines if we can depend on metadata for this crate.

By default (when pipelining is disabled or when the crate type needs to link against
Expand All @@ -673,34 +673,16 @@ def _depend_on_metadata(crate_info, force_depend_on_objects, experimental_use_cc
In some rare cases, even if both of those conditions are true, we still want to
depend on objects. This is what force_depend_on_objects is.

When experimental_use_cc_common_link is True, bin/cdylib crates also use hollow
rlib deps. The rustc step only emits .o files (no rustc linking), so SVH chain
consistency is sufficient; the actual linking is done by cc_common.link, which
does not check SVH.

Callers are responsible for zeroing out experimental_use_cc_common_link for
exec-platform builds before calling this function (see rustc_compile_action).
Exec-platform binaries (build scripts) must use full rlib deps because their
CcInfo linking contexts may lack a CC toolchain.

Args:
crate_info (CrateInfo): The Crate to determine this for.
force_depend_on_objects (bool): if set we will not depend on metadata.
experimental_use_cc_common_link (bool): if set, bin/cdylib crates also use
hollow rlib deps for SVH consistency. Must already be False for
exec-platform builds when this function is called.

Returns:
Whether we can depend on metadata for this crate.
"""
if force_depend_on_objects:
return False

if experimental_use_cc_common_link and crate_info.type in ("bin", "cdylib"):
# cc_common.link: rustc only emits .o files, so hollow rlib deps are safe and
# keep the SVH chain consistent (avoiding E0460 from nondeterministic proc macros).
return True

return crate_info.type in ("rlib", "lib")

def collect_inputs(
Expand Down Expand Up @@ -788,7 +770,7 @@ def collect_inputs(
linkstamp_outs = []

transitive_crate_outputs = dep_info.transitive_crate_outputs
if _depend_on_metadata(crate_info, force_depend_on_objects, experimental_use_cc_common_link):
if _depend_on_metadata(crate_info, force_depend_on_objects):
transitive_crate_outputs = dep_info.transitive_metadata_outputs

nolinkstamp_compile_direct_inputs = []
Expand Down Expand Up @@ -824,12 +806,6 @@ def collect_inputs(
transitive = [
crate_info.srcs,
transitive_crate_outputs,
# Always include hollow rlibs so they are present in the sandbox for
# -Ldependency= resolution. Binaries and proc-macros compile against full
# rlib --extern deps but need hollow rlibs available for transitive
# dependency resolution when those rlibs were themselves compiled against
# hollow deps. For rlib/lib crates this is a no-op (already included above).
dep_info.transitive_metadata_outputs,
crate_info.compile_data,
dep_info.transitive_proc_macro_data,
toolchain.all_files,
Expand Down Expand Up @@ -925,7 +901,7 @@ def construct_arguments(
out_dir,
build_env_files,
build_flags_files,
emit = ["dep-info", "link"],
emit = ["link"],
force_all_deps_direct = False,
add_flags_for_binary = False,
include_link_flags = True,
Expand All @@ -934,7 +910,6 @@ def construct_arguments(
use_json_output = False,
build_metadata = False,
force_depend_on_objects = False,
experimental_use_cc_common_link = False,
skip_expanding_rustc_env = False,
require_explicit_unstable_features = False,
always_use_param_file = False,
Expand Down Expand Up @@ -1070,14 +1045,11 @@ def construct_arguments(
error_format = "json"

if build_metadata:
if crate_info.type in ("rlib", "lib"):
# Hollow rlib approach (Buck2-style): rustc runs to completion with -Zno-codegen,
# producing a hollow .rlib (metadata only, no object code) via --emit=link=<path>.
# No need to kill rustc — -Zno-codegen skips codegen entirely and exits quickly.
rustc_flags.add("-Zno-codegen")

# else: IDE-only metadata for non-rlib types (bin, proc-macro, etc.): rustc exits
# naturally after writing .rmeta via --emit=dep-info,metadata (no kill needed).
# Build a hollow rlib (metadata-full, Buck2 equivalent) using -Zno-codegen.
# This produces an rlib with metadata but no object code, allowing downstream
# crates to start compiling without waiting for codegen.
# RUSTC_BOOTSTRAP=1 must be set in the action env for this unstable flag.
rustc_flags.add("-Zno-codegen")
if crate_info.rustc_rmeta_output:
process_wrapper_flags.add("--output-file", crate_info.rustc_rmeta_output.path)
elif crate_info.rustc_output:
Expand Down Expand Up @@ -1110,12 +1082,10 @@ def construct_arguments(

emit_without_paths = []
for kind in emit:
if kind == "link" and build_metadata and crate_info.type in ("rlib", "lib") and crate_info.metadata:
# Hollow rlib: direct rustc's link output to the -hollow.rlib path.
# The file has .rlib extension so rustc reads it as an rlib archive
# (with optimized MIR in lib.rmeta). Using a .rmeta path would cause
# E0786 "found invalid metadata files" because rustc parses .rmeta files
# as raw metadata blobs, not rlib archives.
if kind == "link" and build_metadata and crate_info.metadata != None:
# Redirect hollow rlib output to the declared metadata file path,
# since -Zno-codegen --emit=link would otherwise write lib<name>.rlib
# which collides with the full action's output.
rustc_flags.add(crate_info.metadata, format = "--emit=link=%s")
elif kind == "link" and crate_info.type == "bin" and crate_info.output != None:
rustc_flags.add(crate_info.output, format = "--emit=link=%s")
Expand Down Expand Up @@ -1192,7 +1162,7 @@ def construct_arguments(
include_link_flags = include_link_flags,
)

use_metadata = _depend_on_metadata(crate_info, force_depend_on_objects, experimental_use_cc_common_link)
use_metadata = _depend_on_metadata(crate_info, force_depend_on_objects)

# These always need to be added, even if not linking this crate.
add_crate_link_flags(rustc_flags, dep_info, force_all_deps_direct, use_metadata)
Expand Down Expand Up @@ -1361,13 +1331,6 @@ def rustc_compile_action(
rustc_output = crate_info.rustc_output
rustc_rmeta_output = crate_info.rustc_rmeta_output

# Use the hollow rlib approach (Buck2-style) for rlib/lib crate types when a metadata
# action is being created. This always applies for rlib/lib regardless of whether
# pipelining is globally enabled — the hollow rlib is simpler than killing rustc.
# Non-rlib types (bin, proc-macro, etc.) use --emit=dep-info,metadata instead
# (rustc exits naturally after writing .rmeta, no process-wrapper kill needed).
use_hollow_rlib = bool(build_metadata) and crate_info.type in ("rlib", "lib")

# Determine whether to use cc_common.link:
# * either if experimental_use_cc_common_link is 1,
# * or if experimental_use_cc_common_link is -1 and
Expand All @@ -1381,12 +1344,6 @@ def rustc_compile_action(
elif ctx.attr.experimental_use_cc_common_link == -1:
experimental_use_cc_common_link = toolchain._experimental_use_cc_common_link

# Exec-platform binaries (build scripts) skip cc_common.link: exec-configuration
# rlib deps may lack a CC toolchain, causing empty CcInfo linking contexts. They
# use standard rustc linking with full rlib deps instead.
if experimental_use_cc_common_link and is_exec_configuration(ctx):
experimental_use_cc_common_link = False

dep_info, build_info, linkstamps = collect_deps(
deps = deps,
proc_macro_deps = proc_macro_deps,
Expand Down Expand Up @@ -1425,34 +1382,12 @@ def rustc_compile_action(
experimental_use_cc_common_link = experimental_use_cc_common_link,
)

# The main Rustc action uses FULL rlib deps so the full rlib it produces records
# full-rlib SVHs. A downstream binary links against full rlibs; if the Rustc action
# had used hollow rlib deps instead, nondeterministic proc macros could produce
# different SVHs for the hollow vs full rlib, causing E0460 in the binary build.
# The RustcMetadata action still uses hollow rlibs (compile_inputs_for_metadata)
# so it can start before full codegen of its deps completes.
compile_inputs_for_metadata = compile_inputs
if use_hollow_rlib:
compile_inputs, _, _, _, _, _ = collect_inputs(
ctx = ctx,
file = ctx.file,
files = ctx.files,
linkstamps = linkstamps,
toolchain = toolchain,
cc_toolchain = cc_toolchain,
feature_configuration = feature_configuration,
crate_info = crate_info,
dep_info = dep_info,
build_info = build_info,
lint_files = lint_files,
stamp = stamp,
force_depend_on_objects = True,
experimental_use_cc_common_link = experimental_use_cc_common_link,
)
compile_inputs_metadata = compile_inputs

# The main Rustc action emits dep-info and link (the full rlib/binary/cdylib).
# When cc_common linking is enabled, emit a `.o` file instead.
emit = ["dep-info", "link"]
# The types of rustc outputs to emit.
# When cc_common linking is enabled, emit a `.o` file, which is later
# passed to the cc_common.link action.
emit = ["link"]
if experimental_use_cc_common_link:
emit = ["obj"]

Expand Down Expand Up @@ -1487,33 +1422,21 @@ def rustc_compile_action(
force_all_deps_direct = force_all_deps_direct,
stamp = stamp,
use_json_output = bool(build_metadata) or bool(rustc_output) or bool(rustc_rmeta_output),
# Force full rlib --extern deps so the full rlib records full-rlib SVHs.
force_depend_on_objects = use_hollow_rlib,
experimental_use_cc_common_link = experimental_use_cc_common_link,
skip_expanding_rustc_env = skip_expanding_rustc_env,
require_explicit_unstable_features = require_explicit_unstable_features,
always_use_param_file = not ctx.executable._process_wrapper,
)

args_metadata = None
if build_metadata:
if use_hollow_rlib:
# Hollow rlib: emit dep-info and link (directed to the -hollow.rlib path via
# -Zno-codegen). dep-info must be included: it affects the SVH stored in the
# rlib, so both actions must include it to keep SVHs consistent.
metadata_emit = ["dep-info", "link"]
else:
# IDE-only metadata for non-rlib types (bin, proc-macro, etc.): rustc exits
# naturally after writing .rmeta with --emit=dep-info,metadata.
metadata_emit = ["dep-info", "metadata"]
args_metadata, _ = construct_arguments(
ctx = ctx,
attr = attr,
file = ctx.file,
toolchain = toolchain,
tool_path = toolchain.rustc.path,
cc_toolchain = cc_toolchain,
emit = metadata_emit,
emit = ["link"],
feature_configuration = feature_configuration,
crate_info = crate_info,
dep_info = dep_info,
Expand All @@ -1528,7 +1451,6 @@ def rustc_compile_action(
stamp = stamp,
use_json_output = True,
build_metadata = True,
experimental_use_cc_common_link = experimental_use_cc_common_link,
require_explicit_unstable_features = require_explicit_unstable_features,
)

Expand All @@ -1537,11 +1459,10 @@ def rustc_compile_action(
# this is the final list of env vars
env.update(env_from_args)

if use_hollow_rlib:
# Both the metadata action and the full Rustc action must have RUSTC_BOOTSTRAP=1
# for SVH compatibility. RUSTC_BOOTSTRAP=1 changes the crate hash — setting it
# on only one action would cause SVH mismatch even for deterministic crates.
# This enables -Zno-codegen on stable Rust compilers for the metadata action.
if build_metadata:
# RUSTC_BOOTSTRAP=1 is required for -Zno-codegen on stable rustc, and must
# be set on both the metadata and full actions for SVH compatibility (since
# RUSTC_BOOTSTRAP affects the crate hash).
env["RUSTC_BOOTSTRAP"] = "1"

if hasattr(attr, "version") and attr.version != "0.0.0":
Expand Down Expand Up @@ -1614,7 +1535,7 @@ def rustc_compile_action(
if args_metadata:
ctx.actions.run(
executable = ctx.executable._process_wrapper,
inputs = compile_inputs_for_metadata,
inputs = compile_inputs_metadata,
outputs = [build_metadata] + [x for x in [rustc_rmeta_output] if x],
env = env,
arguments = args_metadata.all,
Expand Down Expand Up @@ -2228,14 +2149,9 @@ def add_crate_link_flags(args, dep_info, force_all_deps_direct = False, use_meta
crate_to_link_flags = _crate_to_link_flag_metadata if use_metadata else _crate_to_link_flag
args.add_all(direct_crates, uniquify = True, map_each = crate_to_link_flags)

# Use hollow rlib directories for -Ldependency= when use_metadata=True (rlib/lib)
# so that both --extern= and -Ldependency= point to the same hollow rlib file.
# When use_metadata=False (bins, proc-macros), use full rlib directories; pointing
# to hollow dirs alongside full --extern= args would cause E0463 (ambiguous crate).
get_dirname = _get_crate_dirname_pipelined if use_metadata else _get_crate_dirname
args.add_all(
dep_info.transitive_crates,
map_each = get_dirname,
map_each = _get_crate_dirname,
uniquify = True,
format_each = "-Ldependency=%s",
)
Expand Down Expand Up @@ -2293,24 +2209,6 @@ def _get_crate_dirname(crate):
"""
return crate.output.dirname

def _get_crate_dirname_pipelined(crate):
"""For pipelined compilation: returns the _hollow/ directory for pipelined crates

When a crate supports pipelining and has a hollow rlib in its _hollow/ subdirectory,
pointing -Ldependency= to that subdirectory lets rustc find the hollow rlib (which has
the correct SVH matching downstream metadata). Pointing to the parent directory instead
would expose the full rlib (compiled separately, with a different SVH), causing E0460.

Args:
crate (CrateInfo): A CrateInfo provider from the current rule

Returns:
str: The directory to use for -Ldependency= search.
"""
if crate.metadata and crate.metadata_supports_pipelining:
return crate.metadata.dirname
return crate.output.dirname

def _portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name, for_windows = False, for_darwin = False, flavor_msvc = False):
artifact = get_preferred_artifact(lib, use_pic)
if ambiguous_libs and artifact.path in ambiguous_libs:
Expand Down
2 changes: 1 addition & 1 deletion rust/private/unpretty.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ def _rust_unpretty_aspect_impl(target, ctx):
out_dir = out_dir,
build_env_files = build_env_files,
build_flags_files = build_flags_files,
emit = ["dep-info", "metadata"],
emit = ["metadata"],
skip_expanding_rustc_env = True,
)

Expand Down
Loading
Loading