| # 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. | |
| """All providers for rule-based bazel toolchain config.""" | |
| load("@bazel_skylib//rules/directory:providers.bzl", "DirectoryInfo") | |
| load("//cc/private:paths.bzl", "is_path_absolute") | |
| load("//cc/toolchains/impl:args_utils.bzl", "validate_env_variables", "validate_nested_args") | |
| load( | |
| "//cc/toolchains/impl:collect.bzl", | |
| "collect_action_types", | |
| "collect_files", | |
| "collect_provider", | |
| ) | |
| load( | |
| "//cc/toolchains/impl:nested_args.bzl", | |
| "NESTED_ARGS_ATTRS", | |
| "format_dict_values", | |
| "nested_args_provider_from_ctx", | |
| ) | |
| load( | |
| ":cc_toolchain_info.bzl", | |
| "ActionTypeSetInfo", | |
| "ArgsInfo", | |
| "ArgsListInfo", | |
| "BuiltinVariablesInfo", | |
| "EnvInfo", | |
| "FeatureConstraintInfo", | |
| "VariableInfo", | |
| ) | |
| visibility("public") | |
| def _cc_args_impl(ctx): | |
| actions = collect_action_types(ctx.attr.actions) | |
| format_targets = {k: v for v, k in ctx.attr.format.items()} | |
| formatted_env, used_format_vars = format_dict_values( | |
| env = ctx.attr.env, | |
| must_use = [], # checking for unused variables in done when formatting `args`. | |
| format = {k: struct(__raw_string = v) for k, v in ctx.var.items()} | format_targets, | |
| ) | |
| used_env_variables = [ | |
| var | |
| for var in used_format_vars | |
| if var in format_targets and VariableInfo in format_targets[var] | |
| ] | |
| # Ignore file / directory replacements for validation | |
| # Filter out variables from ctx.var which don't have to be used | |
| used_format_vars = [ | |
| v | |
| for v in used_format_vars | |
| if v in format_targets and VariableInfo in format_targets[v] | |
| ] | |
| for path in ctx.attr.allowlist_absolute_include_directories: | |
| if not is_path_absolute(path): | |
| fail("`{}` is not an absolute paths".format(path)) | |
| nested = None | |
| if ctx.attr.args or ctx.attr.nested: | |
| # Forward the format variables used by the env formatting so they don't trigger | |
| # errors if they go unused during the argument formatting. | |
| nested = nested_args_provider_from_ctx(ctx, used_format_vars) | |
| validate_nested_args( | |
| variables = ctx.attr._variables[BuiltinVariablesInfo].variables, | |
| nested_args = nested, | |
| actions = actions.to_list(), | |
| label = ctx.label, | |
| ) | |
| files = nested.files | |
| else: | |
| files = collect_files(ctx.attr.data) | |
| requires = collect_provider(ctx.attr.requires_any_of, FeatureConstraintInfo) | |
| env = EnvInfo( | |
| label = ctx.label, | |
| entries = formatted_env, | |
| requires_not_none = ctx.attr.requires_not_none[VariableInfo].name if ctx.attr.requires_not_none else None, | |
| ) | |
| validate_env_variables( | |
| actions = actions.to_list(), | |
| env = env, | |
| variables = ctx.attr._variables[BuiltinVariablesInfo].variables, | |
| used_format_vars = used_env_variables, | |
| ) | |
| args = ArgsInfo( | |
| label = ctx.label, | |
| actions = actions, | |
| requires_any_of = tuple(requires), | |
| nested = nested, | |
| env = env, | |
| files = files, | |
| allowlist_include_directories = depset( | |
| direct = [d[DirectoryInfo] for d in ctx.attr.allowlist_include_directories], | |
| ), | |
| allowlist_absolute_include_directories = depset( | |
| direct = ctx.attr.allowlist_absolute_include_directories, | |
| ), | |
| ) | |
| return [ | |
| args, | |
| ArgsListInfo( | |
| label = ctx.label, | |
| args = tuple([args]), | |
| files = files, | |
| by_action = tuple([ | |
| struct(action = action, args = tuple([args]), files = files) | |
| for action in actions.to_list() | |
| ]), | |
| allowlist_include_directories = args.allowlist_include_directories, | |
| allowlist_absolute_include_directories = args.allowlist_absolute_include_directories, | |
| ), | |
| ] | |
| _cc_args = rule( | |
| implementation = _cc_args_impl, | |
| attrs = { | |
| "actions": attr.label_list( | |
| providers = [ActionTypeSetInfo], | |
| mandatory = True, | |
| doc = """See documentation for cc_args macro wrapper.""", | |
| ), | |
| "allowlist_absolute_include_directories": attr.string_list( | |
| doc = """See documentation for cc_args macro wrapper.""", | |
| ), | |
| "allowlist_include_directories": attr.label_list( | |
| providers = [DirectoryInfo], | |
| doc = """See documentation for cc_args macro wrapper.""", | |
| ), | |
| "env": attr.string_dict( | |
| doc = """See documentation for cc_args macro wrapper.""", | |
| ), | |
| "requires_any_of": attr.label_list( | |
| providers = [FeatureConstraintInfo], | |
| doc = """See documentation for cc_args macro wrapper.""", | |
| ), | |
| "_variables": attr.label( | |
| default = "//cc/toolchains/variables:variables", | |
| ), | |
| } | NESTED_ARGS_ATTRS, | |
| provides = [ArgsInfo], | |
| doc = """Declares a list of arguments bound to a set of actions. | |
| Roughly equivalent to ctx.actions.args() | |
| Examples: | |
| cc_args( | |
| name = "warnings_as_errors", | |
| args = ["-Werror"], | |
| ) | |
| """, | |
| ) | |
| def cc_args( | |
| *, | |
| name, | |
| actions = None, | |
| allowlist_include_directories = None, | |
| allowlist_absolute_include_directories = None, | |
| args = None, | |
| data = None, | |
| env = None, | |
| format = {}, | |
| iterate_over = None, | |
| nested = None, | |
| requires_not_none = None, | |
| requires_none = None, | |
| requires_true = None, | |
| requires_false = None, | |
| requires_equal = None, | |
| requires_equal_value = None, | |
| requires_any_of = None, | |
| **kwargs): | |
| """Action-specific arguments for use with a `cc_toolchain`. | |
| This rule is the fundamental building building block for every toolchain tool invocation. Each | |
| argument expressed in a toolchain tool invocation (e.g. `gcc`, `llvm-ar`) is declared in a | |
| `cc_args` rule that applies an ordered list of arguments to a set of toolchain | |
| actions. `cc_args` rules can be added unconditionally to a | |
| `cc_toolchain`, conditionally via `select()` statements, or dynamically via an | |
| intermediate `cc_feature`. | |
| Conceptually, this is similar to the old `CFLAGS`, `CPPFLAGS`, etc. environment variables that | |
| many build systems use to determine which flags to use for a given action. The significant | |
| difference is that `cc_args` rules are declared in a structured way that allows for | |
| significantly more powerful and sharable toolchain configurations. Also, due to Bazel's more | |
| granular action types, it's possible to bind flags to very specific actions (e.g. LTO indexing | |
| for an executable vs a dynamic library) multiple different actions (e.g. C++ compile and link | |
| simultaneously). | |
| Example usage: | |
| ``` | |
| load("//cc/toolchains:args.bzl", "cc_args") | |
| # Basic usage: a trivial flag. | |
| # | |
| # An example of expressing `-Werror` as a `cc_args` rule. | |
| cc_args( | |
| name = "warnings_as_errors", | |
| actions = [ | |
| # Applies to all C/C++ compile actions. | |
| "//cc/toolchains/actions:compile_actions", | |
| ], | |
| args = ["-Werror"], | |
| ) | |
| # Basic usage: ordered flags. | |
| # | |
| # An example of linking against libc++, which uses two flags that must be applied in order. | |
| cc_args( | |
| name = "link_libcxx", | |
| actions = [ | |
| # Applies to all link actions. | |
| "//cc/toolchains/actions:link_actions", | |
| ], | |
| # On tool invocation, this appears as `-Xlinker -lc++`. Nothing will ever end up between | |
| # the two flags. | |
| args = [ | |
| "-Xlinker", | |
| "-lc++", | |
| ], | |
| ) | |
| # Advanced usage: built-in variable expansions. | |
| # | |
| # Expands to `-L/path/to/search_dir` for each directory in the built-in variable | |
| # `library_search_directories`. This variable is managed internally by Bazel through inherent | |
| # behaviors of Bazel and the interactions between various C/C++ build rules. | |
| cc_args( | |
| name = "library_search_directories", | |
| actions = [ | |
| "//cc/toolchains/actions:link_actions", | |
| ], | |
| args = ["-L{search_dir}"], | |
| iterate_over = "//cc/toolchains/variables:library_search_directories", | |
| requires_not_none = "//cc/toolchains/variables:library_search_directories", | |
| format = { | |
| "search_dir": "//cc/toolchains/variables:library_search_directories", | |
| }, | |
| ) | |
| ``` | |
| For more extensive examples, see the usages here: | |
| https://github.com/bazelbuild/rules_cc/tree/main/cc/toolchains/args | |
| Args: | |
| name: (str) The name of the target. | |
| actions: (List[Label]) A list of labels of `cc_action_type` or | |
| `cc_action_type_set` rules that dictate which actions these | |
| arguments should be applied to. | |
| allowlist_include_directories: (List[Label]) A list of include paths that are implied by | |
| using this rule. These must point to a skylib | |
| [directory](https://github.com/bazelbuild/bazel-skylib/tree/main/docs/directory_doc.md#directory) | |
| or [subdirectory](https://github.com/bazelbuild/bazel-skylib/tree/main/docs/directory_subdirectory_doc.md#subdirectory) rule. | |
| Some flags (e.g. --sysroot) imply certain include paths are available despite | |
| not explicitly specifying a normal include path flag (`-I`, `-isystem`, etc.). | |
| Bazel checks that all included headers are properly provided by a dependency or | |
| allowlisted through this mechanism. | |
| As a rule of thumb, only use this if Bazel is complaining about absolute paths in | |
| your toolchain and you've ensured that the toolchain is compiling with the | |
| `-no-canonical-prefixes` and/or `-fno-canonical-system-headers` arguments. | |
| These files are not automatically passed to each action. If they | |
| need to be, add them to 'data' as well. | |
| This can help work around errors like: | |
| `the source file 'main.c' includes the following non-builtin files with absolute paths | |
| (if these are builtin files, make sure these paths are in your toolchain)`. | |
| allowlist_absolute_include_directories: (List[str]) Allowlists absolute include directories, | |
| preventing Bazel from emitting errors when an #include of local system files in the | |
| directory occurs. Be careful when adding directories to this list, as it inherently | |
| causes leaks in hermeticity. Prefer to reserve use of this for cases like Xcode, where | |
| the conventional expectation is to allowlist well-known system-absolute include paths | |
| rather than redistributing the SDK. | |
| args: (List[str]) The command-line arguments that are applied by using this rule. This is | |
| mutually exclusive with [nested](#cc_args-nested). | |
| data: (List[Label]) A list of runtime data dependencies that are required for these | |
| arguments to work as intended. | |
| env: (Dict[str, str]) Environment variables that should be set when the tool is invoked. | |
| format: (Dict[str, Label]) A mapping of format strings to the label of a corresponding | |
| target. This target can be a `directory`, `subdirectory`, `cc_variable`, a build setting | |
| (a target that provides `BuildSettingInfo`), or a single file that the value should be | |
| pulled from. All instances of `{variable_name}` in the `args` list will be replaced with | |
| the expanded value in this dictionary. | |
| The complete list of possible variables can be found in | |
| https://github.com/bazelbuild/rules_cc/tree/main/cc/toolchains/variables/BUILD. | |
| It is not possible to declare custom variables--these are inherent to Bazel itself. | |
| iterate_over: (Label) The label of a `cc_variable` that should be iterated over. This is | |
| intended for use with built-in variables that are lists. | |
| nested: (List[Label]) A list of `cc_nested_args` rules that should be | |
| expanded to command-line arguments when this rule is used. This is mutually exclusive | |
| with [args](#cc_args-args). | |
| requires_not_none: (Label) The label of a `cc_variable` that should be checked | |
| for existence before expanding this rule. If the variable is None, this rule will be | |
| ignored. | |
| requires_none: (Label) The label of a `cc_variable` that should be checked for | |
| non-existence before expanding this rule. If the variable is not None, this rule will be | |
| ignored. | |
| requires_true: (Label) The label of a `cc_variable` that should be checked for | |
| truthiness before expanding this rule. If the variable is false, this rule will be | |
| ignored. | |
| requires_false: (Label) The label of a `cc_variable` that should be checked | |
| for falsiness before expanding this rule. If the variable is true, this rule will be | |
| ignored. | |
| requires_equal: (Label) The label of a `cc_variable` that should be checked | |
| for equality before expanding this rule. If the variable is not equal to | |
| (requires_equal_value)[#cc_args-requires_equal_value], this rule will be ignored. | |
| requires_equal_value: (str) The value to compare (requires_equal)[#cc_args-requires_equal] | |
| against. | |
| requires_any_of: (List[Label]) These arguments will be used | |
| in a tool invocation when at least one of the [cc_feature_constraint](#cc_feature_constraint) | |
| entries in this list are satisfied. If omitted, this flag set will be enabled | |
| unconditionally. | |
| **kwargs: [common attributes](https://bazel.build/reference/be/common-definitions#common-attributes) that should be applied to this rule. | |
| """ | |
| return _cc_args( | |
| name = name, | |
| actions = actions, | |
| allowlist_include_directories = allowlist_include_directories, | |
| allowlist_absolute_include_directories = allowlist_absolute_include_directories, | |
| args = args, | |
| data = data, | |
| env = env, | |
| # We flip the key/value pairs in the dictionary here because Bazel doesn't have a | |
| # string-keyed label dict attribute type. | |
| format = {k: v for v, k in format.items()}, | |
| iterate_over = iterate_over, | |
| nested = nested, | |
| requires_not_none = requires_not_none, | |
| requires_none = requires_none, | |
| requires_true = requires_true, | |
| requires_false = requires_false, | |
| requires_equal = requires_equal, | |
| requires_equal_value = requires_equal_value, | |
| requires_any_of = requires_any_of, | |
| **kwargs | |
| ) |