xref: /aosp_15_r20/external/bazelbuild-rules_cc/cc/toolchains/impl/args_utils.bzl (revision eed53cd41c5909d05eedc7ad9720bb158fd93452)
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 for working with args."""
15
16load(":variables.bzl", "get_type")
17
18visibility([
19    "//cc/toolchains",
20    "//tests/rule_based_toolchain/...",
21])
22
23def get_action_type(args_list, action_type):
24    """Returns the corresponding entry in ArgsListInfo.by_action.
25
26    Args:
27        args_list: (ArgsListInfo) The args list to look through
28        action_type: (ActionTypeInfo) The action type to look up.
29    Returns:
30        The information corresponding to this action type.
31
32    """
33    for args in args_list.by_action:
34        if args.action == action_type:
35            return args
36
37    return struct(action = action_type, args = tuple(), files = depset([]))
38
39def validate_nested_args(*, nested_args, variables, actions, label, fail = fail):
40    """Validates the typing for an nested_args invocation.
41
42    Args:
43        nested_args: (NestedArgsInfo) The nested_args to validate
44        variables: (Dict[str, VariableInfo]) A mapping from variable name to
45          the metadata (variable type and valid actions).
46        actions: (List[ActionTypeInfo]) The actions we require these variables
47          to be valid for.
48        label: (Label) The label of the rule we're currently validating.
49          Used for error messages.
50        fail: The fail function. Use for testing only.
51    """
52    stack = [(nested_args, {})]
53
54    for _ in range(9999999):
55        if not stack:
56            break
57        nested_args, overrides = stack.pop()
58        if nested_args.iterate_over != None or nested_args.unwrap_options:
59            # Make sure we don't keep using the same object.
60            overrides = dict(**overrides)
61
62        if nested_args.iterate_over != None:
63            type = get_type(
64                name = nested_args.iterate_over,
65                variables = variables,
66                overrides = overrides,
67                actions = actions,
68                args_label = label,
69                nested_label = nested_args.label,
70                fail = fail,
71            )
72            if type["name"] == "list":
73                # Rewrite the type of the thing we iterate over from a List[T]
74                # to a T.
75                overrides[nested_args.iterate_over] = type["elements"]
76            elif type["name"] == "option" and type["elements"]["name"] == "list":
77                # Rewrite Option[List[T]] to T.
78                overrides[nested_args.iterate_over] = type["elements"]["elements"]
79            else:
80                fail("Attempting to iterate over %s, but it was not a list - it was a %s" % (nested_args.iterate_over, type["repr"]))
81
82        # 1) Validate variables marked with after_option_unwrap = False.
83        # 2) Unwrap Option[T] to T as required.
84        # 3) Validate variables marked with after_option_unwrap = True.
85        for after_option_unwrap in [False, True]:
86            for var_name, requirements in nested_args.requires_types.items():
87                for requirement in requirements:
88                    if requirement.after_option_unwrap == after_option_unwrap:
89                        type = get_type(
90                            name = var_name,
91                            variables = variables,
92                            overrides = overrides,
93                            actions = actions,
94                            args_label = label,
95                            nested_label = nested_args.label,
96                            fail = fail,
97                        )
98                        if type["name"] not in requirement.valid_types:
99                            fail("{msg}, but {var_name} has type {type}".format(
100                                var_name = var_name,
101                                msg = requirement.msg,
102                                type = type["repr"],
103                            ))
104
105            # Only unwrap the options after the first iteration of this loop.
106            if not after_option_unwrap:
107                for var in nested_args.unwrap_options:
108                    type = get_type(
109                        name = var,
110                        variables = variables,
111                        overrides = overrides,
112                        actions = actions,
113                        args_label = label,
114                        nested_label = nested_args.label,
115                        fail = fail,
116                    )
117                    if type["name"] == "option":
118                        overrides[var] = type["elements"]
119
120        for child in nested_args.nested:
121            stack.append((child, overrides))
122