Home | History | Annotate | Download | only in androidmk
      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