1# 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"""All providers for rule-based bazel toolchain config.""" 15 16load("@bazel_skylib//rules/directory:providers.bzl", "DirectoryInfo") 17load("//cc/toolchains/impl:args_utils.bzl", "validate_nested_args") 18load( 19 "//cc/toolchains/impl:collect.bzl", 20 "collect_action_types", 21 "collect_files", 22 "collect_provider", 23) 24load( 25 "//cc/toolchains/impl:nested_args.bzl", 26 "NESTED_ARGS_ATTRS", 27 "nested_args_provider_from_ctx", 28) 29load( 30 ":cc_toolchain_info.bzl", 31 "ActionTypeSetInfo", 32 "ArgsInfo", 33 "ArgsListInfo", 34 "BuiltinVariablesInfo", 35 "FeatureConstraintInfo", 36) 37 38visibility("public") 39 40def _cc_args_impl(ctx): 41 actions = collect_action_types(ctx.attr.actions) 42 43 nested = None 44 if ctx.attr.args or ctx.attr.nested: 45 nested = nested_args_provider_from_ctx(ctx) 46 validate_nested_args( 47 variables = ctx.attr._variables[BuiltinVariablesInfo].variables, 48 nested_args = nested, 49 actions = actions.to_list(), 50 label = ctx.label, 51 ) 52 files = nested.files 53 else: 54 files = collect_files(ctx.attr.data + ctx.attr.allowlist_include_directories) 55 56 requires = collect_provider(ctx.attr.requires_any_of, FeatureConstraintInfo) 57 58 args = ArgsInfo( 59 label = ctx.label, 60 actions = actions, 61 requires_any_of = tuple(requires), 62 nested = nested, 63 env = ctx.attr.env, 64 files = files, 65 allowlist_include_directories = depset( 66 direct = [d[DirectoryInfo] for d in ctx.attr.allowlist_include_directories], 67 ), 68 ) 69 return [ 70 args, 71 ArgsListInfo( 72 label = ctx.label, 73 args = tuple([args]), 74 files = files, 75 by_action = tuple([ 76 struct(action = action, args = tuple([args]), files = files) 77 for action in actions.to_list() 78 ]), 79 allowlist_include_directories = args.allowlist_include_directories, 80 ), 81 ] 82 83_cc_args = rule( 84 implementation = _cc_args_impl, 85 attrs = { 86 "actions": attr.label_list( 87 providers = [ActionTypeSetInfo], 88 mandatory = True, 89 doc = """See documentation for cc_args macro wrapper.""", 90 ), 91 "allowlist_include_directories": attr.label_list( 92 providers = [DirectoryInfo], 93 doc = """See documentation for cc_args macro wrapper.""", 94 ), 95 "env": attr.string_dict( 96 doc = """See documentation for cc_args macro wrapper.""", 97 ), 98 "requires_any_of": attr.label_list( 99 providers = [FeatureConstraintInfo], 100 doc = """See documentation for cc_args macro wrapper.""", 101 ), 102 "_variables": attr.label( 103 default = "//cc/toolchains/variables:variables", 104 ), 105 } | NESTED_ARGS_ATTRS, 106 provides = [ArgsInfo], 107 doc = """Declares a list of arguments bound to a set of actions. 108 109Roughly equivalent to ctx.actions.args() 110 111Examples: 112 cc_args( 113 name = "warnings_as_errors", 114 args = ["-Werror"], 115 ) 116""", 117) 118 119def cc_args( 120 *, 121 name, 122 actions = None, 123 allowlist_include_directories = None, 124 args = None, 125 data = None, 126 env = None, 127 format = {}, 128 iterate_over = None, 129 nested = None, 130 requires_not_none = None, 131 requires_none = None, 132 requires_true = None, 133 requires_false = None, 134 requires_equal = None, 135 requires_equal_value = None, 136 requires_any_of = None, 137 **kwargs): 138 """Action-specific arguments for use with a `cc_toolchain`. 139 140 This rule is the fundamental building building block for every toolchain tool invocation. Each 141 argument expressed in a toolchain tool invocation (e.g. `gcc`, `llvm-ar`) is declared in a 142 `cc_args` rule that applies an ordered list of arguments to a set of toolchain 143 actions. `cc_args` rules can be added unconditionally to a 144 `cc_toolchain`, conditionally via `select()` statements, or dynamically via an 145 intermediate `cc_feature`. 146 147 Conceptually, this is similar to the old `CFLAGS`, `CPPFLAGS`, etc. environment variables that 148 many build systems use to determine which flags to use for a given action. The significant 149 difference is that `cc_args` rules are declared in a structured way that allows for 150 significantly more powerful and sharable toolchain configurations. Also, due to Bazel's more 151 granular action types, it's possible to bind flags to very specific actions (e.g. LTO indexing 152 for an executable vs a dynamic library) multiple different actions (e.g. C++ compile and link 153 simultaneously). 154 155 Example usage: 156 ``` 157 load("//cc/toolchains:args.bzl", "cc_args") 158 159 # Basic usage: a trivial flag. 160 # 161 # An example of expressing `-Werror` as a `cc_args` rule. 162 cc_args( 163 name = "warnings_as_errors", 164 actions = [ 165 # Applies to all C/C++ compile actions. 166 "//cc/toolchains/actions:compile_actions", 167 ], 168 args = ["-Werror"], 169 ) 170 171 # Basic usage: ordered flags. 172 # 173 # An example of linking against libc++, which uses two flags that must be applied in order. 174 cc_args( 175 name = "link_libcxx", 176 actions = [ 177 # Applies to all link actions. 178 "//cc/toolchains/actions:link_actions", 179 ], 180 # On tool invocation, this appears as `-Xlinker -lc++`. Nothing will ever end up between 181 # the two flags. 182 args = [ 183 "-Xlinker", 184 "-lc++", 185 ], 186 ) 187 188 # Advanced usage: built-in variable expansions. 189 # 190 # Expands to `-L/path/to/search_dir` for each directory in the built-in variable 191 # `library_search_directories`. This variable is managed internally by Bazel through inherent 192 # behaviors of Bazel and the interactions between various C/C++ build rules. 193 cc_args( 194 name = "library_search_directories", 195 actions = [ 196 "//cc/toolchains/actions:link_actions", 197 ], 198 args = ["-L{search_dir}"], 199 iterate_over = "//cc/toolchains/variables:library_search_directories", 200 requires_not_none = "//cc/toolchains/variables:library_search_directories", 201 format = { 202 "search_dir": "//cc/toolchains/variables:library_search_directories", 203 }, 204 ) 205 ``` 206 207 For more extensive examples, see the usages here: 208 https://github.com/bazelbuild/rules_cc/tree/main/cc/toolchains/args 209 210 Args: 211 name: (str) The name of the target. 212 actions: (List[Label]) A list of labels of `cc_action_type` or 213 `cc_action_type_set` rules that dictate which actions these 214 arguments should be applied to. 215 allowlist_include_directories: (List[Label]) A list of include paths that are implied by 216 using this rule. These must point to a skylib 217 [directory](https://github.com/bazelbuild/bazel-skylib/tree/main/doc/directory_doc.md#directory) 218 or [subdirectory](https://github.com/bazelbuild/bazel-skylib/tree/main/doc/directory_subdirectory_doc.md#subdirectory) rule. 219 Some flags (e.g. --sysroot) imply certain include paths are available despite 220 not explicitly specifying a normal include path flag (`-I`, `-isystem`, etc.). 221 Bazel checks that all included headers are properly provided by a dependency or 222 allowlisted through this mechanism. 223 224 As a rule of thumb, only use this if Bazel is complaining about absolute paths in 225 your toolchain and you've ensured that the toolchain is compiling with the 226 `-no-canonical-prefixes` and/or `-fno-canonical-system-headers` arguments. 227 228 This can help work around errors like: 229 `the source file 'main.c' includes the following non-builtin files with absolute paths 230 (if these are builtin files, make sure these paths are in your toolchain)`. 231 args: (List[str]) The command-line arguments that are applied by using this rule. This is 232 mutually exclusive with [nested](#cc_args-nested). 233 data: (List[Label]) A list of runtime data dependencies that are required for these 234 arguments to work as intended. 235 env: (Dict[str, str]) Environment variables that should be set when the tool is invoked. 236 format: (Dict[str, Label]) A mapping of format strings to the label of the corresponding 237 `cc_variable` that the value should be pulled from. All instances of 238 `{variable_name}` will be replaced with the expanded value of `variable_name` in this 239 dictionary. The complete list of possible variables can be found in 240 https://github.com/bazelbuild/rules_cc/tree/main/cc/toolchains/variables/BUILD. 241 It is not possible to declare custom variables--these are inherent to Bazel itself. 242 iterate_over: (Label) The label of a `cc_variable` that should be iterated over. This is 243 intended for use with built-in variables that are lists. 244 nested: (List[Label]) A list of `cc_nested_args` rules that should be 245 expanded to command-line arguments when this rule is used. This is mutually exclusive 246 with [args](#cc_args-args). 247 requires_not_none: (Label) The label of a `cc_variable` that should be checked 248 for existence before expanding this rule. If the variable is None, this rule will be 249 ignored. 250 requires_none: (Label) The label of a `cc_variable` that should be checked for 251 non-existence before expanding this rule. If the variable is not None, this rule will be 252 ignored. 253 requires_true: (Label) The label of a `cc_variable` that should be checked for 254 truthiness before expanding this rule. If the variable is false, this rule will be 255 ignored. 256 requires_false: (Label) The label of a `cc_variable` that should be checked 257 for falsiness before expanding this rule. If the variable is true, this rule will be 258 ignored. 259 requires_equal: (Label) The label of a `cc_variable` that should be checked 260 for equality before expanding this rule. If the variable is not equal to 261 (requires_equal_value)[#cc_args-requires_equal_value], this rule will be ignored. 262 requires_equal_value: (str) The value to compare (requires_equal)[#cc_args-requires_equal] 263 against. 264 requires_any_of: (List[Label]) These arguments will be used 265 in a tool invocation when at least one of the [cc_feature_constraint](#cc_feature_constraint) 266 entries in this list are satisfied. If omitted, this flag set will be enabled 267 unconditionally. 268 **kwargs: [common attributes](https://bazel.build/reference/be/common-definitions#common-attributes) that should be applied to this rule. 269 """ 270 return _cc_args( 271 name = name, 272 actions = actions, 273 allowlist_include_directories = allowlist_include_directories, 274 args = args, 275 data = data, 276 env = env, 277 # We flip the key/value pairs in the dictionary here because Bazel doesn't have a 278 # string-keyed label dict attribute type. 279 format = {k: v for v, k in format.items()}, 280 iterate_over = iterate_over, 281 nested = nested, 282 requires_not_none = requires_not_none, 283 requires_none = requires_none, 284 requires_true = requires_true, 285 requires_false = requires_false, 286 requires_equal = requires_equal, 287 requires_equal_value = requires_equal_value, 288 requires_any_of = requires_any_of, 289 **kwargs 290 ) 291