Skip to content
Open
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: 4 additions & 0 deletions crate_universe/src/rendering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,10 @@ impl Renderer {
),
platforms,
),
// Enable configuration trimming for third-party crates to improve cache hit rates.
// This ensures that per_crate_rustc_flag settings (which don't affect third-party
// crates) don't cause unnecessary rebuilds.
skip_per_crate_rustc_flags: true,
srcs: target.srcs.clone(),
tags: {
let mut tags = BTreeSet::from_iter(krate.common_attrs.tags.iter().cloned());
Expand Down
4 changes: 4 additions & 0 deletions crate_universe/src/utils/starlark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ pub(crate) struct CommonAttrs {
pub(crate) rustc_env_files: SelectSet<String>,
#[serde(skip_serializing_if = "SelectList::is_empty")]
pub(crate) rustc_flags: SelectList<String>,
/// Trim per_crate_rustc_flag from configuration to improve cache hit rates.
/// This is set to true for third-party crates generated by crate_universe.
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub(crate) skip_per_crate_rustc_flags: bool,
pub(crate) srcs: Glob,
#[serde(skip_serializing_if = "Set::is_empty")]
pub(crate) tags: Set<String>,
Expand Down
60 changes: 60 additions & 0 deletions rust/private/per_crate_flag_trim.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright 2024 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Transition to trim per_crate_rustc_flag for non-matching targets.

This module provides a configuration trimming mechanism for the
`experimental_per_crate_rustc_flag` setting. When a target has
`skip_per_crate_rustc_flags = True`, this transition clears the setting,
putting the target back into a canonical configuration.

This is useful for third-party crates (e.g., from crate_universe) that will
never match any per-crate flag filter. Without trimming, these crates would
be rebuilt unnecessarily when any per-crate flag is set, even though the
filter doesn't match them.

Usage:
Third-party crate generators (like crate_universe) should set
`skip_per_crate_rustc_flags = True` on generated rust_library targets.
"""

_PER_CRATE_FLAG_SETTING = "@rules_rust//rust/settings:experimental_per_crate_rustc_flag"

def _per_crate_flag_trim_transition_impl(settings, attr):
"""Clear per_crate_rustc_flag for targets marked to skip it.

Args:
settings: A dict of current build settings.
attr: The attributes of the target being configured.

Returns:
A dict with the per_crate_rustc_flag setting (cleared or preserved).
"""
# If this target is marked to skip per-crate flags, clear the setting
# to return it to a canonical configuration
if getattr(attr, "skip_per_crate_rustc_flags", False):
return {
_PER_CRATE_FLAG_SETTING: [],
}

# Otherwise, keep the current value
return {
_PER_CRATE_FLAG_SETTING: settings[_PER_CRATE_FLAG_SETTING],
}

per_crate_flag_trim_transition = transition(
implementation = _per_crate_flag_trim_transition_impl,
inputs = [_PER_CRATE_FLAG_SETTING],
outputs = [_PER_CRATE_FLAG_SETTING],
)
43 changes: 43 additions & 0 deletions rust/private/rust.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ load(
"CrateInfo",
"LintsInfo",
)
load(
":per_crate_flag_trim.bzl",
"per_crate_flag_trim_transition",
)
load(
":rust_allocator_libraries.bzl",
"RUSTC_ALLOCATOR_LIBRARIES_ATTRS",
Expand Down Expand Up @@ -56,6 +60,9 @@ load(

# TODO(marco): Separate each rule into its own file.

# Setting path for per_crate_rustc_flag, used in transition definitions
_PER_CRATE_FLAG_SETTING = "@rules_rust//rust/settings:experimental_per_crate_rustc_flag"

def _assert_no_deprecated_attributes(_ctx):
"""Forces a failure if any deprecated attributes were specified

Expand Down Expand Up @@ -808,6 +815,17 @@ _COMMON_ATTRS = {
doc = "Enable collection of cfg flags with results stored in CrateInfo.cfgs.",
default = Label("//rust/settings:collect_cfgs"),
),
"skip_per_crate_rustc_flags": attr.bool(
doc = dedent("""\
If True, the `experimental_per_crate_rustc_flag` setting is trimmed from this
target's configuration. This puts the target back into a canonical configuration,
improving cache hit rates for targets that would never match any per-crate filter.

This attribute is primarily used by crate_universe for generated third-party crates.
First-party crates should leave this as False (the default).
"""),
default = False,
),
} | RUSTC_ATTRS | RUSTC_ALLOCATOR_LIBRARIES_ATTRS

_PLATFORM_ATTRS = {
Expand Down Expand Up @@ -912,6 +930,7 @@ _RUST_TEST_ATTRS = {
rust_library = rule(
implementation = _rust_library_impl,
provides = COMMON_PROVIDERS,
cfg = per_crate_flag_trim_transition,
attrs = _COMMON_ATTRS | {
"disable_pipelining": attr.bool(
default = False,
Expand All @@ -921,6 +940,9 @@ rust_library = rule(
crates will instead use the `.rlib` file.
"""),
),
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
),
},
fragments = ["cpp"],
toolchains = [
Expand Down Expand Up @@ -994,17 +1016,22 @@ rust_library = rule(
)

def _rust_static_library_transition_impl(settings, attr):
# Trim per_crate_rustc_flag for third-party crates to improve cache hit rates
per_crate_flags = [] if getattr(attr, "skip_per_crate_rustc_flags", False) else settings[_PER_CRATE_FLAG_SETTING]
return {
"//command_line_option:platforms": str(attr.platform) if attr.platform else settings["//command_line_option:platforms"],
_PER_CRATE_FLAG_SETTING: per_crate_flags,
}

_rust_static_library_transition = transition(
implementation = _rust_static_library_transition_impl,
inputs = [
"//command_line_option:platforms",
_PER_CRATE_FLAG_SETTING,
],
outputs = [
"//command_line_option:platforms",
_PER_CRATE_FLAG_SETTING,
],
)

Expand Down Expand Up @@ -1035,17 +1062,22 @@ rust_static_library = rule(
)

def _rust_shared_library_transition_impl(settings, attr):
# Trim per_crate_rustc_flag for third-party crates to improve cache hit rates
per_crate_flags = [] if getattr(attr, "skip_per_crate_rustc_flags", False) else settings[_PER_CRATE_FLAG_SETTING]
return {
"//command_line_option:platforms": str(attr.platform) if attr.platform else settings["//command_line_option:platforms"],
_PER_CRATE_FLAG_SETTING: per_crate_flags,
}

_rust_shared_library_transition = transition(
implementation = _rust_shared_library_transition_impl,
inputs = [
"//command_line_option:platforms",
_PER_CRATE_FLAG_SETTING,
],
outputs = [
"//command_line_option:platforms",
_PER_CRATE_FLAG_SETTING,
],
)

Expand Down Expand Up @@ -1090,6 +1122,7 @@ _proc_macro_dep_transition = transition(
rust_proc_macro = rule(
implementation = _rust_proc_macro_impl,
provides = COMMON_PROVIDERS,
cfg = per_crate_flag_trim_transition,
# Start by copying the common attributes, then override the `deps` attribute
# to apply `_proc_macro_dep_transition`. To add this transition we additionally
# need to declare `_allowlist_function_transition`, see
Expand Down Expand Up @@ -1157,17 +1190,22 @@ _RUST_BINARY_ATTRS = {
} | _EXPERIMENTAL_USE_CC_COMMON_LINK_ATTRS

def _rust_binary_transition_impl(settings, attr):
# Trim per_crate_rustc_flag for third-party crates to improve cache hit rates
per_crate_flags = [] if getattr(attr, "skip_per_crate_rustc_flags", False) else settings[_PER_CRATE_FLAG_SETTING]
return {
"//command_line_option:platforms": str(attr.platform) if attr.platform else settings["//command_line_option:platforms"],
_PER_CRATE_FLAG_SETTING: per_crate_flags,
}

_rust_binary_transition = transition(
implementation = _rust_binary_transition_impl,
inputs = [
"//command_line_option:platforms",
_PER_CRATE_FLAG_SETTING,
],
outputs = [
"//command_line_option:platforms",
_PER_CRATE_FLAG_SETTING,
],
)

Expand Down Expand Up @@ -1404,17 +1442,22 @@ rust_test_without_process_wrapper_test = rule(
)

def _rust_test_transition_impl(settings, attr):
# Trim per_crate_rustc_flag for third-party crates to improve cache hit rates
per_crate_flags = [] if getattr(attr, "skip_per_crate_rustc_flags", False) else settings[_PER_CRATE_FLAG_SETTING]
return {
"//command_line_option:platforms": str(attr.platform) if attr.platform else settings["//command_line_option:platforms"],
_PER_CRATE_FLAG_SETTING: per_crate_flags,
}

_rust_test_transition = transition(
implementation = _rust_test_transition_impl,
inputs = [
"//command_line_option:platforms",
_PER_CRATE_FLAG_SETTING,
],
outputs = [
"//command_line_option:platforms",
_PER_CRATE_FLAG_SETTING,
],
)

Expand Down