Control mutator order

Register mutators inside lambdas that are called in a defined order to
correctly order mutators before and after the arch and deps mutators.

Test: build.ninja identical
Change-Id: Iefe2a3515aee8570e76a6e76925db4cda0e9e822
diff --git a/android/arch.go b/android/arch.go
index 606de48..61564d8 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -260,7 +260,7 @@
 	return target.Os.String() + "_" + target.Arch.String()
 }
 
-func ArchMutator(mctx BottomUpMutatorContext) {
+func archMutator(mctx BottomUpMutatorContext) {
 	var module Module
 	var ok bool
 	if module, ok = mctx.Module().(Module); !ok {
diff --git a/android/module.go b/android/module.go
index d6eee44..06f1fca 100644
--- a/android/module.go
+++ b/android/module.go
@@ -24,15 +24,6 @@
 	"github.com/google/blueprint"
 )
 
-func init() {
-	RegisterTopDownMutator("load_hooks", loadHookMutator).Parallel()
-	RegisterBottomUpMutator("defaults_deps", defaultsDepsMutator).Parallel()
-	RegisterTopDownMutator("defaults", defaultsMutator).Parallel()
-
-	RegisterBottomUpMutator("arch", ArchMutator).Parallel()
-	RegisterTopDownMutator("arch_hooks", archHookMutator).Parallel()
-}
-
 var (
 	DeviceSharedLibrary = "shared_library"
 	DeviceStaticLibrary = "static_library"
@@ -100,6 +91,7 @@
 	blueprint.Module
 
 	GenerateAndroidBuildActions(ModuleContext)
+	DepsMutator(BottomUpMutatorContext)
 
 	base() *ModuleBase
 	Enabled() bool
diff --git a/android/mutator.go b/android/mutator.go
index ff2f9ad..b375bce 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -16,6 +16,61 @@
 
 import "github.com/google/blueprint"
 
+// Mutator phases:
+//   Pre-arch
+//   Arch
+//   Pre-deps
+//   Deps
+//   PostDeps
+
+func registerMutators() {
+	ctx := registerMutatorsContext{}
+
+	register := func(funcs []RegisterMutatorFunc) {
+		for _, f := range funcs {
+			f(ctx)
+		}
+	}
+
+	ctx.TopDown("load_hooks", loadHookMutator).Parallel()
+	ctx.BottomUp("defaults_deps", defaultsDepsMutator).Parallel()
+	ctx.TopDown("defaults", defaultsMutator).Parallel()
+
+	register(preArch)
+
+	ctx.BottomUp("arch", archMutator).Parallel()
+	ctx.TopDown("arch_hooks", archHookMutator).Parallel()
+
+	register(preDeps)
+
+	ctx.BottomUp("deps", depsMutator).Parallel()
+
+	register(postDeps)
+}
+
+type registerMutatorsContext struct{}
+
+type RegisterMutatorsContext interface {
+	TopDown(name string, m AndroidTopDownMutator) MutatorHandle
+	BottomUp(name string, m AndroidBottomUpMutator) MutatorHandle
+}
+
+type RegisterMutatorFunc func(RegisterMutatorsContext)
+
+var preArch, preDeps, postDeps []RegisterMutatorFunc
+
+func PreArchMutators(f RegisterMutatorFunc) {
+	preArch = append(preArch, f)
+}
+
+func PreDepsMutators(f RegisterMutatorFunc) {
+	preDeps = append(preDeps, f)
+}
+
+func PostDepsMutators(f RegisterMutatorFunc) {
+	postDeps = append(postDeps, f)
+}
+
 type AndroidTopDownMutator func(TopDownMutatorContext)
 
 type TopDownMutatorContext interface {
@@ -40,7 +95,7 @@
 	androidBaseContextImpl
 }
 
-func RegisterBottomUpMutator(name string, m AndroidBottomUpMutator) MutatorHandle {
+func (registerMutatorsContext) BottomUp(name string, m AndroidBottomUpMutator) MutatorHandle {
 	f := func(ctx blueprint.BottomUpMutatorContext) {
 		if a, ok := ctx.Module().(Module); ok {
 			actx := &androidBottomUpMutatorContext{
@@ -55,7 +110,7 @@
 	return mutator
 }
 
-func RegisterTopDownMutator(name string, m AndroidTopDownMutator) MutatorHandle {
+func (registerMutatorsContext) TopDown(name string, m AndroidTopDownMutator) MutatorHandle {
 	f := func(ctx blueprint.TopDownMutatorContext) {
 		if a, ok := ctx.Module().(Module); ok {
 			actx := &androidTopDownMutatorContext{
@@ -78,3 +133,9 @@
 	mutator.parallel = true
 	return mutator
 }
+
+func depsMutator(ctx BottomUpMutatorContext) {
+	if m, ok := ctx.Module().(Module); ok {
+		m.DepsMutator(ctx)
+	}
+}
diff --git a/android/register.go b/android/register.go
index 7cc9d50..f869eed 100644
--- a/android/register.go
+++ b/android/register.go
@@ -58,6 +58,8 @@
 		ctx.RegisterSingletonType(t.name, t.factory)
 	}
 
+	registerMutators()
+
 	for _, t := range mutators {
 		var handle blueprint.MutatorHandle
 		if t.bottomUpMutator != nil {
diff --git a/android/variable.go b/android/variable.go
index 29d6c7d..ac7c730 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -24,7 +24,9 @@
 )
 
 func init() {
-	RegisterBottomUpMutator("variable", variableMutator).Parallel()
+	PreDepsMutators(func(ctx RegisterMutatorsContext) {
+		ctx.BottomUp("variable", variableMutator).Parallel()
+	})
 }
 
 type variableProperties struct {
diff --git a/cc/cc.go b/cc/cc.go
index 791d9ea..5811b0a 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -34,20 +34,20 @@
 func init() {
 	android.RegisterModuleType("cc_defaults", defaultsFactory)
 
-	// LinkageMutator must be registered after common.ArchMutator, but that is guaranteed by
-	// the Go initialization order because this package depends on common, so common's init
-	// functions will run first.
-	android.RegisterBottomUpMutator("link", linkageMutator).Parallel()
-	android.RegisterBottomUpMutator("ndk_api", ndkApiMutator).Parallel()
-	android.RegisterBottomUpMutator("test_per_src", testPerSrcMutator).Parallel()
-	android.RegisterBottomUpMutator("begin", beginMutator).Parallel()
-	android.RegisterBottomUpMutator("deps", depsMutator).Parallel()
+	android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("link", linkageMutator).Parallel()
+		ctx.BottomUp("ndk_api", ndkApiMutator).Parallel()
+		ctx.BottomUp("test_per_src", testPerSrcMutator).Parallel()
+		ctx.BottomUp("begin", beginMutator).Parallel()
+	})
 
-	android.RegisterTopDownMutator("asan_deps", sanitizerDepsMutator(asan))
-	android.RegisterBottomUpMutator("asan", sanitizerMutator(asan)).Parallel()
+	android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.TopDown("asan_deps", sanitizerDepsMutator(asan))
+		ctx.BottomUp("asan", sanitizerMutator(asan)).Parallel()
 
-	android.RegisterTopDownMutator("tsan_deps", sanitizerDepsMutator(tsan))
-	android.RegisterBottomUpMutator("tsan", sanitizerMutator(tsan)).Parallel()
+		ctx.TopDown("tsan_deps", sanitizerDepsMutator(tsan))
+		ctx.BottomUp("tsan", sanitizerMutator(tsan)).Parallel()
+	})
 
 	pctx.Import("android/soong/cc/config")
 }
@@ -534,7 +534,11 @@
 	c.begin(ctx)
 }
 
-func (c *Module) depsMutator(actx android.BottomUpMutatorContext) {
+func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
+	if !c.Enabled() {
+		return
+	}
+
 	ctx := &baseModuleContext{
 		BaseContext: actx,
 		moduleContextImpl: moduleContextImpl{
@@ -641,12 +645,6 @@
 	}
 }
 
-func depsMutator(ctx android.BottomUpMutatorContext) {
-	if c, ok := ctx.Module().(*Module); ok && c.Enabled() {
-		c.depsMutator(ctx)
-	}
-}
-
 func (c *Module) clang(ctx BaseModuleContext) bool {
 	clang := Bool(c.Properties.Clang)
 
@@ -911,6 +909,9 @@
 func (*Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 }
 
+func (d *Defaults) DepsMutator(ctx android.BottomUpMutatorContext) {
+}
+
 func defaultsFactory() (blueprint.Module, []interface{}) {
 	return DefaultsFactory()
 }
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 5d70b89..ece83bb 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -56,6 +56,9 @@
 	installPaths []string
 }
 
+func (m *headerModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+}
+
 func (m *headerModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	srcFiles := ctx.ExpandSources(m.properties.Srcs, nil)
 	for _, header := range srcFiles {
diff --git a/genrule/genrule.go b/genrule/genrule.go
index baa6b33..f70c5fc 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -25,8 +25,6 @@
 func init() {
 	android.RegisterModuleType("gensrcs", GenSrcsFactory)
 	android.RegisterModuleType("genrule", GenRuleFactory)
-
-	android.RegisterBottomUpMutator("genrule_deps", genruleDepsMutator).Parallel()
 }
 
 var (
@@ -95,7 +93,7 @@
 	return g.genPath
 }
 
-func genruleDepsMutator(ctx android.BottomUpMutatorContext) {
+func (g *generator) DepsMutator(ctx android.BottomUpMutatorContext) {
 	if g, ok := ctx.Module().(*generator); ok {
 		if g.properties.Tool != "" {
 			ctx.AddFarVariationDependencies([]blueprint.Variation{
diff --git a/java/java.go b/java/java.go
index 808e1db..689fe75 100644
--- a/java/java.go
+++ b/java/java.go
@@ -190,7 +190,7 @@
 
 var defaultJavaLibraries = []string{"core-libart", "core-junit", "ext", "framework"}
 
-func javaDepsMutator(ctx android.BottomUpMutatorContext) {
+func (j *javaBase) DepsMutator(ctx android.BottomUpMutatorContext) {
 	if j, ok := ctx.Module().(JavaModuleType); ok {
 		ctx.AddDependency(ctx.Module(), nil, j.JavaDependencies(ctx)...)
 	}
@@ -513,6 +513,9 @@
 	classJarSpecs, resourceJarSpecs []jarSpec
 }
 
+func (j *JavaPrebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
+}
+
 func (j *JavaPrebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if len(j.properties.Srcs) != 1 {
 		ctx.ModuleErrorf("expected exactly one jar in srcs")