From d7474dd743a284d37756c2b5c5716cf2e0d81f07 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 29 Mar 2024 12:25:29 -0700 Subject: [PATCH] Call TransitionMutator.IncomingTransition when adding dependencies later Adding a dependency on a module that has already had a TransitionMutator run on it may require adjusting the variation name based on the results of IncomingTranstion. Store the variants that existed before the TransitionMutator ran, find one that is a subset of the requested variant, and call TranstionMutator.IncomingTransition to update the value. Bug: 319288033 Test: TestPostTransitionDeps Change-Id: I690357f9792401a3edbc5ae9fdcb666495954fbc --- context.go | 92 ++++++++++++--- context_test.go | 3 +- module_ctx.go | 14 +-- transition.go | 7 +- transition_test.go | 277 ++++++++++++++++++++++++++++++--------------- 5 files changed, 279 insertions(+), 114 deletions(-) diff --git a/context.go b/context.go index 0dbd223..738e4c3 100644 --- a/context.go +++ b/context.go @@ -91,6 +91,8 @@ type Context struct { mutatorInfo []*mutatorInfo variantMutatorNames []string + transitionMutators []*transitionMutatorImpl + depsModified uint32 // positive if a mutator modified the dependencies dependenciesReady bool // set to true on a successful ResolveDependencies @@ -455,10 +457,11 @@ type singletonInfo struct { type mutatorInfo struct { // set during RegisterMutator - topDownMutator TopDownMutator - bottomUpMutator BottomUpMutator - name string - parallel bool + topDownMutator TopDownMutator + bottomUpMutator BottomUpMutator + name string + parallel bool + transitionMutator *transitionMutatorImpl } func newContext() *Context { @@ -686,6 +689,8 @@ type MutatorHandle interface { // method on the mutator context is thread-safe, but the mutator must handle synchronization // for any modifications to global state or any modules outside the one it was invoked on. Parallel() MutatorHandle + + setTransitionMutator(impl *transitionMutatorImpl) MutatorHandle } func (mutator *mutatorInfo) Parallel() MutatorHandle { @@ -693,6 +698,11 @@ func (mutator *mutatorInfo) Parallel() MutatorHandle { return mutator } +func (mutator *mutatorInfo) setTransitionMutator(impl *transitionMutatorImpl) MutatorHandle { + mutator.transitionMutator = impl + return mutator +} + // SetIgnoreUnknownModuleTypes sets the behavior of the context in the case // where it encounters an unknown module type while parsing Blueprints files. By // default, the context will report unknown module types as an error. If this @@ -1489,7 +1499,7 @@ func (c *Context) createVariations(origModule *moduleInfo, mutator *mutatorInfo, var newLogicModule Module var newProperties []interface{} - if i == 0 { + if i == 0 && mutator.transitionMutator == nil { // Reuse the existing module for the first new variant // This both saves creating a new module, and causes the insertion in c.moduleInfo below // with logicModule as the key to replace the original entry in c.moduleInfo @@ -1736,6 +1746,8 @@ func (c *Context) resolveDependencies(ctx context.Context, config interface{}) ( } defer c.EndEvent("clone_modules") + c.clearTransitionMutatorInputVariants() + c.dependenciesReady = true }) @@ -1772,8 +1784,8 @@ func blueprintDepsMutator(ctx BottomUpMutatorContext) { // findExactVariantOrSingle searches the moduleGroup for a module with the same variant as module, // and returns the matching module, or nil if one is not found. A group with exactly one module // is always considered matching. -func findExactVariantOrSingle(module *moduleInfo, possible *moduleGroup, reverse bool) *moduleInfo { - found, _ := findVariant(module, possible, nil, false, reverse) +func (c *Context) findExactVariantOrSingle(module *moduleInfo, config any, possible *moduleGroup, reverse bool) *moduleInfo { + found, _ := c.findVariant(module, config, possible, nil, false, reverse) if found == nil { for _, moduleOrAlias := range possible.modules { if m := moduleOrAlias.module(); m != nil { @@ -1788,7 +1800,7 @@ func findExactVariantOrSingle(module *moduleInfo, possible *moduleGroup, reverse return found } -func (c *Context) addDependency(module *moduleInfo, tag DependencyTag, depName string) (*moduleInfo, []error) { +func (c *Context) addDependency(module *moduleInfo, config any, tag DependencyTag, depName string) (*moduleInfo, []error) { if _, ok := tag.(BaseDependencyTag); ok { panic("BaseDependencyTag is not allowed to be used directly!") } @@ -1805,7 +1817,7 @@ func (c *Context) addDependency(module *moduleInfo, tag DependencyTag, depName s return nil, c.discoveredMissingDependencies(module, depName, nil) } - if m := findExactVariantOrSingle(module, possibleDeps, false); m != nil { + if m := c.findExactVariantOrSingle(module, config, possibleDeps, false); m != nil { module.newDirectDeps = append(module.newDirectDeps, depInfo{m, tag}) atomic.AddUint32(&c.depsModified, 1) return m, nil @@ -1825,7 +1837,7 @@ func (c *Context) addDependency(module *moduleInfo, tag DependencyTag, depName s }} } -func (c *Context) findReverseDependency(module *moduleInfo, destName string) (*moduleInfo, []error) { +func (c *Context) findReverseDependency(module *moduleInfo, config any, destName string) (*moduleInfo, []error) { if destName == module.Name() { return nil, []error{&BlueprintError{ Err: fmt.Errorf("%q depends on itself", destName), @@ -1842,7 +1854,7 @@ func (c *Context) findReverseDependency(module *moduleInfo, destName string) (*m }} } - if m := findExactVariantOrSingle(module, possibleDeps, true); m != nil { + if m := c.findExactVariantOrSingle(module, config, possibleDeps, true); m != nil { return m, nil } @@ -1860,7 +1872,33 @@ func (c *Context) findReverseDependency(module *moduleInfo, destName string) (*m }} } -func findVariant(module *moduleInfo, possibleDeps *moduleGroup, variations []Variation, far bool, reverse bool) (*moduleInfo, variationMap) { +// applyIncomingTransitions takes a variationMap being used to add a dependency on a module in a moduleGroup +// and applies the IncomingTransition method of each completed TransitionMutator to modify the requested variation. +// It finds a variant that existed before the TransitionMutator ran that is a subset of the requested variant to +// use as the module context for IncomingTransition. +func (c *Context) applyIncomingTransitions(config any, group *moduleGroup, variant variationMap) { + for _, transitionMutator := range c.transitionMutators { + for _, inputVariant := range transitionMutator.inputVariants[group] { + if inputVariant.variant.variations.subsetOf(variant) { + sourceVariation := variant[transitionMutator.name] + + ctx := &incomingTransitionContextImpl{ + transitionContextImpl{context: c, source: nil, dep: inputVariant, + depTag: nil, config: config}, + } + + outgoingVariation := transitionMutator.mutator.IncomingTransition(ctx, sourceVariation) + variant[transitionMutator.name] = outgoingVariation + break + } + } + } + +} + +func (c *Context) findVariant(module *moduleInfo, config any, + possibleDeps *moduleGroup, variations []Variation, far bool, reverse bool) (*moduleInfo, variationMap) { + // We can't just append variant.Variant to module.dependencyVariant.variantName and // compare the strings because the result won't be in mutator registration order. // Create a new map instead, and then deep compare the maps. @@ -1882,6 +1920,8 @@ func findVariant(module *moduleInfo, possibleDeps *moduleGroup, variations []Var newVariant[v.Mutator] = v.Variation } + c.applyIncomingTransitions(config, possibleDeps, newVariant) + check := func(variant variationMap) bool { if far { return newVariant.subsetOf(variant) @@ -1901,7 +1941,7 @@ func findVariant(module *moduleInfo, possibleDeps *moduleGroup, variations []Var return foundDep, newVariant } -func (c *Context) addVariationDependency(module *moduleInfo, variations []Variation, +func (c *Context) addVariationDependency(module *moduleInfo, config any, variations []Variation, tag DependencyTag, depName string, far bool) (*moduleInfo, []error) { if _, ok := tag.(BaseDependencyTag); ok { panic("BaseDependencyTag is not allowed to be used directly!") @@ -1912,7 +1952,7 @@ func (c *Context) addVariationDependency(module *moduleInfo, variations []Variat return nil, c.discoveredMissingDependencies(module, depName, nil) } - foundDep, newVariant := findVariant(module, possibleDeps, variations, far, false) + foundDep, newVariant := c.findVariant(module, config, possibleDeps, variations, far, false) if foundDep == nil { if c.allowMissingDependencies { @@ -2936,6 +2976,13 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo, c.moduleInfo = newModuleInfo + isTransitionMutator := mutator.transitionMutator != nil + + var transitionMutatorInputVariants map[*moduleGroup][]*moduleInfo + if isTransitionMutator { + transitionMutatorInputVariants = make(map[*moduleGroup][]*moduleInfo) + } + for _, group := range c.moduleGroups { for i := 0; i < len(group.modules); i++ { module := group.modules[i].module() @@ -2946,6 +2993,10 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo, // Update module group to contain newly split variants if module.splitModules != nil { + if isTransitionMutator { + // For transition mutators, save the pre-split variant for reusing later in applyIncomingTransitions. + transitionMutatorInputVariants[group] = append(transitionMutatorInputVariants[group], module) + } group.modules, i = spliceModules(group.modules, i, module.splitModules) } @@ -2996,6 +3047,11 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo, } } + if isTransitionMutator { + mutator.transitionMutator.inputVariants = transitionMutatorInputVariants + c.transitionMutators = append(c.transitionMutators, mutator.transitionMutator) + } + // Add in any new reverse dependencies that were added by the mutator for module, deps := range reverseDeps { sort.Sort(depSorter(deps)) @@ -3031,6 +3087,14 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo, return deps, errs } +// clearTransitionMutatorInputVariants removes the inputVariants field from every +// TransitionMutator now that all dependencies have been resolved. +func (c *Context) clearTransitionMutatorInputVariants() { + for _, mutator := range c.transitionMutators { + mutator.inputVariants = nil + } +} + // Replaces every build logic module with a clone of itself. Prevents introducing problems where // a mutator sets a non-property member variable on a module, which works until a later mutator // creates variants of that module. diff --git a/context_test.go b/context_test.go index b946497..e69f5da 100644 --- a/context_test.go +++ b/context_test.go @@ -829,7 +829,8 @@ func Test_findVariant(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, _ := findVariant(module, tt.possibleDeps, tt.variations, tt.far, tt.reverse) + ctx := NewContext() + got, _ := ctx.findVariant(module, nil, tt.possibleDeps, tt.variations, tt.far, tt.reverse) if g, w := got == nil, tt.want == "nil"; g != w { t.Fatalf("findVariant() got = %v, want %v", got, tt.want) } diff --git a/module_ctx.go b/module_ctx.go index 6416a25..530eb5e 100644 --- a/module_ctx.go +++ b/module_ctx.go @@ -575,7 +575,7 @@ func (m *baseModuleContext) OtherModuleDependencyVariantExists(variations []Vari if possibleDeps == nil { return false } - found, _ := findVariant(m.module, possibleDeps, variations, false, false) + found, _ := m.context.findVariant(m.module, m.config, possibleDeps, variations, false, false) return found != nil } @@ -584,7 +584,7 @@ func (m *baseModuleContext) OtherModuleFarDependencyVariantExists(variations []V if possibleDeps == nil { return false } - found, _ := findVariant(m.module, possibleDeps, variations, true, false) + found, _ := m.context.findVariant(m.module, m.config, possibleDeps, variations, true, false) return found != nil } @@ -593,7 +593,7 @@ func (m *baseModuleContext) OtherModuleReverseDependencyVariantExists(name strin if possibleDeps == nil { return false } - found, _ := findVariant(m.module, possibleDeps, nil, false, true) + found, _ := m.context.findVariant(m.module, m.config, possibleDeps, nil, false, true) return found != nil } @@ -1117,7 +1117,7 @@ func (mctx *mutatorContext) AddDependency(module Module, tag DependencyTag, deps depInfos := make([]Module, 0, len(deps)) for _, dep := range deps { modInfo := mctx.context.moduleInfo[module] - depInfo, errs := mctx.context.addDependency(modInfo, tag, dep) + depInfo, errs := mctx.context.addDependency(modInfo, mctx.config, tag, dep) if len(errs) > 0 { mctx.errs = append(mctx.errs, errs...) } @@ -1135,7 +1135,7 @@ func (mctx *mutatorContext) AddReverseDependency(module Module, tag DependencyTa panic("BaseDependencyTag is not allowed to be used directly!") } - destModule, errs := mctx.context.findReverseDependency(mctx.context.moduleInfo[module], destName) + destModule, errs := mctx.context.findReverseDependency(mctx.context.moduleInfo[module], mctx.config, destName) if len(errs) > 0 { mctx.errs = append(mctx.errs, errs...) return @@ -1152,7 +1152,7 @@ func (mctx *mutatorContext) AddVariationDependencies(variations []Variation, tag depInfos := make([]Module, 0, len(deps)) for _, dep := range deps { - depInfo, errs := mctx.context.addVariationDependency(mctx.module, variations, tag, dep, false) + depInfo, errs := mctx.context.addVariationDependency(mctx.module, mctx.config, variations, tag, dep, false) if len(errs) > 0 { mctx.errs = append(mctx.errs, errs...) } @@ -1170,7 +1170,7 @@ func (mctx *mutatorContext) AddFarVariationDependencies(variations []Variation, depInfos := make([]Module, 0, len(deps)) for _, dep := range deps { - depInfo, errs := mctx.context.addVariationDependency(mctx.module, variations, tag, dep, true) + depInfo, errs := mctx.context.addVariationDependency(mctx.module, mctx.config, variations, tag, dep, true) if len(errs) > 0 { mctx.errs = append(mctx.errs, errs...) } diff --git a/transition.go b/transition.go index 31f785e..6a03e1d 100644 --- a/transition.go +++ b/transition.go @@ -144,8 +144,9 @@ type OutgoingTransitionContext interface { } type transitionMutatorImpl struct { - name string - mutator TransitionMutator + name string + mutator TransitionMutator + inputVariants map[*moduleGroup][]*moduleInfo } // Adds each argument in items to l if it's not already there. @@ -294,7 +295,7 @@ func (c *Context) RegisterTransitionMutator(name string, mutator TransitionMutat impl := &transitionMutatorImpl{name: name, mutator: mutator} c.RegisterTopDownMutator(name+"_deps", impl.topDownMutator).Parallel() - c.RegisterBottomUpMutator(name, impl.bottomUpMutator).Parallel() + c.RegisterBottomUpMutator(name, impl.bottomUpMutator).Parallel().setTransitionMutator(impl) c.RegisterBottomUpMutator(name+"_mutate", impl.mutateMutator).Parallel() } diff --git a/transition_test.go b/transition_test.go index 128e7d5..dea818a 100644 --- a/transition_test.go +++ b/transition_test.go @@ -15,14 +15,48 @@ package blueprint import ( + "fmt" "slices" + "strings" "testing" ) -func TestTransition(t *testing.T) { +func testTransition(bp string) (*Context, []error) { ctx := newContext() ctx.MockFileSystem(map[string][]byte{ - "Android.bp": []byte(` + "Android.bp": []byte(bp), + }) + + ctx.RegisterBottomUpMutator("deps", depsMutator) + ctx.RegisterTransitionMutator("transition", transitionTestMutator{}) + ctx.RegisterBottomUpMutator("post_transition_deps", postTransitionDepsMutator) + + ctx.RegisterModuleType("transition_module", newTransitionModule) + _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) + if len(errs) > 0 { + return nil, errs + } + + _, errs = ctx.ResolveDependencies(nil) + if len(errs) > 0 { + return nil, errs + } + + return ctx, nil +} + +func assertNoErrors(t *testing.T, errs []error) { + t.Helper() + if len(errs) > 0 { + t.Errorf("unexpected errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } +} + +const testTransitionBp = ` transition_module { name: "A", deps: ["B", "C"], @@ -33,6 +67,7 @@ func TestTransition(t *testing.T) { name: "B", deps: ["C"], outgoing: "c", + %s } transition_module { @@ -43,103 +78,156 @@ func TestTransition(t *testing.T) { transition_module { name: "D", incoming: "d", + deps: ["E"], } - `), + + transition_module { + name: "E", + } + ` + +func getTransitionModule(ctx *Context, name, variant string) *transitionModule { + group := ctx.moduleGroupFromName(name, nil) + module := group.moduleOrAliasByVariantName(variant).module() + return module.logicModule.(*transitionModule) +} + +func checkTransitionVariants(t *testing.T, ctx *Context, name string, expectedVariants []string) { + t.Helper() + group := ctx.moduleGroupFromName(name, nil) + var gotVariants []string + for _, variant := range group.modules { + gotVariants = append(gotVariants, variant.moduleOrAliasVariant().variations["transition"]) + } + if !slices.Equal(expectedVariants, gotVariants) { + t.Errorf("expected variants of %q to be %q, got %q", name, expectedVariants, gotVariants) + } +} + +func checkTransitionDeps(t *testing.T, ctx *Context, m Module, expected ...string) { + t.Helper() + var got []string + ctx.VisitDirectDeps(m, func(m Module) { + got = append(got, ctx.ModuleName(m)+"("+ctx.ModuleSubDir(m)+")") }) - - ctx.RegisterBottomUpMutator("deps", depsMutator) - ctx.RegisterTransitionMutator("transition", transitionTestMutator{}) - - ctx.RegisterModuleType("transition_module", newTransitionModule) - _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) - if len(errs) > 0 { - t.Errorf("unexpected parse errors:") - for _, err := range errs { - t.Errorf(" %s", err) - } - t.FailNow() + if !slices.Equal(got, expected) { + t.Errorf("unexpected %q dependencies, got %q expected %q", + ctx.ModuleName(m), got, expected) } +} - _, errs = ctx.ResolveDependencies(nil) - if len(errs) > 0 { - t.Errorf("unexpected dep errors:") - for _, err := range errs { - t.Errorf(" %s", err) - } - t.FailNow() +func checkTransitionMutate(t *testing.T, m *transitionModule, variant string) { + t.Helper() + if m.properties.Mutated != variant { + t.Errorf("unexpected mutated property in %q, expected %q got %q", m.Name(), variant, m.properties.Mutated) } +} - getModule := func(name, variant string) *transitionModule { - group := ctx.moduleGroupFromName(name, nil) - module := group.moduleOrAliasByVariantName(variant).module() - return module.logicModule.(*transitionModule) - } - - checkVariants := func(name string, expectedVariants []string) { - t.Helper() - group := ctx.moduleGroupFromName(name, nil) - var gotVariants []string - for _, variant := range group.modules { - gotVariants = append(gotVariants, variant.moduleOrAliasVariant().variations["transition"]) - } - if !slices.Equal(expectedVariants, gotVariants) { - t.Errorf("expected variants of %q to be %q, got %q", name, expectedVariants, gotVariants) - } - } +func TestTransition(t *testing.T) { + ctx, errs := testTransition(fmt.Sprintf(testTransitionBp, "")) + assertNoErrors(t, errs) // Module A uses Split to create a and b variants - checkVariants("A", []string{"a", "b"}) + checkTransitionVariants(t, ctx, "A", []string{"a", "b"}) // Module B inherits a and b variants from A - checkVariants("B", []string{"", "a", "b"}) + checkTransitionVariants(t, ctx, "B", []string{"", "a", "b"}) // Module C inherits a and b variants from A, but gets an outgoing c variant from B - checkVariants("C", []string{"", "a", "b", "c"}) + checkTransitionVariants(t, ctx, "C", []string{"", "a", "b", "c"}) // Module D always has incoming variant d - checkVariants("D", []string{"", "d"}) + checkTransitionVariants(t, ctx, "D", []string{"", "d"}) + // Module E inherits d from D + checkTransitionVariants(t, ctx, "E", []string{"", "d"}) - A_a := getModule("A", "a") - A_b := getModule("A", "b") - B_a := getModule("B", "a") - B_b := getModule("B", "b") - C_a := getModule("C", "a") - C_b := getModule("C", "b") - C_c := getModule("C", "c") - D_d := getModule("D", "d") + A_a := getTransitionModule(ctx, "A", "a") + A_b := getTransitionModule(ctx, "A", "b") + B_a := getTransitionModule(ctx, "B", "a") + B_b := getTransitionModule(ctx, "B", "b") + C_a := getTransitionModule(ctx, "C", "a") + C_b := getTransitionModule(ctx, "C", "b") + C_c := getTransitionModule(ctx, "C", "c") + D_d := getTransitionModule(ctx, "D", "d") + E_d := getTransitionModule(ctx, "E", "d") - checkDeps := func(m Module, expected ...string) { - var got []string - ctx.VisitDirectDeps(m, func(m Module) { - got = append(got, ctx.ModuleName(m)+"("+ctx.ModuleSubDir(m)+")") - }) - if !slices.Equal(got, expected) { - t.Errorf("unexpected %q dependencies, got %q expected %q", - ctx.ModuleName(m), got, expected) - } + checkTransitionDeps(t, ctx, A_a, "B(a)", "C(a)") + checkTransitionDeps(t, ctx, A_b, "B(b)", "C(b)") + checkTransitionDeps(t, ctx, B_a, "C(c)") + checkTransitionDeps(t, ctx, B_b, "C(c)") + checkTransitionDeps(t, ctx, C_a, "D(d)") + checkTransitionDeps(t, ctx, C_b, "D(d)") + checkTransitionDeps(t, ctx, C_c, "D(d)") + checkTransitionDeps(t, ctx, D_d, "E(d)") + checkTransitionDeps(t, ctx, E_d) + + checkTransitionMutate(t, A_a, "a") + checkTransitionMutate(t, A_b, "b") + checkTransitionMutate(t, B_a, "a") + checkTransitionMutate(t, B_b, "b") + checkTransitionMutate(t, C_a, "a") + checkTransitionMutate(t, C_b, "b") + checkTransitionMutate(t, C_c, "c") + checkTransitionMutate(t, D_d, "d") + checkTransitionMutate(t, E_d, "d") +} + +func TestPostTransitionDeps(t *testing.T) { + ctx, errs := testTransition(fmt.Sprintf(testTransitionBp, + `post_transition_deps: ["D:late", "E:d"],`)) + assertNoErrors(t, errs) + + // Module A uses Split to create a and b variants + checkTransitionVariants(t, ctx, "A", []string{"a", "b"}) + // Module B inherits a and b variants from A + checkTransitionVariants(t, ctx, "B", []string{"", "a", "b"}) + // Module C inherits a and b variants from A, but gets an outgoing c variant from B + checkTransitionVariants(t, ctx, "C", []string{"", "a", "b", "c"}) + // Module D always has incoming variant d + checkTransitionVariants(t, ctx, "D", []string{"", "d"}) + // Module E inherits d from D + checkTransitionVariants(t, ctx, "E", []string{"", "d"}) + + A_a := getTransitionModule(ctx, "A", "a") + A_b := getTransitionModule(ctx, "A", "b") + B_a := getTransitionModule(ctx, "B", "a") + B_b := getTransitionModule(ctx, "B", "b") + C_a := getTransitionModule(ctx, "C", "a") + C_b := getTransitionModule(ctx, "C", "b") + C_c := getTransitionModule(ctx, "C", "c") + D_d := getTransitionModule(ctx, "D", "d") + E_d := getTransitionModule(ctx, "E", "d") + + checkTransitionDeps(t, ctx, A_a, "B(a)", "C(a)") + checkTransitionDeps(t, ctx, A_b, "B(b)", "C(b)") + checkTransitionDeps(t, ctx, B_a, "C(c)", "D(d)", "E(d)") + checkTransitionDeps(t, ctx, B_b, "C(c)", "D(d)", "E(d)") + checkTransitionDeps(t, ctx, C_a, "D(d)") + checkTransitionDeps(t, ctx, C_b, "D(d)") + checkTransitionDeps(t, ctx, C_c, "D(d)") + checkTransitionDeps(t, ctx, D_d, "E(d)") + checkTransitionDeps(t, ctx, E_d) + + checkTransitionMutate(t, A_a, "a") + checkTransitionMutate(t, A_b, "b") + checkTransitionMutate(t, B_a, "a") + checkTransitionMutate(t, B_b, "b") + checkTransitionMutate(t, C_a, "a") + checkTransitionMutate(t, C_b, "b") + checkTransitionMutate(t, C_c, "c") + checkTransitionMutate(t, D_d, "d") + checkTransitionMutate(t, E_d, "d") +} + +func TestPostTransitionDepsMissingVariant(t *testing.T) { + // TODO: eventually this will create the missing variant on demand + _, errs := testTransition(fmt.Sprintf(testTransitionBp, + `post_transition_deps: ["E:missing"],`)) + expectedError := `Android.bp:8:4: dependency "E" of "B" missing variant: + transition:missing +available variants: + transition: + transition:d` + if len(errs) != 1 || errs[0].Error() != expectedError { + t.Errorf("expected error %q, got %q", expectedError, errs) } - - checkDeps(A_a, "B(a)", "C(a)") - checkDeps(A_b, "B(b)", "C(b)") - checkDeps(B_a, "C(c)") - checkDeps(B_b, "C(c)") - checkDeps(C_a, "D(d)") - checkDeps(C_b, "D(d)") - checkDeps(C_c, "D(d)") - checkDeps(D_d) - - checkMutate := func(m *transitionModule, variant string) { - t.Helper() - if m.properties.Mutated != variant { - t.Errorf("unexpected mutated property in %q, expected %q got %q", m.Name(), variant, m.properties.Mutated) - } - } - - checkMutate(A_a, "a") - checkMutate(A_b, "b") - checkMutate(B_a, "a") - checkMutate(B_b, "b") - checkMutate(C_a, "a") - checkMutate(C_b, "b") - checkMutate(C_c, "c") - checkMutate(D_d, "d") } type transitionTestMutator struct{} @@ -172,10 +260,11 @@ func (transitionTestMutator) Mutate(ctx BottomUpMutatorContext, variation string type transitionModule struct { SimpleName properties struct { - Deps []string - Split []string - Outgoing *string - Incoming *string + Deps []string + Post_transition_deps []string + Split []string + Outgoing *string + Incoming *string Mutated string `blueprint:"mutated"` } @@ -196,3 +285,13 @@ func (f *transitionModule) Deps() []string { func (f *transitionModule) IgnoreDeps() []string { return nil } + +func postTransitionDepsMutator(mctx BottomUpMutatorContext) { + if m, ok := mctx.Module().(*transitionModule); ok { + for _, dep := range m.properties.Post_transition_deps { + module, variation, _ := strings.Cut(dep, ":") + variations := []Variation{{"transition", variation}} + mctx.AddVariationDependencies(variations, walkerDepsTag{follow: true}, module) + } + } +}