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