blob: 422534026939a3b5c5aa6c9fd9db85627a34cb9d [file] [log] [blame]
Jooyung Han12df5fb2019-07-11 16:18:47 +09001// Copyright 2019 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package android
16
17import (
Jingwen Chen40fd90a2020-06-15 05:24:19 +000018 "fmt"
Jooyung Han12df5fb2019-07-11 16:18:47 +090019 "io"
20 "reflect"
Paul Duffin8b0349c2020-11-26 14:33:21 +000021 "strings"
Jooyung Han12df5fb2019-07-11 16:18:47 +090022 "testing"
23)
24
25type customModule struct {
26 ModuleBase
Jingwen Chen40fd90a2020-06-15 05:24:19 +000027 data AndroidMkData
28 distFiles TaggedDistFiles
Jooyung Han12df5fb2019-07-11 16:18:47 +090029}
30
31func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) {
Jingwen Chen40fd90a2020-06-15 05:24:19 +000032 m.distFiles = m.GenerateTaggedDistFiles(ctx)
Jooyung Han12df5fb2019-07-11 16:18:47 +090033}
34
35func (m *customModule) AndroidMk() AndroidMkData {
36 return AndroidMkData{
37 Custom: func(w io.Writer, name, prefix, moduleDir string, data AndroidMkData) {
38 m.data = data
39 },
40 }
41}
42
Jingwen Chen40fd90a2020-06-15 05:24:19 +000043func (m *customModule) OutputFiles(tag string) (Paths, error) {
44 switch tag {
45 case "":
46 return PathsForTesting("one.out"), nil
47 case ".multiple":
48 return PathsForTesting("two.out", "three/four.out"), nil
Jingwen Chen84811862020-07-21 11:32:19 +000049 case ".another-tag":
50 return PathsForTesting("another.out"), nil
Jingwen Chen40fd90a2020-06-15 05:24:19 +000051 default:
52 return nil, fmt.Errorf("unsupported module reference tag %q", tag)
53 }
54}
55
56func (m *customModule) AndroidMkEntries() []AndroidMkEntries {
57 return []AndroidMkEntries{
58 {
59 Class: "CUSTOM_MODULE",
60 DistFiles: m.distFiles,
61 },
62 }
63}
64
Jooyung Han12df5fb2019-07-11 16:18:47 +090065func customModuleFactory() Module {
66 module := &customModule{}
67 InitAndroidModule(module)
68 return module
69}
70
Paul Duffin103aaae2020-11-26 17:36:46 +000071// buildConfigAndCustomModuleFoo creates a config object, processes the supplied
72// bp module and then returns the config and the custom module called "foo".
73func buildConfigAndCustomModuleFoo(t *testing.T, bp string) (Config, *customModule) {
74 t.Helper()
Colin Cross98be1bb2019-12-13 20:41:13 -080075 config := TestConfig(buildDir, nil, bp, nil)
Jingwen Chencda22c92020-11-23 00:22:30 -050076 config.katiEnabled = true // Enable androidmk Singleton
Colin Cross98be1bb2019-12-13 20:41:13 -080077
Colin Crossae8600b2020-10-29 17:09:13 -070078 ctx := NewTestContext(config)
Colin Cross98be1bb2019-12-13 20:41:13 -080079 ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
80 ctx.RegisterModuleType("custom", customModuleFactory)
Colin Crossae8600b2020-10-29 17:09:13 -070081 ctx.Register()
Jooyung Han12df5fb2019-07-11 16:18:47 +090082
83 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
84 FailIfErrored(t, errs)
85 _, errs = ctx.PrepareBuildActions(config)
86 FailIfErrored(t, errs)
87
Paul Duffin103aaae2020-11-26 17:36:46 +000088 module := ctx.ModuleForTests("foo", "").Module().(*customModule)
89 return config, module
90}
91
92func TestAndroidMkSingleton_PassesUpdatedAndroidMkDataToCustomCallback(t *testing.T) {
93 bp := `
94 custom {
95 name: "foo",
96 required: ["bar"],
97 host_required: ["baz"],
98 target_required: ["qux"],
99 }
100 `
101
102 _, m := buildConfigAndCustomModuleFoo(t, bp)
Jooyung Han12df5fb2019-07-11 16:18:47 +0900103
104 assertEqual := func(expected interface{}, actual interface{}) {
105 if !reflect.DeepEqual(expected, actual) {
106 t.Errorf("%q expected, but got %q", expected, actual)
107 }
108 }
109 assertEqual([]string{"bar"}, m.data.Required)
110 assertEqual([]string{"baz"}, m.data.Host_required)
111 assertEqual([]string{"qux"}, m.data.Target_required)
112}
Jingwen Chen40fd90a2020-06-15 05:24:19 +0000113
Paul Duffin8b0349c2020-11-26 14:33:21 +0000114func TestGenerateDistContributionsForMake(t *testing.T) {
115 dc := &distContributions{
116 copiesForGoals: []*copiesForGoals{
117 {
118 goals: "my_goal",
119 copies: []distCopy{
120 distCopyForTest("one.out", "one.out"),
121 distCopyForTest("two.out", "other.out"),
122 },
123 },
124 },
125 }
126
127 makeOutput := generateDistContributionsForMake(dc)
128
129 assertStringEquals(t, `.PHONY: my_goal
130$(call dist-for-goals,my_goal,one.out:one.out)
131$(call dist-for-goals,my_goal,two.out:other.out)
132`, strings.Join(makeOutput, ""))
133}
134
Jingwen Chen40fd90a2020-06-15 05:24:19 +0000135func TestGetDistForGoals(t *testing.T) {
Paul Duffind83988d2020-11-26 17:29:35 +0000136 bp := `
Jingwen Chen40fd90a2020-06-15 05:24:19 +0000137 custom {
138 name: "foo",
139 dist: {
140 targets: ["my_goal", "my_other_goal"],
141 tag: ".multiple",
142 },
143 dists: [
144 {
145 targets: ["my_second_goal"],
146 tag: ".multiple",
147 },
148 {
149 targets: ["my_third_goal"],
150 dir: "test/dir",
151 },
152 {
153 targets: ["my_fourth_goal"],
154 suffix: ".suffix",
155 },
156 {
157 targets: ["my_fifth_goal"],
158 dest: "new-name",
159 },
160 {
161 targets: ["my_sixth_goal"],
162 dest: "new-name",
163 dir: "some/dir",
164 suffix: ".suffix",
165 },
166 ],
167 }
Paul Duffind83988d2020-11-26 17:29:35 +0000168 `
169
170 expectedAndroidMkLines := []string{
171 ".PHONY: my_second_goal\n",
172 "$(call dist-for-goals,my_second_goal,two.out:two.out)\n",
173 "$(call dist-for-goals,my_second_goal,three/four.out:four.out)\n",
174 ".PHONY: my_third_goal\n",
175 "$(call dist-for-goals,my_third_goal,one.out:test/dir/one.out)\n",
176 ".PHONY: my_fourth_goal\n",
177 "$(call dist-for-goals,my_fourth_goal,one.out:one.suffix.out)\n",
178 ".PHONY: my_fifth_goal\n",
179 "$(call dist-for-goals,my_fifth_goal,one.out:new-name)\n",
180 ".PHONY: my_sixth_goal\n",
181 "$(call dist-for-goals,my_sixth_goal,one.out:some/dir/new-name.suffix)\n",
182 ".PHONY: my_goal my_other_goal\n",
183 "$(call dist-for-goals,my_goal my_other_goal,two.out:two.out)\n",
184 "$(call dist-for-goals,my_goal my_other_goal,three/four.out:four.out)\n",
Jingwen Chen40fd90a2020-06-15 05:24:19 +0000185 }
186
Paul Duffind83988d2020-11-26 17:29:35 +0000187 config, module := buildConfigAndCustomModuleFoo(t, bp)
188 entries := AndroidMkEntriesForTest(t, config, "", module)
189 if len(entries) != 1 {
190 t.Errorf("Expected a single AndroidMk entry, got %d", len(entries))
191 }
192 androidMkLines := entries[0].GetDistForGoals(module)
Jingwen Chen40fd90a2020-06-15 05:24:19 +0000193
Paul Duffind83988d2020-11-26 17:29:35 +0000194 if len(androidMkLines) != len(expectedAndroidMkLines) {
195 t.Errorf(
196 "Expected %d AndroidMk lines, got %d:\n%v",
197 len(expectedAndroidMkLines),
198 len(androidMkLines),
199 androidMkLines,
200 )
201 }
202 for idx, line := range androidMkLines {
203 expectedLine := expectedAndroidMkLines[idx]
204 if line != expectedLine {
205 t.Errorf(
206 "Expected AndroidMk line to be '%s', got '%s'",
207 expectedLine,
208 line,
209 )
210 }
Jingwen Chen40fd90a2020-06-15 05:24:19 +0000211 }
212}
Paul Duffin8b0349c2020-11-26 14:33:21 +0000213
214func distCopyForTest(from, to string) distCopy {
215 return distCopy{PathForTesting(from), to}
216}
217
218func TestGetDistContributions(t *testing.T) {
219 compareContributions := func(d1 *distContributions, d2 *distContributions) error {
220 if d1 == nil || d2 == nil {
221 if d1 != d2 {
222 return fmt.Errorf("pointer mismatch, expected both to be nil but they were %p and %p", d1, d2)
223 } else {
224 return nil
225 }
226 }
227 if expected, actual := len(d1.copiesForGoals), len(d2.copiesForGoals); expected != actual {
228 return fmt.Errorf("length mismatch, expected %d found %d", expected, actual)
229 }
230
231 for i, copies1 := range d1.copiesForGoals {
232 copies2 := d2.copiesForGoals[i]
233 if expected, actual := copies1.goals, copies2.goals; expected != actual {
234 return fmt.Errorf("goals mismatch at position %d: expected %q found %q", i, expected, actual)
235 }
236
237 if expected, actual := len(copies1.copies), len(copies2.copies); expected != actual {
238 return fmt.Errorf("length mismatch in copy instructions at position %d, expected %d found %d", i, expected, actual)
239 }
240
241 for j, c1 := range copies1.copies {
242 c2 := copies2.copies[j]
243 if expected, actual := NormalizePathForTesting(c1.from), NormalizePathForTesting(c2.from); expected != actual {
244 return fmt.Errorf("paths mismatch at position %d.%d: expected %q found %q", i, j, expected, actual)
245 }
246
247 if expected, actual := c1.dest, c2.dest; expected != actual {
248 return fmt.Errorf("dest mismatch at position %d.%d: expected %q found %q", i, j, expected, actual)
249 }
250 }
251 }
252
253 return nil
254 }
255
256 formatContributions := func(d *distContributions) string {
257 buf := &strings.Builder{}
258 if d == nil {
259 fmt.Fprint(buf, "nil")
260 } else {
261 for _, copiesForGoals := range d.copiesForGoals {
262 fmt.Fprintf(buf, " Goals: %q {\n", copiesForGoals.goals)
263 for _, c := range copiesForGoals.copies {
264 fmt.Fprintf(buf, " %s -> %s\n", NormalizePathForTesting(c.from), c.dest)
265 }
266 fmt.Fprint(buf, " }\n")
267 }
268 }
269 return buf.String()
270 }
271
272 testHelper := func(t *testing.T, name, bp string, expectedContributions *distContributions) {
273 t.Helper()
274 t.Run(name, func(t *testing.T) {
275 t.Helper()
276
277 config, module := buildConfigAndCustomModuleFoo(t, bp)
278 entries := AndroidMkEntriesForTest(t, config, "", module)
279 if len(entries) != 1 {
280 t.Errorf("Expected a single AndroidMk entry, got %d", len(entries))
281 }
282 distContributions := entries[0].getDistContributions(module)
283
284 if err := compareContributions(expectedContributions, distContributions); err != nil {
285 t.Errorf("%s\nExpected Contributions\n%sActualContributions\n%s",
286 err,
287 formatContributions(expectedContributions),
288 formatContributions(distContributions))
289 }
290 })
291 }
292
293 testHelper(t, "dist-without-tag", `
294 custom {
295 name: "foo",
296 dist: {
297 targets: ["my_goal"]
298 }
299 }
300`,
301 &distContributions{
302 copiesForGoals: []*copiesForGoals{
303 {
304 goals: "my_goal",
305 copies: []distCopy{
306 distCopyForTest("one.out", "one.out"),
307 },
308 },
309 },
310 })
311
312 testHelper(t, "dist-with-tag", `
313 custom {
314 name: "foo",
315 dist: {
316 targets: ["my_goal"],
317 tag: ".another-tag",
318 }
319 }
320`,
321 &distContributions{
322 copiesForGoals: []*copiesForGoals{
323 {
324 goals: "my_goal",
325 copies: []distCopy{
326 distCopyForTest("another.out", "another.out"),
327 },
328 },
329 },
330 })
331
332 testHelper(t, "dists-with-tag", `
333 custom {
334 name: "foo",
335 dists: [
336 {
337 targets: ["my_goal"],
338 tag: ".another-tag",
339 },
340 ],
341 }
342`,
343 &distContributions{
344 copiesForGoals: []*copiesForGoals{
345 {
346 goals: "my_goal",
347 copies: []distCopy{
348 distCopyForTest("another.out", "another.out"),
349 },
350 },
351 },
352 })
353
354 testHelper(t, "multiple-dists-with-and-without-tag", `
355 custom {
356 name: "foo",
357 dists: [
358 {
359 targets: ["my_goal"],
360 },
361 {
362 targets: ["my_second_goal", "my_third_goal"],
363 },
364 ],
365 }
366`,
367 &distContributions{
368 copiesForGoals: []*copiesForGoals{
369 {
370 goals: "my_goal",
371 copies: []distCopy{
372 distCopyForTest("one.out", "one.out"),
373 },
374 },
375 {
376 goals: "my_second_goal my_third_goal",
377 copies: []distCopy{
378 distCopyForTest("one.out", "one.out"),
379 },
380 },
381 },
382 })
383
384 testHelper(t, "dist-plus-dists-without-tags", `
385 custom {
386 name: "foo",
387 dist: {
388 targets: ["my_goal"],
389 },
390 dists: [
391 {
392 targets: ["my_second_goal", "my_third_goal"],
393 },
394 ],
395 }
396`,
397 &distContributions{
398 copiesForGoals: []*copiesForGoals{
399 {
400 goals: "my_second_goal my_third_goal",
401 copies: []distCopy{
402 distCopyForTest("one.out", "one.out"),
403 },
404 },
405 {
406 goals: "my_goal",
407 copies: []distCopy{
408 distCopyForTest("one.out", "one.out"),
409 },
410 },
411 },
412 })
413
414 testHelper(t, "dist-plus-dists-with-tags", `
415 custom {
416 name: "foo",
417 dist: {
418 targets: ["my_goal", "my_other_goal"],
419 tag: ".multiple",
420 },
421 dists: [
422 {
423 targets: ["my_second_goal"],
424 tag: ".multiple",
425 },
426 {
427 targets: ["my_third_goal"],
428 dir: "test/dir",
429 },
430 {
431 targets: ["my_fourth_goal"],
432 suffix: ".suffix",
433 },
434 {
435 targets: ["my_fifth_goal"],
436 dest: "new-name",
437 },
438 {
439 targets: ["my_sixth_goal"],
440 dest: "new-name",
441 dir: "some/dir",
442 suffix: ".suffix",
443 },
444 ],
445 }
446`,
447 &distContributions{
448 copiesForGoals: []*copiesForGoals{
449 {
450 goals: "my_second_goal",
451 copies: []distCopy{
452 distCopyForTest("two.out", "two.out"),
453 distCopyForTest("three/four.out", "four.out"),
454 },
455 },
456 {
457 goals: "my_third_goal",
458 copies: []distCopy{
459 distCopyForTest("one.out", "test/dir/one.out"),
460 },
461 },
462 {
463 goals: "my_fourth_goal",
464 copies: []distCopy{
465 distCopyForTest("one.out", "one.suffix.out"),
466 },
467 },
468 {
469 goals: "my_fifth_goal",
470 copies: []distCopy{
471 distCopyForTest("one.out", "new-name"),
472 },
473 },
474 {
475 goals: "my_sixth_goal",
476 copies: []distCopy{
477 distCopyForTest("one.out", "some/dir/new-name.suffix"),
478 },
479 },
480 {
481 goals: "my_goal my_other_goal",
482 copies: []distCopy{
483 distCopyForTest("two.out", "two.out"),
484 distCopyForTest("three/four.out", "four.out"),
485 },
486 },
487 },
488 })
489}