1package main 2 3import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "strings" 9 "text/scanner" 10 11 mkparser "android/soong/androidmk/parser" 12 13 bpparser "github.com/google/blueprint/parser" 14) 15 16// TODO: non-expanded variables with expressions 17 18type bpFile struct { 19 comments []bpparser.Comment 20 defs []bpparser.Definition 21 localAssignments map[string]*bpparser.Property 22 globalAssignments map[string]*bpparser.Value 23 scope mkparser.Scope 24 module *bpparser.Module 25 26 pos scanner.Position 27 prevLine, line int 28 29 inModule bool 30} 31 32func (f *bpFile) errorf(thing mkparser.MakeThing, s string, args ...interface{}) { 33 orig := thing.Dump() 34 s = fmt.Sprintf(s, args...) 35 f.comments = append(f.comments, bpparser.Comment{ 36 Comment: []string{fmt.Sprintf("// ANDROIDMK TRANSLATION ERROR: %s", s)}, 37 Pos: f.pos, 38 }) 39 lines := strings.Split(orig, "\n") 40 for _, l := range lines { 41 f.incPos() 42 f.comments = append(f.comments, bpparser.Comment{ 43 Comment: []string{"// " + l}, 44 Pos: f.pos, 45 }) 46 } 47} 48 49func (f *bpFile) setPos(pos, endPos scanner.Position) { 50 f.pos = pos 51 52 f.line++ 53 if f.pos.Line > f.prevLine+1 { 54 f.line++ 55 } 56 57 f.pos.Line = f.line 58 f.prevLine = endPos.Line 59} 60 61func (f *bpFile) incPos() { 62 f.pos.Line++ 63 f.line++ 64 f.prevLine++ 65} 66 67type conditional struct { 68 cond string 69 eq bool 70} 71 72func main() { 73 b, err := ioutil.ReadFile(os.Args[1]) 74 if err != nil { 75 fmt.Println(err.Error()) 76 return 77 } 78 79 p := mkparser.NewParser(os.Args[1], bytes.NewBuffer(b)) 80 81 things, errs := p.Parse() 82 if len(errs) > 0 { 83 for _, err := range errs { 84 fmt.Println("ERROR: ", err) 85 } 86 return 87 } 88 89 file := &bpFile{ 90 scope: androidScope(), 91 localAssignments: make(map[string]*bpparser.Property), 92 globalAssignments: make(map[string]*bpparser.Value), 93 } 94 95 var conds []*conditional 96 var assignmentCond *conditional 97 98 for _, t := range things { 99 file.setPos(t.Pos(), t.EndPos()) 100 101 if comment, ok := t.AsComment(); ok { 102 file.comments = append(file.comments, bpparser.Comment{ 103 Pos: file.pos, 104 Comment: []string{"//" + comment.Comment}, 105 }) 106 } else if assignment, ok := t.AsAssignment(); ok { 107 handleAssignment(file, assignment, assignmentCond) 108 } else if directive, ok := t.AsDirective(); ok { 109 switch directive.Name { 110 case "include": 111 val := directive.Args.Value(file.scope) 112 switch { 113 case soongModuleTypes[val]: 114 handleModuleConditionals(file, directive, conds) 115 makeModule(file, val) 116 case val == clear_vars: 117 resetModule(file) 118 default: 119 file.errorf(directive, "unsupported include") 120 continue 121 } 122 case "ifeq", "ifneq", "ifdef", "ifndef": 123 args := directive.Args.Dump() 124 eq := directive.Name == "ifeq" || directive.Name == "ifdef" 125 if _, ok := conditionalTranslations[args]; ok { 126 newCond := conditional{args, eq} 127 conds = append(conds, &newCond) 128 if file.inModule { 129 if assignmentCond == nil { 130 assignmentCond = &newCond 131 } else { 132 file.errorf(directive, "unsupported nested conditional in module") 133 } 134 } 135 } else { 136 file.errorf(directive, "unsupported conditional") 137 conds = append(conds, nil) 138 continue 139 } 140 case "else": 141 if len(conds) == 0 { 142 file.errorf(directive, "missing if before else") 143 continue 144 } else if conds[len(conds)-1] == nil { 145 file.errorf(directive, "else from unsupported contitional") 146 continue 147 } 148 conds[len(conds)-1].eq = !conds[len(conds)-1].eq 149 case "endif": 150 if len(conds) == 0 { 151 file.errorf(directive, "missing if before endif") 152 continue 153 } else if conds[len(conds)-1] == nil { 154 file.errorf(directive, "endif from unsupported contitional") 155 conds = conds[:len(conds)-1] 156 } else { 157 if assignmentCond == conds[len(conds)-1] { 158 assignmentCond = nil 159 } 160 conds = conds[:len(conds)-1] 161 } 162 default: 163 file.errorf(directive, "unsupported directive") 164 continue 165 } 166 } else { 167 file.errorf(t, "unsupported line") 168 } 169 } 170 171 out, err := bpparser.Print(&bpparser.File{ 172 Defs: file.defs, 173 Comments: file.comments, 174 }) 175 if err != nil { 176 fmt.Println(err) 177 return 178 } 179 180 fmt.Print(string(out)) 181} 182 183func handleAssignment(file *bpFile, assignment mkparser.Assignment, c *conditional) { 184 if !assignment.Name.Const() { 185 file.errorf(assignment, "unsupported non-const variable name") 186 return 187 } 188 189 if assignment.Target != nil { 190 file.errorf(assignment, "unsupported target assignment") 191 return 192 } 193 194 name := assignment.Name.Value(nil) 195 prefix := "" 196 197 if strings.HasPrefix(name, "LOCAL_") { 198 for k, v := range propertyPrefixes { 199 if strings.HasSuffix(name, "_"+k) { 200 name = strings.TrimSuffix(name, "_"+k) 201 prefix = v 202 break 203 } 204 } 205 206 if c != nil { 207 if prefix != "" { 208 file.errorf(assignment, "prefix assignment inside conditional, skipping conditional") 209 } else { 210 var ok bool 211 if prefix, ok = conditionalTranslations[c.cond][c.eq]; !ok { 212 panic("unknown conditional") 213 } 214 } 215 } 216 } else { 217 if c != nil { 218 eq := "eq" 219 if !c.eq { 220 eq = "neq" 221 } 222 file.errorf(assignment, "conditional %s %s on global assignment", eq, c.cond) 223 } 224 } 225 226 appendVariable := assignment.Type == "+=" 227 228 var err error 229 if prop, ok := standardProperties[name]; ok { 230 var val *bpparser.Value 231 val, err = makeVariableToBlueprint(file, assignment.Value, prop.ValueType) 232 if err == nil { 233 err = setVariable(file, appendVariable, prefix, prop.string, val, true) 234 } 235 } else if prop, ok := rewriteProperties[name]; ok { 236 err = prop.f(file, prefix, assignment.Value, appendVariable) 237 } else if _, ok := deleteProperties[name]; ok { 238 return 239 } else { 240 switch { 241 case name == "LOCAL_PATH": 242 // Nothing to do, except maybe avoid the "./" in paths? 243 case name == "LOCAL_ARM_MODE": 244 // This is a hack to get the LOCAL_ARM_MODE value inside 245 // of an arch: { arm: {} } block. 246 armModeAssign := assignment 247 armModeAssign.Name = mkparser.SimpleMakeString("LOCAL_ARM_MODE_HACK_arm", assignment.Name.Pos) 248 handleAssignment(file, armModeAssign, c) 249 case name == "LOCAL_ADDITIONAL_DEPENDENCIES": 250 // TODO: check for only .mk files? 251 case strings.HasPrefix(name, "LOCAL_"): 252 file.errorf(assignment, "unsupported assignment to %s", name) 253 return 254 default: 255 var val *bpparser.Value 256 val, err = makeVariableToBlueprint(file, assignment.Value, bpparser.List) 257 err = setVariable(file, appendVariable, prefix, name, val, false) 258 } 259 } 260 if err != nil { 261 file.errorf(assignment, err.Error()) 262 } 263} 264 265func handleModuleConditionals(file *bpFile, directive mkparser.Directive, conds []*conditional) { 266 for _, c := range conds { 267 if c == nil { 268 continue 269 } 270 271 if _, ok := conditionalTranslations[c.cond]; !ok { 272 panic("unknown conditional " + c.cond) 273 } 274 275 disabledPrefix := conditionalTranslations[c.cond][!c.eq] 276 277 // Create a fake assignment with enabled = false 278 val, err := makeVariableToBlueprint(file, mkparser.SimpleMakeString("false", file.pos), bpparser.Bool) 279 if err == nil { 280 err = setVariable(file, false, disabledPrefix, "enabled", val, true) 281 } 282 if err != nil { 283 file.errorf(directive, err.Error()) 284 } 285 } 286} 287 288func makeModule(file *bpFile, t string) { 289 file.module.Type = bpparser.Ident{ 290 Name: t, 291 Pos: file.module.LbracePos, 292 } 293 file.module.RbracePos = file.pos 294 file.defs = append(file.defs, file.module) 295 file.inModule = false 296} 297 298func resetModule(file *bpFile) { 299 file.module = &bpparser.Module{} 300 file.module.LbracePos = file.pos 301 file.localAssignments = make(map[string]*bpparser.Property) 302 file.inModule = true 303} 304 305func makeVariableToBlueprint(file *bpFile, val *mkparser.MakeString, 306 typ bpparser.ValueType) (*bpparser.Value, error) { 307 308 var exp *bpparser.Value 309 var err error 310 switch typ { 311 case bpparser.List: 312 exp, err = makeToListExpression(val, file.scope) 313 case bpparser.String: 314 exp, err = makeToStringExpression(val, file.scope) 315 case bpparser.Bool: 316 exp, err = makeToBoolExpression(val) 317 default: 318 panic("unknown type") 319 } 320 321 if err != nil { 322 return nil, err 323 } 324 325 return exp, nil 326} 327 328func setVariable(file *bpFile, plusequals bool, prefix, name string, value *bpparser.Value, local bool) error { 329 330 if prefix != "" { 331 name = prefix + "." + name 332 } 333 334 pos := file.pos 335 336 var oldValue *bpparser.Value 337 if local { 338 oldProp := file.localAssignments[name] 339 if oldProp != nil { 340 oldValue = &oldProp.Value 341 } 342 } else { 343 oldValue = file.globalAssignments[name] 344 } 345 346 if local { 347 if oldValue != nil && plusequals { 348 val, err := addValues(oldValue, value) 349 if err != nil { 350 return fmt.Errorf("unsupported addition: %s", err.Error()) 351 } 352 val.Expression.Pos = pos 353 *oldValue = *val 354 } else { 355 names := strings.Split(name, ".") 356 container := &file.module.Properties 357 358 for i, n := range names[:len(names)-1] { 359 fqn := strings.Join(names[0:i+1], ".") 360 prop := file.localAssignments[fqn] 361 if prop == nil { 362 prop = &bpparser.Property{ 363 Name: bpparser.Ident{Name: n, Pos: pos}, 364 Pos: pos, 365 Value: bpparser.Value{ 366 Type: bpparser.Map, 367 MapValue: []*bpparser.Property{}, 368 }, 369 } 370 file.localAssignments[fqn] = prop 371 *container = append(*container, prop) 372 } 373 container = &prop.Value.MapValue 374 } 375 376 prop := &bpparser.Property{ 377 Name: bpparser.Ident{Name: names[len(names)-1], Pos: pos}, 378 Pos: pos, 379 Value: *value, 380 } 381 file.localAssignments[name] = prop 382 *container = append(*container, prop) 383 } 384 } else { 385 if oldValue != nil && plusequals { 386 a := &bpparser.Assignment{ 387 Name: bpparser.Ident{ 388 Name: name, 389 Pos: pos, 390 }, 391 Value: *value, 392 OrigValue: *value, 393 Pos: pos, 394 Assigner: "+=", 395 } 396 file.defs = append(file.defs, a) 397 } else { 398 a := &bpparser.Assignment{ 399 Name: bpparser.Ident{ 400 Name: name, 401 Pos: pos, 402 }, 403 Value: *value, 404 OrigValue: *value, 405 Pos: pos, 406 Assigner: "=", 407 } 408 file.globalAssignments[name] = &a.Value 409 file.defs = append(file.defs, a) 410 } 411 } 412 413 return nil 414} 415