Add CreateAliasVariation

CreateAliasVariation creates a new variation for the current mutator
that is an alias to a variation that was created with CreateVarations
by the same mutator.  It is similar to AliasVariation, except that
AliasVariation creates an alias from the pre-mutated variant to one
of the new variations, while CreateAliasVariation creates an alias
from a new post-mutated variation to one of the new variations.

Bug: 164216768
Test: TestCreateAliasVariation
Change-Id: I8847b8d6fc1d3248abc910b2fe2399881f9bdaee
diff --git a/context.go b/context.go
index e2f07e6..75e9b00 100644
--- a/context.go
+++ b/context.go
@@ -201,8 +201,8 @@
 	waitingCount int
 
 	// set during each runMutator
-	splitModules []*moduleInfo
-	aliasTarget  *moduleInfo
+	splitModules   []*moduleInfo
+	pendingAliases []pendingAlias
 
 	// set during PrepareBuildActions
 	actionDefs localBuildActions
@@ -1317,6 +1317,15 @@
 					break
 				}
 			}
+			if newDep == nil {
+				// check aliases against variationNam
+				for _, a := range dep.module.pendingAliases {
+					if a.fromVariant.variations[mutatorName] == variationName {
+						newDep = a.target
+						break
+					}
+				}
+			}
 			if newDep == nil && defaultVariationName != nil {
 				// give it a second chance; match with defaultVariationName
 				for _, m := range dep.module.splitModules {
@@ -1325,6 +1334,15 @@
 						break
 					}
 				}
+				if newDep == nil {
+					// check aliases against defaultVariationName
+					for _, a := range dep.module.pendingAliases {
+						if a.fromVariant.variations[mutatorName] == *defaultVariationName {
+							newDep = a.target
+							break
+						}
+					}
+				}
 			}
 			if newDep == nil {
 				errs = append(errs, &BlueprintError{
@@ -2221,6 +2239,8 @@
 			panic("split module found in sorted module list")
 		}
 
+		module.pendingAliases = nil
+
 		mctx := &mutatorContext{
 			baseModuleContext: baseModuleContext{
 				context: c,
@@ -2315,10 +2335,10 @@
 			}
 
 			// Create any new aliases.
-			if module.aliasTarget != nil {
+			for _, alias := range module.pendingAliases {
 				group.aliases = append(group.aliases, &moduleAlias{
-					variant: module.variant,
-					target:  module.aliasTarget,
+					variant: alias.fromVariant,
+					target:  alias.target,
 				})
 			}
 
@@ -2339,13 +2359,23 @@
 			module.newDirectDeps = nil
 		}
 
+		findAliasTarget := func(variant variant) *moduleInfo {
+			for _, alias := range group.aliases {
+				if alias.variant.variations.equal(variant.variations) {
+					return alias.target
+				}
+			}
+			return 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
+				newTarget := findAliasTarget(alias.target.variant)
+				if newTarget != nil {
+					alias.target = newTarget
 				} else {
 					// The alias was left dangling, remove it.
 					group.aliases = append(group.aliases[:i], group.aliases[i+1:]...)
@@ -3543,14 +3573,17 @@
 	iName := s.nameInterface.UniqueName(newNamespaceContext(iMod), iMod.group.name)
 	jName := s.nameInterface.UniqueName(newNamespaceContext(jMod), jMod.group.name)
 	if iName == jName {
-		iName = s.modules[i].variant.name
-		jName = s.modules[j].variant.name
+		iVariantName := s.modules[i].variant.name
+		jVariantName := s.modules[j].variant.name
+		if iVariantName == jVariantName {
+			panic(fmt.Sprintf("duplicate module name: %s %s: %#v and %#v\n",
+				iName, iVariantName, iMod.variant.variations, jMod.variant.variations))
+		} else {
+			return iVariantName < jVariantName
+		}
+	} else {
+		return iName < jName
 	}
-
-	if iName == jName {
-		panic(fmt.Sprintf("duplicate module name: %s: %#v and %#v\n", iName, iMod, jMod))
-	}
-	return iName < jName
 }
 
 func (s moduleSorter) Swap(i, j int) {
diff --git a/module_ctx.go b/module_ctx.go
index 0b58817..d8b1582 100644
--- a/module_ctx.go
+++ b/module_ctx.go
@@ -850,12 +850,21 @@
 	// Replacements don't take effect until after the mutator pass is finished.
 	ReplaceDependenciesIf(string, ReplaceDependencyPredicate)
 
-	// 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 takes a variationName that was passed to CreateVariations for this module,
+	// and creates an alias from the current variant (before the mutator has run) 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)
+
+	// CreateAliasVariation takes a toVariationName that was passed to CreateVariations for this
+	// module, and creates an alias from a new fromVariationName variant the toVariationName
+	// 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 toVariationName variant using the fromVariationName
+	// variant.
+	CreateAliasVariation(fromVariationName, toVariationName string)
 }
 
 // A Mutator function is called for each Module, and can use
@@ -899,6 +908,11 @@
 	return mctx.createVariations(variationNames, true)
 }
 
+type pendingAlias struct {
+	fromVariant variant
+	target      *moduleInfo
+}
+
 func (mctx *mutatorContext) createVariations(variationNames []string, local bool) []Module {
 	ret := []Module{}
 	modules, errs := mctx.context.createVariations(mctx.module, mctx.name, mctx.defaultVariation, variationNames, local)
@@ -923,13 +937,18 @@
 }
 
 func (mctx *mutatorContext) AliasVariation(variationName string) {
-	if mctx.module.aliasTarget != nil {
-		panic(fmt.Errorf("AliasVariation already called"))
+	for _, alias := range mctx.module.pendingAliases {
+		if alias.fromVariant.variations.equal(mctx.module.variant.variations) {
+			panic(fmt.Errorf("AliasVariation already called"))
+		}
 	}
 
 	for _, variant := range mctx.newVariations {
 		if variant.variant.variations[mctx.name] == variationName {
-			mctx.module.aliasTarget = variant
+			mctx.module.pendingAliases = append(mctx.module.pendingAliases, pendingAlias{
+				fromVariant: mctx.module.variant,
+				target:      variant,
+			})
 			return
 		}
 	}
@@ -941,6 +960,32 @@
 	panic(fmt.Errorf("no %q variation in module variations %q", variationName, foundVariations))
 }
 
+func (mctx *mutatorContext) CreateAliasVariation(aliasVariationName, targetVariationName string) {
+	newVariant := newVariant(mctx.module, mctx.name, aliasVariationName, false)
+
+	for _, alias := range mctx.module.pendingAliases {
+		if alias.fromVariant.variations.equal(newVariant.variations) {
+			panic(fmt.Errorf("can't alias %q to %q, already aliased to %q", aliasVariationName, targetVariationName, alias.target.variant.name))
+		}
+	}
+
+	for _, variant := range mctx.newVariations {
+		if variant.variant.variations[mctx.name] == targetVariationName {
+			mctx.module.pendingAliases = append(mctx.module.pendingAliases, pendingAlias{
+				fromVariant: newVariant,
+				target:      variant,
+			})
+			return
+		}
+	}
+
+	var foundVariations []string
+	for _, variant := range mctx.newVariations {
+		foundVariations = append(foundVariations, variant.variant.variations[mctx.name])
+	}
+	panic(fmt.Errorf("no %q variation in module variations %q", targetVariationName, 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
index e39f51a..22666dc 100644
--- a/module_ctx_test.go
+++ b/module_ctx_test.go
@@ -32,7 +32,7 @@
 func (f *moduleCtxTestModule) GenerateBuildActions(ModuleContext) {
 }
 
-func noCreateAliasMutator(name string) func(ctx BottomUpMutatorContext) {
+func noAliasMutator(name string) func(ctx BottomUpMutatorContext) {
 	return func(ctx BottomUpMutatorContext) {
 		if ctx.ModuleName() == name {
 			ctx.CreateVariations("a", "b")
@@ -40,11 +40,22 @@
 	}
 }
 
+func aliasMutator(name string) func(ctx BottomUpMutatorContext) {
+	return func(ctx BottomUpMutatorContext) {
+		if ctx.ModuleName() == name {
+			ctx.CreateVariations("a", "b")
+			ctx.AliasVariation("b")
+		}
+	}
+}
+
 func createAliasMutator(name string) func(ctx BottomUpMutatorContext) {
 	return func(ctx BottomUpMutatorContext) {
 		if ctx.ModuleName() == name {
 			ctx.CreateVariations("a", "b")
-			ctx.AliasVariation("b")
+			ctx.CreateAliasVariation("c", "a")
+			ctx.CreateAliasVariation("d", "b")
+			ctx.CreateAliasVariation("e", "a")
 		}
 	}
 }
@@ -57,7 +68,7 @@
 	}
 }
 
-func TestAliases(t *testing.T) {
+func TestAliasVariation(t *testing.T) {
 	runWithFailures := func(ctx *Context, expectedErr string) {
 		t.Helper()
 		bp := `
@@ -115,7 +126,7 @@
 		// Tests a dependency from "foo" to "bar" variant "b" through alias "".
 		ctx := NewContext()
 		ctx.RegisterModuleType("test", newModuleCtxTestModule)
-		ctx.RegisterBottomUpMutator("1", createAliasMutator("bar"))
+		ctx.RegisterBottomUpMutator("1", aliasMutator("bar"))
 		ctx.RegisterBottomUpMutator("2", addVariantDepsMutator(nil, nil, "foo", "bar"))
 
 		run(ctx)
@@ -138,8 +149,8 @@
 		// 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("1", aliasMutator("bar"))
+		ctx.RegisterBottomUpMutator("2", aliasMutator("bar"))
 		ctx.RegisterBottomUpMutator("3", addVariantDepsMutator(nil, nil, "foo", "bar"))
 
 		run(ctx)
@@ -162,8 +173,8 @@
 		// 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("1", aliasMutator("bar"))
+		ctx.RegisterBottomUpMutator("2", aliasMutator("bar"))
 		ctx.RegisterBottomUpMutator("3", addVariantDepsMutator([]Variation{{"1", "a"}}, nil, "foo", "bar"))
 
 		run(ctx)
@@ -186,8 +197,8 @@
 		// 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("1", aliasMutator("bar"))
+		ctx.RegisterBottomUpMutator("2", noAliasMutator("bar"))
 		ctx.RegisterBottomUpMutator("3", addVariantDepsMutator(nil, nil, "foo", "bar"))
 
 		runWithFailures(ctx, `dependency "bar" of "foo" missing variant:`+"\n  \n"+
@@ -196,6 +207,121 @@
 	})
 }
 
+func TestCreateAliasVariations(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"}, nil)
+		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 "c" -> "a", "d" -> "b", and "e" -> "a".
+		// Tests a dependency from "foo" to "bar" variant "b" through alias "d".
+		ctx := NewContext()
+		ctx.RegisterModuleType("test", newModuleCtxTestModule)
+		ctx.RegisterBottomUpMutator("1", createAliasMutator("bar"))
+		ctx.RegisterBottomUpMutator("2", addVariantDepsMutator([]Variation{{"1", "d"}}, nil, "foo", "bar"))
+
+		run(ctx)
+
+		foo := ctx.moduleGroupFromName("foo", nil).modules[0]
+		barB := ctx.moduleGroupFromName("bar", nil).modules[1]
+
+		if g, w := barB.variant.name, "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 "c" -> "a_b",
+		// "d" -> "b_b", and "d" -> "a_b".
+		// Tests a dependency from "foo" to "bar" variant "b_b" through alias "d".
+		ctx := NewContext()
+		ctx.RegisterModuleType("test", newModuleCtxTestModule)
+		ctx.RegisterBottomUpMutator("1", createAliasMutator("bar"))
+		ctx.RegisterBottomUpMutator("2", aliasMutator("bar"))
+		ctx.RegisterBottomUpMutator("3", addVariantDepsMutator([]Variation{{"1", "d"}}, nil, "foo", "bar"))
+
+		run(ctx)
+
+		foo := ctx.moduleGroupFromName("foo", nil).modules[0]
+		barBB := ctx.moduleGroupFromName("bar", nil).modules[3]
+
+		if g, w := barBB.variant.name, "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("removed dangling alias", func(t *testing.T) {
+		// Creates a module "bar" with variants "a" and "b" and alias "c" -> "a", "d" -> "b", and "e" -> "a",
+		// 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 "d" fails.
+		ctx := NewContext()
+		ctx.RegisterModuleType("test", newModuleCtxTestModule)
+		ctx.RegisterBottomUpMutator("1", createAliasMutator("bar"))
+		ctx.RegisterBottomUpMutator("2", noAliasMutator("bar"))
+		ctx.RegisterBottomUpMutator("3", addVariantDepsMutator([]Variation{{"1", "d"}}, nil, "foo", "bar"))
+
+		runWithFailures(ctx, `dependency "bar" of "foo" missing variant:`+"\n  1:d\n"+
+			"available variants:"+
+			"\n  1:a, 2:a\n  1:a, 2:b\n  1:b, 2:a\n  1:b, 2:b")
+	})
+}
+
 func expectedErrors(t *testing.T, errs []error, expectedMessages ...string) {
 	t.Helper()
 	if len(errs) != len(expectedMessages) {