xref: /aosp_15_r20/external/bazelbuild-rules_cc/cc/toolchains/impl/variables.bzl (revision eed53cd41c5909d05eedc7ad9720bb158fd93452)
1*eed53cd4SHONG Yifan# Copyright 2024 The Bazel Authors. All rights reserved.
2*eed53cd4SHONG Yifan#
3*eed53cd4SHONG Yifan# Licensed under the Apache License, Version 2.0 (the "License");
4*eed53cd4SHONG Yifan# you may not use this file except in compliance with the License.
5*eed53cd4SHONG Yifan# You may obtain a copy of the License at
6*eed53cd4SHONG Yifan#
7*eed53cd4SHONG Yifan#     http://www.apache.org/licenses/LICENSE-2.0
8*eed53cd4SHONG Yifan#
9*eed53cd4SHONG Yifan# Unless required by applicable law or agreed to in writing, software
10*eed53cd4SHONG Yifan# distributed under the License is distributed on an "AS IS" BASIS,
11*eed53cd4SHONG Yifan# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*eed53cd4SHONG Yifan# See the License for the specific language governing permissions and
13*eed53cd4SHONG Yifan# limitations under the License.
14*eed53cd4SHONG Yifan"""Rules for accessing cc build variables in bazel toolchains safely."""
15*eed53cd4SHONG Yifan
16*eed53cd4SHONG Yifanload("//cc/toolchains:cc_toolchain_info.bzl", "ActionTypeSetInfo", "BuiltinVariablesInfo", "VariableInfo")
17*eed53cd4SHONG Yifanload(":collect.bzl", "collect_action_types", "collect_provider")
18*eed53cd4SHONG Yifan
19*eed53cd4SHONG Yifanvisibility([
20*eed53cd4SHONG Yifan    "//cc/toolchains/variables",
21*eed53cd4SHONG Yifan    "//tests/rule_based_toolchain/...",
22*eed53cd4SHONG Yifan])
23*eed53cd4SHONG Yifan
24*eed53cd4SHONG Yifantypes = struct(
25*eed53cd4SHONG Yifan    unknown = dict(name = "unknown", repr = "unknown"),
26*eed53cd4SHONG Yifan    void = dict(name = "void", repr = "void"),
27*eed53cd4SHONG Yifan    string = dict(name = "string", repr = "string"),
28*eed53cd4SHONG Yifan    bool = dict(name = "bool", repr = "bool"),
29*eed53cd4SHONG Yifan    # File and directory are basically the same thing as string for now.
30*eed53cd4SHONG Yifan    file = dict(name = "file", repr = "File"),
31*eed53cd4SHONG Yifan    directory = dict(name = "directory", repr = "directory"),
32*eed53cd4SHONG Yifan    option = lambda element: dict(
33*eed53cd4SHONG Yifan        name = "option",
34*eed53cd4SHONG Yifan        elements = element,
35*eed53cd4SHONG Yifan        repr = "Option[%s]" % element["repr"],
36*eed53cd4SHONG Yifan    ),
37*eed53cd4SHONG Yifan    list = lambda elements: dict(
38*eed53cd4SHONG Yifan        name = "list",
39*eed53cd4SHONG Yifan        elements = elements,
40*eed53cd4SHONG Yifan        repr = "List[%s]" % elements["repr"],
41*eed53cd4SHONG Yifan    ),
42*eed53cd4SHONG Yifan    struct = lambda **kv: dict(
43*eed53cd4SHONG Yifan        name = "struct",
44*eed53cd4SHONG Yifan        kv = kv,
45*eed53cd4SHONG Yifan        repr = "struct(%s)" % ", ".join([
46*eed53cd4SHONG Yifan            "{k}={v}".format(k = k, v = v["repr"])
47*eed53cd4SHONG Yifan            for k, v in sorted(kv.items())
48*eed53cd4SHONG Yifan        ]),
49*eed53cd4SHONG Yifan    ),
50*eed53cd4SHONG Yifan)
51*eed53cd4SHONG Yifan
52*eed53cd4SHONG Yifandef _cc_variable_impl(ctx):
53*eed53cd4SHONG Yifan    return [VariableInfo(
54*eed53cd4SHONG Yifan        name = ctx.label.name,
55*eed53cd4SHONG Yifan        label = ctx.label,
56*eed53cd4SHONG Yifan        type = json.decode(ctx.attr.type),
57*eed53cd4SHONG Yifan        actions = collect_action_types(ctx.attr.actions) if ctx.attr.actions else None,
58*eed53cd4SHONG Yifan    )]
59*eed53cd4SHONG Yifan
60*eed53cd4SHONG Yifan_cc_variable = rule(
61*eed53cd4SHONG Yifan    implementation = _cc_variable_impl,
62*eed53cd4SHONG Yifan    attrs = {
63*eed53cd4SHONG Yifan        "actions": attr.label_list(providers = [ActionTypeSetInfo]),
64*eed53cd4SHONG Yifan        "type": attr.string(mandatory = True),
65*eed53cd4SHONG Yifan    },
66*eed53cd4SHONG Yifan    provides = [VariableInfo],
67*eed53cd4SHONG Yifan)
68*eed53cd4SHONG Yifan
69*eed53cd4SHONG Yifandef cc_variable(name, type, **kwargs):
70*eed53cd4SHONG Yifan    """Defines a variable for both the specified variable, and all nested ones.
71*eed53cd4SHONG Yifan
72*eed53cd4SHONG Yifan    Eg. cc_variable(
73*eed53cd4SHONG Yifan      name = "foo",
74*eed53cd4SHONG Yifan      type = types.list(types.struct(bar = types.string))
75*eed53cd4SHONG Yifan    )
76*eed53cd4SHONG Yifan
77*eed53cd4SHONG Yifan    would define two targets, ":foo" and ":foo.bar"
78*eed53cd4SHONG Yifan
79*eed53cd4SHONG Yifan    Args:
80*eed53cd4SHONG Yifan        name: (str) The name of the outer variable, and the rule.
81*eed53cd4SHONG Yifan        type: The type of the variable, constructed using types above.
82*eed53cd4SHONG Yifan        **kwargs: kwargs to pass to _cc_variable.
83*eed53cd4SHONG Yifan    """
84*eed53cd4SHONG Yifan    _cc_variable(name = name, type = json.encode(type), **kwargs)
85*eed53cd4SHONG Yifan
86*eed53cd4SHONG Yifandef _cc_builtin_variables_impl(ctx):
87*eed53cd4SHONG Yifan    return [BuiltinVariablesInfo(variables = {
88*eed53cd4SHONG Yifan        variable.name: variable
89*eed53cd4SHONG Yifan        for variable in collect_provider(ctx.attr.srcs, VariableInfo)
90*eed53cd4SHONG Yifan    })]
91*eed53cd4SHONG Yifan
92*eed53cd4SHONG Yifancc_builtin_variables = rule(
93*eed53cd4SHONG Yifan    implementation = _cc_builtin_variables_impl,
94*eed53cd4SHONG Yifan    attrs = {
95*eed53cd4SHONG Yifan        "srcs": attr.label_list(providers = [VariableInfo]),
96*eed53cd4SHONG Yifan    },
97*eed53cd4SHONG Yifan)
98*eed53cd4SHONG Yifan
99*eed53cd4SHONG Yifandef get_type(*, name, variables, overrides, actions, args_label, nested_label, fail):
100*eed53cd4SHONG Yifan    """Gets the type of a variable.
101*eed53cd4SHONG Yifan
102*eed53cd4SHONG Yifan    Args:
103*eed53cd4SHONG Yifan        name: (str) The variable to look up.
104*eed53cd4SHONG Yifan        variables: (dict[str, VariableInfo]) Mapping from variable name to
105*eed53cd4SHONG Yifan          metadata. Top-level variables only
106*eed53cd4SHONG Yifan        overrides: (dict[str, type]) Mapping from variable names to type.
107*eed53cd4SHONG Yifan          Can be used for nested variables.
108*eed53cd4SHONG Yifan        actions: (depset[ActionTypeInfo]) The set of actions for which the
109*eed53cd4SHONG Yifan          variable is requested.
110*eed53cd4SHONG Yifan        args_label: (Label) The label for the args that included the rule that
111*eed53cd4SHONG Yifan          references this variable. Only used for error messages.
112*eed53cd4SHONG Yifan        nested_label: (Label) The label for the rule that references this
113*eed53cd4SHONG Yifan          variable. Only used for error messages.
114*eed53cd4SHONG Yifan        fail: A function to be called upon failure. Use for testing only.
115*eed53cd4SHONG Yifan    Returns:
116*eed53cd4SHONG Yifan        The type of the variable "name".
117*eed53cd4SHONG Yifan    """
118*eed53cd4SHONG Yifan    outer = name.split(".")[0]
119*eed53cd4SHONG Yifan    if outer not in variables:
120*eed53cd4SHONG Yifan        # With a fail function, we actually need to return since the fail
121*eed53cd4SHONG Yifan        # function doesn't propagate.
122*eed53cd4SHONG Yifan        fail("The variable %s does not exist. Did you mean one of the following?\n%s" % (outer, "\n".join(sorted(variables))))
123*eed53cd4SHONG Yifan
124*eed53cd4SHONG Yifan        # buildifier: disable=unreachable
125*eed53cd4SHONG Yifan        return types.void
126*eed53cd4SHONG Yifan
127*eed53cd4SHONG Yifan    if variables[outer].actions != None:
128*eed53cd4SHONG Yifan        valid_actions = variables[outer].actions.to_list()
129*eed53cd4SHONG Yifan        for action in actions:
130*eed53cd4SHONG Yifan            if action not in valid_actions:
131*eed53cd4SHONG Yifan                fail("The variable {var} is inaccessible from the action {action}. This is required because it is referenced in {nested_label}, which is included by {args_label}, which references that action".format(
132*eed53cd4SHONG Yifan                    var = variables[outer].label,
133*eed53cd4SHONG Yifan                    nested_label = nested_label,
134*eed53cd4SHONG Yifan                    args_label = args_label,
135*eed53cd4SHONG Yifan                    action = action.label,
136*eed53cd4SHONG Yifan                ))
137*eed53cd4SHONG Yifan
138*eed53cd4SHONG Yifan                # buildifier: disable=unreachable
139*eed53cd4SHONG Yifan                return types.void
140*eed53cd4SHONG Yifan
141*eed53cd4SHONG Yifan    type = overrides.get(outer, variables[outer].type)
142*eed53cd4SHONG Yifan
143*eed53cd4SHONG Yifan    parent = outer
144*eed53cd4SHONG Yifan    for part in name.split(".")[1:]:
145*eed53cd4SHONG Yifan        full = parent + "." + part
146*eed53cd4SHONG Yifan
147*eed53cd4SHONG Yifan        if type["name"] != "struct":
148*eed53cd4SHONG Yifan            extra_error = ""
149*eed53cd4SHONG Yifan            if type["name"] == "list" and type["elements"]["name"] == "struct":
150*eed53cd4SHONG Yifan                extra_error = " Maybe you meant to use iterate_over."
151*eed53cd4SHONG Yifan
152*eed53cd4SHONG Yifan            fail("Attempted to access %r, but %r was not a struct - it had type %s.%s" % (full, parent, type["repr"], extra_error))
153*eed53cd4SHONG Yifan
154*eed53cd4SHONG Yifan            # buildifier: disable=unreachable
155*eed53cd4SHONG Yifan            return types.void
156*eed53cd4SHONG Yifan
157*eed53cd4SHONG Yifan        if part not in type["kv"] and full not in overrides:
158*eed53cd4SHONG Yifan            attrs = []
159*eed53cd4SHONG Yifan            for attr, value in sorted(type["kv"].items()):
160*eed53cd4SHONG Yifan                attrs.append("%s: %s" % (attr, value["repr"]))
161*eed53cd4SHONG Yifan            fail("Unable to find %r in %r, which had the following attributes:\n%s" % (part, parent, "\n".join(attrs)))
162*eed53cd4SHONG Yifan
163*eed53cd4SHONG Yifan            # buildifier: disable=unreachable
164*eed53cd4SHONG Yifan            return types.void
165*eed53cd4SHONG Yifan
166*eed53cd4SHONG Yifan        type = overrides.get(full, type["kv"][part])
167*eed53cd4SHONG Yifan        parent = full
168*eed53cd4SHONG Yifan
169*eed53cd4SHONG Yifan    return type
170