diff --git a/Blueprints b/Blueprints index c3c89754f626a6c5a6c12ce1868ea73934faba09..100672187ebce597833ab84cd368c903051ce575 100644 --- a/Blueprints +++ b/Blueprints @@ -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", diff --git a/context.go b/context.go index e26a47a8fd32ae64130f4747e40a6abad5b855d2..6a53776046c8911de9762111f6defabb27b99457 100644 --- a/context.go +++ b/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 } diff --git a/module_ctx.go b/module_ctx.go index a227436d035ee51f49b3dd56b46ed1ba82bb24d8..639cbf7783a441acd82ad1fb719c8a90a8c8685e 100644 --- a/module_ctx.go +++ b/module_ctx.go @@ -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) } diff --git a/module_ctx_test.go b/module_ctx_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e7127ae0cce5d8fdc8bf151dd063d1e10172cc71 --- /dev/null +++ b/module_ctx_test.go @@ -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") + }) +}