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