Add support for module variant aliases
Adding a dependency on a module with variants can be problematic if the code adding the dependency is not aware of every mutator that has created variants. Add an AliasVariations to BottomUpMutatorContext, which allows a mutator to alias the original variant of a module to one of the new variants of the module, which will allow future dependencies to be added using the original list of variations. The aliases are transient, and only exist until the next mutator that calls CreateVariations when visiting the module without also calling AliasVariations. Test: TestAlises Change-Id: Ieaa04b5a6bdcb5a1ff5114b1e03460de795d4479
This commit is contained in:
parent
9403b5a790
commit
f7beb89df5
4 changed files with 282 additions and 0 deletions
|
@ -24,6 +24,7 @@ bootstrap_go_package {
|
|||
testSrcs: [
|
||||
"context_test.go",
|
||||
"glob_test.go",
|
||||
"module_ctx_test.go",
|
||||
"ninja_strings_test.go",
|
||||
"ninja_writer_test.go",
|
||||
"splice_modules_test.go",
|
||||
|
|
58
context.go
58
context.go
|
@ -157,11 +157,19 @@ type localBuildActions struct {
|
|||
buildDefs []*buildDef
|
||||
}
|
||||
|
||||
type moduleAlias struct {
|
||||
variantName string
|
||||
variant variationMap
|
||||
dependencyVariant variationMap
|
||||
target *moduleInfo
|
||||
}
|
||||
|
||||
type moduleGroup struct {
|
||||
name string
|
||||
ninjaName string
|
||||
|
||||
modules []*moduleInfo
|
||||
aliases []*moduleAlias
|
||||
|
||||
namespace Namespace
|
||||
}
|
||||
|
@ -197,6 +205,7 @@ type moduleInfo struct {
|
|||
|
||||
// set during each runMutator
|
||||
splitModules []*moduleInfo
|
||||
aliasTarget *moduleInfo
|
||||
|
||||
// set during PrepareBuildActions
|
||||
actionDefs localBuildActions
|
||||
|
@ -1272,6 +1281,10 @@ func (c *Context) prettyPrintGroupVariants(group *moduleGroup) string {
|
|||
for _, mod := range group.modules {
|
||||
variants = append(variants, c.prettyPrintVariant(mod.variant))
|
||||
}
|
||||
for _, mod := range group.aliases {
|
||||
variants = append(variants, c.prettyPrintVariant(mod.variant)+
|
||||
"(alias to "+c.prettyPrintVariant(mod.target.variant)+")")
|
||||
}
|
||||
sort.Strings(variants)
|
||||
return strings.Join(variants, "\n ")
|
||||
}
|
||||
|
@ -1443,6 +1456,11 @@ func (c *Context) findMatchingVariant(module *moduleInfo, possible *moduleGroup,
|
|||
return m
|
||||
}
|
||||
}
|
||||
for _, m := range possible.aliases {
|
||||
if m.variant.equal(variantToMatch) {
|
||||
return m.target
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -1551,6 +1569,15 @@ func (c *Context) addVariationDependency(module *moduleInfo, variations []Variat
|
|||
}
|
||||
}
|
||||
|
||||
if foundDep == nil {
|
||||
for _, m := range possibleDeps.aliases {
|
||||
if check(m.variant) {
|
||||
foundDep = m.target
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if foundDep == nil {
|
||||
return []error{&BlueprintError{
|
||||
Err: fmt.Errorf("dependency %q of %q missing variant:\n %s\navailable variants:\n %s",
|
||||
|
@ -2172,6 +2199,16 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo,
|
|||
group.modules, i = spliceModules(group.modules, i, module.splitModules)
|
||||
}
|
||||
|
||||
// Create any new aliases.
|
||||
if module.aliasTarget != nil {
|
||||
group.aliases = append(group.aliases, &moduleAlias{
|
||||
variantName: module.variantName,
|
||||
variant: module.variant,
|
||||
dependencyVariant: module.dependencyVariant,
|
||||
target: module.aliasTarget,
|
||||
})
|
||||
}
|
||||
|
||||
// Fix up any remaining dependencies on modules that were split into variants
|
||||
// by replacing them with the first variant
|
||||
for j, dep := range module.directDeps {
|
||||
|
@ -2188,6 +2225,21 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo,
|
|||
module.directDeps = append(module.directDeps, module.newDirectDeps...)
|
||||
module.newDirectDeps = nil
|
||||
}
|
||||
|
||||
// Forward or delete any dangling aliases.
|
||||
for i := 0; i < len(group.aliases); i++ {
|
||||
alias := group.aliases[i]
|
||||
|
||||
if alias.target.logicModule == nil {
|
||||
if alias.target.aliasTarget != nil {
|
||||
alias.target = alias.target.aliasTarget
|
||||
} else {
|
||||
// The alias was left dangling, remove it.
|
||||
group.aliases = append(group.aliases[:i], group.aliases[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add in any new reverse dependencies that were added by the mutator
|
||||
|
@ -2528,6 +2580,12 @@ func (c *Context) moduleMatchingVariant(module *moduleInfo, name string) *module
|
|||
}
|
||||
}
|
||||
|
||||
for _, m := range group.aliases {
|
||||
if module.variantName == m.variantName {
|
||||
return m.target
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -785,6 +785,13 @@ type BottomUpMutatorContext interface {
|
|||
// specified name with the current variant of this module. Replacements don't take effect until
|
||||
// after the mutator pass is finished.
|
||||
ReplaceDependencies(string)
|
||||
|
||||
// AliasVariation takes a variationName that was passed to CreateVariations for this module, and creates an
|
||||
// alias from the current variant to the new variant. The alias will be valid until the next time a mutator
|
||||
// calls CreateVariations or CreateLocalVariations on this module without also calling AliasVariation. The
|
||||
// alias can be used to add dependencies on the newly created variant using the variant map from before
|
||||
// CreateVariations was run.
|
||||
AliasVariation(variationName string)
|
||||
}
|
||||
|
||||
// A Mutator function is called for each Module, and can use
|
||||
|
@ -857,6 +864,25 @@ func (mctx *mutatorContext) createVariations(variationNames []string, local bool
|
|||
return ret
|
||||
}
|
||||
|
||||
func (mctx *mutatorContext) AliasVariation(variationName string) {
|
||||
if mctx.module.aliasTarget != nil {
|
||||
panic(fmt.Errorf("AliasVariation already called"))
|
||||
}
|
||||
|
||||
for _, variant := range mctx.newVariations {
|
||||
if variant.variant[mctx.name] == variationName {
|
||||
mctx.module.aliasTarget = variant
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var foundVariations []string
|
||||
for _, variant := range mctx.newVariations {
|
||||
foundVariations = append(foundVariations, variant.variant[mctx.name])
|
||||
}
|
||||
panic(fmt.Errorf("no %q variation in module variations %q", variationName, foundVariations))
|
||||
}
|
||||
|
||||
func (mctx *mutatorContext) SetDependencyVariation(variationName string) {
|
||||
mctx.context.convertDepsToVariation(mctx.module, mctx.name, variationName, nil)
|
||||
}
|
||||
|
|
197
module_ctx_test.go
Normal file
197
module_ctx_test.go
Normal file
|
@ -0,0 +1,197 @@
|
|||
// Copyright 2019 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package blueprint
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type moduleCtxTestModule struct {
|
||||
SimpleName
|
||||
}
|
||||
|
||||
func newModuleCtxTestModule() (Module, []interface{}) {
|
||||
m := &moduleCtxTestModule{}
|
||||
return m, []interface{}{&m.SimpleName.Properties}
|
||||
}
|
||||
|
||||
func (f *moduleCtxTestModule) GenerateBuildActions(ModuleContext) {
|
||||
}
|
||||
|
||||
func noCreateAliasMutator(name string) func(ctx BottomUpMutatorContext) {
|
||||
return func(ctx BottomUpMutatorContext) {
|
||||
if ctx.ModuleName() == name {
|
||||
ctx.CreateVariations("a", "b")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createAliasMutator(name string) func(ctx BottomUpMutatorContext) {
|
||||
return func(ctx BottomUpMutatorContext) {
|
||||
if ctx.ModuleName() == name {
|
||||
ctx.CreateVariations("a", "b")
|
||||
ctx.AliasVariation("b")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addVariantDepsMutator(variants []Variation, tag DependencyTag, from, to string) func(ctx BottomUpMutatorContext) {
|
||||
return func(ctx BottomUpMutatorContext) {
|
||||
if ctx.ModuleName() == from {
|
||||
ctx.AddVariationDependencies(variants, tag, to)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAliases(t *testing.T) {
|
||||
runWithFailures := func(ctx *Context, expectedErr string) {
|
||||
t.Helper()
|
||||
bp := `
|
||||
test {
|
||||
name: "foo",
|
||||
}
|
||||
|
||||
test {
|
||||
name: "bar",
|
||||
}
|
||||
`
|
||||
|
||||
mockFS := map[string][]byte{
|
||||
"Blueprints": []byte(bp),
|
||||
}
|
||||
|
||||
ctx.MockFileSystem(mockFS)
|
||||
|
||||
_, errs := ctx.ParseFileList(".", []string{"Blueprints"})
|
||||
if len(errs) > 0 {
|
||||
t.Errorf("unexpected parse errors:")
|
||||
for _, err := range errs {
|
||||
t.Errorf(" %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
_, errs = ctx.ResolveDependencies(nil)
|
||||
if len(errs) > 0 {
|
||||
if expectedErr == "" {
|
||||
t.Errorf("unexpected dep errors:")
|
||||
for _, err := range errs {
|
||||
t.Errorf(" %s", err)
|
||||
}
|
||||
} else {
|
||||
for _, err := range errs {
|
||||
if strings.Contains(err.Error(), expectedErr) {
|
||||
continue
|
||||
} else {
|
||||
t.Errorf("unexpected dep error: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if expectedErr != "" {
|
||||
t.Errorf("missing dep error: %s", expectedErr)
|
||||
}
|
||||
}
|
||||
|
||||
run := func(ctx *Context) {
|
||||
t.Helper()
|
||||
runWithFailures(ctx, "")
|
||||
}
|
||||
|
||||
t.Run("simple", func(t *testing.T) {
|
||||
// Creates a module "bar" with variants "a" and "b" and alias "" -> "b".
|
||||
// Tests a dependency from "foo" to "bar" variant "b" through alias "".
|
||||
ctx := NewContext()
|
||||
ctx.RegisterModuleType("test", newModuleCtxTestModule)
|
||||
ctx.RegisterBottomUpMutator("1", createAliasMutator("bar"))
|
||||
ctx.RegisterBottomUpMutator("2", addVariantDepsMutator(nil, nil, "foo", "bar"))
|
||||
|
||||
run(ctx)
|
||||
|
||||
foo := ctx.moduleGroupFromName("foo", nil).modules[0]
|
||||
barB := ctx.moduleGroupFromName("bar", nil).modules[1]
|
||||
|
||||
if g, w := barB.variantName, "b"; g != w {
|
||||
t.Fatalf("expected bar.modules[1] variant to be %q, got %q", w, g)
|
||||
}
|
||||
|
||||
if g, w := foo.forwardDeps, []*moduleInfo{barB}; !reflect.DeepEqual(g, w) {
|
||||
t.Fatalf("expected foo deps to be %q, got %q", w, g)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("chained", func(t *testing.T) {
|
||||
// Creates a module "bar" with variants "a_a", "a_b", "b_a" and "b_b" and aliases "" -> "b_b",
|
||||
// "a" -> "a_b", and "b" -> "b_b".
|
||||
// Tests a dependency from "foo" to "bar" variant "b_b" through alias "".
|
||||
ctx := NewContext()
|
||||
ctx.RegisterModuleType("test", newModuleCtxTestModule)
|
||||
ctx.RegisterBottomUpMutator("1", createAliasMutator("bar"))
|
||||
ctx.RegisterBottomUpMutator("2", createAliasMutator("bar"))
|
||||
ctx.RegisterBottomUpMutator("3", addVariantDepsMutator(nil, nil, "foo", "bar"))
|
||||
|
||||
run(ctx)
|
||||
|
||||
foo := ctx.moduleGroupFromName("foo", nil).modules[0]
|
||||
barBB := ctx.moduleGroupFromName("bar", nil).modules[3]
|
||||
|
||||
if g, w := barBB.variantName, "b_b"; g != w {
|
||||
t.Fatalf("expected bar.modules[3] variant to be %q, got %q", w, g)
|
||||
}
|
||||
|
||||
if g, w := foo.forwardDeps, []*moduleInfo{barBB}; !reflect.DeepEqual(g, w) {
|
||||
t.Fatalf("expected foo deps to be %q, got %q", w, g)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("chained2", func(t *testing.T) {
|
||||
// Creates a module "bar" with variants "a_a", "a_b", "b_a" and "b_b" and aliases "" -> "b_b",
|
||||
// "a" -> "a_b", and "b" -> "b_b".
|
||||
// Tests a dependency from "foo" to "bar" variant "a_b" through alias "a".
|
||||
ctx := NewContext()
|
||||
ctx.RegisterModuleType("test", newModuleCtxTestModule)
|
||||
ctx.RegisterBottomUpMutator("1", createAliasMutator("bar"))
|
||||
ctx.RegisterBottomUpMutator("2", createAliasMutator("bar"))
|
||||
ctx.RegisterBottomUpMutator("3", addVariantDepsMutator([]Variation{{"1", "a"}}, nil, "foo", "bar"))
|
||||
|
||||
run(ctx)
|
||||
|
||||
foo := ctx.moduleGroupFromName("foo", nil).modules[0]
|
||||
barAB := ctx.moduleGroupFromName("bar", nil).modules[1]
|
||||
|
||||
if g, w := barAB.variantName, "a_b"; g != w {
|
||||
t.Fatalf("expected bar.modules[1] variant to be %q, got %q", w, g)
|
||||
}
|
||||
|
||||
if g, w := foo.forwardDeps, []*moduleInfo{barAB}; !reflect.DeepEqual(g, w) {
|
||||
t.Fatalf("expected foo deps to be %q, got %q", w, g)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("removed dangling alias", func(t *testing.T) {
|
||||
// Creates a module "bar" with variants "a" and "b" and aliases "" -> "b", then splits the variants into
|
||||
// "a_a", "a_b", "b_a" and "b_b" without creating new aliases.
|
||||
// Tests a dependency from "foo" to removed "bar" alias "" fails.
|
||||
ctx := NewContext()
|
||||
ctx.RegisterModuleType("test", newModuleCtxTestModule)
|
||||
ctx.RegisterBottomUpMutator("1", createAliasMutator("bar"))
|
||||
ctx.RegisterBottomUpMutator("2", noCreateAliasMutator("bar"))
|
||||
ctx.RegisterBottomUpMutator("3", addVariantDepsMutator(nil, nil, "foo", "bar"))
|
||||
|
||||
runWithFailures(ctx, `dependency "bar" of "foo" missing variant:`+"\n \n"+
|
||||
"available variants:"+
|
||||
"\n 1:a, 2:a\n 1:a, 2:b\n 1:b, 2:a\n 1:b, 2:b")
|
||||
})
|
||||
}
|
Loading…
Reference in a new issue