| # Copyright 2023 The Pigweed Authors |
| # |
| # 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 |
| # |
| # https://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 the pw_cc_toolchain rule.""" |
| |
| load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") |
| load( |
| "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", |
| "action_config", |
| "feature", |
| "flag_group", |
| "flag_set", |
| "tool", |
| "variable_with_value", |
| ) |
| load( |
| "//cc_toolchain/private:providers.bzl", |
| "ToolchainFeatureInfo", |
| ) |
| load( |
| "//cc_toolchain/private:utils.bzl", |
| "ALL_ASM_ACTIONS", |
| "ALL_CPP_COMPILER_ACTIONS", |
| "ALL_C_COMPILER_ACTIONS", |
| "ALL_LINK_ACTIONS", |
| "check_deps", |
| ) |
| |
| PW_CC_TOOLCHAIN_CONFIG_ATTRS = { |
| "feature_deps": "pw_cc_toolchain_feature labels that provide features for this toolchain", |
| "ar": "Path to the tool to use for ar (static link) actions", |
| "cpp": "Path to the tool to use for C++ compile actions", |
| "gcc": "Path to the tool to use for C compile actions", |
| "gcov": "Pah to the tool to use for generating code coverag data", |
| "ld": "Path to the tool to use for link actions", |
| "strip": "Path to the tool to use for strip actions", |
| |
| # Attributes originally part of create_cc_toolchain_config_info. |
| "toolchain_identifier": "See documentation for cc_common.create_cc_toolchain_config_info()", |
| "host_system_name": "See documentation for cc_common.create_cc_toolchain_config_info()", |
| "target_system_name": "See documentation for cc_common.create_cc_toolchain_config_info()", |
| "target_cpu": "See documentation for cc_common.create_cc_toolchain_config_info()", |
| "target_libc": "See documentation for cc_common.create_cc_toolchain_config_info()", |
| "compiler": "See documentation for cc_common.create_cc_toolchain_config_info()", |
| "abi_version": "See documentation for cc_common.create_cc_toolchain_config_info()", |
| "abi_libc_version": "See documentation for cc_common.create_cc_toolchain_config_info()", |
| "cc_target_os": "See documentation for cc_common.create_cc_toolchain_config_info()", |
| } |
| |
| PW_CC_TOOLCHAIN_SHARED_ATTRS = ["toolchain_identifier"] |
| |
| PW_CC_TOOLCHAIN_BLOCKED_ATTRS = { |
| "toolchain_config": "pw_cc_toolchain includes a generated toolchain config", |
| "artifact_name_patterns": "pw_cc_toolchain does not yet support artifact name patterns", |
| "features": "Use feature_deps to add pw_cc_toolchain_feature deps to the toolchain", |
| "action_configs": "pw_cc_toolchain does not yet support action configs, use the \"ar\", \"cpp\", \"gcc\", \"gcov\", \"ld\", and \"strip\" attributes to set toolchain tools", |
| "cxx_builtin_include_directories": "Use a pw_cc_toolchain_feature to add cxx_builtin_include_directories", |
| "tool_paths": "pw_cc_toolchain does not support tool_paths, use \"ar\", \"cpp\", \"gcc\", \"gcov\", \"ld\", and \"strip\" attributes to set toolchain tools", |
| "make_variables": "pw_cc_toolchain does not yet support make variables", |
| "builtin_sysroot": "Use a pw_cc_toolchain_feature to add a builtin_sysroot", |
| } |
| |
| def _action_configs(action_tool, action_list): |
| """Binds a tool to an action. |
| |
| Args: |
| action_tool (File): Tool to bind to the specified actions. |
| action_list (List[str]): List of actions to bind to the specified tool. |
| |
| Returns: |
| action_config: A action_config binding the provided tool to the |
| specified actions. |
| """ |
| return [ |
| action_config( |
| action_name = action, |
| tools = [ |
| tool( |
| tool = action_tool, |
| ), |
| ], |
| ) |
| for action in action_list |
| ] |
| |
| def _archiver_flags_feature(is_mac): |
| """Returns our implementation of the legacy archiver_flags feature. |
| |
| We provide our own implementation of the archiver_flags. The default |
| implementation of this legacy feature at |
| https://github.com/bazelbuild/bazel/blob/252d36384b8b630d77d21fac0d2c5608632aa393/src/main/java/com/google/devtools/build/lib/rules/cpp/CppActionConfigs.java#L620-L660 |
| contains a bug that prevents it from working with llvm-libtool-darwin only |
| fixed in |
| https://github.com/bazelbuild/bazel/commit/ae7cfa59461b2c694226be689662d387e9c38427, |
| which has not yet been released. |
| |
| However, we don't merely fix the bug. Part of the Pigweed build involves |
| linking some empty libraries (with no object files). This leads to invoking |
| the archiving tool with no input files. Such an invocation is considered a |
| success by llvm-ar, but not by llvm-libtool-darwin. So for now, we use |
| flags appropriate for llvm-ar here, even on MacOS. |
| |
| Args: |
| is_mac: Does the toolchain this feature will be included in target MacOS? |
| |
| Returns: |
| The archiver_flags feature. |
| """ |
| |
| # TODO(b/297413805): Remove this implementation. |
| return feature( |
| name = "archiver_flags", |
| flag_sets = [ |
| flag_set( |
| actions = [ |
| ACTION_NAMES.cpp_link_static_library, |
| ], |
| flag_groups = [ |
| flag_group( |
| flags = _archiver_flags(is_mac), |
| ), |
| flag_group( |
| expand_if_available = "output_execpath", |
| flags = ["%{output_execpath}"], |
| ), |
| ], |
| ), |
| flag_set( |
| actions = [ |
| ACTION_NAMES.cpp_link_static_library, |
| ], |
| flag_groups = [ |
| flag_group( |
| expand_if_available = "libraries_to_link", |
| iterate_over = "libraries_to_link", |
| flag_groups = [ |
| flag_group( |
| expand_if_equal = variable_with_value( |
| name = "libraries_to_link.type", |
| value = "object_file", |
| ), |
| flags = ["%{libraries_to_link.name}"], |
| ), |
| flag_group( |
| expand_if_equal = variable_with_value( |
| name = "libraries_to_link.type", |
| value = "object_file_group", |
| ), |
| flags = ["%{libraries_to_link.object_files}"], |
| iterate_over = "libraries_to_link.object_files", |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ) |
| |
| def _archiver_flags(is_mac): |
| """Returns flags for llvm-ar.""" |
| if is_mac: |
| return ["--format=darwin", "rcs"] |
| else: |
| return ["rcsD"] |
| |
| def _pw_cc_toolchain_config_impl(ctx): |
| """Rule that provides a CcToolchainConfigInfo. |
| |
| Args: |
| ctx: The context of the current build rule. |
| |
| Returns: |
| CcToolchainConfigInfo |
| """ |
| check_deps(ctx) |
| all_actions = [] |
| all_actions += _action_configs(ctx.executable.gcc, ALL_ASM_ACTIONS) |
| all_actions += _action_configs(ctx.executable.gcc, ALL_C_COMPILER_ACTIONS) |
| all_actions += _action_configs(ctx.executable.cpp, ALL_CPP_COMPILER_ACTIONS) |
| all_actions += _action_configs(ctx.executable.cpp, ALL_LINK_ACTIONS) |
| |
| all_actions += [ |
| action_config( |
| action_name = ACTION_NAMES.cpp_link_static_library, |
| implies = ["archiver_flags", "linker_param_file"], |
| tools = [ |
| tool( |
| tool = ctx.executable.ar, |
| ), |
| ], |
| ), |
| action_config( |
| action_name = ACTION_NAMES.llvm_cov, |
| tools = [ |
| tool( |
| tool = ctx.executable.gcov, |
| ), |
| ], |
| ), |
| action_config( |
| action_name = ACTION_NAMES.strip, |
| tools = [ |
| tool( |
| tool = ctx.executable.strip, |
| ), |
| ], |
| ), |
| ] |
| |
| features = [dep[ToolchainFeatureInfo].feature for dep in ctx.attr.feature_deps] |
| features.append(_archiver_flags_feature(ctx.attr.target_libc == "macosx")) |
| builtin_include_dirs = [] |
| for dep in ctx.attr.feature_deps: |
| builtin_include_dirs.extend(dep[ToolchainFeatureInfo].cxx_builtin_include_directories) |
| |
| sysroot_dir = None |
| for dep in ctx.attr.feature_deps: |
| dep_sysroot = dep[ToolchainFeatureInfo].builtin_sysroot |
| if dep_sysroot: |
| if sysroot_dir: |
| fail("Failed to set sysroot at `{}`, already have sysroot at `{}` ".format(dep_sysroot, sysroot_dir)) |
| sysroot_dir = dep_sysroot |
| |
| return cc_common.create_cc_toolchain_config_info( |
| ctx = ctx, |
| action_configs = all_actions, |
| features = features, |
| cxx_builtin_include_directories = builtin_include_dirs, |
| toolchain_identifier = ctx.attr.toolchain_identifier, |
| host_system_name = ctx.attr.host_system_name, |
| target_system_name = ctx.attr.target_system_name, |
| target_cpu = ctx.attr.target_cpu, |
| target_libc = ctx.attr.target_libc, |
| compiler = ctx.attr.compiler, |
| abi_version = ctx.attr.abi_version, |
| abi_libc_version = ctx.attr.abi_libc_version, |
| builtin_sysroot = sysroot_dir, |
| cc_target_os = ctx.attr.cc_target_os, |
| ) |
| |
| pw_cc_toolchain_config = rule( |
| implementation = _pw_cc_toolchain_config_impl, |
| attrs = { |
| # Attributes new to this rule. |
| "feature_deps": attr.label_list(), |
| "gcc": attr.label(allow_single_file = True, executable = True, cfg = "exec"), |
| "ld": attr.label(allow_single_file = True, executable = True, cfg = "exec"), |
| "ar": attr.label(allow_single_file = True, executable = True, cfg = "exec"), |
| "cpp": attr.label(allow_single_file = True, executable = True, cfg = "exec"), |
| "gcov": attr.label(allow_single_file = True, executable = True, cfg = "exec"), |
| "strip": attr.label(allow_single_file = True, executable = True, cfg = "exec"), |
| |
| # Attributes from create_cc_toolchain_config_info. |
| "toolchain_identifier": attr.string(), |
| "host_system_name": attr.string(), |
| "target_system_name": attr.string(), |
| "target_cpu": attr.string(), |
| "target_libc": attr.string(), |
| "compiler": attr.string(), |
| "abi_version": attr.string(), |
| "abi_libc_version": attr.string(), |
| "cc_target_os": attr.string(), |
| }, |
| provides = [CcToolchainConfigInfo], |
| ) |
| |
| def _check_args(rule_label, kwargs): |
| """Checks that args provided to pw_cc_toolchain are valid. |
| |
| Args: |
| rule_label: The label of the pw_cc_toolchain rule. |
| kwargs: All attributes supported by pw_cc_toolchain. |
| |
| Returns: |
| None |
| """ |
| for attr_name, msg in PW_CC_TOOLCHAIN_BLOCKED_ATTRS.items(): |
| if attr_name in kwargs: |
| fail( |
| "Toolchain {} has an invalid attribute \"{}\": {}".format( |
| rule_label, |
| attr_name, |
| msg, |
| ), |
| ) |
| |
| def _split_args(kwargs, filter_dict): |
| """Splits kwargs into two dictionaries guided by a filter. |
| |
| All items in the kwargs dictionary whose keys are present in the filter |
| dictionary are returned as a new dictionary as the first item in the tuple. |
| All remaining arguments are returned as a dictionary in the second item of |
| the tuple. |
| |
| Args: |
| kwargs: Dictionary of args to split. |
| filter_dict: The dictionary used as the filter. |
| |
| Returns |
| Tuple[Dict, Dict] |
| """ |
| filtered_args = {} |
| remainder = {} |
| |
| for attr_name, val in kwargs.items(): |
| if attr_name in filter_dict: |
| filtered_args[attr_name] = val |
| else: |
| remainder[attr_name] = val |
| |
| return filtered_args, remainder |
| |
| def pw_cc_toolchain(**kwargs): |
| """A bound cc_toolchain and pw_cc_toolchain_config pair. |
| |
| Args: |
| **kwargs: All attributes supported by cc_toolchain and pw_cc_toolchain_config. |
| """ |
| |
| _check_args(native.package_relative_label(kwargs["name"]), kwargs) |
| |
| cc_toolchain_config_args, cc_toolchain_args = _split_args(kwargs, PW_CC_TOOLCHAIN_CONFIG_ATTRS) |
| |
| # Bind pw_cc_toolchain_config and the cc_toolchain. |
| config_name = "{}_config".format(cc_toolchain_args["name"]) |
| cc_toolchain_config_args["name"] = config_name |
| cc_toolchain_args["toolchain_config"] = ":{}".format(config_name) |
| |
| # Copy over arguments that should be shared by both rules. |
| for arg_name in PW_CC_TOOLCHAIN_SHARED_ATTRS: |
| if arg_name in cc_toolchain_config_args: |
| cc_toolchain_args[arg_name] = cc_toolchain_config_args[arg_name] |
| |
| pw_cc_toolchain_config(**cc_toolchain_config_args) |
| native.cc_toolchain(**cc_toolchain_args) |