blob: 2e0d25c4c0d7724f2d977ee541969f45fff63369 [file] [log] [blame]
Andres Moralesda8706f2015-04-29 12:46:49 -07001package main
2
3import (
4 "bufio"
5 "fmt"
6 "os"
7 "path"
Andres Moralesaf11df12015-04-30 12:14:34 -07008 "regexp"
Andres Moralesda8706f2015-04-29 12:46:49 -07009 "strings"
10
11 bpparser "github.com/google/blueprint/parser"
12)
13
14type androidMkWriter struct {
15 *bufio.Writer
16
Andres Moralesaf11df12015-04-30 12:14:34 -070017 blueprint *bpparser.File
18 path string
19
20 mapScope map[string][]*bpparser.Property
Andres Moralesda8706f2015-04-29 12:46:49 -070021}
22
Andres Moralesaf11df12015-04-30 12:14:34 -070023func valueToString(value bpparser.Value) string {
Andres Moralesda8706f2015-04-29 12:46:49 -070024 if value.Variable != "" {
25 return fmt.Sprintf("$(%s)", value.Variable)
26 } else {
27 switch value.Type {
28 case bpparser.Bool:
29 return fmt.Sprintf(`"%t"`, value.BoolValue)
30 case bpparser.String:
Andres Moralesaf11df12015-04-30 12:14:34 -070031 return fmt.Sprintf(`"%s"`, processWildcards(value.StringValue))
Andres Moralesda8706f2015-04-29 12:46:49 -070032 case bpparser.List:
Andres Moralesaf11df12015-04-30 12:14:34 -070033 return fmt.Sprintf("\\\n%s\n", listToMkString(value.ListValue))
Andres Moralesda8706f2015-04-29 12:46:49 -070034 case bpparser.Map:
Andres Moralesaf11df12015-04-30 12:14:34 -070035 return fmt.Sprintf("ERROR can't convert map to string")
36 default:
37 return fmt.Sprintf("ERROR: unsupported type %d", value.Type)
Andres Moralesda8706f2015-04-29 12:46:49 -070038 }
39 }
Andres Moralesda8706f2015-04-29 12:46:49 -070040}
41
Andres Moralesaf11df12015-04-30 12:14:34 -070042// TODO: handle non-recursive wildcards?
43func processWildcards(s string) string {
44 re := regexp.MustCompile("(.*)/\\*\\*/(.*)")
45 submatches := re.FindAllStringSubmatch(s, -1)
46 if submatches != nil && len(submatches[0]) > 2 {
47 // Found a wildcard rule
48 return fmt.Sprintf("$(call find-files-in-subdirs, $(LOCAL_PATH), %s, %s)",
49 submatches[0][2], submatches[0][1])
50 }
51
52 return s
53}
54
55func listToMkString(list []bpparser.Value) string {
Andres Moralesda8706f2015-04-29 12:46:49 -070056 lines := make([]string, 0, len(list))
57 for _, tok := range list {
Andres Moralesaf11df12015-04-30 12:14:34 -070058 if tok.Type == bpparser.String {
59 lines = append(lines, fmt.Sprintf("\t\"%s\"", processWildcards(tok.StringValue)))
60 } else {
61 lines = append(lines, fmt.Sprintf("# ERROR: unsupported type %s in list",
62 tok.Type.String()))
63 }
Andres Moralesda8706f2015-04-29 12:46:49 -070064 }
65
66 return strings.Join(lines, " \\\n")
67}
68
Andres Moralesaf11df12015-04-30 12:14:34 -070069func translateTargetConditionals(props []*bpparser.Property,
70 disabledBuilds map[string]bool, isHostRule bool) (computedProps []string) {
71 for _, target := range props {
72 conditionals := targetScopedPropertyConditionals
73 if isHostRule {
74 conditionals = hostScopedPropertyConditionals
75 }
76
77 conditional, ok := conditionals[target.Name.Name]
78 if !ok {
79 // not found
80 conditional = fmt.Sprintf(
81 "ifeq(true, true) # ERROR: unsupported conditional host [%s]",
82 target.Name.Name)
83 }
84
85 var scopedProps []string
86 for _, targetScopedProp := range target.Value.MapValue {
87 if mkProp, ok := standardProperties[targetScopedProp.Name.Name]; ok {
88 scopedProps = append(scopedProps, fmt.Sprintf("%s += %s",
89 mkProp.string, valueToString(targetScopedProp.Value)))
90 } else if "disabled" == targetScopedProp.Name.Name {
91 if targetScopedProp.Value.BoolValue {
92 disabledBuilds[target.Name.Name] = true
93 } else {
94 delete(disabledBuilds, target.Name.Name)
95 }
96 }
97 }
98
99 if len(scopedProps) > 0 {
100 computedProps = append(computedProps, conditional)
101 computedProps = append(computedProps, scopedProps...)
102 computedProps = append(computedProps, "endif")
103 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700104 }
Andres Moralesaf11df12015-04-30 12:14:34 -0700105
106 return
107}
108
109func translateSuffixProperties(suffixProps []*bpparser.Property,
110 suffixMap map[string]string) (computedProps []string) {
111 for _, suffixProp := range suffixProps {
112 if suffix, ok := suffixMap[suffixProp.Name.Name]; ok {
113 for _, stdProp := range suffixProp.Value.MapValue {
114 if mkProp, ok := standardProperties[stdProp.Name.Name]; ok {
115 computedProps = append(computedProps, fmt.Sprintf("%s_%s := %s", mkProp.string, suffix, valueToString(stdProp.Value)))
116 } else {
117 computedProps = append(computedProps, fmt.Sprintf("# ERROR: unsupported property %s", stdProp.Name.Name))
118 }
119 }
120 }
121 }
122 return
123}
124
125func (w *androidMkWriter) lookupMap(parent bpparser.Value) (mapValue []*bpparser.Property) {
126 if parent.Variable != "" {
127 mapValue = w.mapScope[parent.Variable]
128 } else {
129 mapValue = parent.MapValue
130 }
131 return
Andres Moralesda8706f2015-04-29 12:46:49 -0700132}
133
134func (w *androidMkWriter) handleComment(comment *bpparser.Comment) {
135 for _, c := range comment.Comment {
Andres Moralesaf11df12015-04-30 12:14:34 -0700136 fmt.Fprintf(w, "#%s\n", c)
Andres Moralesda8706f2015-04-29 12:46:49 -0700137 }
138}
139
Andres Moralesaf11df12015-04-30 12:14:34 -0700140func (w *androidMkWriter) writeModule(moduleRule string, props []string,
141 disabledBuilds map[string]bool, isHostRule bool) {
142 disabledConditionals := disabledTargetConditionals
143 if isHostRule {
144 disabledConditionals = disabledHostConditionals
145 }
146 for build, _ := range disabledBuilds {
147 if conditional, ok := disabledConditionals[build]; ok {
148 fmt.Fprintf(w, "%s\n", conditional)
149 defer fmt.Fprintf(w, "endif\n")
Andres Moralesda8706f2015-04-29 12:46:49 -0700150 }
Andres Moralesaf11df12015-04-30 12:14:34 -0700151 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700152
Andres Moralesaf11df12015-04-30 12:14:34 -0700153 fmt.Fprintf(w, "include $(CLEAR_VARS)\n")
154 fmt.Fprintf(w, "%s\n", strings.Join(props, "\n"))
155 fmt.Fprintf(w, "include $(%s)\n\n", moduleRule)
156}
Andres Moralesda8706f2015-04-29 12:46:49 -0700157
Andres Moralesaf11df12015-04-30 12:14:34 -0700158func (w *androidMkWriter) handleModule(module *bpparser.Module) {
159 moduleRule := fmt.Sprintf(module.Type.Name)
160 if translation, ok := moduleTypeToRule[module.Type.Name]; ok {
161 moduleRule = translation
162 }
163
164 isHostRule := strings.Contains(moduleRule, "HOST")
165 hostSupported := false
166 standardProps := make([]string, 0, len(module.Properties))
167 disabledBuilds := make(map[string]bool)
168 for _, prop := range module.Properties {
169 if mkProp, ok := standardProperties[prop.Name.Name]; ok {
170 standardProps = append(standardProps, fmt.Sprintf("%s := %s", mkProp.string, valueToString(prop.Value)))
171 } else if suffixMap, ok := suffixProperties[prop.Name.Name]; ok {
172 suffixProps := w.lookupMap(prop.Value)
173 standardProps = append(standardProps, translateSuffixProperties(suffixProps, suffixMap)...)
174 } else if "target" == prop.Name.Name {
175 props := w.lookupMap(prop.Value)
176 standardProps = append(standardProps, translateTargetConditionals(props, disabledBuilds, isHostRule)...)
177 } else if "host_supported" == prop.Name.Name {
178 hostSupported = prop.Value.BoolValue
179 } else {
180 standardProps = append(standardProps, fmt.Sprintf("# ERROR: Unsupported property %s", prop.Name.Name))
181 }
182 }
183
184 // write out target build
185 w.writeModule(moduleRule, standardProps, disabledBuilds, isHostRule)
186 if hostSupported {
187 hostModuleRule := "NO CORRESPONDING HOST RULE" + moduleRule
188 if trans, ok := targetToHostModuleRule[moduleRule]; ok {
189 hostModuleRule = trans
190 }
191 w.writeModule(hostModuleRule, standardProps,
192 disabledBuilds, true)
193 }
194}
195
196func (w *androidMkWriter) handleSubdirs(value bpparser.Value) {
197 switch value.Type {
198 case bpparser.String:
199 fmt.Fprintf(w, "$(call all-makefiles-under, %s)\n", value.StringValue)
200 case bpparser.List:
201 for _, tok := range value.ListValue {
202 fmt.Fprintf(w, "$(call all-makefiles-under, %s)\n", tok.StringValue)
203 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700204 }
205}
206
207func (w *androidMkWriter) handleAssignment(assignment *bpparser.Assignment) {
Andres Moralesaf11df12015-04-30 12:14:34 -0700208 if "subdirs" == assignment.Name.Name {
209 w.handleSubdirs(assignment.OrigValue)
210 } else if assignment.OrigValue.Type == bpparser.Map {
211 // maps may be assigned in Soong, but can only be translated to .mk
212 // in the context of the module
213 w.mapScope[assignment.Name.Name] = assignment.OrigValue.MapValue
214 } else {
215 assigner := ":="
216 if assignment.Assigner != "=" {
217 assigner = assignment.Assigner
218 }
219 fmt.Fprintf(w, "%s %s %s\n", assignment.Name.Name, assigner,
220 valueToString(assignment.OrigValue))
Andres Moralesda8706f2015-04-29 12:46:49 -0700221 }
Andres Moralesda8706f2015-04-29 12:46:49 -0700222}
223
224func (w *androidMkWriter) iter() <-chan interface{} {
Andres Moralesaf11df12015-04-30 12:14:34 -0700225 ch := make(chan interface{}, len(w.blueprint.Comments)+len(w.blueprint.Defs))
Andres Moralesda8706f2015-04-29 12:46:49 -0700226 go func() {
227 commIdx := 0
228 defsIdx := 0
Andres Moralesaf11df12015-04-30 12:14:34 -0700229 for defsIdx < len(w.blueprint.Defs) || commIdx < len(w.blueprint.Comments) {
230 if defsIdx == len(w.blueprint.Defs) {
231 ch <- w.blueprint.Comments[commIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700232 commIdx++
Andres Moralesaf11df12015-04-30 12:14:34 -0700233 } else if commIdx == len(w.blueprint.Comments) {
234 ch <- w.blueprint.Defs[defsIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700235 defsIdx++
236 } else {
237 commentsPos := 0
238 defsPos := 0
239
Andres Moralesaf11df12015-04-30 12:14:34 -0700240 def := w.blueprint.Defs[defsIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700241 switch def := def.(type) {
242 case *bpparser.Module:
243 defsPos = def.LbracePos.Line
244 case *bpparser.Assignment:
245 defsPos = def.Pos.Line
246 }
247
Andres Moralesaf11df12015-04-30 12:14:34 -0700248 comment := w.blueprint.Comments[commIdx]
Andres Moralesda8706f2015-04-29 12:46:49 -0700249 commentsPos = comment.Pos.Line
250
251 if commentsPos < defsPos {
252 commIdx++
253 ch <- comment
254 } else {
255 defsIdx++
256 ch <- def
257 }
258 }
259 }
260 close(ch)
261 }()
262 return ch
263}
264
265func (w *androidMkWriter) write() {
Andres Moralesaf11df12015-04-30 12:14:34 -0700266 outFilePath := fmt.Sprintf("%s/Androidbp.mk", w.path)
Andres Moralesda8706f2015-04-29 12:46:49 -0700267 fmt.Printf("Writing %s\n", outFilePath)
268
269 f, err := os.Create(outFilePath)
270 if err != nil {
271 panic(err)
272 }
273
Andres Moralesaf11df12015-04-30 12:14:34 -0700274 defer f.Close()
Andres Moralesda8706f2015-04-29 12:46:49 -0700275
276 w.Writer = bufio.NewWriter(f)
277
Andres Moralesaf11df12015-04-30 12:14:34 -0700278 w.WriteString("LOCAL_PATH := $(call my-dir)\n")
279
Andres Moralesda8706f2015-04-29 12:46:49 -0700280 for block := range w.iter() {
281 switch block := block.(type) {
282 case *bpparser.Module:
283 w.handleModule(block)
284 case *bpparser.Assignment:
285 w.handleAssignment(block)
286 case bpparser.Comment:
287 w.handleComment(&block)
288 }
289 }
290
291 if err = w.Flush(); err != nil {
292 panic(err)
293 }
294}
295
296func main() {
297 if len(os.Args) < 2 {
298 fmt.Println("No filename supplied")
299 return
300 }
301
302 reader, err := os.Open(os.Args[1])
303 if err != nil {
304 fmt.Println(err.Error())
305 return
306 }
307
308 scope := bpparser.NewScope(nil)
Andres Moralesaf11df12015-04-30 12:14:34 -0700309 blueprint, errs := bpparser.Parse(os.Args[1], reader, scope)
Andres Moralesda8706f2015-04-29 12:46:49 -0700310 if len(errs) > 0 {
311 fmt.Println("%d errors parsing %s", len(errs), os.Args[1])
312 fmt.Println(errs)
313 return
314 }
315
316 writer := &androidMkWriter{
Andres Moralesaf11df12015-04-30 12:14:34 -0700317 blueprint: blueprint,
318 path: path.Dir(os.Args[1]),
319 mapScope: make(map[string][]*bpparser.Property),
Andres Moralesda8706f2015-04-29 12:46:49 -0700320 }
321
322 writer.write()
323}