Skip to content

Commit

Permalink
Document feature-related toolchain rules
Browse files Browse the repository at this point in the history
BEGIN_PUBLIC

Document feature-related toolchain rules

Extends the toolchain API documentation to cover all feature-related rules.

END_PUBLIC

PiperOrigin-RevId: 681476538
Change-Id: Icfe35e03937bdb0bc55eae24f57eafe0db7b16eb
  • Loading branch information
Googler authored and copybara-github committed Oct 2, 2024
1 parent b06d2f7 commit 1af2140
Show file tree
Hide file tree
Showing 7 changed files with 411 additions and 69 deletions.
120 changes: 64 additions & 56 deletions cc/toolchains/feature.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -110,22 +110,23 @@ While two features with the same `feature_name` may not be bound to the same
toolchain, they can happily live alongside each other in the same BUILD file.
Example:
```
cc_feature(
name = "sysroot_macos",
feature_name = "sysroot",
...
)
cc_feature(
name = "sysroot_macos",
feature_name = "sysroot",
...
)
cc_feature(
name = "sysroot_linux",
feature_name = "sysroot",
...
)
cc_feature(
name = "sysroot_linux",
feature_name = "sysroot",
...
)
```
""",
),
"args": attr.label_list(
doc = """Args that, when expanded, implement this feature.""",
doc = """A list of `cc_args` or `cc_args_list` labels that are expanded when this feature is enabled.""",
providers = [ArgsListInfo],
),
"requires_any_of": attr.label_list(
Expand All @@ -152,7 +153,7 @@ silently disabled.
),
"mutually_exclusive": attr.label_list(
providers = [MutuallyExclusiveCategoryInfo],
doc = """A list of things that this is mutually exclusive with.
doc = """A list of things that this feature is mutually exclusive with.
It can be either:
* A feature, in which case the two features are mutually exclusive.
Expand All @@ -171,14 +172,16 @@ In the example below, if you missed the "overrides" attribute, it would complain
that the feature "opt" was defined twice.
Example:
cc_feature(
name = "opt",
feature_name = "opt",
...
overrides = "@toolchain//features/well_known:opt",
)
```
load("//cc/toolchains:feature.bzl", "cc_feature")
cc_feature(
name = "opt",
feature_name = "opt",
args = [":size_optimized"],
overrides = "//cc/toolchains/features:opt",
)
```
""",
),
},
Expand All @@ -188,53 +191,58 @@ Example:
FeatureConstraintInfo,
MutuallyExclusiveCategoryInfo,
],
doc = """Defines the implemented behavior of a C/C++ toolchain feature.
doc = """A dynamic set of toolchain flags that create a singular [feature](https://bazel.build/docs/cc-toolchain-config-reference#features) definition.
A feature is basically a toggleable list of args. There are a variety of
dependencies and compatibility requirements that must be satisfied for the
listed args to be applied.
A feature is basically a dynamically toggleable `cc_args_list`. There are a variety of
dependencies and compatibility requirements that must be satisfied to enable a
`cc_feature`. Once those conditions are met, the arguments in [`cc_feature.args`](#cc_feature-args)
are expanded and added to the command-line.
A feature may be enabled or disabled through the following mechanisms:
* Via command-line flags, or a `.bazelrc`.
* Through inter-feature relationships (enabling one feature may implicitly
enable another).
* Individual rules may elect to manually enable or disable features through the
builtin `features` attribute.
Because of the toggleable nature of toolchain features, it's generally best to
avoid defining features as part of your toolchain with the following exceptions:
* You want build files to be able to configure compiler flags. For example, a
* Via command-line flags, or a `.bazelrc` file via the
[`--features` flag](https://bazel.build/reference/command-line-reference#flag--features)
* Through inter-feature relationships (via [`cc_feature.implies`](#cc_feature-implies)) where one
feature may implicitly enable another.
* Individual rules (e.g. `cc_library`) or `package` definitions may elect to manually enable or
disable features through the
[`features` attribute](https://bazel.build/reference/be/common-definitions#common.features).
Note that a feature may alternate between enabled and disabled dynamically over the course of a
build. Because of their toggleable nature, it's generally best to avoid adding arguments to a
`cc_toolchain` as a `cc_feature` unless strictly necessary. Instead, prefer to express arguments
via [`cc_toolchain.args`](#cc_toolchain-args) whenever possible.
You should use a `cc_feature` when any of the following apply:
* You need the flags to be dynamically toggled over the course of a build.
* You want build files to be able to configure the flags in question. For example, a
binary might specify `features = ["optimize_for_size"]` to create a small
binary instead of optimizing for performance.
* You need to carry forward Starlark toolchain behaviors. If you're migrating a
complex Starlark-based toolchain definition to these rules, many of the
workflows and flags were likely based on features. This rule exists to support
those existing structures.
workflows and flags were likely based on features.
If you want to be able to configure flags via the bazel command-line, instead
consider making a bool_flag, and then making your `cc_args` `select` on those
flags.
If you only need to configure flags via the Bazel command-line, instead
consider adding a
[`bool_flag`](https://github.com/bazelbuild/bazel-skylib/tree/main/doc/common_settings_doc.md#bool_flag)
paired with a [`config_setting`](https://bazel.build/reference/be/general#config_setting)
and then make your `cc_args` rule `select` on the `config_setting`.
For more details about how Bazel handles features, see the official Bazel
documentation at
https://bazel.build/docs/cc-toolchain-config-reference#features.
Examples:
# A feature that can be easily toggled to optimize for size
cc_feature(
name = "optimize_for_size",
feature_name = "optimize_for_size",
args = [":optimize_for_size_args"],
)
# This feature signals a capability, and doesn't have associated flags.
#
# For a list of well-known features, see:
# https://bazel.build/docs/cc-toolchain-config-reference#wellknown-features
cc_feature(
name = "supports_pic",
overrides = "//cc/toolchains/features:supports_pic
)
Example:
```
load("//cc/toolchains:feature.bzl", "cc_feature")
# A feature that enables LTO, which may be incompatible when doing interop with various
# languages (e.g. rust, go), or may need to be disabled for particular `cc_binary` rules
# for various reasons.
cc_feature(
name = "lto",
feature_name = "lto",
args = [":lto_args"],
)
```
""",
)
24 changes: 21 additions & 3 deletions cc/toolchains/feature_constraint.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,26 @@ cc_feature_constraint = rule(
),
},
provides = [FeatureConstraintInfo],
doc = """Defines a constraint on features.
doc = """Defines a compound relationship between features.
Can be used with require_any_of to specify that something is only enabled when
a constraint is met.""",
This rule can be used with [`cc_args.require_any_of`](#cc_args-require_any_of) to specify that a set
of arguments are only enabled when a constraint is met. Both `all_of` and `none_of` must be
satisfied simultaneously.
This is basically a `cc_feature_set` that supports `none_of` expressions. This extra flexibility
is why this rule may only be used by [`cc_args.require_any_of`](#cc_args-require_any_of).
Example:
```
load("//cc/toolchains:feature_constraint.bzl", "cc_feature_constraint")
# A constraint that requires a `linker_supports_thinlto` feature to be enabled,
# AND a `no_optimization` to be disabled.
cc_feature_constraint(
name = "thinlto_constraint",
all_of = [":linker_supports_thinlto"],
none_of = [":no_optimization"],
)
```
""",
)
20 changes: 13 additions & 7 deletions cc/toolchains/feature_set.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,20 @@ cc_feature_set = rule(
provides = [FeatureSetInfo],
doc = """Defines a set of features.
This may be used by both `cc_feature` and `cc_args` rules, and is effectively a way to express
a logical `AND` operation across multiple requred features.
Example:
```
load("//cc/toolchains:feature_set.bzl", "cc_feature_set")
cc_feature_set(
name = "thin_lto_requirements",
all_of = [
":thin_lto",
":opt",
],
)
cc_feature_set(
name = "thin_lto_requirements",
all_of = [
":thin_lto",
":opt",
],
)
```
""",
)
15 changes: 15 additions & 0 deletions cc/toolchains/impl/documented_api.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@
load("//cc/toolchains:actions.bzl", _cc_action_type = "cc_action_type", _cc_action_type_set = "cc_action_type_set")
load("//cc/toolchains:args.bzl", _cc_args = "cc_args")
load("//cc/toolchains:args_list.bzl", _cc_args_list = "cc_args_list")
load("//cc/toolchains:feature.bzl", _cc_feature = "cc_feature")
load("//cc/toolchains:feature_constraint.bzl", _cc_feature_constraint = "cc_feature_constraint")
load("//cc/toolchains:feature_set.bzl", _cc_feature_set = "cc_feature_set")
load("//cc/toolchains:mutually_exclusive_category.bzl", _cc_mutually_exclusive_category = "cc_mutually_exclusive_category")
load("//cc/toolchains:nested_args.bzl", _cc_nested_args = "cc_nested_args")
load("//cc/toolchains:tool.bzl", _cc_tool = "cc_tool")
load("//cc/toolchains:tool_map.bzl", _cc_tool_map = "cc_tool_map")
load("//cc/toolchains/impl:external_feature.bzl", _cc_external_feature = "cc_external_feature")
load("//cc/toolchains/impl:variables.bzl", _cc_variable = "cc_variable")

cc_tool_map = _cc_tool_map
Expand All @@ -29,6 +34,11 @@ cc_args_list = _cc_args_list
cc_action_type = _cc_action_type
cc_action_type_set = _cc_action_type_set
cc_variable = _cc_variable
cc_feature = _cc_feature
cc_feature_constraint = _cc_feature_constraint
cc_feature_set = _cc_feature_set
cc_mutually_exclusive_category = _cc_mutually_exclusive_category
cc_external_feature = _cc_external_feature

# This list is used to automatically remap instances of `foo` to [`foo`](#foo)
# links in the generated documentation so that maintainers don't need to manually
Expand All @@ -42,4 +52,9 @@ DOCUMENTED_TOOLCHAIN_RULES = [
"cc_action_type",
"cc_action_type_set",
"cc_variable",
"cc_feature",
"cc_feature_constraint",
"cc_feature_set",
"cc_mutually_exclusive_category",
"cc_external_feature",
]
23 changes: 22 additions & 1 deletion cc/toolchains/impl/external_feature.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,26 @@ cc_external_feature = rule(
),
},
provides = [FeatureInfo, FeatureSetInfo, FeatureConstraintInfo],
doc = "A declaration that a feature with this name is defined elsewhere.",
doc = """A declaration that a [feature](https://bazel.build/docs/cc-toolchain-config-reference#features) with this name is defined elsewhere.
This rule communicates that a feature has been defined externally to make it possible to reference
features that live outside the rule-based cc toolchain ecosystem. This allows various toolchain
rules to reference the external feature without accidentally re-defining said feature.
This rule is currently considered a private API of the toolchain rules to encourage the Bazel
ecosystem to migrate to properly defining their features as rules.
Example:
```
load("//cc/toolchains:external_feature.bzl", "cc_external_feature")
# rules_rust defines a feature that is disabled whenever rust artifacts are being linked using
# the cc toolchain to signal that incompatible flags should be disabled as well.
cc_external_feature(
name = "rules_rust_unsupported_feature",
feature_name = "rules_rust_unsupported_feature",
overridable = False,
)
```
""",
)
35 changes: 34 additions & 1 deletion cc/toolchains/mutually_exclusive_category.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,40 @@ def _cc_mutually_exclusive_category_impl(ctx):

cc_mutually_exclusive_category = rule(
implementation = _cc_mutually_exclusive_category_impl,
doc = "A category of features, for which only one can be enabled",
doc = """A rule used to categorize `cc_feature` definitions for which only one can be enabled.
This is used by [`cc_feature.mutually_exclusive`](#cc_feature-mutually_exclusive) to express groups
of `cc_feature` definitions that are inherently incompatible with each other and must be treated as
mutually exclusive.
Warning: These groups are keyed by name, so two `cc_mutually_exclusive_category` definitions of the
same name in different packages will resolve to the same logical group.
Example:
```
load("//cc/toolchains:feature.bzl", "cc_feature")
load("//cc/toolchains:mutually_exclusive_category.bzl", "cc_mutually_exclusive_category")
cc_mutually_exclusive_category(
name = "opt_level",
)
cc_feature(
name = "speed_optimized",
mutually_exclusive = [":opt_level"],
)
cc_feature(
name = "size_optimized",
mutually_exclusive = [":opt_level"],
)
cc_feature(
name = "unoptimized",
mutually_exclusive = [":opt_level"],
)
```
""",
attrs = {},
provides = [MutuallyExclusiveCategoryInfo],
)
Loading

0 comments on commit 1af2140

Please sign in to comment.