blob: 58afe7725ad9bb89bdb088b6a311bf28d466864a [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)))
116 } else if "disabled" == targetScopedProp.Name.Name {
117 if targetScopedProp.Value.BoolValue {
118 disabledBuilds[target.Name.Name] = true
119 } else {
120 delete(disabledBuilds, target.Name.Name)
121 }
122 }
123 }
124
125 if len(scopedProps) > 0 {
126 computedProps = append(computedProps, conditional)
127 computedProps = append(computedProps, scopedProps...)
128 computedProps = append(computedProps, "endif")
129 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700130 }
Andres Moralesaf11df12015-04-30 12:14:34 -0700131
132 return
133}
134
135func translateSuffixProperties(suffixProps []*bpparser.Property,
136 suffixMap map[string]string) (computedProps []string) {
137 for _, suffixProp := range suffixProps {
138 if suffix, ok := suffixMap[suffixProp.Name.Name]; ok {
139 for _, stdProp := range suffixProp.Value.MapValue {
140 if mkProp, ok := standardProperties[stdProp.Name.Name]; ok {
141 computedProps = append(computedProps, fmt.Sprintf("%s_%s := %s", mkProp.string, suffix, valueToString(stdProp.Value)))
142 } else {
143 computedProps = append(computedProps, fmt.Sprintf("# ERROR: unsupported property %s", stdProp.Name.Name))
144 }
145 }
146 }
147 }
148 return
149}
150
151func (w *androidMkWriter) lookupMap(parent bpparser.Value) (mapValue []*bpparser.Property) {
152 if parent.Variable != "" {
153 mapValue = w.mapScope[parent.Variable]
154 } else {
155 mapValue = parent.MapValue
156 }
157 return
Andres Moralesda8706f2015-04-29 12:46:49 -0700158}
159
160func (w *androidMkWriter) handleComment(comment *bpparser.Comment) {
161 for _, c := range comment.Comment {
Andres Moralesaf11df12015-04-30 12:14:34 -0700162 fmt.Fprintf(w, "#%s\n", c)
Andres Moralesda8706f2015-04-29 12:46:49 -0700163 }
164}
165
Andres Moralesaf11df12015-04-30 12:14:34 -0700166func (w *androidMkWriter) writeModule(moduleRule string, props []string,
167 disabledBuilds map[string]bool, isHostRule bool) {
168 disabledConditionals := disabledTargetConditionals
169 if isHostRule {
170 disabledConditionals = disabledHostConditionals
171 }
172 for build, _ := range disabledBuilds {
173 if conditional, ok := disabledConditionals[build]; ok {
174 fmt.Fprintf(w, "%s\n", conditional)
175 defer fmt.Fprintf(w, "endif\n")
Andres Moralesda8706f2015-04-29 12:46:49 -0700176 }
Andres Moralesaf11df12015-04-30 12:14:34 -0700177 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700178
Andres Moralesaf11df12015-04-30 12:14:34 -0700179 fmt.Fprintf(w, "include $(CLEAR_VARS)\n")
180 fmt.Fprintf(w, "%s\n", strings.Join(props, "\n"))
181 fmt.Fprintf(w, "include $(%s)\n\n", moduleRule)
182}
Andres Moralesda8706f2015-04-29 12:46:49 -0700183
Andres Moralesaf11df12015-04-30 12:14:34 -0700184func (w *androidMkWriter) handleModule(module *bpparser.Module) {
185 moduleRule := fmt.Sprintf(module.Type.Name)
186 if translation, ok := moduleTypeToRule[module.Type.Name]; ok {
187 moduleRule = translation
188 }
189
190 isHostRule := strings.Contains(moduleRule, "HOST")
191 hostSupported := false
192 standardProps := make([]string, 0, len(module.Properties))
193 disabledBuilds := make(map[string]bool)
194 for _, prop := range module.Properties {
195 if mkProp, ok := standardProperties[prop.Name.Name]; ok {
196 standardProps = append(standardProps, fmt.Sprintf("%s := %s", mkProp.string, valueToString(prop.Value)))
197 } else if suffixMap, ok := suffixProperties[prop.Name.Name]; ok {
198 suffixProps := w.lookupMap(prop.Value)
199 standardProps = append(standardProps, translateSuffixProperties(suffixProps, suffixMap)...)
200 } else if "target" == prop.Name.Name {
201 props := w.lookupMap(prop.Value)
202 standardProps = append(standardProps, translateTargetConditionals(props, disabledBuilds, isHostRule)...)
203 } else if "host_supported" == prop.Name.Name {
204 hostSupported = prop.Value.BoolValue
205 } else {
206 standardProps = append(standardProps, fmt.Sprintf("# ERROR: Unsupported property %s", prop.Name.Name))
207 }
208 }
209
210 // write out target build
211 w.writeModule(moduleRule, standardProps, disabledBuilds, isHostRule)
212 if hostSupported {
213 hostModuleRule := "NO CORRESPONDING HOST RULE" + moduleRule
214 if trans, ok := targetToHostModuleRule[moduleRule]; ok {
215 hostModuleRule = trans
216 }
217 w.writeModule(hostModuleRule, standardProps,
218 disabledBuilds, true)
219 }
220}
221
222func (w *androidMkWriter) handleSubdirs(value bpparser.Value) {
Andres Morales8ae47de2015-05-11 12:26:07 -0700223 subdirs := make([]string, 0, len(value.ListValue))
224 for _, tok := range value.ListValue {
225 subdirs = append(subdirs, tok.StringValue)
Andres Moralesda8706f2015-04-29 12:46:49 -0700226 }
Ying Wang38284902015-06-02 18:44:59 -0700227 // The current makefile may be generated to outside the source tree (such as the out directory), with a different structure.
228 fmt.Fprintf(w, "# Uncomment the following line if you really want to include subdir Android.mks.\n")
229 fmt.Fprintf(w, "# include $(wildcard $(addsuffix $(LOCAL_PATH)/%s/, Android.mk))\n", strings.Join(subdirs, " "))
Andres Moralesda8706f2015-04-29 12:46:49 -0700230}
231
232func (w *androidMkWriter) handleAssignment(assignment *bpparser.Assignment) {
Andres Moralesaf11df12015-04-30 12:14:34 -0700233 if "subdirs" == assignment.Name.Name {
234 w.handleSubdirs(assignment.OrigValue)
235 } else if assignment.OrigValue.Type == bpparser.Map {
236 // maps may be assigned in Soong, but can only be translated to .mk
237 // in the context of the module
238 w.mapScope[assignment.Name.Name] = assignment.OrigValue.MapValue
239 } else {
240 assigner := ":="
241 if assignment.Assigner != "=" {
242 assigner = assignment.Assigner
243 }
244 fmt.Fprintf(w, "%s %s %s\n", assignment.Name.Name, assigner,
245 valueToString(assignment.OrigValue))
Andres Moralesda8706f2015-04-29 12:46:49 -0700246 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700247}
248
249func (w *androidMkWriter) iter() <-chan interface{} {
Andres Moralesaf11df12015-04-30 12:14:34 -0700250 ch := make(chan interface{}, len(w.blueprint.Comments)+len(w.blueprint.Defs))
Andres Moralesda8706f2015-04-29 12:46:49 -0700251 go func() {
252 commIdx := 0
253 defsIdx := 0
Andres Moralesaf11df12015-04-30 12:14:34 -0700254 for defsIdx < len(w.blueprint.Defs) || commIdx < len(w.blueprint.Comments) {
255 if defsIdx == len(w.blueprint.Defs) {
256 ch <- w.blueprint.Comments[commIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700257 commIdx++
Andres Moralesaf11df12015-04-30 12:14:34 -0700258 } else if commIdx == len(w.blueprint.Comments) {
259 ch <- w.blueprint.Defs[defsIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700260 defsIdx++
261 } else {
262 commentsPos := 0
263 defsPos := 0
264
Andres Moralesaf11df12015-04-30 12:14:34 -0700265 def := w.blueprint.Defs[defsIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700266 switch def := def.(type) {
267 case *bpparser.Module:
268 defsPos = def.LbracePos.Line
269 case *bpparser.Assignment:
270 defsPos = def.Pos.Line
271 }
272
Andres Moralesaf11df12015-04-30 12:14:34 -0700273 comment := w.blueprint.Comments[commIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700274 commentsPos = comment.Pos.Line
275
276 if commentsPos < defsPos {
277 commIdx++
278 ch <- comment
279 } else {
280 defsIdx++
281 ch <- def
282 }
283 }
284 }
285 close(ch)
286 }()
287 return ch
288}
289
Andres Morales8ae47de2015-05-11 12:26:07 -0700290func (w *androidMkWriter) handleLocalPath() error {
Dan Willemsen360a39c2015-06-11 14:34:50 -0700291 if w.printedLocalPath {
292 return nil
293 }
294 w.printedLocalPath = true
295
Ying Wang38284902015-06-02 18:44:59 -0700296 localPath, err := filepath.Abs(w.path)
Andres Morales8ae47de2015-05-11 12:26:07 -0700297 if err != nil {
298 return err
299 }
300
Ying Wang38284902015-06-02 18:44:59 -0700301 top, err := getTopOfAndroidTree(localPath)
Andres Morales8ae47de2015-05-11 12:26:07 -0700302 if err != nil {
303 return err
304 }
305
Ying Wang38284902015-06-02 18:44:59 -0700306 rel, err := filepath.Rel(top, localPath)
Andres Morales8ae47de2015-05-11 12:26:07 -0700307 if err != nil {
308 return err
309 }
310
311 w.WriteString("LOCAL_PATH := " + rel + "\n")
312 return nil
313}
314
Ying Wang38284902015-06-02 18:44:59 -0700315func (w *androidMkWriter) write(androidMk string) error {
316 fmt.Printf("Writing %s\n", androidMk)
Andres Moralesda8706f2015-04-29 12:46:49 -0700317
Ying Wang38284902015-06-02 18:44:59 -0700318 f, err := os.Create(androidMk)
Andres Moralesda8706f2015-04-29 12:46:49 -0700319 if err != nil {
320 panic(err)
321 }
322
Andres Moralesaf11df12015-04-30 12:14:34 -0700323 defer f.Close()
Andres Moralesda8706f2015-04-29 12:46:49 -0700324
325 w.Writer = bufio.NewWriter(f)
326
327 for block := range w.iter() {
328 switch block := block.(type) {
329 case *bpparser.Module:
Dan Willemsen360a39c2015-06-11 14:34:50 -0700330 if err := w.handleLocalPath(); err != nil {
331 return err
332 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700333 w.handleModule(block)
334 case *bpparser.Assignment:
Dan Willemsen360a39c2015-06-11 14:34:50 -0700335 if err := w.handleLocalPath(); err != nil {
336 return err
337 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700338 w.handleAssignment(block)
339 case bpparser.Comment:
340 w.handleComment(&block)
341 }
342 }
343
344 if err = w.Flush(); err != nil {
345 panic(err)
346 }
Ying Wang38284902015-06-02 18:44:59 -0700347 return nil
Andres Moralesda8706f2015-04-29 12:46:49 -0700348}
349
350func main() {
351 if len(os.Args) < 2 {
352 fmt.Println("No filename supplied")
Ying Wang38284902015-06-02 18:44:59 -0700353 os.Exit(1)
Andres Moralesda8706f2015-04-29 12:46:49 -0700354 }
355
Ying Wang38284902015-06-02 18:44:59 -0700356 androidBp := os.Args[1]
357 var androidMk string
358 if len(os.Args) >= 3 {
359 androidMk = os.Args[2]
360 } else {
361 androidMk = androidBp + ".mk"
362 }
363
364 reader, err := os.Open(androidBp)
Andres Moralesda8706f2015-04-29 12:46:49 -0700365 if err != nil {
366 fmt.Println(err.Error())
Ying Wang38284902015-06-02 18:44:59 -0700367 os.Exit(1)
Andres Moralesda8706f2015-04-29 12:46:49 -0700368 }
369
370 scope := bpparser.NewScope(nil)
Ying Wang38284902015-06-02 18:44:59 -0700371 blueprint, errs := bpparser.Parse(androidBp, reader, scope)
Andres Moralesda8706f2015-04-29 12:46:49 -0700372 if len(errs) > 0 {
Ying Wang38284902015-06-02 18:44:59 -0700373 fmt.Println("%d errors parsing %s", len(errs), androidBp)
Andres Moralesda8706f2015-04-29 12:46:49 -0700374 fmt.Println(errs)
Ying Wang38284902015-06-02 18:44:59 -0700375 os.Exit(1)
Andres Moralesda8706f2015-04-29 12:46:49 -0700376 }
377
378 writer := &androidMkWriter{
Andres Moralesaf11df12015-04-30 12:14:34 -0700379 blueprint: blueprint,
Ying Wang38284902015-06-02 18:44:59 -0700380 path: path.Dir(androidBp),
Andres Moralesaf11df12015-04-30 12:14:34 -0700381 mapScope: make(map[string][]*bpparser.Property),
Andres Moralesda8706f2015-04-29 12:46:49 -0700382 }
383
Ying Wang38284902015-06-02 18:44:59 -0700384 err = writer.write(androidMk)
385 if err != nil {
386 fmt.Println(err.Error())
387 os.Exit(1)
388 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700389}