# Copyright 2024 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. """Helper functions for working with args.""" load(":variables.bzl", "get_type") visibility([ "//cc/toolchains", "//tests/rule_based_toolchain/...", ]) def get_action_type(args_list, action_type): """Returns the corresponding entry in ArgsListInfo.by_action. Args: args_list: (ArgsListInfo) The args list to look through action_type: (ActionTypeInfo) The action type to look up. Returns: The information corresponding to this action type. """ for args in args_list.by_action: if args.action == action_type: return args return struct(action = action_type, args = tuple(), files = depset([])) def validate_nested_args(*, nested_args, variables, actions, label, fail = fail): """Validates the typing for an nested_args invocation. Args: nested_args: (NestedArgsInfo) The nested_args to validate variables: (Dict[str, VariableInfo]) A mapping from variable name to the metadata (variable type and valid actions). actions: (List[ActionTypeInfo]) The actions we require these variables to be valid for. label: (Label) The label of the rule we're currently validating. Used for error messages. fail: The fail function. Use for testing only. """ stack = [(nested_args, {})] for _ in range(9999999): if not stack: break nested_args, overrides = stack.pop() if nested_args.iterate_over != None or nested_args.unwrap_options: # Make sure we don't keep using the same object. overrides = dict(**overrides) if nested_args.iterate_over != None: type = get_type( name = nested_args.iterate_over, variables = variables, overrides = overrides, actions = actions, args_label = label, nested_label = nested_args.label, fail = fail, ) if type["name"] == "list": # Rewrite the type of the thing we iterate over from a List[T] # to a T. overrides[nested_args.iterate_over] = type["elements"] elif type["name"] == "option" and type["elements"]["name"] == "list": # Rewrite Option[List[T]] to T. overrides[nested_args.iterate_over] = type["elements"]["elements"] else: fail("Attempting to iterate over %s, but it was not a list - it was a %s" % (nested_args.iterate_over, type["repr"])) # 1) Validate variables marked with after_option_unwrap = False. # 2) Unwrap Option[T] to T as required. # 3) Validate variables marked with after_option_unwrap = True. for after_option_unwrap in [False, True]: for var_name, requirements in nested_args.requires_types.items(): for requirement in requirements: if requirement.after_option_unwrap == after_option_unwrap: type = get_type( name = var_name, variables = variables, overrides = overrides, actions = actions, args_label = label, nested_label = nested_args.label, fail = fail, ) if type["name"] not in requirement.valid_types: fail("{msg}, but {var_name} has type {type}".format( var_name = var_name, msg = requirement.msg, type = type["repr"], )) # Only unwrap the options after the first iteration of this loop. if not after_option_unwrap: for var in nested_args.unwrap_options: type = get_type( name = var, variables = variables, overrides = overrides, actions = actions, args_label = label, nested_label = nested_args.label, fail = fail, ) if type["name"] == "option": overrides[var] = type["elements"] for child in nested_args.nested: stack.append((child, overrides))