2015-04-29 21:46:49 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2015-05-11 21:26:07 +02:00
|
|
|
"errors"
|
2015-04-29 21:46:49 +02:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path"
|
2015-05-11 21:26:07 +02:00
|
|
|
"path/filepath"
|
2015-04-30 21:14:34 +02:00
|
|
|
"regexp"
|
2015-04-29 21:46:49 +02:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
bpparser "github.com/google/blueprint/parser"
|
|
|
|
)
|
|
|
|
|
2015-05-11 21:26:07 +02:00
|
|
|
var recursiveSubdirRegex *regexp.Regexp = regexp.MustCompile("(.+)/\\*\\*/(.+)")
|
|
|
|
|
2015-04-29 21:46:49 +02:00
|
|
|
type androidMkWriter struct {
|
|
|
|
*bufio.Writer
|
|
|
|
|
2015-04-30 21:14:34 +02:00
|
|
|
blueprint *bpparser.File
|
|
|
|
path string
|
|
|
|
|
|
|
|
mapScope map[string][]*bpparser.Property
|
2015-04-29 21:46:49 +02:00
|
|
|
}
|
|
|
|
|
2015-04-30 21:14:34 +02:00
|
|
|
func valueToString(value bpparser.Value) string {
|
2015-04-29 21:46:49 +02:00
|
|
|
if value.Variable != "" {
|
|
|
|
return fmt.Sprintf("$(%s)", value.Variable)
|
|
|
|
} else {
|
|
|
|
switch value.Type {
|
|
|
|
case bpparser.Bool:
|
|
|
|
return fmt.Sprintf(`"%t"`, value.BoolValue)
|
|
|
|
case bpparser.String:
|
2015-04-30 21:14:34 +02:00
|
|
|
return fmt.Sprintf(`"%s"`, processWildcards(value.StringValue))
|
2015-04-29 21:46:49 +02:00
|
|
|
case bpparser.List:
|
2015-04-30 21:14:34 +02:00
|
|
|
return fmt.Sprintf("\\\n%s\n", listToMkString(value.ListValue))
|
2015-04-29 21:46:49 +02:00
|
|
|
case bpparser.Map:
|
2015-04-30 21:14:34 +02:00
|
|
|
return fmt.Sprintf("ERROR can't convert map to string")
|
|
|
|
default:
|
|
|
|
return fmt.Sprintf("ERROR: unsupported type %d", value.Type)
|
2015-04-29 21:46:49 +02:00
|
|
|
}
|
|
|
|
}
|
2015-04-30 21:14:34 +02:00
|
|
|
}
|
|
|
|
|
2015-05-11 21:26:07 +02:00
|
|
|
func getTopOfAndroidTree(wd string) (string, error) {
|
|
|
|
if !filepath.IsAbs(wd) {
|
|
|
|
return "", errors.New("path must be absolute: " + wd)
|
|
|
|
}
|
|
|
|
|
|
|
|
topfile := "build/soong/bootstrap.bash"
|
|
|
|
|
|
|
|
for "/" != wd {
|
|
|
|
expected := filepath.Join(wd, topfile)
|
|
|
|
|
|
|
|
if _, err := os.Stat(expected); err == nil {
|
|
|
|
// Found the top
|
|
|
|
return wd, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
wd = filepath.Join(wd, "..")
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", errors.New("couldn't find top of tree from " + wd)
|
|
|
|
}
|
|
|
|
|
2015-04-30 21:14:34 +02:00
|
|
|
// TODO: handle non-recursive wildcards?
|
|
|
|
func processWildcards(s string) string {
|
2015-05-11 21:26:07 +02:00
|
|
|
submatches := recursiveSubdirRegex.FindStringSubmatch(s)
|
|
|
|
if len(submatches) > 2 {
|
2015-04-30 21:14:34 +02:00
|
|
|
// Found a wildcard rule
|
|
|
|
return fmt.Sprintf("$(call find-files-in-subdirs, $(LOCAL_PATH), %s, %s)",
|
2015-05-11 21:26:07 +02:00
|
|
|
submatches[2], submatches[1])
|
2015-04-30 21:14:34 +02:00
|
|
|
}
|
2015-04-29 21:46:49 +02:00
|
|
|
|
2015-04-30 21:14:34 +02:00
|
|
|
return s
|
2015-04-29 21:46:49 +02:00
|
|
|
}
|
|
|
|
|
2015-04-30 21:14:34 +02:00
|
|
|
func listToMkString(list []bpparser.Value) string {
|
2015-04-29 21:46:49 +02:00
|
|
|
lines := make([]string, 0, len(list))
|
|
|
|
for _, tok := range list {
|
2015-04-30 21:14:34 +02:00
|
|
|
if tok.Type == bpparser.String {
|
|
|
|
lines = append(lines, fmt.Sprintf("\t\"%s\"", processWildcards(tok.StringValue)))
|
|
|
|
} else {
|
|
|
|
lines = append(lines, fmt.Sprintf("# ERROR: unsupported type %s in list",
|
|
|
|
tok.Type.String()))
|
|
|
|
}
|
2015-04-29 21:46:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return strings.Join(lines, " \\\n")
|
|
|
|
}
|
|
|
|
|
2015-04-30 21:14:34 +02:00
|
|
|
func translateTargetConditionals(props []*bpparser.Property,
|
|
|
|
disabledBuilds map[string]bool, isHostRule bool) (computedProps []string) {
|
|
|
|
for _, target := range props {
|
|
|
|
conditionals := targetScopedPropertyConditionals
|
|
|
|
if isHostRule {
|
|
|
|
conditionals = hostScopedPropertyConditionals
|
|
|
|
}
|
|
|
|
|
|
|
|
conditional, ok := conditionals[target.Name.Name]
|
|
|
|
if !ok {
|
|
|
|
// not found
|
|
|
|
conditional = fmt.Sprintf(
|
|
|
|
"ifeq(true, true) # ERROR: unsupported conditional host [%s]",
|
|
|
|
target.Name.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
var scopedProps []string
|
|
|
|
for _, targetScopedProp := range target.Value.MapValue {
|
|
|
|
if mkProp, ok := standardProperties[targetScopedProp.Name.Name]; ok {
|
|
|
|
scopedProps = append(scopedProps, fmt.Sprintf("%s += %s",
|
|
|
|
mkProp.string, valueToString(targetScopedProp.Value)))
|
|
|
|
} else if "disabled" == targetScopedProp.Name.Name {
|
|
|
|
if targetScopedProp.Value.BoolValue {
|
|
|
|
disabledBuilds[target.Name.Name] = true
|
|
|
|
} else {
|
|
|
|
delete(disabledBuilds, target.Name.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(scopedProps) > 0 {
|
|
|
|
computedProps = append(computedProps, conditional)
|
|
|
|
computedProps = append(computedProps, scopedProps...)
|
|
|
|
computedProps = append(computedProps, "endif")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func translateSuffixProperties(suffixProps []*bpparser.Property,
|
|
|
|
suffixMap map[string]string) (computedProps []string) {
|
|
|
|
for _, suffixProp := range suffixProps {
|
|
|
|
if suffix, ok := suffixMap[suffixProp.Name.Name]; ok {
|
|
|
|
for _, stdProp := range suffixProp.Value.MapValue {
|
|
|
|
if mkProp, ok := standardProperties[stdProp.Name.Name]; ok {
|
|
|
|
computedProps = append(computedProps, fmt.Sprintf("%s_%s := %s", mkProp.string, suffix, valueToString(stdProp.Value)))
|
|
|
|
} else {
|
|
|
|
computedProps = append(computedProps, fmt.Sprintf("# ERROR: unsupported property %s", stdProp.Name.Name))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-04-29 21:46:49 +02:00
|
|
|
}
|
2015-04-30 21:14:34 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *androidMkWriter) lookupMap(parent bpparser.Value) (mapValue []*bpparser.Property) {
|
|
|
|
if parent.Variable != "" {
|
|
|
|
mapValue = w.mapScope[parent.Variable]
|
|
|
|
} else {
|
|
|
|
mapValue = parent.MapValue
|
|
|
|
}
|
|
|
|
return
|
2015-04-29 21:46:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (w *androidMkWriter) handleComment(comment *bpparser.Comment) {
|
|
|
|
for _, c := range comment.Comment {
|
2015-04-30 21:14:34 +02:00
|
|
|
fmt.Fprintf(w, "#%s\n", c)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *androidMkWriter) writeModule(moduleRule string, props []string,
|
|
|
|
disabledBuilds map[string]bool, isHostRule bool) {
|
|
|
|
disabledConditionals := disabledTargetConditionals
|
|
|
|
if isHostRule {
|
|
|
|
disabledConditionals = disabledHostConditionals
|
2015-04-29 21:46:49 +02:00
|
|
|
}
|
2015-04-30 21:14:34 +02:00
|
|
|
for build, _ := range disabledBuilds {
|
|
|
|
if conditional, ok := disabledConditionals[build]; ok {
|
|
|
|
fmt.Fprintf(w, "%s\n", conditional)
|
|
|
|
defer fmt.Fprintf(w, "endif\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "include $(CLEAR_VARS)\n")
|
|
|
|
fmt.Fprintf(w, "%s\n", strings.Join(props, "\n"))
|
|
|
|
fmt.Fprintf(w, "include $(%s)\n\n", moduleRule)
|
2015-04-29 21:46:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (w *androidMkWriter) handleModule(module *bpparser.Module) {
|
2015-04-30 21:14:34 +02:00
|
|
|
moduleRule := fmt.Sprintf(module.Type.Name)
|
|
|
|
if translation, ok := moduleTypeToRule[module.Type.Name]; ok {
|
|
|
|
moduleRule = translation
|
|
|
|
}
|
|
|
|
|
|
|
|
isHostRule := strings.Contains(moduleRule, "HOST")
|
|
|
|
hostSupported := false
|
|
|
|
standardProps := make([]string, 0, len(module.Properties))
|
|
|
|
disabledBuilds := make(map[string]bool)
|
|
|
|
for _, prop := range module.Properties {
|
|
|
|
if mkProp, ok := standardProperties[prop.Name.Name]; ok {
|
|
|
|
standardProps = append(standardProps, fmt.Sprintf("%s := %s", mkProp.string, valueToString(prop.Value)))
|
|
|
|
} else if suffixMap, ok := suffixProperties[prop.Name.Name]; ok {
|
|
|
|
suffixProps := w.lookupMap(prop.Value)
|
|
|
|
standardProps = append(standardProps, translateSuffixProperties(suffixProps, suffixMap)...)
|
|
|
|
} else if "target" == prop.Name.Name {
|
|
|
|
props := w.lookupMap(prop.Value)
|
|
|
|
standardProps = append(standardProps, translateTargetConditionals(props, disabledBuilds, isHostRule)...)
|
|
|
|
} else if "host_supported" == prop.Name.Name {
|
|
|
|
hostSupported = prop.Value.BoolValue
|
|
|
|
} else {
|
|
|
|
standardProps = append(standardProps, fmt.Sprintf("# ERROR: Unsupported property %s", prop.Name.Name))
|
2015-04-29 21:46:49 +02:00
|
|
|
}
|
2015-04-30 21:14:34 +02:00
|
|
|
}
|
2015-04-29 21:46:49 +02:00
|
|
|
|
2015-04-30 21:14:34 +02:00
|
|
|
// write out target build
|
|
|
|
w.writeModule(moduleRule, standardProps, disabledBuilds, isHostRule)
|
|
|
|
if hostSupported {
|
|
|
|
hostModuleRule := "NO CORRESPONDING HOST RULE" + moduleRule
|
|
|
|
if trans, ok := targetToHostModuleRule[moduleRule]; ok {
|
|
|
|
hostModuleRule = trans
|
|
|
|
}
|
|
|
|
w.writeModule(hostModuleRule, standardProps,
|
|
|
|
disabledBuilds, true)
|
|
|
|
}
|
|
|
|
}
|
2015-04-29 21:46:49 +02:00
|
|
|
|
2015-04-30 21:14:34 +02:00
|
|
|
func (w *androidMkWriter) handleSubdirs(value bpparser.Value) {
|
2015-05-11 21:26:07 +02:00
|
|
|
subdirs := make([]string, 0, len(value.ListValue))
|
|
|
|
for _, tok := range value.ListValue {
|
|
|
|
subdirs = append(subdirs, tok.StringValue)
|
2015-04-29 21:46:49 +02:00
|
|
|
}
|
2015-05-11 21:26:07 +02:00
|
|
|
fmt.Fprintf(w, "include $(wildcard $(addsuffix %s, Android.mk))\n", strings.Join(subdirs, " "))
|
2015-04-29 21:46:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (w *androidMkWriter) handleAssignment(assignment *bpparser.Assignment) {
|
2015-04-30 21:14:34 +02:00
|
|
|
if "subdirs" == assignment.Name.Name {
|
|
|
|
w.handleSubdirs(assignment.OrigValue)
|
|
|
|
} else if assignment.OrigValue.Type == bpparser.Map {
|
|
|
|
// maps may be assigned in Soong, but can only be translated to .mk
|
|
|
|
// in the context of the module
|
|
|
|
w.mapScope[assignment.Name.Name] = assignment.OrigValue.MapValue
|
|
|
|
} else {
|
|
|
|
assigner := ":="
|
|
|
|
if assignment.Assigner != "=" {
|
|
|
|
assigner = assignment.Assigner
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "%s %s %s\n", assignment.Name.Name, assigner,
|
|
|
|
valueToString(assignment.OrigValue))
|
2015-04-29 21:46:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *androidMkWriter) iter() <-chan interface{} {
|
2015-04-30 21:14:34 +02:00
|
|
|
ch := make(chan interface{}, len(w.blueprint.Comments)+len(w.blueprint.Defs))
|
2015-04-29 21:46:49 +02:00
|
|
|
go func() {
|
|
|
|
commIdx := 0
|
|
|
|
defsIdx := 0
|
2015-04-30 21:14:34 +02:00
|
|
|
for defsIdx < len(w.blueprint.Defs) || commIdx < len(w.blueprint.Comments) {
|
|
|
|
if defsIdx == len(w.blueprint.Defs) {
|
|
|
|
ch <- w.blueprint.Comments[commIdx]
|
2015-04-29 21:46:49 +02:00
|
|
|
commIdx++
|
2015-04-30 21:14:34 +02:00
|
|
|
} else if commIdx == len(w.blueprint.Comments) {
|
|
|
|
ch <- w.blueprint.Defs[defsIdx]
|
2015-04-29 21:46:49 +02:00
|
|
|
defsIdx++
|
|
|
|
} else {
|
|
|
|
commentsPos := 0
|
|
|
|
defsPos := 0
|
|
|
|
|
2015-04-30 21:14:34 +02:00
|
|
|
def := w.blueprint.Defs[defsIdx]
|
2015-04-29 21:46:49 +02:00
|
|
|
switch def := def.(type) {
|
|
|
|
case *bpparser.Module:
|
|
|
|
defsPos = def.LbracePos.Line
|
|
|
|
case *bpparser.Assignment:
|
|
|
|
defsPos = def.Pos.Line
|
|
|
|
}
|
|
|
|
|
2015-04-30 21:14:34 +02:00
|
|
|
comment := w.blueprint.Comments[commIdx]
|
2015-04-29 21:46:49 +02:00
|
|
|
commentsPos = comment.Pos.Line
|
|
|
|
|
|
|
|
if commentsPos < defsPos {
|
|
|
|
commIdx++
|
|
|
|
ch <- comment
|
|
|
|
} else {
|
|
|
|
defsIdx++
|
|
|
|
ch <- def
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close(ch)
|
|
|
|
}()
|
|
|
|
return ch
|
|
|
|
}
|
|
|
|
|
2015-05-11 21:26:07 +02:00
|
|
|
func (w *androidMkWriter) handleLocalPath() error {
|
|
|
|
androidMkDir, err := filepath.Abs(w.path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
top, err := getTopOfAndroidTree(androidMkDir)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
rel, err := filepath.Rel(top, androidMkDir)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
w.WriteString("LOCAL_PATH := " + rel + "\n")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-04-29 21:46:49 +02:00
|
|
|
func (w *androidMkWriter) write() {
|
2015-04-30 21:14:34 +02:00
|
|
|
outFilePath := fmt.Sprintf("%s/Androidbp.mk", w.path)
|
2015-04-29 21:46:49 +02:00
|
|
|
fmt.Printf("Writing %s\n", outFilePath)
|
|
|
|
|
|
|
|
f, err := os.Create(outFilePath)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2015-04-30 21:14:34 +02:00
|
|
|
defer f.Close()
|
2015-04-29 21:46:49 +02:00
|
|
|
|
|
|
|
w.Writer = bufio.NewWriter(f)
|
|
|
|
|
2015-05-11 21:26:07 +02:00
|
|
|
if err := w.handleLocalPath(); err != nil {
|
|
|
|
fmt.Println(err.Error())
|
|
|
|
return
|
|
|
|
}
|
2015-04-30 21:14:34 +02:00
|
|
|
|
2015-04-29 21:46:49 +02:00
|
|
|
for block := range w.iter() {
|
|
|
|
switch block := block.(type) {
|
|
|
|
case *bpparser.Module:
|
|
|
|
w.handleModule(block)
|
|
|
|
case *bpparser.Assignment:
|
|
|
|
w.handleAssignment(block)
|
|
|
|
case bpparser.Comment:
|
|
|
|
w.handleComment(&block)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = w.Flush(); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
if len(os.Args) < 2 {
|
|
|
|
fmt.Println("No filename supplied")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
reader, err := os.Open(os.Args[1])
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
scope := bpparser.NewScope(nil)
|
2015-04-30 21:14:34 +02:00
|
|
|
blueprint, errs := bpparser.Parse(os.Args[1], reader, scope)
|
2015-04-29 21:46:49 +02:00
|
|
|
if len(errs) > 0 {
|
|
|
|
fmt.Println("%d errors parsing %s", len(errs), os.Args[1])
|
|
|
|
fmt.Println(errs)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
writer := &androidMkWriter{
|
2015-04-30 21:14:34 +02:00
|
|
|
blueprint: blueprint,
|
|
|
|
path: path.Dir(os.Args[1]),
|
|
|
|
mapScope: make(map[string][]*bpparser.Property),
|
2015-04-29 21:46:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
writer.write()
|
|
|
|
}
|