1# Copyright 2024 The Bazel Authors. All rights reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://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, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Helper functions to allow us to collect data from attr.label_list.""" 15 16load( 17 "//cc/toolchains:cc_toolchain_info.bzl", 18 "ActionTypeConfigSetInfo", 19 "ActionTypeSetInfo", 20 "ArgsListInfo", 21 "FeatureSetInfo", 22 "ToolInfo", 23) 24 25visibility([ 26 "//cc/toolchains/...", 27 "//tests/rule_based_toolchain/...", 28]) 29 30def collect_provider(targets, provider): 31 """Collects providers from a label list. 32 33 Args: 34 targets: (List[Target]) An attribute from attr.label_list 35 provider: (provider) The provider to look up 36 Returns: 37 A list of the providers 38 """ 39 return [target[provider] for target in targets] 40 41def collect_defaultinfo(targets): 42 """Collects DefaultInfo from a label list. 43 44 Args: 45 targets: (List[Target]) An attribute from attr.label_list 46 Returns: 47 A list of the associated defaultinfo 48 """ 49 return collect_provider(targets, DefaultInfo) 50 51def _make_collector(provider, field): 52 def collector(targets, direct = [], transitive = []): 53 # Avoid mutating what was passed in. 54 transitive = transitive[:] 55 for value in collect_provider(targets, provider): 56 transitive.append(getattr(value, field)) 57 return depset(direct = direct, transitive = transitive) 58 59 return collector 60 61collect_action_types = _make_collector(ActionTypeSetInfo, "actions") 62collect_features = _make_collector(FeatureSetInfo, "features") 63collect_files = _make_collector(DefaultInfo, "files") 64 65def collect_data(ctx, targets): 66 """Collects from a 'data' attribute. 67 68 This is distinguished from collect_files by the fact that data attributes 69 attributes include runfiles. 70 71 Args: 72 ctx: (Context) The ctx for the current rule 73 targets: (List[Target]) A list of files or executables 74 75 Returns: 76 A depset containing all files for each of the targets, and all runfiles 77 required to run them. 78 """ 79 return ctx.runfiles(transitive_files = collect_files(targets)).merge_all([ 80 info.default_runfiles 81 for info in collect_defaultinfo(targets) 82 if info.default_runfiles != None 83 ]) 84 85def collect_tools(ctx, targets, fail = fail): 86 """Collects tools from a label_list. 87 88 Each entry in the label list may either be a cc_tool or a binary. 89 90 Args: 91 ctx: (Context) The ctx for the current rule 92 targets: (List[Target]) A list of targets. Each of these targets may be 93 either a cc_tool or an executable. 94 fail: (function) The fail function. Should only be used in tests. 95 96 Returns: 97 A List[ToolInfo], with regular executables creating custom tool info. 98 """ 99 tools = [] 100 for target in targets: 101 info = target[DefaultInfo] 102 if ToolInfo in target: 103 tools.append(target[ToolInfo]) 104 elif info.files_to_run != None and info.files_to_run.executable != None: 105 tools.append(ToolInfo( 106 label = target.label, 107 exe = info.files_to_run.executable, 108 runfiles = collect_data(ctx, [target]), 109 requires_any_of = tuple(), 110 execution_requirements = tuple(), 111 )) 112 else: 113 fail("Expected %s to be a cc_tool or a binary rule" % target.label) 114 115 return tools 116 117def collect_args_lists(targets, label): 118 """Collects a label_list of ArgsListInfo into a single ArgsListInfo 119 120 Args: 121 targets: (List[Target]) A label_list of targets providing ArgsListInfo 122 label: The label to attach to the resulting ArgsListInfo 123 Returns: 124 An ArgsListInfo that is the result of joining all of the ArgsListInfos 125 together. 126 """ 127 args = [] 128 by_action = {} 129 transitive_files = [] 130 for target in targets: 131 args_list = target[ArgsListInfo] 132 args.extend(args_list.args) 133 transitive_files.extend([args_info.files for args_info in args_list.args]) 134 for value in args_list.by_action: 135 out = by_action.setdefault( 136 value.action, 137 struct(args = [], transitive_files = [], action = value.action), 138 ) 139 out.args.extend(value.args) 140 out.transitive_files.append(value.files) 141 142 return ArgsListInfo( 143 label = label, 144 args = tuple(args), 145 files = depset(transitive = transitive_files), 146 by_action = tuple([ 147 struct( 148 action = k, 149 args = tuple(v.args), 150 files = depset(transitive = v.transitive_files), 151 ) 152 for k, v in by_action.items() 153 ]), 154 ) 155 156def collect_action_type_config_sets(targets, label, fail = fail): 157 """Collects several `cc_action_type_config` labels together. 158 159 Args: 160 targets: (List[Target]) A list of targets providing ActionTypeConfigSetInfo 161 label: The label to apply to the resulting config. 162 fail: (function) The fail function. Should only be used in tests. 163 Returns: 164 A combined ActionTypeConfigSetInfo representing a variety of action 165 types. 166 """ 167 configs = {} 168 for atcs in collect_provider(targets, ActionTypeConfigSetInfo): 169 for action_type, config in atcs.configs.items(): 170 if action_type in configs: 171 fail("The action type %s is configured by both %s and %s. Each action type may only be configured once." % (action_type.label, config.label, configs[action_type].label)) 172 configs[action_type] = config 173 return ActionTypeConfigSetInfo(label = label, configs = configs) 174