aboutsummaryrefslogtreecommitdiffstats
path: root/bpfix
diff options
context:
space:
mode:
authorColin Cross2018-03-22 14:43:16 -0500
committerColin Cross2018-04-16 12:32:16 -0500
commit9c55d237f6b72896209344aee18a1702c2f9ac3e (patch)
tree9cc07e0ff8e5305611ad267d56bee111834ab1cf /bpfix
parentadee968a4bace28bd26253bc436043eb513f6117 (diff)
downloadplatform-build-soong-9c55d237f6b72896209344aee18a1702c2f9ac3e.tar.gz
platform-build-soong-9c55d237f6b72896209344aee18a1702c2f9ac3e.tar.xz
platform-build-soong-9c55d237f6b72896209344aee18a1702c2f9ac3e.zip
Merge matching properties in bpfix
androidmk will start to generate multiple static_libs: properties in a single module, add a pass in bpfix to fix them up into a single property. Test: bpfix_test.go Change-Id: I30955b6efbb767c02ba77f2f18d44951ef094bad
Diffstat (limited to 'bpfix')
-rw-r--r--bpfix/bpfix/bpfix.go133
-rw-r--r--bpfix/bpfix/bpfix_test.go128
2 files changed, 261 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 (
26 "github.com/google/blueprint/parser" 26 "github.com/google/blueprint/parser"
27) 27)
28 28
29// Reformat takes a blueprint file as a string and returns a formatted version
30func Reformat(input string) (string, error) {
31 tree, err := parse("<string>", bytes.NewBufferString(input))
32 if err != nil {
33 return "", err
34 }
35
36 res, err := parser.Print(tree)
37 if err != nil {
38 return "", err
39 }
40
41 return string(res), nil
42}
43
29// A FixRequest specifies the details of which fixes to apply to an individual file 44// A FixRequest specifies the details of which fixes to apply to an individual file
30// A FixRequest doesn't specify whether to do a dry run or where to write the results; that's in cmd/bpfix.go 45// A FixRequest doesn't specify whether to do a dry run or where to write the results; that's in cmd/bpfix.go
31type FixRequest struct { 46type FixRequest struct {
32 simplifyKnownRedundantVariables bool 47 simplifyKnownRedundantVariables bool
33 rewriteIncorrectAndroidmkPrebuilts bool 48 rewriteIncorrectAndroidmkPrebuilts bool
49 mergeMatchingModuleProperties bool
34} 50}
35 51
36func NewFixRequest() FixRequest { 52func NewFixRequest() FixRequest {
@@ -41,6 +57,7 @@ func (r FixRequest) AddAll() (result FixRequest) {
41 result = r 57 result = r
42 result.simplifyKnownRedundantVariables = true 58 result.simplifyKnownRedundantVariables = true
43 result.rewriteIncorrectAndroidmkPrebuilts = true 59 result.rewriteIncorrectAndroidmkPrebuilts = true
60 result.mergeMatchingModuleProperties = true
44 return result 61 return result
45} 62}
46 63
@@ -136,6 +153,13 @@ func (f *Fixer) fixTreeOnce(config FixRequest) error {
136 return err 153 return err
137 } 154 }
138 } 155 }
156
157 if config.mergeMatchingModuleProperties {
158 err := f.mergeMatchingModuleProperties()
159 if err != nil {
160 return err
161 }
162 }
139 return nil 163 return nil
140} 164}
141 165
@@ -179,6 +203,115 @@ func (f *Fixer) rewriteIncorrectAndroidmkPrebuilts() error {
179 return nil 203 return nil
180} 204}
181 205
206func (f *Fixer) mergeMatchingModuleProperties() error {
207 // Make sure all the offsets are accurate
208 buf, err := f.reparse()
209 if err != nil {
210 return err
211 }
212
213 var patchlist parser.PatchList
214 for _, def := range f.tree.Defs {
215 mod, ok := def.(*parser.Module)
216 if !ok {
217 continue
218 }
219
220 err := mergeMatchingProperties(&mod.Properties, buf, &patchlist)
221 if err != nil {
222 return err
223 }
224 }
225
226 newBuf := new(bytes.Buffer)
227 err = patchlist.Apply(bytes.NewReader(buf), newBuf)
228 if err != nil {
229 return err
230 }
231
232 newTree, err := parse(f.tree.Name, newBuf)
233 if err != nil {
234 return err
235 }
236
237 f.tree = newTree
238
239 return nil
240}
241
242func mergeMatchingProperties(properties *[]*parser.Property, buf []byte, patchlist *parser.PatchList) error {
243 seen := make(map[string]*parser.Property)
244 for i := 0; i < len(*properties); i++ {
245 property := (*properties)[i]
246 if prev, exists := seen[property.Name]; exists {
247 err := mergeProperties(prev, property, buf, patchlist)
248 if err != nil {
249 return err
250 }
251 *properties = append((*properties)[:i], (*properties)[i+1:]...)
252 } else {
253 seen[property.Name] = property
254 if mapProperty, ok := property.Value.(*parser.Map); ok {
255 err := mergeMatchingProperties(&mapProperty.Properties, buf, patchlist)
256 if err != nil {
257 return err
258 }
259 }
260 }
261 }
262 return nil
263}
264
265func mergeProperties(a, b *parser.Property, buf []byte, patchlist *parser.PatchList) error {
266 if a.Value.Type() != b.Value.Type() {
267 return fmt.Errorf("type mismatch when merging properties %q: %s and %s", a.Name, a.Value.Type(), b.Value.Type())
268 }
269
270 switch a.Value.Type() {
271 case parser.StringType:
272 return fmt.Errorf("conflicting definitions of string property %q", a.Name)
273 case parser.ListType:
274 return mergeListProperties(a, b, buf, patchlist)
275 }
276
277 return nil
278}
279
280func mergeListProperties(a, b *parser.Property, buf []byte, patchlist *parser.PatchList) error {
281 aval, oka := a.Value.(*parser.List)
282 bval, okb := b.Value.(*parser.List)
283 if !oka || !okb {
284 // Merging expressions not supported yet
285 return nil
286 }
287
288 s := string(buf[bval.LBracePos.Offset+1 : bval.RBracePos.Offset])
289 if bval.LBracePos.Line != bval.RBracePos.Line {
290 if s[0] != '\n' {
291 panic("expected \n")
292 }
293 // If B is a multi line list, skip the first "\n" in case A already has a trailing "\n"
294 s = s[1:]
295 }
296 if aval.LBracePos.Line == aval.RBracePos.Line {
297 // A is a single line list with no trailing comma
298 if len(aval.Values) > 0 {
299 s = "," + s
300 }
301 }
302
303 err := patchlist.Add(aval.RBracePos.Offset, aval.RBracePos.Offset, s)
304 if err != nil {
305 return err
306 }
307 err = patchlist.Add(b.NamePos.Offset, b.End().Offset+2, "")
308 if err != nil {
309 return err
310 }
311
312 return nil
313}
314
182// removes from <items> every item present in <removals> 315// removes from <items> every item present in <removals>
183func filterExpressionList(items *parser.List, removals *parser.List) { 316func filterExpressionList(items *parser.List, removals *parser.List) {
184 writeIndex := 0 317 writeIndex := 0
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index 1e1e6933..51708eb6 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -17,6 +17,7 @@
17package bpfix 17package bpfix
18 18
19import ( 19import (
20 "bytes"
20 "fmt" 21 "fmt"
21 "strings" 22 "strings"
22 "testing" 23 "testing"
@@ -115,3 +116,130 @@ func TestSimplifyKnownVariablesDuplicatingEachOther(t *testing.T) {
115 implFilterListTest(t, []string{}, []string{"include"}, []string{}) 116 implFilterListTest(t, []string{}, []string{"include"}, []string{})
116 implFilterListTest(t, []string{}, []string{}, []string{}) 117 implFilterListTest(t, []string{}, []string{}, []string{})
117} 118}
119
120func TestMergeMatchingProperties(t *testing.T) {
121 tests := []struct {
122 name string
123 in string
124 out string
125 }{
126 {
127 name: "empty",
128 in: `
129 java_library {
130 name: "foo",
131 static_libs: [],
132 static_libs: [],
133 }
134 `,
135 out: `
136 java_library {
137 name: "foo",
138 static_libs: [],
139 }
140 `,
141 },
142 {
143 name: "single line into multiline",
144 in: `
145 java_library {
146 name: "foo",
147 static_libs: [
148 "a",
149 "b",
150 ],
151 //c1
152 static_libs: ["c" /*c2*/],
153 }
154 `,
155 out: `
156 java_library {
157 name: "foo",
158 static_libs: [
159 "a",
160 "b",
161 "c", /*c2*/
162 ],
163 //c1
164 }
165 `,
166 },
167 {
168 name: "multiline into multiline",
169 in: `
170 java_library {
171 name: "foo",
172 static_libs: [
173 "a",
174 "b",
175 ],
176 //c1
177 static_libs: [
178 //c2
179 "c", //c3
180 "d",
181 ],
182 }
183 `,
184 out: `
185 java_library {
186 name: "foo",
187 static_libs: [
188 "a",
189 "b",
190 //c2
191 "c", //c3
192 "d",
193 ],
194 //c1
195 }
196 `,
197 },
198 }
199
200 for _, test := range tests {
201 t.Run(test.name, func(t *testing.T) {
202 expected, err := Reformat(test.out)
203 if err != nil {
204 t.Error(err)
205 }
206
207 in, err := Reformat(test.in)
208 if err != nil {
209 t.Error(err)
210 }
211
212 tree, errs := parser.Parse("<testcase>", bytes.NewBufferString(in), parser.NewScope(nil))
213 if errs != nil {
214 t.Fatal(errs)
215 }
216
217 fixer := NewFixer(tree)
218
219 got := ""
220 prev := "foo"
221 passes := 0
222 for got != prev && passes < 10 {
223 err := fixer.mergeMatchingModuleProperties()
224 if err != nil {
225 t.Fatal(err)
226 }
227
228 out, err := parser.Print(fixer.tree)
229 if err != nil {
230 t.Fatal(err)
231 }
232
233 prev = got
234 got = string(out)
235 passes++
236 }
237
238 if got != expected {
239 t.Errorf("failed testcase '%s'\ninput:\n%s\n\nexpected:\n%s\ngot:\n%s\n",
240 test.name, in, expected, got)
241 }
242
243 })
244 }
245}