1# Copyright 2023 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://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, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14"""Implementation of the pw_cc_toolchain rule.""" 15 16load("//features:builtin_features.bzl", "BUILTIN_FEATURES") 17load( 18 ":providers.bzl", 19 "PwActionConfigSetInfo", 20 "PwActionNameSetInfo", 21 "PwExtraActionFilesSetInfo", 22 "PwFeatureInfo", 23 "PwFeatureSetInfo", 24 "PwFlagSetInfo", 25 "PwToolchainConfigInfo", 26) 27load( 28 ":utils.bzl", 29 "ALL_FILE_GROUPS", 30 "to_untyped_config", 31) 32 33# These attributes of pw_cc_toolchain are deprecated. 34PW_CC_TOOLCHAIN_DEPRECATED_TOOL_ATTRS = { 35 "ar": "Path to the tool to use for `ar` (static link) actions", 36 "cpp": "Path to the tool to use for C++ compile actions", 37 "gcc": "Path to the tool to use for C compile actions", 38 "gcov": "Path to the tool to use for generating code coverage data", 39 "ld": "Path to the tool to use for link actions", 40 "objcopy": "Path to the tool to use for objcopy actions", 41 "objdump": "Path to the tool to use for objdump actions", 42 "strip": "Path to the tool to use for strip actions", 43} 44 45PW_CC_TOOLCHAIN_CONFIG_ATTRS = { 46 "abi_libc_version": "See documentation for cc_common.create_cc_toolchain_config_info()", 47 "abi_version": "See documentation for cc_common.create_cc_toolchain_config_info()", 48 "action_configs": "List of `pw_cc_action_config` labels that bind tools to the appropriate actions", 49 "builtin_sysroot": "See documentation for cc_common.create_cc_toolchain_config_info()", 50 "cc_target_os": "See documentation for cc_common.create_cc_toolchain_config_info()", 51 "compiler": "See documentation for cc_common.create_cc_toolchain_config_info()", 52 "cxx_builtin_include_directories": "See documentation for cc_common.create_cc_toolchain_config_info()", 53 "extra_action_files": "Files that are required to run specific actions.", 54 "flag_sets": "List of `pw_cc_flag_set`s to unconditionally apply to their respective action configs", 55 "host_system_name": "See documentation for cc_common.create_cc_toolchain_config_info()", 56 "target_cpu": "See documentation for cc_common.create_cc_toolchain_config_info()", 57 "target_libc": "See documentation for cc_common.create_cc_toolchain_config_info()", 58 "target_system_name": "See documentation for cc_common.create_cc_toolchain_config_info()", 59 "toolchain_features": "List of `pw_cc_feature`s that this toolchain supports", 60 61 # Attributes originally part of create_cc_toolchain_config_info. 62 "toolchain_identifier": "See documentation for cc_common.create_cc_toolchain_config_info()", 63} 64 65PW_CC_TOOLCHAIN_SHARED_ATTRS = ["toolchain_identifier"] 66 67PW_CC_TOOLCHAIN_BLOCKED_ATTRS = { 68 "artifact_name_patterns": "pw_cc_toolchain does not yet support artifact name patterns", 69 "features": "Use toolchain_features to add pw_cc_toolchain_feature deps to the toolchain", 70 "make_variables": "pw_cc_toolchain does not yet support make variables", 71 "tool_paths": "pw_cc_toolchain does not support tool_paths, use \"action_configs\" to set toolchain tools", 72 "toolchain_config": "pw_cc_toolchain includes a generated toolchain config", 73} 74 75def _pw_cc_toolchain_config_impl(ctx): 76 """Rule that provides a CcToolchainConfigInfo. 77 78 Args: 79 ctx: The context of the current build rule. 80 81 Returns: 82 CcToolchainConfigInfo 83 """ 84 builtin_include_dirs = ctx.attr.cxx_builtin_include_directories if ctx.attr.cxx_builtin_include_directories else [] 85 sysroot_dir = ctx.attr.builtin_sysroot if ctx.attr.builtin_sysroot else None 86 87 feature_set = PwFeatureSetInfo(features = depset( 88 [ft[PwFeatureInfo] for ft in ctx.attr._builtin_features], 89 transitive = [ 90 feature_set[PwFeatureSetInfo].features 91 for feature_set in ctx.attr.toolchain_features 92 ], 93 )) 94 action_config_set = PwActionConfigSetInfo( 95 label = ctx.label, 96 action_configs = depset(transitive = [ 97 acs[PwActionConfigSetInfo].action_configs 98 for acs in ctx.attr.action_configs 99 ]), 100 ) 101 extra_action_files = PwExtraActionFilesSetInfo(srcs = depset(transitive = [ 102 ffa[PwExtraActionFilesSetInfo].srcs 103 for ffa in ctx.attr.extra_action_files 104 ])) 105 flag_sets = [fs[PwFlagSetInfo] for fs in ctx.attr.flag_sets] 106 out = to_untyped_config(feature_set, action_config_set, flag_sets, extra_action_files) 107 108 extra = [] 109 return [ 110 cc_common.create_cc_toolchain_config_info( 111 ctx = ctx, 112 action_configs = out.action_configs, 113 features = out.features, 114 cxx_builtin_include_directories = builtin_include_dirs, 115 toolchain_identifier = ctx.attr.toolchain_identifier, 116 host_system_name = ctx.attr.host_system_name, 117 target_system_name = ctx.attr.target_system_name, 118 target_cpu = ctx.attr.target_cpu, 119 target_libc = ctx.attr.target_libc, 120 compiler = ctx.attr.compiler, 121 abi_version = ctx.attr.abi_version, 122 abi_libc_version = ctx.attr.abi_libc_version, 123 builtin_sysroot = sysroot_dir, 124 cc_target_os = ctx.attr.cc_target_os, 125 ), 126 PwToolchainConfigInfo(action_to_files = out.action_to_files), 127 DefaultInfo(files = depset(transitive = extra + out.action_to_files.values())), 128 ] 129 130pw_cc_toolchain_config = rule( 131 implementation = _pw_cc_toolchain_config_impl, 132 attrs = { 133 "abi_libc_version": attr.string(), 134 "abi_version": attr.string(), 135 # Attributes new to this rule. 136 "action_configs": attr.label_list(providers = [PwActionConfigSetInfo]), 137 "builtin_sysroot": attr.string(), 138 "cc_target_os": attr.string(), 139 "compiler": attr.string(), 140 "cxx_builtin_include_directories": attr.string_list(), 141 "extra_action_files": attr.label_list(providers = [PwExtraActionFilesSetInfo]), 142 "flag_sets": attr.label_list(providers = [PwFlagSetInfo]), 143 "host_system_name": attr.string(), 144 "target_cpu": attr.string(), 145 "target_libc": attr.string(), 146 "target_system_name": attr.string(), 147 "toolchain_features": attr.label_list(providers = [PwFeatureSetInfo]), 148 149 # Attributes from create_cc_toolchain_config_info. 150 "toolchain_identifier": attr.string(), 151 "_builtin_features": attr.label_list(default = BUILTIN_FEATURES), 152 }, 153 provides = [CcToolchainConfigInfo, PwToolchainConfigInfo], 154) 155 156def _check_args(rule_label, kwargs): 157 """Checks that args provided to pw_cc_toolchain are valid. 158 159 Args: 160 rule_label: The label of the pw_cc_toolchain rule. 161 kwargs: All attributes supported by pw_cc_toolchain. 162 163 Returns: 164 None 165 """ 166 for attr_name, msg in PW_CC_TOOLCHAIN_BLOCKED_ATTRS.items(): 167 if attr_name in kwargs: 168 fail( 169 "Toolchain {} has an invalid attribute \"{}\": {}".format( 170 rule_label, 171 attr_name, 172 msg, 173 ), 174 ) 175 176def _split_args(kwargs, filter_dict): 177 """Splits kwargs into two dictionaries guided by a filter. 178 179 All items in the kwargs dictionary whose keys are present in the filter 180 dictionary are returned as a new dictionary as the first item in the tuple. 181 All remaining arguments are returned as a dictionary in the second item of 182 the tuple. 183 184 Args: 185 kwargs: dictionary of args to split. 186 filter_dict: The dictionary used as the filter. 187 188 Returns: 189 tuple[dict, dict] 190 """ 191 filtered_args = {} 192 remainder = {} 193 194 for attr_name, val in kwargs.items(): 195 if attr_name in ALL_FILE_GROUPS: 196 fail("Don't use %s. Instead, use pw_cc_action_files" % attr_name) 197 elif attr_name in filter_dict: 198 filtered_args[attr_name] = val 199 else: 200 remainder[attr_name] = val 201 202 return filtered_args, remainder 203 204def _cc_file_collector_impl(ctx): 205 actions = depset(transitive = [ 206 names[PwActionNameSetInfo].actions 207 for names in ctx.attr.actions 208 ]).to_list() 209 action_to_files = ctx.attr.config[PwToolchainConfigInfo].action_to_files 210 211 extra = [] 212 return [DefaultInfo(files = depset(transitive = [ 213 action_to_files[action] 214 for action in actions 215 ] + extra))] 216 217_cc_file_collector = rule( 218 implementation = _cc_file_collector_impl, 219 attrs = { 220 "actions": attr.label_list(providers = [PwActionNameSetInfo], mandatory = True), 221 "config": attr.label(providers = [PwToolchainConfigInfo], mandatory = True), 222 }, 223) 224 225def pw_cc_toolchain(name, action_config_flag_sets = None, **kwargs): 226 """A suite of cc_toolchain, pw_cc_toolchain_config, and *_files rules. 227 228 Generated rules: 229 {name}: A `cc_toolchain` for this toolchain. 230 _{name}_config: A `pw_cc_toolchain_config` for this toolchain. 231 _{name}_*_files: Generated rules that group together files for 232 "ar_files", "as_files", "compiler_files", "coverage_files", 233 "dwp_files", "linker_files", "objcopy_files", and "strip_files" 234 normally enumerated as part of the `cc_toolchain` rule. 235 236 Args: 237 name: str: The name of the label for the toolchain. 238 action_config_flag_sets: Deprecated. Do not use. 239 **kwargs: All attributes supported by either cc_toolchain or pw_cc_toolchain_config. 240 """ 241 242 # TODO(b/322872628): Remove this once it's no longer in use. 243 if action_config_flag_sets != None: 244 kwargs["flag_sets"] = action_config_flag_sets 245 246 _check_args(native.package_relative_label(name), kwargs) 247 248 # Split args between `pw_cc_toolchain_config` and `native.cc_toolchain`. 249 cc_toolchain_config_args, cc_toolchain_args = _split_args(kwargs, PW_CC_TOOLCHAIN_CONFIG_ATTRS | PW_CC_TOOLCHAIN_DEPRECATED_TOOL_ATTRS) 250 251 # Bind pw_cc_toolchain_config and the cc_toolchain. 252 config_name = "_{}_config".format(name) 253 pw_cc_toolchain_config( 254 name = config_name, 255 visibility = ["//visibility:private"], 256 compatible_with = kwargs.get("compatible_with", None), 257 target_compatible_with = kwargs.get("target_compatible_with", None), 258 exec_compatible_with = kwargs.get("exec_compatible_with", None), 259 tags = kwargs.get("tags", None), 260 **cc_toolchain_config_args 261 ) 262 263 for group, actions in ALL_FILE_GROUPS.items(): 264 group_name = "_{}_{}".format(name, group) 265 _cc_file_collector( 266 name = group_name, 267 config = config_name, 268 actions = actions, 269 visibility = ["//visibility:private"], 270 compatible_with = kwargs.get("compatible_with", None), 271 target_compatible_with = kwargs.get("target_compatible_with", None), 272 exec_compatible_with = kwargs.get("exec_compatible_with", None), 273 tags = kwargs.get("tags", None), 274 ) 275 cc_toolchain_args[group] = group_name 276 277 # Copy over arguments that should be shared by both rules. 278 for arg_name in PW_CC_TOOLCHAIN_SHARED_ATTRS: 279 if arg_name in cc_toolchain_config_args: 280 cc_toolchain_args[arg_name] = cc_toolchain_config_args[arg_name] 281 282 native.cc_toolchain( 283 name = name, 284 toolchain_config = config_name, 285 # TODO: b/321268080 - Remove after transition of this option is complete. 286 exec_transition_for_inputs = False, 287 all_files = config_name, 288 **cc_toolchain_args 289 ) 290