blob: 5d05d917cd15809bdab868968a57df106da2aa9e [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"
Paul Duffin62269492020-11-26 20:18:42 +000023
24 "github.com/google/blueprint/proptools"
Jooyung Han12df5fb2019-07-11 16:18:47 +090025)
26
27type customModule struct {
28 ModuleBase
Paul Duffin62269492020-11-26 20:18:42 +000029
30 properties struct {
31 Default_dist_files *string
32 Dist_output_file *bool
33 }
34
35 data AndroidMkData
36 distFiles TaggedDistFiles
37 outputFile OptionalPath
Jooyung Han12df5fb2019-07-11 16:18:47 +090038}
39
Paul Duffin62269492020-11-26 20:18:42 +000040const (
41 defaultDistFiles_None = "none"
42 defaultDistFiles_Default = "default"
43 defaultDistFiles_Tagged = "tagged"
44)
45
Jooyung Han12df5fb2019-07-11 16:18:47 +090046func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) {
Paul Duffin62269492020-11-26 20:18:42 +000047
48 // If the dist_output_file: true then create an output file that is stored in
49 // the OutputFile property of the AndroidMkEntry.
50 if proptools.BoolDefault(m.properties.Dist_output_file, true) {
51 m.outputFile = OptionalPathForPath(PathForTesting("dist-output-file.out"))
52 }
53
54 // Based on the setting of the default_dist_files property possibly create a
55 // TaggedDistFiles structure that will be stored in the DistFiles property of
56 // the AndroidMkEntry.
57 defaultDistFiles := proptools.StringDefault(m.properties.Default_dist_files, defaultDistFiles_Tagged)
58 switch defaultDistFiles {
59 case defaultDistFiles_None:
60 // Do nothing
61
62 case defaultDistFiles_Default:
63 m.distFiles = MakeDefaultDistFiles(PathForTesting("default-dist.out"))
64
65 case defaultDistFiles_Tagged:
66 m.distFiles = m.GenerateTaggedDistFiles(ctx)
67 }
Jooyung Han12df5fb2019-07-11 16:18:47 +090068}
69
70func (m *customModule) AndroidMk() AndroidMkData {
71 return AndroidMkData{
72 Custom: func(w io.Writer, name, prefix, moduleDir string, data AndroidMkData) {
73 m.data = data
74 },
75 }
76}
77
Jingwen Chen40fd90a2020-06-15 05:24:19 +000078func (m *customModule) OutputFiles(tag string) (Paths, error) {
79 switch tag {
80 case "":
81 return PathsForTesting("one.out"), nil
82 case ".multiple":
83 return PathsForTesting("two.out", "three/four.out"), nil
Jingwen Chen84811862020-07-21 11:32:19 +000084 case ".another-tag":
85 return PathsForTesting("another.out"), nil
Jingwen Chen40fd90a2020-06-15 05:24:19 +000086 default:
87 return nil, fmt.Errorf("unsupported module reference tag %q", tag)
88 }
89}
90
91func (m *customModule) AndroidMkEntries() []AndroidMkEntries {
92 return []AndroidMkEntries{
93 {
Paul Duffin62269492020-11-26 20:18:42 +000094 Class: "CUSTOM_MODULE",
95 DistFiles: m.distFiles,
96 OutputFile: m.outputFile,
Jingwen Chen40fd90a2020-06-15 05:24:19 +000097 },
98 }
99}
100
Jooyung Han12df5fb2019-07-11 16:18:47 +0900101func customModuleFactory() Module {
102 module := &customModule{}
Paul Duffin62269492020-11-26 20:18:42 +0000103
104 module.AddProperties(&module.properties)
105
Jooyung Han12df5fb2019-07-11 16:18:47 +0900106 InitAndroidModule(module)
107 return module
108}
109
Paul Duffin103aaae2020-11-26 17:36:46 +0000110// buildConfigAndCustomModuleFoo creates a config object, processes the supplied
111// bp module and then returns the config and the custom module called "foo".
112func buildConfigAndCustomModuleFoo(t *testing.T, bp string) (Config, *customModule) {
113 t.Helper()
Colin Cross98be1bb2019-12-13 20:41:13 -0800114 config := TestConfig(buildDir, nil, bp, nil)
Jingwen Chencda22c92020-11-23 00:22:30 -0500115 config.katiEnabled = true // Enable androidmk Singleton
Colin Cross98be1bb2019-12-13 20:41:13 -0800116
Colin Crossae8600b2020-10-29 17:09:13 -0700117 ctx := NewTestContext(config)
Colin Cross98be1bb2019-12-13 20:41:13 -0800118 ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
119 ctx.RegisterModuleType("custom", customModuleFactory)
Colin Crossae8600b2020-10-29 17:09:13 -0700120 ctx.Register()
Jooyung Han12df5fb2019-07-11 16:18:47 +0900121
122 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
123 FailIfErrored(t, errs)
124 _, errs = ctx.PrepareBuildActions(config)
125 FailIfErrored(t, errs)
126
Paul Duffin103aaae2020-11-26 17:36:46 +0000127 module := ctx.ModuleForTests("foo", "").Module().(*customModule)
128 return config, module
129}
130
131func TestAndroidMkSingleton_PassesUpdatedAndroidMkDataToCustomCallback(t *testing.T) {
132 bp := `
133 custom {
134 name: "foo",
135 required: ["bar"],
136 host_required: ["baz"],
137 target_required: ["qux"],
138 }
139 `
140
141 _, m := buildConfigAndCustomModuleFoo(t, bp)
Jooyung Han12df5fb2019-07-11 16:18:47 +0900142
143 assertEqual := func(expected interface{}, actual interface{}) {
144 if !reflect.DeepEqual(expected, actual) {
145 t.Errorf("%q expected, but got %q", expected, actual)
146 }
147 }
148 assertEqual([]string{"bar"}, m.data.Required)
149 assertEqual([]string{"baz"}, m.data.Host_required)
150 assertEqual([]string{"qux"}, m.data.Target_required)
151}
Jingwen Chen40fd90a2020-06-15 05:24:19 +0000152
Paul Duffin8b0349c2020-11-26 14:33:21 +0000153func TestGenerateDistContributionsForMake(t *testing.T) {
154 dc := &distContributions{
155 copiesForGoals: []*copiesForGoals{
156 {
157 goals: "my_goal",
158 copies: []distCopy{
159 distCopyForTest("one.out", "one.out"),
160 distCopyForTest("two.out", "other.out"),
161 },
162 },
163 },
164 }
165
166 makeOutput := generateDistContributionsForMake(dc)
167
168 assertStringEquals(t, `.PHONY: my_goal
169$(call dist-for-goals,my_goal,one.out:one.out)
170$(call dist-for-goals,my_goal,two.out:other.out)
171`, strings.Join(makeOutput, ""))
172}
173
Jingwen Chen40fd90a2020-06-15 05:24:19 +0000174func TestGetDistForGoals(t *testing.T) {
Paul Duffind83988d2020-11-26 17:29:35 +0000175 bp := `
Jingwen Chen40fd90a2020-06-15 05:24:19 +0000176 custom {
177 name: "foo",
178 dist: {
179 targets: ["my_goal", "my_other_goal"],
180 tag: ".multiple",
181 },
182 dists: [
183 {
184 targets: ["my_second_goal"],
185 tag: ".multiple",
186 },
187 {
188 targets: ["my_third_goal"],
189 dir: "test/dir",
190 },
191 {
192 targets: ["my_fourth_goal"],
193 suffix: ".suffix",
194 },
195 {
196 targets: ["my_fifth_goal"],
197 dest: "new-name",
198 },
199 {
200 targets: ["my_sixth_goal"],
201 dest: "new-name",
202 dir: "some/dir",
203 suffix: ".suffix",
204 },
205 ],
206 }
Paul Duffind83988d2020-11-26 17:29:35 +0000207 `
208
209 expectedAndroidMkLines := []string{
210 ".PHONY: my_second_goal\n",
211 "$(call dist-for-goals,my_second_goal,two.out:two.out)\n",
212 "$(call dist-for-goals,my_second_goal,three/four.out:four.out)\n",
213 ".PHONY: my_third_goal\n",
214 "$(call dist-for-goals,my_third_goal,one.out:test/dir/one.out)\n",
215 ".PHONY: my_fourth_goal\n",
216 "$(call dist-for-goals,my_fourth_goal,one.out:one.suffix.out)\n",
217 ".PHONY: my_fifth_goal\n",
218 "$(call dist-for-goals,my_fifth_goal,one.out:new-name)\n",
219 ".PHONY: my_sixth_goal\n",
220 "$(call dist-for-goals,my_sixth_goal,one.out:some/dir/new-name.suffix)\n",
221 ".PHONY: my_goal my_other_goal\n",
222 "$(call dist-for-goals,my_goal my_other_goal,two.out:two.out)\n",
223 "$(call dist-for-goals,my_goal my_other_goal,three/four.out:four.out)\n",
Jingwen Chen40fd90a2020-06-15 05:24:19 +0000224 }
225
Paul Duffind83988d2020-11-26 17:29:35 +0000226 config, module := buildConfigAndCustomModuleFoo(t, bp)
227 entries := AndroidMkEntriesForTest(t, config, "", module)
228 if len(entries) != 1 {
229 t.Errorf("Expected a single AndroidMk entry, got %d", len(entries))
230 }
231 androidMkLines := entries[0].GetDistForGoals(module)
Jingwen Chen40fd90a2020-06-15 05:24:19 +0000232
Paul Duffind83988d2020-11-26 17:29:35 +0000233 if len(androidMkLines) != len(expectedAndroidMkLines) {
234 t.Errorf(
235 "Expected %d AndroidMk lines, got %d:\n%v",
236 len(expectedAndroidMkLines),
237 len(androidMkLines),
238 androidMkLines,
239 )
240 }
241 for idx, line := range androidMkLines {
242 expectedLine := expectedAndroidMkLines[idx]
243 if line != expectedLine {
244 t.Errorf(
245 "Expected AndroidMk line to be '%s', got '%s'",
246 expectedLine,
247 line,
248 )
249 }
Jingwen Chen40fd90a2020-06-15 05:24:19 +0000250 }
251}
Paul Duffin8b0349c2020-11-26 14:33:21 +0000252
253func distCopyForTest(from, to string) distCopy {
254 return distCopy{PathForTesting(from), to}
255}
256
257func TestGetDistContributions(t *testing.T) {
258 compareContributions := func(d1 *distContributions, d2 *distContributions) error {
259 if d1 == nil || d2 == nil {
260 if d1 != d2 {
261 return fmt.Errorf("pointer mismatch, expected both to be nil but they were %p and %p", d1, d2)
262 } else {
263 return nil
264 }
265 }
266 if expected, actual := len(d1.copiesForGoals), len(d2.copiesForGoals); expected != actual {
267 return fmt.Errorf("length mismatch, expected %d found %d", expected, actual)
268 }
269
270 for i, copies1 := range d1.copiesForGoals {
271 copies2 := d2.copiesForGoals[i]
272 if expected, actual := copies1.goals, copies2.goals; expected != actual {
273 return fmt.Errorf("goals mismatch at position %d: expected %q found %q", i, expected, actual)
274 }
275
276 if expected, actual := len(copies1.copies), len(copies2.copies); expected != actual {
277 return fmt.Errorf("length mismatch in copy instructions at position %d, expected %d found %d", i, expected, actual)
278 }
279
280 for j, c1 := range copies1.copies {
281 c2 := copies2.copies[j]
282 if expected, actual := NormalizePathForTesting(c1.from), NormalizePathForTesting(c2.from); expected != actual {
283 return fmt.Errorf("paths mismatch at position %d.%d: expected %q found %q", i, j, expected, actual)
284 }
285
286 if expected, actual := c1.dest, c2.dest; expected != actual {
287 return fmt.Errorf("dest mismatch at position %d.%d: expected %q found %q", i, j, expected, actual)
288 }
289 }
290 }
291
292 return nil
293 }
294
295 formatContributions := func(d *distContributions) string {
296 buf := &strings.Builder{}
297 if d == nil {
298 fmt.Fprint(buf, "nil")
299 } else {
300 for _, copiesForGoals := range d.copiesForGoals {
301 fmt.Fprintf(buf, " Goals: %q {\n", copiesForGoals.goals)
302 for _, c := range copiesForGoals.copies {
303 fmt.Fprintf(buf, " %s -> %s\n", NormalizePathForTesting(c.from), c.dest)
304 }
305 fmt.Fprint(buf, " }\n")
306 }
307 }
308 return buf.String()
309 }
310
311 testHelper := func(t *testing.T, name, bp string, expectedContributions *distContributions) {
312 t.Helper()
313 t.Run(name, func(t *testing.T) {
314 t.Helper()
315
316 config, module := buildConfigAndCustomModuleFoo(t, bp)
317 entries := AndroidMkEntriesForTest(t, config, "", module)
318 if len(entries) != 1 {
319 t.Errorf("Expected a single AndroidMk entry, got %d", len(entries))
320 }
321 distContributions := entries[0].getDistContributions(module)
322
323 if err := compareContributions(expectedContributions, distContributions); err != nil {
324 t.Errorf("%s\nExpected Contributions\n%sActualContributions\n%s",
325 err,
326 formatContributions(expectedContributions),
327 formatContributions(distContributions))
328 }
329 })
330 }
331
332 testHelper(t, "dist-without-tag", `
333 custom {
334 name: "foo",
335 dist: {
336 targets: ["my_goal"]
337 }
338 }
339`,
340 &distContributions{
341 copiesForGoals: []*copiesForGoals{
342 {
343 goals: "my_goal",
344 copies: []distCopy{
345 distCopyForTest("one.out", "one.out"),
346 },
347 },
348 },
349 })
350
351 testHelper(t, "dist-with-tag", `
352 custom {
353 name: "foo",
354 dist: {
355 targets: ["my_goal"],
356 tag: ".another-tag",
357 }
358 }
359`,
360 &distContributions{
361 copiesForGoals: []*copiesForGoals{
362 {
363 goals: "my_goal",
364 copies: []distCopy{
365 distCopyForTest("another.out", "another.out"),
366 },
367 },
368 },
369 })
370
371 testHelper(t, "dists-with-tag", `
372 custom {
373 name: "foo",
374 dists: [
375 {
376 targets: ["my_goal"],
377 tag: ".another-tag",
378 },
379 ],
380 }
381`,
382 &distContributions{
383 copiesForGoals: []*copiesForGoals{
384 {
385 goals: "my_goal",
386 copies: []distCopy{
387 distCopyForTest("another.out", "another.out"),
388 },
389 },
390 },
391 })
392
393 testHelper(t, "multiple-dists-with-and-without-tag", `
394 custom {
395 name: "foo",
396 dists: [
397 {
398 targets: ["my_goal"],
399 },
400 {
401 targets: ["my_second_goal", "my_third_goal"],
402 },
403 ],
404 }
405`,
406 &distContributions{
407 copiesForGoals: []*copiesForGoals{
408 {
409 goals: "my_goal",
410 copies: []distCopy{
411 distCopyForTest("one.out", "one.out"),
412 },
413 },
414 {
415 goals: "my_second_goal my_third_goal",
416 copies: []distCopy{
417 distCopyForTest("one.out", "one.out"),
418 },
419 },
420 },
421 })
422
423 testHelper(t, "dist-plus-dists-without-tags", `
424 custom {
425 name: "foo",
426 dist: {
427 targets: ["my_goal"],
428 },
429 dists: [
430 {
431 targets: ["my_second_goal", "my_third_goal"],
432 },
433 ],
434 }
435`,
436 &distContributions{
437 copiesForGoals: []*copiesForGoals{
438 {
439 goals: "my_second_goal my_third_goal",
440 copies: []distCopy{
441 distCopyForTest("one.out", "one.out"),
442 },
443 },
444 {
445 goals: "my_goal",
446 copies: []distCopy{
447 distCopyForTest("one.out", "one.out"),
448 },
449 },
450 },
451 })
452
453 testHelper(t, "dist-plus-dists-with-tags", `
454 custom {
455 name: "foo",
456 dist: {
457 targets: ["my_goal", "my_other_goal"],
458 tag: ".multiple",
459 },
460 dists: [
461 {
462 targets: ["my_second_goal"],
463 tag: ".multiple",
464 },
465 {
466 targets: ["my_third_goal"],
467 dir: "test/dir",
468 },
469 {
470 targets: ["my_fourth_goal"],
471 suffix: ".suffix",
472 },
473 {
474 targets: ["my_fifth_goal"],
475 dest: "new-name",
476 },
477 {
478 targets: ["my_sixth_goal"],
479 dest: "new-name",
480 dir: "some/dir",
481 suffix: ".suffix",
482 },
483 ],
484 }
485`,
486 &distContributions{
487 copiesForGoals: []*copiesForGoals{
488 {
489 goals: "my_second_goal",
490 copies: []distCopy{
491 distCopyForTest("two.out", "two.out"),
492 distCopyForTest("three/four.out", "four.out"),
493 },
494 },
495 {
496 goals: "my_third_goal",
497 copies: []distCopy{
498 distCopyForTest("one.out", "test/dir/one.out"),
499 },
500 },
501 {
502 goals: "my_fourth_goal",
503 copies: []distCopy{
504 distCopyForTest("one.out", "one.suffix.out"),
505 },
506 },
507 {
508 goals: "my_fifth_goal",
509 copies: []distCopy{
510 distCopyForTest("one.out", "new-name"),
511 },
512 },
513 {
514 goals: "my_sixth_goal",
515 copies: []distCopy{
516 distCopyForTest("one.out", "some/dir/new-name.suffix"),
517 },
518 },
519 {
520 goals: "my_goal my_other_goal",
521 copies: []distCopy{
522 distCopyForTest("two.out", "two.out"),
523 distCopyForTest("three/four.out", "four.out"),
524 },
525 },
526 },
527 })
Paul Duffin62269492020-11-26 20:18:42 +0000528
529 // The above test the default values of default_dist_files and use_output_file.
530
531 // The following tests explicitly test the different combinations of those settings.
532 testHelper(t, "tagged-dist-files-no-output", `
533 custom {
534 name: "foo",
535 default_dist_files: "tagged",
536 dist_output_file: false,
537 dists: [
538 {
539 targets: ["my_goal"],
540 },
541 {
542 targets: ["my_goal"],
543 tag: ".multiple",
544 },
545 ],
546 }
547`, &distContributions{
548 copiesForGoals: []*copiesForGoals{
549 {
550 goals: "my_goal",
551 copies: []distCopy{
552 distCopyForTest("one.out", "one.out"),
553 },
554 },
555 {
556 goals: "my_goal",
557 copies: []distCopy{
558 distCopyForTest("two.out", "two.out"),
559 distCopyForTest("three/four.out", "four.out"),
560 },
561 },
562 },
563 })
564
565 testHelper(t, "default-dist-files-no-output", `
566 custom {
567 name: "foo",
568 default_dist_files: "default",
569 dist_output_file: false,
570 dists: [
571 {
572 targets: ["my_goal"],
573 },
574 // The following is silently ignored because the dist files do not
575 // contain the tagged files.
576 {
577 targets: ["my_goal"],
578 tag: ".multiple",
579 },
580 ],
581 }
582`, &distContributions{
583 copiesForGoals: []*copiesForGoals{
584 {
585 goals: "my_goal",
586 copies: []distCopy{
587 distCopyForTest("default-dist.out", "default-dist.out"),
588 },
589 },
590 },
591 })
592
593 testHelper(t, "no-dist-files-no-output", `
594 custom {
595 name: "foo",
596 default_dist_files: "none",
597 dist_output_file: false,
598 dists: [
599 // The following is silently ignored because there is not default file
600 // in either the dist files or the output file.
601 {
602 targets: ["my_goal"],
603 },
604 // The following is silently ignored because the dist files do not
605 // contain the tagged files.
606 {
607 targets: ["my_goal"],
608 tag: ".multiple",
609 },
610 ],
611 }
612`, nil)
613
614 testHelper(t, "tagged-dist-files-default-output", `
615 custom {
616 name: "foo",
617 default_dist_files: "tagged",
618 dist_output_file: true,
619 dists: [
620 {
621 targets: ["my_goal"],
622 },
623 {
624 targets: ["my_goal"],
625 tag: ".multiple",
626 },
627 ],
628 }
629`, &distContributions{
630 copiesForGoals: []*copiesForGoals{
631 {
632 goals: "my_goal",
633 copies: []distCopy{
634 distCopyForTest("one.out", "one.out"),
635 },
636 },
637 {
638 goals: "my_goal",
639 copies: []distCopy{
640 distCopyForTest("two.out", "two.out"),
641 distCopyForTest("three/four.out", "four.out"),
642 },
643 },
644 },
645 })
646
647 testHelper(t, "default-dist-files-default-output", `
648 custom {
649 name: "foo",
650 default_dist_files: "default",
651 dist_output_file: true,
652 dists: [
653 {
654 targets: ["my_goal"],
655 },
656 // The following is silently ignored because the dist files do not
657 // contain the tagged files.
658 {
659 targets: ["my_goal"],
660 tag: ".multiple",
661 },
662 ],
663 }
664`, &distContributions{
665 copiesForGoals: []*copiesForGoals{
666 {
667 goals: "my_goal",
668 copies: []distCopy{
669 distCopyForTest("default-dist.out", "default-dist.out"),
670 },
671 },
672 },
673 })
674
675 testHelper(t, "no-dist-files-default-output", `
676 custom {
677 name: "foo",
678 default_dist_files: "none",
679 dist_output_file: true,
680 dists: [
681 {
682 targets: ["my_goal"],
683 },
684 // The following is silently ignored because the dist files do not
685 // contain the tagged files.
686 {
687 targets: ["my_goal"],
688 tag: ".multiple",
689 },
690 ],
691 }
692`, &distContributions{
693 copiesForGoals: []*copiesForGoals{
694 {
695 goals: "my_goal",
696 copies: []distCopy{
697 distCopyForTest("dist-output-file.out", "dist-output-file.out"),
698 },
699 },
700 },
701 })
Paul Duffin8b0349c2020-11-26 14:33:21 +0000702}