1// Copyright 2014 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 blueprint 16 17import ( 18 "bytes" 19 "errors" 20 "fmt" 21 "hash/fnv" 22 "os" 23 "reflect" 24 "slices" 25 "strconv" 26 "strings" 27 "sync" 28 "testing" 29 "text/scanner" 30 "time" 31 32 "github.com/google/blueprint/parser" 33 "github.com/google/blueprint/proptools" 34) 35 36type Walker interface { 37 Walk() bool 38} 39 40func walkDependencyGraph(ctx *Context, topModule *moduleInfo, allowDuplicates bool) (string, string) { 41 var outputDown string 42 var outputUp string 43 ctx.walkDeps(topModule, allowDuplicates, 44 func(dep depInfo, parent *moduleInfo) bool { 45 outputDown += ctx.ModuleName(dep.module.logicModule) 46 if tag, ok := dep.tag.(walkerDepsTag); ok { 47 if !tag.follow { 48 return false 49 } 50 } 51 if dep.module.logicModule.(Walker).Walk() { 52 return true 53 } 54 55 return false 56 }, 57 func(dep depInfo, parent *moduleInfo) { 58 outputUp += ctx.ModuleName(dep.module.logicModule) 59 }) 60 return outputDown, outputUp 61} 62 63type depsProvider interface { 64 Deps() []string 65 IgnoreDeps() []string 66} 67 68type IncrementalTestProvider struct { 69 Value string 70} 71 72var IncrementalTestProviderKey = NewProvider[IncrementalTestProvider]() 73 74type baseTestModule struct { 75 SimpleName 76 properties struct { 77 Deps []string 78 Ignored_deps []string 79 } 80 GenerateBuildActionsCalled bool 81} 82 83func (b *baseTestModule) Deps() []string { 84 return b.properties.Deps 85} 86 87func (b *baseTestModule) IgnoreDeps() []string { 88 return b.properties.Ignored_deps 89} 90 91var pctx PackageContext 92 93func init() { 94 pctx = NewPackageContext("android/blueprint") 95} 96func (b *baseTestModule) GenerateBuildActions(ctx ModuleContext) { 97 b.GenerateBuildActionsCalled = true 98 outputFile := ctx.ModuleName() + "_phony_output" 99 ctx.Build(pctx, BuildParams{ 100 Rule: Phony, 101 Outputs: []string{outputFile}, 102 }) 103 SetProvider(ctx, IncrementalTestProviderKey, IncrementalTestProvider{ 104 Value: ctx.ModuleName(), 105 }) 106} 107 108type fooModule struct { 109 baseTestModule 110} 111 112func newFooModule() (Module, []interface{}) { 113 m := &fooModule{} 114 return m, []interface{}{&m.baseTestModule.properties, &m.SimpleName.Properties} 115} 116 117func (f *fooModule) Walk() bool { 118 return true 119} 120 121type barModule struct { 122 SimpleName 123 baseTestModule 124} 125 126func newBarModule() (Module, []interface{}) { 127 m := &barModule{} 128 return m, []interface{}{&m.baseTestModule.properties, &m.SimpleName.Properties} 129} 130 131func (b *barModule) Walk() bool { 132 return false 133} 134 135type incrementalModule struct { 136 SimpleName 137 baseTestModule 138 IncrementalModule 139} 140 141var _ Incremental = &incrementalModule{} 142 143func newIncrementalModule() (Module, []interface{}) { 144 m := &incrementalModule{} 145 return m, []interface{}{&m.baseTestModule.properties, &m.SimpleName.Properties} 146} 147 148type walkerDepsTag struct { 149 BaseDependencyTag 150 // True if the dependency should be followed, false otherwise. 151 follow bool 152} 153 154func depsMutator(mctx BottomUpMutatorContext) { 155 if m, ok := mctx.Module().(depsProvider); ok { 156 mctx.AddDependency(mctx.Module(), walkerDepsTag{follow: false}, m.IgnoreDeps()...) 157 mctx.AddDependency(mctx.Module(), walkerDepsTag{follow: true}, m.Deps()...) 158 } 159} 160 161func TestContextParse(t *testing.T) { 162 ctx := NewContext() 163 ctx.RegisterModuleType("foo_module", newFooModule) 164 ctx.RegisterModuleType("bar_module", newBarModule) 165 166 r := bytes.NewBufferString(` 167 foo_module { 168 name: "MyFooModule", 169 deps: ["MyBarModule"], 170 } 171 172 bar_module { 173 name: "MyBarModule", 174 } 175 `) 176 177 _, _, errs := ctx.parseOne(".", "Blueprint", r, parser.NewScope(nil), nil) 178 if len(errs) > 0 { 179 t.Errorf("unexpected parse errors:") 180 for _, err := range errs { 181 t.Errorf(" %s", err) 182 } 183 t.FailNow() 184 } 185 186 _, errs = ctx.ResolveDependencies(nil) 187 if len(errs) > 0 { 188 t.Errorf("unexpected dep errors:") 189 for _, err := range errs { 190 t.Errorf(" %s", err) 191 } 192 t.FailNow() 193 } 194} 195 196// > |===B---D - represents a non-walkable edge 197// > A = represents a walkable edge 198// > |===C===E---G 199// > | | A should not be visited because it's the root node. 200// > |===F===| B, D and E should not be walked. 201func TestWalkDeps(t *testing.T) { 202 ctx := NewContext() 203 ctx.MockFileSystem(map[string][]byte{ 204 "Android.bp": []byte(` 205 foo_module { 206 name: "A", 207 deps: ["B", "C"], 208 } 209 210 bar_module { 211 name: "B", 212 deps: ["D"], 213 } 214 215 foo_module { 216 name: "C", 217 deps: ["E", "F"], 218 } 219 220 foo_module { 221 name: "D", 222 } 223 224 bar_module { 225 name: "E", 226 deps: ["G"], 227 } 228 229 foo_module { 230 name: "F", 231 deps: ["G"], 232 } 233 234 foo_module { 235 name: "G", 236 } 237 `), 238 }) 239 240 ctx.RegisterModuleType("foo_module", newFooModule) 241 ctx.RegisterModuleType("bar_module", newBarModule) 242 ctx.RegisterBottomUpMutator("deps", depsMutator) 243 _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) 244 if len(errs) > 0 { 245 t.Errorf("unexpected parse errors:") 246 for _, err := range errs { 247 t.Errorf(" %s", err) 248 } 249 t.FailNow() 250 } 251 252 _, errs = ctx.ResolveDependencies(nil) 253 if len(errs) > 0 { 254 t.Errorf("unexpected dep errors:") 255 for _, err := range errs { 256 t.Errorf(" %s", err) 257 } 258 t.FailNow() 259 } 260 261 topModule := ctx.moduleGroupFromName("A", nil).modules.firstModule() 262 outputDown, outputUp := walkDependencyGraph(ctx, topModule, false) 263 if outputDown != "BCEFG" { 264 t.Errorf("unexpected walkDeps behaviour: %s\ndown should be: BCEFG", outputDown) 265 } 266 if outputUp != "BEGFC" { 267 t.Errorf("unexpected walkDeps behaviour: %s\nup should be: BEGFC", outputUp) 268 } 269} 270 271// > |===B---D - represents a non-walkable edge 272// > A = represents a walkable edge 273// > |===C===E===\ A should not be visited because it's the root node. 274// > | | B, D should not be walked. 275// > |===F===G===H G should be visited multiple times 276// > \===/ H should only be visited once 277func TestWalkDepsDuplicates(t *testing.T) { 278 ctx := NewContext() 279 ctx.MockFileSystem(map[string][]byte{ 280 "Android.bp": []byte(` 281 foo_module { 282 name: "A", 283 deps: ["B", "C"], 284 } 285 286 bar_module { 287 name: "B", 288 deps: ["D"], 289 } 290 291 foo_module { 292 name: "C", 293 deps: ["E", "F"], 294 } 295 296 foo_module { 297 name: "D", 298 } 299 300 foo_module { 301 name: "E", 302 deps: ["G"], 303 } 304 305 foo_module { 306 name: "F", 307 deps: ["G", "G"], 308 } 309 310 foo_module { 311 name: "G", 312 deps: ["H"], 313 } 314 315 foo_module { 316 name: "H", 317 } 318 `), 319 }) 320 321 ctx.RegisterModuleType("foo_module", newFooModule) 322 ctx.RegisterModuleType("bar_module", newBarModule) 323 ctx.RegisterBottomUpMutator("deps", depsMutator) 324 _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) 325 if len(errs) > 0 { 326 t.Errorf("unexpected parse errors:") 327 for _, err := range errs { 328 t.Errorf(" %s", err) 329 } 330 t.FailNow() 331 } 332 333 _, errs = ctx.ResolveDependencies(nil) 334 if len(errs) > 0 { 335 t.Errorf("unexpected dep errors:") 336 for _, err := range errs { 337 t.Errorf(" %s", err) 338 } 339 t.FailNow() 340 } 341 342 topModule := ctx.moduleGroupFromName("A", nil).modules.firstModule() 343 outputDown, outputUp := walkDependencyGraph(ctx, topModule, true) 344 if outputDown != "BCEGHFGG" { 345 t.Errorf("unexpected walkDeps behaviour: %s\ndown should be: BCEGHFGG", outputDown) 346 } 347 if outputUp != "BHGEGGFC" { 348 t.Errorf("unexpected walkDeps behaviour: %s\nup should be: BHGEGGFC", outputUp) 349 } 350} 351 352// > - represents a non-walkable edge 353// > A = represents a walkable edge 354// > |===B-------\ A should not be visited because it's the root node. 355// > | | B -> D should not be walked. 356// > |===C===D===E B -> C -> D -> E should be walked 357func TestWalkDepsDuplicates_IgnoreFirstPath(t *testing.T) { 358 ctx := NewContext() 359 ctx.MockFileSystem(map[string][]byte{ 360 "Android.bp": []byte(` 361 foo_module { 362 name: "A", 363 deps: ["B"], 364 } 365 366 foo_module { 367 name: "B", 368 deps: ["C"], 369 ignored_deps: ["D"], 370 } 371 372 foo_module { 373 name: "C", 374 deps: ["D"], 375 } 376 377 foo_module { 378 name: "D", 379 deps: ["E"], 380 } 381 382 foo_module { 383 name: "E", 384 } 385 `), 386 }) 387 388 ctx.RegisterModuleType("foo_module", newFooModule) 389 ctx.RegisterModuleType("bar_module", newBarModule) 390 ctx.RegisterBottomUpMutator("deps", depsMutator) 391 _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) 392 if len(errs) > 0 { 393 t.Errorf("unexpected parse errors:") 394 for _, err := range errs { 395 t.Errorf(" %s", err) 396 } 397 t.FailNow() 398 } 399 400 _, errs = ctx.ResolveDependencies(nil) 401 if len(errs) > 0 { 402 t.Errorf("unexpected dep errors:") 403 for _, err := range errs { 404 t.Errorf(" %s", err) 405 } 406 t.FailNow() 407 } 408 409 topModule := ctx.moduleGroupFromName("A", nil).modules.firstModule() 410 outputDown, outputUp := walkDependencyGraph(ctx, topModule, true) 411 expectedDown := "BDCDE" 412 if outputDown != expectedDown { 413 t.Errorf("unexpected walkDeps behaviour: %s\ndown should be: %s", outputDown, expectedDown) 414 } 415 expectedUp := "DEDCB" 416 if outputUp != expectedUp { 417 t.Errorf("unexpected walkDeps behaviour: %s\nup should be: %s", outputUp, expectedUp) 418 } 419} 420 421func TestCreateModule(t *testing.T) { 422 ctx := newContext() 423 ctx.MockFileSystem(map[string][]byte{ 424 "Android.bp": []byte(` 425 foo_module { 426 name: "A", 427 deps: ["B", "C"], 428 } 429 `), 430 }) 431 432 ctx.RegisterBottomUpMutator("create", createTestMutator).UsesCreateModule() 433 ctx.RegisterBottomUpMutator("deps", depsMutator) 434 435 ctx.RegisterModuleType("foo_module", newFooModule) 436 ctx.RegisterModuleType("bar_module", newBarModule) 437 _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) 438 if len(errs) > 0 { 439 t.Errorf("unexpected parse errors:") 440 for _, err := range errs { 441 t.Errorf(" %s", err) 442 } 443 t.FailNow() 444 } 445 446 _, errs = ctx.ResolveDependencies(nil) 447 if len(errs) > 0 { 448 t.Errorf("unexpected dep errors:") 449 for _, err := range errs { 450 t.Errorf(" %s", err) 451 } 452 t.FailNow() 453 } 454 455 a := ctx.moduleGroupFromName("A", nil).modules.firstModule().logicModule.(*fooModule) 456 b := ctx.moduleGroupFromName("B", nil).modules.firstModule().logicModule.(*barModule) 457 c := ctx.moduleGroupFromName("C", nil).modules.firstModule().logicModule.(*barModule) 458 d := ctx.moduleGroupFromName("D", nil).modules.firstModule().logicModule.(*fooModule) 459 460 checkDeps := func(m Module, expected string) { 461 var deps []string 462 ctx.VisitDirectDeps(m, func(m Module) { 463 deps = append(deps, ctx.ModuleName(m)) 464 }) 465 got := strings.Join(deps, ",") 466 if got != expected { 467 t.Errorf("unexpected %q dependencies, got %q expected %q", 468 ctx.ModuleName(m), got, expected) 469 } 470 } 471 472 checkDeps(a, "B,C") 473 checkDeps(b, "D") 474 checkDeps(c, "D") 475 checkDeps(d, "") 476} 477 478func createTestMutator(ctx BottomUpMutatorContext) { 479 type props struct { 480 Name string 481 Deps []string 482 } 483 484 ctx.CreateModule(newBarModule, "new_bar", &props{ 485 Name: "B", 486 Deps: []string{"D"}, 487 }) 488 489 ctx.CreateModule(newBarModule, "new_bar", &props{ 490 Name: "C", 491 Deps: []string{"D"}, 492 }) 493 494 ctx.CreateModule(newFooModule, "new_foo", &props{ 495 Name: "D", 496 }) 497} 498 499func TestWalkFileOrder(t *testing.T) { 500 // Run the test once to see how long it normally takes 501 start := time.Now() 502 doTestWalkFileOrder(t, time.Duration(0)) 503 duration := time.Since(start) 504 505 // Run the test again, but put enough of a sleep into each visitor to detect ordering 506 // problems if they exist 507 doTestWalkFileOrder(t, duration) 508} 509 510// test that WalkBlueprintsFiles calls asyncVisitor in the right order 511func doTestWalkFileOrder(t *testing.T, sleepDuration time.Duration) { 512 // setup mock context 513 ctx := newContext() 514 mockFiles := map[string][]byte{ 515 "Android.bp": []byte(` 516 sample_module { 517 name: "a", 518 } 519 `), 520 "dir1/Android.bp": []byte(` 521 sample_module { 522 name: "b", 523 } 524 `), 525 "dir1/dir2/Android.bp": []byte(` 526 sample_module { 527 name: "c", 528 } 529 `), 530 } 531 ctx.MockFileSystem(mockFiles) 532 533 // prepare to monitor the visit order 534 visitOrder := []string{} 535 visitLock := sync.Mutex{} 536 correctVisitOrder := []string{"Android.bp", "dir1/Android.bp", "dir1/dir2/Android.bp"} 537 538 // sleep longer when processing the earlier files 539 chooseSleepDuration := func(fileName string) (duration time.Duration) { 540 duration = time.Duration(0) 541 for i := len(correctVisitOrder) - 1; i >= 0; i-- { 542 if fileName == correctVisitOrder[i] { 543 return duration 544 } 545 duration = duration + sleepDuration 546 } 547 panic("unrecognized file name " + fileName) 548 } 549 550 visitor := func(file *parser.File) { 551 time.Sleep(chooseSleepDuration(file.Name)) 552 visitLock.Lock() 553 defer visitLock.Unlock() 554 visitOrder = append(visitOrder, file.Name) 555 } 556 keys := []string{"Android.bp", "dir1/Android.bp", "dir1/dir2/Android.bp"} 557 558 // visit the blueprints files 559 ctx.WalkBlueprintsFiles(".", keys, visitor) 560 561 // check the order 562 if !reflect.DeepEqual(visitOrder, correctVisitOrder) { 563 t.Errorf("Incorrect visit order; expected %v, got %v", correctVisitOrder, visitOrder) 564 } 565} 566 567// test that WalkBlueprintsFiles reports syntax errors 568func TestWalkingWithSyntaxError(t *testing.T) { 569 // setup mock context 570 ctx := newContext() 571 mockFiles := map[string][]byte{ 572 "Android.bp": []byte(` 573 sample_module { 574 name: "a" "b", 575 } 576 `), 577 "dir1/Android.bp": []byte(` 578 sample_module { 579 name: "b", 580 `), 581 "dir1/dir2/Android.bp": []byte(` 582 sample_module { 583 name: "c", 584 } 585 `), 586 } 587 ctx.MockFileSystem(mockFiles) 588 589 keys := []string{"Android.bp", "dir1/Android.bp", "dir1/dir2/Android.bp"} 590 591 // visit the blueprints files 592 _, errs := ctx.WalkBlueprintsFiles(".", keys, func(file *parser.File) {}) 593 594 expectedErrs := []error{ 595 errors.New(`Android.bp:3:18: expected "}", found String`), 596 errors.New(`dir1/Android.bp:4:3: expected "}", found EOF`), 597 } 598 if fmt.Sprintf("%s", expectedErrs) != fmt.Sprintf("%s", errs) { 599 t.Errorf("Incorrect errors; expected:\n%s\ngot:\n%s", expectedErrs, errs) 600 } 601 602} 603 604func TestParseFailsForModuleWithoutName(t *testing.T) { 605 ctx := NewContext() 606 ctx.MockFileSystem(map[string][]byte{ 607 "Android.bp": []byte(` 608 foo_module { 609 name: "A", 610 } 611 612 bar_module { 613 deps: ["A"], 614 } 615 `), 616 }) 617 ctx.RegisterModuleType("foo_module", newFooModule) 618 ctx.RegisterModuleType("bar_module", newBarModule) 619 620 _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) 621 622 expectedErrs := []error{ 623 errors.New(`Android.bp:6:4: property 'name' is missing from a module`), 624 } 625 if fmt.Sprintf("%s", expectedErrs) != fmt.Sprintf("%s", errs) { 626 t.Errorf("Incorrect errors; expected:\n%s\ngot:\n%s", expectedErrs, errs) 627 } 628} 629 630func Test_findVariant(t *testing.T) { 631 module := &moduleInfo{ 632 variant: variant{ 633 name: "normal_local", 634 variations: variationMap{ 635 map[string]string{ 636 "normal": "normal", 637 }, 638 }, 639 }, 640 } 641 642 type alias struct { 643 variant variant 644 target int 645 } 646 647 makeDependencyGroup := func(in ...*moduleInfo) *moduleGroup { 648 group := &moduleGroup{ 649 name: "dep", 650 } 651 for _, m := range in { 652 m.group = group 653 group.modules = append(group.modules, m) 654 } 655 return group 656 } 657 658 tests := []struct { 659 name string 660 possibleDeps *moduleGroup 661 variations []Variation 662 far bool 663 reverse bool 664 want string 665 }{ 666 { 667 name: "AddVariationDependencies(nil)", 668 // A dependency that matches the non-local variations of the module 669 possibleDeps: makeDependencyGroup( 670 &moduleInfo{ 671 variant: variant{ 672 name: "normal", 673 variations: variationMap{ 674 map[string]string{ 675 "normal": "normal", 676 }, 677 }, 678 }, 679 }, 680 ), 681 variations: nil, 682 far: false, 683 reverse: false, 684 want: "normal", 685 }, 686 { 687 name: "AddVariationDependencies(a)", 688 // A dependency with local variations 689 possibleDeps: makeDependencyGroup( 690 &moduleInfo{ 691 variant: variant{ 692 name: "normal_a", 693 variations: variationMap{ 694 map[string]string{ 695 "normal": "normal", 696 "a": "a", 697 }, 698 }, 699 }, 700 }, 701 ), 702 variations: []Variation{{"a", "a"}}, 703 far: false, 704 reverse: false, 705 want: "normal_a", 706 }, 707 { 708 name: "AddFarVariationDependencies(far)", 709 // A dependency with far variations 710 possibleDeps: makeDependencyGroup( 711 &moduleInfo{ 712 variant: variant{ 713 name: "", 714 variations: variationMap{}, 715 }, 716 }, 717 &moduleInfo{ 718 variant: variant{ 719 name: "far", 720 variations: variationMap{ 721 map[string]string{ 722 "far": "far", 723 }, 724 }, 725 }, 726 }, 727 ), 728 variations: []Variation{{"far", "far"}}, 729 far: true, 730 reverse: false, 731 want: "far", 732 }, 733 } 734 for _, tt := range tests { 735 t.Run(tt.name, func(t *testing.T) { 736 ctx := NewContext() 737 got, _, errs := ctx.findVariant(module, nil, tt.possibleDeps, tt.variations, tt.far, tt.reverse) 738 if errs != nil { 739 t.Fatal(errs) 740 } 741 if g, w := got == nil, tt.want == "nil"; g != w { 742 t.Fatalf("findVariant() got = %v, want %v", got, tt.want) 743 } 744 if got != nil { 745 if g, w := got.String(), fmt.Sprintf("module %q variant %q", "dep", tt.want); g != w { 746 t.Errorf("findVariant() got = %v, want %v", g, w) 747 } 748 } 749 }) 750 } 751} 752 753func Test_parallelVisit(t *testing.T) { 754 addDep := func(from, to *moduleInfo) { 755 from.directDeps = append(from.directDeps, depInfo{to, nil}) 756 from.forwardDeps = append(from.forwardDeps, to) 757 to.reverseDeps = append(to.reverseDeps, from) 758 } 759 760 create := func(name string) *moduleInfo { 761 m := &moduleInfo{ 762 group: &moduleGroup{ 763 name: name, 764 }, 765 } 766 m.group.modules = moduleList{m} 767 return m 768 } 769 moduleA := create("A") 770 moduleB := create("B") 771 moduleC := create("C") 772 moduleD := create("D") 773 moduleE := create("E") 774 moduleF := create("F") 775 moduleG := create("G") 776 777 // A depends on B, B depends on C. Nothing depends on D through G, and they don't depend on 778 // anything. 779 addDep(moduleA, moduleB) 780 addDep(moduleB, moduleC) 781 782 t.Run("no modules", func(t *testing.T) { 783 errs := parallelVisit(slices.Values([]*moduleInfo(nil)), bottomUpVisitorImpl{}, 1, 784 func(module *moduleInfo, pause chan<- pauseSpec) bool { 785 panic("unexpected call to visitor") 786 }) 787 if errs != nil { 788 t.Errorf("expected no errors, got %q", errs) 789 } 790 }) 791 t.Run("bottom up", func(t *testing.T) { 792 order := "" 793 errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC}), bottomUpVisitorImpl{}, 1, 794 func(module *moduleInfo, pause chan<- pauseSpec) bool { 795 order += module.group.name 796 return false 797 }) 798 if errs != nil { 799 t.Errorf("expected no errors, got %q", errs) 800 } 801 if g, w := order, "CBA"; g != w { 802 t.Errorf("expected order %q, got %q", w, g) 803 } 804 }) 805 t.Run("pause", func(t *testing.T) { 806 order := "" 807 errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC, moduleD}), bottomUpVisitorImpl{}, 1, 808 func(module *moduleInfo, pause chan<- pauseSpec) bool { 809 if module == moduleC { 810 // Pause module C on module D 811 unpause := make(chan struct{}) 812 pause <- pauseSpec{moduleC, moduleD, unpause} 813 <-unpause 814 } 815 order += module.group.name 816 return false 817 }) 818 if errs != nil { 819 t.Errorf("expected no errors, got %q", errs) 820 } 821 if g, w := order, "DCBA"; g != w { 822 t.Errorf("expected order %q, got %q", w, g) 823 } 824 }) 825 t.Run("cancel", func(t *testing.T) { 826 order := "" 827 errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC}), bottomUpVisitorImpl{}, 1, 828 func(module *moduleInfo, pause chan<- pauseSpec) bool { 829 order += module.group.name 830 // Cancel in module B 831 return module == moduleB 832 }) 833 if errs != nil { 834 t.Errorf("expected no errors, got %q", errs) 835 } 836 if g, w := order, "CB"; g != w { 837 t.Errorf("expected order %q, got %q", w, g) 838 } 839 }) 840 t.Run("pause and cancel", func(t *testing.T) { 841 order := "" 842 errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC, moduleD}), bottomUpVisitorImpl{}, 1, 843 func(module *moduleInfo, pause chan<- pauseSpec) bool { 844 if module == moduleC { 845 // Pause module C on module D 846 unpause := make(chan struct{}) 847 pause <- pauseSpec{moduleC, moduleD, unpause} 848 <-unpause 849 } 850 order += module.group.name 851 // Cancel in module D 852 return module == moduleD 853 }) 854 if errs != nil { 855 t.Errorf("expected no errors, got %q", errs) 856 } 857 if g, w := order, "D"; g != w { 858 t.Errorf("expected order %q, got %q", w, g) 859 } 860 }) 861 t.Run("parallel", func(t *testing.T) { 862 order := "" 863 errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC}), bottomUpVisitorImpl{}, 3, 864 func(module *moduleInfo, pause chan<- pauseSpec) bool { 865 order += module.group.name 866 return false 867 }) 868 if errs != nil { 869 t.Errorf("expected no errors, got %q", errs) 870 } 871 if g, w := order, "CBA"; g != w { 872 t.Errorf("expected order %q, got %q", w, g) 873 } 874 }) 875 t.Run("pause existing", func(t *testing.T) { 876 order := "" 877 errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC}), bottomUpVisitorImpl{}, 3, 878 func(module *moduleInfo, pause chan<- pauseSpec) bool { 879 if module == moduleA { 880 // Pause module A on module B (an existing dependency) 881 unpause := make(chan struct{}) 882 pause <- pauseSpec{moduleA, moduleB, unpause} 883 <-unpause 884 } 885 order += module.group.name 886 return false 887 }) 888 if errs != nil { 889 t.Errorf("expected no errors, got %q", errs) 890 } 891 if g, w := order, "CBA"; g != w { 892 t.Errorf("expected order %q, got %q", w, g) 893 } 894 }) 895 t.Run("cycle", func(t *testing.T) { 896 errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC}), bottomUpVisitorImpl{}, 3, 897 func(module *moduleInfo, pause chan<- pauseSpec) bool { 898 if module == moduleC { 899 // Pause module C on module A (a dependency cycle) 900 unpause := make(chan struct{}) 901 pause <- pauseSpec{moduleC, moduleA, unpause} 902 <-unpause 903 } 904 return false 905 }) 906 want := []string{ 907 `encountered dependency cycle`, 908 `module "C" depends on module "A"`, 909 `module "A" depends on module "B"`, 910 `module "B" depends on module "C"`, 911 } 912 for i := range want { 913 if len(errs) <= i { 914 t.Errorf("missing error %s", want[i]) 915 } else if !strings.Contains(errs[i].Error(), want[i]) { 916 t.Errorf("expected error %s, got %s", want[i], errs[i]) 917 } 918 } 919 if len(errs) > len(want) { 920 for _, err := range errs[len(want):] { 921 t.Errorf("unexpected error %s", err.Error()) 922 } 923 } 924 }) 925 t.Run("pause cycle", func(t *testing.T) { 926 errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC, moduleD}), bottomUpVisitorImpl{}, 3, 927 func(module *moduleInfo, pause chan<- pauseSpec) bool { 928 if module == moduleC { 929 // Pause module C on module D 930 unpause := make(chan struct{}) 931 pause <- pauseSpec{moduleC, moduleD, unpause} 932 <-unpause 933 } 934 if module == moduleD { 935 // Pause module D on module C (a pause cycle) 936 unpause := make(chan struct{}) 937 pause <- pauseSpec{moduleD, moduleC, unpause} 938 <-unpause 939 } 940 return false 941 }) 942 want := []string{ 943 `encountered dependency cycle`, 944 `module "D" depends on module "C"`, 945 `module "C" depends on module "D"`, 946 } 947 for i := range want { 948 if len(errs) <= i { 949 t.Errorf("missing error %s", want[i]) 950 } else if !strings.Contains(errs[i].Error(), want[i]) { 951 t.Errorf("expected error %s, got %s", want[i], errs[i]) 952 } 953 } 954 if len(errs) > len(want) { 955 for _, err := range errs[len(want):] { 956 t.Errorf("unexpected error %s", err.Error()) 957 } 958 } 959 }) 960 t.Run("pause cycle with deps", func(t *testing.T) { 961 pauseDeps := map[*moduleInfo]*moduleInfo{ 962 // F and G form a pause cycle 963 moduleF: moduleG, 964 moduleG: moduleF, 965 // D depends on E which depends on the pause cycle, making E the first alphabetical 966 // entry in pauseMap, which is not part of the cycle. 967 moduleD: moduleE, 968 moduleE: moduleF, 969 } 970 errs := parallelVisit(slices.Values([]*moduleInfo{moduleD, moduleE, moduleF, moduleG}), bottomUpVisitorImpl{}, 4, 971 func(module *moduleInfo, pause chan<- pauseSpec) bool { 972 if dep, ok := pauseDeps[module]; ok { 973 unpause := make(chan struct{}) 974 pause <- pauseSpec{module, dep, unpause} 975 <-unpause 976 } 977 return false 978 }) 979 want := []string{ 980 `encountered dependency cycle`, 981 `module "G" depends on module "F"`, 982 `module "F" depends on module "G"`, 983 } 984 for i := range want { 985 if len(errs) <= i { 986 t.Errorf("missing error %s", want[i]) 987 } else if !strings.Contains(errs[i].Error(), want[i]) { 988 t.Errorf("expected error %s, got %s", want[i], errs[i]) 989 } 990 } 991 if len(errs) > len(want) { 992 for _, err := range errs[len(want):] { 993 t.Errorf("unexpected error %s", err.Error()) 994 } 995 } 996 }) 997} 998 999func TestDeduplicateOrderOnlyDeps(t *testing.T) { 1000 b := func(output string, inputs []string, orderOnlyDeps []string) *buildDef { 1001 return &buildDef{ 1002 OutputStrings: []string{output}, 1003 InputStrings: inputs, 1004 OrderOnlyStrings: orderOnlyDeps, 1005 } 1006 } 1007 m := func(bs ...*buildDef) *moduleInfo { 1008 return &moduleInfo{actionDefs: localBuildActions{buildDefs: bs}} 1009 } 1010 type testcase struct { 1011 modules []*moduleInfo 1012 expectedPhonys []*buildDef 1013 conversions map[string][]string 1014 } 1015 fnvHash := func(s string) string { 1016 hash := fnv.New64a() 1017 hash.Write([]byte(s)) 1018 return strconv.FormatUint(hash.Sum64(), 16) 1019 } 1020 testCases := []testcase{{ 1021 modules: []*moduleInfo{ 1022 m(b("A", nil, []string{"d"})), 1023 m(b("B", nil, []string{"d"})), 1024 }, 1025 expectedPhonys: []*buildDef{ 1026 b("dedup-"+fnvHash("d"), []string{"d"}, nil), 1027 }, 1028 conversions: map[string][]string{ 1029 "A": []string{"dedup-" + fnvHash("d")}, 1030 "B": []string{"dedup-" + fnvHash("d")}, 1031 }, 1032 }, { 1033 modules: []*moduleInfo{ 1034 m(b("A", nil, []string{"a"})), 1035 m(b("B", nil, []string{"b"})), 1036 }, 1037 }, { 1038 modules: []*moduleInfo{ 1039 m(b("A", nil, []string{"a"})), 1040 m(b("B", nil, []string{"b"})), 1041 m(b("C", nil, []string{"a"})), 1042 }, 1043 expectedPhonys: []*buildDef{b("dedup-"+fnvHash("a"), []string{"a"}, nil)}, 1044 conversions: map[string][]string{ 1045 "A": []string{"dedup-" + fnvHash("a")}, 1046 "B": []string{"b"}, 1047 "C": []string{"dedup-" + fnvHash("a")}, 1048 }, 1049 }, { 1050 modules: []*moduleInfo{ 1051 m(b("A", nil, []string{"a", "b"}), 1052 b("B", nil, []string{"a", "b"})), 1053 m(b("C", nil, []string{"a", "c"}), 1054 b("D", nil, []string{"a", "c"})), 1055 }, 1056 expectedPhonys: []*buildDef{ 1057 b("dedup-"+fnvHash("ab"), []string{"a", "b"}, nil), 1058 b("dedup-"+fnvHash("ac"), []string{"a", "c"}, nil)}, 1059 conversions: map[string][]string{ 1060 "A": []string{"dedup-" + fnvHash("ab")}, 1061 "B": []string{"dedup-" + fnvHash("ab")}, 1062 "C": []string{"dedup-" + fnvHash("ac")}, 1063 "D": []string{"dedup-" + fnvHash("ac")}, 1064 }, 1065 }} 1066 for index, tc := range testCases { 1067 t.Run(fmt.Sprintf("TestCase-%d", index), func(t *testing.T) { 1068 ctx := NewContext() 1069 actualPhonys := ctx.deduplicateOrderOnlyDeps(tc.modules) 1070 if len(actualPhonys.variables) != 0 { 1071 t.Errorf("No variables expected but found %v", actualPhonys.variables) 1072 } 1073 if len(actualPhonys.rules) != 0 { 1074 t.Errorf("No rules expected but found %v", actualPhonys.rules) 1075 } 1076 if e, a := len(tc.expectedPhonys), len(actualPhonys.buildDefs); e != a { 1077 t.Errorf("Expected %d build statements but got %d", e, a) 1078 } 1079 for i := 0; i < len(tc.expectedPhonys); i++ { 1080 a := actualPhonys.buildDefs[i] 1081 e := tc.expectedPhonys[i] 1082 if !reflect.DeepEqual(e.Outputs, a.Outputs) { 1083 t.Errorf("phonys expected %v but actualPhonys %v", e.Outputs, a.Outputs) 1084 } 1085 if !reflect.DeepEqual(e.Inputs, a.Inputs) { 1086 t.Errorf("phonys expected %v but actualPhonys %v", e.Inputs, a.Inputs) 1087 } 1088 } 1089 find := func(k string) *buildDef { 1090 for _, m := range tc.modules { 1091 for _, b := range m.actionDefs.buildDefs { 1092 if reflect.DeepEqual(b.OutputStrings, []string{k}) { 1093 return b 1094 } 1095 } 1096 } 1097 return nil 1098 } 1099 for k, conversion := range tc.conversions { 1100 actual := find(k) 1101 if actual == nil { 1102 t.Errorf("Couldn't find %s", k) 1103 } 1104 if !reflect.DeepEqual(actual.OrderOnlyStrings, conversion) { 1105 t.Errorf("expected %s.OrderOnly = %v but got %v", k, conversion, actual.OrderOnly) 1106 } 1107 } 1108 }) 1109 } 1110} 1111 1112func TestSourceRootDirAllowed(t *testing.T) { 1113 type pathCase struct { 1114 path string 1115 decidingPrefix string 1116 allowed bool 1117 } 1118 testcases := []struct { 1119 desc string 1120 rootDirs []string 1121 pathCases []pathCase 1122 }{ 1123 { 1124 desc: "simple case", 1125 rootDirs: []string{ 1126 "a", 1127 "b/c/d", 1128 "-c", 1129 "-d/c/a", 1130 "c/some_single_file", 1131 }, 1132 pathCases: []pathCase{ 1133 { 1134 path: "a", 1135 decidingPrefix: "a", 1136 allowed: true, 1137 }, 1138 { 1139 path: "a/b/c", 1140 decidingPrefix: "a", 1141 allowed: true, 1142 }, 1143 { 1144 path: "b", 1145 decidingPrefix: "", 1146 allowed: true, 1147 }, 1148 { 1149 path: "b/c/d/a", 1150 decidingPrefix: "b/c/d", 1151 allowed: true, 1152 }, 1153 { 1154 path: "c", 1155 decidingPrefix: "c", 1156 allowed: false, 1157 }, 1158 { 1159 path: "c/a/b", 1160 decidingPrefix: "c", 1161 allowed: false, 1162 }, 1163 { 1164 path: "c/some_single_file", 1165 decidingPrefix: "c/some_single_file", 1166 allowed: true, 1167 }, 1168 { 1169 path: "d/c/a/abc", 1170 decidingPrefix: "d/c/a", 1171 allowed: false, 1172 }, 1173 }, 1174 }, 1175 { 1176 desc: "root directory order matters", 1177 rootDirs: []string{ 1178 "-a", 1179 "a/c/some_allowed_file", 1180 "a/b/d/some_allowed_file", 1181 "a/b", 1182 "a/c", 1183 "-a/b/d", 1184 }, 1185 pathCases: []pathCase{ 1186 { 1187 path: "a", 1188 decidingPrefix: "a", 1189 allowed: false, 1190 }, 1191 { 1192 path: "a/some_disallowed_file", 1193 decidingPrefix: "a", 1194 allowed: false, 1195 }, 1196 { 1197 path: "a/c/some_allowed_file", 1198 decidingPrefix: "a/c/some_allowed_file", 1199 allowed: true, 1200 }, 1201 { 1202 path: "a/b/d/some_allowed_file", 1203 decidingPrefix: "a/b/d/some_allowed_file", 1204 allowed: true, 1205 }, 1206 { 1207 path: "a/b/c", 1208 decidingPrefix: "a/b", 1209 allowed: true, 1210 }, 1211 { 1212 path: "a/b/c/some_allowed_file", 1213 decidingPrefix: "a/b", 1214 allowed: true, 1215 }, 1216 { 1217 path: "a/b/d", 1218 decidingPrefix: "a/b/d", 1219 allowed: false, 1220 }, 1221 }, 1222 }, 1223 } 1224 for _, tc := range testcases { 1225 dirs := SourceRootDirs{} 1226 dirs.Add(tc.rootDirs...) 1227 for _, pc := range tc.pathCases { 1228 t.Run(fmt.Sprintf("%s: %s", tc.desc, pc.path), func(t *testing.T) { 1229 allowed, decidingPrefix := dirs.SourceRootDirAllowed(pc.path) 1230 if allowed != pc.allowed { 1231 if pc.allowed { 1232 t.Errorf("expected path %q to be allowed, but was not; root allowlist: %q", pc.path, tc.rootDirs) 1233 } else { 1234 t.Errorf("path %q was allowed unexpectedly; root allowlist: %q", pc.path, tc.rootDirs) 1235 } 1236 } 1237 if decidingPrefix != pc.decidingPrefix { 1238 t.Errorf("expected decidingPrefix to be %q, but got %q", pc.decidingPrefix, decidingPrefix) 1239 } 1240 }) 1241 } 1242 } 1243} 1244 1245func TestSourceRootDirs(t *testing.T) { 1246 root_foo_bp := ` 1247 foo_module { 1248 name: "foo", 1249 deps: ["foo_dir1", "foo_dir_ignored_special_case"], 1250 } 1251 ` 1252 dir1_foo_bp := ` 1253 foo_module { 1254 name: "foo_dir1", 1255 deps: ["foo_dir_ignored"], 1256 } 1257 ` 1258 dir_ignored_foo_bp := ` 1259 foo_module { 1260 name: "foo_dir_ignored", 1261 } 1262 ` 1263 dir_ignored_special_case_foo_bp := ` 1264 foo_module { 1265 name: "foo_dir_ignored_special_case", 1266 } 1267 ` 1268 mockFs := map[string][]byte{ 1269 "Android.bp": []byte(root_foo_bp), 1270 "dir1/Android.bp": []byte(dir1_foo_bp), 1271 "dir_ignored/Android.bp": []byte(dir_ignored_foo_bp), 1272 "dir_ignored/special_case/Android.bp": []byte(dir_ignored_special_case_foo_bp), 1273 } 1274 fileList := []string{} 1275 for f := range mockFs { 1276 fileList = append(fileList, f) 1277 } 1278 testCases := []struct { 1279 sourceRootDirs []string 1280 expectedModuleDefs []string 1281 unexpectedModuleDefs []string 1282 expectedErrs []string 1283 }{ 1284 { 1285 sourceRootDirs: []string{}, 1286 expectedModuleDefs: []string{ 1287 "foo", 1288 "foo_dir1", 1289 "foo_dir_ignored", 1290 "foo_dir_ignored_special_case", 1291 }, 1292 }, 1293 { 1294 sourceRootDirs: []string{"-", ""}, 1295 unexpectedModuleDefs: []string{ 1296 "foo", 1297 "foo_dir1", 1298 "foo_dir_ignored", 1299 "foo_dir_ignored_special_case", 1300 }, 1301 }, 1302 { 1303 sourceRootDirs: []string{"-"}, 1304 unexpectedModuleDefs: []string{ 1305 "foo", 1306 "foo_dir1", 1307 "foo_dir_ignored", 1308 "foo_dir_ignored_special_case", 1309 }, 1310 }, 1311 { 1312 sourceRootDirs: []string{"dir1"}, 1313 expectedModuleDefs: []string{ 1314 "foo", 1315 "foo_dir1", 1316 "foo_dir_ignored", 1317 "foo_dir_ignored_special_case", 1318 }, 1319 }, 1320 { 1321 sourceRootDirs: []string{"-dir1"}, 1322 expectedModuleDefs: []string{ 1323 "foo", 1324 "foo_dir_ignored", 1325 "foo_dir_ignored_special_case", 1326 }, 1327 unexpectedModuleDefs: []string{ 1328 "foo_dir1", 1329 }, 1330 expectedErrs: []string{ 1331 `Android.bp:2:2: module "foo" depends on skipped module "foo_dir1"; "foo_dir1" was defined in files(s) [dir1/Android.bp], but was skipped for reason(s) ["dir1/Android.bp" is a descendant of "dir1", and that path prefix was not included in PRODUCT_SOURCE_ROOT_DIRS]`, 1332 }, 1333 }, 1334 { 1335 sourceRootDirs: []string{"-", "dir1"}, 1336 expectedModuleDefs: []string{ 1337 "foo_dir1", 1338 }, 1339 unexpectedModuleDefs: []string{ 1340 "foo", 1341 "foo_dir_ignored", 1342 "foo_dir_ignored_special_case", 1343 }, 1344 expectedErrs: []string{ 1345 `dir1/Android.bp:2:2: module "foo_dir1" depends on skipped module "foo_dir_ignored"; "foo_dir_ignored" was defined in files(s) [dir_ignored/Android.bp], but was skipped for reason(s) ["dir_ignored/Android.bp" is a descendant of "", and that path prefix was not included in PRODUCT_SOURCE_ROOT_DIRS]`, 1346 }, 1347 }, 1348 { 1349 sourceRootDirs: []string{"-", "dir1", "dir_ignored/special_case/Android.bp"}, 1350 expectedModuleDefs: []string{ 1351 "foo_dir1", 1352 "foo_dir_ignored_special_case", 1353 }, 1354 unexpectedModuleDefs: []string{ 1355 "foo", 1356 "foo_dir_ignored", 1357 }, 1358 expectedErrs: []string{ 1359 "dir1/Android.bp:2:2: module \"foo_dir1\" depends on skipped module \"foo_dir_ignored\"; \"foo_dir_ignored\" was defined in files(s) [dir_ignored/Android.bp], but was skipped for reason(s) [\"dir_ignored/Android.bp\" is a descendant of \"\", and that path prefix was not included in PRODUCT_SOURCE_ROOT_DIRS]", 1360 }, 1361 }, 1362 } 1363 for _, tc := range testCases { 1364 t.Run(fmt.Sprintf(`source root dirs are %q`, tc.sourceRootDirs), func(t *testing.T) { 1365 ctx := NewContext() 1366 ctx.MockFileSystem(mockFs) 1367 ctx.RegisterModuleType("foo_module", newFooModule) 1368 ctx.RegisterBottomUpMutator("deps", depsMutator) 1369 ctx.AddSourceRootDirs(tc.sourceRootDirs...) 1370 ctx.ParseFileList(".", fileList, nil) 1371 _, actualErrs := ctx.ResolveDependencies(nil) 1372 1373 stringErrs := []string(nil) 1374 for _, err := range actualErrs { 1375 stringErrs = append(stringErrs, err.Error()) 1376 } 1377 if !reflect.DeepEqual(tc.expectedErrs, stringErrs) { 1378 t.Errorf("expected to find errors %v; got %v", tc.expectedErrs, stringErrs) 1379 } 1380 for _, modName := range tc.expectedModuleDefs { 1381 allMods := ctx.moduleGroupFromName(modName, nil) 1382 if allMods == nil || len(allMods.modules) != 1 { 1383 mods := moduleList{} 1384 if allMods != nil { 1385 mods = allMods.modules 1386 } 1387 t.Errorf("expected to find one definition for module %q, but got %v", modName, mods) 1388 } 1389 } 1390 1391 for _, modName := range tc.unexpectedModuleDefs { 1392 allMods := ctx.moduleGroupFromName(modName, nil) 1393 if allMods != nil { 1394 t.Errorf("expected to find no definitions for module %q, but got %v", modName, allMods.modules) 1395 } 1396 } 1397 }) 1398 } 1399} 1400 1401func incrementalSetup(t *testing.T) *Context { 1402 ctx := NewContext() 1403 fileSystem := map[string][]byte{ 1404 "Android.bp": []byte(` 1405 incremental_module { 1406 name: "MyIncrementalModule", 1407 deps: ["MyBarModule"], 1408 } 1409 1410 bar_module { 1411 name: "MyBarModule", 1412 } 1413 `), 1414 } 1415 ctx.MockFileSystem(fileSystem) 1416 ctx.RegisterBottomUpMutator("deps", depsMutator) 1417 ctx.RegisterModuleType("incremental_module", newIncrementalModule) 1418 ctx.RegisterModuleType("bar_module", newBarModule) 1419 1420 _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) 1421 if len(errs) > 0 { 1422 t.Errorf("unexpected parse errors:") 1423 for _, err := range errs { 1424 t.Errorf(" %s", err) 1425 } 1426 t.FailNow() 1427 } 1428 1429 _, errs = ctx.ResolveDependencies(nil) 1430 if len(errs) > 0 { 1431 t.Errorf("unexpected dep errors:") 1432 for _, err := range errs { 1433 t.Errorf(" %s", err) 1434 } 1435 t.FailNow() 1436 } 1437 1438 return ctx 1439} 1440 1441func incrementalSetupForRestore(t *testing.T, orderOnlyStrings []string) (*Context, any) { 1442 ctx := incrementalSetup(t) 1443 incInfo := ctx.moduleGroupFromName("MyIncrementalModule", nil).modules.firstModule() 1444 barInfo := ctx.moduleGroupFromName("MyBarModule", nil).modules.firstModule() 1445 1446 providerHashes := make([]uint64, len(providerRegistry)) 1447 // Use fixed value since SetProvider hasn't been called yet, so we can't go 1448 // through the providers of the module. 1449 for k, v := range map[providerKey]any{ 1450 IncrementalTestProviderKey.providerKey: IncrementalTestProvider{ 1451 Value: barInfo.Name(), 1452 }, 1453 } { 1454 hash, err := proptools.CalculateHash(v) 1455 if err != nil { 1456 panic(fmt.Sprintf("Can't hash value of providers")) 1457 } 1458 providerHashes[k.id] = hash 1459 } 1460 cacheKey := calculateHashKey(incInfo, [][]uint64{providerHashes}) 1461 var providerValue any = IncrementalTestProvider{Value: "MyIncrementalModule"} 1462 toCache := BuildActionCache{ 1463 cacheKey: &BuildActionCachedData{ 1464 Pos: &scanner.Position{ 1465 Filename: "Android.bp", 1466 Line: 2, 1467 Column: 4, 1468 Offset: 4, 1469 }, 1470 Providers: []CachedProvider{{ 1471 Id: &IncrementalTestProviderKey.providerKey, 1472 Value: &providerValue, 1473 }}, 1474 OrderOnlyStrings: orderOnlyStrings, 1475 }, 1476 } 1477 ctx.SetIncrementalEnabled(true) 1478 ctx.SetIncrementalAnalysis(true) 1479 ctx.buildActionsFromCache = toCache 1480 1481 return ctx, providerValue 1482} 1483 1484func calculateHashKey(m *moduleInfo, providerHashes [][]uint64) BuildActionCacheKey { 1485 hash, err := proptools.CalculateHash(m.properties) 1486 if err != nil { 1487 panic(newPanicErrorf(err, "failed to calculate properties hash")) 1488 } 1489 cacheInput := new(BuildActionCacheInput) 1490 cacheInput.PropertiesHash = hash 1491 cacheInput.ProvidersHash = providerHashes 1492 hash, err = proptools.CalculateHash(&cacheInput) 1493 if err != nil { 1494 panic(newPanicErrorf(err, "failed to calculate cache input hash")) 1495 } 1496 return BuildActionCacheKey{ 1497 Id: m.ModuleCacheKey(), 1498 InputHash: hash, 1499 } 1500} 1501 1502func TestCacheBuildActions(t *testing.T) { 1503 ctx := incrementalSetup(t) 1504 ctx.SetIncrementalEnabled(true) 1505 1506 _, errs := ctx.PrepareBuildActions(nil) 1507 if len(errs) > 0 { 1508 t.Errorf("unexpected errors calling generateModuleBuildActions:") 1509 for _, err := range errs { 1510 t.Errorf(" %s", err) 1511 } 1512 t.FailNow() 1513 } 1514 1515 incInfo := ctx.moduleGroupFromName("MyIncrementalModule", nil).modules.firstModule() 1516 barInfo := ctx.moduleGroupFromName("MyBarModule", nil).modules.firstModule() 1517 if len(ctx.buildActionsToCache) != 1 { 1518 t.Errorf("build actions are not cached for the incremental module") 1519 } 1520 cacheKey := calculateHashKey(incInfo, [][]uint64{barInfo.providerInitialValueHashes}) 1521 cache := ctx.buildActionsToCache[cacheKey] 1522 if cache == nil { 1523 t.Errorf("failed to find cached build actions for the incremental module") 1524 } 1525 var providerValue any = IncrementalTestProvider{Value: "MyIncrementalModule"} 1526 expectedCache := BuildActionCachedData{ 1527 Pos: &scanner.Position{ 1528 Filename: "Android.bp", 1529 Line: 2, 1530 Column: 4, 1531 Offset: 4, 1532 }, 1533 Providers: []CachedProvider{{ 1534 Id: &IncrementalTestProviderKey.providerKey, 1535 Value: &providerValue, 1536 }}, 1537 } 1538 if !reflect.DeepEqual(expectedCache, *cache) { 1539 t.Errorf("expected: %v actual %v", expectedCache, *cache) 1540 } 1541} 1542 1543func TestRestoreBuildActions(t *testing.T) { 1544 ctx, providerValue := incrementalSetupForRestore(t, nil) 1545 incInfo := ctx.moduleGroupFromName("MyIncrementalModule", nil).modules.firstModule() 1546 barInfo := ctx.moduleGroupFromName("MyBarModule", nil).modules.firstModule() 1547 _, errs := ctx.PrepareBuildActions(nil) 1548 if len(errs) > 0 { 1549 t.Errorf("unexpected errors calling generateModuleBuildActions:") 1550 for _, err := range errs { 1551 t.Errorf(" %s", err) 1552 } 1553 t.FailNow() 1554 } 1555 1556 // Verify that the GenerateBuildActions was skipped for the incremental module 1557 incRerun := incInfo.logicModule.(*incrementalModule).GenerateBuildActionsCalled 1558 barRerun := barInfo.logicModule.(*barModule).GenerateBuildActionsCalled 1559 if incRerun || !barRerun { 1560 t.Errorf("failed to skip/rerun GenerateBuildActions: %t %t", incRerun, barRerun) 1561 } 1562 // Verify that the provider is set correctly for the incremental module 1563 if !reflect.DeepEqual(incInfo.providers[IncrementalTestProviderKey.id], providerValue) { 1564 t.Errorf("provider is not set correctly when restoring from cache") 1565 } 1566} 1567 1568func TestSkipNinjaForCacheHit(t *testing.T) { 1569 ctx, _ := incrementalSetupForRestore(t, nil) 1570 _, errs := ctx.PrepareBuildActions(nil) 1571 if len(errs) > 0 { 1572 t.Errorf("unexpected errors calling generateModuleBuildActions:") 1573 for _, err := range errs { 1574 t.Errorf(" %s", err) 1575 } 1576 t.FailNow() 1577 } 1578 1579 buf := bytes.NewBuffer(nil) 1580 w := newNinjaWriter(buf) 1581 ctx.writeAllModuleActions(w, true, "test.ninja") 1582 // Verify that soong updated the ninja file for the bar module and skipped the 1583 // ninja file writing of the incremental module 1584 file, err := ctx.fs.Open("test.0.ninja") 1585 if err != nil { 1586 t.Errorf("no ninja file for MyBarModule") 1587 } 1588 content := make([]byte, 1024) 1589 file.Read(content) 1590 if !strings.Contains(string(content), "build MyBarModule_phony_output: phony") { 1591 t.Errorf("ninja file doesn't have build statements for MyBarModule: %s", string(content)) 1592 } 1593 1594 file, err = ctx.fs.Open("test_incremental_ninja/.-MyIncrementalModule-none-incremental_module.ninja") 1595 if !os.IsNotExist(err) { 1596 t.Errorf("shouldn't generate ninja file for MyIncrementalModule: %s", err.Error()) 1597 } 1598} 1599 1600func TestNotSkipNinjaForCacheMiss(t *testing.T) { 1601 ctx := incrementalSetup(t) 1602 ctx.SetIncrementalEnabled(true) 1603 ctx.SetIncrementalAnalysis(true) 1604 _, errs := ctx.PrepareBuildActions(nil) 1605 if len(errs) > 0 { 1606 t.Errorf("unexpected errors calling generateModuleBuildActions:") 1607 for _, err := range errs { 1608 t.Errorf(" %s", err) 1609 } 1610 t.FailNow() 1611 } 1612 1613 buf := bytes.NewBuffer(nil) 1614 w := newNinjaWriter(buf) 1615 ctx.writeAllModuleActions(w, true, "test.ninja") 1616 // Verify that soong updated the ninja files for both the bar module and the 1617 // incremental module 1618 file, err := ctx.fs.Open("test.0.ninja") 1619 if err != nil { 1620 t.Errorf("no ninja file for MyBarModule") 1621 } 1622 content := make([]byte, 1024) 1623 file.Read(content) 1624 if !strings.Contains(string(content), "build MyBarModule_phony_output: phony") { 1625 t.Errorf("ninja file doesn't have build statements for MyBarModule: %s", string(content)) 1626 } 1627 1628 file, err = ctx.fs.Open("test_incremental_ninja/.-MyIncrementalModule-none-incremental_module.ninja") 1629 if err != nil { 1630 t.Errorf("no ninja file for MyIncrementalModule") 1631 } 1632 file.Read(content) 1633 if !strings.Contains(string(content), "build MyIncrementalModule_phony_output: phony") { 1634 t.Errorf("ninja file doesn't have build statements for MyIncrementalModule: %s", string(content)) 1635 } 1636} 1637 1638func TestOrderOnlyStringsCaching(t *testing.T) { 1639 ctx := incrementalSetup(t) 1640 ctx.SetIncrementalEnabled(true) 1641 _, errs := ctx.PrepareBuildActions(nil) 1642 if len(errs) > 0 { 1643 t.Errorf("unexpected errors calling generateModuleBuildActions:") 1644 for _, err := range errs { 1645 t.Errorf(" %s", err) 1646 } 1647 t.FailNow() 1648 } 1649 incInfo := ctx.moduleGroupFromName("MyIncrementalModule", nil).modules.firstModule() 1650 barInfo := ctx.moduleGroupFromName("MyBarModule", nil).modules.firstModule() 1651 bDef := buildDef{ 1652 Rule: Phony, 1653 OrderOnlyStrings: []string{"test.lib"}, 1654 } 1655 incInfo.actionDefs.buildDefs = append(incInfo.actionDefs.buildDefs, &bDef) 1656 barInfo.actionDefs.buildDefs = append(barInfo.actionDefs.buildDefs, &bDef) 1657 1658 buf := bytes.NewBuffer(nil) 1659 w := newNinjaWriter(buf) 1660 ctx.writeAllModuleActions(w, true, "test.ninja") 1661 1662 verifyOrderOnlyStringsCache(t, ctx, incInfo, barInfo) 1663} 1664 1665func TestOrderOnlyStringsRestoring(t *testing.T) { 1666 phony := "dedup-d479e9a8133ff998" 1667 orderOnlyStrings := []string{phony} 1668 ctx, _ := incrementalSetupForRestore(t, orderOnlyStrings) 1669 ctx.orderOnlyStringsFromCache = make(OrderOnlyStringsCache) 1670 ctx.orderOnlyStringsFromCache[phony] = []string{"test.lib"} 1671 _, errs := ctx.PrepareBuildActions(nil) 1672 if len(errs) > 0 { 1673 t.Errorf("unexpected errors calling generateModuleBuildActions:") 1674 for _, err := range errs { 1675 t.Errorf(" %s", err) 1676 } 1677 t.FailNow() 1678 } 1679 1680 buf := bytes.NewBuffer(nil) 1681 w := newNinjaWriter(buf) 1682 ctx.writeAllModuleActions(w, true, "test.ninja") 1683 1684 incInfo := ctx.moduleGroupFromName("MyIncrementalModule", nil).modules.firstModule() 1685 barInfo := ctx.moduleGroupFromName("MyBarModule", nil).modules.firstModule() 1686 verifyOrderOnlyStringsCache(t, ctx, incInfo, barInfo) 1687 1688 // Verify dedup-d479e9a8133ff998 is still written to the common ninja file even 1689 // though MyBarModule no longer uses it. 1690 expected := strings.Join([]string{"build", phony + ":", "phony", "test.lib"}, " ") 1691 if !strings.Contains(buf.String(), expected) { 1692 t.Errorf("phony target not found: %s", buf.String()) 1693 } 1694} 1695 1696func verifyOrderOnlyStringsCache(t *testing.T, ctx *Context, incInfo, barInfo *moduleInfo) { 1697 // Verify that soong cache all the order only strings that are used by the 1698 // incremental modules 1699 ok, key := mapContainsValue(ctx.orderOnlyStringsToCache, "test.lib") 1700 if !ok { 1701 t.Errorf("no order only strings used by incremetnal modules cached: %v", ctx.orderOnlyStringsToCache) 1702 } 1703 1704 // Verify that the dedup-* order only strings used by MyIncrementalModule is 1705 // cached along with its other cached values 1706 cacheKey := calculateHashKey(incInfo, [][]uint64{barInfo.providerInitialValueHashes}) 1707 cache := ctx.buildActionsToCache[cacheKey] 1708 if cache == nil { 1709 t.Errorf("failed to find cached build actions for the incremental module") 1710 } 1711 if !listContainsValue(cache.OrderOnlyStrings, key) { 1712 t.Errorf("no order only strings cached for MyIncrementalModule: %v", cache.OrderOnlyStrings) 1713 } 1714} 1715 1716func listContainsValue[K comparable](l []K, target K) bool { 1717 for _, value := range l { 1718 if value == target { 1719 return true 1720 } 1721 } 1722 return false 1723} 1724 1725func mapContainsValue[K comparable, V comparable](m map[K][]V, target V) (bool, K) { 1726 for k, v := range m { 1727 if listContainsValue(v, target) { 1728 return true, k 1729 } 1730 } 1731 var key K 1732 return false, key 1733} 1734 1735func TestDisallowedMutatorMethods(t *testing.T) { 1736 testCases := []struct { 1737 name string 1738 mutatorHandleFunc func(MutatorHandle) 1739 mutatorFunc func(BottomUpMutatorContext) 1740 expectedPanic string 1741 }{ 1742 { 1743 name: "rename", 1744 mutatorHandleFunc: func(handle MutatorHandle) { handle.UsesRename() }, 1745 mutatorFunc: func(ctx BottomUpMutatorContext) { ctx.Rename("qux") }, 1746 expectedPanic: "method Rename called from mutator that was not marked UsesRename", 1747 }, 1748 { 1749 name: "replace_dependencies", 1750 mutatorHandleFunc: func(handle MutatorHandle) { handle.UsesReplaceDependencies() }, 1751 mutatorFunc: func(ctx BottomUpMutatorContext) { ctx.ReplaceDependencies("bar") }, 1752 expectedPanic: "method ReplaceDependenciesIf called from mutator that was not marked UsesReplaceDependencies", 1753 }, 1754 { 1755 name: "replace_dependencies_if", 1756 mutatorHandleFunc: func(handle MutatorHandle) { handle.UsesReplaceDependencies() }, 1757 mutatorFunc: func(ctx BottomUpMutatorContext) { 1758 ctx.ReplaceDependenciesIf("bar", func(from Module, tag DependencyTag, to Module) bool { return false }) 1759 }, 1760 expectedPanic: "method ReplaceDependenciesIf called from mutator that was not marked UsesReplaceDependencies", 1761 }, 1762 { 1763 name: "reverse_dependencies", 1764 mutatorHandleFunc: func(handle MutatorHandle) { handle.UsesReverseDependencies() }, 1765 mutatorFunc: func(ctx BottomUpMutatorContext) { ctx.AddReverseDependency(ctx.Module(), nil, "baz") }, 1766 expectedPanic: "method AddReverseDependency called from mutator that was not marked UsesReverseDependencies", 1767 }, 1768 { 1769 name: "create_module", 1770 mutatorHandleFunc: func(handle MutatorHandle) { handle.UsesCreateModule() }, 1771 mutatorFunc: func(ctx BottomUpMutatorContext) { 1772 ctx.CreateModule(newFooModule, "create_module", 1773 &struct{ Name string }{Name: "quz"}) 1774 }, 1775 expectedPanic: "method CreateModule called from mutator that was not marked UsesCreateModule", 1776 }, 1777 } 1778 1779 runTest := func(mutatorHandleFunc func(MutatorHandle), mutatorFunc func(ctx BottomUpMutatorContext), expectedPanic string) { 1780 ctx := NewContext() 1781 1782 ctx.MockFileSystem(map[string][]byte{ 1783 "Android.bp": []byte(` 1784 foo_module { 1785 name: "foo", 1786 } 1787 1788 foo_module { 1789 name: "bar", 1790 deps: ["foo"], 1791 } 1792 1793 foo_module { 1794 name: "baz", 1795 } 1796 `)}) 1797 1798 ctx.RegisterModuleType("foo_module", newFooModule) 1799 ctx.RegisterBottomUpMutator("deps", depsMutator) 1800 handle := ctx.RegisterBottomUpMutator("mutator", func(ctx BottomUpMutatorContext) { 1801 if ctx.ModuleName() == "foo" { 1802 mutatorFunc(ctx) 1803 } 1804 }) 1805 mutatorHandleFunc(handle) 1806 1807 _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) 1808 if len(errs) > 0 { 1809 t.Errorf("unexpected parse errors:") 1810 for _, err := range errs { 1811 t.Errorf(" %s", err) 1812 } 1813 t.FailNow() 1814 } 1815 1816 _, errs = ctx.ResolveDependencies(nil) 1817 if expectedPanic != "" { 1818 if len(errs) == 0 { 1819 t.Errorf("missing expected error %q", expectedPanic) 1820 } else if !strings.Contains(errs[0].Error(), expectedPanic) { 1821 t.Errorf("missing expected error %q in %q", expectedPanic, errs[0].Error()) 1822 } 1823 } else if len(errs) > 0 { 1824 t.Errorf("unexpected dep errors:") 1825 for _, err := range errs { 1826 t.Errorf(" %s", err) 1827 } 1828 t.FailNow() 1829 } 1830 } 1831 1832 noopMutatorHandleFunc := func(MutatorHandle) {} 1833 1834 for _, testCase := range testCases { 1835 t.Run(testCase.name, func(t *testing.T) { 1836 t.Run("allowed", func(t *testing.T) { 1837 // Test that the method doesn't panic when the handle function is called. 1838 runTest(testCase.mutatorHandleFunc, testCase.mutatorFunc, "") 1839 }) 1840 t.Run("disallowed", func(t *testing.T) { 1841 // Test that the method does panic with the expected error when the 1842 // handle function is not called. 1843 runTest(noopMutatorHandleFunc, testCase.mutatorFunc, testCase.expectedPanic) 1844 }) 1845 }) 1846 } 1847 1848} 1849