1*7594170eSAndroid Build Coastguard Worker# Copyright (C) 2023 The Android Open Source Project 2*7594170eSAndroid Build Coastguard Worker# 3*7594170eSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*7594170eSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*7594170eSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*7594170eSAndroid Build Coastguard Worker# 7*7594170eSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*7594170eSAndroid Build Coastguard Worker# 9*7594170eSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*7594170eSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*7594170eSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*7594170eSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*7594170eSAndroid Build Coastguard Worker# limitations under the License. 14*7594170eSAndroid Build Coastguard Worker 15*7594170eSAndroid Build Coastguard Workerload("@bazel_skylib//lib:selects.bzl", "selects") 16*7594170eSAndroid Build Coastguard Worker 17*7594170eSAndroid Build Coastguard Workerdef _not(name, config_setting): 18*7594170eSAndroid Build Coastguard Worker native.alias( 19*7594170eSAndroid Build Coastguard Worker name = name, 20*7594170eSAndroid Build Coastguard Worker actual = select({ 21*7594170eSAndroid Build Coastguard Worker config_setting: "//build/bazel/utils:always_off_config_setting", 22*7594170eSAndroid Build Coastguard Worker "//conditions:default": "//build/bazel/utils:always_on_config_setting", 23*7594170eSAndroid Build Coastguard Worker }), 24*7594170eSAndroid Build Coastguard Worker ) 25*7594170eSAndroid Build Coastguard Worker 26*7594170eSAndroid Build Coastguard Workerdef _or(name, match_any): 27*7594170eSAndroid Build Coastguard Worker if not match_any: 28*7594170eSAndroid Build Coastguard Worker native.alias( 29*7594170eSAndroid Build Coastguard Worker name = name, 30*7594170eSAndroid Build Coastguard Worker actual = "//build/bazel/utils:always_off_config_setting", 31*7594170eSAndroid Build Coastguard Worker ) 32*7594170eSAndroid Build Coastguard Worker else: 33*7594170eSAndroid Build Coastguard Worker selects.config_setting_group( 34*7594170eSAndroid Build Coastguard Worker name = name, 35*7594170eSAndroid Build Coastguard Worker match_any = match_any, 36*7594170eSAndroid Build Coastguard Worker ) 37*7594170eSAndroid Build Coastguard Worker 38*7594170eSAndroid Build Coastguard Workerdef _and(name, match_all): 39*7594170eSAndroid Build Coastguard Worker if not match_all: 40*7594170eSAndroid Build Coastguard Worker native.alias( 41*7594170eSAndroid Build Coastguard Worker name = name, 42*7594170eSAndroid Build Coastguard Worker actual = "//build/bazel/utils:always_on_config_setting", 43*7594170eSAndroid Build Coastguard Worker ) 44*7594170eSAndroid Build Coastguard Worker else: 45*7594170eSAndroid Build Coastguard Worker selects.config_setting_group( 46*7594170eSAndroid Build Coastguard Worker name = name, 47*7594170eSAndroid Build Coastguard Worker match_all = match_all, 48*7594170eSAndroid Build Coastguard Worker ) 49*7594170eSAndroid Build Coastguard Worker 50*7594170eSAndroid Build Coastguard Workerdef config_setting_boolean_algebra(*, name, expr): 51*7594170eSAndroid Build Coastguard Worker """ 52*7594170eSAndroid Build Coastguard Worker Computes the given boolean expression of config settings. 53*7594170eSAndroid Build Coastguard Worker 54*7594170eSAndroid Build Coastguard Worker The format of the expr argument is a dictionary with a single key/value pair. 55*7594170eSAndroid Build Coastguard Worker The key can be AND, OR, or NOT. The value or AND/OR keys must be a list 56*7594170eSAndroid Build Coastguard Worker of strings or more expression dictionaries, where the strings are labels of config settings. 57*7594170eSAndroid Build Coastguard Worker The value of NOT keys must be a string or an expression dictionary. 58*7594170eSAndroid Build Coastguard Worker 59*7594170eSAndroid Build Coastguard Worker The result will be a new config setting which is the evaluation of the expression. 60*7594170eSAndroid Build Coastguard Worker 61*7594170eSAndroid Build Coastguard Worker A bunch of internal config settings will also be created, but they should be treated 62*7594170eSAndroid Build Coastguard Worker as an implementation detail and not relied on. They could change in future updates to 63*7594170eSAndroid Build Coastguard Worker this method. 64*7594170eSAndroid Build Coastguard Worker 65*7594170eSAndroid Build Coastguard Worker Example: 66*7594170eSAndroid Build Coastguard Worker config_setting_boolean_algebra( 67*7594170eSAndroid Build Coastguard Worker name = "my_config_setting", 68*7594170eSAndroid Build Coastguard Worker expr = {"AND": [ 69*7594170eSAndroid Build Coastguard Worker ":config_setting_1", 70*7594170eSAndroid Build Coastguard Worker {"NOT": ":config_setting_2"}, 71*7594170eSAndroid Build Coastguard Worker {"OR": [ 72*7594170eSAndroid Build Coastguard Worker ":config_setting_3", 73*7594170eSAndroid Build Coastguard Worker {"NOT": "config_setting_4"}, 74*7594170eSAndroid Build Coastguard Worker ]} 75*7594170eSAndroid Build Coastguard Worker ]} 76*7594170eSAndroid Build Coastguard Worker ) 77*7594170eSAndroid Build Coastguard Worker """ 78*7594170eSAndroid Build Coastguard Worker 79*7594170eSAndroid Build Coastguard Worker # The implementation of this function is modeled after a recursive function, 80*7594170eSAndroid Build Coastguard Worker # but due to the special nature of the problem it's simplified quite a bit from 81*7594170eSAndroid Build Coastguard Worker # a full recursion-to-iteration algorithm. (no need for return values, no need to return 82*7594170eSAndroid Build Coastguard Worker # to prior stack frames once we start executing a new one) 83*7594170eSAndroid Build Coastguard Worker stack = [struct( 84*7594170eSAndroid Build Coastguard Worker expr = expr, 85*7594170eSAndroid Build Coastguard Worker name = name, 86*7594170eSAndroid Build Coastguard Worker )] 87*7594170eSAndroid Build Coastguard Worker 88*7594170eSAndroid Build Coastguard Worker # Starlark doesn't support infinite loops, so just make a large loop 89*7594170eSAndroid Build Coastguard Worker for _ in range(1000): 90*7594170eSAndroid Build Coastguard Worker if not stack: 91*7594170eSAndroid Build Coastguard Worker break 92*7594170eSAndroid Build Coastguard Worker 93*7594170eSAndroid Build Coastguard Worker frame = stack.pop() 94*7594170eSAndroid Build Coastguard Worker name = frame.name 95*7594170eSAndroid Build Coastguard Worker expr = frame.expr 96*7594170eSAndroid Build Coastguard Worker expr_type = type(expr) 97*7594170eSAndroid Build Coastguard Worker if expr_type == "string": 98*7594170eSAndroid Build Coastguard Worker native.alias( 99*7594170eSAndroid Build Coastguard Worker name = name, 100*7594170eSAndroid Build Coastguard Worker actual = expr, 101*7594170eSAndroid Build Coastguard Worker ) 102*7594170eSAndroid Build Coastguard Worker continue 103*7594170eSAndroid Build Coastguard Worker elif expr_type == "dict": 104*7594170eSAndroid Build Coastguard Worker if len(expr) != 1: 105*7594170eSAndroid Build Coastguard Worker fail("Dictionaries should have exactly 1 key/value") 106*7594170eSAndroid Build Coastguard Worker op, operands = expr.items()[0] 107*7594170eSAndroid Build Coastguard Worker if op == "NOT": 108*7594170eSAndroid Build Coastguard Worker # traditionally this would come after the recursive call, but because it's a rule 109*7594170eSAndroid Build Coastguard Worker # definition it doesn't matter if name + ".0" exists yet or not. Having the 110*7594170eSAndroid Build Coastguard Worker # recursive call come last makes the non-recursive version easier to implement 111*7594170eSAndroid Build Coastguard Worker _not(name, name + ".0") 112*7594170eSAndroid Build Coastguard Worker stack.append(struct( 113*7594170eSAndroid Build Coastguard Worker name = name + ".0", 114*7594170eSAndroid Build Coastguard Worker expr = operands, 115*7594170eSAndroid Build Coastguard Worker )) 116*7594170eSAndroid Build Coastguard Worker continue 117*7594170eSAndroid Build Coastguard Worker 118*7594170eSAndroid Build Coastguard Worker if type(operands) != "list": 119*7594170eSAndroid Build Coastguard Worker fail("Operand to AND/OR must be a list, got %s" % type(operands)) 120*7594170eSAndroid Build Coastguard Worker 121*7594170eSAndroid Build Coastguard Worker operand_names = [name + "." + str(i) for i, elem in enumerate(operands)] 122*7594170eSAndroid Build Coastguard Worker 123*7594170eSAndroid Build Coastguard Worker if op == "AND": 124*7594170eSAndroid Build Coastguard Worker _and(name, operand_names) 125*7594170eSAndroid Build Coastguard Worker elif op == "OR": 126*7594170eSAndroid Build Coastguard Worker _or(name, operand_names) 127*7594170eSAndroid Build Coastguard Worker else: 128*7594170eSAndroid Build Coastguard Worker fail("Operator must be AND, OR, or NOT, got %s" % op) 129*7594170eSAndroid Build Coastguard Worker 130*7594170eSAndroid Build Coastguard Worker for elem_name, elem in zip(operand_names, operands): 131*7594170eSAndroid Build Coastguard Worker # because we don't need a return value from these recursive calls, 132*7594170eSAndroid Build Coastguard Worker # we can queue them all up at once without returning to the current stack frame. 133*7594170eSAndroid Build Coastguard Worker stack.append(struct( 134*7594170eSAndroid Build Coastguard Worker name = elem_name, 135*7594170eSAndroid Build Coastguard Worker expr = elem, 136*7594170eSAndroid Build Coastguard Worker )) 137*7594170eSAndroid Build Coastguard Worker else: 138*7594170eSAndroid Build Coastguard Worker fail("Expression must be string or dict, got %s" % expr_type) 139*7594170eSAndroid Build Coastguard Worker if stack: 140*7594170eSAndroid Build Coastguard Worker fail("Recursion took too many iterations!") 141