blob: d9e2c87a4f5a0f1799b3ba2fadcee57589bb9629 [file] [log] [blame]
Colin Cross41955e82019-05-29 14:40:35 -07001// Copyright 2015 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
Jooyung Hand48f3c32019-08-23 11:18:57 +090017import (
Colin Cross6ac95762021-11-09 13:17:44 -080018 "bytes"
19 "path/filepath"
20 "runtime"
Jooyung Hand48f3c32019-08-23 11:18:57 +090021 "testing"
Colin Cross6ac95762021-11-09 13:17:44 -080022
23 mkparser "android/soong/androidmk/parser"
Jooyung Hand48f3c32019-08-23 11:18:57 +090024)
Colin Cross41955e82019-05-29 14:40:35 -070025
26func TestSrcIsModule(t *testing.T) {
27 type args struct {
28 s string
29 }
30 tests := []struct {
31 name string
32 args args
33 wantModule string
34 }{
35 {
36 name: "file",
37 args: args{
38 s: "foo",
39 },
40 wantModule: "",
41 },
42 {
43 name: "module",
44 args: args{
45 s: ":foo",
46 },
47 wantModule: "foo",
48 },
49 {
50 name: "tag",
51 args: args{
52 s: ":foo{.bar}",
53 },
54 wantModule: "foo{.bar}",
55 },
56 {
57 name: "extra colon",
58 args: args{
59 s: ":foo:bar",
60 },
61 wantModule: "foo:bar",
62 },
Paul Duffine6ba0722021-07-12 20:12:12 +010063 {
64 name: "fully qualified",
65 args: args{
66 s: "//foo:bar",
67 },
68 wantModule: "//foo:bar",
69 },
70 {
71 name: "fully qualified with tag",
72 args: args{
73 s: "//foo:bar{.tag}",
74 },
75 wantModule: "//foo:bar{.tag}",
76 },
77 {
78 name: "invalid unqualified name",
79 args: args{
80 s: ":foo/bar",
81 },
82 wantModule: "",
83 },
Colin Cross41955e82019-05-29 14:40:35 -070084 }
85 for _, tt := range tests {
86 t.Run(tt.name, func(t *testing.T) {
87 if gotModule := SrcIsModule(tt.args.s); gotModule != tt.wantModule {
88 t.Errorf("SrcIsModule() = %v, want %v", gotModule, tt.wantModule)
89 }
90 })
91 }
92}
93
94func TestSrcIsModuleWithTag(t *testing.T) {
95 type args struct {
96 s string
97 }
98 tests := []struct {
99 name string
100 args args
101 wantModule string
102 wantTag string
103 }{
104 {
105 name: "file",
106 args: args{
107 s: "foo",
108 },
109 wantModule: "",
110 wantTag: "",
111 },
112 {
113 name: "module",
114 args: args{
115 s: ":foo",
116 },
117 wantModule: "foo",
118 wantTag: "",
119 },
120 {
121 name: "tag",
122 args: args{
123 s: ":foo{.bar}",
124 },
125 wantModule: "foo",
126 wantTag: ".bar",
127 },
128 {
129 name: "empty tag",
130 args: args{
131 s: ":foo{}",
132 },
133 wantModule: "foo",
134 wantTag: "",
135 },
136 {
137 name: "extra colon",
138 args: args{
139 s: ":foo:bar",
140 },
141 wantModule: "foo:bar",
142 },
143 {
144 name: "invalid tag",
145 args: args{
146 s: ":foo{.bar",
147 },
148 wantModule: "foo{.bar",
149 },
150 {
151 name: "invalid tag 2",
152 args: args{
153 s: ":foo.bar}",
154 },
155 wantModule: "foo.bar}",
156 },
Paul Duffine6ba0722021-07-12 20:12:12 +0100157 {
158 name: "fully qualified",
159 args: args{
160 s: "//foo:bar",
161 },
162 wantModule: "//foo:bar",
163 },
164 {
165 name: "fully qualified with tag",
166 args: args{
167 s: "//foo:bar{.tag}",
168 },
169 wantModule: "//foo:bar",
170 wantTag: ".tag",
171 },
172 {
173 name: "invalid unqualified name",
174 args: args{
175 s: ":foo/bar",
176 },
177 wantModule: "",
178 },
179 {
180 name: "invalid unqualified name with tag",
181 args: args{
182 s: ":foo/bar{.tag}",
183 },
184 wantModule: "",
185 },
Colin Cross41955e82019-05-29 14:40:35 -0700186 }
187 for _, tt := range tests {
188 t.Run(tt.name, func(t *testing.T) {
189 gotModule, gotTag := SrcIsModuleWithTag(tt.args.s)
190 if gotModule != tt.wantModule {
191 t.Errorf("SrcIsModuleWithTag() gotModule = %v, want %v", gotModule, tt.wantModule)
192 }
193 if gotTag != tt.wantTag {
194 t.Errorf("SrcIsModuleWithTag() gotTag = %v, want %v", gotTag, tt.wantTag)
195 }
196 })
197 }
198}
Jooyung Hand48f3c32019-08-23 11:18:57 +0900199
200type depsModule struct {
201 ModuleBase
202 props struct {
203 Deps []string
204 }
205}
206
207func (m *depsModule) GenerateAndroidBuildActions(ctx ModuleContext) {
Colin Cross6ac95762021-11-09 13:17:44 -0800208 outputFile := PathForModuleOut(ctx, ctx.ModuleName())
209 ctx.Build(pctx, BuildParams{
210 Rule: Touch,
211 Output: outputFile,
212 })
213 installFile := ctx.InstallFile(PathForModuleInstall(ctx), ctx.ModuleName(), outputFile)
214 ctx.InstallSymlink(PathForModuleInstall(ctx, "symlinks"), ctx.ModuleName(), installFile)
Jooyung Hand48f3c32019-08-23 11:18:57 +0900215}
216
217func (m *depsModule) DepsMutator(ctx BottomUpMutatorContext) {
Colin Cross6ac95762021-11-09 13:17:44 -0800218 ctx.AddDependency(ctx.Module(), installDepTag{}, m.props.Deps...)
Jooyung Hand48f3c32019-08-23 11:18:57 +0900219}
220
221func depsModuleFactory() Module {
222 m := &depsModule{}
223 m.AddProperties(&m.props)
Colin Cross6ac95762021-11-09 13:17:44 -0800224 InitAndroidArchModule(m, HostAndDeviceDefault, MultilibCommon)
Jooyung Hand48f3c32019-08-23 11:18:57 +0900225 return m
226}
227
Paul Duffinf62dc9b2021-03-16 19:44:51 +0000228var prepareForModuleTests = FixtureRegisterWithContext(func(ctx RegistrationContext) {
229 ctx.RegisterModuleType("deps", depsModuleFactory)
230})
231
Jooyung Hand48f3c32019-08-23 11:18:57 +0900232func TestErrorDependsOnDisabledModule(t *testing.T) {
Jooyung Hand48f3c32019-08-23 11:18:57 +0900233 bp := `
234 deps {
235 name: "foo",
236 deps: ["bar"],
237 }
238 deps {
239 name: "bar",
240 enabled: false,
241 }
242 `
243
Paul Duffin30ac3e72021-03-20 00:36:14 +0000244 prepareForModuleTests.
Paul Duffinf62dc9b2021-03-16 19:44:51 +0000245 ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo": depends on disabled module "bar"`)).
Paul Duffin30ac3e72021-03-20 00:36:14 +0000246 RunTestWithBp(t, bp)
Jooyung Hand48f3c32019-08-23 11:18:57 +0900247}
Jingwen Chence679d22020-09-23 04:30:02 +0000248
249func TestValidateCorrectBuildParams(t *testing.T) {
Paul Duffinf62dc9b2021-03-16 19:44:51 +0000250 config := TestConfig(t.TempDir(), nil, "", nil)
Jingwen Chence679d22020-09-23 04:30:02 +0000251 pathContext := PathContextForTesting(config)
252 bparams := convertBuildParams(BuildParams{
253 // Test with Output
254 Output: PathForOutput(pathContext, "undeclared_symlink"),
255 SymlinkOutput: PathForOutput(pathContext, "undeclared_symlink"),
256 })
257
258 err := validateBuildParams(bparams)
259 if err != nil {
260 t.Error(err)
261 }
262
263 bparams = convertBuildParams(BuildParams{
264 // Test with ImplicitOutput
265 ImplicitOutput: PathForOutput(pathContext, "undeclared_symlink"),
266 SymlinkOutput: PathForOutput(pathContext, "undeclared_symlink"),
267 })
268
269 err = validateBuildParams(bparams)
270 if err != nil {
271 t.Error(err)
272 }
273}
274
275func TestValidateIncorrectBuildParams(t *testing.T) {
Paul Duffinf62dc9b2021-03-16 19:44:51 +0000276 config := TestConfig(t.TempDir(), nil, "", nil)
Jingwen Chence679d22020-09-23 04:30:02 +0000277 pathContext := PathContextForTesting(config)
278 params := BuildParams{
279 Output: PathForOutput(pathContext, "regular_output"),
280 Outputs: PathsForOutput(pathContext, []string{"out1", "out2"}),
281 ImplicitOutput: PathForOutput(pathContext, "implicit_output"),
282 ImplicitOutputs: PathsForOutput(pathContext, []string{"i_out1", "_out2"}),
283 SymlinkOutput: PathForOutput(pathContext, "undeclared_symlink"),
284 }
285
286 bparams := convertBuildParams(params)
287 err := validateBuildParams(bparams)
288 if err != nil {
289 FailIfNoMatchingErrors(t, "undeclared_symlink is not a declared output or implicit output", []error{err})
290 } else {
291 t.Errorf("Expected build params to fail validation: %+v", bparams)
292 }
293}
Paul Duffin89968e32020-11-23 18:17:03 +0000294
295func TestDistErrorChecking(t *testing.T) {
296 bp := `
297 deps {
298 name: "foo",
299 dist: {
300 dest: "../invalid-dest",
301 dir: "../invalid-dir",
302 suffix: "invalid/suffix",
303 },
304 dists: [
305 {
306 dest: "../invalid-dest0",
307 dir: "../invalid-dir0",
308 suffix: "invalid/suffix0",
309 },
310 {
311 dest: "../invalid-dest1",
312 dir: "../invalid-dir1",
313 suffix: "invalid/suffix1",
314 },
315 ],
316 }
317 `
318
Paul Duffin89968e32020-11-23 18:17:03 +0000319 expectedErrs := []string{
320 "\\QAndroid.bp:5:13: module \"foo\": dist.dest: Path is outside directory: ../invalid-dest\\E",
321 "\\QAndroid.bp:6:12: module \"foo\": dist.dir: Path is outside directory: ../invalid-dir\\E",
322 "\\QAndroid.bp:7:15: module \"foo\": dist.suffix: Suffix may not contain a '/' character.\\E",
323 "\\QAndroid.bp:11:15: module \"foo\": dists[0].dest: Path is outside directory: ../invalid-dest0\\E",
324 "\\QAndroid.bp:12:14: module \"foo\": dists[0].dir: Path is outside directory: ../invalid-dir0\\E",
325 "\\QAndroid.bp:13:17: module \"foo\": dists[0].suffix: Suffix may not contain a '/' character.\\E",
326 "\\QAndroid.bp:16:15: module \"foo\": dists[1].dest: Path is outside directory: ../invalid-dest1\\E",
327 "\\QAndroid.bp:17:14: module \"foo\": dists[1].dir: Path is outside directory: ../invalid-dir1\\E",
328 "\\QAndroid.bp:18:17: module \"foo\": dists[1].suffix: Suffix may not contain a '/' character.\\E",
329 }
Paul Duffinf62dc9b2021-03-16 19:44:51 +0000330
Paul Duffin30ac3e72021-03-20 00:36:14 +0000331 prepareForModuleTests.
Paul Duffinf62dc9b2021-03-16 19:44:51 +0000332 ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(expectedErrs)).
Paul Duffin30ac3e72021-03-20 00:36:14 +0000333 RunTestWithBp(t, bp)
Paul Duffin89968e32020-11-23 18:17:03 +0000334}
Colin Cross6ac95762021-11-09 13:17:44 -0800335
336func TestInstall(t *testing.T) {
337 if runtime.GOOS != "linux" {
338 t.Skip("requires linux")
339 }
340 bp := `
341 deps {
342 name: "foo",
343 deps: ["bar"],
344 }
345
346 deps {
347 name: "bar",
348 deps: ["baz", "qux"],
349 }
350
351 deps {
352 name: "baz",
353 deps: ["qux"],
354 }
355
356 deps {
357 name: "qux",
358 }
359 `
360
361 result := GroupFixturePreparers(
362 prepareForModuleTests,
363 PrepareForTestWithArchMutator,
364 ).RunTestWithBp(t, bp)
365
366 module := func(name string, host bool) TestingModule {
367 variant := "android_common"
368 if host {
369 variant = result.Config.BuildOSCommonTarget.String()
370 }
371 return result.ModuleForTests(name, variant)
372 }
373
374 outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
375
376 installRule := func(name string) TestingBuildParams {
377 return module(name, false).Output(filepath.Join("out/soong/target/product/test_device/system", name))
378 }
379
380 symlinkRule := func(name string) TestingBuildParams {
381 return module(name, false).Output(filepath.Join("out/soong/target/product/test_device/system/symlinks", name))
382 }
383
384 hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) }
385
386 hostInstallRule := func(name string) TestingBuildParams {
387 return module(name, true).Output(filepath.Join("out/soong/host/linux-x86", name))
388 }
389
390 hostSymlinkRule := func(name string) TestingBuildParams {
391 return module(name, true).Output(filepath.Join("out/soong/host/linux-x86/symlinks", name))
392 }
393
394 assertInputs := func(params TestingBuildParams, inputs ...Path) {
395 t.Helper()
396 AssertArrayString(t, "expected inputs", Paths(inputs).Strings(),
397 append(PathsIfNonNil(params.Input), params.Inputs...).Strings())
398 }
399
400 assertImplicits := func(params TestingBuildParams, implicits ...Path) {
401 t.Helper()
402 AssertArrayString(t, "expected implicit dependencies", Paths(implicits).Strings(),
403 append(PathsIfNonNil(params.Implicit), params.Implicits...).Strings())
404 }
405
406 assertOrderOnlys := func(params TestingBuildParams, orderonlys ...Path) {
407 t.Helper()
408 AssertArrayString(t, "expected orderonly dependencies", Paths(orderonlys).Strings(),
409 params.OrderOnly.Strings())
410 }
411
412 // Check host install rule dependencies
413 assertInputs(hostInstallRule("foo"), hostOutputRule("foo").Output)
414 assertImplicits(hostInstallRule("foo"),
415 hostInstallRule("bar").Output,
416 hostSymlinkRule("bar").Output,
417 hostInstallRule("baz").Output,
418 hostSymlinkRule("baz").Output,
419 hostInstallRule("qux").Output,
420 hostSymlinkRule("qux").Output,
421 )
422 assertOrderOnlys(hostInstallRule("foo"))
423
Colin Cross64002af2021-11-09 16:37:52 -0800424 // Check host symlink rule dependencies. Host symlinks must use a normal dependency, not an
425 // order-only dependency, so that the tool gets updated when the symlink is depended on.
Colin Cross6ac95762021-11-09 13:17:44 -0800426 assertInputs(hostSymlinkRule("foo"), hostInstallRule("foo").Output)
427 assertImplicits(hostSymlinkRule("foo"))
428 assertOrderOnlys(hostSymlinkRule("foo"))
429
430 // Check device install rule dependencies
431 assertInputs(installRule("foo"), outputRule("foo").Output)
432 assertImplicits(installRule("foo"))
433 assertOrderOnlys(installRule("foo"),
434 installRule("bar").Output,
435 symlinkRule("bar").Output,
436 installRule("baz").Output,
437 symlinkRule("baz").Output,
438 installRule("qux").Output,
439 symlinkRule("qux").Output,
440 )
441
Colin Cross64002af2021-11-09 16:37:52 -0800442 // Check device symlink rule dependencies. Device symlinks could use an order-only dependency,
443 // but the current implementation uses a normal dependency.
Colin Cross6ac95762021-11-09 13:17:44 -0800444 assertInputs(symlinkRule("foo"), installRule("foo").Output)
445 assertImplicits(symlinkRule("foo"))
446 assertOrderOnlys(symlinkRule("foo"))
447}
448
Colin Crossc68db4b2021-11-11 18:59:15 -0800449func TestInstallKatiEnabled(t *testing.T) {
Colin Cross6ac95762021-11-09 13:17:44 -0800450 if runtime.GOOS != "linux" {
451 t.Skip("requires linux")
452 }
453 bp := `
454 deps {
455 name: "foo",
456 deps: ["bar"],
457 }
458
459 deps {
460 name: "bar",
461 deps: ["baz", "qux"],
462 }
463
464 deps {
465 name: "baz",
466 deps: ["qux"],
467 }
468
469 deps {
470 name: "qux",
471 }
472 `
473
474 result := GroupFixturePreparers(
475 prepareForModuleTests,
476 PrepareForTestWithArchMutator,
477 FixtureModifyConfig(SetKatiEnabledForTests),
478 FixtureRegisterWithContext(func(ctx RegistrationContext) {
479 ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
480 }),
481 ).RunTestWithBp(t, bp)
482
483 installs := result.SingletonForTests("makevars").Singleton().(*makeVarsSingleton).installsForTesting
484 buf := bytes.NewBuffer(append([]byte(nil), installs...))
485 parser := mkparser.NewParser("makevars", buf)
486
487 nodes, errs := parser.Parse()
488 if len(errs) > 0 {
489 t.Fatalf("error parsing install rules: %s", errs[0])
490 }
491
492 rules := parseMkRules(t, result.Config, nodes)
493
494 module := func(name string, host bool) TestingModule {
495 variant := "android_common"
496 if host {
497 variant = result.Config.BuildOSCommonTarget.String()
498 }
499 return result.ModuleForTests(name, variant)
500 }
501
502 outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
503
504 ruleForOutput := func(output string) installMakeRule {
505 for _, rule := range rules {
506 if rule.target == output {
507 return rule
508 }
509 }
510 t.Fatalf("no make install rule for %s", output)
511 return installMakeRule{}
512 }
513
514 installRule := func(name string) installMakeRule {
515 return ruleForOutput(filepath.Join("out/target/product/test_device/system", name))
516 }
517
518 symlinkRule := func(name string) installMakeRule {
519 return ruleForOutput(filepath.Join("out/target/product/test_device/system/symlinks", name))
520 }
521
522 hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) }
523
524 hostInstallRule := func(name string) installMakeRule {
525 return ruleForOutput(filepath.Join("out/host/linux-x86", name))
526 }
527
528 hostSymlinkRule := func(name string) installMakeRule {
529 return ruleForOutput(filepath.Join("out/host/linux-x86/symlinks", name))
530 }
531
532 assertDeps := func(rule installMakeRule, deps ...string) {
533 t.Helper()
534 AssertArrayString(t, "expected inputs", deps, rule.deps)
535 }
536
537 assertOrderOnlys := func(rule installMakeRule, orderonlys ...string) {
538 t.Helper()
539 AssertArrayString(t, "expected orderonly dependencies", orderonlys, rule.orderOnlyDeps)
540 }
541
542 // Check host install rule dependencies
543 assertDeps(hostInstallRule("foo"),
544 hostOutputRule("foo").Output.String(),
545 hostInstallRule("bar").target,
546 hostSymlinkRule("bar").target,
547 hostInstallRule("baz").target,
548 hostSymlinkRule("baz").target,
549 hostInstallRule("qux").target,
550 hostSymlinkRule("qux").target,
551 )
552 assertOrderOnlys(hostInstallRule("foo"))
553
Colin Cross64002af2021-11-09 16:37:52 -0800554 // Check host symlink rule dependencies. Host symlinks must use a normal dependency, not an
555 // order-only dependency, so that the tool gets updated when the symlink is depended on.
556 assertDeps(hostSymlinkRule("foo"), hostInstallRule("foo").target)
557 assertOrderOnlys(hostSymlinkRule("foo"))
Colin Cross6ac95762021-11-09 13:17:44 -0800558
559 // Check device install rule dependencies
560 assertDeps(installRule("foo"), outputRule("foo").Output.String())
561 assertOrderOnlys(installRule("foo"),
562 installRule("bar").target,
563 symlinkRule("bar").target,
564 installRule("baz").target,
565 symlinkRule("baz").target,
566 installRule("qux").target,
567 symlinkRule("qux").target,
568 )
569
Colin Cross64002af2021-11-09 16:37:52 -0800570 // Check device symlink rule dependencies. Device symlinks could use an order-only dependency,
571 // but the current implementation uses a normal dependency.
572 assertDeps(symlinkRule("foo"), installRule("foo").target)
573 assertOrderOnlys(symlinkRule("foo"))
Colin Cross6ac95762021-11-09 13:17:44 -0800574}
575
576type installMakeRule struct {
577 target string
578 deps []string
579 orderOnlyDeps []string
580}
581
582func parseMkRules(t *testing.T, config Config, nodes []mkparser.Node) []installMakeRule {
583 var rules []installMakeRule
584 for _, node := range nodes {
585 if mkParserRule, ok := node.(*mkparser.Rule); ok {
586 var rule installMakeRule
587
588 if targets := mkParserRule.Target.Words(); len(targets) == 0 {
589 t.Fatalf("no targets for rule %s", mkParserRule.Dump())
590 } else if len(targets) > 1 {
591 t.Fatalf("unsupported multiple targets for rule %s", mkParserRule.Dump())
592 } else if !targets[0].Const() {
593 t.Fatalf("unsupported non-const target for rule %s", mkParserRule.Dump())
594 } else {
595 rule.target = normalizeStringRelativeToTop(config, targets[0].Value(nil))
596 }
597
598 prereqList := &rule.deps
599 for _, prereq := range mkParserRule.Prerequisites.Words() {
600 if !prereq.Const() {
601 t.Fatalf("unsupported non-const prerequisite for rule %s", mkParserRule.Dump())
602 }
603
604 if prereq.Value(nil) == "|" {
605 prereqList = &rule.orderOnlyDeps
606 continue
607 }
608
609 *prereqList = append(*prereqList, normalizeStringRelativeToTop(config, prereq.Value(nil)))
610 }
611
612 rules = append(rules, rule)
613 }
614 }
615
616 return rules
617}