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