blob: 7889588efd661e319846163951fd72351304e145 [file] [log] [blame]
Andres Moralesda8706f2015-04-29 12:46:49 -07001package main
2
3import (
4 "bufio"
Andres Morales8ae47de2015-05-11 12:26:07 -07005 "errors"
Andres Moralesda8706f2015-04-29 12:46:49 -07006 "fmt"
7 "os"
8 "path"
Andres Morales8ae47de2015-05-11 12:26:07 -07009 "path/filepath"
Andres Moralesaf11df12015-04-30 12:14:34 -070010 "regexp"
Andres Moralesda8706f2015-04-29 12:46:49 -070011 "strings"
12
13 bpparser "github.com/google/blueprint/parser"
14)
15
Andres Morales8ae47de2015-05-11 12:26:07 -070016var recursiveSubdirRegex *regexp.Regexp = regexp.MustCompile("(.+)/\\*\\*/(.+)")
17
Andres Moralesda8706f2015-04-29 12:46:49 -070018type androidMkWriter struct {
19 *bufio.Writer
20
Andres Moralesaf11df12015-04-30 12:14:34 -070021 blueprint *bpparser.File
22 path string
23
Dan Willemsen360a39c2015-06-11 14:34:50 -070024 printedLocalPath bool
25
Andres Moralesaf11df12015-04-30 12:14:34 -070026 mapScope map[string][]*bpparser.Property
Andres Moralesda8706f2015-04-29 12:46:49 -070027}
28
Andres Moralesaf11df12015-04-30 12:14:34 -070029func valueToString(value bpparser.Value) string {
Andres Moralesda8706f2015-04-29 12:46:49 -070030 if value.Variable != "" {
31 return fmt.Sprintf("$(%s)", value.Variable)
32 } else {
33 switch value.Type {
34 case bpparser.Bool:
Ying Wang38284902015-06-02 18:44:59 -070035 return fmt.Sprintf("%t", value.BoolValue)
Andres Moralesda8706f2015-04-29 12:46:49 -070036 case bpparser.String:
Ying Wang38284902015-06-02 18:44:59 -070037 return fmt.Sprintf("%s", processWildcards(value.StringValue))
Andres Moralesda8706f2015-04-29 12:46:49 -070038 case bpparser.List:
Andres Moralesaf11df12015-04-30 12:14:34 -070039 return fmt.Sprintf("\\\n%s\n", listToMkString(value.ListValue))
Andres Moralesda8706f2015-04-29 12:46:49 -070040 case bpparser.Map:
Andres Moralesaf11df12015-04-30 12:14:34 -070041 return fmt.Sprintf("ERROR can't convert map to string")
42 default:
43 return fmt.Sprintf("ERROR: unsupported type %d", value.Type)
Andres Moralesda8706f2015-04-29 12:46:49 -070044 }
45 }
Andres Moralesda8706f2015-04-29 12:46:49 -070046}
47
Andres Morales8ae47de2015-05-11 12:26:07 -070048func getTopOfAndroidTree(wd string) (string, error) {
49 if !filepath.IsAbs(wd) {
50 return "", errors.New("path must be absolute: " + wd)
51 }
52
53 topfile := "build/soong/bootstrap.bash"
54
55 for "/" != wd {
56 expected := filepath.Join(wd, topfile)
57
58 if _, err := os.Stat(expected); err == nil {
59 // Found the top
60 return wd, nil
61 }
62
63 wd = filepath.Join(wd, "..")
64 }
65
66 return "", errors.New("couldn't find top of tree from " + wd)
67}
68
Andres Moralesaf11df12015-04-30 12:14:34 -070069// TODO: handle non-recursive wildcards?
70func processWildcards(s string) string {
Andres Morales8ae47de2015-05-11 12:26:07 -070071 submatches := recursiveSubdirRegex.FindStringSubmatch(s)
72 if len(submatches) > 2 {
Andres Moralesaf11df12015-04-30 12:14:34 -070073 // Found a wildcard rule
74 return fmt.Sprintf("$(call find-files-in-subdirs, $(LOCAL_PATH), %s, %s)",
Andres Morales8ae47de2015-05-11 12:26:07 -070075 submatches[2], submatches[1])
Andres Moralesaf11df12015-04-30 12:14:34 -070076 }
77
78 return s
79}
80
81func listToMkString(list []bpparser.Value) string {
Andres Moralesda8706f2015-04-29 12:46:49 -070082 lines := make([]string, 0, len(list))
83 for _, tok := range list {
Andres Moralesaf11df12015-04-30 12:14:34 -070084 if tok.Type == bpparser.String {
Ying Wang38284902015-06-02 18:44:59 -070085 lines = append(lines, fmt.Sprintf(" %s", processWildcards(tok.StringValue)))
Andres Moralesaf11df12015-04-30 12:14:34 -070086 } else {
87 lines = append(lines, fmt.Sprintf("# ERROR: unsupported type %s in list",
88 tok.Type.String()))
89 }
Andres Moralesda8706f2015-04-29 12:46:49 -070090 }
91
92 return strings.Join(lines, " \\\n")
93}
94
Andres Moralesaf11df12015-04-30 12:14:34 -070095func translateTargetConditionals(props []*bpparser.Property,
96 disabledBuilds map[string]bool, isHostRule bool) (computedProps []string) {
97 for _, target := range props {
98 conditionals := targetScopedPropertyConditionals
Dan Willemsen68fdfcc2015-06-11 14:05:01 -070099 altConditionals := hostScopedPropertyConditionals
Andres Moralesaf11df12015-04-30 12:14:34 -0700100 if isHostRule {
Dan Willemsen68fdfcc2015-06-11 14:05:01 -0700101 conditionals, altConditionals = altConditionals, conditionals
Andres Moralesaf11df12015-04-30 12:14:34 -0700102 }
103
104 conditional, ok := conditionals[target.Name.Name]
105 if !ok {
Dan Willemsen68fdfcc2015-06-11 14:05:01 -0700106 if _, ok := altConditionals[target.Name.Name]; ok {
107 // This is only for the other build type
108 continue
109 } else {
110 // not found
111 conditional = fmt.Sprintf(
112 "ifeq(true, true) # ERROR: unsupported conditional [%s]",
113 target.Name.Name)
114 }
Andres Moralesaf11df12015-04-30 12:14:34 -0700115 }
116
117 var scopedProps []string
118 for _, targetScopedProp := range target.Value.MapValue {
119 if mkProp, ok := standardProperties[targetScopedProp.Name.Name]; ok {
120 scopedProps = append(scopedProps, fmt.Sprintf("%s += %s",
121 mkProp.string, valueToString(targetScopedProp.Value)))
Dan Willemsen57ad08c2015-06-10 16:20:14 -0700122 } else if rwProp, ok := rewriteProperties[targetScopedProp.Name.Name]; ok {
123 scopedProps = append(scopedProps, rwProp.f(rwProp.string, targetScopedProp, nil)...)
Andres Moralesaf11df12015-04-30 12:14:34 -0700124 } else if "disabled" == targetScopedProp.Name.Name {
125 if targetScopedProp.Value.BoolValue {
126 disabledBuilds[target.Name.Name] = true
127 } else {
128 delete(disabledBuilds, target.Name.Name)
129 }
130 }
131 }
132
133 if len(scopedProps) > 0 {
Dan Willemsen68fdfcc2015-06-11 14:05:01 -0700134 if conditional != "" {
135 computedProps = append(computedProps, conditional)
136 computedProps = append(computedProps, scopedProps...)
137 computedProps = append(computedProps, "endif")
138 } else {
139 computedProps = append(computedProps, scopedProps...)
140 }
Andres Moralesaf11df12015-04-30 12:14:34 -0700141 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700142 }
Andres Moralesaf11df12015-04-30 12:14:34 -0700143
144 return
145}
146
147func translateSuffixProperties(suffixProps []*bpparser.Property,
148 suffixMap map[string]string) (computedProps []string) {
149 for _, suffixProp := range suffixProps {
150 if suffix, ok := suffixMap[suffixProp.Name.Name]; ok {
151 for _, stdProp := range suffixProp.Value.MapValue {
152 if mkProp, ok := standardProperties[stdProp.Name.Name]; ok {
153 computedProps = append(computedProps, fmt.Sprintf("%s_%s := %s", mkProp.string, suffix, valueToString(stdProp.Value)))
Dan Willemsen57ad08c2015-06-10 16:20:14 -0700154 } else if rwProp, ok := rewriteProperties[stdProp.Name.Name]; ok {
155 computedProps = append(computedProps, rwProp.f(rwProp.string, stdProp, &suffix)...)
Andres Moralesaf11df12015-04-30 12:14:34 -0700156 } else {
157 computedProps = append(computedProps, fmt.Sprintf("# ERROR: unsupported property %s", stdProp.Name.Name))
158 }
159 }
160 }
161 }
162 return
163}
164
Dan Willemsen57ad08c2015-06-10 16:20:14 -0700165func prependLocalPath(name string, prop *bpparser.Property, suffix *string) (computedProps []string) {
166 includes := make([]string, 0, len(prop.Value.ListValue))
167 for _, tok := range prop.Value.ListValue {
168 if tok.Type == bpparser.String {
169 includes = append(includes, fmt.Sprintf(" $(LOCAL_PATH)/%s", tok.StringValue))
170 } else {
171 includes = append(includes, fmt.Sprintf("# ERROR: unsupported type %s in list",
172 tok.Type.String()))
173 }
174 }
175 if suffix != nil {
176 name += "_" + *suffix
177 }
178 return append(computedProps, fmt.Sprintf("%s := \\\n%s\n", name, strings.Join(includes, " \\\n")))
179}
180
Andres Moralesaf11df12015-04-30 12:14:34 -0700181func (w *androidMkWriter) lookupMap(parent bpparser.Value) (mapValue []*bpparser.Property) {
182 if parent.Variable != "" {
183 mapValue = w.mapScope[parent.Variable]
184 } else {
185 mapValue = parent.MapValue
186 }
187 return
Andres Moralesda8706f2015-04-29 12:46:49 -0700188}
189
190func (w *androidMkWriter) handleComment(comment *bpparser.Comment) {
191 for _, c := range comment.Comment {
Andres Moralesaf11df12015-04-30 12:14:34 -0700192 fmt.Fprintf(w, "#%s\n", c)
Andres Moralesda8706f2015-04-29 12:46:49 -0700193 }
194}
195
Andres Moralesaf11df12015-04-30 12:14:34 -0700196func (w *androidMkWriter) writeModule(moduleRule string, props []string,
197 disabledBuilds map[string]bool, isHostRule bool) {
198 disabledConditionals := disabledTargetConditionals
199 if isHostRule {
200 disabledConditionals = disabledHostConditionals
201 }
202 for build, _ := range disabledBuilds {
203 if conditional, ok := disabledConditionals[build]; ok {
204 fmt.Fprintf(w, "%s\n", conditional)
205 defer fmt.Fprintf(w, "endif\n")
Andres Moralesda8706f2015-04-29 12:46:49 -0700206 }
Andres Moralesaf11df12015-04-30 12:14:34 -0700207 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700208
Andres Moralesaf11df12015-04-30 12:14:34 -0700209 fmt.Fprintf(w, "include $(CLEAR_VARS)\n")
210 fmt.Fprintf(w, "%s\n", strings.Join(props, "\n"))
211 fmt.Fprintf(w, "include $(%s)\n\n", moduleRule)
212}
Andres Moralesda8706f2015-04-29 12:46:49 -0700213
Dan Willemsen68fdfcc2015-06-11 14:05:01 -0700214func (w *androidMkWriter) parsePropsAndWriteModule(moduleRule string, isHostRule bool, module *bpparser.Module) (hostSupported bool) {
Andres Moralesaf11df12015-04-30 12:14:34 -0700215 standardProps := make([]string, 0, len(module.Properties))
216 disabledBuilds := make(map[string]bool)
217 for _, prop := range module.Properties {
218 if mkProp, ok := standardProperties[prop.Name.Name]; ok {
219 standardProps = append(standardProps, fmt.Sprintf("%s := %s", mkProp.string, valueToString(prop.Value)))
Dan Willemsen57ad08c2015-06-10 16:20:14 -0700220 } else if rwProp, ok := rewriteProperties[prop.Name.Name]; ok {
221 standardProps = append(standardProps, rwProp.f(rwProp.string, prop, nil)...)
Andres Moralesaf11df12015-04-30 12:14:34 -0700222 } else if suffixMap, ok := suffixProperties[prop.Name.Name]; ok {
223 suffixProps := w.lookupMap(prop.Value)
224 standardProps = append(standardProps, translateSuffixProperties(suffixProps, suffixMap)...)
225 } else if "target" == prop.Name.Name {
226 props := w.lookupMap(prop.Value)
227 standardProps = append(standardProps, translateTargetConditionals(props, disabledBuilds, isHostRule)...)
228 } else if "host_supported" == prop.Name.Name {
229 hostSupported = prop.Value.BoolValue
230 } else {
231 standardProps = append(standardProps, fmt.Sprintf("# ERROR: Unsupported property %s", prop.Name.Name))
232 }
233 }
234
235 // write out target build
236 w.writeModule(moduleRule, standardProps, disabledBuilds, isHostRule)
Dan Willemsen68fdfcc2015-06-11 14:05:01 -0700237 return
238}
239
240func (w *androidMkWriter) handleModule(module *bpparser.Module) {
241 moduleRule := fmt.Sprintf(module.Type.Name)
242 if translation, ok := moduleTypeToRule[module.Type.Name]; ok {
243 moduleRule = translation
244 }
245
246 isHostRule := strings.Contains(moduleRule, "HOST")
247 hostSupported := w.parsePropsAndWriteModule(moduleRule, isHostRule, module)
248
249 if !isHostRule && hostSupported {
Andres Moralesaf11df12015-04-30 12:14:34 -0700250 hostModuleRule := "NO CORRESPONDING HOST RULE" + moduleRule
251 if trans, ok := targetToHostModuleRule[moduleRule]; ok {
252 hostModuleRule = trans
253 }
Dan Willemsen68fdfcc2015-06-11 14:05:01 -0700254
255 w.parsePropsAndWriteModule(hostModuleRule, true, module)
Andres Moralesaf11df12015-04-30 12:14:34 -0700256 }
257}
258
259func (w *androidMkWriter) handleSubdirs(value bpparser.Value) {
Andres Morales8ae47de2015-05-11 12:26:07 -0700260 subdirs := make([]string, 0, len(value.ListValue))
261 for _, tok := range value.ListValue {
262 subdirs = append(subdirs, tok.StringValue)
Andres Moralesda8706f2015-04-29 12:46:49 -0700263 }
Ying Wang38284902015-06-02 18:44:59 -0700264 // The current makefile may be generated to outside the source tree (such as the out directory), with a different structure.
265 fmt.Fprintf(w, "# Uncomment the following line if you really want to include subdir Android.mks.\n")
266 fmt.Fprintf(w, "# include $(wildcard $(addsuffix $(LOCAL_PATH)/%s/, Android.mk))\n", strings.Join(subdirs, " "))
Andres Moralesda8706f2015-04-29 12:46:49 -0700267}
268
269func (w *androidMkWriter) handleAssignment(assignment *bpparser.Assignment) {
Andres Moralesaf11df12015-04-30 12:14:34 -0700270 if "subdirs" == assignment.Name.Name {
271 w.handleSubdirs(assignment.OrigValue)
272 } else if assignment.OrigValue.Type == bpparser.Map {
273 // maps may be assigned in Soong, but can only be translated to .mk
274 // in the context of the module
275 w.mapScope[assignment.Name.Name] = assignment.OrigValue.MapValue
276 } else {
277 assigner := ":="
278 if assignment.Assigner != "=" {
279 assigner = assignment.Assigner
280 }
281 fmt.Fprintf(w, "%s %s %s\n", assignment.Name.Name, assigner,
282 valueToString(assignment.OrigValue))
Andres Moralesda8706f2015-04-29 12:46:49 -0700283 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700284}
285
286func (w *androidMkWriter) iter() <-chan interface{} {
Andres Moralesaf11df12015-04-30 12:14:34 -0700287 ch := make(chan interface{}, len(w.blueprint.Comments)+len(w.blueprint.Defs))
Andres Moralesda8706f2015-04-29 12:46:49 -0700288 go func() {
289 commIdx := 0
290 defsIdx := 0
Andres Moralesaf11df12015-04-30 12:14:34 -0700291 for defsIdx < len(w.blueprint.Defs) || commIdx < len(w.blueprint.Comments) {
292 if defsIdx == len(w.blueprint.Defs) {
293 ch <- w.blueprint.Comments[commIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700294 commIdx++
Andres Moralesaf11df12015-04-30 12:14:34 -0700295 } else if commIdx == len(w.blueprint.Comments) {
296 ch <- w.blueprint.Defs[defsIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700297 defsIdx++
298 } else {
299 commentsPos := 0
300 defsPos := 0
301
Andres Moralesaf11df12015-04-30 12:14:34 -0700302 def := w.blueprint.Defs[defsIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700303 switch def := def.(type) {
304 case *bpparser.Module:
305 defsPos = def.LbracePos.Line
306 case *bpparser.Assignment:
307 defsPos = def.Pos.Line
308 }
309
Andres Moralesaf11df12015-04-30 12:14:34 -0700310 comment := w.blueprint.Comments[commIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700311 commentsPos = comment.Pos.Line
312
313 if commentsPos < defsPos {
314 commIdx++
315 ch <- comment
316 } else {
317 defsIdx++
318 ch <- def
319 }
320 }
321 }
322 close(ch)
323 }()
324 return ch
325}
326
Andres Morales8ae47de2015-05-11 12:26:07 -0700327func (w *androidMkWriter) handleLocalPath() error {
Dan Willemsen360a39c2015-06-11 14:34:50 -0700328 if w.printedLocalPath {
329 return nil
330 }
331 w.printedLocalPath = true
332
Ying Wang38284902015-06-02 18:44:59 -0700333 localPath, err := filepath.Abs(w.path)
Andres Morales8ae47de2015-05-11 12:26:07 -0700334 if err != nil {
335 return err
336 }
337
Ying Wang38284902015-06-02 18:44:59 -0700338 top, err := getTopOfAndroidTree(localPath)
Andres Morales8ae47de2015-05-11 12:26:07 -0700339 if err != nil {
340 return err
341 }
342
Ying Wang38284902015-06-02 18:44:59 -0700343 rel, err := filepath.Rel(top, localPath)
Andres Morales8ae47de2015-05-11 12:26:07 -0700344 if err != nil {
345 return err
346 }
347
348 w.WriteString("LOCAL_PATH := " + rel + "\n")
349 return nil
350}
351
Ying Wang38284902015-06-02 18:44:59 -0700352func (w *androidMkWriter) write(androidMk string) error {
353 fmt.Printf("Writing %s\n", androidMk)
Andres Moralesda8706f2015-04-29 12:46:49 -0700354
Ying Wang38284902015-06-02 18:44:59 -0700355 f, err := os.Create(androidMk)
Andres Moralesda8706f2015-04-29 12:46:49 -0700356 if err != nil {
357 panic(err)
358 }
359
Andres Moralesaf11df12015-04-30 12:14:34 -0700360 defer f.Close()
Andres Moralesda8706f2015-04-29 12:46:49 -0700361
362 w.Writer = bufio.NewWriter(f)
363
364 for block := range w.iter() {
365 switch block := block.(type) {
366 case *bpparser.Module:
Dan Willemsen360a39c2015-06-11 14:34:50 -0700367 if err := w.handleLocalPath(); err != nil {
368 return err
369 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700370 w.handleModule(block)
371 case *bpparser.Assignment:
Dan Willemsen360a39c2015-06-11 14:34:50 -0700372 if err := w.handleLocalPath(); err != nil {
373 return err
374 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700375 w.handleAssignment(block)
376 case bpparser.Comment:
377 w.handleComment(&block)
378 }
379 }
380
381 if err = w.Flush(); err != nil {
382 panic(err)
383 }
Ying Wang38284902015-06-02 18:44:59 -0700384 return nil
Andres Moralesda8706f2015-04-29 12:46:49 -0700385}
386
387func main() {
388 if len(os.Args) < 2 {
389 fmt.Println("No filename supplied")
Ying Wang38284902015-06-02 18:44:59 -0700390 os.Exit(1)
Andres Moralesda8706f2015-04-29 12:46:49 -0700391 }
392
Ying Wang38284902015-06-02 18:44:59 -0700393 androidBp := os.Args[1]
394 var androidMk string
395 if len(os.Args) >= 3 {
396 androidMk = os.Args[2]
397 } else {
398 androidMk = androidBp + ".mk"
399 }
400
401 reader, err := os.Open(androidBp)
Andres Moralesda8706f2015-04-29 12:46:49 -0700402 if err != nil {
403 fmt.Println(err.Error())
Ying Wang38284902015-06-02 18:44:59 -0700404 os.Exit(1)
Andres Moralesda8706f2015-04-29 12:46:49 -0700405 }
406
407 scope := bpparser.NewScope(nil)
Ying Wang38284902015-06-02 18:44:59 -0700408 blueprint, errs := bpparser.Parse(androidBp, reader, scope)
Andres Moralesda8706f2015-04-29 12:46:49 -0700409 if len(errs) > 0 {
Ying Wang38284902015-06-02 18:44:59 -0700410 fmt.Println("%d errors parsing %s", len(errs), androidBp)
Andres Moralesda8706f2015-04-29 12:46:49 -0700411 fmt.Println(errs)
Ying Wang38284902015-06-02 18:44:59 -0700412 os.Exit(1)
Andres Moralesda8706f2015-04-29 12:46:49 -0700413 }
414
415 writer := &androidMkWriter{
Andres Moralesaf11df12015-04-30 12:14:34 -0700416 blueprint: blueprint,
Ying Wang38284902015-06-02 18:44:59 -0700417 path: path.Dir(androidBp),
Andres Moralesaf11df12015-04-30 12:14:34 -0700418 mapScope: make(map[string][]*bpparser.Property),
Andres Moralesda8706f2015-04-29 12:46:49 -0700419 }
420
Ying Wang38284902015-06-02 18:44:59 -0700421 err = writer.write(androidMk)
422 if err != nil {
423 fmt.Println(err.Error())
424 os.Exit(1)
425 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700426}