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 Worker// Convert makefile containing device configuration to Starlark file 16*333d2b36SAndroid Build Coastguard Worker// The conversion can handle the following constructs in a makefile: 17*333d2b36SAndroid Build Coastguard Worker// - comments 18*333d2b36SAndroid Build Coastguard Worker// - simple variable assignments 19*333d2b36SAndroid Build Coastguard Worker// - $(call init-product,<file>) 20*333d2b36SAndroid Build Coastguard Worker// - $(call inherit-product-if-exists 21*333d2b36SAndroid Build Coastguard Worker// - if directives 22*333d2b36SAndroid Build Coastguard Worker// 23*333d2b36SAndroid Build Coastguard Worker// All other constructs are carried over to the output starlark file as comments. 24*333d2b36SAndroid Build Coastguard Workerpackage mk2rbc 25*333d2b36SAndroid Build Coastguard Worker 26*333d2b36SAndroid Build Coastguard Workerimport ( 27*333d2b36SAndroid Build Coastguard Worker "bytes" 28*333d2b36SAndroid Build Coastguard Worker "fmt" 29*333d2b36SAndroid Build Coastguard Worker "io" 30*333d2b36SAndroid Build Coastguard Worker "io/fs" 31*333d2b36SAndroid Build Coastguard Worker "io/ioutil" 32*333d2b36SAndroid Build Coastguard Worker "os" 33*333d2b36SAndroid Build Coastguard Worker "path/filepath" 34*333d2b36SAndroid Build Coastguard Worker "regexp" 35*333d2b36SAndroid Build Coastguard Worker "sort" 36*333d2b36SAndroid Build Coastguard Worker "strconv" 37*333d2b36SAndroid Build Coastguard Worker "strings" 38*333d2b36SAndroid Build Coastguard Worker "text/scanner" 39*333d2b36SAndroid Build Coastguard Worker 40*333d2b36SAndroid Build Coastguard Worker mkparser "android/soong/androidmk/parser" 41*333d2b36SAndroid Build Coastguard Worker) 42*333d2b36SAndroid Build Coastguard Worker 43*333d2b36SAndroid Build Coastguard Workerconst ( 44*333d2b36SAndroid Build Coastguard Worker annotationCommentPrefix = "RBC#" 45*333d2b36SAndroid Build Coastguard Worker baseUri = "//build/make/core:product_config.rbc" 46*333d2b36SAndroid Build Coastguard Worker // The name of the struct exported by the product_config.rbc 47*333d2b36SAndroid Build Coastguard Worker // that contains the functions and variables available to 48*333d2b36SAndroid Build Coastguard Worker // product configuration Starlark files. 49*333d2b36SAndroid Build Coastguard Worker baseName = "rblf" 50*333d2b36SAndroid Build Coastguard Worker 51*333d2b36SAndroid Build Coastguard Worker soongNsPrefix = "SOONG_CONFIG_" 52*333d2b36SAndroid Build Coastguard Worker 53*333d2b36SAndroid Build Coastguard Worker // And here are the functions and variables: 54*333d2b36SAndroid Build Coastguard Worker cfnGetCfg = baseName + ".cfg" 55*333d2b36SAndroid Build Coastguard Worker cfnMain = baseName + ".product_configuration" 56*333d2b36SAndroid Build Coastguard Worker cfnBoardMain = baseName + ".board_configuration" 57*333d2b36SAndroid Build Coastguard Worker cfnPrintVars = baseName + ".printvars" 58*333d2b36SAndroid Build Coastguard Worker cfnInherit = baseName + ".inherit" 59*333d2b36SAndroid Build Coastguard Worker cfnSetListDefault = baseName + ".setdefault" 60*333d2b36SAndroid Build Coastguard Worker) 61*333d2b36SAndroid Build Coastguard Worker 62*333d2b36SAndroid Build Coastguard Workerconst ( 63*333d2b36SAndroid Build Coastguard Worker soongConfigAppend = "soong_config_append" 64*333d2b36SAndroid Build Coastguard Worker soongConfigAssign = "soong_config_set" 65*333d2b36SAndroid Build Coastguard Worker) 66*333d2b36SAndroid Build Coastguard Worker 67*333d2b36SAndroid Build Coastguard Workervar knownFunctions = map[string]interface { 68*333d2b36SAndroid Build Coastguard Worker parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr 69*333d2b36SAndroid Build Coastguard Worker}{ 70*333d2b36SAndroid Build Coastguard Worker "abspath": &simpleCallParser{name: baseName + ".abspath", returnType: starlarkTypeString}, 71*333d2b36SAndroid Build Coastguard Worker "add-product-dex-preopt-module-config": &simpleCallParser{name: baseName + ".add_product_dex_preopt_module_config", returnType: starlarkTypeString, addHandle: true}, 72*333d2b36SAndroid Build Coastguard Worker "add_soong_config_namespace": &simpleCallParser{name: baseName + ".soong_config_namespace", returnType: starlarkTypeVoid, addGlobals: true}, 73*333d2b36SAndroid Build Coastguard Worker "add_soong_config_var_value": &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true}, 74*333d2b36SAndroid Build Coastguard Worker soongConfigAssign: &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true}, 75*333d2b36SAndroid Build Coastguard Worker "soong_config_set_bool": &simpleCallParser{name: baseName + ".soong_config_set_bool", returnType: starlarkTypeVoid, addGlobals: true}, 76*333d2b36SAndroid Build Coastguard Worker soongConfigAppend: &simpleCallParser{name: baseName + ".soong_config_append", returnType: starlarkTypeVoid, addGlobals: true}, 77*333d2b36SAndroid Build Coastguard Worker "soong_config_get": &simpleCallParser{name: baseName + ".soong_config_get", returnType: starlarkTypeString, addGlobals: true}, 78*333d2b36SAndroid Build Coastguard Worker "add-to-product-copy-files-if-exists": &simpleCallParser{name: baseName + ".copy_if_exists", returnType: starlarkTypeList}, 79*333d2b36SAndroid Build Coastguard Worker "addprefix": &simpleCallParser{name: baseName + ".addprefix", returnType: starlarkTypeList}, 80*333d2b36SAndroid Build Coastguard Worker "addsuffix": &simpleCallParser{name: baseName + ".addsuffix", returnType: starlarkTypeList}, 81*333d2b36SAndroid Build Coastguard Worker "and": &andOrParser{isAnd: true}, 82*333d2b36SAndroid Build Coastguard Worker "clear-var-list": &simpleCallParser{name: baseName + ".clear_var_list", returnType: starlarkTypeVoid, addGlobals: true, addHandle: true}, 83*333d2b36SAndroid Build Coastguard Worker "copy-files": &simpleCallParser{name: baseName + ".copy_files", returnType: starlarkTypeList}, 84*333d2b36SAndroid Build Coastguard Worker "dir": &simpleCallParser{name: baseName + ".dir", returnType: starlarkTypeString}, 85*333d2b36SAndroid Build Coastguard Worker "dist-for-goals": &simpleCallParser{name: baseName + ".mkdist_for_goals", returnType: starlarkTypeVoid, addGlobals: true}, 86*333d2b36SAndroid Build Coastguard Worker "enforce-product-packages-exist": &simpleCallParser{name: baseName + ".enforce_product_packages_exist", returnType: starlarkTypeVoid, addHandle: true}, 87*333d2b36SAndroid Build Coastguard Worker "error": &makeControlFuncParser{name: baseName + ".mkerror"}, 88*333d2b36SAndroid Build Coastguard Worker "findstring": &simpleCallParser{name: baseName + ".findstring", returnType: starlarkTypeInt}, 89*333d2b36SAndroid Build Coastguard Worker "find-copy-subdir-files": &simpleCallParser{name: baseName + ".find_and_copy", returnType: starlarkTypeList}, 90*333d2b36SAndroid Build Coastguard Worker "filter": &simpleCallParser{name: baseName + ".filter", returnType: starlarkTypeList}, 91*333d2b36SAndroid Build Coastguard Worker "filter-out": &simpleCallParser{name: baseName + ".filter_out", returnType: starlarkTypeList}, 92*333d2b36SAndroid Build Coastguard Worker "firstword": &simpleCallParser{name: baseName + ".first_word", returnType: starlarkTypeString}, 93*333d2b36SAndroid Build Coastguard Worker "foreach": &foreachCallParser{}, 94*333d2b36SAndroid Build Coastguard Worker "if": &ifCallParser{}, 95*333d2b36SAndroid Build Coastguard Worker "info": &makeControlFuncParser{name: baseName + ".mkinfo"}, 96*333d2b36SAndroid Build Coastguard Worker "is-board-platform": &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true}, 97*333d2b36SAndroid Build Coastguard Worker "is-board-platform2": &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true}, 98*333d2b36SAndroid Build Coastguard Worker "is-board-platform-in-list": &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true}, 99*333d2b36SAndroid Build Coastguard Worker "is-board-platform-in-list2": &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true}, 100*333d2b36SAndroid Build Coastguard Worker "is-product-in-list": &isProductInListCallParser{}, 101*333d2b36SAndroid Build Coastguard Worker "is-vendor-board-platform": &isVendorBoardPlatformCallParser{}, 102*333d2b36SAndroid Build Coastguard Worker "is-vendor-board-qcom": &isVendorBoardQcomCallParser{}, 103*333d2b36SAndroid Build Coastguard Worker "lastword": &simpleCallParser{name: baseName + ".last_word", returnType: starlarkTypeString}, 104*333d2b36SAndroid Build Coastguard Worker "notdir": &simpleCallParser{name: baseName + ".notdir", returnType: starlarkTypeString}, 105*333d2b36SAndroid Build Coastguard Worker "math_max": &mathMaxOrMinCallParser{function: "max"}, 106*333d2b36SAndroid Build Coastguard Worker "math_min": &mathMaxOrMinCallParser{function: "min"}, 107*333d2b36SAndroid Build Coastguard Worker "math_gt_or_eq": &mathComparisonCallParser{op: ">="}, 108*333d2b36SAndroid Build Coastguard Worker "math_gt": &mathComparisonCallParser{op: ">"}, 109*333d2b36SAndroid Build Coastguard Worker "math_lt": &mathComparisonCallParser{op: "<"}, 110*333d2b36SAndroid Build Coastguard Worker "my-dir": &myDirCallParser{}, 111*333d2b36SAndroid Build Coastguard Worker "or": &andOrParser{isAnd: false}, 112*333d2b36SAndroid Build Coastguard Worker "patsubst": &substCallParser{fname: "patsubst"}, 113*333d2b36SAndroid Build Coastguard Worker "product-copy-files-by-pattern": &simpleCallParser{name: baseName + ".product_copy_files_by_pattern", returnType: starlarkTypeList}, 114*333d2b36SAndroid Build Coastguard Worker "require-artifacts-in-path": &simpleCallParser{name: baseName + ".require_artifacts_in_path", returnType: starlarkTypeVoid, addHandle: true}, 115*333d2b36SAndroid Build Coastguard Worker "require-artifacts-in-path-relaxed": &simpleCallParser{name: baseName + ".require_artifacts_in_path_relaxed", returnType: starlarkTypeVoid, addHandle: true}, 116*333d2b36SAndroid Build Coastguard Worker // TODO(asmundak): remove it once all calls are removed from configuration makefiles. see b/183161002 117*333d2b36SAndroid Build Coastguard Worker "shell": &shellCallParser{}, 118*333d2b36SAndroid Build Coastguard Worker "sort": &simpleCallParser{name: baseName + ".mksort", returnType: starlarkTypeList}, 119*333d2b36SAndroid Build Coastguard Worker "strip": &simpleCallParser{name: baseName + ".mkstrip", returnType: starlarkTypeString}, 120*333d2b36SAndroid Build Coastguard Worker "subst": &substCallParser{fname: "subst"}, 121*333d2b36SAndroid Build Coastguard Worker "to-lower": &lowerUpperParser{isUpper: false}, 122*333d2b36SAndroid Build Coastguard Worker "to-upper": &lowerUpperParser{isUpper: true}, 123*333d2b36SAndroid Build Coastguard Worker "warning": &makeControlFuncParser{name: baseName + ".mkwarning"}, 124*333d2b36SAndroid Build Coastguard Worker "word": &wordCallParser{}, 125*333d2b36SAndroid Build Coastguard Worker "words": &wordsCallParser{}, 126*333d2b36SAndroid Build Coastguard Worker "wildcard": &simpleCallParser{name: baseName + ".expand_wildcard", returnType: starlarkTypeList}, 127*333d2b36SAndroid Build Coastguard Worker} 128*333d2b36SAndroid Build Coastguard Worker 129*333d2b36SAndroid Build Coastguard Worker// The same as knownFunctions, but returns a []starlarkNode instead of a starlarkExpr 130*333d2b36SAndroid Build Coastguard Workervar knownNodeFunctions = map[string]interface { 131*333d2b36SAndroid Build Coastguard Worker parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) []starlarkNode 132*333d2b36SAndroid Build Coastguard Worker}{ 133*333d2b36SAndroid Build Coastguard Worker "eval": &evalNodeParser{}, 134*333d2b36SAndroid Build Coastguard Worker "if": &ifCallNodeParser{}, 135*333d2b36SAndroid Build Coastguard Worker "inherit-product": &inheritProductCallParser{loadAlways: true}, 136*333d2b36SAndroid Build Coastguard Worker "inherit-product-if-exists": &inheritProductCallParser{loadAlways: false}, 137*333d2b36SAndroid Build Coastguard Worker "foreach": &foreachCallNodeParser{}, 138*333d2b36SAndroid Build Coastguard Worker} 139*333d2b36SAndroid Build Coastguard Worker 140*333d2b36SAndroid Build Coastguard Worker// These look like variables, but are actually functions, and would give 141*333d2b36SAndroid Build Coastguard Worker// undefined variable errors if we converted them as variables. Instead, 142*333d2b36SAndroid Build Coastguard Worker// emit an error instead of converting them. 143*333d2b36SAndroid Build Coastguard Workervar unsupportedFunctions = map[string]bool{ 144*333d2b36SAndroid Build Coastguard Worker "local-generated-sources-dir": true, 145*333d2b36SAndroid Build Coastguard Worker "local-intermediates-dir": true, 146*333d2b36SAndroid Build Coastguard Worker} 147*333d2b36SAndroid Build Coastguard Worker 148*333d2b36SAndroid Build Coastguard Worker// These are functions that we don't implement conversions for, but 149*333d2b36SAndroid Build Coastguard Worker// we allow seeing their definitions in the product config files. 150*333d2b36SAndroid Build Coastguard Workervar ignoredDefines = map[string]bool{ 151*333d2b36SAndroid Build Coastguard Worker "find-word-in-list": true, // internal macro 152*333d2b36SAndroid Build Coastguard Worker "get-vendor-board-platforms": true, // internal macro, used by is-board-platform, etc. 153*333d2b36SAndroid Build Coastguard Worker "is-android-codename": true, // unused by product config 154*333d2b36SAndroid Build Coastguard Worker "is-android-codename-in-list": true, // unused by product config 155*333d2b36SAndroid Build Coastguard Worker "is-chipset-in-board-platform": true, // unused by product config 156*333d2b36SAndroid Build Coastguard Worker "is-chipset-prefix-in-board-platform": true, // unused by product config 157*333d2b36SAndroid Build Coastguard Worker "is-not-board-platform": true, // defined but never used 158*333d2b36SAndroid Build Coastguard Worker "is-platform-sdk-version-at-least": true, // unused by product config 159*333d2b36SAndroid Build Coastguard Worker "match-prefix": true, // internal macro 160*333d2b36SAndroid Build Coastguard Worker "match-word": true, // internal macro 161*333d2b36SAndroid Build Coastguard Worker "match-word-in-list": true, // internal macro 162*333d2b36SAndroid Build Coastguard Worker "tb-modules": true, // defined in hardware/amlogic/tb_modules/tb_detect.mk, unused 163*333d2b36SAndroid Build Coastguard Worker} 164*333d2b36SAndroid Build Coastguard Worker 165*333d2b36SAndroid Build Coastguard Workervar identifierFullMatchRegex = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$") 166*333d2b36SAndroid Build Coastguard Worker 167*333d2b36SAndroid Build Coastguard Workerfunc RelativeToCwd(path string) (string, error) { 168*333d2b36SAndroid Build Coastguard Worker cwd, err := os.Getwd() 169*333d2b36SAndroid Build Coastguard Worker if err != nil { 170*333d2b36SAndroid Build Coastguard Worker return "", err 171*333d2b36SAndroid Build Coastguard Worker } 172*333d2b36SAndroid Build Coastguard Worker path, err = filepath.Rel(cwd, path) 173*333d2b36SAndroid Build Coastguard Worker if err != nil { 174*333d2b36SAndroid Build Coastguard Worker return "", err 175*333d2b36SAndroid Build Coastguard Worker } 176*333d2b36SAndroid Build Coastguard Worker if strings.HasPrefix(path, "../") { 177*333d2b36SAndroid Build Coastguard Worker return "", fmt.Errorf("Could not make path relative to current working directory: " + path) 178*333d2b36SAndroid Build Coastguard Worker } 179*333d2b36SAndroid Build Coastguard Worker return path, nil 180*333d2b36SAndroid Build Coastguard Worker} 181*333d2b36SAndroid Build Coastguard Worker 182*333d2b36SAndroid Build Coastguard Worker// Conversion request parameters 183*333d2b36SAndroid Build Coastguard Workertype Request struct { 184*333d2b36SAndroid Build Coastguard Worker MkFile string // file to convert 185*333d2b36SAndroid Build Coastguard Worker Reader io.Reader // if set, read input from this stream instead 186*333d2b36SAndroid Build Coastguard Worker OutputSuffix string // generated Starlark files suffix 187*333d2b36SAndroid Build Coastguard Worker OutputDir string // if set, root of the output hierarchy 188*333d2b36SAndroid Build Coastguard Worker ErrorLogger ErrorLogger 189*333d2b36SAndroid Build Coastguard Worker TracedVariables []string // trace assignment to these variables 190*333d2b36SAndroid Build Coastguard Worker TraceCalls bool 191*333d2b36SAndroid Build Coastguard Worker SourceFS fs.FS 192*333d2b36SAndroid Build Coastguard Worker MakefileFinder MakefileFinder 193*333d2b36SAndroid Build Coastguard Worker} 194*333d2b36SAndroid Build Coastguard Worker 195*333d2b36SAndroid Build Coastguard Worker// ErrorLogger prints errors and gathers error statistics. 196*333d2b36SAndroid Build Coastguard Worker// Its NewError function is called on every error encountered during the conversion. 197*333d2b36SAndroid Build Coastguard Workertype ErrorLogger interface { 198*333d2b36SAndroid Build Coastguard Worker NewError(el ErrorLocation, node mkparser.Node, text string, args ...interface{}) 199*333d2b36SAndroid Build Coastguard Worker} 200*333d2b36SAndroid Build Coastguard Worker 201*333d2b36SAndroid Build Coastguard Workertype ErrorLocation struct { 202*333d2b36SAndroid Build Coastguard Worker MkFile string 203*333d2b36SAndroid Build Coastguard Worker MkLine int 204*333d2b36SAndroid Build Coastguard Worker} 205*333d2b36SAndroid Build Coastguard Worker 206*333d2b36SAndroid Build Coastguard Workerfunc (el ErrorLocation) String() string { 207*333d2b36SAndroid Build Coastguard Worker return fmt.Sprintf("%s:%d", el.MkFile, el.MkLine) 208*333d2b36SAndroid Build Coastguard Worker} 209*333d2b36SAndroid Build Coastguard Worker 210*333d2b36SAndroid Build Coastguard Worker// Derives module name for a given file. It is base name 211*333d2b36SAndroid Build Coastguard Worker// (file name without suffix), with some characters replaced to make it a Starlark identifier 212*333d2b36SAndroid Build Coastguard Workerfunc moduleNameForFile(mkFile string) string { 213*333d2b36SAndroid Build Coastguard Worker base := strings.TrimSuffix(filepath.Base(mkFile), filepath.Ext(mkFile)) 214*333d2b36SAndroid Build Coastguard Worker // TODO(asmundak): what else can be in the product file names? 215*333d2b36SAndroid Build Coastguard Worker return strings.NewReplacer("-", "_", ".", "_").Replace(base) 216*333d2b36SAndroid Build Coastguard Worker 217*333d2b36SAndroid Build Coastguard Worker} 218*333d2b36SAndroid Build Coastguard Worker 219*333d2b36SAndroid Build Coastguard Workerfunc cloneMakeString(mkString *mkparser.MakeString) *mkparser.MakeString { 220*333d2b36SAndroid Build Coastguard Worker r := &mkparser.MakeString{StringPos: mkString.StringPos} 221*333d2b36SAndroid Build Coastguard Worker r.Strings = append(r.Strings, mkString.Strings...) 222*333d2b36SAndroid Build Coastguard Worker r.Variables = append(r.Variables, mkString.Variables...) 223*333d2b36SAndroid Build Coastguard Worker return r 224*333d2b36SAndroid Build Coastguard Worker} 225*333d2b36SAndroid Build Coastguard Worker 226*333d2b36SAndroid Build Coastguard Workerfunc isMakeControlFunc(s string) bool { 227*333d2b36SAndroid Build Coastguard Worker return s == "error" || s == "warning" || s == "info" 228*333d2b36SAndroid Build Coastguard Worker} 229*333d2b36SAndroid Build Coastguard Worker 230*333d2b36SAndroid Build Coastguard Worker// varAssignmentScope points to the last assignment for each variable 231*333d2b36SAndroid Build Coastguard Worker// in the current block. It is used during the parsing to chain 232*333d2b36SAndroid Build Coastguard Worker// the assignments to a variable together. 233*333d2b36SAndroid Build Coastguard Workertype varAssignmentScope struct { 234*333d2b36SAndroid Build Coastguard Worker outer *varAssignmentScope 235*333d2b36SAndroid Build Coastguard Worker vars map[string]bool 236*333d2b36SAndroid Build Coastguard Worker} 237*333d2b36SAndroid Build Coastguard Worker 238*333d2b36SAndroid Build Coastguard Worker// Starlark output generation context 239*333d2b36SAndroid Build Coastguard Workertype generationContext struct { 240*333d2b36SAndroid Build Coastguard Worker buf strings.Builder 241*333d2b36SAndroid Build Coastguard Worker starScript *StarlarkScript 242*333d2b36SAndroid Build Coastguard Worker indentLevel int 243*333d2b36SAndroid Build Coastguard Worker inAssignment bool 244*333d2b36SAndroid Build Coastguard Worker tracedCount int 245*333d2b36SAndroid Build Coastguard Worker varAssignments *varAssignmentScope 246*333d2b36SAndroid Build Coastguard Worker} 247*333d2b36SAndroid Build Coastguard Worker 248*333d2b36SAndroid Build Coastguard Workerfunc NewGenerateContext(ss *StarlarkScript) *generationContext { 249*333d2b36SAndroid Build Coastguard Worker return &generationContext{ 250*333d2b36SAndroid Build Coastguard Worker starScript: ss, 251*333d2b36SAndroid Build Coastguard Worker varAssignments: &varAssignmentScope{ 252*333d2b36SAndroid Build Coastguard Worker outer: nil, 253*333d2b36SAndroid Build Coastguard Worker vars: make(map[string]bool), 254*333d2b36SAndroid Build Coastguard Worker }, 255*333d2b36SAndroid Build Coastguard Worker } 256*333d2b36SAndroid Build Coastguard Worker} 257*333d2b36SAndroid Build Coastguard Worker 258*333d2b36SAndroid Build Coastguard Workerfunc (gctx *generationContext) pushVariableAssignments() { 259*333d2b36SAndroid Build Coastguard Worker va := &varAssignmentScope{ 260*333d2b36SAndroid Build Coastguard Worker outer: gctx.varAssignments, 261*333d2b36SAndroid Build Coastguard Worker vars: make(map[string]bool), 262*333d2b36SAndroid Build Coastguard Worker } 263*333d2b36SAndroid Build Coastguard Worker gctx.varAssignments = va 264*333d2b36SAndroid Build Coastguard Worker} 265*333d2b36SAndroid Build Coastguard Worker 266*333d2b36SAndroid Build Coastguard Workerfunc (gctx *generationContext) popVariableAssignments() { 267*333d2b36SAndroid Build Coastguard Worker gctx.varAssignments = gctx.varAssignments.outer 268*333d2b36SAndroid Build Coastguard Worker} 269*333d2b36SAndroid Build Coastguard Worker 270*333d2b36SAndroid Build Coastguard Workerfunc (gctx *generationContext) hasBeenAssigned(v variable) bool { 271*333d2b36SAndroid Build Coastguard Worker for va := gctx.varAssignments; va != nil; va = va.outer { 272*333d2b36SAndroid Build Coastguard Worker if _, ok := va.vars[v.name()]; ok { 273*333d2b36SAndroid Build Coastguard Worker return true 274*333d2b36SAndroid Build Coastguard Worker } 275*333d2b36SAndroid Build Coastguard Worker } 276*333d2b36SAndroid Build Coastguard Worker return false 277*333d2b36SAndroid Build Coastguard Worker} 278*333d2b36SAndroid Build Coastguard Worker 279*333d2b36SAndroid Build Coastguard Workerfunc (gctx *generationContext) setHasBeenAssigned(v variable) { 280*333d2b36SAndroid Build Coastguard Worker gctx.varAssignments.vars[v.name()] = true 281*333d2b36SAndroid Build Coastguard Worker} 282*333d2b36SAndroid Build Coastguard Worker 283*333d2b36SAndroid Build Coastguard Worker// emit returns generated script 284*333d2b36SAndroid Build Coastguard Workerfunc (gctx *generationContext) emit() string { 285*333d2b36SAndroid Build Coastguard Worker ss := gctx.starScript 286*333d2b36SAndroid Build Coastguard Worker 287*333d2b36SAndroid Build Coastguard Worker // The emitted code has the following layout: 288*333d2b36SAndroid Build Coastguard Worker // <initial comments> 289*333d2b36SAndroid Build Coastguard Worker // preamble, i.e., 290*333d2b36SAndroid Build Coastguard Worker // load statement for the runtime support 291*333d2b36SAndroid Build Coastguard Worker // load statement for each unique submodule pulled in by this one 292*333d2b36SAndroid Build Coastguard Worker // def init(g, handle): 293*333d2b36SAndroid Build Coastguard Worker // cfg = rblf.cfg(handle) 294*333d2b36SAndroid Build Coastguard Worker // <statements> 295*333d2b36SAndroid Build Coastguard Worker // <warning if conversion was not clean> 296*333d2b36SAndroid Build Coastguard Worker 297*333d2b36SAndroid Build Coastguard Worker iNode := len(ss.nodes) 298*333d2b36SAndroid Build Coastguard Worker for i, node := range ss.nodes { 299*333d2b36SAndroid Build Coastguard Worker if _, ok := node.(*commentNode); !ok { 300*333d2b36SAndroid Build Coastguard Worker iNode = i 301*333d2b36SAndroid Build Coastguard Worker break 302*333d2b36SAndroid Build Coastguard Worker } 303*333d2b36SAndroid Build Coastguard Worker node.emit(gctx) 304*333d2b36SAndroid Build Coastguard Worker } 305*333d2b36SAndroid Build Coastguard Worker 306*333d2b36SAndroid Build Coastguard Worker gctx.emitPreamble() 307*333d2b36SAndroid Build Coastguard Worker 308*333d2b36SAndroid Build Coastguard Worker gctx.newLine() 309*333d2b36SAndroid Build Coastguard Worker // The arguments passed to the init function are the global dictionary 310*333d2b36SAndroid Build Coastguard Worker // ('g') and the product configuration dictionary ('cfg') 311*333d2b36SAndroid Build Coastguard Worker gctx.write("def init(g, handle):") 312*333d2b36SAndroid Build Coastguard Worker gctx.indentLevel++ 313*333d2b36SAndroid Build Coastguard Worker if gctx.starScript.traceCalls { 314*333d2b36SAndroid Build Coastguard Worker gctx.newLine() 315*333d2b36SAndroid Build Coastguard Worker gctx.writef(`print(">%s")`, gctx.starScript.mkFile) 316*333d2b36SAndroid Build Coastguard Worker } 317*333d2b36SAndroid Build Coastguard Worker gctx.newLine() 318*333d2b36SAndroid Build Coastguard Worker gctx.writef("cfg = %s(handle)", cfnGetCfg) 319*333d2b36SAndroid Build Coastguard Worker for _, node := range ss.nodes[iNode:] { 320*333d2b36SAndroid Build Coastguard Worker node.emit(gctx) 321*333d2b36SAndroid Build Coastguard Worker } 322*333d2b36SAndroid Build Coastguard Worker 323*333d2b36SAndroid Build Coastguard Worker if gctx.starScript.traceCalls { 324*333d2b36SAndroid Build Coastguard Worker gctx.newLine() 325*333d2b36SAndroid Build Coastguard Worker gctx.writef(`print("<%s")`, gctx.starScript.mkFile) 326*333d2b36SAndroid Build Coastguard Worker } 327*333d2b36SAndroid Build Coastguard Worker gctx.indentLevel-- 328*333d2b36SAndroid Build Coastguard Worker gctx.write("\n") 329*333d2b36SAndroid Build Coastguard Worker return gctx.buf.String() 330*333d2b36SAndroid Build Coastguard Worker} 331*333d2b36SAndroid Build Coastguard Worker 332*333d2b36SAndroid Build Coastguard Workerfunc (gctx *generationContext) emitPreamble() { 333*333d2b36SAndroid Build Coastguard Worker gctx.newLine() 334*333d2b36SAndroid Build Coastguard Worker gctx.writef("load(%q, %q)", baseUri, baseName) 335*333d2b36SAndroid Build Coastguard Worker // Emit exactly one load statement for each URI. 336*333d2b36SAndroid Build Coastguard Worker loadedSubConfigs := make(map[string]string) 337*333d2b36SAndroid Build Coastguard Worker for _, mi := range gctx.starScript.inherited { 338*333d2b36SAndroid Build Coastguard Worker uri := mi.path 339*333d2b36SAndroid Build Coastguard Worker if strings.HasPrefix(uri, "/") && !strings.HasPrefix(uri, "//") { 340*333d2b36SAndroid Build Coastguard Worker var err error 341*333d2b36SAndroid Build Coastguard Worker uri, err = RelativeToCwd(uri) 342*333d2b36SAndroid Build Coastguard Worker if err != nil { 343*333d2b36SAndroid Build Coastguard Worker panic(err) 344*333d2b36SAndroid Build Coastguard Worker } 345*333d2b36SAndroid Build Coastguard Worker uri = "//" + uri 346*333d2b36SAndroid Build Coastguard Worker } 347*333d2b36SAndroid Build Coastguard Worker if m, ok := loadedSubConfigs[uri]; ok { 348*333d2b36SAndroid Build Coastguard Worker // No need to emit load statement, but fix module name. 349*333d2b36SAndroid Build Coastguard Worker mi.moduleLocalName = m 350*333d2b36SAndroid Build Coastguard Worker continue 351*333d2b36SAndroid Build Coastguard Worker } 352*333d2b36SAndroid Build Coastguard Worker if mi.optional || mi.missing { 353*333d2b36SAndroid Build Coastguard Worker uri += "|init" 354*333d2b36SAndroid Build Coastguard Worker } 355*333d2b36SAndroid Build Coastguard Worker gctx.newLine() 356*333d2b36SAndroid Build Coastguard Worker gctx.writef("load(%q, %s = \"init\")", uri, mi.entryName()) 357*333d2b36SAndroid Build Coastguard Worker loadedSubConfigs[uri] = mi.moduleLocalName 358*333d2b36SAndroid Build Coastguard Worker } 359*333d2b36SAndroid Build Coastguard Worker gctx.write("\n") 360*333d2b36SAndroid Build Coastguard Worker} 361*333d2b36SAndroid Build Coastguard Worker 362*333d2b36SAndroid Build Coastguard Workerfunc (gctx *generationContext) emitPass() { 363*333d2b36SAndroid Build Coastguard Worker gctx.newLine() 364*333d2b36SAndroid Build Coastguard Worker gctx.write("pass") 365*333d2b36SAndroid Build Coastguard Worker} 366*333d2b36SAndroid Build Coastguard Worker 367*333d2b36SAndroid Build Coastguard Workerfunc (gctx *generationContext) write(ss ...string) { 368*333d2b36SAndroid Build Coastguard Worker for _, s := range ss { 369*333d2b36SAndroid Build Coastguard Worker gctx.buf.WriteString(s) 370*333d2b36SAndroid Build Coastguard Worker } 371*333d2b36SAndroid Build Coastguard Worker} 372*333d2b36SAndroid Build Coastguard Worker 373*333d2b36SAndroid Build Coastguard Workerfunc (gctx *generationContext) writef(format string, args ...interface{}) { 374*333d2b36SAndroid Build Coastguard Worker gctx.write(fmt.Sprintf(format, args...)) 375*333d2b36SAndroid Build Coastguard Worker} 376*333d2b36SAndroid Build Coastguard Worker 377*333d2b36SAndroid Build Coastguard Workerfunc (gctx *generationContext) newLine() { 378*333d2b36SAndroid Build Coastguard Worker if gctx.buf.Len() == 0 { 379*333d2b36SAndroid Build Coastguard Worker return 380*333d2b36SAndroid Build Coastguard Worker } 381*333d2b36SAndroid Build Coastguard Worker gctx.write("\n") 382*333d2b36SAndroid Build Coastguard Worker gctx.writef("%*s", 2*gctx.indentLevel, "") 383*333d2b36SAndroid Build Coastguard Worker} 384*333d2b36SAndroid Build Coastguard Worker 385*333d2b36SAndroid Build Coastguard Workerfunc (gctx *generationContext) emitConversionError(el ErrorLocation, message string) { 386*333d2b36SAndroid Build Coastguard Worker gctx.writef(`rblf.mk2rbc_error("%s", %q)`, el, message) 387*333d2b36SAndroid Build Coastguard Worker} 388*333d2b36SAndroid Build Coastguard Worker 389*333d2b36SAndroid Build Coastguard Workerfunc (gctx *generationContext) emitLoadCheck(im inheritedModule) { 390*333d2b36SAndroid Build Coastguard Worker if !im.needsLoadCheck() { 391*333d2b36SAndroid Build Coastguard Worker return 392*333d2b36SAndroid Build Coastguard Worker } 393*333d2b36SAndroid Build Coastguard Worker gctx.newLine() 394*333d2b36SAndroid Build Coastguard Worker gctx.writef("if not %s:", im.entryName()) 395*333d2b36SAndroid Build Coastguard Worker gctx.indentLevel++ 396*333d2b36SAndroid Build Coastguard Worker gctx.newLine() 397*333d2b36SAndroid Build Coastguard Worker gctx.write(`rblf.mkerror("`, gctx.starScript.mkFile, `", "Cannot find %s" % (`) 398*333d2b36SAndroid Build Coastguard Worker im.pathExpr().emit(gctx) 399*333d2b36SAndroid Build Coastguard Worker gctx.write("))") 400*333d2b36SAndroid Build Coastguard Worker gctx.indentLevel-- 401*333d2b36SAndroid Build Coastguard Worker} 402*333d2b36SAndroid Build Coastguard Worker 403*333d2b36SAndroid Build Coastguard Workertype knownVariable struct { 404*333d2b36SAndroid Build Coastguard Worker name string 405*333d2b36SAndroid Build Coastguard Worker class varClass 406*333d2b36SAndroid Build Coastguard Worker valueType starlarkType 407*333d2b36SAndroid Build Coastguard Worker} 408*333d2b36SAndroid Build Coastguard Worker 409*333d2b36SAndroid Build Coastguard Workertype knownVariables map[string]knownVariable 410*333d2b36SAndroid Build Coastguard Worker 411*333d2b36SAndroid Build Coastguard Workerfunc (pcv knownVariables) NewVariable(name string, varClass varClass, valueType starlarkType) { 412*333d2b36SAndroid Build Coastguard Worker v, exists := pcv[name] 413*333d2b36SAndroid Build Coastguard Worker if !exists { 414*333d2b36SAndroid Build Coastguard Worker pcv[name] = knownVariable{name, varClass, valueType} 415*333d2b36SAndroid Build Coastguard Worker return 416*333d2b36SAndroid Build Coastguard Worker } 417*333d2b36SAndroid Build Coastguard Worker // Conflict resolution: 418*333d2b36SAndroid Build Coastguard Worker // * config class trumps everything 419*333d2b36SAndroid Build Coastguard Worker // * any type trumps unknown type 420*333d2b36SAndroid Build Coastguard Worker match := varClass == v.class 421*333d2b36SAndroid Build Coastguard Worker if !match { 422*333d2b36SAndroid Build Coastguard Worker if varClass == VarClassConfig { 423*333d2b36SAndroid Build Coastguard Worker v.class = VarClassConfig 424*333d2b36SAndroid Build Coastguard Worker match = true 425*333d2b36SAndroid Build Coastguard Worker } else if v.class == VarClassConfig { 426*333d2b36SAndroid Build Coastguard Worker match = true 427*333d2b36SAndroid Build Coastguard Worker } 428*333d2b36SAndroid Build Coastguard Worker } 429*333d2b36SAndroid Build Coastguard Worker if valueType != v.valueType { 430*333d2b36SAndroid Build Coastguard Worker if valueType != starlarkTypeUnknown { 431*333d2b36SAndroid Build Coastguard Worker if v.valueType == starlarkTypeUnknown { 432*333d2b36SAndroid Build Coastguard Worker v.valueType = valueType 433*333d2b36SAndroid Build Coastguard Worker } else { 434*333d2b36SAndroid Build Coastguard Worker match = false 435*333d2b36SAndroid Build Coastguard Worker } 436*333d2b36SAndroid Build Coastguard Worker } 437*333d2b36SAndroid Build Coastguard Worker } 438*333d2b36SAndroid Build Coastguard Worker if !match { 439*333d2b36SAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "cannot redefine %s as %v/%v (already defined as %v/%v)\n", 440*333d2b36SAndroid Build Coastguard Worker name, varClass, valueType, v.class, v.valueType) 441*333d2b36SAndroid Build Coastguard Worker } 442*333d2b36SAndroid Build Coastguard Worker} 443*333d2b36SAndroid Build Coastguard Worker 444*333d2b36SAndroid Build Coastguard Worker// All known product variables. 445*333d2b36SAndroid Build Coastguard Workervar KnownVariables = make(knownVariables) 446*333d2b36SAndroid Build Coastguard Worker 447*333d2b36SAndroid Build Coastguard Workerfunc init() { 448*333d2b36SAndroid Build Coastguard Worker for _, kv := range []string{ 449*333d2b36SAndroid Build Coastguard Worker // Kernel-related variables that we know are lists. 450*333d2b36SAndroid Build Coastguard Worker "BOARD_VENDOR_KERNEL_MODULES", 451*333d2b36SAndroid Build Coastguard Worker "BOARD_VENDOR_RAMDISK_KERNEL_MODULES", 452*333d2b36SAndroid Build Coastguard Worker "BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD", 453*333d2b36SAndroid Build Coastguard Worker "BOARD_RECOVERY_KERNEL_MODULES", 454*333d2b36SAndroid Build Coastguard Worker // Other variables we knwo are lists 455*333d2b36SAndroid Build Coastguard Worker "ART_APEX_JARS", 456*333d2b36SAndroid Build Coastguard Worker } { 457*333d2b36SAndroid Build Coastguard Worker KnownVariables.NewVariable(kv, VarClassSoong, starlarkTypeList) 458*333d2b36SAndroid Build Coastguard Worker } 459*333d2b36SAndroid Build Coastguard Worker} 460*333d2b36SAndroid Build Coastguard Worker 461*333d2b36SAndroid Build Coastguard Worker// Information about the generated Starlark script. 462*333d2b36SAndroid Build Coastguard Workertype StarlarkScript struct { 463*333d2b36SAndroid Build Coastguard Worker mkFile string 464*333d2b36SAndroid Build Coastguard Worker moduleName string 465*333d2b36SAndroid Build Coastguard Worker mkPos scanner.Position 466*333d2b36SAndroid Build Coastguard Worker nodes []starlarkNode 467*333d2b36SAndroid Build Coastguard Worker inherited []*moduleInfo 468*333d2b36SAndroid Build Coastguard Worker hasErrors bool 469*333d2b36SAndroid Build Coastguard Worker traceCalls bool // print enter/exit each init function 470*333d2b36SAndroid Build Coastguard Worker sourceFS fs.FS 471*333d2b36SAndroid Build Coastguard Worker makefileFinder MakefileFinder 472*333d2b36SAndroid Build Coastguard Worker nodeLocator func(pos mkparser.Pos) int 473*333d2b36SAndroid Build Coastguard Worker} 474*333d2b36SAndroid Build Coastguard Worker 475*333d2b36SAndroid Build Coastguard Worker// parseContext holds the script we are generating and all the ephemeral data 476*333d2b36SAndroid Build Coastguard Worker// needed during the parsing. 477*333d2b36SAndroid Build Coastguard Workertype parseContext struct { 478*333d2b36SAndroid Build Coastguard Worker script *StarlarkScript 479*333d2b36SAndroid Build Coastguard Worker nodes []mkparser.Node // Makefile as parsed by mkparser 480*333d2b36SAndroid Build Coastguard Worker currentNodeIndex int // Node in it we are processing 481*333d2b36SAndroid Build Coastguard Worker ifNestLevel int 482*333d2b36SAndroid Build Coastguard Worker moduleNameCount map[string]int // count of imported modules with given basename 483*333d2b36SAndroid Build Coastguard Worker fatalError error 484*333d2b36SAndroid Build Coastguard Worker outputSuffix string 485*333d2b36SAndroid Build Coastguard Worker errorLogger ErrorLogger 486*333d2b36SAndroid Build Coastguard Worker tracedVariables map[string]bool // variables to be traced in the generated script 487*333d2b36SAndroid Build Coastguard Worker variables map[string]variable 488*333d2b36SAndroid Build Coastguard Worker outputDir string 489*333d2b36SAndroid Build Coastguard Worker dependentModules map[string]*moduleInfo 490*333d2b36SAndroid Build Coastguard Worker soongNamespaces map[string]map[string]bool 491*333d2b36SAndroid Build Coastguard Worker includeTops []string 492*333d2b36SAndroid Build Coastguard Worker typeHints map[string]starlarkType 493*333d2b36SAndroid Build Coastguard Worker atTopOfMakefile bool 494*333d2b36SAndroid Build Coastguard Worker} 495*333d2b36SAndroid Build Coastguard Worker 496*333d2b36SAndroid Build Coastguard Workerfunc newParseContext(ss *StarlarkScript, nodes []mkparser.Node) *parseContext { 497*333d2b36SAndroid Build Coastguard Worker predefined := []struct{ name, value string }{ 498*333d2b36SAndroid Build Coastguard Worker {"SRC_TARGET_DIR", filepath.Join("build", "make", "target")}, 499*333d2b36SAndroid Build Coastguard Worker {"LOCAL_PATH", filepath.Dir(ss.mkFile)}, 500*333d2b36SAndroid Build Coastguard Worker {"MAKEFILE_LIST", ss.mkFile}, 501*333d2b36SAndroid Build Coastguard Worker {"TOPDIR", ""}, // TOPDIR is just set to an empty string in cleanbuild.mk and core.mk 502*333d2b36SAndroid Build Coastguard Worker // TODO(asmundak): maybe read it from build/make/core/envsetup.mk? 503*333d2b36SAndroid Build Coastguard Worker {"TARGET_COPY_OUT_SYSTEM", "system"}, 504*333d2b36SAndroid Build Coastguard Worker {"TARGET_COPY_OUT_SYSTEM_OTHER", "system_other"}, 505*333d2b36SAndroid Build Coastguard Worker {"TARGET_COPY_OUT_DATA", "data"}, 506*333d2b36SAndroid Build Coastguard Worker {"TARGET_COPY_OUT_ASAN", filepath.Join("data", "asan")}, 507*333d2b36SAndroid Build Coastguard Worker {"TARGET_COPY_OUT_OEM", "oem"}, 508*333d2b36SAndroid Build Coastguard Worker {"TARGET_COPY_OUT_RAMDISK", "ramdisk"}, 509*333d2b36SAndroid Build Coastguard Worker {"TARGET_COPY_OUT_DEBUG_RAMDISK", "debug_ramdisk"}, 510*333d2b36SAndroid Build Coastguard Worker {"TARGET_COPY_OUT_VENDOR_DEBUG_RAMDISK", "vendor_debug_ramdisk"}, 511*333d2b36SAndroid Build Coastguard Worker {"TARGET_COPY_OUT_TEST_HARNESS_RAMDISK", "test_harness_ramdisk"}, 512*333d2b36SAndroid Build Coastguard Worker {"TARGET_COPY_OUT_ROOT", "root"}, 513*333d2b36SAndroid Build Coastguard Worker {"TARGET_COPY_OUT_RECOVERY", "recovery"}, 514*333d2b36SAndroid Build Coastguard Worker {"TARGET_COPY_OUT_VENDOR_RAMDISK", "vendor_ramdisk"}, 515*333d2b36SAndroid Build Coastguard Worker // TODO(asmundak): to process internal config files, we need the following variables: 516*333d2b36SAndroid Build Coastguard Worker // TARGET_VENDOR 517*333d2b36SAndroid Build Coastguard Worker // target_base_product 518*333d2b36SAndroid Build Coastguard Worker // 519*333d2b36SAndroid Build Coastguard Worker 520*333d2b36SAndroid Build Coastguard Worker // the following utility variables are set in build/make/common/core.mk: 521*333d2b36SAndroid Build Coastguard Worker {"empty", ""}, 522*333d2b36SAndroid Build Coastguard Worker {"space", " "}, 523*333d2b36SAndroid Build Coastguard Worker {"comma", ","}, 524*333d2b36SAndroid Build Coastguard Worker {"newline", "\n"}, 525*333d2b36SAndroid Build Coastguard Worker {"pound", "#"}, 526*333d2b36SAndroid Build Coastguard Worker {"backslash", "\\"}, 527*333d2b36SAndroid Build Coastguard Worker } 528*333d2b36SAndroid Build Coastguard Worker ctx := &parseContext{ 529*333d2b36SAndroid Build Coastguard Worker script: ss, 530*333d2b36SAndroid Build Coastguard Worker nodes: nodes, 531*333d2b36SAndroid Build Coastguard Worker currentNodeIndex: 0, 532*333d2b36SAndroid Build Coastguard Worker ifNestLevel: 0, 533*333d2b36SAndroid Build Coastguard Worker moduleNameCount: make(map[string]int), 534*333d2b36SAndroid Build Coastguard Worker variables: make(map[string]variable), 535*333d2b36SAndroid Build Coastguard Worker dependentModules: make(map[string]*moduleInfo), 536*333d2b36SAndroid Build Coastguard Worker soongNamespaces: make(map[string]map[string]bool), 537*333d2b36SAndroid Build Coastguard Worker includeTops: []string{}, 538*333d2b36SAndroid Build Coastguard Worker typeHints: make(map[string]starlarkType), 539*333d2b36SAndroid Build Coastguard Worker atTopOfMakefile: true, 540*333d2b36SAndroid Build Coastguard Worker } 541*333d2b36SAndroid Build Coastguard Worker for _, item := range predefined { 542*333d2b36SAndroid Build Coastguard Worker ctx.variables[item.name] = &predefinedVariable{ 543*333d2b36SAndroid Build Coastguard Worker baseVariable: baseVariable{nam: item.name, typ: starlarkTypeString}, 544*333d2b36SAndroid Build Coastguard Worker value: &stringLiteralExpr{item.value}, 545*333d2b36SAndroid Build Coastguard Worker } 546*333d2b36SAndroid Build Coastguard Worker } 547*333d2b36SAndroid Build Coastguard Worker 548*333d2b36SAndroid Build Coastguard Worker return ctx 549*333d2b36SAndroid Build Coastguard Worker} 550*333d2b36SAndroid Build Coastguard Worker 551*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) hasNodes() bool { 552*333d2b36SAndroid Build Coastguard Worker return ctx.currentNodeIndex < len(ctx.nodes) 553*333d2b36SAndroid Build Coastguard Worker} 554*333d2b36SAndroid Build Coastguard Worker 555*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) getNode() mkparser.Node { 556*333d2b36SAndroid Build Coastguard Worker if !ctx.hasNodes() { 557*333d2b36SAndroid Build Coastguard Worker return nil 558*333d2b36SAndroid Build Coastguard Worker } 559*333d2b36SAndroid Build Coastguard Worker node := ctx.nodes[ctx.currentNodeIndex] 560*333d2b36SAndroid Build Coastguard Worker ctx.currentNodeIndex++ 561*333d2b36SAndroid Build Coastguard Worker return node 562*333d2b36SAndroid Build Coastguard Worker} 563*333d2b36SAndroid Build Coastguard Worker 564*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) backNode() { 565*333d2b36SAndroid Build Coastguard Worker if ctx.currentNodeIndex <= 0 { 566*333d2b36SAndroid Build Coastguard Worker panic("Cannot back off") 567*333d2b36SAndroid Build Coastguard Worker } 568*333d2b36SAndroid Build Coastguard Worker ctx.currentNodeIndex-- 569*333d2b36SAndroid Build Coastguard Worker} 570*333d2b36SAndroid Build Coastguard Worker 571*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) handleAssignment(a *mkparser.Assignment) []starlarkNode { 572*333d2b36SAndroid Build Coastguard Worker // Handle only simple variables 573*333d2b36SAndroid Build Coastguard Worker if !a.Name.Const() || a.Target != nil { 574*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{ctx.newBadNode(a, "Only simple variables are handled")} 575*333d2b36SAndroid Build Coastguard Worker } 576*333d2b36SAndroid Build Coastguard Worker name := a.Name.Strings[0] 577*333d2b36SAndroid Build Coastguard Worker // The `override` directive 578*333d2b36SAndroid Build Coastguard Worker // override FOO := 579*333d2b36SAndroid Build Coastguard Worker // is parsed as an assignment to a variable named `override FOO`. 580*333d2b36SAndroid Build Coastguard Worker // There are very few places where `override` is used, just flag it. 581*333d2b36SAndroid Build Coastguard Worker if strings.HasPrefix(name, "override ") { 582*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{ctx.newBadNode(a, "cannot handle override directive")} 583*333d2b36SAndroid Build Coastguard Worker } 584*333d2b36SAndroid Build Coastguard Worker if name == ".KATI_READONLY" { 585*333d2b36SAndroid Build Coastguard Worker // Skip assignments to .KATI_READONLY. If it was in the output file, it 586*333d2b36SAndroid Build Coastguard Worker // would be an error because it would be sorted before the definition of 587*333d2b36SAndroid Build Coastguard Worker // the variable it's trying to make readonly. 588*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{} 589*333d2b36SAndroid Build Coastguard Worker } 590*333d2b36SAndroid Build Coastguard Worker 591*333d2b36SAndroid Build Coastguard Worker // Soong configuration 592*333d2b36SAndroid Build Coastguard Worker if strings.HasPrefix(name, soongNsPrefix) { 593*333d2b36SAndroid Build Coastguard Worker return ctx.handleSoongNsAssignment(strings.TrimPrefix(name, soongNsPrefix), a) 594*333d2b36SAndroid Build Coastguard Worker } 595*333d2b36SAndroid Build Coastguard Worker lhs := ctx.addVariable(name) 596*333d2b36SAndroid Build Coastguard Worker if lhs == nil { 597*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{ctx.newBadNode(a, "unknown variable %s", name)} 598*333d2b36SAndroid Build Coastguard Worker } 599*333d2b36SAndroid Build Coastguard Worker _, isTraced := ctx.tracedVariables[lhs.name()] 600*333d2b36SAndroid Build Coastguard Worker asgn := &assignmentNode{lhs: lhs, mkValue: a.Value, isTraced: isTraced, location: ctx.errorLocation(a)} 601*333d2b36SAndroid Build Coastguard Worker if lhs.valueType() == starlarkTypeUnknown { 602*333d2b36SAndroid Build Coastguard Worker // Try to divine variable type from the RHS 603*333d2b36SAndroid Build Coastguard Worker asgn.value = ctx.parseMakeString(a, a.Value) 604*333d2b36SAndroid Build Coastguard Worker inferred_type := asgn.value.typ() 605*333d2b36SAndroid Build Coastguard Worker if inferred_type != starlarkTypeUnknown { 606*333d2b36SAndroid Build Coastguard Worker lhs.setValueType(inferred_type) 607*333d2b36SAndroid Build Coastguard Worker } 608*333d2b36SAndroid Build Coastguard Worker } 609*333d2b36SAndroid Build Coastguard Worker if lhs.valueType() == starlarkTypeList { 610*333d2b36SAndroid Build Coastguard Worker xConcat, xBad := ctx.buildConcatExpr(a) 611*333d2b36SAndroid Build Coastguard Worker if xBad != nil { 612*333d2b36SAndroid Build Coastguard Worker asgn.value = xBad 613*333d2b36SAndroid Build Coastguard Worker } else { 614*333d2b36SAndroid Build Coastguard Worker switch len(xConcat.items) { 615*333d2b36SAndroid Build Coastguard Worker case 0: 616*333d2b36SAndroid Build Coastguard Worker asgn.value = &listExpr{} 617*333d2b36SAndroid Build Coastguard Worker case 1: 618*333d2b36SAndroid Build Coastguard Worker asgn.value = xConcat.items[0] 619*333d2b36SAndroid Build Coastguard Worker default: 620*333d2b36SAndroid Build Coastguard Worker asgn.value = xConcat 621*333d2b36SAndroid Build Coastguard Worker } 622*333d2b36SAndroid Build Coastguard Worker } 623*333d2b36SAndroid Build Coastguard Worker } else { 624*333d2b36SAndroid Build Coastguard Worker asgn.value = ctx.parseMakeString(a, a.Value) 625*333d2b36SAndroid Build Coastguard Worker } 626*333d2b36SAndroid Build Coastguard Worker 627*333d2b36SAndroid Build Coastguard Worker if asgn.lhs.valueType() == starlarkTypeString && 628*333d2b36SAndroid Build Coastguard Worker asgn.value.typ() != starlarkTypeUnknown && 629*333d2b36SAndroid Build Coastguard Worker asgn.value.typ() != starlarkTypeString { 630*333d2b36SAndroid Build Coastguard Worker asgn.value = &toStringExpr{expr: asgn.value} 631*333d2b36SAndroid Build Coastguard Worker } 632*333d2b36SAndroid Build Coastguard Worker 633*333d2b36SAndroid Build Coastguard Worker switch a.Type { 634*333d2b36SAndroid Build Coastguard Worker case "=", ":=": 635*333d2b36SAndroid Build Coastguard Worker asgn.flavor = asgnSet 636*333d2b36SAndroid Build Coastguard Worker case "+=": 637*333d2b36SAndroid Build Coastguard Worker asgn.flavor = asgnAppend 638*333d2b36SAndroid Build Coastguard Worker case "?=": 639*333d2b36SAndroid Build Coastguard Worker if _, ok := lhs.(*productConfigVariable); ok { 640*333d2b36SAndroid Build Coastguard Worker // Make sets all product configuration variables to empty strings before running product 641*333d2b36SAndroid Build Coastguard Worker // config makefiles. ?= will have no effect on a variable that has been assigned before, 642*333d2b36SAndroid Build Coastguard Worker // even if assigned to an empty string. So just skip emitting any code for this 643*333d2b36SAndroid Build Coastguard Worker // assignment. 644*333d2b36SAndroid Build Coastguard Worker return nil 645*333d2b36SAndroid Build Coastguard Worker } 646*333d2b36SAndroid Build Coastguard Worker asgn.flavor = asgnMaybeSet 647*333d2b36SAndroid Build Coastguard Worker default: 648*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("unexpected assignment type %s", a.Type)) 649*333d2b36SAndroid Build Coastguard Worker } 650*333d2b36SAndroid Build Coastguard Worker 651*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{asgn} 652*333d2b36SAndroid Build Coastguard Worker} 653*333d2b36SAndroid Build Coastguard Worker 654*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) handleSoongNsAssignment(name string, asgn *mkparser.Assignment) []starlarkNode { 655*333d2b36SAndroid Build Coastguard Worker val := ctx.parseMakeString(asgn, asgn.Value) 656*333d2b36SAndroid Build Coastguard Worker if xBad, ok := val.(*badExpr); ok { 657*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{&exprNode{expr: xBad}} 658*333d2b36SAndroid Build Coastguard Worker } 659*333d2b36SAndroid Build Coastguard Worker 660*333d2b36SAndroid Build Coastguard Worker // Unfortunately, Soong namespaces can be set up by directly setting corresponding Make 661*333d2b36SAndroid Build Coastguard Worker // variables instead of via add_soong_config_namespace + add_soong_config_var_value. 662*333d2b36SAndroid Build Coastguard Worker // Try to divine the call from the assignment as follows: 663*333d2b36SAndroid Build Coastguard Worker if name == "NAMESPACES" { 664*333d2b36SAndroid Build Coastguard Worker // Upon seeng 665*333d2b36SAndroid Build Coastguard Worker // SOONG_CONFIG_NAMESPACES += foo 666*333d2b36SAndroid Build Coastguard Worker // remember that there is a namespace `foo` and act as we saw 667*333d2b36SAndroid Build Coastguard Worker // $(call add_soong_config_namespace,foo) 668*333d2b36SAndroid Build Coastguard Worker s, ok := maybeString(val) 669*333d2b36SAndroid Build Coastguard Worker if !ok { 670*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{ctx.newBadNode(asgn, "cannot handle variables in SOONG_CONFIG_NAMESPACES assignment, please use add_soong_config_namespace instead")} 671*333d2b36SAndroid Build Coastguard Worker } 672*333d2b36SAndroid Build Coastguard Worker result := make([]starlarkNode, 0) 673*333d2b36SAndroid Build Coastguard Worker for _, ns := range strings.Fields(s) { 674*333d2b36SAndroid Build Coastguard Worker ctx.addSoongNamespace(ns) 675*333d2b36SAndroid Build Coastguard Worker result = append(result, &exprNode{&callExpr{ 676*333d2b36SAndroid Build Coastguard Worker name: baseName + ".soong_config_namespace", 677*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{&globalsExpr{}, &stringLiteralExpr{ns}}, 678*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeVoid, 679*333d2b36SAndroid Build Coastguard Worker }}) 680*333d2b36SAndroid Build Coastguard Worker } 681*333d2b36SAndroid Build Coastguard Worker return result 682*333d2b36SAndroid Build Coastguard Worker } else { 683*333d2b36SAndroid Build Coastguard Worker // Upon seeing 684*333d2b36SAndroid Build Coastguard Worker // SOONG_CONFIG_x_y = v 685*333d2b36SAndroid Build Coastguard Worker // find a namespace called `x` and act as if we encountered 686*333d2b36SAndroid Build Coastguard Worker // $(call soong_config_set,x,y,v) 687*333d2b36SAndroid Build Coastguard Worker // or check that `x_y` is a namespace, and then add the RHS of this assignment as variables in 688*333d2b36SAndroid Build Coastguard Worker // it. 689*333d2b36SAndroid Build Coastguard Worker // Emit an error in the ambiguous situation (namespaces `foo_bar` with a variable `baz` 690*333d2b36SAndroid Build Coastguard Worker // and `foo` with a variable `bar_baz`. 691*333d2b36SAndroid Build Coastguard Worker namespaceName := "" 692*333d2b36SAndroid Build Coastguard Worker if ctx.hasSoongNamespace(name) { 693*333d2b36SAndroid Build Coastguard Worker namespaceName = name 694*333d2b36SAndroid Build Coastguard Worker } 695*333d2b36SAndroid Build Coastguard Worker var varName string 696*333d2b36SAndroid Build Coastguard Worker for pos, ch := range name { 697*333d2b36SAndroid Build Coastguard Worker if !(ch == '_' && ctx.hasSoongNamespace(name[0:pos])) { 698*333d2b36SAndroid Build Coastguard Worker continue 699*333d2b36SAndroid Build Coastguard Worker } 700*333d2b36SAndroid Build Coastguard Worker if namespaceName != "" { 701*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{ctx.newBadNode(asgn, "ambiguous soong namespace (may be either `%s` or `%s`)", namespaceName, name[0:pos])} 702*333d2b36SAndroid Build Coastguard Worker } 703*333d2b36SAndroid Build Coastguard Worker namespaceName = name[0:pos] 704*333d2b36SAndroid Build Coastguard Worker varName = name[pos+1:] 705*333d2b36SAndroid Build Coastguard Worker } 706*333d2b36SAndroid Build Coastguard Worker if namespaceName == "" { 707*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{ctx.newBadNode(asgn, "cannot figure out Soong namespace, please use add_soong_config_var_value macro instead")} 708*333d2b36SAndroid Build Coastguard Worker } 709*333d2b36SAndroid Build Coastguard Worker if varName == "" { 710*333d2b36SAndroid Build Coastguard Worker // Remember variables in this namespace 711*333d2b36SAndroid Build Coastguard Worker s, ok := maybeString(val) 712*333d2b36SAndroid Build Coastguard Worker if !ok { 713*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{ctx.newBadNode(asgn, "cannot handle variables in SOONG_CONFIG_ assignment, please use add_soong_config_var_value instead")} 714*333d2b36SAndroid Build Coastguard Worker } 715*333d2b36SAndroid Build Coastguard Worker ctx.updateSoongNamespace(asgn.Type != "+=", namespaceName, strings.Fields(s)) 716*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{} 717*333d2b36SAndroid Build Coastguard Worker } 718*333d2b36SAndroid Build Coastguard Worker 719*333d2b36SAndroid Build Coastguard Worker // Finally, handle assignment to a namespace variable 720*333d2b36SAndroid Build Coastguard Worker if !ctx.hasNamespaceVar(namespaceName, varName) { 721*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{ctx.newBadNode(asgn, "no %s variable in %s namespace, please use add_soong_config_var_value instead", varName, namespaceName)} 722*333d2b36SAndroid Build Coastguard Worker } 723*333d2b36SAndroid Build Coastguard Worker fname := baseName + "." + soongConfigAssign 724*333d2b36SAndroid Build Coastguard Worker if asgn.Type == "+=" { 725*333d2b36SAndroid Build Coastguard Worker fname = baseName + "." + soongConfigAppend 726*333d2b36SAndroid Build Coastguard Worker } 727*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{&exprNode{&callExpr{ 728*333d2b36SAndroid Build Coastguard Worker name: fname, 729*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{&globalsExpr{}, &stringLiteralExpr{namespaceName}, &stringLiteralExpr{varName}, val}, 730*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeVoid, 731*333d2b36SAndroid Build Coastguard Worker }}} 732*333d2b36SAndroid Build Coastguard Worker } 733*333d2b36SAndroid Build Coastguard Worker} 734*333d2b36SAndroid Build Coastguard Worker 735*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) buildConcatExpr(a *mkparser.Assignment) (*concatExpr, *badExpr) { 736*333d2b36SAndroid Build Coastguard Worker xConcat := &concatExpr{} 737*333d2b36SAndroid Build Coastguard Worker var xItemList *listExpr 738*333d2b36SAndroid Build Coastguard Worker addToItemList := func(x ...starlarkExpr) { 739*333d2b36SAndroid Build Coastguard Worker if xItemList == nil { 740*333d2b36SAndroid Build Coastguard Worker xItemList = &listExpr{[]starlarkExpr{}} 741*333d2b36SAndroid Build Coastguard Worker } 742*333d2b36SAndroid Build Coastguard Worker xItemList.items = append(xItemList.items, x...) 743*333d2b36SAndroid Build Coastguard Worker } 744*333d2b36SAndroid Build Coastguard Worker finishItemList := func() { 745*333d2b36SAndroid Build Coastguard Worker if xItemList != nil { 746*333d2b36SAndroid Build Coastguard Worker xConcat.items = append(xConcat.items, xItemList) 747*333d2b36SAndroid Build Coastguard Worker xItemList = nil 748*333d2b36SAndroid Build Coastguard Worker } 749*333d2b36SAndroid Build Coastguard Worker } 750*333d2b36SAndroid Build Coastguard Worker 751*333d2b36SAndroid Build Coastguard Worker items := a.Value.Words() 752*333d2b36SAndroid Build Coastguard Worker for _, item := range items { 753*333d2b36SAndroid Build Coastguard Worker // A function call in RHS is supposed to return a list, all other item 754*333d2b36SAndroid Build Coastguard Worker // expressions return individual elements. 755*333d2b36SAndroid Build Coastguard Worker switch x := ctx.parseMakeString(a, item).(type) { 756*333d2b36SAndroid Build Coastguard Worker case *badExpr: 757*333d2b36SAndroid Build Coastguard Worker return nil, x 758*333d2b36SAndroid Build Coastguard Worker case *stringLiteralExpr: 759*333d2b36SAndroid Build Coastguard Worker addToItemList(maybeConvertToStringList(x).(*listExpr).items...) 760*333d2b36SAndroid Build Coastguard Worker default: 761*333d2b36SAndroid Build Coastguard Worker switch x.typ() { 762*333d2b36SAndroid Build Coastguard Worker case starlarkTypeList: 763*333d2b36SAndroid Build Coastguard Worker finishItemList() 764*333d2b36SAndroid Build Coastguard Worker xConcat.items = append(xConcat.items, x) 765*333d2b36SAndroid Build Coastguard Worker case starlarkTypeString: 766*333d2b36SAndroid Build Coastguard Worker finishItemList() 767*333d2b36SAndroid Build Coastguard Worker xConcat.items = append(xConcat.items, &callExpr{ 768*333d2b36SAndroid Build Coastguard Worker object: x, 769*333d2b36SAndroid Build Coastguard Worker name: "split", 770*333d2b36SAndroid Build Coastguard Worker args: nil, 771*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeList, 772*333d2b36SAndroid Build Coastguard Worker }) 773*333d2b36SAndroid Build Coastguard Worker default: 774*333d2b36SAndroid Build Coastguard Worker addToItemList(x) 775*333d2b36SAndroid Build Coastguard Worker } 776*333d2b36SAndroid Build Coastguard Worker } 777*333d2b36SAndroid Build Coastguard Worker } 778*333d2b36SAndroid Build Coastguard Worker if xItemList != nil { 779*333d2b36SAndroid Build Coastguard Worker xConcat.items = append(xConcat.items, xItemList) 780*333d2b36SAndroid Build Coastguard Worker } 781*333d2b36SAndroid Build Coastguard Worker return xConcat, nil 782*333d2b36SAndroid Build Coastguard Worker} 783*333d2b36SAndroid Build Coastguard Worker 784*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) newDependentModule(path string, optional bool) *moduleInfo { 785*333d2b36SAndroid Build Coastguard Worker modulePath := ctx.loadedModulePath(path) 786*333d2b36SAndroid Build Coastguard Worker if mi, ok := ctx.dependentModules[modulePath]; ok { 787*333d2b36SAndroid Build Coastguard Worker mi.optional = mi.optional && optional 788*333d2b36SAndroid Build Coastguard Worker return mi 789*333d2b36SAndroid Build Coastguard Worker } 790*333d2b36SAndroid Build Coastguard Worker moduleName := moduleNameForFile(path) 791*333d2b36SAndroid Build Coastguard Worker moduleLocalName := "_" + moduleName 792*333d2b36SAndroid Build Coastguard Worker n, found := ctx.moduleNameCount[moduleName] 793*333d2b36SAndroid Build Coastguard Worker if found { 794*333d2b36SAndroid Build Coastguard Worker moduleLocalName += fmt.Sprintf("%d", n) 795*333d2b36SAndroid Build Coastguard Worker } 796*333d2b36SAndroid Build Coastguard Worker ctx.moduleNameCount[moduleName] = n + 1 797*333d2b36SAndroid Build Coastguard Worker _, err := fs.Stat(ctx.script.sourceFS, path) 798*333d2b36SAndroid Build Coastguard Worker mi := &moduleInfo{ 799*333d2b36SAndroid Build Coastguard Worker path: modulePath, 800*333d2b36SAndroid Build Coastguard Worker originalPath: path, 801*333d2b36SAndroid Build Coastguard Worker moduleLocalName: moduleLocalName, 802*333d2b36SAndroid Build Coastguard Worker optional: optional, 803*333d2b36SAndroid Build Coastguard Worker missing: err != nil, 804*333d2b36SAndroid Build Coastguard Worker } 805*333d2b36SAndroid Build Coastguard Worker ctx.dependentModules[modulePath] = mi 806*333d2b36SAndroid Build Coastguard Worker ctx.script.inherited = append(ctx.script.inherited, mi) 807*333d2b36SAndroid Build Coastguard Worker return mi 808*333d2b36SAndroid Build Coastguard Worker} 809*333d2b36SAndroid Build Coastguard Worker 810*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) handleSubConfig( 811*333d2b36SAndroid Build Coastguard Worker v mkparser.Node, pathExpr starlarkExpr, loadAlways bool, processModule func(inheritedModule) starlarkNode) []starlarkNode { 812*333d2b36SAndroid Build Coastguard Worker 813*333d2b36SAndroid Build Coastguard Worker // Allow seeing $(sort $(wildcard realPathExpr)) or $(wildcard realPathExpr) 814*333d2b36SAndroid Build Coastguard Worker // because those are functionally the same as not having the sort/wildcard calls. 815*333d2b36SAndroid Build Coastguard Worker if ce, ok := pathExpr.(*callExpr); ok && ce.name == "rblf.mksort" && len(ce.args) == 1 { 816*333d2b36SAndroid Build Coastguard Worker if ce2, ok2 := ce.args[0].(*callExpr); ok2 && ce2.name == "rblf.expand_wildcard" && len(ce2.args) == 1 { 817*333d2b36SAndroid Build Coastguard Worker pathExpr = ce2.args[0] 818*333d2b36SAndroid Build Coastguard Worker } 819*333d2b36SAndroid Build Coastguard Worker } else if ce2, ok2 := pathExpr.(*callExpr); ok2 && ce2.name == "rblf.expand_wildcard" && len(ce2.args) == 1 { 820*333d2b36SAndroid Build Coastguard Worker pathExpr = ce2.args[0] 821*333d2b36SAndroid Build Coastguard Worker } 822*333d2b36SAndroid Build Coastguard Worker 823*333d2b36SAndroid Build Coastguard Worker // In a simple case, the name of a module to inherit/include is known statically. 824*333d2b36SAndroid Build Coastguard Worker if path, ok := maybeString(pathExpr); ok { 825*333d2b36SAndroid Build Coastguard Worker // Note that even if this directive loads a module unconditionally, a module may be 826*333d2b36SAndroid Build Coastguard Worker // absent without causing any harm if this directive is inside an if/else block. 827*333d2b36SAndroid Build Coastguard Worker moduleShouldExist := loadAlways && ctx.ifNestLevel == 0 828*333d2b36SAndroid Build Coastguard Worker if strings.Contains(path, "*") { 829*333d2b36SAndroid Build Coastguard Worker if paths, err := fs.Glob(ctx.script.sourceFS, path); err == nil { 830*333d2b36SAndroid Build Coastguard Worker sort.Strings(paths) 831*333d2b36SAndroid Build Coastguard Worker result := make([]starlarkNode, 0) 832*333d2b36SAndroid Build Coastguard Worker for _, p := range paths { 833*333d2b36SAndroid Build Coastguard Worker mi := ctx.newDependentModule(p, !moduleShouldExist) 834*333d2b36SAndroid Build Coastguard Worker result = append(result, processModule(inheritedStaticModule{mi, loadAlways})) 835*333d2b36SAndroid Build Coastguard Worker } 836*333d2b36SAndroid Build Coastguard Worker return result 837*333d2b36SAndroid Build Coastguard Worker } else { 838*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{ctx.newBadNode(v, "cannot glob wildcard argument")} 839*333d2b36SAndroid Build Coastguard Worker } 840*333d2b36SAndroid Build Coastguard Worker } else { 841*333d2b36SAndroid Build Coastguard Worker mi := ctx.newDependentModule(path, !moduleShouldExist) 842*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{processModule(inheritedStaticModule{mi, loadAlways})} 843*333d2b36SAndroid Build Coastguard Worker } 844*333d2b36SAndroid Build Coastguard Worker } 845*333d2b36SAndroid Build Coastguard Worker 846*333d2b36SAndroid Build Coastguard Worker // If module path references variables (e.g., $(v1)/foo/$(v2)/device-config.mk), find all the paths in the 847*333d2b36SAndroid Build Coastguard Worker // source tree that may be a match and the corresponding variable values. For instance, if the source tree 848*333d2b36SAndroid Build Coastguard Worker // contains vendor1/foo/abc/dev.mk and vendor2/foo/def/dev.mk, the first one will be inherited when 849*333d2b36SAndroid Build Coastguard Worker // (v1, v2) == ('vendor1', 'abc'), and the second one when (v1, v2) == ('vendor2', 'def'). 850*333d2b36SAndroid Build Coastguard Worker // We then emit the code that loads all of them, e.g.: 851*333d2b36SAndroid Build Coastguard Worker // load("//vendor1/foo/abc:dev.rbc", _dev1_init="init") 852*333d2b36SAndroid Build Coastguard Worker // load("//vendor2/foo/def/dev.rbc", _dev2_init="init") 853*333d2b36SAndroid Build Coastguard Worker // And then inherit it as follows: 854*333d2b36SAndroid Build Coastguard Worker // _e = { 855*333d2b36SAndroid Build Coastguard Worker // "vendor1/foo/abc/dev.mk": ("vendor1/foo/abc/dev", _dev1_init), 856*333d2b36SAndroid Build Coastguard Worker // "vendor2/foo/def/dev.mk": ("vendor2/foo/def/dev", _dev_init2) }.get("%s/foo/%s/dev.mk" % (v1, v2)) 857*333d2b36SAndroid Build Coastguard Worker // if _e: 858*333d2b36SAndroid Build Coastguard Worker // rblf.inherit(handle, _e[0], _e[1]) 859*333d2b36SAndroid Build Coastguard Worker // 860*333d2b36SAndroid Build Coastguard Worker var matchingPaths []string 861*333d2b36SAndroid Build Coastguard Worker var needsWarning = false 862*333d2b36SAndroid Build Coastguard Worker if interpolate, ok := pathExpr.(*interpolateExpr); ok { 863*333d2b36SAndroid Build Coastguard Worker pathPattern := []string{interpolate.chunks[0]} 864*333d2b36SAndroid Build Coastguard Worker for _, chunk := range interpolate.chunks[1:] { 865*333d2b36SAndroid Build Coastguard Worker if chunk != "" { 866*333d2b36SAndroid Build Coastguard Worker pathPattern = append(pathPattern, chunk) 867*333d2b36SAndroid Build Coastguard Worker } 868*333d2b36SAndroid Build Coastguard Worker } 869*333d2b36SAndroid Build Coastguard Worker if len(pathPattern) == 1 { 870*333d2b36SAndroid Build Coastguard Worker pathPattern = append(pathPattern, "") 871*333d2b36SAndroid Build Coastguard Worker } 872*333d2b36SAndroid Build Coastguard Worker matchingPaths = ctx.findMatchingPaths(pathPattern) 873*333d2b36SAndroid Build Coastguard Worker needsWarning = pathPattern[0] == "" && len(ctx.includeTops) == 0 874*333d2b36SAndroid Build Coastguard Worker } else if len(ctx.includeTops) > 0 { 875*333d2b36SAndroid Build Coastguard Worker matchingPaths = append(matchingPaths, ctx.findMatchingPaths([]string{"", ""})...) 876*333d2b36SAndroid Build Coastguard Worker } else { 877*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{ctx.newBadNode(v, "inherit-product/include argument is too complex")} 878*333d2b36SAndroid Build Coastguard Worker } 879*333d2b36SAndroid Build Coastguard Worker 880*333d2b36SAndroid Build Coastguard Worker // Safeguard against $(call inherit-product,$(PRODUCT_PATH)) 881*333d2b36SAndroid Build Coastguard Worker const maxMatchingFiles = 150 882*333d2b36SAndroid Build Coastguard Worker if len(matchingPaths) > maxMatchingFiles { 883*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{ctx.newBadNode(v, "there are >%d files matching the pattern, please rewrite it", maxMatchingFiles)} 884*333d2b36SAndroid Build Coastguard Worker } 885*333d2b36SAndroid Build Coastguard Worker 886*333d2b36SAndroid Build Coastguard Worker res := inheritedDynamicModule{pathExpr, []*moduleInfo{}, loadAlways, ctx.errorLocation(v), needsWarning} 887*333d2b36SAndroid Build Coastguard Worker for _, p := range matchingPaths { 888*333d2b36SAndroid Build Coastguard Worker // A product configuration files discovered dynamically may attempt to inherit 889*333d2b36SAndroid Build Coastguard Worker // from another one which does not exist in this source tree. Prevent load errors 890*333d2b36SAndroid Build Coastguard Worker // by always loading the dynamic files as optional. 891*333d2b36SAndroid Build Coastguard Worker res.candidateModules = append(res.candidateModules, ctx.newDependentModule(p, true)) 892*333d2b36SAndroid Build Coastguard Worker } 893*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{processModule(res)} 894*333d2b36SAndroid Build Coastguard Worker} 895*333d2b36SAndroid Build Coastguard Worker 896*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) findMatchingPaths(pattern []string) []string { 897*333d2b36SAndroid Build Coastguard Worker files := ctx.script.makefileFinder.Find(".") 898*333d2b36SAndroid Build Coastguard Worker if len(pattern) == 0 { 899*333d2b36SAndroid Build Coastguard Worker return files 900*333d2b36SAndroid Build Coastguard Worker } 901*333d2b36SAndroid Build Coastguard Worker 902*333d2b36SAndroid Build Coastguard Worker // Create regular expression from the pattern 903*333d2b36SAndroid Build Coastguard Worker regexString := "^" + regexp.QuoteMeta(pattern[0]) 904*333d2b36SAndroid Build Coastguard Worker for _, s := range pattern[1:] { 905*333d2b36SAndroid Build Coastguard Worker regexString += ".*" + regexp.QuoteMeta(s) 906*333d2b36SAndroid Build Coastguard Worker } 907*333d2b36SAndroid Build Coastguard Worker regexString += "$" 908*333d2b36SAndroid Build Coastguard Worker rex := regexp.MustCompile(regexString) 909*333d2b36SAndroid Build Coastguard Worker 910*333d2b36SAndroid Build Coastguard Worker includeTopRegexString := "" 911*333d2b36SAndroid Build Coastguard Worker if len(ctx.includeTops) > 0 { 912*333d2b36SAndroid Build Coastguard Worker for i, top := range ctx.includeTops { 913*333d2b36SAndroid Build Coastguard Worker if i > 0 { 914*333d2b36SAndroid Build Coastguard Worker includeTopRegexString += "|" 915*333d2b36SAndroid Build Coastguard Worker } 916*333d2b36SAndroid Build Coastguard Worker includeTopRegexString += "^" + regexp.QuoteMeta(top) 917*333d2b36SAndroid Build Coastguard Worker } 918*333d2b36SAndroid Build Coastguard Worker } else { 919*333d2b36SAndroid Build Coastguard Worker includeTopRegexString = ".*" 920*333d2b36SAndroid Build Coastguard Worker } 921*333d2b36SAndroid Build Coastguard Worker 922*333d2b36SAndroid Build Coastguard Worker includeTopRegex := regexp.MustCompile(includeTopRegexString) 923*333d2b36SAndroid Build Coastguard Worker 924*333d2b36SAndroid Build Coastguard Worker // Now match 925*333d2b36SAndroid Build Coastguard Worker var res []string 926*333d2b36SAndroid Build Coastguard Worker for _, p := range files { 927*333d2b36SAndroid Build Coastguard Worker if rex.MatchString(p) && includeTopRegex.MatchString(p) { 928*333d2b36SAndroid Build Coastguard Worker res = append(res, p) 929*333d2b36SAndroid Build Coastguard Worker } 930*333d2b36SAndroid Build Coastguard Worker } 931*333d2b36SAndroid Build Coastguard Worker return res 932*333d2b36SAndroid Build Coastguard Worker} 933*333d2b36SAndroid Build Coastguard Worker 934*333d2b36SAndroid Build Coastguard Workertype inheritProductCallParser struct { 935*333d2b36SAndroid Build Coastguard Worker loadAlways bool 936*333d2b36SAndroid Build Coastguard Worker} 937*333d2b36SAndroid Build Coastguard Worker 938*333d2b36SAndroid Build Coastguard Workerfunc (p *inheritProductCallParser) parse(ctx *parseContext, v mkparser.Node, args *mkparser.MakeString) []starlarkNode { 939*333d2b36SAndroid Build Coastguard Worker args.TrimLeftSpaces() 940*333d2b36SAndroid Build Coastguard Worker args.TrimRightSpaces() 941*333d2b36SAndroid Build Coastguard Worker pathExpr := ctx.parseMakeString(v, args) 942*333d2b36SAndroid Build Coastguard Worker if _, ok := pathExpr.(*badExpr); ok { 943*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{ctx.newBadNode(v, "Unable to parse argument to inherit")} 944*333d2b36SAndroid Build Coastguard Worker } 945*333d2b36SAndroid Build Coastguard Worker return ctx.handleSubConfig(v, pathExpr, p.loadAlways, func(im inheritedModule) starlarkNode { 946*333d2b36SAndroid Build Coastguard Worker return &inheritNode{im, p.loadAlways} 947*333d2b36SAndroid Build Coastguard Worker }) 948*333d2b36SAndroid Build Coastguard Worker} 949*333d2b36SAndroid Build Coastguard Worker 950*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) handleInclude(v *mkparser.Directive) []starlarkNode { 951*333d2b36SAndroid Build Coastguard Worker loadAlways := v.Name[0] != '-' 952*333d2b36SAndroid Build Coastguard Worker v.Args.TrimRightSpaces() 953*333d2b36SAndroid Build Coastguard Worker v.Args.TrimLeftSpaces() 954*333d2b36SAndroid Build Coastguard Worker return ctx.handleSubConfig(v, ctx.parseMakeString(v, v.Args), loadAlways, func(im inheritedModule) starlarkNode { 955*333d2b36SAndroid Build Coastguard Worker return &includeNode{im, loadAlways} 956*333d2b36SAndroid Build Coastguard Worker }) 957*333d2b36SAndroid Build Coastguard Worker} 958*333d2b36SAndroid Build Coastguard Worker 959*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) handleVariable(v *mkparser.Variable) []starlarkNode { 960*333d2b36SAndroid Build Coastguard Worker // Handle: 961*333d2b36SAndroid Build Coastguard Worker // $(call inherit-product,...) 962*333d2b36SAndroid Build Coastguard Worker // $(call inherit-product-if-exists,...) 963*333d2b36SAndroid Build Coastguard Worker // $(info xxx) 964*333d2b36SAndroid Build Coastguard Worker // $(warning xxx) 965*333d2b36SAndroid Build Coastguard Worker // $(error xxx) 966*333d2b36SAndroid Build Coastguard Worker // $(call other-custom-functions,...) 967*333d2b36SAndroid Build Coastguard Worker 968*333d2b36SAndroid Build Coastguard Worker if name, args, ok := ctx.maybeParseFunctionCall(v, v.Name); ok { 969*333d2b36SAndroid Build Coastguard Worker if kf, ok := knownNodeFunctions[name]; ok { 970*333d2b36SAndroid Build Coastguard Worker return kf.parse(ctx, v, args) 971*333d2b36SAndroid Build Coastguard Worker } 972*333d2b36SAndroid Build Coastguard Worker } 973*333d2b36SAndroid Build Coastguard Worker 974*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{&exprNode{expr: ctx.parseReference(v, v.Name)}} 975*333d2b36SAndroid Build Coastguard Worker} 976*333d2b36SAndroid Build Coastguard Worker 977*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) maybeHandleDefine(directive *mkparser.Directive) starlarkNode { 978*333d2b36SAndroid Build Coastguard Worker macro_name := strings.Fields(directive.Args.Strings[0])[0] 979*333d2b36SAndroid Build Coastguard Worker // Ignore the macros that we handle 980*333d2b36SAndroid Build Coastguard Worker _, ignored := ignoredDefines[macro_name] 981*333d2b36SAndroid Build Coastguard Worker _, known := knownFunctions[macro_name] 982*333d2b36SAndroid Build Coastguard Worker if !ignored && !known { 983*333d2b36SAndroid Build Coastguard Worker return ctx.newBadNode(directive, "define is not supported: %s", macro_name) 984*333d2b36SAndroid Build Coastguard Worker } 985*333d2b36SAndroid Build Coastguard Worker return nil 986*333d2b36SAndroid Build Coastguard Worker} 987*333d2b36SAndroid Build Coastguard Worker 988*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) handleIfBlock(ifDirective *mkparser.Directive) starlarkNode { 989*333d2b36SAndroid Build Coastguard Worker ssSwitch := &switchNode{ 990*333d2b36SAndroid Build Coastguard Worker ssCases: []*switchCase{ctx.processBranch(ifDirective)}, 991*333d2b36SAndroid Build Coastguard Worker } 992*333d2b36SAndroid Build Coastguard Worker for ctx.hasNodes() && ctx.fatalError == nil { 993*333d2b36SAndroid Build Coastguard Worker node := ctx.getNode() 994*333d2b36SAndroid Build Coastguard Worker switch x := node.(type) { 995*333d2b36SAndroid Build Coastguard Worker case *mkparser.Directive: 996*333d2b36SAndroid Build Coastguard Worker switch x.Name { 997*333d2b36SAndroid Build Coastguard Worker case "else", "elifdef", "elifndef", "elifeq", "elifneq": 998*333d2b36SAndroid Build Coastguard Worker ssSwitch.ssCases = append(ssSwitch.ssCases, ctx.processBranch(x)) 999*333d2b36SAndroid Build Coastguard Worker case "endif": 1000*333d2b36SAndroid Build Coastguard Worker return ssSwitch 1001*333d2b36SAndroid Build Coastguard Worker default: 1002*333d2b36SAndroid Build Coastguard Worker return ctx.newBadNode(node, "unexpected directive %s", x.Name) 1003*333d2b36SAndroid Build Coastguard Worker } 1004*333d2b36SAndroid Build Coastguard Worker default: 1005*333d2b36SAndroid Build Coastguard Worker return ctx.newBadNode(ifDirective, "unexpected statement") 1006*333d2b36SAndroid Build Coastguard Worker } 1007*333d2b36SAndroid Build Coastguard Worker } 1008*333d2b36SAndroid Build Coastguard Worker if ctx.fatalError == nil { 1009*333d2b36SAndroid Build Coastguard Worker ctx.fatalError = fmt.Errorf("no matching endif for %s", ifDirective.Dump()) 1010*333d2b36SAndroid Build Coastguard Worker } 1011*333d2b36SAndroid Build Coastguard Worker return ctx.newBadNode(ifDirective, "no matching endif for %s", ifDirective.Dump()) 1012*333d2b36SAndroid Build Coastguard Worker} 1013*333d2b36SAndroid Build Coastguard Worker 1014*333d2b36SAndroid Build Coastguard Worker// processBranch processes a single branch (if/elseif/else) until the next directive 1015*333d2b36SAndroid Build Coastguard Worker// on the same level. 1016*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) processBranch(check *mkparser.Directive) *switchCase { 1017*333d2b36SAndroid Build Coastguard Worker block := &switchCase{gate: ctx.parseCondition(check)} 1018*333d2b36SAndroid Build Coastguard Worker defer func() { 1019*333d2b36SAndroid Build Coastguard Worker ctx.ifNestLevel-- 1020*333d2b36SAndroid Build Coastguard Worker }() 1021*333d2b36SAndroid Build Coastguard Worker ctx.ifNestLevel++ 1022*333d2b36SAndroid Build Coastguard Worker 1023*333d2b36SAndroid Build Coastguard Worker for ctx.hasNodes() { 1024*333d2b36SAndroid Build Coastguard Worker node := ctx.getNode() 1025*333d2b36SAndroid Build Coastguard Worker if d, ok := node.(*mkparser.Directive); ok { 1026*333d2b36SAndroid Build Coastguard Worker switch d.Name { 1027*333d2b36SAndroid Build Coastguard Worker case "else", "elifdef", "elifndef", "elifeq", "elifneq", "endif": 1028*333d2b36SAndroid Build Coastguard Worker ctx.backNode() 1029*333d2b36SAndroid Build Coastguard Worker return block 1030*333d2b36SAndroid Build Coastguard Worker } 1031*333d2b36SAndroid Build Coastguard Worker } 1032*333d2b36SAndroid Build Coastguard Worker block.nodes = append(block.nodes, ctx.handleSimpleStatement(node)...) 1033*333d2b36SAndroid Build Coastguard Worker } 1034*333d2b36SAndroid Build Coastguard Worker ctx.fatalError = fmt.Errorf("no matching endif for %s", check.Dump()) 1035*333d2b36SAndroid Build Coastguard Worker return block 1036*333d2b36SAndroid Build Coastguard Worker} 1037*333d2b36SAndroid Build Coastguard Worker 1038*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) parseCondition(check *mkparser.Directive) starlarkNode { 1039*333d2b36SAndroid Build Coastguard Worker switch check.Name { 1040*333d2b36SAndroid Build Coastguard Worker case "ifdef", "ifndef", "elifdef", "elifndef": 1041*333d2b36SAndroid Build Coastguard Worker if !check.Args.Const() { 1042*333d2b36SAndroid Build Coastguard Worker return ctx.newBadNode(check, "ifdef variable ref too complex: %s", check.Args.Dump()) 1043*333d2b36SAndroid Build Coastguard Worker } 1044*333d2b36SAndroid Build Coastguard Worker v := NewVariableRefExpr(ctx.addVariable(check.Args.Strings[0])) 1045*333d2b36SAndroid Build Coastguard Worker if strings.HasSuffix(check.Name, "ndef") { 1046*333d2b36SAndroid Build Coastguard Worker v = ¬Expr{v} 1047*333d2b36SAndroid Build Coastguard Worker } 1048*333d2b36SAndroid Build Coastguard Worker return &ifNode{ 1049*333d2b36SAndroid Build Coastguard Worker isElif: strings.HasPrefix(check.Name, "elif"), 1050*333d2b36SAndroid Build Coastguard Worker expr: v, 1051*333d2b36SAndroid Build Coastguard Worker } 1052*333d2b36SAndroid Build Coastguard Worker case "ifeq", "ifneq", "elifeq", "elifneq": 1053*333d2b36SAndroid Build Coastguard Worker return &ifNode{ 1054*333d2b36SAndroid Build Coastguard Worker isElif: strings.HasPrefix(check.Name, "elif"), 1055*333d2b36SAndroid Build Coastguard Worker expr: ctx.parseCompare(check), 1056*333d2b36SAndroid Build Coastguard Worker } 1057*333d2b36SAndroid Build Coastguard Worker case "else": 1058*333d2b36SAndroid Build Coastguard Worker return &elseNode{} 1059*333d2b36SAndroid Build Coastguard Worker default: 1060*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("%s: unknown directive: %s", ctx.script.mkFile, check.Dump())) 1061*333d2b36SAndroid Build Coastguard Worker } 1062*333d2b36SAndroid Build Coastguard Worker} 1063*333d2b36SAndroid Build Coastguard Worker 1064*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) newBadExpr(node mkparser.Node, text string, args ...interface{}) starlarkExpr { 1065*333d2b36SAndroid Build Coastguard Worker if ctx.errorLogger != nil { 1066*333d2b36SAndroid Build Coastguard Worker ctx.errorLogger.NewError(ctx.errorLocation(node), node, text, args...) 1067*333d2b36SAndroid Build Coastguard Worker } 1068*333d2b36SAndroid Build Coastguard Worker ctx.script.hasErrors = true 1069*333d2b36SAndroid Build Coastguard Worker return &badExpr{errorLocation: ctx.errorLocation(node), message: fmt.Sprintf(text, args...)} 1070*333d2b36SAndroid Build Coastguard Worker} 1071*333d2b36SAndroid Build Coastguard Worker 1072*333d2b36SAndroid Build Coastguard Worker// records that the given node failed to be converted and includes an explanatory message 1073*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) newBadNode(failedNode mkparser.Node, message string, args ...interface{}) starlarkNode { 1074*333d2b36SAndroid Build Coastguard Worker return &exprNode{ctx.newBadExpr(failedNode, message, args...)} 1075*333d2b36SAndroid Build Coastguard Worker} 1076*333d2b36SAndroid Build Coastguard Worker 1077*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) parseCompare(cond *mkparser.Directive) starlarkExpr { 1078*333d2b36SAndroid Build Coastguard Worker // Strip outer parentheses 1079*333d2b36SAndroid Build Coastguard Worker mkArg := cloneMakeString(cond.Args) 1080*333d2b36SAndroid Build Coastguard Worker mkArg.Strings[0] = strings.TrimLeft(mkArg.Strings[0], "( ") 1081*333d2b36SAndroid Build Coastguard Worker n := len(mkArg.Strings) 1082*333d2b36SAndroid Build Coastguard Worker mkArg.Strings[n-1] = strings.TrimRight(mkArg.Strings[n-1], ") ") 1083*333d2b36SAndroid Build Coastguard Worker args := mkArg.Split(",") 1084*333d2b36SAndroid Build Coastguard Worker // TODO(asmundak): handle the case where the arguments are in quotes and space-separated 1085*333d2b36SAndroid Build Coastguard Worker if len(args) != 2 { 1086*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(cond, "ifeq/ifneq len(args) != 2 %s", cond.Dump()) 1087*333d2b36SAndroid Build Coastguard Worker } 1088*333d2b36SAndroid Build Coastguard Worker args[0].TrimRightSpaces() 1089*333d2b36SAndroid Build Coastguard Worker args[1].TrimLeftSpaces() 1090*333d2b36SAndroid Build Coastguard Worker 1091*333d2b36SAndroid Build Coastguard Worker isEq := !strings.HasSuffix(cond.Name, "neq") 1092*333d2b36SAndroid Build Coastguard Worker xLeft := ctx.parseMakeString(cond, args[0]) 1093*333d2b36SAndroid Build Coastguard Worker xRight := ctx.parseMakeString(cond, args[1]) 1094*333d2b36SAndroid Build Coastguard Worker if bad, ok := xLeft.(*badExpr); ok { 1095*333d2b36SAndroid Build Coastguard Worker return bad 1096*333d2b36SAndroid Build Coastguard Worker } 1097*333d2b36SAndroid Build Coastguard Worker if bad, ok := xRight.(*badExpr); ok { 1098*333d2b36SAndroid Build Coastguard Worker return bad 1099*333d2b36SAndroid Build Coastguard Worker } 1100*333d2b36SAndroid Build Coastguard Worker 1101*333d2b36SAndroid Build Coastguard Worker if expr, ok := ctx.parseCompareSpecialCases(cond, xLeft, xRight); ok { 1102*333d2b36SAndroid Build Coastguard Worker return expr 1103*333d2b36SAndroid Build Coastguard Worker } 1104*333d2b36SAndroid Build Coastguard Worker 1105*333d2b36SAndroid Build Coastguard Worker var stringOperand string 1106*333d2b36SAndroid Build Coastguard Worker var otherOperand starlarkExpr 1107*333d2b36SAndroid Build Coastguard Worker if s, ok := maybeString(xLeft); ok { 1108*333d2b36SAndroid Build Coastguard Worker stringOperand = s 1109*333d2b36SAndroid Build Coastguard Worker otherOperand = xRight 1110*333d2b36SAndroid Build Coastguard Worker } else if s, ok := maybeString(xRight); ok { 1111*333d2b36SAndroid Build Coastguard Worker stringOperand = s 1112*333d2b36SAndroid Build Coastguard Worker otherOperand = xLeft 1113*333d2b36SAndroid Build Coastguard Worker } 1114*333d2b36SAndroid Build Coastguard Worker 1115*333d2b36SAndroid Build Coastguard Worker // If we've identified one of the operands as being a string literal, check 1116*333d2b36SAndroid Build Coastguard Worker // for some special cases we can do to simplify the resulting expression. 1117*333d2b36SAndroid Build Coastguard Worker if otherOperand != nil { 1118*333d2b36SAndroid Build Coastguard Worker if stringOperand == "" { 1119*333d2b36SAndroid Build Coastguard Worker if isEq { 1120*333d2b36SAndroid Build Coastguard Worker return negateExpr(otherOperand) 1121*333d2b36SAndroid Build Coastguard Worker } else { 1122*333d2b36SAndroid Build Coastguard Worker return otherOperand 1123*333d2b36SAndroid Build Coastguard Worker } 1124*333d2b36SAndroid Build Coastguard Worker } 1125*333d2b36SAndroid Build Coastguard Worker if stringOperand == "true" && otherOperand.typ() == starlarkTypeBool { 1126*333d2b36SAndroid Build Coastguard Worker if !isEq { 1127*333d2b36SAndroid Build Coastguard Worker return negateExpr(otherOperand) 1128*333d2b36SAndroid Build Coastguard Worker } else { 1129*333d2b36SAndroid Build Coastguard Worker return otherOperand 1130*333d2b36SAndroid Build Coastguard Worker } 1131*333d2b36SAndroid Build Coastguard Worker } 1132*333d2b36SAndroid Build Coastguard Worker if otherOperand.typ() == starlarkTypeList { 1133*333d2b36SAndroid Build Coastguard Worker fields := strings.Fields(stringOperand) 1134*333d2b36SAndroid Build Coastguard Worker elements := make([]starlarkExpr, len(fields)) 1135*333d2b36SAndroid Build Coastguard Worker for i, s := range fields { 1136*333d2b36SAndroid Build Coastguard Worker elements[i] = &stringLiteralExpr{literal: s} 1137*333d2b36SAndroid Build Coastguard Worker } 1138*333d2b36SAndroid Build Coastguard Worker return &eqExpr{ 1139*333d2b36SAndroid Build Coastguard Worker left: otherOperand, 1140*333d2b36SAndroid Build Coastguard Worker right: &listExpr{elements}, 1141*333d2b36SAndroid Build Coastguard Worker isEq: isEq, 1142*333d2b36SAndroid Build Coastguard Worker } 1143*333d2b36SAndroid Build Coastguard Worker } 1144*333d2b36SAndroid Build Coastguard Worker if intOperand, err := strconv.Atoi(strings.TrimSpace(stringOperand)); err == nil && otherOperand.typ() == starlarkTypeInt { 1145*333d2b36SAndroid Build Coastguard Worker return &eqExpr{ 1146*333d2b36SAndroid Build Coastguard Worker left: otherOperand, 1147*333d2b36SAndroid Build Coastguard Worker right: &intLiteralExpr{literal: intOperand}, 1148*333d2b36SAndroid Build Coastguard Worker isEq: isEq, 1149*333d2b36SAndroid Build Coastguard Worker } 1150*333d2b36SAndroid Build Coastguard Worker } 1151*333d2b36SAndroid Build Coastguard Worker } 1152*333d2b36SAndroid Build Coastguard Worker 1153*333d2b36SAndroid Build Coastguard Worker return &eqExpr{left: xLeft, right: xRight, isEq: isEq} 1154*333d2b36SAndroid Build Coastguard Worker} 1155*333d2b36SAndroid Build Coastguard Worker 1156*333d2b36SAndroid Build Coastguard Worker// Given an if statement's directive and the left/right starlarkExprs, 1157*333d2b36SAndroid Build Coastguard Worker// check if the starlarkExprs are one of a few hardcoded special cases 1158*333d2b36SAndroid Build Coastguard Worker// that can be converted to a simpler equality expression than simply comparing 1159*333d2b36SAndroid Build Coastguard Worker// the two. 1160*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) parseCompareSpecialCases(directive *mkparser.Directive, left starlarkExpr, 1161*333d2b36SAndroid Build Coastguard Worker right starlarkExpr) (starlarkExpr, bool) { 1162*333d2b36SAndroid Build Coastguard Worker isEq := !strings.HasSuffix(directive.Name, "neq") 1163*333d2b36SAndroid Build Coastguard Worker 1164*333d2b36SAndroid Build Coastguard Worker // All the special cases require a call on one side and a 1165*333d2b36SAndroid Build Coastguard Worker // string literal/variable on the other. Turn the left/right variables into 1166*333d2b36SAndroid Build Coastguard Worker // call/value variables, and return false if that's not possible. 1167*333d2b36SAndroid Build Coastguard Worker var value starlarkExpr = nil 1168*333d2b36SAndroid Build Coastguard Worker call, ok := left.(*callExpr) 1169*333d2b36SAndroid Build Coastguard Worker if ok { 1170*333d2b36SAndroid Build Coastguard Worker switch right.(type) { 1171*333d2b36SAndroid Build Coastguard Worker case *stringLiteralExpr, *variableRefExpr: 1172*333d2b36SAndroid Build Coastguard Worker value = right 1173*333d2b36SAndroid Build Coastguard Worker } 1174*333d2b36SAndroid Build Coastguard Worker } else { 1175*333d2b36SAndroid Build Coastguard Worker call, _ = right.(*callExpr) 1176*333d2b36SAndroid Build Coastguard Worker switch left.(type) { 1177*333d2b36SAndroid Build Coastguard Worker case *stringLiteralExpr, *variableRefExpr: 1178*333d2b36SAndroid Build Coastguard Worker value = left 1179*333d2b36SAndroid Build Coastguard Worker } 1180*333d2b36SAndroid Build Coastguard Worker } 1181*333d2b36SAndroid Build Coastguard Worker 1182*333d2b36SAndroid Build Coastguard Worker if call == nil || value == nil { 1183*333d2b36SAndroid Build Coastguard Worker return nil, false 1184*333d2b36SAndroid Build Coastguard Worker } 1185*333d2b36SAndroid Build Coastguard Worker 1186*333d2b36SAndroid Build Coastguard Worker switch call.name { 1187*333d2b36SAndroid Build Coastguard Worker case baseName + ".filter": 1188*333d2b36SAndroid Build Coastguard Worker return ctx.parseCompareFilterFuncResult(directive, call, value, isEq) 1189*333d2b36SAndroid Build Coastguard Worker case baseName + ".findstring": 1190*333d2b36SAndroid Build Coastguard Worker return ctx.parseCheckFindstringFuncResult(directive, call, value, !isEq), true 1191*333d2b36SAndroid Build Coastguard Worker case baseName + ".strip": 1192*333d2b36SAndroid Build Coastguard Worker return ctx.parseCompareStripFuncResult(directive, call, value, !isEq), true 1193*333d2b36SAndroid Build Coastguard Worker } 1194*333d2b36SAndroid Build Coastguard Worker return nil, false 1195*333d2b36SAndroid Build Coastguard Worker} 1196*333d2b36SAndroid Build Coastguard Worker 1197*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) parseCompareFilterFuncResult(cond *mkparser.Directive, 1198*333d2b36SAndroid Build Coastguard Worker filterFuncCall *callExpr, xValue starlarkExpr, negate bool) (starlarkExpr, bool) { 1199*333d2b36SAndroid Build Coastguard Worker // We handle: 1200*333d2b36SAndroid Build Coastguard Worker // * ifeq/ifneq (,$(filter v1 v2 ..., EXPR) becomes if EXPR not in/in ["v1", "v2", ...] 1201*333d2b36SAndroid Build Coastguard Worker // * ifeq/ifneq (,$(filter EXPR, v1 v2 ...) becomes if EXPR not in/in ["v1", "v2", ...] 1202*333d2b36SAndroid Build Coastguard Worker if x, ok := xValue.(*stringLiteralExpr); !ok || x.literal != "" { 1203*333d2b36SAndroid Build Coastguard Worker return nil, false 1204*333d2b36SAndroid Build Coastguard Worker } 1205*333d2b36SAndroid Build Coastguard Worker xPattern := filterFuncCall.args[0] 1206*333d2b36SAndroid Build Coastguard Worker xText := filterFuncCall.args[1] 1207*333d2b36SAndroid Build Coastguard Worker var xInList *stringLiteralExpr 1208*333d2b36SAndroid Build Coastguard Worker var expr starlarkExpr 1209*333d2b36SAndroid Build Coastguard Worker var ok bool 1210*333d2b36SAndroid Build Coastguard Worker if xInList, ok = xPattern.(*stringLiteralExpr); ok && !strings.ContainsRune(xInList.literal, '%') && xText.typ() == starlarkTypeList { 1211*333d2b36SAndroid Build Coastguard Worker expr = xText 1212*333d2b36SAndroid Build Coastguard Worker } else if xInList, ok = xText.(*stringLiteralExpr); ok { 1213*333d2b36SAndroid Build Coastguard Worker expr = xPattern 1214*333d2b36SAndroid Build Coastguard Worker } else { 1215*333d2b36SAndroid Build Coastguard Worker return nil, false 1216*333d2b36SAndroid Build Coastguard Worker } 1217*333d2b36SAndroid Build Coastguard Worker slExpr := newStringListExpr(strings.Fields(xInList.literal)) 1218*333d2b36SAndroid Build Coastguard Worker // Generate simpler code for the common cases: 1219*333d2b36SAndroid Build Coastguard Worker if expr.typ() == starlarkTypeList { 1220*333d2b36SAndroid Build Coastguard Worker if len(slExpr.items) == 1 { 1221*333d2b36SAndroid Build Coastguard Worker // Checking that a string belongs to list 1222*333d2b36SAndroid Build Coastguard Worker return &inExpr{isNot: negate, list: expr, expr: slExpr.items[0]}, true 1223*333d2b36SAndroid Build Coastguard Worker } else { 1224*333d2b36SAndroid Build Coastguard Worker return nil, false 1225*333d2b36SAndroid Build Coastguard Worker } 1226*333d2b36SAndroid Build Coastguard Worker } else if len(slExpr.items) == 1 { 1227*333d2b36SAndroid Build Coastguard Worker return &eqExpr{left: expr, right: slExpr.items[0], isEq: !negate}, true 1228*333d2b36SAndroid Build Coastguard Worker } else { 1229*333d2b36SAndroid Build Coastguard Worker return &inExpr{isNot: negate, list: newStringListExpr(strings.Fields(xInList.literal)), expr: expr}, true 1230*333d2b36SAndroid Build Coastguard Worker } 1231*333d2b36SAndroid Build Coastguard Worker} 1232*333d2b36SAndroid Build Coastguard Worker 1233*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) parseCheckFindstringFuncResult(directive *mkparser.Directive, 1234*333d2b36SAndroid Build Coastguard Worker xCall *callExpr, xValue starlarkExpr, negate bool) starlarkExpr { 1235*333d2b36SAndroid Build Coastguard Worker if isEmptyString(xValue) { 1236*333d2b36SAndroid Build Coastguard Worker return &eqExpr{ 1237*333d2b36SAndroid Build Coastguard Worker left: &callExpr{ 1238*333d2b36SAndroid Build Coastguard Worker object: xCall.args[1], 1239*333d2b36SAndroid Build Coastguard Worker name: "find", 1240*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{xCall.args[0]}, 1241*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeInt, 1242*333d2b36SAndroid Build Coastguard Worker }, 1243*333d2b36SAndroid Build Coastguard Worker right: &intLiteralExpr{-1}, 1244*333d2b36SAndroid Build Coastguard Worker isEq: !negate, 1245*333d2b36SAndroid Build Coastguard Worker } 1246*333d2b36SAndroid Build Coastguard Worker } else if s, ok := maybeString(xValue); ok { 1247*333d2b36SAndroid Build Coastguard Worker if s2, ok := maybeString(xCall.args[0]); ok && s == s2 { 1248*333d2b36SAndroid Build Coastguard Worker return &eqExpr{ 1249*333d2b36SAndroid Build Coastguard Worker left: &callExpr{ 1250*333d2b36SAndroid Build Coastguard Worker object: xCall.args[1], 1251*333d2b36SAndroid Build Coastguard Worker name: "find", 1252*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{xCall.args[0]}, 1253*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeInt, 1254*333d2b36SAndroid Build Coastguard Worker }, 1255*333d2b36SAndroid Build Coastguard Worker right: &intLiteralExpr{-1}, 1256*333d2b36SAndroid Build Coastguard Worker isEq: negate, 1257*333d2b36SAndroid Build Coastguard Worker } 1258*333d2b36SAndroid Build Coastguard Worker } 1259*333d2b36SAndroid Build Coastguard Worker } 1260*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(directive, "$(findstring) can only be compared to nothing or its first argument") 1261*333d2b36SAndroid Build Coastguard Worker} 1262*333d2b36SAndroid Build Coastguard Worker 1263*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) parseCompareStripFuncResult(directive *mkparser.Directive, 1264*333d2b36SAndroid Build Coastguard Worker xCall *callExpr, xValue starlarkExpr, negate bool) starlarkExpr { 1265*333d2b36SAndroid Build Coastguard Worker if _, ok := xValue.(*stringLiteralExpr); !ok { 1266*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(directive, "strip result can be compared only to string: %s", xValue) 1267*333d2b36SAndroid Build Coastguard Worker } 1268*333d2b36SAndroid Build Coastguard Worker return &eqExpr{ 1269*333d2b36SAndroid Build Coastguard Worker left: &callExpr{ 1270*333d2b36SAndroid Build Coastguard Worker name: "strip", 1271*333d2b36SAndroid Build Coastguard Worker args: xCall.args, 1272*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeString, 1273*333d2b36SAndroid Build Coastguard Worker }, 1274*333d2b36SAndroid Build Coastguard Worker right: xValue, isEq: !negate} 1275*333d2b36SAndroid Build Coastguard Worker} 1276*333d2b36SAndroid Build Coastguard Worker 1277*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) maybeParseFunctionCall(node mkparser.Node, ref *mkparser.MakeString) (name string, args *mkparser.MakeString, ok bool) { 1278*333d2b36SAndroid Build Coastguard Worker ref.TrimLeftSpaces() 1279*333d2b36SAndroid Build Coastguard Worker ref.TrimRightSpaces() 1280*333d2b36SAndroid Build Coastguard Worker 1281*333d2b36SAndroid Build Coastguard Worker words := ref.SplitN(" ", 2) 1282*333d2b36SAndroid Build Coastguard Worker if !words[0].Const() { 1283*333d2b36SAndroid Build Coastguard Worker return "", nil, false 1284*333d2b36SAndroid Build Coastguard Worker } 1285*333d2b36SAndroid Build Coastguard Worker 1286*333d2b36SAndroid Build Coastguard Worker name = words[0].Dump() 1287*333d2b36SAndroid Build Coastguard Worker args = mkparser.SimpleMakeString("", words[0].Pos()) 1288*333d2b36SAndroid Build Coastguard Worker if len(words) >= 2 { 1289*333d2b36SAndroid Build Coastguard Worker args = words[1] 1290*333d2b36SAndroid Build Coastguard Worker } 1291*333d2b36SAndroid Build Coastguard Worker args.TrimLeftSpaces() 1292*333d2b36SAndroid Build Coastguard Worker if name == "call" { 1293*333d2b36SAndroid Build Coastguard Worker words = args.SplitN(",", 2) 1294*333d2b36SAndroid Build Coastguard Worker if words[0].Empty() || !words[0].Const() { 1295*333d2b36SAndroid Build Coastguard Worker return "", nil, false 1296*333d2b36SAndroid Build Coastguard Worker } 1297*333d2b36SAndroid Build Coastguard Worker name = words[0].Dump() 1298*333d2b36SAndroid Build Coastguard Worker if len(words) < 2 { 1299*333d2b36SAndroid Build Coastguard Worker args = mkparser.SimpleMakeString("", words[0].Pos()) 1300*333d2b36SAndroid Build Coastguard Worker } else { 1301*333d2b36SAndroid Build Coastguard Worker args = words[1] 1302*333d2b36SAndroid Build Coastguard Worker } 1303*333d2b36SAndroid Build Coastguard Worker } 1304*333d2b36SAndroid Build Coastguard Worker ok = true 1305*333d2b36SAndroid Build Coastguard Worker return 1306*333d2b36SAndroid Build Coastguard Worker} 1307*333d2b36SAndroid Build Coastguard Worker 1308*333d2b36SAndroid Build Coastguard Worker// parses $(...), returning an expression 1309*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) parseReference(node mkparser.Node, ref *mkparser.MakeString) starlarkExpr { 1310*333d2b36SAndroid Build Coastguard Worker ref.TrimLeftSpaces() 1311*333d2b36SAndroid Build Coastguard Worker ref.TrimRightSpaces() 1312*333d2b36SAndroid Build Coastguard Worker refDump := ref.Dump() 1313*333d2b36SAndroid Build Coastguard Worker 1314*333d2b36SAndroid Build Coastguard Worker // Handle only the case where the first (or only) word is constant 1315*333d2b36SAndroid Build Coastguard Worker words := ref.SplitN(" ", 2) 1316*333d2b36SAndroid Build Coastguard Worker if !words[0].Const() { 1317*333d2b36SAndroid Build Coastguard Worker if len(words) == 1 { 1318*333d2b36SAndroid Build Coastguard Worker expr := ctx.parseMakeString(node, ref) 1319*333d2b36SAndroid Build Coastguard Worker return &callExpr{ 1320*333d2b36SAndroid Build Coastguard Worker object: &identifierExpr{"cfg"}, 1321*333d2b36SAndroid Build Coastguard Worker name: "get", 1322*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{ 1323*333d2b36SAndroid Build Coastguard Worker expr, 1324*333d2b36SAndroid Build Coastguard Worker &callExpr{ 1325*333d2b36SAndroid Build Coastguard Worker object: &identifierExpr{"g"}, 1326*333d2b36SAndroid Build Coastguard Worker name: "get", 1327*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{ 1328*333d2b36SAndroid Build Coastguard Worker expr, 1329*333d2b36SAndroid Build Coastguard Worker &stringLiteralExpr{literal: ""}, 1330*333d2b36SAndroid Build Coastguard Worker }, 1331*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeUnknown, 1332*333d2b36SAndroid Build Coastguard Worker }, 1333*333d2b36SAndroid Build Coastguard Worker }, 1334*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeUnknown, 1335*333d2b36SAndroid Build Coastguard Worker } 1336*333d2b36SAndroid Build Coastguard Worker } else { 1337*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "reference is too complex: %s", refDump) 1338*333d2b36SAndroid Build Coastguard Worker } 1339*333d2b36SAndroid Build Coastguard Worker } 1340*333d2b36SAndroid Build Coastguard Worker 1341*333d2b36SAndroid Build Coastguard Worker if name, _, ok := ctx.maybeParseFunctionCall(node, ref); ok { 1342*333d2b36SAndroid Build Coastguard Worker if _, unsupported := unsupportedFunctions[name]; unsupported { 1343*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "%s is not supported", refDump) 1344*333d2b36SAndroid Build Coastguard Worker } 1345*333d2b36SAndroid Build Coastguard Worker } 1346*333d2b36SAndroid Build Coastguard Worker 1347*333d2b36SAndroid Build Coastguard Worker // If it is a single word, it can be a simple variable 1348*333d2b36SAndroid Build Coastguard Worker // reference or a function call 1349*333d2b36SAndroid Build Coastguard Worker if len(words) == 1 && !isMakeControlFunc(refDump) && refDump != "shell" && refDump != "eval" { 1350*333d2b36SAndroid Build Coastguard Worker if strings.HasPrefix(refDump, soongNsPrefix) { 1351*333d2b36SAndroid Build Coastguard Worker // TODO (asmundak): if we find many, maybe handle them. 1352*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "SOONG_CONFIG_ variables cannot be referenced, use soong_config_get instead: %s", refDump) 1353*333d2b36SAndroid Build Coastguard Worker } 1354*333d2b36SAndroid Build Coastguard Worker // Handle substitution references: https://www.gnu.org/software/make/manual/html_node/Substitution-Refs.html 1355*333d2b36SAndroid Build Coastguard Worker if strings.Contains(refDump, ":") { 1356*333d2b36SAndroid Build Coastguard Worker parts := strings.SplitN(refDump, ":", 2) 1357*333d2b36SAndroid Build Coastguard Worker substParts := strings.SplitN(parts[1], "=", 2) 1358*333d2b36SAndroid Build Coastguard Worker if len(substParts) < 2 || strings.Count(substParts[0], "%") > 1 { 1359*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "Invalid substitution reference") 1360*333d2b36SAndroid Build Coastguard Worker } 1361*333d2b36SAndroid Build Coastguard Worker if !strings.Contains(substParts[0], "%") { 1362*333d2b36SAndroid Build Coastguard Worker if strings.Contains(substParts[1], "%") { 1363*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "A substitution reference must have a %% in the \"before\" part of the substitution if it has one in the \"after\" part.") 1364*333d2b36SAndroid Build Coastguard Worker } 1365*333d2b36SAndroid Build Coastguard Worker substParts[0] = "%" + substParts[0] 1366*333d2b36SAndroid Build Coastguard Worker substParts[1] = "%" + substParts[1] 1367*333d2b36SAndroid Build Coastguard Worker } 1368*333d2b36SAndroid Build Coastguard Worker v := ctx.addVariable(parts[0]) 1369*333d2b36SAndroid Build Coastguard Worker if v == nil { 1370*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "unknown variable %s", refDump) 1371*333d2b36SAndroid Build Coastguard Worker } 1372*333d2b36SAndroid Build Coastguard Worker return &callExpr{ 1373*333d2b36SAndroid Build Coastguard Worker name: baseName + ".mkpatsubst", 1374*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeString, 1375*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{ 1376*333d2b36SAndroid Build Coastguard Worker &stringLiteralExpr{literal: substParts[0]}, 1377*333d2b36SAndroid Build Coastguard Worker &stringLiteralExpr{literal: substParts[1]}, 1378*333d2b36SAndroid Build Coastguard Worker NewVariableRefExpr(v), 1379*333d2b36SAndroid Build Coastguard Worker }, 1380*333d2b36SAndroid Build Coastguard Worker } 1381*333d2b36SAndroid Build Coastguard Worker } 1382*333d2b36SAndroid Build Coastguard Worker if v := ctx.addVariable(refDump); v != nil { 1383*333d2b36SAndroid Build Coastguard Worker return NewVariableRefExpr(v) 1384*333d2b36SAndroid Build Coastguard Worker } 1385*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "unknown variable %s", refDump) 1386*333d2b36SAndroid Build Coastguard Worker } 1387*333d2b36SAndroid Build Coastguard Worker 1388*333d2b36SAndroid Build Coastguard Worker if name, args, ok := ctx.maybeParseFunctionCall(node, ref); ok { 1389*333d2b36SAndroid Build Coastguard Worker if kf, found := knownFunctions[name]; found { 1390*333d2b36SAndroid Build Coastguard Worker return kf.parse(ctx, node, args) 1391*333d2b36SAndroid Build Coastguard Worker } else { 1392*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "cannot handle invoking %s", name) 1393*333d2b36SAndroid Build Coastguard Worker } 1394*333d2b36SAndroid Build Coastguard Worker } 1395*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "cannot handle %s", refDump) 1396*333d2b36SAndroid Build Coastguard Worker} 1397*333d2b36SAndroid Build Coastguard Worker 1398*333d2b36SAndroid Build Coastguard Workertype simpleCallParser struct { 1399*333d2b36SAndroid Build Coastguard Worker name string 1400*333d2b36SAndroid Build Coastguard Worker returnType starlarkType 1401*333d2b36SAndroid Build Coastguard Worker addGlobals bool 1402*333d2b36SAndroid Build Coastguard Worker addHandle bool 1403*333d2b36SAndroid Build Coastguard Worker} 1404*333d2b36SAndroid Build Coastguard Worker 1405*333d2b36SAndroid Build Coastguard Workerfunc (p *simpleCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { 1406*333d2b36SAndroid Build Coastguard Worker expr := &callExpr{name: p.name, returnType: p.returnType} 1407*333d2b36SAndroid Build Coastguard Worker if p.addGlobals { 1408*333d2b36SAndroid Build Coastguard Worker expr.args = append(expr.args, &globalsExpr{}) 1409*333d2b36SAndroid Build Coastguard Worker } 1410*333d2b36SAndroid Build Coastguard Worker if p.addHandle { 1411*333d2b36SAndroid Build Coastguard Worker expr.args = append(expr.args, &identifierExpr{name: "handle"}) 1412*333d2b36SAndroid Build Coastguard Worker } 1413*333d2b36SAndroid Build Coastguard Worker for _, arg := range args.Split(",") { 1414*333d2b36SAndroid Build Coastguard Worker arg.TrimLeftSpaces() 1415*333d2b36SAndroid Build Coastguard Worker arg.TrimRightSpaces() 1416*333d2b36SAndroid Build Coastguard Worker x := ctx.parseMakeString(node, arg) 1417*333d2b36SAndroid Build Coastguard Worker if xBad, ok := x.(*badExpr); ok { 1418*333d2b36SAndroid Build Coastguard Worker return xBad 1419*333d2b36SAndroid Build Coastguard Worker } 1420*333d2b36SAndroid Build Coastguard Worker expr.args = append(expr.args, x) 1421*333d2b36SAndroid Build Coastguard Worker } 1422*333d2b36SAndroid Build Coastguard Worker return expr 1423*333d2b36SAndroid Build Coastguard Worker} 1424*333d2b36SAndroid Build Coastguard Worker 1425*333d2b36SAndroid Build Coastguard Workertype makeControlFuncParser struct { 1426*333d2b36SAndroid Build Coastguard Worker name string 1427*333d2b36SAndroid Build Coastguard Worker} 1428*333d2b36SAndroid Build Coastguard Worker 1429*333d2b36SAndroid Build Coastguard Workerfunc (p *makeControlFuncParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { 1430*333d2b36SAndroid Build Coastguard Worker // Make control functions need special treatment as everything 1431*333d2b36SAndroid Build Coastguard Worker // after the name is a single text argument 1432*333d2b36SAndroid Build Coastguard Worker x := ctx.parseMakeString(node, args) 1433*333d2b36SAndroid Build Coastguard Worker if xBad, ok := x.(*badExpr); ok { 1434*333d2b36SAndroid Build Coastguard Worker return xBad 1435*333d2b36SAndroid Build Coastguard Worker } 1436*333d2b36SAndroid Build Coastguard Worker return &callExpr{ 1437*333d2b36SAndroid Build Coastguard Worker name: p.name, 1438*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{ 1439*333d2b36SAndroid Build Coastguard Worker &stringLiteralExpr{ctx.script.mkFile}, 1440*333d2b36SAndroid Build Coastguard Worker x, 1441*333d2b36SAndroid Build Coastguard Worker }, 1442*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeUnknown, 1443*333d2b36SAndroid Build Coastguard Worker } 1444*333d2b36SAndroid Build Coastguard Worker} 1445*333d2b36SAndroid Build Coastguard Worker 1446*333d2b36SAndroid Build Coastguard Workertype shellCallParser struct{} 1447*333d2b36SAndroid Build Coastguard Worker 1448*333d2b36SAndroid Build Coastguard Workerfunc (p *shellCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { 1449*333d2b36SAndroid Build Coastguard Worker // Shell functions need special treatment as everything 1450*333d2b36SAndroid Build Coastguard Worker // after the name is a single text argument 1451*333d2b36SAndroid Build Coastguard Worker x := ctx.parseMakeString(node, args) 1452*333d2b36SAndroid Build Coastguard Worker if xBad, ok := x.(*badExpr); ok { 1453*333d2b36SAndroid Build Coastguard Worker return xBad 1454*333d2b36SAndroid Build Coastguard Worker } 1455*333d2b36SAndroid Build Coastguard Worker return &callExpr{ 1456*333d2b36SAndroid Build Coastguard Worker name: baseName + ".shell", 1457*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{x}, 1458*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeUnknown, 1459*333d2b36SAndroid Build Coastguard Worker } 1460*333d2b36SAndroid Build Coastguard Worker} 1461*333d2b36SAndroid Build Coastguard Worker 1462*333d2b36SAndroid Build Coastguard Workertype myDirCallParser struct{} 1463*333d2b36SAndroid Build Coastguard Worker 1464*333d2b36SAndroid Build Coastguard Workerfunc (p *myDirCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { 1465*333d2b36SAndroid Build Coastguard Worker if !args.Empty() { 1466*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "my-dir function cannot have any arguments passed to it.") 1467*333d2b36SAndroid Build Coastguard Worker } 1468*333d2b36SAndroid Build Coastguard Worker return &stringLiteralExpr{literal: filepath.Dir(ctx.script.mkFile)} 1469*333d2b36SAndroid Build Coastguard Worker} 1470*333d2b36SAndroid Build Coastguard Worker 1471*333d2b36SAndroid Build Coastguard Workertype andOrParser struct { 1472*333d2b36SAndroid Build Coastguard Worker isAnd bool 1473*333d2b36SAndroid Build Coastguard Worker} 1474*333d2b36SAndroid Build Coastguard Worker 1475*333d2b36SAndroid Build Coastguard Workerfunc (p *andOrParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { 1476*333d2b36SAndroid Build Coastguard Worker if args.Empty() { 1477*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "and/or function must have at least 1 argument") 1478*333d2b36SAndroid Build Coastguard Worker } 1479*333d2b36SAndroid Build Coastguard Worker op := "or" 1480*333d2b36SAndroid Build Coastguard Worker if p.isAnd { 1481*333d2b36SAndroid Build Coastguard Worker op = "and" 1482*333d2b36SAndroid Build Coastguard Worker } 1483*333d2b36SAndroid Build Coastguard Worker 1484*333d2b36SAndroid Build Coastguard Worker argsParsed := make([]starlarkExpr, 0) 1485*333d2b36SAndroid Build Coastguard Worker 1486*333d2b36SAndroid Build Coastguard Worker for _, arg := range args.Split(",") { 1487*333d2b36SAndroid Build Coastguard Worker arg.TrimLeftSpaces() 1488*333d2b36SAndroid Build Coastguard Worker arg.TrimRightSpaces() 1489*333d2b36SAndroid Build Coastguard Worker x := ctx.parseMakeString(node, arg) 1490*333d2b36SAndroid Build Coastguard Worker if xBad, ok := x.(*badExpr); ok { 1491*333d2b36SAndroid Build Coastguard Worker return xBad 1492*333d2b36SAndroid Build Coastguard Worker } 1493*333d2b36SAndroid Build Coastguard Worker argsParsed = append(argsParsed, x) 1494*333d2b36SAndroid Build Coastguard Worker } 1495*333d2b36SAndroid Build Coastguard Worker typ := starlarkTypeUnknown 1496*333d2b36SAndroid Build Coastguard Worker for _, arg := range argsParsed { 1497*333d2b36SAndroid Build Coastguard Worker if typ != arg.typ() && arg.typ() != starlarkTypeUnknown && typ != starlarkTypeUnknown { 1498*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "Expected all arguments to $(or) or $(and) to have the same type, found %q and %q", typ.String(), arg.typ().String()) 1499*333d2b36SAndroid Build Coastguard Worker } 1500*333d2b36SAndroid Build Coastguard Worker if arg.typ() != starlarkTypeUnknown { 1501*333d2b36SAndroid Build Coastguard Worker typ = arg.typ() 1502*333d2b36SAndroid Build Coastguard Worker } 1503*333d2b36SAndroid Build Coastguard Worker } 1504*333d2b36SAndroid Build Coastguard Worker result := argsParsed[0] 1505*333d2b36SAndroid Build Coastguard Worker for _, arg := range argsParsed[1:] { 1506*333d2b36SAndroid Build Coastguard Worker result = &binaryOpExpr{ 1507*333d2b36SAndroid Build Coastguard Worker left: result, 1508*333d2b36SAndroid Build Coastguard Worker right: arg, 1509*333d2b36SAndroid Build Coastguard Worker op: op, 1510*333d2b36SAndroid Build Coastguard Worker returnType: typ, 1511*333d2b36SAndroid Build Coastguard Worker } 1512*333d2b36SAndroid Build Coastguard Worker } 1513*333d2b36SAndroid Build Coastguard Worker return result 1514*333d2b36SAndroid Build Coastguard Worker} 1515*333d2b36SAndroid Build Coastguard Worker 1516*333d2b36SAndroid Build Coastguard Workertype isProductInListCallParser struct{} 1517*333d2b36SAndroid Build Coastguard Worker 1518*333d2b36SAndroid Build Coastguard Workerfunc (p *isProductInListCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { 1519*333d2b36SAndroid Build Coastguard Worker if args.Empty() { 1520*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "is-product-in-list requires an argument") 1521*333d2b36SAndroid Build Coastguard Worker } 1522*333d2b36SAndroid Build Coastguard Worker return &inExpr{ 1523*333d2b36SAndroid Build Coastguard Worker expr: NewVariableRefExpr(ctx.addVariable("TARGET_PRODUCT")), 1524*333d2b36SAndroid Build Coastguard Worker list: maybeConvertToStringList(ctx.parseMakeString(node, args)), 1525*333d2b36SAndroid Build Coastguard Worker isNot: false, 1526*333d2b36SAndroid Build Coastguard Worker } 1527*333d2b36SAndroid Build Coastguard Worker} 1528*333d2b36SAndroid Build Coastguard Worker 1529*333d2b36SAndroid Build Coastguard Workertype isVendorBoardPlatformCallParser struct{} 1530*333d2b36SAndroid Build Coastguard Worker 1531*333d2b36SAndroid Build Coastguard Workerfunc (p *isVendorBoardPlatformCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { 1532*333d2b36SAndroid Build Coastguard Worker if args.Empty() || !identifierFullMatchRegex.MatchString(args.Dump()) { 1533*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "cannot handle non-constant argument to is-vendor-board-platform") 1534*333d2b36SAndroid Build Coastguard Worker } 1535*333d2b36SAndroid Build Coastguard Worker return &inExpr{ 1536*333d2b36SAndroid Build Coastguard Worker expr: NewVariableRefExpr(ctx.addVariable("TARGET_BOARD_PLATFORM")), 1537*333d2b36SAndroid Build Coastguard Worker list: NewVariableRefExpr(ctx.addVariable(args.Dump() + "_BOARD_PLATFORMS")), 1538*333d2b36SAndroid Build Coastguard Worker isNot: false, 1539*333d2b36SAndroid Build Coastguard Worker } 1540*333d2b36SAndroid Build Coastguard Worker} 1541*333d2b36SAndroid Build Coastguard Worker 1542*333d2b36SAndroid Build Coastguard Workertype isVendorBoardQcomCallParser struct{} 1543*333d2b36SAndroid Build Coastguard Worker 1544*333d2b36SAndroid Build Coastguard Workerfunc (p *isVendorBoardQcomCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { 1545*333d2b36SAndroid Build Coastguard Worker if !args.Empty() { 1546*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "is-vendor-board-qcom does not accept any arguments") 1547*333d2b36SAndroid Build Coastguard Worker } 1548*333d2b36SAndroid Build Coastguard Worker return &inExpr{ 1549*333d2b36SAndroid Build Coastguard Worker expr: NewVariableRefExpr(ctx.addVariable("TARGET_BOARD_PLATFORM")), 1550*333d2b36SAndroid Build Coastguard Worker list: NewVariableRefExpr(ctx.addVariable("QCOM_BOARD_PLATFORMS")), 1551*333d2b36SAndroid Build Coastguard Worker isNot: false, 1552*333d2b36SAndroid Build Coastguard Worker } 1553*333d2b36SAndroid Build Coastguard Worker} 1554*333d2b36SAndroid Build Coastguard Worker 1555*333d2b36SAndroid Build Coastguard Workertype substCallParser struct { 1556*333d2b36SAndroid Build Coastguard Worker fname string 1557*333d2b36SAndroid Build Coastguard Worker} 1558*333d2b36SAndroid Build Coastguard Worker 1559*333d2b36SAndroid Build Coastguard Workerfunc (p *substCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { 1560*333d2b36SAndroid Build Coastguard Worker words := args.Split(",") 1561*333d2b36SAndroid Build Coastguard Worker if len(words) != 3 { 1562*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "%s function should have 3 arguments", p.fname) 1563*333d2b36SAndroid Build Coastguard Worker } 1564*333d2b36SAndroid Build Coastguard Worker from := ctx.parseMakeString(node, words[0]) 1565*333d2b36SAndroid Build Coastguard Worker if xBad, ok := from.(*badExpr); ok { 1566*333d2b36SAndroid Build Coastguard Worker return xBad 1567*333d2b36SAndroid Build Coastguard Worker } 1568*333d2b36SAndroid Build Coastguard Worker to := ctx.parseMakeString(node, words[1]) 1569*333d2b36SAndroid Build Coastguard Worker if xBad, ok := to.(*badExpr); ok { 1570*333d2b36SAndroid Build Coastguard Worker return xBad 1571*333d2b36SAndroid Build Coastguard Worker } 1572*333d2b36SAndroid Build Coastguard Worker words[2].TrimLeftSpaces() 1573*333d2b36SAndroid Build Coastguard Worker words[2].TrimRightSpaces() 1574*333d2b36SAndroid Build Coastguard Worker obj := ctx.parseMakeString(node, words[2]) 1575*333d2b36SAndroid Build Coastguard Worker typ := obj.typ() 1576*333d2b36SAndroid Build Coastguard Worker if typ == starlarkTypeString && p.fname == "subst" { 1577*333d2b36SAndroid Build Coastguard Worker // Optimization: if it's $(subst from, to, string), emit string.replace(from, to) 1578*333d2b36SAndroid Build Coastguard Worker return &callExpr{ 1579*333d2b36SAndroid Build Coastguard Worker object: obj, 1580*333d2b36SAndroid Build Coastguard Worker name: "replace", 1581*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{from, to}, 1582*333d2b36SAndroid Build Coastguard Worker returnType: typ, 1583*333d2b36SAndroid Build Coastguard Worker } 1584*333d2b36SAndroid Build Coastguard Worker } 1585*333d2b36SAndroid Build Coastguard Worker return &callExpr{ 1586*333d2b36SAndroid Build Coastguard Worker name: baseName + ".mk" + p.fname, 1587*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{from, to, obj}, 1588*333d2b36SAndroid Build Coastguard Worker returnType: obj.typ(), 1589*333d2b36SAndroid Build Coastguard Worker } 1590*333d2b36SAndroid Build Coastguard Worker} 1591*333d2b36SAndroid Build Coastguard Worker 1592*333d2b36SAndroid Build Coastguard Workertype ifCallParser struct{} 1593*333d2b36SAndroid Build Coastguard Worker 1594*333d2b36SAndroid Build Coastguard Workerfunc (p *ifCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { 1595*333d2b36SAndroid Build Coastguard Worker words := args.Split(",") 1596*333d2b36SAndroid Build Coastguard Worker if len(words) != 2 && len(words) != 3 { 1597*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "if function should have 2 or 3 arguments, found "+strconv.Itoa(len(words))) 1598*333d2b36SAndroid Build Coastguard Worker } 1599*333d2b36SAndroid Build Coastguard Worker condition := ctx.parseMakeString(node, words[0]) 1600*333d2b36SAndroid Build Coastguard Worker ifTrue := ctx.parseMakeString(node, words[1]) 1601*333d2b36SAndroid Build Coastguard Worker var ifFalse starlarkExpr 1602*333d2b36SAndroid Build Coastguard Worker if len(words) == 3 { 1603*333d2b36SAndroid Build Coastguard Worker ifFalse = ctx.parseMakeString(node, words[2]) 1604*333d2b36SAndroid Build Coastguard Worker } else { 1605*333d2b36SAndroid Build Coastguard Worker switch ifTrue.typ() { 1606*333d2b36SAndroid Build Coastguard Worker case starlarkTypeList: 1607*333d2b36SAndroid Build Coastguard Worker ifFalse = &listExpr{items: []starlarkExpr{}} 1608*333d2b36SAndroid Build Coastguard Worker case starlarkTypeInt: 1609*333d2b36SAndroid Build Coastguard Worker ifFalse = &intLiteralExpr{literal: 0} 1610*333d2b36SAndroid Build Coastguard Worker case starlarkTypeBool: 1611*333d2b36SAndroid Build Coastguard Worker ifFalse = &boolLiteralExpr{literal: false} 1612*333d2b36SAndroid Build Coastguard Worker default: 1613*333d2b36SAndroid Build Coastguard Worker ifFalse = &stringLiteralExpr{literal: ""} 1614*333d2b36SAndroid Build Coastguard Worker } 1615*333d2b36SAndroid Build Coastguard Worker } 1616*333d2b36SAndroid Build Coastguard Worker return &ifExpr{ 1617*333d2b36SAndroid Build Coastguard Worker condition, 1618*333d2b36SAndroid Build Coastguard Worker ifTrue, 1619*333d2b36SAndroid Build Coastguard Worker ifFalse, 1620*333d2b36SAndroid Build Coastguard Worker } 1621*333d2b36SAndroid Build Coastguard Worker} 1622*333d2b36SAndroid Build Coastguard Worker 1623*333d2b36SAndroid Build Coastguard Workertype ifCallNodeParser struct{} 1624*333d2b36SAndroid Build Coastguard Worker 1625*333d2b36SAndroid Build Coastguard Workerfunc (p *ifCallNodeParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) []starlarkNode { 1626*333d2b36SAndroid Build Coastguard Worker words := args.Split(",") 1627*333d2b36SAndroid Build Coastguard Worker if len(words) != 2 && len(words) != 3 { 1628*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{ctx.newBadNode(node, "if function should have 2 or 3 arguments, found "+strconv.Itoa(len(words)))} 1629*333d2b36SAndroid Build Coastguard Worker } 1630*333d2b36SAndroid Build Coastguard Worker 1631*333d2b36SAndroid Build Coastguard Worker ifn := &ifNode{expr: ctx.parseMakeString(node, words[0])} 1632*333d2b36SAndroid Build Coastguard Worker cases := []*switchCase{ 1633*333d2b36SAndroid Build Coastguard Worker { 1634*333d2b36SAndroid Build Coastguard Worker gate: ifn, 1635*333d2b36SAndroid Build Coastguard Worker nodes: ctx.parseNodeMakeString(node, words[1]), 1636*333d2b36SAndroid Build Coastguard Worker }, 1637*333d2b36SAndroid Build Coastguard Worker } 1638*333d2b36SAndroid Build Coastguard Worker if len(words) == 3 { 1639*333d2b36SAndroid Build Coastguard Worker cases = append(cases, &switchCase{ 1640*333d2b36SAndroid Build Coastguard Worker gate: &elseNode{}, 1641*333d2b36SAndroid Build Coastguard Worker nodes: ctx.parseNodeMakeString(node, words[2]), 1642*333d2b36SAndroid Build Coastguard Worker }) 1643*333d2b36SAndroid Build Coastguard Worker } 1644*333d2b36SAndroid Build Coastguard Worker if len(cases) == 2 { 1645*333d2b36SAndroid Build Coastguard Worker if len(cases[1].nodes) == 0 { 1646*333d2b36SAndroid Build Coastguard Worker // Remove else branch if it has no contents 1647*333d2b36SAndroid Build Coastguard Worker cases = cases[:1] 1648*333d2b36SAndroid Build Coastguard Worker } else if len(cases[0].nodes) == 0 { 1649*333d2b36SAndroid Build Coastguard Worker // If the if branch has no contents but the else does, 1650*333d2b36SAndroid Build Coastguard Worker // move them to the if and negate its condition 1651*333d2b36SAndroid Build Coastguard Worker ifn.expr = negateExpr(ifn.expr) 1652*333d2b36SAndroid Build Coastguard Worker cases[0].nodes = cases[1].nodes 1653*333d2b36SAndroid Build Coastguard Worker cases = cases[:1] 1654*333d2b36SAndroid Build Coastguard Worker } 1655*333d2b36SAndroid Build Coastguard Worker } 1656*333d2b36SAndroid Build Coastguard Worker 1657*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{&switchNode{ssCases: cases}} 1658*333d2b36SAndroid Build Coastguard Worker} 1659*333d2b36SAndroid Build Coastguard Worker 1660*333d2b36SAndroid Build Coastguard Workertype foreachCallParser struct{} 1661*333d2b36SAndroid Build Coastguard Worker 1662*333d2b36SAndroid Build Coastguard Workerfunc (p *foreachCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { 1663*333d2b36SAndroid Build Coastguard Worker words := args.Split(",") 1664*333d2b36SAndroid Build Coastguard Worker if len(words) != 3 { 1665*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "foreach function should have 3 arguments, found "+strconv.Itoa(len(words))) 1666*333d2b36SAndroid Build Coastguard Worker } 1667*333d2b36SAndroid Build Coastguard Worker if !words[0].Const() || words[0].Empty() || !identifierFullMatchRegex.MatchString(words[0].Strings[0]) { 1668*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "first argument to foreach function must be a simple string identifier") 1669*333d2b36SAndroid Build Coastguard Worker } 1670*333d2b36SAndroid Build Coastguard Worker loopVarName := words[0].Strings[0] 1671*333d2b36SAndroid Build Coastguard Worker list := ctx.parseMakeString(node, words[1]) 1672*333d2b36SAndroid Build Coastguard Worker action := ctx.parseMakeString(node, words[2]).transform(func(expr starlarkExpr) starlarkExpr { 1673*333d2b36SAndroid Build Coastguard Worker if varRefExpr, ok := expr.(*variableRefExpr); ok && varRefExpr.ref.name() == loopVarName { 1674*333d2b36SAndroid Build Coastguard Worker return &identifierExpr{loopVarName} 1675*333d2b36SAndroid Build Coastguard Worker } 1676*333d2b36SAndroid Build Coastguard Worker return nil 1677*333d2b36SAndroid Build Coastguard Worker }) 1678*333d2b36SAndroid Build Coastguard Worker 1679*333d2b36SAndroid Build Coastguard Worker if list.typ() != starlarkTypeList { 1680*333d2b36SAndroid Build Coastguard Worker list = &callExpr{ 1681*333d2b36SAndroid Build Coastguard Worker name: baseName + ".words", 1682*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeList, 1683*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{list}, 1684*333d2b36SAndroid Build Coastguard Worker } 1685*333d2b36SAndroid Build Coastguard Worker } 1686*333d2b36SAndroid Build Coastguard Worker 1687*333d2b36SAndroid Build Coastguard Worker var result starlarkExpr = &foreachExpr{ 1688*333d2b36SAndroid Build Coastguard Worker varName: loopVarName, 1689*333d2b36SAndroid Build Coastguard Worker list: list, 1690*333d2b36SAndroid Build Coastguard Worker action: action, 1691*333d2b36SAndroid Build Coastguard Worker } 1692*333d2b36SAndroid Build Coastguard Worker 1693*333d2b36SAndroid Build Coastguard Worker if action.typ() == starlarkTypeList { 1694*333d2b36SAndroid Build Coastguard Worker result = &callExpr{ 1695*333d2b36SAndroid Build Coastguard Worker name: baseName + ".flatten_2d_list", 1696*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{result}, 1697*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeList, 1698*333d2b36SAndroid Build Coastguard Worker } 1699*333d2b36SAndroid Build Coastguard Worker } 1700*333d2b36SAndroid Build Coastguard Worker 1701*333d2b36SAndroid Build Coastguard Worker return result 1702*333d2b36SAndroid Build Coastguard Worker} 1703*333d2b36SAndroid Build Coastguard Worker 1704*333d2b36SAndroid Build Coastguard Workerfunc transformNode(node starlarkNode, transformer func(expr starlarkExpr) starlarkExpr) { 1705*333d2b36SAndroid Build Coastguard Worker switch a := node.(type) { 1706*333d2b36SAndroid Build Coastguard Worker case *ifNode: 1707*333d2b36SAndroid Build Coastguard Worker a.expr = a.expr.transform(transformer) 1708*333d2b36SAndroid Build Coastguard Worker case *switchCase: 1709*333d2b36SAndroid Build Coastguard Worker transformNode(a.gate, transformer) 1710*333d2b36SAndroid Build Coastguard Worker for _, n := range a.nodes { 1711*333d2b36SAndroid Build Coastguard Worker transformNode(n, transformer) 1712*333d2b36SAndroid Build Coastguard Worker } 1713*333d2b36SAndroid Build Coastguard Worker case *switchNode: 1714*333d2b36SAndroid Build Coastguard Worker for _, n := range a.ssCases { 1715*333d2b36SAndroid Build Coastguard Worker transformNode(n, transformer) 1716*333d2b36SAndroid Build Coastguard Worker } 1717*333d2b36SAndroid Build Coastguard Worker case *exprNode: 1718*333d2b36SAndroid Build Coastguard Worker a.expr = a.expr.transform(transformer) 1719*333d2b36SAndroid Build Coastguard Worker case *assignmentNode: 1720*333d2b36SAndroid Build Coastguard Worker a.value = a.value.transform(transformer) 1721*333d2b36SAndroid Build Coastguard Worker case *foreachNode: 1722*333d2b36SAndroid Build Coastguard Worker a.list = a.list.transform(transformer) 1723*333d2b36SAndroid Build Coastguard Worker for _, n := range a.actions { 1724*333d2b36SAndroid Build Coastguard Worker transformNode(n, transformer) 1725*333d2b36SAndroid Build Coastguard Worker } 1726*333d2b36SAndroid Build Coastguard Worker case *inheritNode: 1727*333d2b36SAndroid Build Coastguard Worker if b, ok := a.module.(inheritedDynamicModule); ok { 1728*333d2b36SAndroid Build Coastguard Worker b.path = b.path.transform(transformer) 1729*333d2b36SAndroid Build Coastguard Worker a.module = b 1730*333d2b36SAndroid Build Coastguard Worker } 1731*333d2b36SAndroid Build Coastguard Worker case *includeNode: 1732*333d2b36SAndroid Build Coastguard Worker if b, ok := a.module.(inheritedDynamicModule); ok { 1733*333d2b36SAndroid Build Coastguard Worker b.path = b.path.transform(transformer) 1734*333d2b36SAndroid Build Coastguard Worker a.module = b 1735*333d2b36SAndroid Build Coastguard Worker } 1736*333d2b36SAndroid Build Coastguard Worker } 1737*333d2b36SAndroid Build Coastguard Worker} 1738*333d2b36SAndroid Build Coastguard Worker 1739*333d2b36SAndroid Build Coastguard Workertype foreachCallNodeParser struct{} 1740*333d2b36SAndroid Build Coastguard Worker 1741*333d2b36SAndroid Build Coastguard Workerfunc (p *foreachCallNodeParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) []starlarkNode { 1742*333d2b36SAndroid Build Coastguard Worker words := args.Split(",") 1743*333d2b36SAndroid Build Coastguard Worker if len(words) != 3 { 1744*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{ctx.newBadNode(node, "foreach function should have 3 arguments, found "+strconv.Itoa(len(words)))} 1745*333d2b36SAndroid Build Coastguard Worker } 1746*333d2b36SAndroid Build Coastguard Worker if !words[0].Const() || words[0].Empty() || !identifierFullMatchRegex.MatchString(words[0].Strings[0]) { 1747*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{ctx.newBadNode(node, "first argument to foreach function must be a simple string identifier")} 1748*333d2b36SAndroid Build Coastguard Worker } 1749*333d2b36SAndroid Build Coastguard Worker 1750*333d2b36SAndroid Build Coastguard Worker loopVarName := words[0].Strings[0] 1751*333d2b36SAndroid Build Coastguard Worker 1752*333d2b36SAndroid Build Coastguard Worker list := ctx.parseMakeString(node, words[1]) 1753*333d2b36SAndroid Build Coastguard Worker if list.typ() != starlarkTypeList { 1754*333d2b36SAndroid Build Coastguard Worker list = &callExpr{ 1755*333d2b36SAndroid Build Coastguard Worker name: baseName + ".words", 1756*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeList, 1757*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{list}, 1758*333d2b36SAndroid Build Coastguard Worker } 1759*333d2b36SAndroid Build Coastguard Worker } 1760*333d2b36SAndroid Build Coastguard Worker 1761*333d2b36SAndroid Build Coastguard Worker actions := ctx.parseNodeMakeString(node, words[2]) 1762*333d2b36SAndroid Build Coastguard Worker // TODO(colefaust): Replace transforming code with something more elegant 1763*333d2b36SAndroid Build Coastguard Worker for _, action := range actions { 1764*333d2b36SAndroid Build Coastguard Worker transformNode(action, func(expr starlarkExpr) starlarkExpr { 1765*333d2b36SAndroid Build Coastguard Worker if varRefExpr, ok := expr.(*variableRefExpr); ok && varRefExpr.ref.name() == loopVarName { 1766*333d2b36SAndroid Build Coastguard Worker return &identifierExpr{loopVarName} 1767*333d2b36SAndroid Build Coastguard Worker } 1768*333d2b36SAndroid Build Coastguard Worker return nil 1769*333d2b36SAndroid Build Coastguard Worker }) 1770*333d2b36SAndroid Build Coastguard Worker } 1771*333d2b36SAndroid Build Coastguard Worker 1772*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{&foreachNode{ 1773*333d2b36SAndroid Build Coastguard Worker varName: loopVarName, 1774*333d2b36SAndroid Build Coastguard Worker list: list, 1775*333d2b36SAndroid Build Coastguard Worker actions: actions, 1776*333d2b36SAndroid Build Coastguard Worker }} 1777*333d2b36SAndroid Build Coastguard Worker} 1778*333d2b36SAndroid Build Coastguard Worker 1779*333d2b36SAndroid Build Coastguard Workertype wordCallParser struct{} 1780*333d2b36SAndroid Build Coastguard Worker 1781*333d2b36SAndroid Build Coastguard Workerfunc (p *wordCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { 1782*333d2b36SAndroid Build Coastguard Worker words := args.Split(",") 1783*333d2b36SAndroid Build Coastguard Worker if len(words) != 2 { 1784*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "word function should have 2 arguments") 1785*333d2b36SAndroid Build Coastguard Worker } 1786*333d2b36SAndroid Build Coastguard Worker var index = 0 1787*333d2b36SAndroid Build Coastguard Worker if words[0].Const() { 1788*333d2b36SAndroid Build Coastguard Worker if i, err := strconv.Atoi(strings.TrimSpace(words[0].Strings[0])); err == nil { 1789*333d2b36SAndroid Build Coastguard Worker index = i 1790*333d2b36SAndroid Build Coastguard Worker } 1791*333d2b36SAndroid Build Coastguard Worker } 1792*333d2b36SAndroid Build Coastguard Worker if index < 1 { 1793*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, "word index should be constant positive integer") 1794*333d2b36SAndroid Build Coastguard Worker } 1795*333d2b36SAndroid Build Coastguard Worker words[1].TrimLeftSpaces() 1796*333d2b36SAndroid Build Coastguard Worker words[1].TrimRightSpaces() 1797*333d2b36SAndroid Build Coastguard Worker array := ctx.parseMakeString(node, words[1]) 1798*333d2b36SAndroid Build Coastguard Worker if bad, ok := array.(*badExpr); ok { 1799*333d2b36SAndroid Build Coastguard Worker return bad 1800*333d2b36SAndroid Build Coastguard Worker } 1801*333d2b36SAndroid Build Coastguard Worker if array.typ() != starlarkTypeList { 1802*333d2b36SAndroid Build Coastguard Worker array = &callExpr{ 1803*333d2b36SAndroid Build Coastguard Worker name: baseName + ".words", 1804*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{array}, 1805*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeList, 1806*333d2b36SAndroid Build Coastguard Worker } 1807*333d2b36SAndroid Build Coastguard Worker } 1808*333d2b36SAndroid Build Coastguard Worker return &indexExpr{array, &intLiteralExpr{index - 1}} 1809*333d2b36SAndroid Build Coastguard Worker} 1810*333d2b36SAndroid Build Coastguard Worker 1811*333d2b36SAndroid Build Coastguard Workertype wordsCallParser struct{} 1812*333d2b36SAndroid Build Coastguard Worker 1813*333d2b36SAndroid Build Coastguard Workerfunc (p *wordsCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { 1814*333d2b36SAndroid Build Coastguard Worker args.TrimLeftSpaces() 1815*333d2b36SAndroid Build Coastguard Worker args.TrimRightSpaces() 1816*333d2b36SAndroid Build Coastguard Worker array := ctx.parseMakeString(node, args) 1817*333d2b36SAndroid Build Coastguard Worker if bad, ok := array.(*badExpr); ok { 1818*333d2b36SAndroid Build Coastguard Worker return bad 1819*333d2b36SAndroid Build Coastguard Worker } 1820*333d2b36SAndroid Build Coastguard Worker if array.typ() != starlarkTypeList { 1821*333d2b36SAndroid Build Coastguard Worker array = &callExpr{ 1822*333d2b36SAndroid Build Coastguard Worker name: baseName + ".words", 1823*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{array}, 1824*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeList, 1825*333d2b36SAndroid Build Coastguard Worker } 1826*333d2b36SAndroid Build Coastguard Worker } 1827*333d2b36SAndroid Build Coastguard Worker return &callExpr{ 1828*333d2b36SAndroid Build Coastguard Worker name: "len", 1829*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{array}, 1830*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeInt, 1831*333d2b36SAndroid Build Coastguard Worker } 1832*333d2b36SAndroid Build Coastguard Worker} 1833*333d2b36SAndroid Build Coastguard Worker 1834*333d2b36SAndroid Build Coastguard Workerfunc parseIntegerArguments(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString, expectedArgs int) ([]starlarkExpr, error) { 1835*333d2b36SAndroid Build Coastguard Worker parsedArgs := make([]starlarkExpr, 0) 1836*333d2b36SAndroid Build Coastguard Worker for _, arg := range args.Split(",") { 1837*333d2b36SAndroid Build Coastguard Worker expr := ctx.parseMakeString(node, arg) 1838*333d2b36SAndroid Build Coastguard Worker if expr.typ() == starlarkTypeList { 1839*333d2b36SAndroid Build Coastguard Worker return nil, fmt.Errorf("argument to math argument has type list, which cannot be converted to int") 1840*333d2b36SAndroid Build Coastguard Worker } 1841*333d2b36SAndroid Build Coastguard Worker if s, ok := maybeString(expr); ok { 1842*333d2b36SAndroid Build Coastguard Worker intVal, err := strconv.Atoi(strings.TrimSpace(s)) 1843*333d2b36SAndroid Build Coastguard Worker if err != nil { 1844*333d2b36SAndroid Build Coastguard Worker return nil, err 1845*333d2b36SAndroid Build Coastguard Worker } 1846*333d2b36SAndroid Build Coastguard Worker expr = &intLiteralExpr{literal: intVal} 1847*333d2b36SAndroid Build Coastguard Worker } else if expr.typ() != starlarkTypeInt { 1848*333d2b36SAndroid Build Coastguard Worker expr = &callExpr{ 1849*333d2b36SAndroid Build Coastguard Worker name: "int", 1850*333d2b36SAndroid Build Coastguard Worker args: []starlarkExpr{expr}, 1851*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeInt, 1852*333d2b36SAndroid Build Coastguard Worker } 1853*333d2b36SAndroid Build Coastguard Worker } 1854*333d2b36SAndroid Build Coastguard Worker parsedArgs = append(parsedArgs, expr) 1855*333d2b36SAndroid Build Coastguard Worker } 1856*333d2b36SAndroid Build Coastguard Worker if len(parsedArgs) != expectedArgs { 1857*333d2b36SAndroid Build Coastguard Worker return nil, fmt.Errorf("function should have %d arguments", expectedArgs) 1858*333d2b36SAndroid Build Coastguard Worker } 1859*333d2b36SAndroid Build Coastguard Worker return parsedArgs, nil 1860*333d2b36SAndroid Build Coastguard Worker} 1861*333d2b36SAndroid Build Coastguard Worker 1862*333d2b36SAndroid Build Coastguard Workertype mathComparisonCallParser struct { 1863*333d2b36SAndroid Build Coastguard Worker op string 1864*333d2b36SAndroid Build Coastguard Worker} 1865*333d2b36SAndroid Build Coastguard Worker 1866*333d2b36SAndroid Build Coastguard Workerfunc (p *mathComparisonCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { 1867*333d2b36SAndroid Build Coastguard Worker parsedArgs, err := parseIntegerArguments(ctx, node, args, 2) 1868*333d2b36SAndroid Build Coastguard Worker if err != nil { 1869*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, err.Error()) 1870*333d2b36SAndroid Build Coastguard Worker } 1871*333d2b36SAndroid Build Coastguard Worker return &binaryOpExpr{ 1872*333d2b36SAndroid Build Coastguard Worker left: parsedArgs[0], 1873*333d2b36SAndroid Build Coastguard Worker right: parsedArgs[1], 1874*333d2b36SAndroid Build Coastguard Worker op: p.op, 1875*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeBool, 1876*333d2b36SAndroid Build Coastguard Worker } 1877*333d2b36SAndroid Build Coastguard Worker} 1878*333d2b36SAndroid Build Coastguard Worker 1879*333d2b36SAndroid Build Coastguard Workertype mathMaxOrMinCallParser struct { 1880*333d2b36SAndroid Build Coastguard Worker function string 1881*333d2b36SAndroid Build Coastguard Worker} 1882*333d2b36SAndroid Build Coastguard Worker 1883*333d2b36SAndroid Build Coastguard Workerfunc (p *mathMaxOrMinCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { 1884*333d2b36SAndroid Build Coastguard Worker parsedArgs, err := parseIntegerArguments(ctx, node, args, 2) 1885*333d2b36SAndroid Build Coastguard Worker if err != nil { 1886*333d2b36SAndroid Build Coastguard Worker return ctx.newBadExpr(node, err.Error()) 1887*333d2b36SAndroid Build Coastguard Worker } 1888*333d2b36SAndroid Build Coastguard Worker return &callExpr{ 1889*333d2b36SAndroid Build Coastguard Worker object: nil, 1890*333d2b36SAndroid Build Coastguard Worker name: p.function, 1891*333d2b36SAndroid Build Coastguard Worker args: parsedArgs, 1892*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeInt, 1893*333d2b36SAndroid Build Coastguard Worker } 1894*333d2b36SAndroid Build Coastguard Worker} 1895*333d2b36SAndroid Build Coastguard Worker 1896*333d2b36SAndroid Build Coastguard Workertype evalNodeParser struct{} 1897*333d2b36SAndroid Build Coastguard Worker 1898*333d2b36SAndroid Build Coastguard Workerfunc (p *evalNodeParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) []starlarkNode { 1899*333d2b36SAndroid Build Coastguard Worker parser := mkparser.NewParser("Eval expression", strings.NewReader(args.Dump())) 1900*333d2b36SAndroid Build Coastguard Worker nodes, errs := parser.Parse() 1901*333d2b36SAndroid Build Coastguard Worker if errs != nil { 1902*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{ctx.newBadNode(node, "Unable to parse eval statement")} 1903*333d2b36SAndroid Build Coastguard Worker } 1904*333d2b36SAndroid Build Coastguard Worker 1905*333d2b36SAndroid Build Coastguard Worker if len(nodes) == 0 { 1906*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{} 1907*333d2b36SAndroid Build Coastguard Worker } else if len(nodes) == 1 { 1908*333d2b36SAndroid Build Coastguard Worker // Replace the nodeLocator with one that just returns the location of 1909*333d2b36SAndroid Build Coastguard Worker // the $(eval) node. Otherwise, statements inside an $(eval) will show as 1910*333d2b36SAndroid Build Coastguard Worker // being on line 1 of the file, because they're on line 1 of 1911*333d2b36SAndroid Build Coastguard Worker // strings.NewReader(args.Dump()) 1912*333d2b36SAndroid Build Coastguard Worker oldNodeLocator := ctx.script.nodeLocator 1913*333d2b36SAndroid Build Coastguard Worker ctx.script.nodeLocator = func(pos mkparser.Pos) int { 1914*333d2b36SAndroid Build Coastguard Worker return oldNodeLocator(node.Pos()) 1915*333d2b36SAndroid Build Coastguard Worker } 1916*333d2b36SAndroid Build Coastguard Worker defer func() { 1917*333d2b36SAndroid Build Coastguard Worker ctx.script.nodeLocator = oldNodeLocator 1918*333d2b36SAndroid Build Coastguard Worker }() 1919*333d2b36SAndroid Build Coastguard Worker 1920*333d2b36SAndroid Build Coastguard Worker switch n := nodes[0].(type) { 1921*333d2b36SAndroid Build Coastguard Worker case *mkparser.Assignment: 1922*333d2b36SAndroid Build Coastguard Worker if n.Name.Const() { 1923*333d2b36SAndroid Build Coastguard Worker return ctx.handleAssignment(n) 1924*333d2b36SAndroid Build Coastguard Worker } 1925*333d2b36SAndroid Build Coastguard Worker case *mkparser.Comment: 1926*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{&commentNode{strings.TrimSpace("#" + n.Comment)}} 1927*333d2b36SAndroid Build Coastguard Worker case *mkparser.Directive: 1928*333d2b36SAndroid Build Coastguard Worker if n.Name == "include" || n.Name == "-include" { 1929*333d2b36SAndroid Build Coastguard Worker return ctx.handleInclude(n) 1930*333d2b36SAndroid Build Coastguard Worker } 1931*333d2b36SAndroid Build Coastguard Worker case *mkparser.Variable: 1932*333d2b36SAndroid Build Coastguard Worker // Technically inherit-product(-if-exists) don't need to be put inside 1933*333d2b36SAndroid Build Coastguard Worker // an eval, but some makefiles do it, presumably because they copy+pasted 1934*333d2b36SAndroid Build Coastguard Worker // from a $(eval include ...) 1935*333d2b36SAndroid Build Coastguard Worker if name, _, ok := ctx.maybeParseFunctionCall(n, n.Name); ok { 1936*333d2b36SAndroid Build Coastguard Worker if name == "inherit-product" || name == "inherit-product-if-exists" { 1937*333d2b36SAndroid Build Coastguard Worker return ctx.handleVariable(n) 1938*333d2b36SAndroid Build Coastguard Worker } 1939*333d2b36SAndroid Build Coastguard Worker } 1940*333d2b36SAndroid Build Coastguard Worker } 1941*333d2b36SAndroid Build Coastguard Worker } 1942*333d2b36SAndroid Build Coastguard Worker 1943*333d2b36SAndroid Build Coastguard Worker return []starlarkNode{ctx.newBadNode(node, "Eval expression too complex; only assignments, comments, includes, and inherit-products are supported")} 1944*333d2b36SAndroid Build Coastguard Worker} 1945*333d2b36SAndroid Build Coastguard Worker 1946*333d2b36SAndroid Build Coastguard Workertype lowerUpperParser struct { 1947*333d2b36SAndroid Build Coastguard Worker isUpper bool 1948*333d2b36SAndroid Build Coastguard Worker} 1949*333d2b36SAndroid Build Coastguard Worker 1950*333d2b36SAndroid Build Coastguard Workerfunc (p *lowerUpperParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { 1951*333d2b36SAndroid Build Coastguard Worker fn := "lower" 1952*333d2b36SAndroid Build Coastguard Worker if p.isUpper { 1953*333d2b36SAndroid Build Coastguard Worker fn = "upper" 1954*333d2b36SAndroid Build Coastguard Worker } 1955*333d2b36SAndroid Build Coastguard Worker arg := ctx.parseMakeString(node, args) 1956*333d2b36SAndroid Build Coastguard Worker 1957*333d2b36SAndroid Build Coastguard Worker return &callExpr{ 1958*333d2b36SAndroid Build Coastguard Worker object: arg, 1959*333d2b36SAndroid Build Coastguard Worker name: fn, 1960*333d2b36SAndroid Build Coastguard Worker returnType: starlarkTypeString, 1961*333d2b36SAndroid Build Coastguard Worker } 1962*333d2b36SAndroid Build Coastguard Worker} 1963*333d2b36SAndroid Build Coastguard Worker 1964*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) parseMakeString(node mkparser.Node, mk *mkparser.MakeString) starlarkExpr { 1965*333d2b36SAndroid Build Coastguard Worker if mk.Const() { 1966*333d2b36SAndroid Build Coastguard Worker return &stringLiteralExpr{mk.Dump()} 1967*333d2b36SAndroid Build Coastguard Worker } 1968*333d2b36SAndroid Build Coastguard Worker if mkRef, ok := mk.SingleVariable(); ok { 1969*333d2b36SAndroid Build Coastguard Worker return ctx.parseReference(node, mkRef) 1970*333d2b36SAndroid Build Coastguard Worker } 1971*333d2b36SAndroid Build Coastguard Worker // If we reached here, it's neither string literal nor a simple variable, 1972*333d2b36SAndroid Build Coastguard Worker // we need a full-blown interpolation node that will generate 1973*333d2b36SAndroid Build Coastguard Worker // "a%b%c" % (X, Y) for a$(X)b$(Y)c 1974*333d2b36SAndroid Build Coastguard Worker parts := make([]starlarkExpr, len(mk.Variables)+len(mk.Strings)) 1975*333d2b36SAndroid Build Coastguard Worker for i := 0; i < len(parts); i++ { 1976*333d2b36SAndroid Build Coastguard Worker if i%2 == 0 { 1977*333d2b36SAndroid Build Coastguard Worker parts[i] = &stringLiteralExpr{literal: mk.Strings[i/2]} 1978*333d2b36SAndroid Build Coastguard Worker } else { 1979*333d2b36SAndroid Build Coastguard Worker parts[i] = ctx.parseReference(node, mk.Variables[i/2].Name) 1980*333d2b36SAndroid Build Coastguard Worker if x, ok := parts[i].(*badExpr); ok { 1981*333d2b36SAndroid Build Coastguard Worker return x 1982*333d2b36SAndroid Build Coastguard Worker } 1983*333d2b36SAndroid Build Coastguard Worker } 1984*333d2b36SAndroid Build Coastguard Worker } 1985*333d2b36SAndroid Build Coastguard Worker return NewInterpolateExpr(parts) 1986*333d2b36SAndroid Build Coastguard Worker} 1987*333d2b36SAndroid Build Coastguard Worker 1988*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) parseNodeMakeString(node mkparser.Node, mk *mkparser.MakeString) []starlarkNode { 1989*333d2b36SAndroid Build Coastguard Worker // Discard any constant values in the make string, as they would be top level 1990*333d2b36SAndroid Build Coastguard Worker // string literals and do nothing. 1991*333d2b36SAndroid Build Coastguard Worker result := make([]starlarkNode, 0, len(mk.Variables)) 1992*333d2b36SAndroid Build Coastguard Worker for i := range mk.Variables { 1993*333d2b36SAndroid Build Coastguard Worker result = append(result, ctx.handleVariable(&mk.Variables[i])...) 1994*333d2b36SAndroid Build Coastguard Worker } 1995*333d2b36SAndroid Build Coastguard Worker return result 1996*333d2b36SAndroid Build Coastguard Worker} 1997*333d2b36SAndroid Build Coastguard Worker 1998*333d2b36SAndroid Build Coastguard Worker// Handles the statements whose treatment is the same in all contexts: comment, 1999*333d2b36SAndroid Build Coastguard Worker// assignment, variable (which is a macro call in reality) and all constructs that 2000*333d2b36SAndroid Build Coastguard Worker// do not handle in any context ('define directive and any unrecognized stuff). 2001*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) handleSimpleStatement(node mkparser.Node) []starlarkNode { 2002*333d2b36SAndroid Build Coastguard Worker var result []starlarkNode 2003*333d2b36SAndroid Build Coastguard Worker switch x := node.(type) { 2004*333d2b36SAndroid Build Coastguard Worker case *mkparser.Comment: 2005*333d2b36SAndroid Build Coastguard Worker if n, handled := ctx.maybeHandleAnnotation(x); handled && n != nil { 2006*333d2b36SAndroid Build Coastguard Worker result = []starlarkNode{n} 2007*333d2b36SAndroid Build Coastguard Worker } else if !handled { 2008*333d2b36SAndroid Build Coastguard Worker result = []starlarkNode{&commentNode{strings.TrimSpace("#" + x.Comment)}} 2009*333d2b36SAndroid Build Coastguard Worker } 2010*333d2b36SAndroid Build Coastguard Worker case *mkparser.Assignment: 2011*333d2b36SAndroid Build Coastguard Worker result = ctx.handleAssignment(x) 2012*333d2b36SAndroid Build Coastguard Worker case *mkparser.Variable: 2013*333d2b36SAndroid Build Coastguard Worker result = ctx.handleVariable(x) 2014*333d2b36SAndroid Build Coastguard Worker case *mkparser.Directive: 2015*333d2b36SAndroid Build Coastguard Worker switch x.Name { 2016*333d2b36SAndroid Build Coastguard Worker case "define": 2017*333d2b36SAndroid Build Coastguard Worker if res := ctx.maybeHandleDefine(x); res != nil { 2018*333d2b36SAndroid Build Coastguard Worker result = []starlarkNode{res} 2019*333d2b36SAndroid Build Coastguard Worker } 2020*333d2b36SAndroid Build Coastguard Worker case "include", "-include": 2021*333d2b36SAndroid Build Coastguard Worker result = ctx.handleInclude(x) 2022*333d2b36SAndroid Build Coastguard Worker case "ifeq", "ifneq", "ifdef", "ifndef": 2023*333d2b36SAndroid Build Coastguard Worker result = []starlarkNode{ctx.handleIfBlock(x)} 2024*333d2b36SAndroid Build Coastguard Worker default: 2025*333d2b36SAndroid Build Coastguard Worker result = []starlarkNode{ctx.newBadNode(x, "unexpected directive %s", x.Name)} 2026*333d2b36SAndroid Build Coastguard Worker } 2027*333d2b36SAndroid Build Coastguard Worker default: 2028*333d2b36SAndroid Build Coastguard Worker result = []starlarkNode{ctx.newBadNode(x, "unsupported line %s", strings.ReplaceAll(x.Dump(), "\n", "\n#"))} 2029*333d2b36SAndroid Build Coastguard Worker } 2030*333d2b36SAndroid Build Coastguard Worker 2031*333d2b36SAndroid Build Coastguard Worker // Clear the includeTops after each non-comment statement 2032*333d2b36SAndroid Build Coastguard Worker // so that include annotations placed on certain statements don't apply 2033*333d2b36SAndroid Build Coastguard Worker // globally for the rest of the makefile was well. 2034*333d2b36SAndroid Build Coastguard Worker if _, wasComment := node.(*mkparser.Comment); !wasComment { 2035*333d2b36SAndroid Build Coastguard Worker ctx.atTopOfMakefile = false 2036*333d2b36SAndroid Build Coastguard Worker ctx.includeTops = []string{} 2037*333d2b36SAndroid Build Coastguard Worker } 2038*333d2b36SAndroid Build Coastguard Worker 2039*333d2b36SAndroid Build Coastguard Worker if result == nil { 2040*333d2b36SAndroid Build Coastguard Worker result = []starlarkNode{} 2041*333d2b36SAndroid Build Coastguard Worker } 2042*333d2b36SAndroid Build Coastguard Worker 2043*333d2b36SAndroid Build Coastguard Worker return result 2044*333d2b36SAndroid Build Coastguard Worker} 2045*333d2b36SAndroid Build Coastguard Worker 2046*333d2b36SAndroid Build Coastguard Worker// The types allowed in a type_hint 2047*333d2b36SAndroid Build Coastguard Workervar typeHintMap = map[string]starlarkType{ 2048*333d2b36SAndroid Build Coastguard Worker "string": starlarkTypeString, 2049*333d2b36SAndroid Build Coastguard Worker "list": starlarkTypeList, 2050*333d2b36SAndroid Build Coastguard Worker} 2051*333d2b36SAndroid Build Coastguard Worker 2052*333d2b36SAndroid Build Coastguard Worker// Processes annotation. An annotation is a comment that starts with #RBC# and provides 2053*333d2b36SAndroid Build Coastguard Worker// a conversion hint -- say, where to look for the dynamically calculated inherit/include 2054*333d2b36SAndroid Build Coastguard Worker// paths. Returns true if the comment was a successfully-handled annotation. 2055*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) maybeHandleAnnotation(cnode *mkparser.Comment) (starlarkNode, bool) { 2056*333d2b36SAndroid Build Coastguard Worker maybeTrim := func(s, prefix string) (string, bool) { 2057*333d2b36SAndroid Build Coastguard Worker if strings.HasPrefix(s, prefix) { 2058*333d2b36SAndroid Build Coastguard Worker return strings.TrimSpace(strings.TrimPrefix(s, prefix)), true 2059*333d2b36SAndroid Build Coastguard Worker } 2060*333d2b36SAndroid Build Coastguard Worker return s, false 2061*333d2b36SAndroid Build Coastguard Worker } 2062*333d2b36SAndroid Build Coastguard Worker annotation, ok := maybeTrim(cnode.Comment, annotationCommentPrefix) 2063*333d2b36SAndroid Build Coastguard Worker if !ok { 2064*333d2b36SAndroid Build Coastguard Worker return nil, false 2065*333d2b36SAndroid Build Coastguard Worker } 2066*333d2b36SAndroid Build Coastguard Worker if p, ok := maybeTrim(annotation, "include_top"); ok { 2067*333d2b36SAndroid Build Coastguard Worker // Don't allow duplicate include tops, because then we will generate 2068*333d2b36SAndroid Build Coastguard Worker // invalid starlark code. (duplicate keys in the _entry dictionary) 2069*333d2b36SAndroid Build Coastguard Worker for _, top := range ctx.includeTops { 2070*333d2b36SAndroid Build Coastguard Worker if top == p { 2071*333d2b36SAndroid Build Coastguard Worker return nil, true 2072*333d2b36SAndroid Build Coastguard Worker } 2073*333d2b36SAndroid Build Coastguard Worker } 2074*333d2b36SAndroid Build Coastguard Worker ctx.includeTops = append(ctx.includeTops, p) 2075*333d2b36SAndroid Build Coastguard Worker return nil, true 2076*333d2b36SAndroid Build Coastguard Worker } else if p, ok := maybeTrim(annotation, "type_hint"); ok { 2077*333d2b36SAndroid Build Coastguard Worker // Type hints must come at the beginning the file, to avoid confusion 2078*333d2b36SAndroid Build Coastguard Worker // if a type hint was specified later and thus only takes effect for half 2079*333d2b36SAndroid Build Coastguard Worker // of the file. 2080*333d2b36SAndroid Build Coastguard Worker if !ctx.atTopOfMakefile { 2081*333d2b36SAndroid Build Coastguard Worker return ctx.newBadNode(cnode, "type_hint annotations must come before the first Makefile statement"), true 2082*333d2b36SAndroid Build Coastguard Worker } 2083*333d2b36SAndroid Build Coastguard Worker 2084*333d2b36SAndroid Build Coastguard Worker parts := strings.Fields(p) 2085*333d2b36SAndroid Build Coastguard Worker if len(parts) <= 1 { 2086*333d2b36SAndroid Build Coastguard Worker return ctx.newBadNode(cnode, "Invalid type_hint annotation: %s. Must be a variable type followed by a list of variables of that type", p), true 2087*333d2b36SAndroid Build Coastguard Worker } 2088*333d2b36SAndroid Build Coastguard Worker 2089*333d2b36SAndroid Build Coastguard Worker var varType starlarkType 2090*333d2b36SAndroid Build Coastguard Worker if varType, ok = typeHintMap[parts[0]]; !ok { 2091*333d2b36SAndroid Build Coastguard Worker varType = starlarkTypeUnknown 2092*333d2b36SAndroid Build Coastguard Worker } 2093*333d2b36SAndroid Build Coastguard Worker if varType == starlarkTypeUnknown { 2094*333d2b36SAndroid Build Coastguard Worker return ctx.newBadNode(cnode, "Invalid type_hint annotation. Only list/string types are accepted, found %s", parts[0]), true 2095*333d2b36SAndroid Build Coastguard Worker } 2096*333d2b36SAndroid Build Coastguard Worker 2097*333d2b36SAndroid Build Coastguard Worker for _, name := range parts[1:] { 2098*333d2b36SAndroid Build Coastguard Worker // Don't allow duplicate type hints 2099*333d2b36SAndroid Build Coastguard Worker if _, ok := ctx.typeHints[name]; ok { 2100*333d2b36SAndroid Build Coastguard Worker return ctx.newBadNode(cnode, "Duplicate type hint for variable %s", name), true 2101*333d2b36SAndroid Build Coastguard Worker } 2102*333d2b36SAndroid Build Coastguard Worker ctx.typeHints[name] = varType 2103*333d2b36SAndroid Build Coastguard Worker } 2104*333d2b36SAndroid Build Coastguard Worker return nil, true 2105*333d2b36SAndroid Build Coastguard Worker } 2106*333d2b36SAndroid Build Coastguard Worker return ctx.newBadNode(cnode, "unsupported annotation %s", cnode.Comment), true 2107*333d2b36SAndroid Build Coastguard Worker} 2108*333d2b36SAndroid Build Coastguard Worker 2109*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) loadedModulePath(path string) string { 2110*333d2b36SAndroid Build Coastguard Worker // During the transition to Roboleaf some of the product configuration files 2111*333d2b36SAndroid Build Coastguard Worker // will be converted and checked in while the others will be generated on the fly 2112*333d2b36SAndroid Build Coastguard Worker // and run. The runner (rbcrun application) accommodates this by allowing three 2113*333d2b36SAndroid Build Coastguard Worker // different ways to specify the loaded file location: 2114*333d2b36SAndroid Build Coastguard Worker // 1) load(":<file>",...) loads <file> from the same directory 2115*333d2b36SAndroid Build Coastguard Worker // 2) load("//path/relative/to/source/root:<file>", ...) loads <file> source tree 2116*333d2b36SAndroid Build Coastguard Worker // 3) load("/absolute/path/to/<file> absolute path 2117*333d2b36SAndroid Build Coastguard Worker // If the file being generated and the file it wants to load are in the same directory, 2118*333d2b36SAndroid Build Coastguard Worker // generate option 1. 2119*333d2b36SAndroid Build Coastguard Worker // Otherwise, if output directory is not specified, generate 2) 2120*333d2b36SAndroid Build Coastguard Worker // Finally, if output directory has been specified and the file being generated and 2121*333d2b36SAndroid Build Coastguard Worker // the file it wants to load from are in the different directories, generate 2) or 3): 2122*333d2b36SAndroid Build Coastguard Worker // * if the file being loaded exists in the source tree, generate 2) 2123*333d2b36SAndroid Build Coastguard Worker // * otherwise, generate 3) 2124*333d2b36SAndroid Build Coastguard Worker // Finally, figure out the loaded module path and name and create a node for it 2125*333d2b36SAndroid Build Coastguard Worker loadedModuleDir := filepath.Dir(path) 2126*333d2b36SAndroid Build Coastguard Worker base := filepath.Base(path) 2127*333d2b36SAndroid Build Coastguard Worker loadedModuleName := strings.TrimSuffix(base, filepath.Ext(base)) + ctx.outputSuffix 2128*333d2b36SAndroid Build Coastguard Worker if loadedModuleDir == filepath.Dir(ctx.script.mkFile) { 2129*333d2b36SAndroid Build Coastguard Worker return ":" + loadedModuleName 2130*333d2b36SAndroid Build Coastguard Worker } 2131*333d2b36SAndroid Build Coastguard Worker if ctx.outputDir == "" { 2132*333d2b36SAndroid Build Coastguard Worker return fmt.Sprintf("//%s:%s", loadedModuleDir, loadedModuleName) 2133*333d2b36SAndroid Build Coastguard Worker } 2134*333d2b36SAndroid Build Coastguard Worker if _, err := os.Stat(filepath.Join(loadedModuleDir, loadedModuleName)); err == nil { 2135*333d2b36SAndroid Build Coastguard Worker return fmt.Sprintf("//%s:%s", loadedModuleDir, loadedModuleName) 2136*333d2b36SAndroid Build Coastguard Worker } 2137*333d2b36SAndroid Build Coastguard Worker return filepath.Join(ctx.outputDir, loadedModuleDir, loadedModuleName) 2138*333d2b36SAndroid Build Coastguard Worker} 2139*333d2b36SAndroid Build Coastguard Worker 2140*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) addSoongNamespace(ns string) { 2141*333d2b36SAndroid Build Coastguard Worker if _, ok := ctx.soongNamespaces[ns]; ok { 2142*333d2b36SAndroid Build Coastguard Worker return 2143*333d2b36SAndroid Build Coastguard Worker } 2144*333d2b36SAndroid Build Coastguard Worker ctx.soongNamespaces[ns] = make(map[string]bool) 2145*333d2b36SAndroid Build Coastguard Worker} 2146*333d2b36SAndroid Build Coastguard Worker 2147*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) hasSoongNamespace(name string) bool { 2148*333d2b36SAndroid Build Coastguard Worker _, ok := ctx.soongNamespaces[name] 2149*333d2b36SAndroid Build Coastguard Worker return ok 2150*333d2b36SAndroid Build Coastguard Worker} 2151*333d2b36SAndroid Build Coastguard Worker 2152*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) updateSoongNamespace(replace bool, namespaceName string, varNames []string) { 2153*333d2b36SAndroid Build Coastguard Worker ctx.addSoongNamespace(namespaceName) 2154*333d2b36SAndroid Build Coastguard Worker vars := ctx.soongNamespaces[namespaceName] 2155*333d2b36SAndroid Build Coastguard Worker if replace { 2156*333d2b36SAndroid Build Coastguard Worker vars = make(map[string]bool) 2157*333d2b36SAndroid Build Coastguard Worker ctx.soongNamespaces[namespaceName] = vars 2158*333d2b36SAndroid Build Coastguard Worker } 2159*333d2b36SAndroid Build Coastguard Worker for _, v := range varNames { 2160*333d2b36SAndroid Build Coastguard Worker vars[v] = true 2161*333d2b36SAndroid Build Coastguard Worker } 2162*333d2b36SAndroid Build Coastguard Worker} 2163*333d2b36SAndroid Build Coastguard Worker 2164*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) hasNamespaceVar(namespaceName string, varName string) bool { 2165*333d2b36SAndroid Build Coastguard Worker vars, ok := ctx.soongNamespaces[namespaceName] 2166*333d2b36SAndroid Build Coastguard Worker if ok { 2167*333d2b36SAndroid Build Coastguard Worker _, ok = vars[varName] 2168*333d2b36SAndroid Build Coastguard Worker } 2169*333d2b36SAndroid Build Coastguard Worker return ok 2170*333d2b36SAndroid Build Coastguard Worker} 2171*333d2b36SAndroid Build Coastguard Worker 2172*333d2b36SAndroid Build Coastguard Workerfunc (ctx *parseContext) errorLocation(node mkparser.Node) ErrorLocation { 2173*333d2b36SAndroid Build Coastguard Worker return ErrorLocation{ctx.script.mkFile, ctx.script.nodeLocator(node.Pos())} 2174*333d2b36SAndroid Build Coastguard Worker} 2175*333d2b36SAndroid Build Coastguard Worker 2176*333d2b36SAndroid Build Coastguard Workerfunc (ss *StarlarkScript) String() string { 2177*333d2b36SAndroid Build Coastguard Worker return NewGenerateContext(ss).emit() 2178*333d2b36SAndroid Build Coastguard Worker} 2179*333d2b36SAndroid Build Coastguard Worker 2180*333d2b36SAndroid Build Coastguard Workerfunc (ss *StarlarkScript) SubConfigFiles() []string { 2181*333d2b36SAndroid Build Coastguard Worker 2182*333d2b36SAndroid Build Coastguard Worker var subs []string 2183*333d2b36SAndroid Build Coastguard Worker for _, src := range ss.inherited { 2184*333d2b36SAndroid Build Coastguard Worker subs = append(subs, src.originalPath) 2185*333d2b36SAndroid Build Coastguard Worker } 2186*333d2b36SAndroid Build Coastguard Worker return subs 2187*333d2b36SAndroid Build Coastguard Worker} 2188*333d2b36SAndroid Build Coastguard Worker 2189*333d2b36SAndroid Build Coastguard Workerfunc (ss *StarlarkScript) HasErrors() bool { 2190*333d2b36SAndroid Build Coastguard Worker return ss.hasErrors 2191*333d2b36SAndroid Build Coastguard Worker} 2192*333d2b36SAndroid Build Coastguard Worker 2193*333d2b36SAndroid Build Coastguard Worker// Convert reads and parses a makefile. If successful, parsed tree 2194*333d2b36SAndroid Build Coastguard Worker// is returned and then can be passed to String() to get the generated 2195*333d2b36SAndroid Build Coastguard Worker// Starlark file. 2196*333d2b36SAndroid Build Coastguard Workerfunc Convert(req Request) (*StarlarkScript, error) { 2197*333d2b36SAndroid Build Coastguard Worker reader := req.Reader 2198*333d2b36SAndroid Build Coastguard Worker if reader == nil { 2199*333d2b36SAndroid Build Coastguard Worker mkContents, err := ioutil.ReadFile(req.MkFile) 2200*333d2b36SAndroid Build Coastguard Worker if err != nil { 2201*333d2b36SAndroid Build Coastguard Worker return nil, err 2202*333d2b36SAndroid Build Coastguard Worker } 2203*333d2b36SAndroid Build Coastguard Worker reader = bytes.NewBuffer(mkContents) 2204*333d2b36SAndroid Build Coastguard Worker } 2205*333d2b36SAndroid Build Coastguard Worker parser := mkparser.NewParser(req.MkFile, reader) 2206*333d2b36SAndroid Build Coastguard Worker nodes, errs := parser.Parse() 2207*333d2b36SAndroid Build Coastguard Worker if len(errs) > 0 { 2208*333d2b36SAndroid Build Coastguard Worker for _, e := range errs { 2209*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, "ERROR:", e) 2210*333d2b36SAndroid Build Coastguard Worker } 2211*333d2b36SAndroid Build Coastguard Worker return nil, fmt.Errorf("bad makefile %s", req.MkFile) 2212*333d2b36SAndroid Build Coastguard Worker } 2213*333d2b36SAndroid Build Coastguard Worker starScript := &StarlarkScript{ 2214*333d2b36SAndroid Build Coastguard Worker moduleName: moduleNameForFile(req.MkFile), 2215*333d2b36SAndroid Build Coastguard Worker mkFile: req.MkFile, 2216*333d2b36SAndroid Build Coastguard Worker traceCalls: req.TraceCalls, 2217*333d2b36SAndroid Build Coastguard Worker sourceFS: req.SourceFS, 2218*333d2b36SAndroid Build Coastguard Worker makefileFinder: req.MakefileFinder, 2219*333d2b36SAndroid Build Coastguard Worker nodeLocator: func(pos mkparser.Pos) int { return parser.Unpack(pos).Line }, 2220*333d2b36SAndroid Build Coastguard Worker nodes: make([]starlarkNode, 0), 2221*333d2b36SAndroid Build Coastguard Worker } 2222*333d2b36SAndroid Build Coastguard Worker ctx := newParseContext(starScript, nodes) 2223*333d2b36SAndroid Build Coastguard Worker ctx.outputSuffix = req.OutputSuffix 2224*333d2b36SAndroid Build Coastguard Worker ctx.outputDir = req.OutputDir 2225*333d2b36SAndroid Build Coastguard Worker ctx.errorLogger = req.ErrorLogger 2226*333d2b36SAndroid Build Coastguard Worker if len(req.TracedVariables) > 0 { 2227*333d2b36SAndroid Build Coastguard Worker ctx.tracedVariables = make(map[string]bool) 2228*333d2b36SAndroid Build Coastguard Worker for _, v := range req.TracedVariables { 2229*333d2b36SAndroid Build Coastguard Worker ctx.tracedVariables[v] = true 2230*333d2b36SAndroid Build Coastguard Worker } 2231*333d2b36SAndroid Build Coastguard Worker } 2232*333d2b36SAndroid Build Coastguard Worker for ctx.hasNodes() && ctx.fatalError == nil { 2233*333d2b36SAndroid Build Coastguard Worker starScript.nodes = append(starScript.nodes, ctx.handleSimpleStatement(ctx.getNode())...) 2234*333d2b36SAndroid Build Coastguard Worker } 2235*333d2b36SAndroid Build Coastguard Worker if ctx.fatalError != nil { 2236*333d2b36SAndroid Build Coastguard Worker return nil, ctx.fatalError 2237*333d2b36SAndroid Build Coastguard Worker } 2238*333d2b36SAndroid Build Coastguard Worker return starScript, nil 2239*333d2b36SAndroid Build Coastguard Worker} 2240*333d2b36SAndroid Build Coastguard Worker 2241*333d2b36SAndroid Build Coastguard Workerfunc Launcher(mainModuleUri, inputVariablesUri, mainModuleName string) string { 2242*333d2b36SAndroid Build Coastguard Worker var buf bytes.Buffer 2243*333d2b36SAndroid Build Coastguard Worker fmt.Fprintf(&buf, "load(%q, %q)\n", baseUri, baseName) 2244*333d2b36SAndroid Build Coastguard Worker fmt.Fprintf(&buf, "load(%q, input_variables_init = \"init\")\n", inputVariablesUri) 2245*333d2b36SAndroid Build Coastguard Worker fmt.Fprintf(&buf, "load(%q, \"init\")\n", mainModuleUri) 2246*333d2b36SAndroid Build Coastguard Worker fmt.Fprintf(&buf, "%s(%s(%q, init, input_variables_init))\n", cfnPrintVars, cfnMain, mainModuleName) 2247*333d2b36SAndroid Build Coastguard Worker return buf.String() 2248*333d2b36SAndroid Build Coastguard Worker} 2249*333d2b36SAndroid Build Coastguard Worker 2250*333d2b36SAndroid Build Coastguard Workerfunc BoardLauncher(mainModuleUri string, inputVariablesUri string) string { 2251*333d2b36SAndroid Build Coastguard Worker var buf bytes.Buffer 2252*333d2b36SAndroid Build Coastguard Worker fmt.Fprintf(&buf, "load(%q, %q)\n", baseUri, baseName) 2253*333d2b36SAndroid Build Coastguard Worker fmt.Fprintf(&buf, "load(%q, \"init\")\n", mainModuleUri) 2254*333d2b36SAndroid Build Coastguard Worker fmt.Fprintf(&buf, "load(%q, input_variables_init = \"init\")\n", inputVariablesUri) 2255*333d2b36SAndroid Build Coastguard Worker fmt.Fprintf(&buf, "%s(%s(init, input_variables_init))\n", cfnPrintVars, cfnBoardMain) 2256*333d2b36SAndroid Build Coastguard Worker return buf.String() 2257*333d2b36SAndroid Build Coastguard Worker} 2258*333d2b36SAndroid Build Coastguard Worker 2259*333d2b36SAndroid Build Coastguard Workerfunc MakePath2ModuleName(mkPath string) string { 2260*333d2b36SAndroid Build Coastguard Worker return strings.TrimSuffix(mkPath, filepath.Ext(mkPath)) 2261*333d2b36SAndroid Build Coastguard Worker} 2262