| # Copyright 2023 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. | |
| """Implementation of cc_tool""" | |
| load("@bazel_skylib//rules/directory:providers.bzl", "DirectoryInfo") | |
| load("//cc/toolchains/impl:collect.bzl", "collect_data", "collect_provider") | |
| load("//cc/toolchains/impl:label_utils.bzl", "deduplicate_label_list") | |
| load("//cc/toolchains/impl:nested_args.bzl", "format_dict_values") | |
| load( | |
| ":cc_toolchain_info.bzl", | |
| "ExecutionRequirementsInfo", | |
| "ToolCapabilityInfo", | |
| "ToolInfo", | |
| ) | |
| def _cc_tool_impl(ctx): | |
| exe_info = ctx.attr.src[DefaultInfo] | |
| if exe_info.files_to_run != None and exe_info.files_to_run.executable != None: | |
| exe = exe_info.files_to_run.executable | |
| elif len(exe_info.files.to_list()) == 1: | |
| exe = exe_info.files.to_list()[0] | |
| else: | |
| fail("Expected cc_tool's src attribute to be either an executable or a single file") | |
| format_targets = {} | |
| for i in range(len(ctx.attr.format_keys)): | |
| format_targets[ctx.attr.format_keys[i]] = ctx.attr.format_values[ctx.attr.format_value_indexes[i]] | |
| env, _ = format_dict_values( | |
| env = ctx.attr.env, | |
| format = format_targets, | |
| must_use = format_targets.keys(), | |
| ) | |
| execution_requirements = [] | |
| for info in collect_provider(ctx.attr.execution_requirements, ExecutionRequirementsInfo): | |
| execution_requirements.extend(info.requirements) | |
| runfiles = collect_data(ctx, ctx.attr.data + [ctx.attr.src]) | |
| tool = ToolInfo( | |
| label = ctx.label, | |
| exe = exe, | |
| runfiles = runfiles, | |
| execution_requirements = tuple(ctx.attr.tags + execution_requirements), | |
| env = env, | |
| allowlist_include_directories = depset( | |
| direct = [d[DirectoryInfo] for d in ctx.attr.allowlist_include_directories], | |
| ), | |
| capabilities = tuple(collect_provider(ctx.attr.capabilities, ToolCapabilityInfo)), | |
| ) | |
| link = ctx.actions.declare_file(ctx.label.name) | |
| ctx.actions.symlink( | |
| output = link, | |
| target_file = exe, | |
| is_executable = True, | |
| ) | |
| return [ | |
| tool, | |
| # This isn't required, but now we can do "bazel run <tool>", which can | |
| # be very helpful when debugging toolchains. | |
| DefaultInfo( | |
| files = depset([link]), | |
| runfiles = runfiles, | |
| executable = link, | |
| ), | |
| ] | |
| _cc_tool = rule( | |
| implementation = _cc_tool_impl, | |
| # @unsorted-dict-items | |
| attrs = { | |
| "src": attr.label( | |
| allow_files = True, | |
| doc = """The underlying binary that this tool represents. | |
| Usually just a single prebuilt (eg. @toolchain//:bin/clang), but may be any | |
| executable label. | |
| """, | |
| ), | |
| "data": attr.label_list( | |
| allow_files = True, | |
| doc = """Additional files that are required for this tool to run. | |
| Frequently, clang and gcc require additional files to execute as they often shell out to | |
| other binaries (e.g. `cc1`). | |
| """, | |
| ), | |
| "allowlist_include_directories": attr.label_list( | |
| providers = [DirectoryInfo], | |
| doc = """Include paths implied by using this tool. | |
| Compilers may include a set of built-in headers that are implicitly available | |
| unless flags like `-nostdinc` are provided. 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)`. | |
| """, | |
| ), | |
| "env": attr.string_dict( | |
| doc = """Environment variables to apply when running this tool. | |
| Format expansion is performed on values using the format attribute. | |
| """, | |
| ), | |
| "execution_requirements": attr.label_list( | |
| providers = [ExecutionRequirementsInfo], | |
| doc = """Additional execution requirements for actions that run this tool. | |
| Each label must provide `ExecutionRequirementsInfo`. For fixed execution requirements, use `tags`. | |
| """, | |
| ), | |
| "format_keys": attr.string_list( | |
| doc = """Format strings used in substitutions for env values. | |
| Use the cc_tool macro's format attribute instead. | |
| """, | |
| ), | |
| "format_values": attr.label_list( | |
| allow_files = True, | |
| doc = """Targets used in substitutions for env values. | |
| Use the cc_tool macro's format attribute instead. | |
| """, | |
| ), | |
| "format_value_indexes": attr.int_list( | |
| doc = """Index of the target in format_values for each format key. | |
| Use the cc_tool macro's format attribute instead. | |
| """, | |
| ), | |
| "capabilities": attr.label_list( | |
| providers = [ToolCapabilityInfo], | |
| doc = """Declares that a tool is capable of doing something. | |
| For example, `@rules_cc//cc/toolchains/capabilities:supports_pic`. | |
| """, | |
| ), | |
| }, | |
| provides = [ToolInfo], | |
| doc = """Declares a tool for use by toolchain actions. | |
| `cc_tool` rules are used in a `cc_tool_map` rule to ensure all files and | |
| metadata required to run a tool are available when constructing a `cc_toolchain`. | |
| In general, include all files that are always required to run a tool (e.g. libexec/** and | |
| cross-referenced tools in bin/*) in the [data](#cc_tool-data) attribute. If some files are only | |
| required when certain flags are passed to the tool, consider using a `cc_args` rule to | |
| bind the files to the flags that require them. This reduces the overhead required to properly | |
| enumerate a sandbox with all the files required to run a tool, and ensures that there isn't | |
| unintentional leakage across configurations and actions. | |
| Example: | |
| ``` | |
| load("//cc/toolchains:tool.bzl", "cc_tool") | |
| cc_tool( | |
| name = "clang", | |
| src = "@llvm_toolchain//:bin/clang", | |
| # Suppose clang needs libc to run. | |
| data = ["@llvm_toolchain//:lib/x86_64-linux-gnu/libc.so.6"] | |
| capabilities = ["//cc/toolchains/capabilities:supports_pic"], | |
| ) | |
| ``` | |
| """, | |
| executable = True, | |
| ) | |
| def cc_tool( | |
| *, | |
| name, | |
| src = None, | |
| data = None, | |
| allowlist_include_directories = None, | |
| env = None, | |
| execution_requirements = None, | |
| format = {}, | |
| capabilities = None, | |
| **kwargs): | |
| """Declares a tool for use by toolchain actions. | |
| `cc_tool` rules are used in a `cc_tool_map` rule to ensure all files and | |
| metadata required to run a tool are available when constructing a `cc_toolchain`. | |
| In general, include all files that are always required to run a tool (e.g. libexec/** and | |
| cross-referenced tools in bin/*) in the [data](#cc_tool-data) attribute. If some files are only | |
| required when certain flags are passed to the tool, consider using a `cc_args` rule to | |
| bind the files to the flags that require them. This reduces the overhead required to properly | |
| enumerate a sandbox with all the files required to run a tool, and ensures that there isn't | |
| unintentional leakage across configurations and actions. | |
| Example: | |
| ``` | |
| load("//cc/toolchains:tool.bzl", "cc_tool") | |
| cc_tool( | |
| name = "clang", | |
| src = "@llvm_toolchain//:bin/clang", | |
| # Suppose clang needs libc to run. | |
| data = ["@llvm_toolchain//:lib/x86_64-linux-gnu/libc.so.6"] | |
| capabilities = ["//cc/toolchains/capabilities:supports_pic"], | |
| ) | |
| ``` | |
| Args: | |
| name: (str) The name of the target. | |
| src: (Label) The underlying binary that this tool represents. | |
| Usually just a single prebuilt (e.g. @toolchain//:bin/clang), but may be any | |
| executable label. | |
| data: (List[Label]) Additional files that are required for this tool to run. | |
| Frequently, clang and gcc require additional files to execute as they often shell out to | |
| other binaries (e.g. `cc1`). | |
| allowlist_include_directories: (List[Label]) Include paths implied by using this tool. | |
| Compilers may include a set of built-in headers that are implicitly available | |
| unless flags like `-nostdinc` are provided. 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)`. | |
| env: (Dict[str, str]) Environment variables to apply when running this tool. | |
| Format expansion is performed on values using `format`. | |
| execution_requirements: (List[Label]) Additional execution requirements for actions that | |
| run this tool. Each label must provide `ExecutionRequirementsInfo`. For fixed | |
| execution requirements, use `tags`. | |
| format: (Dict[str, Label]) A mapping of format strings to the label of a corresponding | |
| target. This target can be a `directory`, `subdirectory`, or a single file that the | |
| value should be pulled from. All instances of `{variable_name}` in the `env` dictionary | |
| values will be replaced with the expanded value in this dictionary. | |
| capabilities: (List[Label]) Declares that a tool is capable of doing something. | |
| For example, `@rules_cc//cc/toolchains/capabilities:supports_pic`. | |
| **kwargs: [common attributes](https://bazel.build/reference/be/common-definitions#common-attributes) | |
| that should be applied to this rule. | |
| """ | |
| format_keys = format.keys() | |
| format_values = deduplicate_label_list( | |
| name = name, | |
| labels = [format[key] for key in format_keys], | |
| ) | |
| return _cc_tool( | |
| name = name, | |
| src = src, | |
| data = data, | |
| allowlist_include_directories = allowlist_include_directories, | |
| env = env, | |
| execution_requirements = execution_requirements, | |
| format_keys = format_keys, | |
| format_values = format_values.labels, | |
| format_value_indexes = format_values.indexes, | |
| capabilities = capabilities, | |
| **kwargs | |
| ) |