blob: f0acbfe7c32905c9feefe394872018c55be487c7 [file] [log] [blame]
Googler2b6cdcf2024-02-27 04:44:55 -08001# Copyright 2024 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Implementation of the cc_feature rule."""
15
16load(
17 "//cc/toolchains/impl:collect.bzl",
18 "collect_args_lists",
19 "collect_features",
20 "collect_provider",
21)
22load(
23 ":cc_toolchain_info.bzl",
24 "ArgsListInfo",
25 "FeatureConstraintInfo",
26 "FeatureInfo",
27 "FeatureSetInfo",
28 "MutuallyExclusiveCategoryInfo",
29)
30
31def _cc_feature_impl(ctx):
32 if bool(ctx.attr.feature_name) == (ctx.attr.overrides != None):
33 fail("Exactly one of 'feature_name' and 'overrides' are required")
34
35 if ctx.attr.overrides == None:
36 overrides = None
37
38 # In the future, we may consider making feature_name optional,
39 # defaulting to ctx.label.name. However, starting that way would make it
40 # very difficult if we did want to later change that.
41 name = ctx.attr.feature_name
42 else:
43 overrides = ctx.attr.overrides[FeatureInfo]
44 if not overrides.overridable:
45 fail("Attempting to override %s, which is not overridable" % overrides.label)
46 name = overrides.name
47
48 # In the following scenario:
49 # cc_args(name = "foo", env = {"FOO": "BAR"}, args = ["--foo"])
50 # cc_action_config(name = "ac", args=[":foo"])
51
52 # We will translate this into providers roughly equivalent to the following:
53 # cc_args(name = "implied_by_ac_env", env = {"FOO": "BAR"}, args = ["--foo"])
54 # cc_feature(name = "implied_by_ac", args = [":implied_by_ac_env"])
55 # cc_action_config(
56 # name = "c_compile",
57 # implies = [":implied_by_c_compile"]
58 # )
59
60 # The reason for this is because although the legacy providers support
61 # flag_sets in action_config, they don't support env sets.
62 if name.startswith("implied_by_"):
63 fail("Feature names starting with 'implied_by' are reserved")
64
Googler66613ac2024-09-05 09:05:23 -070065 args = collect_args_lists(ctx.attr.args, ctx.label)
Googler2b6cdcf2024-02-27 04:44:55 -080066 feature = FeatureInfo(
67 label = ctx.label,
68 name = name,
Googler84fceed2024-08-01 19:19:23 -070069 # Unused field, but leave it just in case we want to reuse it in the
70 # future.
71 enabled = False,
Googler66613ac2024-09-05 09:05:23 -070072 args = args,
Googler2b6cdcf2024-02-27 04:44:55 -080073 implies = collect_features(ctx.attr.implies),
74 requires_any_of = tuple(collect_provider(
75 ctx.attr.requires_any_of,
76 FeatureSetInfo,
77 )),
78 mutually_exclusive = tuple(collect_provider(
79 ctx.attr.mutually_exclusive,
80 MutuallyExclusiveCategoryInfo,
81 )),
82 external = False,
83 overridable = False,
84 overrides = overrides,
Googler66613ac2024-09-05 09:05:23 -070085 allowlist_include_directories = args.allowlist_include_directories,
Googler2b6cdcf2024-02-27 04:44:55 -080086 )
87
88 return [
89 feature,
90 FeatureSetInfo(label = ctx.label, features = depset([feature])),
91 FeatureConstraintInfo(
92 label = ctx.label,
93 all_of = depset([feature]),
94 none_of = depset([]),
95 ),
96 MutuallyExclusiveCategoryInfo(label = ctx.label, name = name),
97 ]
98
99cc_feature = rule(
100 implementation = _cc_feature_impl,
101 # @unsorted-dict-items
102 attrs = {
103 "feature_name": attr.string(
104 doc = """The name of the feature that this rule implements.
105
106The feature name is a string that will be used in the `features` attribute of
107rules to enable them (eg. `cc_binary(..., features = ["opt"])`.
108
109While two features with the same `feature_name` may not be bound to the same
110toolchain, they can happily live alongside each other in the same BUILD file.
111
112Example:
Googler1af21402024-10-02 09:09:54 -0700113```
114cc_feature(
115 name = "sysroot_macos",
116 feature_name = "sysroot",
117 ...
118)
Googler2b6cdcf2024-02-27 04:44:55 -0800119
Googler1af21402024-10-02 09:09:54 -0700120cc_feature(
121 name = "sysroot_linux",
122 feature_name = "sysroot",
123 ...
124)
125```
Googler2b6cdcf2024-02-27 04:44:55 -0800126""",
127 ),
Googler2b6cdcf2024-02-27 04:44:55 -0800128 "args": attr.label_list(
Googler1af21402024-10-02 09:09:54 -0700129 doc = """A list of `cc_args` or `cc_args_list` labels that are expanded when this feature is enabled.""",
Googler2b6cdcf2024-02-27 04:44:55 -0800130 providers = [ArgsListInfo],
131 ),
132 "requires_any_of": attr.label_list(
133 doc = """A list of feature sets that define toolchain compatibility.
134
135If *at least one* of the listed `cc_feature_set`s are fully satisfied (all
136features exist in the toolchain AND are currently enabled), this feature is
137deemed compatible and may be enabled.
138
139Note: Even if `cc_feature.requires_any_of` is satisfied, a feature is not
140enabled unless another mechanism (e.g. command-line flags, `cc_feature.implies`,
Googler84fceed2024-08-01 19:19:23 -0700141`cc_toolchain_config.enabled_features`) signals that the feature should actually
142be enabled.
Googler2b6cdcf2024-02-27 04:44:55 -0800143""",
144 providers = [FeatureSetInfo],
145 ),
146 "implies": attr.label_list(
147 providers = [FeatureSetInfo],
148 doc = """List of features enabled along with this feature.
149
150Warning: If any of the features cannot be enabled, this feature is
151silently disabled.
152""",
153 ),
154 "mutually_exclusive": attr.label_list(
155 providers = [MutuallyExclusiveCategoryInfo],
Googler1af21402024-10-02 09:09:54 -0700156 doc = """A list of things that this feature is mutually exclusive with.
Googler2b6cdcf2024-02-27 04:44:55 -0800157
158It can be either:
159* A feature, in which case the two features are mutually exclusive.
160* A `cc_mutually_exclusive_category`, in which case all features that write
161 `mutually_exclusive = [":category"]` are mutually exclusive with each other.
162
163If this feature has a side-effect of implementing another feature, it can be
Googler84fceed2024-08-01 19:19:23 -0700164useful to list that feature here to ensure they aren't enabled at the same time.
Googler2b6cdcf2024-02-27 04:44:55 -0800165""",
166 ),
167 "overrides": attr.label(
168 providers = [FeatureInfo],
169 doc = """A declaration that this feature overrides a known feature.
170
171In the example below, if you missed the "overrides" attribute, it would complain
172that the feature "opt" was defined twice.
173
174Example:
Googler1af21402024-10-02 09:09:54 -0700175```
176load("//cc/toolchains:feature.bzl", "cc_feature")
Googler2b6cdcf2024-02-27 04:44:55 -0800177
Googler1af21402024-10-02 09:09:54 -0700178cc_feature(
179 name = "opt",
180 feature_name = "opt",
181 args = [":size_optimized"],
182 overrides = "//cc/toolchains/features:opt",
183)
184```
Googler2b6cdcf2024-02-27 04:44:55 -0800185""",
186 ),
187 },
188 provides = [
189 FeatureInfo,
190 FeatureSetInfo,
191 FeatureConstraintInfo,
192 MutuallyExclusiveCategoryInfo,
193 ],
Googler1af21402024-10-02 09:09:54 -0700194 doc = """A dynamic set of toolchain flags that create a singular [feature](https://bazel.build/docs/cc-toolchain-config-reference#features) definition.
Googler2b6cdcf2024-02-27 04:44:55 -0800195
Googler1af21402024-10-02 09:09:54 -0700196A feature is basically a dynamically toggleable `cc_args_list`. There are a variety of
197dependencies and compatibility requirements that must be satisfied to enable a
198`cc_feature`. Once those conditions are met, the arguments in [`cc_feature.args`](#cc_feature-args)
199are expanded and added to the command-line.
Googler2b6cdcf2024-02-27 04:44:55 -0800200
201A feature may be enabled or disabled through the following mechanisms:
Googler1af21402024-10-02 09:09:54 -0700202* Via command-line flags, or a `.bazelrc` file via the
203 [`--features` flag](https://bazel.build/reference/command-line-reference#flag--features)
204* Through inter-feature relationships (via [`cc_feature.implies`](#cc_feature-implies)) where one
205 feature may implicitly enable another.
206* Individual rules (e.g. `cc_library`) or `package` definitions may elect to manually enable or
207 disable features through the
208 [`features` attribute](https://bazel.build/reference/be/common-definitions#common.features).
Googler2b6cdcf2024-02-27 04:44:55 -0800209
Googler1af21402024-10-02 09:09:54 -0700210Note that a feature may alternate between enabled and disabled dynamically over the course of a
211build. Because of their toggleable nature, it's generally best to avoid adding arguments to a
212`cc_toolchain` as a `cc_feature` unless strictly necessary. Instead, prefer to express arguments
213via [`cc_toolchain.args`](#cc_toolchain-args) whenever possible.
214
215You should use a `cc_feature` when any of the following apply:
216* You need the flags to be dynamically toggled over the course of a build.
217* You want build files to be able to configure the flags in question. For example, a
Googler2b6cdcf2024-02-27 04:44:55 -0800218 binary might specify `features = ["optimize_for_size"]` to create a small
219 binary instead of optimizing for performance.
220* You need to carry forward Starlark toolchain behaviors. If you're migrating a
221 complex Starlark-based toolchain definition to these rules, many of the
Googler1af21402024-10-02 09:09:54 -0700222 workflows and flags were likely based on features.
Googler2b6cdcf2024-02-27 04:44:55 -0800223
Googler1af21402024-10-02 09:09:54 -0700224If you only need to configure flags via the Bazel command-line, instead
225consider adding a
226[`bool_flag`](https://github.com/bazelbuild/bazel-skylib/tree/main/doc/common_settings_doc.md#bool_flag)
227paired with a [`config_setting`](https://bazel.build/reference/be/general#config_setting)
228and then make your `cc_args` rule `select` on the `config_setting`.
Googler2b6cdcf2024-02-27 04:44:55 -0800229
230For more details about how Bazel handles features, see the official Bazel
231documentation at
232https://bazel.build/docs/cc-toolchain-config-reference#features.
233
Googler1af21402024-10-02 09:09:54 -0700234Example:
235```
236load("//cc/toolchains:feature.bzl", "cc_feature")
Googler2b6cdcf2024-02-27 04:44:55 -0800237
Googler1af21402024-10-02 09:09:54 -0700238# A feature that enables LTO, which may be incompatible when doing interop with various
239# languages (e.g. rust, go), or may need to be disabled for particular `cc_binary` rules
240# for various reasons.
241cc_feature(
242 name = "lto",
243 feature_name = "lto",
244 args = [":lto_args"],
245)
246```
Googler2b6cdcf2024-02-27 04:44:55 -0800247""",
248)