blob: 8607a8d34f872e3720439deff89847bc13852bb4 [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
Colin Cross6ac95762021-11-09 13:17:44 -0800207func (m *depsModule) InstallBypassMake() bool {
208 return true
209}
210
Jooyung Hand48f3c32019-08-23 11:18:57 +0900211func (m *depsModule) GenerateAndroidBuildActions(ctx ModuleContext) {
Colin Cross6ac95762021-11-09 13:17:44 -0800212 outputFile := PathForModuleOut(ctx, ctx.ModuleName())
213 ctx.Build(pctx, BuildParams{
214 Rule: Touch,
215 Output: outputFile,
216 })
217 installFile := ctx.InstallFile(PathForModuleInstall(ctx), ctx.ModuleName(), outputFile)
218 ctx.InstallSymlink(PathForModuleInstall(ctx, "symlinks"), ctx.ModuleName(), installFile)
Jooyung Hand48f3c32019-08-23 11:18:57 +0900219}
220
221func (m *depsModule) DepsMutator(ctx BottomUpMutatorContext) {
Colin Cross6ac95762021-11-09 13:17:44 -0800222 ctx.AddDependency(ctx.Module(), installDepTag{}, m.props.Deps...)
Jooyung Hand48f3c32019-08-23 11:18:57 +0900223}
224
225func depsModuleFactory() Module {
226 m := &depsModule{}
227 m.AddProperties(&m.props)
Colin Cross6ac95762021-11-09 13:17:44 -0800228 InitAndroidArchModule(m, HostAndDeviceDefault, MultilibCommon)
Jooyung Hand48f3c32019-08-23 11:18:57 +0900229 return m
230}
231
Paul Duffinf62dc9b2021-03-16 19:44:51 +0000232var prepareForModuleTests = FixtureRegisterWithContext(func(ctx RegistrationContext) {
233 ctx.RegisterModuleType("deps", depsModuleFactory)
234})
235
Jooyung Hand48f3c32019-08-23 11:18:57 +0900236func TestErrorDependsOnDisabledModule(t *testing.T) {
Jooyung Hand48f3c32019-08-23 11:18:57 +0900237 bp := `
238 deps {
239 name: "foo",
240 deps: ["bar"],
241 }
242 deps {
243 name: "bar",
244 enabled: false,
245 }
246 `
247
Paul Duffin30ac3e72021-03-20 00:36:14 +0000248 prepareForModuleTests.
Paul Duffinf62dc9b2021-03-16 19:44:51 +0000249 ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo": depends on disabled module "bar"`)).
Paul Duffin30ac3e72021-03-20 00:36:14 +0000250 RunTestWithBp(t, bp)
Jooyung Hand48f3c32019-08-23 11:18:57 +0900251}
Jingwen Chence679d22020-09-23 04:30:02 +0000252
253func TestValidateCorrectBuildParams(t *testing.T) {
Paul Duffinf62dc9b2021-03-16 19:44:51 +0000254 config := TestConfig(t.TempDir(), nil, "", nil)
Jingwen Chence679d22020-09-23 04:30:02 +0000255 pathContext := PathContextForTesting(config)
256 bparams := convertBuildParams(BuildParams{
257 // Test with Output
258 Output: PathForOutput(pathContext, "undeclared_symlink"),
259 SymlinkOutput: PathForOutput(pathContext, "undeclared_symlink"),
260 })
261
262 err := validateBuildParams(bparams)
263 if err != nil {
264 t.Error(err)
265 }
266
267 bparams = convertBuildParams(BuildParams{
268 // Test with ImplicitOutput
269 ImplicitOutput: PathForOutput(pathContext, "undeclared_symlink"),
270 SymlinkOutput: PathForOutput(pathContext, "undeclared_symlink"),
271 })
272
273 err = validateBuildParams(bparams)
274 if err != nil {
275 t.Error(err)
276 }
277}
278
279func TestValidateIncorrectBuildParams(t *testing.T) {
Paul Duffinf62dc9b2021-03-16 19:44:51 +0000280 config := TestConfig(t.TempDir(), nil, "", nil)
Jingwen Chence679d22020-09-23 04:30:02 +0000281 pathContext := PathContextForTesting(config)
282 params := BuildParams{
283 Output: PathForOutput(pathContext, "regular_output"),
284 Outputs: PathsForOutput(pathContext, []string{"out1", "out2"}),
285 ImplicitOutput: PathForOutput(pathContext, "implicit_output"),
286 ImplicitOutputs: PathsForOutput(pathContext, []string{"i_out1", "_out2"}),
287 SymlinkOutput: PathForOutput(pathContext, "undeclared_symlink"),
288 }
289
290 bparams := convertBuildParams(params)
291 err := validateBuildParams(bparams)
292 if err != nil {
293 FailIfNoMatchingErrors(t, "undeclared_symlink is not a declared output or implicit output", []error{err})
294 } else {
295 t.Errorf("Expected build params to fail validation: %+v", bparams)
296 }
297}
Paul Duffin89968e32020-11-23 18:17:03 +0000298
299func TestDistErrorChecking(t *testing.T) {
300 bp := `
301 deps {
302 name: "foo",
303 dist: {
304 dest: "../invalid-dest",
305 dir: "../invalid-dir",
306 suffix: "invalid/suffix",
307 },
308 dists: [
309 {
310 dest: "../invalid-dest0",
311 dir: "../invalid-dir0",
312 suffix: "invalid/suffix0",
313 },
314 {
315 dest: "../invalid-dest1",
316 dir: "../invalid-dir1",
317 suffix: "invalid/suffix1",
318 },
319 ],
320 }
321 `
322
Paul Duffin89968e32020-11-23 18:17:03 +0000323 expectedErrs := []string{
324 "\\QAndroid.bp:5:13: module \"foo\": dist.dest: Path is outside directory: ../invalid-dest\\E",
325 "\\QAndroid.bp:6:12: module \"foo\": dist.dir: Path is outside directory: ../invalid-dir\\E",
326 "\\QAndroid.bp:7:15: module \"foo\": dist.suffix: Suffix may not contain a '/' character.\\E",
327 "\\QAndroid.bp:11:15: module \"foo\": dists[0].dest: Path is outside directory: ../invalid-dest0\\E",
328 "\\QAndroid.bp:12:14: module \"foo\": dists[0].dir: Path is outside directory: ../invalid-dir0\\E",
329 "\\QAndroid.bp:13:17: module \"foo\": dists[0].suffix: Suffix may not contain a '/' character.\\E",
330 "\\QAndroid.bp:16:15: module \"foo\": dists[1].dest: Path is outside directory: ../invalid-dest1\\E",
331 "\\QAndroid.bp:17:14: module \"foo\": dists[1].dir: Path is outside directory: ../invalid-dir1\\E",
332 "\\QAndroid.bp:18:17: module \"foo\": dists[1].suffix: Suffix may not contain a '/' character.\\E",
333 }
Paul Duffinf62dc9b2021-03-16 19:44:51 +0000334
Paul Duffin30ac3e72021-03-20 00:36:14 +0000335 prepareForModuleTests.
Paul Duffinf62dc9b2021-03-16 19:44:51 +0000336 ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(expectedErrs)).
Paul Duffin30ac3e72021-03-20 00:36:14 +0000337 RunTestWithBp(t, bp)
Paul Duffin89968e32020-11-23 18:17:03 +0000338}
Colin Cross6ac95762021-11-09 13:17:44 -0800339
340func TestInstall(t *testing.T) {
341 if runtime.GOOS != "linux" {
342 t.Skip("requires linux")
343 }
344 bp := `
345 deps {
346 name: "foo",
347 deps: ["bar"],
348 }
349
350 deps {
351 name: "bar",
352 deps: ["baz", "qux"],
353 }
354
355 deps {
356 name: "baz",
357 deps: ["qux"],
358 }
359
360 deps {
361 name: "qux",
362 }
363 `
364
365 result := GroupFixturePreparers(
366 prepareForModuleTests,
367 PrepareForTestWithArchMutator,
368 ).RunTestWithBp(t, bp)
369
370 module := func(name string, host bool) TestingModule {
371 variant := "android_common"
372 if host {
373 variant = result.Config.BuildOSCommonTarget.String()
374 }
375 return result.ModuleForTests(name, variant)
376 }
377
378 outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
379
380 installRule := func(name string) TestingBuildParams {
381 return module(name, false).Output(filepath.Join("out/soong/target/product/test_device/system", name))
382 }
383
384 symlinkRule := func(name string) TestingBuildParams {
385 return module(name, false).Output(filepath.Join("out/soong/target/product/test_device/system/symlinks", name))
386 }
387
388 hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) }
389
390 hostInstallRule := func(name string) TestingBuildParams {
391 return module(name, true).Output(filepath.Join("out/soong/host/linux-x86", name))
392 }
393
394 hostSymlinkRule := func(name string) TestingBuildParams {
395 return module(name, true).Output(filepath.Join("out/soong/host/linux-x86/symlinks", name))
396 }
397
398 assertInputs := func(params TestingBuildParams, inputs ...Path) {
399 t.Helper()
400 AssertArrayString(t, "expected inputs", Paths(inputs).Strings(),
401 append(PathsIfNonNil(params.Input), params.Inputs...).Strings())
402 }
403
404 assertImplicits := func(params TestingBuildParams, implicits ...Path) {
405 t.Helper()
406 AssertArrayString(t, "expected implicit dependencies", Paths(implicits).Strings(),
407 append(PathsIfNonNil(params.Implicit), params.Implicits...).Strings())
408 }
409
410 assertOrderOnlys := func(params TestingBuildParams, orderonlys ...Path) {
411 t.Helper()
412 AssertArrayString(t, "expected orderonly dependencies", Paths(orderonlys).Strings(),
413 params.OrderOnly.Strings())
414 }
415
416 // Check host install rule dependencies
417 assertInputs(hostInstallRule("foo"), hostOutputRule("foo").Output)
418 assertImplicits(hostInstallRule("foo"),
419 hostInstallRule("bar").Output,
420 hostSymlinkRule("bar").Output,
421 hostInstallRule("baz").Output,
422 hostSymlinkRule("baz").Output,
423 hostInstallRule("qux").Output,
424 hostSymlinkRule("qux").Output,
425 )
426 assertOrderOnlys(hostInstallRule("foo"))
427
Colin Cross64002af2021-11-09 16:37:52 -0800428 // Check host symlink rule dependencies. Host symlinks must use a normal dependency, not an
429 // order-only dependency, so that the tool gets updated when the symlink is depended on.
Colin Cross6ac95762021-11-09 13:17:44 -0800430 assertInputs(hostSymlinkRule("foo"), hostInstallRule("foo").Output)
431 assertImplicits(hostSymlinkRule("foo"))
432 assertOrderOnlys(hostSymlinkRule("foo"))
433
434 // Check device install rule dependencies
435 assertInputs(installRule("foo"), outputRule("foo").Output)
436 assertImplicits(installRule("foo"))
437 assertOrderOnlys(installRule("foo"),
438 installRule("bar").Output,
439 symlinkRule("bar").Output,
440 installRule("baz").Output,
441 symlinkRule("baz").Output,
442 installRule("qux").Output,
443 symlinkRule("qux").Output,
444 )
445
Colin Cross64002af2021-11-09 16:37:52 -0800446 // Check device symlink rule dependencies. Device symlinks could use an order-only dependency,
447 // but the current implementation uses a normal dependency.
Colin Cross6ac95762021-11-09 13:17:44 -0800448 assertInputs(symlinkRule("foo"), installRule("foo").Output)
449 assertImplicits(symlinkRule("foo"))
450 assertOrderOnlys(symlinkRule("foo"))
451}
452
453func TestInstallBypassMake(t *testing.T) {
454 if runtime.GOOS != "linux" {
455 t.Skip("requires linux")
456 }
457 bp := `
458 deps {
459 name: "foo",
460 deps: ["bar"],
461 }
462
463 deps {
464 name: "bar",
465 deps: ["baz", "qux"],
466 }
467
468 deps {
469 name: "baz",
470 deps: ["qux"],
471 }
472
473 deps {
474 name: "qux",
475 }
476 `
477
478 result := GroupFixturePreparers(
479 prepareForModuleTests,
480 PrepareForTestWithArchMutator,
481 FixtureModifyConfig(SetKatiEnabledForTests),
482 FixtureRegisterWithContext(func(ctx RegistrationContext) {
483 ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
484 }),
485 ).RunTestWithBp(t, bp)
486
487 installs := result.SingletonForTests("makevars").Singleton().(*makeVarsSingleton).installsForTesting
488 buf := bytes.NewBuffer(append([]byte(nil), installs...))
489 parser := mkparser.NewParser("makevars", buf)
490
491 nodes, errs := parser.Parse()
492 if len(errs) > 0 {
493 t.Fatalf("error parsing install rules: %s", errs[0])
494 }
495
496 rules := parseMkRules(t, result.Config, nodes)
497
498 module := func(name string, host bool) TestingModule {
499 variant := "android_common"
500 if host {
501 variant = result.Config.BuildOSCommonTarget.String()
502 }
503 return result.ModuleForTests(name, variant)
504 }
505
506 outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
507
508 ruleForOutput := func(output string) installMakeRule {
509 for _, rule := range rules {
510 if rule.target == output {
511 return rule
512 }
513 }
514 t.Fatalf("no make install rule for %s", output)
515 return installMakeRule{}
516 }
517
518 installRule := func(name string) installMakeRule {
519 return ruleForOutput(filepath.Join("out/target/product/test_device/system", name))
520 }
521
522 symlinkRule := func(name string) installMakeRule {
523 return ruleForOutput(filepath.Join("out/target/product/test_device/system/symlinks", name))
524 }
525
526 hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) }
527
528 hostInstallRule := func(name string) installMakeRule {
529 return ruleForOutput(filepath.Join("out/host/linux-x86", name))
530 }
531
532 hostSymlinkRule := func(name string) installMakeRule {
533 return ruleForOutput(filepath.Join("out/host/linux-x86/symlinks", name))
534 }
535
536 assertDeps := func(rule installMakeRule, deps ...string) {
537 t.Helper()
538 AssertArrayString(t, "expected inputs", deps, rule.deps)
539 }
540
541 assertOrderOnlys := func(rule installMakeRule, orderonlys ...string) {
542 t.Helper()
543 AssertArrayString(t, "expected orderonly dependencies", orderonlys, rule.orderOnlyDeps)
544 }
545
546 // Check host install rule dependencies
547 assertDeps(hostInstallRule("foo"),
548 hostOutputRule("foo").Output.String(),
549 hostInstallRule("bar").target,
550 hostSymlinkRule("bar").target,
551 hostInstallRule("baz").target,
552 hostSymlinkRule("baz").target,
553 hostInstallRule("qux").target,
554 hostSymlinkRule("qux").target,
555 )
556 assertOrderOnlys(hostInstallRule("foo"))
557
Colin Cross64002af2021-11-09 16:37:52 -0800558 // Check host symlink rule dependencies. Host symlinks must use a normal dependency, not an
559 // order-only dependency, so that the tool gets updated when the symlink is depended on.
560 assertDeps(hostSymlinkRule("foo"), hostInstallRule("foo").target)
561 assertOrderOnlys(hostSymlinkRule("foo"))
Colin Cross6ac95762021-11-09 13:17:44 -0800562
563 // Check device install rule dependencies
564 assertDeps(installRule("foo"), outputRule("foo").Output.String())
565 assertOrderOnlys(installRule("foo"),
566 installRule("bar").target,
567 symlinkRule("bar").target,
568 installRule("baz").target,
569 symlinkRule("baz").target,
570 installRule("qux").target,
571 symlinkRule("qux").target,
572 )
573
Colin Cross64002af2021-11-09 16:37:52 -0800574 // Check device symlink rule dependencies. Device symlinks could use an order-only dependency,
575 // but the current implementation uses a normal dependency.
576 assertDeps(symlinkRule("foo"), installRule("foo").target)
577 assertOrderOnlys(symlinkRule("foo"))
Colin Cross6ac95762021-11-09 13:17:44 -0800578}
579
580type installMakeRule struct {
581 target string
582 deps []string
583 orderOnlyDeps []string
584}
585
586func parseMkRules(t *testing.T, config Config, nodes []mkparser.Node) []installMakeRule {
587 var rules []installMakeRule
588 for _, node := range nodes {
589 if mkParserRule, ok := node.(*mkparser.Rule); ok {
590 var rule installMakeRule
591
592 if targets := mkParserRule.Target.Words(); len(targets) == 0 {
593 t.Fatalf("no targets for rule %s", mkParserRule.Dump())
594 } else if len(targets) > 1 {
595 t.Fatalf("unsupported multiple targets for rule %s", mkParserRule.Dump())
596 } else if !targets[0].Const() {
597 t.Fatalf("unsupported non-const target for rule %s", mkParserRule.Dump())
598 } else {
599 rule.target = normalizeStringRelativeToTop(config, targets[0].Value(nil))
600 }
601
602 prereqList := &rule.deps
603 for _, prereq := range mkParserRule.Prerequisites.Words() {
604 if !prereq.Const() {
605 t.Fatalf("unsupported non-const prerequisite for rule %s", mkParserRule.Dump())
606 }
607
608 if prereq.Value(nil) == "|" {
609 prereqList = &rule.orderOnlyDeps
610 continue
611 }
612
613 *prereqList = append(*prereqList, normalizeStringRelativeToTop(config, prereq.Value(nil)))
614 }
615
616 rules = append(rules, rule)
617 }
618 }
619
620 return rules
621}