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
This commit is contained in:
Colin Cross 2024-03-29 12:25:29 -07:00
parent 6ed94b7f85
commit d7474dd743
5 changed files with 279 additions and 114 deletions

View file

@ -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.

View file

@ -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)
}

View file

@ -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...)
}

View file

@ -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()
}

View file

@ -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)
}
}
}