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"""Conversion helper functions to legacy cc_toolchain_config_info.""" 15 16load( 17 "//cc:cc_toolchain_config_lib.bzl", 18 legacy_action_config = "action_config", 19 legacy_env_entry = "env_entry", 20 legacy_env_set = "env_set", 21 legacy_feature = "feature", 22 legacy_feature_set = "feature_set", 23 legacy_flag_set = "flag_set", 24 legacy_tool = "tool", 25 legacy_with_feature_set = "with_feature_set", 26) 27 28visibility([ 29 "//cc/toolchains/...", 30 "//tests/rule_based_toolchain/...", 31]) 32 33# Note that throughout this file, we sort anything for which the order is 34# nondeterministic (eg. depset's .to_list(), dictionary iteration). 35# This allows our tests to call equals() on the output, 36# and *may* provide better caching properties. 37 38def _convert_actions(actions): 39 return sorted([action.name for action in actions.to_list()]) 40 41def convert_feature_constraint(constraint): 42 return legacy_with_feature_set( 43 features = sorted([ft.name for ft in constraint.all_of.to_list()]), 44 not_features = sorted([ft.name for ft in constraint.none_of.to_list()]), 45 ) 46 47def convert_args(args, strip_actions = False): 48 """Converts an ArgsInfo to flag_sets and env_sets. 49 50 Args: 51 args: (ArgsInfo) The args to convert 52 strip_actions: (bool) Whether to strip the actions from the resulting flag_set. 53 Returns: 54 struct(flag_sets = List[flag_set], env_sets = List[env_sets]) 55 """ 56 actions = _convert_actions(args.actions) 57 with_features = [ 58 convert_feature_constraint(fc) 59 for fc in args.requires_any_of 60 ] 61 62 flag_sets = [] 63 if args.nested != None: 64 flag_sets.append(legacy_flag_set( 65 actions = [] if strip_actions else actions, 66 with_features = with_features, 67 flag_groups = [args.nested.legacy_flag_group], 68 )) 69 70 env_sets = [] 71 if args.env: 72 env_sets.append(legacy_env_set( 73 actions = actions, 74 with_features = with_features, 75 env_entries = [ 76 legacy_env_entry( 77 key = key, 78 value = value, 79 ) 80 for key, value in args.env.items() 81 ], 82 )) 83 return struct( 84 flag_sets = flag_sets, 85 env_sets = env_sets, 86 ) 87 88def _convert_args_sequence(args_sequence, strip_actions = False): 89 flag_sets = [] 90 env_sets = [] 91 for args in args_sequence: 92 legacy_args = convert_args(args, strip_actions) 93 flag_sets.extend(legacy_args.flag_sets) 94 env_sets.extend(legacy_args.env_sets) 95 96 return struct(flag_sets = flag_sets, env_sets = env_sets) 97 98def convert_feature(feature, enabled = False): 99 if feature.external: 100 return None 101 102 args = _convert_args_sequence(feature.args.args) 103 104 return legacy_feature( 105 name = feature.name, 106 enabled = enabled or feature.enabled, 107 flag_sets = args.flag_sets, 108 env_sets = args.env_sets, 109 implies = sorted([ft.name for ft in feature.implies.to_list()]), 110 requires = [ 111 legacy_feature_set(sorted([ 112 feature.name 113 for feature in requirement.features.to_list() 114 ])) 115 for requirement in feature.requires_any_of 116 ], 117 provides = [ 118 mutex.name 119 for mutex in feature.mutually_exclusive 120 ], 121 ) 122 123def convert_tool(tool): 124 return legacy_tool( 125 tool = tool.exe, 126 execution_requirements = list(tool.execution_requirements), 127 with_features = [], 128 ) 129 130def convert_capability(capability): 131 return legacy_feature( 132 name = capability.name, 133 enabled = False, 134 ) 135 136def _convert_tool_map(tool_map, args_by_action): 137 action_configs = [] 138 caps = {} 139 for action_type, tool in tool_map.configs.items(): 140 action_args = args_by_action.get(action_type.name, default = None) 141 flag_sets = action_args.flag_sets if action_args != None else [] 142 action_configs.append(legacy_action_config( 143 action_name = action_type.name, 144 enabled = True, 145 flag_sets = flag_sets, 146 tools = [convert_tool(tool)], 147 implies = [cap.feature.name for cap in tool.capabilities], 148 )) 149 for cap in tool.capabilities: 150 caps[cap] = None 151 152 cap_features = [ 153 legacy_feature(name = cap.feature.name, enabled = False) 154 for cap in caps 155 ] 156 return action_configs, cap_features 157 158def convert_toolchain(toolchain): 159 """Converts a rule-based toolchain into the legacy providers. 160 161 Args: 162 toolchain: (ToolchainConfigInfo) The toolchain config to convert. 163 Returns: 164 A struct containing parameters suitable to pass to 165 cc_common.create_cc_toolchain_config_info. 166 """ 167 168 # Ordering of arguments is important! Intended argument ordering is: 169 # 1. Arguments listed in `args`. 170 # 2. Legacy/built-in features. 171 # 3. User-defined features. 172 # While we could just attach arguments to a feature, legacy/built-in features will appear 173 # before the user-defined features if we do not bind args directly to the action configs. 174 # For that reason, there's additional logic in this function to ensure that the args are 175 # attached to the action configs directly, as that is the only way to ensure the correct 176 # ordering. 177 args_by_action = {} 178 for a in toolchain.args.by_action: 179 args = args_by_action.setdefault(a.action.name, struct(flag_sets = [], env_sets = [])) 180 new_args = _convert_args_sequence(a.args, strip_actions = True) 181 args.flag_sets.extend(new_args.flag_sets) 182 args.env_sets.extend(new_args.env_sets) 183 184 action_configs, cap_features = _convert_tool_map(toolchain.tool_map, args_by_action) 185 features = [ 186 convert_feature(feature, enabled = feature in toolchain.enabled_features) 187 for feature in toolchain.features 188 ] 189 features.extend(cap_features) 190 191 features.append(legacy_feature( 192 # We reserve names starting with implied_by. This ensures we don't 193 # conflict with the name of a feature the user creates. 194 name = "implied_by_always_enabled_env_sets", 195 enabled = True, 196 env_sets = _convert_args_sequence(toolchain.args.args).env_sets, 197 )) 198 199 cxx_builtin_include_directories = [ 200 d.path 201 for d in toolchain.allowlist_include_directories.to_list() 202 ] 203 204 return struct( 205 features = [ft for ft in features if ft != None], 206 action_configs = sorted(action_configs, key = lambda ac: ac.action_name), 207 cxx_builtin_include_directories = cxx_builtin_include_directories, 208 ) 209