| # 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 |
| ) |