blob: 5a924b19f48854cc46e23044d1a583a97750e789 [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
99 if isHostRule {
100 conditionals = hostScopedPropertyConditionals
101 }
102
103 conditional, ok := conditionals[target.Name.Name]
104 if !ok {
105 // not found
106 conditional = fmt.Sprintf(
107 "ifeq(true, true) # ERROR: unsupported conditional host [%s]",
108 target.Name.Name)
109 }
110
111 var scopedProps []string
112 for _, targetScopedProp := range target.Value.MapValue {
113 if mkProp, ok := standardProperties[targetScopedProp.Name.Name]; ok {
114 scopedProps = append(scopedProps, fmt.Sprintf("%s += %s",
115 mkProp.string, valueToString(targetScopedProp.Value)))
Dan Willemsen57ad08c2015-06-10 16:20:14 -0700116 } else if rwProp, ok := rewriteProperties[targetScopedProp.Name.Name]; ok {
117 scopedProps = append(scopedProps, rwProp.f(rwProp.string, targetScopedProp, nil)...)
Andres Moralesaf11df12015-04-30 12:14:34 -0700118 } else if "disabled" == targetScopedProp.Name.Name {
119 if targetScopedProp.Value.BoolValue {
120 disabledBuilds[target.Name.Name] = true
121 } else {
122 delete(disabledBuilds, target.Name.Name)
123 }
124 }
125 }
126
127 if len(scopedProps) > 0 {
128 computedProps = append(computedProps, conditional)
129 computedProps = append(computedProps, scopedProps...)
130 computedProps = append(computedProps, "endif")
131 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700132 }
Andres Moralesaf11df12015-04-30 12:14:34 -0700133
134 return
135}
136
137func translateSuffixProperties(suffixProps []*bpparser.Property,
138 suffixMap map[string]string) (computedProps []string) {
139 for _, suffixProp := range suffixProps {
140 if suffix, ok := suffixMap[suffixProp.Name.Name]; ok {
141 for _, stdProp := range suffixProp.Value.MapValue {
142 if mkProp, ok := standardProperties[stdProp.Name.Name]; ok {
143 computedProps = append(computedProps, fmt.Sprintf("%s_%s := %s", mkProp.string, suffix, valueToString(stdProp.Value)))
Dan Willemsen57ad08c2015-06-10 16:20:14 -0700144 } else if rwProp, ok := rewriteProperties[stdProp.Name.Name]; ok {
145 computedProps = append(computedProps, rwProp.f(rwProp.string, stdProp, &suffix)...)
Andres Moralesaf11df12015-04-30 12:14:34 -0700146 } else {
147 computedProps = append(computedProps, fmt.Sprintf("# ERROR: unsupported property %s", stdProp.Name.Name))
148 }
149 }
150 }
151 }
152 return
153}
154
Dan Willemsen57ad08c2015-06-10 16:20:14 -0700155func prependLocalPath(name string, prop *bpparser.Property, suffix *string) (computedProps []string) {
156 includes := make([]string, 0, len(prop.Value.ListValue))
157 for _, tok := range prop.Value.ListValue {
158 if tok.Type == bpparser.String {
159 includes = append(includes, fmt.Sprintf(" $(LOCAL_PATH)/%s", tok.StringValue))
160 } else {
161 includes = append(includes, fmt.Sprintf("# ERROR: unsupported type %s in list",
162 tok.Type.String()))
163 }
164 }
165 if suffix != nil {
166 name += "_" + *suffix
167 }
168 return append(computedProps, fmt.Sprintf("%s := \\\n%s\n", name, strings.Join(includes, " \\\n")))
169}
170
Andres Moralesaf11df12015-04-30 12:14:34 -0700171func (w *androidMkWriter) lookupMap(parent bpparser.Value) (mapValue []*bpparser.Property) {
172 if parent.Variable != "" {
173 mapValue = w.mapScope[parent.Variable]
174 } else {
175 mapValue = parent.MapValue
176 }
177 return
Andres Moralesda8706f2015-04-29 12:46:49 -0700178}
179
180func (w *androidMkWriter) handleComment(comment *bpparser.Comment) {
181 for _, c := range comment.Comment {
Andres Moralesaf11df12015-04-30 12:14:34 -0700182 fmt.Fprintf(w, "#%s\n", c)
Andres Moralesda8706f2015-04-29 12:46:49 -0700183 }
184}
185
Andres Moralesaf11df12015-04-30 12:14:34 -0700186func (w *androidMkWriter) writeModule(moduleRule string, props []string,
187 disabledBuilds map[string]bool, isHostRule bool) {
188 disabledConditionals := disabledTargetConditionals
189 if isHostRule {
190 disabledConditionals = disabledHostConditionals
191 }
192 for build, _ := range disabledBuilds {
193 if conditional, ok := disabledConditionals[build]; ok {
194 fmt.Fprintf(w, "%s\n", conditional)
195 defer fmt.Fprintf(w, "endif\n")
Andres Moralesda8706f2015-04-29 12:46:49 -0700196 }
Andres Moralesaf11df12015-04-30 12:14:34 -0700197 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700198
Andres Moralesaf11df12015-04-30 12:14:34 -0700199 fmt.Fprintf(w, "include $(CLEAR_VARS)\n")
200 fmt.Fprintf(w, "%s\n", strings.Join(props, "\n"))
201 fmt.Fprintf(w, "include $(%s)\n\n", moduleRule)
202}
Andres Moralesda8706f2015-04-29 12:46:49 -0700203
Andres Moralesaf11df12015-04-30 12:14:34 -0700204func (w *androidMkWriter) handleModule(module *bpparser.Module) {
205 moduleRule := fmt.Sprintf(module.Type.Name)
206 if translation, ok := moduleTypeToRule[module.Type.Name]; ok {
207 moduleRule = translation
208 }
209
210 isHostRule := strings.Contains(moduleRule, "HOST")
211 hostSupported := false
212 standardProps := make([]string, 0, len(module.Properties))
213 disabledBuilds := make(map[string]bool)
214 for _, prop := range module.Properties {
215 if mkProp, ok := standardProperties[prop.Name.Name]; ok {
216 standardProps = append(standardProps, fmt.Sprintf("%s := %s", mkProp.string, valueToString(prop.Value)))
Dan Willemsen57ad08c2015-06-10 16:20:14 -0700217 } else if rwProp, ok := rewriteProperties[prop.Name.Name]; ok {
218 standardProps = append(standardProps, rwProp.f(rwProp.string, prop, nil)...)
Andres Moralesaf11df12015-04-30 12:14:34 -0700219 } else if suffixMap, ok := suffixProperties[prop.Name.Name]; ok {
220 suffixProps := w.lookupMap(prop.Value)
221 standardProps = append(standardProps, translateSuffixProperties(suffixProps, suffixMap)...)
222 } else if "target" == prop.Name.Name {
223 props := w.lookupMap(prop.Value)
224 standardProps = append(standardProps, translateTargetConditionals(props, disabledBuilds, isHostRule)...)
225 } else if "host_supported" == prop.Name.Name {
226 hostSupported = prop.Value.BoolValue
227 } else {
228 standardProps = append(standardProps, fmt.Sprintf("# ERROR: Unsupported property %s", prop.Name.Name))
229 }
230 }
231
232 // write out target build
233 w.writeModule(moduleRule, standardProps, disabledBuilds, isHostRule)
234 if hostSupported {
235 hostModuleRule := "NO CORRESPONDING HOST RULE" + moduleRule
236 if trans, ok := targetToHostModuleRule[moduleRule]; ok {
237 hostModuleRule = trans
238 }
239 w.writeModule(hostModuleRule, standardProps,
240 disabledBuilds, true)
241 }
242}
243
244func (w *androidMkWriter) handleSubdirs(value bpparser.Value) {
Andres Morales8ae47de2015-05-11 12:26:07 -0700245 subdirs := make([]string, 0, len(value.ListValue))
246 for _, tok := range value.ListValue {
247 subdirs = append(subdirs, tok.StringValue)
Andres Moralesda8706f2015-04-29 12:46:49 -0700248 }
Ying Wang38284902015-06-02 18:44:59 -0700249 // The current makefile may be generated to outside the source tree (such as the out directory), with a different structure.
250 fmt.Fprintf(w, "# Uncomment the following line if you really want to include subdir Android.mks.\n")
251 fmt.Fprintf(w, "# include $(wildcard $(addsuffix $(LOCAL_PATH)/%s/, Android.mk))\n", strings.Join(subdirs, " "))
Andres Moralesda8706f2015-04-29 12:46:49 -0700252}
253
254func (w *androidMkWriter) handleAssignment(assignment *bpparser.Assignment) {
Andres Moralesaf11df12015-04-30 12:14:34 -0700255 if "subdirs" == assignment.Name.Name {
256 w.handleSubdirs(assignment.OrigValue)
257 } else if assignment.OrigValue.Type == bpparser.Map {
258 // maps may be assigned in Soong, but can only be translated to .mk
259 // in the context of the module
260 w.mapScope[assignment.Name.Name] = assignment.OrigValue.MapValue
261 } else {
262 assigner := ":="
263 if assignment.Assigner != "=" {
264 assigner = assignment.Assigner
265 }
266 fmt.Fprintf(w, "%s %s %s\n", assignment.Name.Name, assigner,
267 valueToString(assignment.OrigValue))
Andres Moralesda8706f2015-04-29 12:46:49 -0700268 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700269}
270
271func (w *androidMkWriter) iter() <-chan interface{} {
Andres Moralesaf11df12015-04-30 12:14:34 -0700272 ch := make(chan interface{}, len(w.blueprint.Comments)+len(w.blueprint.Defs))
Andres Moralesda8706f2015-04-29 12:46:49 -0700273 go func() {
274 commIdx := 0
275 defsIdx := 0
Andres Moralesaf11df12015-04-30 12:14:34 -0700276 for defsIdx < len(w.blueprint.Defs) || commIdx < len(w.blueprint.Comments) {
277 if defsIdx == len(w.blueprint.Defs) {
278 ch <- w.blueprint.Comments[commIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700279 commIdx++
Andres Moralesaf11df12015-04-30 12:14:34 -0700280 } else if commIdx == len(w.blueprint.Comments) {
281 ch <- w.blueprint.Defs[defsIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700282 defsIdx++
283 } else {
284 commentsPos := 0
285 defsPos := 0
286
Andres Moralesaf11df12015-04-30 12:14:34 -0700287 def := w.blueprint.Defs[defsIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700288 switch def := def.(type) {
289 case *bpparser.Module:
290 defsPos = def.LbracePos.Line
291 case *bpparser.Assignment:
292 defsPos = def.Pos.Line
293 }
294
Andres Moralesaf11df12015-04-30 12:14:34 -0700295 comment := w.blueprint.Comments[commIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700296 commentsPos = comment.Pos.Line
297
298 if commentsPos < defsPos {
299 commIdx++
300 ch <- comment
301 } else {
302 defsIdx++
303 ch <- def
304 }
305 }
306 }
307 close(ch)
308 }()
309 return ch
310}
311
Andres Morales8ae47de2015-05-11 12:26:07 -0700312func (w *androidMkWriter) handleLocalPath() error {
Dan Willemsen360a39c2015-06-11 14:34:50 -0700313 if w.printedLocalPath {
314 return nil
315 }
316 w.printedLocalPath = true
317
Ying Wang38284902015-06-02 18:44:59 -0700318 localPath, err := filepath.Abs(w.path)
Andres Morales8ae47de2015-05-11 12:26:07 -0700319 if err != nil {
320 return err
321 }
322
Ying Wang38284902015-06-02 18:44:59 -0700323 top, err := getTopOfAndroidTree(localPath)
Andres Morales8ae47de2015-05-11 12:26:07 -0700324 if err != nil {
325 return err
326 }
327
Ying Wang38284902015-06-02 18:44:59 -0700328 rel, err := filepath.Rel(top, localPath)
Andres Morales8ae47de2015-05-11 12:26:07 -0700329 if err != nil {
330 return err
331 }
332
333 w.WriteString("LOCAL_PATH := " + rel + "\n")
334 return nil
335}
336
Ying Wang38284902015-06-02 18:44:59 -0700337func (w *androidMkWriter) write(androidMk string) error {
338 fmt.Printf("Writing %s\n", androidMk)
Andres Moralesda8706f2015-04-29 12:46:49 -0700339
Ying Wang38284902015-06-02 18:44:59 -0700340 f, err := os.Create(androidMk)
Andres Moralesda8706f2015-04-29 12:46:49 -0700341 if err != nil {
342 panic(err)
343 }
344
Andres Moralesaf11df12015-04-30 12:14:34 -0700345 defer f.Close()
Andres Moralesda8706f2015-04-29 12:46:49 -0700346
347 w.Writer = bufio.NewWriter(f)
348
349 for block := range w.iter() {
350 switch block := block.(type) {
351 case *bpparser.Module:
Dan Willemsen360a39c2015-06-11 14:34:50 -0700352 if err := w.handleLocalPath(); err != nil {
353 return err
354 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700355 w.handleModule(block)
356 case *bpparser.Assignment:
Dan Willemsen360a39c2015-06-11 14:34:50 -0700357 if err := w.handleLocalPath(); err != nil {
358 return err
359 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700360 w.handleAssignment(block)
361 case bpparser.Comment:
362 w.handleComment(&block)
363 }
364 }
365
366 if err = w.Flush(); err != nil {
367 panic(err)
368 }
Ying Wang38284902015-06-02 18:44:59 -0700369 return nil
Andres Moralesda8706f2015-04-29 12:46:49 -0700370}
371
372func main() {
373 if len(os.Args) < 2 {
374 fmt.Println("No filename supplied")
Ying Wang38284902015-06-02 18:44:59 -0700375 os.Exit(1)
Andres Moralesda8706f2015-04-29 12:46:49 -0700376 }
377
Ying Wang38284902015-06-02 18:44:59 -0700378 androidBp := os.Args[1]
379 var androidMk string
380 if len(os.Args) >= 3 {
381 androidMk = os.Args[2]
382 } else {
383 androidMk = androidBp + ".mk"
384 }
385
386 reader, err := os.Open(androidBp)
Andres Moralesda8706f2015-04-29 12:46:49 -0700387 if err != nil {
388 fmt.Println(err.Error())
Ying Wang38284902015-06-02 18:44:59 -0700389 os.Exit(1)
Andres Moralesda8706f2015-04-29 12:46:49 -0700390 }
391
392 scope := bpparser.NewScope(nil)
Ying Wang38284902015-06-02 18:44:59 -0700393 blueprint, errs := bpparser.Parse(androidBp, reader, scope)
Andres Moralesda8706f2015-04-29 12:46:49 -0700394 if len(errs) > 0 {
Ying Wang38284902015-06-02 18:44:59 -0700395 fmt.Println("%d errors parsing %s", len(errs), androidBp)
Andres Moralesda8706f2015-04-29 12:46:49 -0700396 fmt.Println(errs)
Ying Wang38284902015-06-02 18:44:59 -0700397 os.Exit(1)
Andres Moralesda8706f2015-04-29 12:46:49 -0700398 }
399
400 writer := &androidMkWriter{
Andres Moralesaf11df12015-04-30 12:14:34 -0700401 blueprint: blueprint,
Ying Wang38284902015-06-02 18:44:59 -0700402 path: path.Dir(androidBp),
Andres Moralesaf11df12015-04-30 12:14:34 -0700403 mapScope: make(map[string][]*bpparser.Property),
Andres Moralesda8706f2015-04-29 12:46:49 -0700404 }
405
Ying Wang38284902015-06-02 18:44:59 -0700406 err = writer.write(androidMk)
407 if err != nil {
408 fmt.Println(err.Error())
409 os.Exit(1)
410 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700411}