aboutsummaryrefslogtreecommitdiffstats
path: root/bpfix/bpfix/bpfix.go
diff options
context:
space:
mode:
Diffstat (limited to 'bpfix/bpfix/bpfix.go')
-rw-r--r--bpfix/bpfix/bpfix.go133
1 files changed, 133 insertions, 0 deletions
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index 4300e81b..4ab42a4e 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -26,11 +26,27 @@ import (
"github.com/google/blueprint/parser"
)
+// Reformat takes a blueprint file as a string and returns a formatted version
+func Reformat(input string) (string, error) {
+ tree, err := parse("<string>", bytes.NewBufferString(input))
+ if err != nil {
+ return "", err
+ }
+
+ res, err := parser.Print(tree)
+ if err != nil {
+ return "", err
+ }
+
+ return string(res), nil
+}
+
// A FixRequest specifies the details of which fixes to apply to an individual file
// A FixRequest doesn't specify whether to do a dry run or where to write the results; that's in cmd/bpfix.go
type FixRequest struct {
simplifyKnownRedundantVariables bool
rewriteIncorrectAndroidmkPrebuilts bool
+ mergeMatchingModuleProperties bool
}
func NewFixRequest() FixRequest {
@@ -41,6 +57,7 @@ func (r FixRequest) AddAll() (result FixRequest) {
result = r
result.simplifyKnownRedundantVariables = true
result.rewriteIncorrectAndroidmkPrebuilts = true
+ result.mergeMatchingModuleProperties = true
return result
}
@@ -136,6 +153,13 @@ func (f *Fixer) fixTreeOnce(config FixRequest) error {
return err
}
}
+
+ if config.mergeMatchingModuleProperties {
+ err := f.mergeMatchingModuleProperties()
+ if err != nil {
+ return err
+ }
+ }
return nil
}
@@ -179,6 +203,115 @@ func (f *Fixer) rewriteIncorrectAndroidmkPrebuilts() error {
return nil
}
+func (f *Fixer) mergeMatchingModuleProperties() error {
+ // Make sure all the offsets are accurate
+ buf, err := f.reparse()
+ if err != nil {
+ return err
+ }
+
+ var patchlist parser.PatchList
+ for _, def := range f.tree.Defs {
+ mod, ok := def.(*parser.Module)
+ if !ok {
+ continue
+ }
+
+ err := mergeMatchingProperties(&mod.Properties, buf, &patchlist)
+ if err != nil {
+ return err
+ }
+ }
+
+ newBuf := new(bytes.Buffer)
+ err = patchlist.Apply(bytes.NewReader(buf), newBuf)
+ if err != nil {
+ return err
+ }
+
+ newTree, err := parse(f.tree.Name, newBuf)
+ if err != nil {
+ return err
+ }
+
+ f.tree = newTree
+
+ return nil
+}
+
+func mergeMatchingProperties(properties *[]*parser.Property, buf []byte, patchlist *parser.PatchList) error {
+ seen := make(map[string]*parser.Property)
+ for i := 0; i < len(*properties); i++ {
+ property := (*properties)[i]
+ if prev, exists := seen[property.Name]; exists {
+ err := mergeProperties(prev, property, buf, patchlist)
+ if err != nil {
+ return err
+ }
+ *properties = append((*properties)[:i], (*properties)[i+1:]...)
+ } else {
+ seen[property.Name] = property
+ if mapProperty, ok := property.Value.(*parser.Map); ok {
+ err := mergeMatchingProperties(&mapProperty.Properties, buf, patchlist)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ }
+ return nil
+}
+
+func mergeProperties(a, b *parser.Property, buf []byte, patchlist *parser.PatchList) error {
+ if a.Value.Type() != b.Value.Type() {
+ return fmt.Errorf("type mismatch when merging properties %q: %s and %s", a.Name, a.Value.Type(), b.Value.Type())
+ }
+
+ switch a.Value.Type() {
+ case parser.StringType:
+ return fmt.Errorf("conflicting definitions of string property %q", a.Name)
+ case parser.ListType:
+ return mergeListProperties(a, b, buf, patchlist)
+ }
+
+ return nil
+}
+
+func mergeListProperties(a, b *parser.Property, buf []byte, patchlist *parser.PatchList) error {
+ aval, oka := a.Value.(*parser.List)
+ bval, okb := b.Value.(*parser.List)
+ if !oka || !okb {
+ // Merging expressions not supported yet
+ return nil
+ }
+
+ s := string(buf[bval.LBracePos.Offset+1 : bval.RBracePos.Offset])
+ if bval.LBracePos.Line != bval.RBracePos.Line {
+ if s[0] != '\n' {
+ panic("expected \n")
+ }
+ // If B is a multi line list, skip the first "\n" in case A already has a trailing "\n"
+ s = s[1:]
+ }
+ if aval.LBracePos.Line == aval.RBracePos.Line {
+ // A is a single line list with no trailing comma
+ if len(aval.Values) > 0 {
+ s = "," + s
+ }
+ }
+
+ err := patchlist.Add(aval.RBracePos.Offset, aval.RBracePos.Offset, s)
+ if err != nil {
+ return err
+ }
+ err = patchlist.Add(b.NamePos.Offset, b.End().Offset+2, "")
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
// removes from <items> every item present in <removals>
func filterExpressionList(items *parser.List, removals *parser.List) {
writeIndex := 0