1// Copyright 2021 Google LLC 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package mk2rbc 16 17import ( 18 "bytes" 19 "fmt" 20 "io/ioutil" 21 "os" 22 "path/filepath" 23 "strings" 24 25 mkparser "android/soong/androidmk/parser" 26) 27 28// Implements mkparser.Scope, to be used by mkparser.Value.Value() 29type localDirEval struct { 30 localDir string 31 hasErrors bool 32} 33 34func (l *localDirEval) Get(name string) string { 35 if name == "LOCAL_DIR" { 36 return l.localDir 37 } 38 l.hasErrors = true 39 return fmt.Sprintf("$(%s)", name) 40} 41 42func (l *localDirEval) Set(_, _ string) { 43} 44 45func (l *localDirEval) Call(_ string, _ []string) []string { 46 l.hasErrors = true 47 return []string{"$(call ...)"} 48} 49 50func (l *localDirEval) SetFunc(_ string, _ func([]string) []string) { 51} 52 53// UpdateProductConfigMap builds product configuration map. 54// The product configuration map maps a product name (i.e., the value of the 55// TARGET_PRODUCT variable) to the top-level configuration file. 56// In the Android's Make-based build machinery, the equivalent of the 57// product configuration map is $(PRODUCT_MAKEFILES), which is the list 58// of <product>:<configuration makefile> pairs (if <product>: is missing, 59// <product> is the basename of the configuration makefile). 60// UpdateProductConfigMap emulates this build logic by processing the 61// assignments to PRODUCT_MAKEFILES in the file passed to it. 62func UpdateProductConfigMap(configMap map[string]string, configMakefile string) error { 63 contents, err := ioutil.ReadFile(configMakefile) 64 if err != nil { 65 return err 66 } 67 parser := mkparser.NewParser(configMakefile, bytes.NewBuffer(contents)) 68 nodes, errs := parser.Parse() 69 if len(errs) > 0 { 70 for _, e := range errs { 71 fmt.Fprintln(os.Stderr, "ERROR:", e) 72 } 73 return fmt.Errorf("cannot parse %s", configMakefile) 74 } 75 76 ldEval := &localDirEval{localDir: filepath.Dir(configMakefile)} 77 78 for _, node := range nodes { 79 // We are interested in assignments to 'PRODUCT_MAKEFILES' 80 asgn, ok := node.(*mkparser.Assignment) 81 if !ok { 82 continue 83 } 84 if !(asgn.Name.Const() && asgn.Name.Strings[0] == "PRODUCT_MAKEFILES") { 85 continue 86 } 87 88 // Resolve the references to $(LOCAL_DIR) in $(PRODUCT_MAKEFILES). 89 ldEval.hasErrors = false 90 value := asgn.Value.Value(ldEval) 91 if ldEval.hasErrors { 92 return fmt.Errorf("cannot evaluate %s", asgn.Value.Dump()) 93 } 94 // Each item is either <product>:<configuration makefile>, or 95 // just <configuration makefile> 96 for _, token := range strings.Fields(value) { 97 var product, config_path string 98 if n := strings.Index(token, ":"); n >= 0 { 99 product = token[0:n] 100 config_path = token[n+1:] 101 } else { 102 config_path = token 103 product = filepath.Base(config_path) 104 product = strings.TrimSuffix(product, filepath.Ext(product)) 105 } 106 configMap[product] = config_path 107 } 108 } 109 return nil 110} 111