blob: 6e0bc7f75ec347431e4987a5dcac39a42fee61af [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
24 mapScope map[string][]*bpparser.Property
Andres Moralesda8706f2015-04-29 12:46:49 -070025}
26
Andres Moralesaf11df12015-04-30 12:14:34 -070027func valueToString(value bpparser.Value) string {
Andres Moralesda8706f2015-04-29 12:46:49 -070028 if value.Variable != "" {
29 return fmt.Sprintf("$(%s)", value.Variable)
30 } else {
31 switch value.Type {
32 case bpparser.Bool:
Ying Wang38284902015-06-02 18:44:59 -070033 return fmt.Sprintf("%t", value.BoolValue)
Andres Moralesda8706f2015-04-29 12:46:49 -070034 case bpparser.String:
Ying Wang38284902015-06-02 18:44:59 -070035 return fmt.Sprintf("%s", processWildcards(value.StringValue))
Andres Moralesda8706f2015-04-29 12:46:49 -070036 case bpparser.List:
Andres Moralesaf11df12015-04-30 12:14:34 -070037 return fmt.Sprintf("\\\n%s\n", listToMkString(value.ListValue))
Andres Moralesda8706f2015-04-29 12:46:49 -070038 case bpparser.Map:
Andres Moralesaf11df12015-04-30 12:14:34 -070039 return fmt.Sprintf("ERROR can't convert map to string")
40 default:
41 return fmt.Sprintf("ERROR: unsupported type %d", value.Type)
Andres Moralesda8706f2015-04-29 12:46:49 -070042 }
43 }
Andres Moralesda8706f2015-04-29 12:46:49 -070044}
45
Andres Morales8ae47de2015-05-11 12:26:07 -070046func getTopOfAndroidTree(wd string) (string, error) {
47 if !filepath.IsAbs(wd) {
48 return "", errors.New("path must be absolute: " + wd)
49 }
50
51 topfile := "build/soong/bootstrap.bash"
52
53 for "/" != wd {
54 expected := filepath.Join(wd, topfile)
55
56 if _, err := os.Stat(expected); err == nil {
57 // Found the top
58 return wd, nil
59 }
60
61 wd = filepath.Join(wd, "..")
62 }
63
64 return "", errors.New("couldn't find top of tree from " + wd)
65}
66
Andres Moralesaf11df12015-04-30 12:14:34 -070067// TODO: handle non-recursive wildcards?
68func processWildcards(s string) string {
Andres Morales8ae47de2015-05-11 12:26:07 -070069 submatches := recursiveSubdirRegex.FindStringSubmatch(s)
70 if len(submatches) > 2 {
Andres Moralesaf11df12015-04-30 12:14:34 -070071 // Found a wildcard rule
72 return fmt.Sprintf("$(call find-files-in-subdirs, $(LOCAL_PATH), %s, %s)",
Andres Morales8ae47de2015-05-11 12:26:07 -070073 submatches[2], submatches[1])
Andres Moralesaf11df12015-04-30 12:14:34 -070074 }
75
76 return s
77}
78
79func listToMkString(list []bpparser.Value) string {
Andres Moralesda8706f2015-04-29 12:46:49 -070080 lines := make([]string, 0, len(list))
81 for _, tok := range list {
Andres Moralesaf11df12015-04-30 12:14:34 -070082 if tok.Type == bpparser.String {
Ying Wang38284902015-06-02 18:44:59 -070083 lines = append(lines, fmt.Sprintf(" %s", processWildcards(tok.StringValue)))
Andres Moralesaf11df12015-04-30 12:14:34 -070084 } else {
85 lines = append(lines, fmt.Sprintf("# ERROR: unsupported type %s in list",
86 tok.Type.String()))
87 }
Andres Moralesda8706f2015-04-29 12:46:49 -070088 }
89
90 return strings.Join(lines, " \\\n")
91}
92
Andres Moralesaf11df12015-04-30 12:14:34 -070093func translateTargetConditionals(props []*bpparser.Property,
94 disabledBuilds map[string]bool, isHostRule bool) (computedProps []string) {
95 for _, target := range props {
96 conditionals := targetScopedPropertyConditionals
97 if isHostRule {
98 conditionals = hostScopedPropertyConditionals
99 }
100
101 conditional, ok := conditionals[target.Name.Name]
102 if !ok {
103 // not found
104 conditional = fmt.Sprintf(
105 "ifeq(true, true) # ERROR: unsupported conditional host [%s]",
106 target.Name.Name)
107 }
108
109 var scopedProps []string
110 for _, targetScopedProp := range target.Value.MapValue {
111 if mkProp, ok := standardProperties[targetScopedProp.Name.Name]; ok {
112 scopedProps = append(scopedProps, fmt.Sprintf("%s += %s",
113 mkProp.string, valueToString(targetScopedProp.Value)))
114 } else if "disabled" == targetScopedProp.Name.Name {
115 if targetScopedProp.Value.BoolValue {
116 disabledBuilds[target.Name.Name] = true
117 } else {
118 delete(disabledBuilds, target.Name.Name)
119 }
120 }
121 }
122
123 if len(scopedProps) > 0 {
124 computedProps = append(computedProps, conditional)
125 computedProps = append(computedProps, scopedProps...)
126 computedProps = append(computedProps, "endif")
127 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700128 }
Andres Moralesaf11df12015-04-30 12:14:34 -0700129
130 return
131}
132
133func translateSuffixProperties(suffixProps []*bpparser.Property,
134 suffixMap map[string]string) (computedProps []string) {
135 for _, suffixProp := range suffixProps {
136 if suffix, ok := suffixMap[suffixProp.Name.Name]; ok {
137 for _, stdProp := range suffixProp.Value.MapValue {
138 if mkProp, ok := standardProperties[stdProp.Name.Name]; ok {
139 computedProps = append(computedProps, fmt.Sprintf("%s_%s := %s", mkProp.string, suffix, valueToString(stdProp.Value)))
140 } else {
141 computedProps = append(computedProps, fmt.Sprintf("# ERROR: unsupported property %s", stdProp.Name.Name))
142 }
143 }
144 }
145 }
146 return
147}
148
149func (w *androidMkWriter) lookupMap(parent bpparser.Value) (mapValue []*bpparser.Property) {
150 if parent.Variable != "" {
151 mapValue = w.mapScope[parent.Variable]
152 } else {
153 mapValue = parent.MapValue
154 }
155 return
Andres Moralesda8706f2015-04-29 12:46:49 -0700156}
157
158func (w *androidMkWriter) handleComment(comment *bpparser.Comment) {
159 for _, c := range comment.Comment {
Andres Moralesaf11df12015-04-30 12:14:34 -0700160 fmt.Fprintf(w, "#%s\n", c)
Andres Moralesda8706f2015-04-29 12:46:49 -0700161 }
162}
163
Andres Moralesaf11df12015-04-30 12:14:34 -0700164func (w *androidMkWriter) writeModule(moduleRule string, props []string,
165 disabledBuilds map[string]bool, isHostRule bool) {
166 disabledConditionals := disabledTargetConditionals
167 if isHostRule {
168 disabledConditionals = disabledHostConditionals
169 }
170 for build, _ := range disabledBuilds {
171 if conditional, ok := disabledConditionals[build]; ok {
172 fmt.Fprintf(w, "%s\n", conditional)
173 defer fmt.Fprintf(w, "endif\n")
Andres Moralesda8706f2015-04-29 12:46:49 -0700174 }
Andres Moralesaf11df12015-04-30 12:14:34 -0700175 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700176
Andres Moralesaf11df12015-04-30 12:14:34 -0700177 fmt.Fprintf(w, "include $(CLEAR_VARS)\n")
178 fmt.Fprintf(w, "%s\n", strings.Join(props, "\n"))
179 fmt.Fprintf(w, "include $(%s)\n\n", moduleRule)
180}
Andres Moralesda8706f2015-04-29 12:46:49 -0700181
Andres Moralesaf11df12015-04-30 12:14:34 -0700182func (w *androidMkWriter) handleModule(module *bpparser.Module) {
183 moduleRule := fmt.Sprintf(module.Type.Name)
184 if translation, ok := moduleTypeToRule[module.Type.Name]; ok {
185 moduleRule = translation
186 }
187
188 isHostRule := strings.Contains(moduleRule, "HOST")
189 hostSupported := false
190 standardProps := make([]string, 0, len(module.Properties))
191 disabledBuilds := make(map[string]bool)
192 for _, prop := range module.Properties {
193 if mkProp, ok := standardProperties[prop.Name.Name]; ok {
194 standardProps = append(standardProps, fmt.Sprintf("%s := %s", mkProp.string, valueToString(prop.Value)))
195 } else if suffixMap, ok := suffixProperties[prop.Name.Name]; ok {
196 suffixProps := w.lookupMap(prop.Value)
197 standardProps = append(standardProps, translateSuffixProperties(suffixProps, suffixMap)...)
198 } else if "target" == prop.Name.Name {
199 props := w.lookupMap(prop.Value)
200 standardProps = append(standardProps, translateTargetConditionals(props, disabledBuilds, isHostRule)...)
201 } else if "host_supported" == prop.Name.Name {
202 hostSupported = prop.Value.BoolValue
203 } else {
204 standardProps = append(standardProps, fmt.Sprintf("# ERROR: Unsupported property %s", prop.Name.Name))
205 }
206 }
207
208 // write out target build
209 w.writeModule(moduleRule, standardProps, disabledBuilds, isHostRule)
210 if hostSupported {
211 hostModuleRule := "NO CORRESPONDING HOST RULE" + moduleRule
212 if trans, ok := targetToHostModuleRule[moduleRule]; ok {
213 hostModuleRule = trans
214 }
215 w.writeModule(hostModuleRule, standardProps,
216 disabledBuilds, true)
217 }
218}
219
220func (w *androidMkWriter) handleSubdirs(value bpparser.Value) {
Andres Morales8ae47de2015-05-11 12:26:07 -0700221 subdirs := make([]string, 0, len(value.ListValue))
222 for _, tok := range value.ListValue {
223 subdirs = append(subdirs, tok.StringValue)
Andres Moralesda8706f2015-04-29 12:46:49 -0700224 }
Ying Wang38284902015-06-02 18:44:59 -0700225 // The current makefile may be generated to outside the source tree (such as the out directory), with a different structure.
226 fmt.Fprintf(w, "# Uncomment the following line if you really want to include subdir Android.mks.\n")
227 fmt.Fprintf(w, "# include $(wildcard $(addsuffix $(LOCAL_PATH)/%s/, Android.mk))\n", strings.Join(subdirs, " "))
Andres Moralesda8706f2015-04-29 12:46:49 -0700228}
229
230func (w *androidMkWriter) handleAssignment(assignment *bpparser.Assignment) {
Andres Moralesaf11df12015-04-30 12:14:34 -0700231 if "subdirs" == assignment.Name.Name {
232 w.handleSubdirs(assignment.OrigValue)
233 } else if assignment.OrigValue.Type == bpparser.Map {
234 // maps may be assigned in Soong, but can only be translated to .mk
235 // in the context of the module
236 w.mapScope[assignment.Name.Name] = assignment.OrigValue.MapValue
237 } else {
238 assigner := ":="
239 if assignment.Assigner != "=" {
240 assigner = assignment.Assigner
241 }
242 fmt.Fprintf(w, "%s %s %s\n", assignment.Name.Name, assigner,
243 valueToString(assignment.OrigValue))
Andres Moralesda8706f2015-04-29 12:46:49 -0700244 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700245}
246
247func (w *androidMkWriter) iter() <-chan interface{} {
Andres Moralesaf11df12015-04-30 12:14:34 -0700248 ch := make(chan interface{}, len(w.blueprint.Comments)+len(w.blueprint.Defs))
Andres Moralesda8706f2015-04-29 12:46:49 -0700249 go func() {
250 commIdx := 0
251 defsIdx := 0
Andres Moralesaf11df12015-04-30 12:14:34 -0700252 for defsIdx < len(w.blueprint.Defs) || commIdx < len(w.blueprint.Comments) {
253 if defsIdx == len(w.blueprint.Defs) {
254 ch <- w.blueprint.Comments[commIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700255 commIdx++
Andres Moralesaf11df12015-04-30 12:14:34 -0700256 } else if commIdx == len(w.blueprint.Comments) {
257 ch <- w.blueprint.Defs[defsIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700258 defsIdx++
259 } else {
260 commentsPos := 0
261 defsPos := 0
262
Andres Moralesaf11df12015-04-30 12:14:34 -0700263 def := w.blueprint.Defs[defsIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700264 switch def := def.(type) {
265 case *bpparser.Module:
266 defsPos = def.LbracePos.Line
267 case *bpparser.Assignment:
268 defsPos = def.Pos.Line
269 }
270
Andres Moralesaf11df12015-04-30 12:14:34 -0700271 comment := w.blueprint.Comments[commIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700272 commentsPos = comment.Pos.Line
273
274 if commentsPos < defsPos {
275 commIdx++
276 ch <- comment
277 } else {
278 defsIdx++
279 ch <- def
280 }
281 }
282 }
283 close(ch)
284 }()
285 return ch
286}
287
Andres Morales8ae47de2015-05-11 12:26:07 -0700288func (w *androidMkWriter) handleLocalPath() error {
Ying Wang38284902015-06-02 18:44:59 -0700289 localPath, err := filepath.Abs(w.path)
Andres Morales8ae47de2015-05-11 12:26:07 -0700290 if err != nil {
291 return err
292 }
293
Ying Wang38284902015-06-02 18:44:59 -0700294 top, err := getTopOfAndroidTree(localPath)
Andres Morales8ae47de2015-05-11 12:26:07 -0700295 if err != nil {
296 return err
297 }
298
Ying Wang38284902015-06-02 18:44:59 -0700299 rel, err := filepath.Rel(top, localPath)
Andres Morales8ae47de2015-05-11 12:26:07 -0700300 if err != nil {
301 return err
302 }
303
304 w.WriteString("LOCAL_PATH := " + rel + "\n")
305 return nil
306}
307
Ying Wang38284902015-06-02 18:44:59 -0700308func (w *androidMkWriter) write(androidMk string) error {
309 fmt.Printf("Writing %s\n", androidMk)
Andres Moralesda8706f2015-04-29 12:46:49 -0700310
Ying Wang38284902015-06-02 18:44:59 -0700311 f, err := os.Create(androidMk)
Andres Moralesda8706f2015-04-29 12:46:49 -0700312 if err != nil {
313 panic(err)
314 }
315
Andres Moralesaf11df12015-04-30 12:14:34 -0700316 defer f.Close()
Andres Moralesda8706f2015-04-29 12:46:49 -0700317
318 w.Writer = bufio.NewWriter(f)
319
Andres Morales8ae47de2015-05-11 12:26:07 -0700320 if err := w.handleLocalPath(); err != nil {
Ying Wang38284902015-06-02 18:44:59 -0700321 return err
Andres Morales8ae47de2015-05-11 12:26:07 -0700322 }
Andres Moralesaf11df12015-04-30 12:14:34 -0700323
Andres Moralesda8706f2015-04-29 12:46:49 -0700324 for block := range w.iter() {
325 switch block := block.(type) {
326 case *bpparser.Module:
327 w.handleModule(block)
328 case *bpparser.Assignment:
329 w.handleAssignment(block)
330 case bpparser.Comment:
331 w.handleComment(&block)
332 }
333 }
334
335 if err = w.Flush(); err != nil {
336 panic(err)
337 }
Ying Wang38284902015-06-02 18:44:59 -0700338 return nil
Andres Moralesda8706f2015-04-29 12:46:49 -0700339}
340
341func main() {
342 if len(os.Args) < 2 {
343 fmt.Println("No filename supplied")
Ying Wang38284902015-06-02 18:44:59 -0700344 os.Exit(1)
Andres Moralesda8706f2015-04-29 12:46:49 -0700345 }
346
Ying Wang38284902015-06-02 18:44:59 -0700347 androidBp := os.Args[1]
348 var androidMk string
349 if len(os.Args) >= 3 {
350 androidMk = os.Args[2]
351 } else {
352 androidMk = androidBp + ".mk"
353 }
354
355 reader, err := os.Open(androidBp)
Andres Moralesda8706f2015-04-29 12:46:49 -0700356 if err != nil {
357 fmt.Println(err.Error())
Ying Wang38284902015-06-02 18:44:59 -0700358 os.Exit(1)
Andres Moralesda8706f2015-04-29 12:46:49 -0700359 }
360
361 scope := bpparser.NewScope(nil)
Ying Wang38284902015-06-02 18:44:59 -0700362 blueprint, errs := bpparser.Parse(androidBp, reader, scope)
Andres Moralesda8706f2015-04-29 12:46:49 -0700363 if len(errs) > 0 {
Ying Wang38284902015-06-02 18:44:59 -0700364 fmt.Println("%d errors parsing %s", len(errs), androidBp)
Andres Moralesda8706f2015-04-29 12:46:49 -0700365 fmt.Println(errs)
Ying Wang38284902015-06-02 18:44:59 -0700366 os.Exit(1)
Andres Moralesda8706f2015-04-29 12:46:49 -0700367 }
368
369 writer := &androidMkWriter{
Andres Moralesaf11df12015-04-30 12:14:34 -0700370 blueprint: blueprint,
Ying Wang38284902015-06-02 18:44:59 -0700371 path: path.Dir(androidBp),
Andres Moralesaf11df12015-04-30 12:14:34 -0700372 mapScope: make(map[string][]*bpparser.Property),
Andres Moralesda8706f2015-04-29 12:46:49 -0700373 }
374
Ying Wang38284902015-06-02 18:44:59 -0700375 err = writer.write(androidMk)
376 if err != nil {
377 fmt.Println(err.Error())
378 os.Exit(1)
379 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700380}