1*eed53cd4SHONG Yifan# Copyright 2024 The Bazel Authors. All rights reserved. 2*eed53cd4SHONG Yifan# 3*eed53cd4SHONG Yifan# Licensed under the Apache License, Version 2.0 (the "License"); 4*eed53cd4SHONG Yifan# you may not use this file except in compliance with the License. 5*eed53cd4SHONG Yifan# You may obtain a copy of the License at 6*eed53cd4SHONG Yifan# 7*eed53cd4SHONG Yifan# http://www.apache.org/licenses/LICENSE-2.0 8*eed53cd4SHONG Yifan# 9*eed53cd4SHONG Yifan# Unless required by applicable law or agreed to in writing, software 10*eed53cd4SHONG Yifan# distributed under the License is distributed on an "AS IS" BASIS, 11*eed53cd4SHONG Yifan# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*eed53cd4SHONG Yifan# See the License for the specific language governing permissions and 13*eed53cd4SHONG Yifan# limitations under the License. 14*eed53cd4SHONG Yifan"""Implementation of the cc_feature rule.""" 15*eed53cd4SHONG Yifan 16*eed53cd4SHONG Yifanload( 17*eed53cd4SHONG Yifan "//cc/toolchains/impl:collect.bzl", 18*eed53cd4SHONG Yifan "collect_args_lists", 19*eed53cd4SHONG Yifan "collect_features", 20*eed53cd4SHONG Yifan "collect_provider", 21*eed53cd4SHONG Yifan) 22*eed53cd4SHONG Yifanload( 23*eed53cd4SHONG Yifan ":cc_toolchain_info.bzl", 24*eed53cd4SHONG Yifan "ArgsListInfo", 25*eed53cd4SHONG Yifan "FeatureConstraintInfo", 26*eed53cd4SHONG Yifan "FeatureInfo", 27*eed53cd4SHONG Yifan "FeatureSetInfo", 28*eed53cd4SHONG Yifan "MutuallyExclusiveCategoryInfo", 29*eed53cd4SHONG Yifan) 30*eed53cd4SHONG Yifan 31*eed53cd4SHONG Yifandef _cc_feature_impl(ctx): 32*eed53cd4SHONG Yifan if bool(ctx.attr.feature_name) == (ctx.attr.overrides != None): 33*eed53cd4SHONG Yifan fail("Exactly one of 'feature_name' and 'overrides' are required") 34*eed53cd4SHONG Yifan 35*eed53cd4SHONG Yifan if ctx.attr.overrides == None: 36*eed53cd4SHONG Yifan overrides = None 37*eed53cd4SHONG Yifan 38*eed53cd4SHONG Yifan # In the future, we may consider making feature_name optional, 39*eed53cd4SHONG Yifan # defaulting to ctx.label.name. However, starting that way would make it 40*eed53cd4SHONG Yifan # very difficult if we did want to later change that. 41*eed53cd4SHONG Yifan name = ctx.attr.feature_name 42*eed53cd4SHONG Yifan else: 43*eed53cd4SHONG Yifan overrides = ctx.attr.overrides[FeatureInfo] 44*eed53cd4SHONG Yifan if not overrides.overridable: 45*eed53cd4SHONG Yifan fail("Attempting to override %s, which is not overridable" % overrides.label) 46*eed53cd4SHONG Yifan name = overrides.name 47*eed53cd4SHONG Yifan 48*eed53cd4SHONG Yifan # In the following scenario: 49*eed53cd4SHONG Yifan # cc_args(name = "foo", env = {"FOO": "BAR"}, args = ["--foo"]) 50*eed53cd4SHONG Yifan # cc_action_config(name = "ac", args=[":foo"]) 51*eed53cd4SHONG Yifan 52*eed53cd4SHONG Yifan # We will translate this into providers roughly equivalent to the following: 53*eed53cd4SHONG Yifan # cc_args(name = "implied_by_ac_env", env = {"FOO": "BAR"}, args = ["--foo"]) 54*eed53cd4SHONG Yifan # cc_feature(name = "implied_by_ac", args = [":implied_by_ac_env"]) 55*eed53cd4SHONG Yifan # cc_action_config( 56*eed53cd4SHONG Yifan # name = "c_compile", 57*eed53cd4SHONG Yifan # implies = [":implied_by_c_compile"] 58*eed53cd4SHONG Yifan # ) 59*eed53cd4SHONG Yifan 60*eed53cd4SHONG Yifan # The reason for this is because although the legacy providers support 61*eed53cd4SHONG Yifan # flag_sets in action_config, they don't support env sets. 62*eed53cd4SHONG Yifan if name.startswith("implied_by_"): 63*eed53cd4SHONG Yifan fail("Feature names starting with 'implied_by' are reserved") 64*eed53cd4SHONG Yifan 65*eed53cd4SHONG Yifan feature = FeatureInfo( 66*eed53cd4SHONG Yifan label = ctx.label, 67*eed53cd4SHONG Yifan name = name, 68*eed53cd4SHONG Yifan enabled = ctx.attr.enabled, 69*eed53cd4SHONG Yifan args = collect_args_lists(ctx.attr.args, ctx.label), 70*eed53cd4SHONG Yifan implies = collect_features(ctx.attr.implies), 71*eed53cd4SHONG Yifan requires_any_of = tuple(collect_provider( 72*eed53cd4SHONG Yifan ctx.attr.requires_any_of, 73*eed53cd4SHONG Yifan FeatureSetInfo, 74*eed53cd4SHONG Yifan )), 75*eed53cd4SHONG Yifan mutually_exclusive = tuple(collect_provider( 76*eed53cd4SHONG Yifan ctx.attr.mutually_exclusive, 77*eed53cd4SHONG Yifan MutuallyExclusiveCategoryInfo, 78*eed53cd4SHONG Yifan )), 79*eed53cd4SHONG Yifan external = False, 80*eed53cd4SHONG Yifan overridable = False, 81*eed53cd4SHONG Yifan overrides = overrides, 82*eed53cd4SHONG Yifan ) 83*eed53cd4SHONG Yifan 84*eed53cd4SHONG Yifan return [ 85*eed53cd4SHONG Yifan feature, 86*eed53cd4SHONG Yifan FeatureSetInfo(label = ctx.label, features = depset([feature])), 87*eed53cd4SHONG Yifan FeatureConstraintInfo( 88*eed53cd4SHONG Yifan label = ctx.label, 89*eed53cd4SHONG Yifan all_of = depset([feature]), 90*eed53cd4SHONG Yifan none_of = depset([]), 91*eed53cd4SHONG Yifan ), 92*eed53cd4SHONG Yifan MutuallyExclusiveCategoryInfo(label = ctx.label, name = name), 93*eed53cd4SHONG Yifan ] 94*eed53cd4SHONG Yifan 95*eed53cd4SHONG Yifancc_feature = rule( 96*eed53cd4SHONG Yifan implementation = _cc_feature_impl, 97*eed53cd4SHONG Yifan # @unsorted-dict-items 98*eed53cd4SHONG Yifan attrs = { 99*eed53cd4SHONG Yifan "feature_name": attr.string( 100*eed53cd4SHONG Yifan doc = """The name of the feature that this rule implements. 101*eed53cd4SHONG Yifan 102*eed53cd4SHONG YifanThe feature name is a string that will be used in the `features` attribute of 103*eed53cd4SHONG Yifanrules to enable them (eg. `cc_binary(..., features = ["opt"])`. 104*eed53cd4SHONG Yifan 105*eed53cd4SHONG YifanWhile two features with the same `feature_name` may not be bound to the same 106*eed53cd4SHONG Yifantoolchain, they can happily live alongside each other in the same BUILD file. 107*eed53cd4SHONG Yifan 108*eed53cd4SHONG YifanExample: 109*eed53cd4SHONG Yifan 110*eed53cd4SHONG Yifan cc_feature( 111*eed53cd4SHONG Yifan name = "sysroot_macos", 112*eed53cd4SHONG Yifan feature_name = "sysroot", 113*eed53cd4SHONG Yifan ... 114*eed53cd4SHONG Yifan ) 115*eed53cd4SHONG Yifan 116*eed53cd4SHONG Yifan cc_feature( 117*eed53cd4SHONG Yifan name = "sysroot_linux", 118*eed53cd4SHONG Yifan feature_name = "sysroot", 119*eed53cd4SHONG Yifan ... 120*eed53cd4SHONG Yifan ) 121*eed53cd4SHONG Yifan""", 122*eed53cd4SHONG Yifan ), 123*eed53cd4SHONG Yifan "enabled": attr.bool( 124*eed53cd4SHONG Yifan mandatory = True, 125*eed53cd4SHONG Yifan doc = """Whether or not this feature is enabled by default.""", 126*eed53cd4SHONG Yifan ), 127*eed53cd4SHONG Yifan "args": attr.label_list( 128*eed53cd4SHONG Yifan mandatory = True, 129*eed53cd4SHONG Yifan doc = """Args that, when expanded, implement this feature.""", 130*eed53cd4SHONG Yifan providers = [ArgsListInfo], 131*eed53cd4SHONG Yifan ), 132*eed53cd4SHONG Yifan "requires_any_of": attr.label_list( 133*eed53cd4SHONG Yifan doc = """A list of feature sets that define toolchain compatibility. 134*eed53cd4SHONG Yifan 135*eed53cd4SHONG YifanIf *at least one* of the listed `cc_feature_set`s are fully satisfied (all 136*eed53cd4SHONG Yifanfeatures exist in the toolchain AND are currently enabled), this feature is 137*eed53cd4SHONG Yifandeemed compatible and may be enabled. 138*eed53cd4SHONG Yifan 139*eed53cd4SHONG YifanNote: Even if `cc_feature.requires_any_of` is satisfied, a feature is not 140*eed53cd4SHONG Yifanenabled unless another mechanism (e.g. command-line flags, `cc_feature.implies`, 141*eed53cd4SHONG Yifan`cc_feature.enabled`) signals that the feature should actually be enabled. 142*eed53cd4SHONG Yifan""", 143*eed53cd4SHONG Yifan providers = [FeatureSetInfo], 144*eed53cd4SHONG Yifan ), 145*eed53cd4SHONG Yifan "implies": attr.label_list( 146*eed53cd4SHONG Yifan providers = [FeatureSetInfo], 147*eed53cd4SHONG Yifan doc = """List of features enabled along with this feature. 148*eed53cd4SHONG Yifan 149*eed53cd4SHONG YifanWarning: If any of the features cannot be enabled, this feature is 150*eed53cd4SHONG Yifansilently disabled. 151*eed53cd4SHONG Yifan""", 152*eed53cd4SHONG Yifan ), 153*eed53cd4SHONG Yifan "mutually_exclusive": attr.label_list( 154*eed53cd4SHONG Yifan providers = [MutuallyExclusiveCategoryInfo], 155*eed53cd4SHONG Yifan doc = """A list of things that this is mutually exclusive with. 156*eed53cd4SHONG Yifan 157*eed53cd4SHONG YifanIt can be either: 158*eed53cd4SHONG Yifan* A feature, in which case the two features are mutually exclusive. 159*eed53cd4SHONG Yifan* A `cc_mutually_exclusive_category`, in which case all features that write 160*eed53cd4SHONG Yifan `mutually_exclusive = [":category"]` are mutually exclusive with each other. 161*eed53cd4SHONG Yifan 162*eed53cd4SHONG YifanIf this feature has a side-effect of implementing another feature, it can be 163*eed53cd4SHONG Yifanuseful to list that feature here to ensure they aren't enabled at the 164*eed53cd4SHONG Yifansame time. 165*eed53cd4SHONG Yifan""", 166*eed53cd4SHONG Yifan ), 167*eed53cd4SHONG Yifan "overrides": attr.label( 168*eed53cd4SHONG Yifan providers = [FeatureInfo], 169*eed53cd4SHONG Yifan doc = """A declaration that this feature overrides a known feature. 170*eed53cd4SHONG Yifan 171*eed53cd4SHONG YifanIn the example below, if you missed the "overrides" attribute, it would complain 172*eed53cd4SHONG Yifanthat the feature "opt" was defined twice. 173*eed53cd4SHONG Yifan 174*eed53cd4SHONG YifanExample: 175*eed53cd4SHONG Yifan 176*eed53cd4SHONG Yifan cc_feature( 177*eed53cd4SHONG Yifan name = "opt", 178*eed53cd4SHONG Yifan feature_name = "opt", 179*eed53cd4SHONG Yifan ... 180*eed53cd4SHONG Yifan overrides = "@toolchain//features/well_known:opt", 181*eed53cd4SHONG Yifan ) 182*eed53cd4SHONG Yifan 183*eed53cd4SHONG Yifan""", 184*eed53cd4SHONG Yifan ), 185*eed53cd4SHONG Yifan }, 186*eed53cd4SHONG Yifan provides = [ 187*eed53cd4SHONG Yifan FeatureInfo, 188*eed53cd4SHONG Yifan FeatureSetInfo, 189*eed53cd4SHONG Yifan FeatureConstraintInfo, 190*eed53cd4SHONG Yifan MutuallyExclusiveCategoryInfo, 191*eed53cd4SHONG Yifan ], 192*eed53cd4SHONG Yifan doc = """Defines the implemented behavior of a C/C++ toolchain feature. 193*eed53cd4SHONG Yifan 194*eed53cd4SHONG YifanA feature is basically a toggleable list of args. There are a variety of 195*eed53cd4SHONG Yifandependencies and compatibility requirements that must be satisfied for the 196*eed53cd4SHONG Yifanlisted args to be applied. 197*eed53cd4SHONG Yifan 198*eed53cd4SHONG YifanA feature may be enabled or disabled through the following mechanisms: 199*eed53cd4SHONG Yifan* Via command-line flags, or a `.bazelrc`. 200*eed53cd4SHONG Yifan* Through inter-feature relationships (enabling one feature may implicitly 201*eed53cd4SHONG Yifan enable another). 202*eed53cd4SHONG Yifan* Individual rules may elect to manually enable or disable features through the 203*eed53cd4SHONG Yifan builtin `features` attribute. 204*eed53cd4SHONG Yifan 205*eed53cd4SHONG YifanBecause of the toggleable nature of toolchain features, it's generally best to 206*eed53cd4SHONG Yifanavoid defining features as part of your toolchain with the following exceptions: 207*eed53cd4SHONG Yifan* You want build files to be able to configure compiler flags. For example, a 208*eed53cd4SHONG Yifan binary might specify `features = ["optimize_for_size"]` to create a small 209*eed53cd4SHONG Yifan binary instead of optimizing for performance. 210*eed53cd4SHONG Yifan* You need to carry forward Starlark toolchain behaviors. If you're migrating a 211*eed53cd4SHONG Yifan complex Starlark-based toolchain definition to these rules, many of the 212*eed53cd4SHONG Yifan workflows and flags were likely based on features. This rule exists to support 213*eed53cd4SHONG Yifan those existing structures. 214*eed53cd4SHONG Yifan 215*eed53cd4SHONG YifanIf you want to be able to configure flags via the bazel command-line, instead 216*eed53cd4SHONG Yifanconsider making a bool_flag, and then making your `cc_args` `select` on those 217*eed53cd4SHONG Yifanflags. 218*eed53cd4SHONG Yifan 219*eed53cd4SHONG YifanFor more details about how Bazel handles features, see the official Bazel 220*eed53cd4SHONG Yifandocumentation at 221*eed53cd4SHONG Yifanhttps://bazel.build/docs/cc-toolchain-config-reference#features. 222*eed53cd4SHONG Yifan 223*eed53cd4SHONG YifanExamples: 224*eed53cd4SHONG Yifan 225*eed53cd4SHONG Yifan # A feature that can be easily toggled to optimize for size 226*eed53cd4SHONG Yifan cc_feature( 227*eed53cd4SHONG Yifan name = "optimize_for_size", 228*eed53cd4SHONG Yifan enabled = False, 229*eed53cd4SHONG Yifan feature_name = "optimize_for_size", 230*eed53cd4SHONG Yifan args = [":optimize_for_size_args"], 231*eed53cd4SHONG Yifan ) 232*eed53cd4SHONG Yifan 233*eed53cd4SHONG Yifan # This feature signals a capability, and doesn't have associated flags. 234*eed53cd4SHONG Yifan # 235*eed53cd4SHONG Yifan # For a list of well-known features, see: 236*eed53cd4SHONG Yifan # https://bazel.build/docs/cc-toolchain-config-reference#wellknown-features 237*eed53cd4SHONG Yifan cc_feature( 238*eed53cd4SHONG Yifan name = "supports_pic", 239*eed53cd4SHONG Yifan enabled = True, 240*eed53cd4SHONG Yifan overrides = "//cc/toolchains/features:supports_pic 241*eed53cd4SHONG Yifan ) 242*eed53cd4SHONG Yifan""", 243*eed53cd4SHONG Yifan) 244