1*333d2b36SAndroid Build Coastguard Worker// Copyright 2021 Google LLC 2*333d2b36SAndroid Build Coastguard Worker// 3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*333d2b36SAndroid Build Coastguard Worker// 7*333d2b36SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*333d2b36SAndroid Build Coastguard Worker// 9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*333d2b36SAndroid Build Coastguard Worker// limitations under the License. 14*333d2b36SAndroid Build Coastguard Worker 15*333d2b36SAndroid Build Coastguard Workerpackage mk2rbc 16*333d2b36SAndroid Build Coastguard Worker 17*333d2b36SAndroid Build Coastguard Workerimport ( 18*333d2b36SAndroid Build Coastguard Worker "bytes" 19*333d2b36SAndroid Build Coastguard Worker "fmt" 20*333d2b36SAndroid Build Coastguard Worker "io/ioutil" 21*333d2b36SAndroid Build Coastguard Worker "os" 22*333d2b36SAndroid Build Coastguard Worker "regexp" 23*333d2b36SAndroid Build Coastguard Worker "strings" 24*333d2b36SAndroid Build Coastguard Worker 25*333d2b36SAndroid Build Coastguard Worker mkparser "android/soong/androidmk/parser" 26*333d2b36SAndroid Build Coastguard Worker) 27*333d2b36SAndroid Build Coastguard Worker 28*333d2b36SAndroid Build Coastguard Workertype context struct { 29*333d2b36SAndroid Build Coastguard Worker includeFileScope mkparser.Scope 30*333d2b36SAndroid Build Coastguard Worker registrar variableRegistrar 31*333d2b36SAndroid Build Coastguard Worker} 32*333d2b36SAndroid Build Coastguard Worker 33*333d2b36SAndroid Build Coastguard Worker// Scans the makefile Soong uses to generate soong.variables file, 34*333d2b36SAndroid Build Coastguard Worker// collecting variable names and types from the lines that look like this: 35*333d2b36SAndroid Build Coastguard Worker// 36*333d2b36SAndroid Build Coastguard Worker// $(call add_json_XXX, <...>, $(VAR)) 37*333d2b36SAndroid Build Coastguard Workerfunc FindSoongVariables(mkFile string, includeFileScope mkparser.Scope, registrar variableRegistrar) error { 38*333d2b36SAndroid Build Coastguard Worker ctx := context{includeFileScope, registrar} 39*333d2b36SAndroid Build Coastguard Worker return ctx.doFind(mkFile) 40*333d2b36SAndroid Build Coastguard Worker} 41*333d2b36SAndroid Build Coastguard Worker 42*333d2b36SAndroid Build Coastguard Workerfunc (ctx *context) doFind(mkFile string) error { 43*333d2b36SAndroid Build Coastguard Worker mkContents, err := ioutil.ReadFile(mkFile) 44*333d2b36SAndroid Build Coastguard Worker if err != nil { 45*333d2b36SAndroid Build Coastguard Worker return err 46*333d2b36SAndroid Build Coastguard Worker } 47*333d2b36SAndroid Build Coastguard Worker parser := mkparser.NewParser(mkFile, bytes.NewBuffer(mkContents)) 48*333d2b36SAndroid Build Coastguard Worker nodes, errs := parser.Parse() 49*333d2b36SAndroid Build Coastguard Worker if len(errs) > 0 { 50*333d2b36SAndroid Build Coastguard Worker for _, e := range errs { 51*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, "ERROR:", e) 52*333d2b36SAndroid Build Coastguard Worker } 53*333d2b36SAndroid Build Coastguard Worker return fmt.Errorf("cannot parse %s", mkFile) 54*333d2b36SAndroid Build Coastguard Worker } 55*333d2b36SAndroid Build Coastguard Worker for _, node := range nodes { 56*333d2b36SAndroid Build Coastguard Worker switch t := node.(type) { 57*333d2b36SAndroid Build Coastguard Worker case *mkparser.Variable: 58*333d2b36SAndroid Build Coastguard Worker ctx.handleVariable(t) 59*333d2b36SAndroid Build Coastguard Worker case *mkparser.Directive: 60*333d2b36SAndroid Build Coastguard Worker ctx.handleInclude(t) 61*333d2b36SAndroid Build Coastguard Worker } 62*333d2b36SAndroid Build Coastguard Worker } 63*333d2b36SAndroid Build Coastguard Worker return nil 64*333d2b36SAndroid Build Coastguard Worker} 65*333d2b36SAndroid Build Coastguard Worker 66*333d2b36SAndroid Build Coastguard Workerfunc (ctx context) NewSoongVariable(name, typeString string) { 67*333d2b36SAndroid Build Coastguard Worker var valueType starlarkType 68*333d2b36SAndroid Build Coastguard Worker switch typeString { 69*333d2b36SAndroid Build Coastguard Worker case "bool": 70*333d2b36SAndroid Build Coastguard Worker // TODO: We run into several issues later on if we type this as a bool: 71*333d2b36SAndroid Build Coastguard Worker // - We still assign bool-typed variables to strings 72*333d2b36SAndroid Build Coastguard Worker // - When emitting the final results as make code, some bool's false values have to 73*333d2b36SAndroid Build Coastguard Worker // be an empty string, and some have to be false in order to match the make variables. 74*333d2b36SAndroid Build Coastguard Worker valueType = starlarkTypeString 75*333d2b36SAndroid Build Coastguard Worker case "csv": 76*333d2b36SAndroid Build Coastguard Worker // Only PLATFORM_VERSION_ALL_CODENAMES, and it's a list 77*333d2b36SAndroid Build Coastguard Worker valueType = starlarkTypeList 78*333d2b36SAndroid Build Coastguard Worker case "list": 79*333d2b36SAndroid Build Coastguard Worker valueType = starlarkTypeList 80*333d2b36SAndroid Build Coastguard Worker case "str": 81*333d2b36SAndroid Build Coastguard Worker valueType = starlarkTypeString 82*333d2b36SAndroid Build Coastguard Worker case "val": 83*333d2b36SAndroid Build Coastguard Worker // Only PLATFORM_SDK_VERSION uses this, and it's integer 84*333d2b36SAndroid Build Coastguard Worker valueType = starlarkTypeInt 85*333d2b36SAndroid Build Coastguard Worker default: 86*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("unknown Soong variable type %s", typeString)) 87*333d2b36SAndroid Build Coastguard Worker } 88*333d2b36SAndroid Build Coastguard Worker 89*333d2b36SAndroid Build Coastguard Worker ctx.registrar.NewVariable(name, VarClassSoong, valueType) 90*333d2b36SAndroid Build Coastguard Worker} 91*333d2b36SAndroid Build Coastguard Worker 92*333d2b36SAndroid Build Coastguard Workerfunc (ctx context) handleInclude(t *mkparser.Directive) { 93*333d2b36SAndroid Build Coastguard Worker if t.Name != "include" && t.Name != "-include" { 94*333d2b36SAndroid Build Coastguard Worker return 95*333d2b36SAndroid Build Coastguard Worker } 96*333d2b36SAndroid Build Coastguard Worker includedPath := t.Args.Value(ctx.includeFileScope) 97*333d2b36SAndroid Build Coastguard Worker err := ctx.doFind(includedPath) 98*333d2b36SAndroid Build Coastguard Worker if err != nil && t.Name == "include" { 99*333d2b36SAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "cannot include %s: %s", includedPath, err) 100*333d2b36SAndroid Build Coastguard Worker } 101*333d2b36SAndroid Build Coastguard Worker} 102*333d2b36SAndroid Build Coastguard Worker 103*333d2b36SAndroid Build Coastguard Workervar callFuncRex = regexp.MustCompile("^call +add_json_(str|val|bool|csv|list) *,") 104*333d2b36SAndroid Build Coastguard Worker 105*333d2b36SAndroid Build Coastguard Workerfunc (ctx context) handleVariable(t *mkparser.Variable) { 106*333d2b36SAndroid Build Coastguard Worker // From the variable reference looking as follows: 107*333d2b36SAndroid Build Coastguard Worker // $(call json_add_TYPE,arg1,$(VAR)) 108*333d2b36SAndroid Build Coastguard Worker // we infer that the type of $(VAR) is TYPE 109*333d2b36SAndroid Build Coastguard Worker // VAR can be a simple variable name, or another call 110*333d2b36SAndroid Build Coastguard Worker // (e.g., $(call invert_bool, $(X)), from which we can infer 111*333d2b36SAndroid Build Coastguard Worker // that the type of X is bool 112*333d2b36SAndroid Build Coastguard Worker if prefix, v, ok := prefixedVariable(t.Name); ok && strings.HasPrefix(prefix, "call add_json") { 113*333d2b36SAndroid Build Coastguard Worker if match := callFuncRex.FindStringSubmatch(prefix); match != nil { 114*333d2b36SAndroid Build Coastguard Worker ctx.inferSoongVariableType(match[1], v) 115*333d2b36SAndroid Build Coastguard Worker // NOTE(asmundak): sometimes arg1 (the name of the Soong variable defined 116*333d2b36SAndroid Build Coastguard Worker // in this statement) may indicate that there is a Make counterpart. E.g, from 117*333d2b36SAndroid Build Coastguard Worker // $(call add_json_bool, DisablePreopt, $(call invert_bool,$(ENABLE_PREOPT))) 118*333d2b36SAndroid Build Coastguard Worker // it may be inferred that there is a Make boolean variable DISABLE_PREOPT. 119*333d2b36SAndroid Build Coastguard Worker // Unfortunately, Soong variable names have no 1:1 correspondence to Make variables, 120*333d2b36SAndroid Build Coastguard Worker // for instance, 121*333d2b36SAndroid Build Coastguard Worker // $(call add_json_list, PatternsOnSystemOther, $(SYSTEM_OTHER_ODEX_FILTER)) 122*333d2b36SAndroid Build Coastguard Worker // does not mean that there is PATTERNS_ON_SYSTEM_OTHER 123*333d2b36SAndroid Build Coastguard Worker // Our main interest lies in finding the variables whose values are lists, and 124*333d2b36SAndroid Build Coastguard Worker // so far there are none that can be found this way, so it is not important. 125*333d2b36SAndroid Build Coastguard Worker } else { 126*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("cannot match the call: %s", prefix)) 127*333d2b36SAndroid Build Coastguard Worker } 128*333d2b36SAndroid Build Coastguard Worker } 129*333d2b36SAndroid Build Coastguard Worker} 130*333d2b36SAndroid Build Coastguard Worker 131*333d2b36SAndroid Build Coastguard Workervar ( 132*333d2b36SAndroid Build Coastguard Worker callInvertBoolRex = regexp.MustCompile("^call +invert_bool *, *$") 133*333d2b36SAndroid Build Coastguard Worker callFilterBoolRex = regexp.MustCompile("^(filter|filter-out) +(true|false), *$") 134*333d2b36SAndroid Build Coastguard Worker) 135*333d2b36SAndroid Build Coastguard Worker 136*333d2b36SAndroid Build Coastguard Workerfunc (ctx context) inferSoongVariableType(vType string, n *mkparser.MakeString) { 137*333d2b36SAndroid Build Coastguard Worker if n.Const() { 138*333d2b36SAndroid Build Coastguard Worker ctx.NewSoongVariable(n.Strings[0], vType) 139*333d2b36SAndroid Build Coastguard Worker return 140*333d2b36SAndroid Build Coastguard Worker } 141*333d2b36SAndroid Build Coastguard Worker if prefix, v, ok := prefixedVariable(n); ok { 142*333d2b36SAndroid Build Coastguard Worker if callInvertBoolRex.MatchString(prefix) || callFilterBoolRex.MatchString(prefix) { 143*333d2b36SAndroid Build Coastguard Worker // It is $(call invert_bool, $(VAR)) or $(filter[-out] [false|true],$(VAR)) 144*333d2b36SAndroid Build Coastguard Worker ctx.inferSoongVariableType("bool", v) 145*333d2b36SAndroid Build Coastguard Worker } 146*333d2b36SAndroid Build Coastguard Worker } 147*333d2b36SAndroid Build Coastguard Worker} 148*333d2b36SAndroid Build Coastguard Worker 149*333d2b36SAndroid Build Coastguard Worker// If MakeString is foo$(BAR), returns 'foo', BAR(as *MakeString) and true 150*333d2b36SAndroid Build Coastguard Workerfunc prefixedVariable(s *mkparser.MakeString) (string, *mkparser.MakeString, bool) { 151*333d2b36SAndroid Build Coastguard Worker if len(s.Strings) != 2 || s.Strings[1] != "" { 152*333d2b36SAndroid Build Coastguard Worker return "", nil, false 153*333d2b36SAndroid Build Coastguard Worker } 154*333d2b36SAndroid Build Coastguard Worker return s.Strings[0], s.Variables[0].Name, true 155*333d2b36SAndroid Build Coastguard Worker} 156