1*90c8c64dSAndroid Build Coastguard Worker// Copyright (C) 2020 The Android Open Source Project 2*90c8c64dSAndroid Build Coastguard Worker// 3*90c8c64dSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*90c8c64dSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*90c8c64dSAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*90c8c64dSAndroid Build Coastguard Worker// 7*90c8c64dSAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*90c8c64dSAndroid Build Coastguard Worker// 9*90c8c64dSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*90c8c64dSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*90c8c64dSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*90c8c64dSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*90c8c64dSAndroid Build Coastguard Worker// limitations under the License. 14*90c8c64dSAndroid Build Coastguard Worker 15*90c8c64dSAndroid Build Coastguard Workerpackage main 16*90c8c64dSAndroid Build Coastguard Worker 17*90c8c64dSAndroid Build Coastguard Workerimport ( 18*90c8c64dSAndroid Build Coastguard Worker "encoding/json" 19*90c8c64dSAndroid Build Coastguard Worker "flag" 20*90c8c64dSAndroid Build Coastguard Worker "fmt" 21*90c8c64dSAndroid Build Coastguard Worker "io" 22*90c8c64dSAndroid Build Coastguard Worker "os" 23*90c8c64dSAndroid Build Coastguard Worker "strings" 24*90c8c64dSAndroid Build Coastguard Worker 25*90c8c64dSAndroid Build Coastguard Worker "github.com/google/blueprint/parser" 26*90c8c64dSAndroid Build Coastguard Worker) 27*90c8c64dSAndroid Build Coastguard Worker 28*90c8c64dSAndroid Build Coastguard Workertype FlatModule struct { 29*90c8c64dSAndroid Build Coastguard Worker Type string 30*90c8c64dSAndroid Build Coastguard Worker Name string 31*90c8c64dSAndroid Build Coastguard Worker PropertyMap map[string]interface{} 32*90c8c64dSAndroid Build Coastguard Worker} 33*90c8c64dSAndroid Build Coastguard Worker 34*90c8c64dSAndroid Build Coastguard Workerfunc expandScalarTypeExpression(value parser.Expression) (scalar interface{}, isScalar bool) { 35*90c8c64dSAndroid Build Coastguard Worker if s, ok := value.(*parser.Bool); ok { 36*90c8c64dSAndroid Build Coastguard Worker return s.Value, true 37*90c8c64dSAndroid Build Coastguard Worker } else if s, ok := value.(*parser.String); ok { 38*90c8c64dSAndroid Build Coastguard Worker return s.Value, true 39*90c8c64dSAndroid Build Coastguard Worker } else if s, ok := value.(*parser.Int64); ok { 40*90c8c64dSAndroid Build Coastguard Worker return s.Value, true 41*90c8c64dSAndroid Build Coastguard Worker } 42*90c8c64dSAndroid Build Coastguard Worker return nil, false 43*90c8c64dSAndroid Build Coastguard Worker} 44*90c8c64dSAndroid Build Coastguard Worker 45*90c8c64dSAndroid Build Coastguard Workerfunc populatePropertyMap(propMap map[string]interface{}, prefix string, m *parser.Map) { 46*90c8c64dSAndroid Build Coastguard Worker for _, prop := range m.Properties { 47*90c8c64dSAndroid Build Coastguard Worker name := prop.Name 48*90c8c64dSAndroid Build Coastguard Worker if prefix != "" { 49*90c8c64dSAndroid Build Coastguard Worker name = prefix + "." + name 50*90c8c64dSAndroid Build Coastguard Worker } 51*90c8c64dSAndroid Build Coastguard Worker value := prop.Value 52*90c8c64dSAndroid Build Coastguard Worker if s, isScalar := expandScalarTypeExpression(value); isScalar { 53*90c8c64dSAndroid Build Coastguard Worker propMap[name] = s 54*90c8c64dSAndroid Build Coastguard Worker } else if list, ok := value.(*parser.List); ok { 55*90c8c64dSAndroid Build Coastguard Worker var l []interface{} 56*90c8c64dSAndroid Build Coastguard Worker for _, v := range list.Values { 57*90c8c64dSAndroid Build Coastguard Worker if s, isScalar := expandScalarTypeExpression(v); isScalar { 58*90c8c64dSAndroid Build Coastguard Worker l = append(l, s) 59*90c8c64dSAndroid Build Coastguard Worker } 60*90c8c64dSAndroid Build Coastguard Worker } 61*90c8c64dSAndroid Build Coastguard Worker propMap[name] = l 62*90c8c64dSAndroid Build Coastguard Worker } else if mm, ok := value.(*parser.Map); ok { 63*90c8c64dSAndroid Build Coastguard Worker populatePropertyMap(propMap, name, mm) 64*90c8c64dSAndroid Build Coastguard Worker } 65*90c8c64dSAndroid Build Coastguard Worker } 66*90c8c64dSAndroid Build Coastguard Worker} 67*90c8c64dSAndroid Build Coastguard Worker 68*90c8c64dSAndroid Build Coastguard Workervar anonymousModuleCount int 69*90c8c64dSAndroid Build Coastguard Worker 70*90c8c64dSAndroid Build Coastguard Workerfunc flattenModule(module *parser.Module) (flattened FlatModule) { 71*90c8c64dSAndroid Build Coastguard Worker flattened.Type = module.Type 72*90c8c64dSAndroid Build Coastguard Worker if prop, found := module.GetProperty("name"); found { 73*90c8c64dSAndroid Build Coastguard Worker if value, ok := prop.Value.(*parser.String); ok { 74*90c8c64dSAndroid Build Coastguard Worker flattened.Name = value.Value 75*90c8c64dSAndroid Build Coastguard Worker } 76*90c8c64dSAndroid Build Coastguard Worker } else { 77*90c8c64dSAndroid Build Coastguard Worker flattened.Name = fmt.Sprintf("anonymous@<%d>", anonymousModuleCount) 78*90c8c64dSAndroid Build Coastguard Worker anonymousModuleCount++ 79*90c8c64dSAndroid Build Coastguard Worker } 80*90c8c64dSAndroid Build Coastguard Worker flattened.PropertyMap = make(map[string]interface{}) 81*90c8c64dSAndroid Build Coastguard Worker populatePropertyMap(flattened.PropertyMap, "", &module.Map) 82*90c8c64dSAndroid Build Coastguard Worker return flattened 83*90c8c64dSAndroid Build Coastguard Worker} 84*90c8c64dSAndroid Build Coastguard Worker 85*90c8c64dSAndroid Build Coastguard Workerfunc processFile(filename string, in io.Reader) ([]FlatModule, error) { 86*90c8c64dSAndroid Build Coastguard Worker if in == nil { 87*90c8c64dSAndroid Build Coastguard Worker if file, err := os.Open(filename); err != nil { 88*90c8c64dSAndroid Build Coastguard Worker return nil, err 89*90c8c64dSAndroid Build Coastguard Worker } else { 90*90c8c64dSAndroid Build Coastguard Worker defer file.Close() 91*90c8c64dSAndroid Build Coastguard Worker in = file 92*90c8c64dSAndroid Build Coastguard Worker } 93*90c8c64dSAndroid Build Coastguard Worker } 94*90c8c64dSAndroid Build Coastguard Worker 95*90c8c64dSAndroid Build Coastguard Worker ast, errs := parser.ParseAndEval(filename, in, &parser.Scope{}) 96*90c8c64dSAndroid Build Coastguard Worker if len(errs) > 0 { 97*90c8c64dSAndroid Build Coastguard Worker for _, err := range errs { 98*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, err) 99*90c8c64dSAndroid Build Coastguard Worker } 100*90c8c64dSAndroid Build Coastguard Worker return nil, fmt.Errorf("%d parsing errors", len(errs)) 101*90c8c64dSAndroid Build Coastguard Worker } 102*90c8c64dSAndroid Build Coastguard Worker 103*90c8c64dSAndroid Build Coastguard Worker var modules []FlatModule 104*90c8c64dSAndroid Build Coastguard Worker for _, def := range ast.Defs { 105*90c8c64dSAndroid Build Coastguard Worker if module, ok := def.(*parser.Module); ok { 106*90c8c64dSAndroid Build Coastguard Worker modules = append(modules, flattenModule(module)) 107*90c8c64dSAndroid Build Coastguard Worker } 108*90c8c64dSAndroid Build Coastguard Worker } 109*90c8c64dSAndroid Build Coastguard Worker return modules, nil 110*90c8c64dSAndroid Build Coastguard Worker} 111*90c8c64dSAndroid Build Coastguard Worker 112*90c8c64dSAndroid Build Coastguard Workerfunc quoteBashString(s string) string { 113*90c8c64dSAndroid Build Coastguard Worker return strings.ReplaceAll(s, "$", "\\$") 114*90c8c64dSAndroid Build Coastguard Worker} 115*90c8c64dSAndroid Build Coastguard Worker 116*90c8c64dSAndroid Build Coastguard Workerfunc printBash(flatModules []FlatModule, w io.Writer) { 117*90c8c64dSAndroid Build Coastguard Worker var moduleNameList []string 118*90c8c64dSAndroid Build Coastguard Worker if len(flatModules) == 0 { 119*90c8c64dSAndroid Build Coastguard Worker // Early bail out if we have nothing to output 120*90c8c64dSAndroid Build Coastguard Worker return 121*90c8c64dSAndroid Build Coastguard Worker } 122*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintf(w, "declare -a MODULE_NAMES\n") 123*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintf(w, "declare -A MODULE_TYPE_DICT\n") 124*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintf(w, "declare -A MODULE_PROP_KEYS_DICT\n") 125*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintf(w, "declare -A MODULE_PROP_VALUES_DICT\n") 126*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintf(w, "\n") 127*90c8c64dSAndroid Build Coastguard Worker for _, module := range flatModules { 128*90c8c64dSAndroid Build Coastguard Worker name := quoteBashString(module.Name) 129*90c8c64dSAndroid Build Coastguard Worker moduleNameList = append(moduleNameList, name) 130*90c8c64dSAndroid Build Coastguard Worker var modulePropKeys []string 131*90c8c64dSAndroid Build Coastguard Worker for k := range module.PropertyMap { 132*90c8c64dSAndroid Build Coastguard Worker modulePropKeys = append(modulePropKeys, k) 133*90c8c64dSAndroid Build Coastguard Worker } 134*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintf(w, "MODULE_TYPE_DICT[%q]=%q\n", name, quoteBashString(module.Type)) 135*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintf(w, "MODULE_PROP_KEYS_DICT[%q]=%q\n", name, 136*90c8c64dSAndroid Build Coastguard Worker quoteBashString(strings.Join(modulePropKeys, " "))) 137*90c8c64dSAndroid Build Coastguard Worker for k, v := range module.PropertyMap { 138*90c8c64dSAndroid Build Coastguard Worker var propValue string 139*90c8c64dSAndroid Build Coastguard Worker if vl, ok := v.([]interface{}); ok { 140*90c8c64dSAndroid Build Coastguard Worker var list []string 141*90c8c64dSAndroid Build Coastguard Worker for _, s := range vl { 142*90c8c64dSAndroid Build Coastguard Worker list = append(list, fmt.Sprintf("%v", s)) 143*90c8c64dSAndroid Build Coastguard Worker } 144*90c8c64dSAndroid Build Coastguard Worker propValue = fmt.Sprintf("%s", strings.Join(list, " ")) 145*90c8c64dSAndroid Build Coastguard Worker } else { 146*90c8c64dSAndroid Build Coastguard Worker propValue = fmt.Sprintf("%v", v) 147*90c8c64dSAndroid Build Coastguard Worker } 148*90c8c64dSAndroid Build Coastguard Worker key := name + ":" + quoteBashString(k) 149*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintf(w, "MODULE_PROP_VALUES_DICT[%q]=%q\n", key, quoteBashString(propValue)) 150*90c8c64dSAndroid Build Coastguard Worker } 151*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintf(w, "\n") 152*90c8c64dSAndroid Build Coastguard Worker } 153*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintf(w, "MODULE_NAMES=(\n") 154*90c8c64dSAndroid Build Coastguard Worker for _, name := range moduleNameList { 155*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintf(w, " %q\n", name) 156*90c8c64dSAndroid Build Coastguard Worker } 157*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintf(w, ")\n") 158*90c8c64dSAndroid Build Coastguard Worker} 159*90c8c64dSAndroid Build Coastguard Worker 160*90c8c64dSAndroid Build Coastguard Workervar ( 161*90c8c64dSAndroid Build Coastguard Worker outputBashFlag = flag.Bool("bash", false, "Output in bash format") 162*90c8c64dSAndroid Build Coastguard Worker outputJsonFlag = flag.Bool("json", false, "Output in json format (this is the default)") 163*90c8c64dSAndroid Build Coastguard Worker helpFlag = flag.Bool("help", false, "Display this message and exit") 164*90c8c64dSAndroid Build Coastguard Worker exitCode = 0 165*90c8c64dSAndroid Build Coastguard Worker) 166*90c8c64dSAndroid Build Coastguard Worker 167*90c8c64dSAndroid Build Coastguard Workerfunc init() { 168*90c8c64dSAndroid Build Coastguard Worker flag.Usage = usage 169*90c8c64dSAndroid Build Coastguard Worker} 170*90c8c64dSAndroid Build Coastguard Worker 171*90c8c64dSAndroid Build Coastguard Workerfunc usage() { 172*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "Usage: %s [OPTION]... [FILE]...\n", os.Args[0]) 173*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "Flatten Android.bp to python friendly json text.\n") 174*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "If no file list is specified, read from standard input.\n") 175*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "\n") 176*90c8c64dSAndroid Build Coastguard Worker flag.PrintDefaults() 177*90c8c64dSAndroid Build Coastguard Worker} 178*90c8c64dSAndroid Build Coastguard Worker 179*90c8c64dSAndroid Build Coastguard Workerfunc main() { 180*90c8c64dSAndroid Build Coastguard Worker defer func() { 181*90c8c64dSAndroid Build Coastguard Worker if err := recover(); err != nil { 182*90c8c64dSAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "error: %v\n", err) 183*90c8c64dSAndroid Build Coastguard Worker exitCode = 1 184*90c8c64dSAndroid Build Coastguard Worker } 185*90c8c64dSAndroid Build Coastguard Worker os.Exit(exitCode) 186*90c8c64dSAndroid Build Coastguard Worker }() 187*90c8c64dSAndroid Build Coastguard Worker 188*90c8c64dSAndroid Build Coastguard Worker flag.Parse() 189*90c8c64dSAndroid Build Coastguard Worker 190*90c8c64dSAndroid Build Coastguard Worker if *helpFlag { 191*90c8c64dSAndroid Build Coastguard Worker usage() 192*90c8c64dSAndroid Build Coastguard Worker return 193*90c8c64dSAndroid Build Coastguard Worker } 194*90c8c64dSAndroid Build Coastguard Worker 195*90c8c64dSAndroid Build Coastguard Worker flatModules := []FlatModule{} 196*90c8c64dSAndroid Build Coastguard Worker 197*90c8c64dSAndroid Build Coastguard Worker if flag.NArg() == 0 { 198*90c8c64dSAndroid Build Coastguard Worker if modules, err := processFile("<stdin>", os.Stdin); err != nil { 199*90c8c64dSAndroid Build Coastguard Worker panic(err) 200*90c8c64dSAndroid Build Coastguard Worker } else { 201*90c8c64dSAndroid Build Coastguard Worker flatModules = append(flatModules, modules...) 202*90c8c64dSAndroid Build Coastguard Worker } 203*90c8c64dSAndroid Build Coastguard Worker } 204*90c8c64dSAndroid Build Coastguard Worker 205*90c8c64dSAndroid Build Coastguard Worker for _, pathname := range flag.Args() { 206*90c8c64dSAndroid Build Coastguard Worker switch fileInfo, err := os.Stat(pathname); { 207*90c8c64dSAndroid Build Coastguard Worker case err != nil: 208*90c8c64dSAndroid Build Coastguard Worker panic(err) 209*90c8c64dSAndroid Build Coastguard Worker case fileInfo.IsDir(): 210*90c8c64dSAndroid Build Coastguard Worker panic(fmt.Errorf("%q is a directory", pathname)) 211*90c8c64dSAndroid Build Coastguard Worker default: 212*90c8c64dSAndroid Build Coastguard Worker if modules, err := processFile(pathname, nil); err != nil { 213*90c8c64dSAndroid Build Coastguard Worker panic(err) 214*90c8c64dSAndroid Build Coastguard Worker } else { 215*90c8c64dSAndroid Build Coastguard Worker flatModules = append(flatModules, modules...) 216*90c8c64dSAndroid Build Coastguard Worker } 217*90c8c64dSAndroid Build Coastguard Worker } 218*90c8c64dSAndroid Build Coastguard Worker } 219*90c8c64dSAndroid Build Coastguard Worker 220*90c8c64dSAndroid Build Coastguard Worker if *outputBashFlag { 221*90c8c64dSAndroid Build Coastguard Worker printBash(flatModules, os.Stdout) 222*90c8c64dSAndroid Build Coastguard Worker } else { 223*90c8c64dSAndroid Build Coastguard Worker if b, err := json.MarshalIndent(flatModules, "", " "); err != nil { 224*90c8c64dSAndroid Build Coastguard Worker panic(err) 225*90c8c64dSAndroid Build Coastguard Worker } else { 226*90c8c64dSAndroid Build Coastguard Worker fmt.Printf("%s\n", b) 227*90c8c64dSAndroid Build Coastguard Worker } 228*90c8c64dSAndroid Build Coastguard Worker } 229*90c8c64dSAndroid Build Coastguard Worker} 230