• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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