Add support for Mutators

Add Mutators, which can be used to split a module into multiple
variants.

Change-Id: Ib992da2e93a557559a3a34571d5033192e129e49
diff --git a/blueprint/context.go b/blueprint/context.go
index be4b074..788d45b 100644
--- a/blueprint/context.go
+++ b/blueprint/context.go
@@ -2,6 +2,7 @@
 
 import (
 	"blueprint/parser"
+	"blueprint/proptools"
 	"bytes"
 	"errors"
 	"fmt"
@@ -51,6 +52,7 @@
 	moduleInfo         map[Module]*moduleInfo
 	moduleGroupsSorted []*moduleGroup
 	singletonInfo      map[string]*singletonInfo
+	mutatorInfo        []*mutatorInfo
 
 	dependenciesReady bool // set to true on a successful ResolveDependencies
 	buildActionsReady bool // set to true on a successful PrepareBuildActions
@@ -105,9 +107,31 @@
 }
 
 type moduleInfo struct {
-	directDeps  []*moduleInfo // set during ResolveDependencies
-	logicModule Module
-	group       *moduleGroup
+	name             []subName
+	logicModule      Module
+	group            *moduleGroup
+	moduleProperties []interface{}
+
+	// set during ResolveDependencies
+	directDeps []*moduleInfo
+
+	// set during each runMutator
+	splitModules []*moduleInfo
+}
+
+type subName struct {
+	mutatorName string
+	variantName string
+}
+
+func (module *moduleInfo) subName() string {
+	names := []string{}
+	for _, subName := range module.name {
+		if subName.variantName != "" {
+			names = append(names, subName.variantName)
+		}
+	}
+	return strings.Join(names, "_")
 }
 
 type singletonInfo struct {
@@ -119,6 +143,13 @@
 	actionDefs localBuildActions
 }
 
+type mutatorInfo struct {
+	// set during RegisterMutator
+	topDownMutator TopDownMutator
+	bottomUpMutator    BottomUpMutator
+	name              string
+}
+
 func (e *Error) Error() string {
 
 	return fmt.Sprintf("%s: %s", e.Pos, e.Err)
@@ -146,7 +177,8 @@
 // Blueprints file) with a Module factory function.  When the given module type
 // name is encountered in a Blueprints file during parsing, the Module factory
 // is invoked to instantiate a new Module object to handle the build action
-// generation for the module.
+// generation for the module.  If a Mutator splits a module into multiple variants,
+// the factory is invoked again to create a new Module for each variant.
 //
 // The module type names given here must be unique for the context.  The factory
 // function should be a named function so that its package and name can be
@@ -246,6 +278,43 @@
 	return typ.PkgPath() + "." + typ.Name()
 }
 
+// RegisterTopDownMutator registers a mutator that will be invoked to propagate
+// dependency info top-down between Modules.  Each registered mutator
+// is invoked once per Module, and is invoked on a module before being invoked
+// on any of its dependencies
+//
+// The mutator type names given here must be unique for the context.
+func (c *Context) RegisterTopDownMutator(name string, mutator TopDownMutator) {
+	for _, m := range c.mutatorInfo {
+		if m.name == name && m.topDownMutator != nil {
+			panic(fmt.Errorf("mutator name %s is already registered", name))
+		}
+	}
+
+	c.mutatorInfo = append(c.mutatorInfo, &mutatorInfo{
+		topDownMutator: mutator,
+		name:              name,
+	})
+}
+
+// RegisterBottomUpMutator registers a mutator that will be invoked to split
+// Modules into variants.  Each registered mutator is invoked once per Module,
+// and is invoked on dependencies before being invoked on dependers.
+//
+// The mutator type names given here must be unique for the context.
+func (c *Context) RegisterBottomUpMutator(name string, mutator BottomUpMutator) {
+	for _, m := range c.mutatorInfo {
+		if m.name == name && m.bottomUpMutator != nil {
+			panic(fmt.Errorf("mutator name %s is already registered", name))
+		}
+	}
+
+	c.mutatorInfo = append(c.mutatorInfo, &mutatorInfo{
+		bottomUpMutator: mutator,
+		name:           name,
+	})
+}
+
 // 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
@@ -513,6 +582,95 @@
 	}
 }
 
+func (c *Context) createVariants(origModule *moduleInfo, mutatorName string,
+	variantNames []string) []*moduleInfo {
+
+	newModules := []*moduleInfo{}
+	origVariantName := origModule.name
+	group := origModule.group
+
+	for i, variantName := range variantNames {
+		typeName := group.typeName
+		factory, ok := c.moduleFactories[typeName]
+		if !ok {
+			panic(fmt.Sprintf("unrecognized module type %q during cloning", typeName))
+		}
+
+		var newLogicModule Module
+		var newProperties []interface{}
+
+		if i == 0 {
+			// Reuse the existing module for the first new variant
+			newLogicModule = origModule.logicModule
+			newProperties = origModule.moduleProperties
+		} else {
+			props := []interface{}{
+				&group.properties,
+			}
+			newLogicModule, newProperties = factory()
+
+			newProperties = append(props, newProperties...)
+
+			if len(newProperties) != len(origModule.moduleProperties) {
+				panic("mismatched properties array length in " + group.properties.Name)
+			}
+
+			for i := range newProperties {
+				dst := reflect.ValueOf(newProperties[i]).Elem()
+				src := reflect.ValueOf(origModule.moduleProperties[i]).Elem()
+
+				proptools.CopyProperties(dst, src)
+			}
+		}
+
+		newVariantName := append([]subName(nil), origVariantName...)
+		newSubName := subName{
+			mutatorName: mutatorName,
+			variantName: variantName,
+		}
+		newVariantName = append(newVariantName, newSubName)
+
+		newModule := &moduleInfo{
+			group:            group,
+			directDeps:       append([]*moduleInfo(nil), origModule.directDeps...),
+			logicModule:      newLogicModule,
+			name:             newVariantName,
+			moduleProperties: newProperties,
+		}
+
+		newModules = append(newModules, newModule)
+		c.moduleInfo[newModule.logicModule] = newModule
+
+		c.convertDepsToVariant(newModule, newSubName)
+	}
+
+	// Mark original variant as invalid.  Modules that depend on this module will still
+	// depend on origModule, but we'll fix it when the mutator is called on them.
+	origModule.logicModule = nil
+	origModule.splitModules = newModules
+
+	return newModules
+}
+
+func (c *Context) convertDepsToVariant(module *moduleInfo, newSubName subName) {
+	for i, dep := range module.directDeps {
+		if dep.logicModule == nil {
+			var newDep *moduleInfo
+			for _, m := range dep.splitModules {
+				if len(m.name) > 0 && m.name[len(m.name)-1] == newSubName {
+					newDep = m
+					break
+				}
+			}
+			if newDep == nil {
+				panic(fmt.Sprintf("failed to find variant %s for module %s needed by %s",
+					newSubName.variantName, dep.group.properties.Name, module.group.properties.Name))
+			}
+			module.directDeps[i] = newDep
+		}
+	}
+}
+
 func (c *Context) processModuleDef(moduleDef *parser.Module,
 	relBlueprintsFile string) []error {
 
@@ -579,8 +737,9 @@
 	}
 
 	module := &moduleInfo{
-		group:       group,
-		logicModule: logicModule,
+		group:            group,
+		logicModule:      logicModule,
+		moduleProperties: properties,
 	}
 
 	c.moduleGroups[name] = group
@@ -677,38 +836,48 @@
 			panic("expected a single module in resolveDependencies")
 		}
 		group.modules[0].directDeps = make([]*moduleInfo, 0, len(depNames))
-		depsPos := group.propertyPos["deps"]
 
 		for _, depName := range depNames {
-			if depName == group.properties.Name {
-				errs = append(errs, &Error{
-					Err: fmt.Errorf("%q depends on itself", depName),
-					Pos: depsPos,
-				})
+			newErrs := c.addDependency(group.modules[0], depName)
+			if len(newErrs) > 0 {
+				errs = append(errs, newErrs...)
 				continue
 			}
-
-			depInfo, ok := c.moduleGroups[depName]
-			if !ok {
-				errs = append(errs, &Error{
-					Err: fmt.Errorf("%q depends on undefined module %q",
-						group.properties.Name, depName),
-					Pos: depsPos,
-				})
-				continue
-			}
-
-			if len(depInfo.modules) != 1 {
-				panic("expected a single module in resolveDependencies")
-			}
-
-			group.modules[0].directDeps = append(group.modules[0].directDeps, depInfo.modules[0])
 		}
 	}
 
 	return
 }
 
+func (c *Context) addDependency(module *moduleInfo, depName string) []error {
+	depsPos := module.group.propertyPos["deps"]
+
+	if depName == module.group.properties.Name {
+		return []error{&Error{
+			Err: fmt.Errorf("%q depends on itself", depName),
+			Pos: depsPos,
+		}}
+	}
+
+	depInfo, ok := c.moduleGroups[depName]
+	if !ok {
+		return []error{&Error{
+			Err: fmt.Errorf("%q depends on undefined module %q",
+				module.group.properties.Name, depName),
+			Pos: depsPos,
+		}}
+	}
+
+	if len(depInfo.modules) != 1 {
+		panic(fmt.Sprintf("cannot add dependency from %s to %s, it already has multiple variants",
+			module.group.properties.Name, depInfo.properties.Name))
+	}
+
+	module.directDeps = append(module.directDeps, depInfo.modules[0])
+
+	return nil
+}
+
 // rebuildSortedModuleList recursively walks the module dependency graph and
 // builds a sorted list of modules such that dependencies of a module always
 // appear first.  It also reports errors when it encounters dependency cycles.
@@ -825,6 +994,11 @@
 		}
 	}
 
+	errs = c.runMutators(config)
+	if len(errs) > 0 {
+		return nil, errs
+	}
+
 	liveGlobals := newLiveTracker(config)
 
 	c.initSpecialVariables()
@@ -865,6 +1039,107 @@
 	return deps, nil
 }
 
+func (c *Context) runMutators(config interface{}) (errs []error) {
+	for _, mutator := range c.mutatorInfo {
+		if mutator.topDownMutator != nil {
+			errs = c.runTopDownMutator(config, mutator.name, mutator.topDownMutator)
+		} else if mutator.bottomUpMutator != nil {
+			errs = c.runBottomUpMutator(config, mutator.name, mutator.bottomUpMutator)
+		} else {
+			panic("no mutator set on " + mutator.name)
+		}
+		if len(errs) > 0 {
+			return errs
+		}
+	}
+
+	return nil
+}
+
+func (c *Context) runTopDownMutator(config interface{},
+	name string, mutator TopDownMutator) (errs []error) {
+
+	for i := 0; i < len(c.moduleGroupsSorted); i++ {
+		group := c.moduleGroupsSorted[len(c.moduleGroupsSorted)-1-i]
+		for _, module := range group.modules {
+			mctx := &mutatorContext{
+				baseModuleContext: baseModuleContext{
+					context: c,
+					config:  config,
+					group:   group,
+				},
+				module: module,
+				name:   name,
+			}
+
+			mutator(mctx)
+			if len(mctx.errs) > 0 {
+				errs = append(errs, mctx.errs...)
+				return errs
+			}
+		}
+	}
+
+	return errs
+}
+
+func (c *Context) runBottomUpMutator(config interface{},
+	name string, mutator BottomUpMutator) (errs []error) {
+
+	dependenciesModified := false
+
+	for _, group := range c.moduleGroupsSorted {
+		newModules := make([]*moduleInfo, 0, len(group.modules))
+
+		for _, module := range group.modules {
+			mctx := &mutatorContext{
+				baseModuleContext: baseModuleContext{
+					context: c,
+					config:  config,
+					group:   group,
+				},
+				module: module,
+				name:   name,
+			}
+
+			mutator(mctx)
+			if len(mctx.errs) > 0 {
+				errs = append(errs, mctx.errs...)
+				return errs
+			}
+
+			// Fix up any remaining dependencies on modules that were split into variants
+			// by replacing them with the first variant
+			for i, dep := range module.directDeps {
+				if dep.logicModule == nil {
+					module.directDeps[i] = dep.splitModules[0]
+				}
+			}
+
+			if mctx.dependenciesModified {
+				dependenciesModified = true
+			}
+
+			if module.splitModules != nil {
+				newModules = append(newModules, module.splitModules...)
+			} else {
+				newModules = append(newModules, module)
+			}
+		}
+
+		group.modules = newModules
+	}
+
+	if dependenciesModified {
+		errs = c.rebuildSortedModuleList()
+		if len(errs) > 0 {
+			return errs
+		}
+	}
+
+	return errs
+}
+
 func (c *Context) initSpecialVariables() {
 	c.buildDir = nil
 	c.requiredNinjaMajor = 1
@@ -918,7 +1193,8 @@
 						config:  config,
 						group:   group,
 					},
-					module: module,
+					module:        module,
+					primaryModule: group.modules[0],
 				},
 				scope: scope,
 			}
@@ -1004,12 +1280,11 @@
 		return errs
 	}
 
-	out.buildDefs = in.buildDefs
+	out.buildDefs = append(out.buildDefs, in.buildDefs...)
 
 	// We use the now-incorrect set of live "globals" to determine which local
 	// definitions are live.  As we go through copying those live locals to the
-	// moduleInfo we remove them from the live globals set.
-	out.variables = nil
+	// moduleGroup we remove them from the live globals set.
 	for _, v := range in.variables {
 		_, isLive := liveGlobals.variables[v]
 		if isLive {
@@ -1018,7 +1293,6 @@
 		}
 	}
 
-	out.rules = nil
 	for _, r := range in.rules {
 		_, isLive := liveGlobals.rules[r]
 		if isLive {
@@ -1074,6 +1348,22 @@
 	walk(topModule)
 }
 
+func (c *Context) visitDirectDeps(module *moduleInfo, visit func(Module)) {
+	for _, dep := range module.directDeps {
+		visit(dep.logicModule)
+	}
+}
+
+func (c *Context) visitDirectDepsIf(module *moduleInfo, pred func(Module) bool,
+	visit func(Module)) {
+
+	for _, dep := range module.directDeps {
+		if pred(dep.logicModule) {
+			visit(dep.logicModule)
+		}
+	}
+}
+
 func (c *Context) sortedModuleNames() []string {
 	if c.cachedSortedModuleNames == nil {
 		c.cachedSortedModuleNames = make([]string, 0, len(c.moduleGroups))
diff --git a/blueprint/module_ctx.go b/blueprint/module_ctx.go
index e60e82f..fd89a86 100644
--- a/blueprint/module_ctx.go
+++ b/blueprint/module_ctx.go
@@ -7,13 +7,17 @@
 )
 
 // A Module handles generating all of the Ninja build actions needed to build a
-// single module that is defined in a Blueprints file.  Module objects are
-// created during the parse phase of a Context using one of the registered
-// module types (and the associated ModuleFactory function).  The Module's
-// properties struct is automatically filled in with the property values
-// specified in the Blueprints file (see Context.RegisterModuleType for more
+// single module based on properties defined in a Blueprints file.  Module
+// objects are initially created during the parse phase of a Context using one
+// of the registered module types (and the associated ModuleFactory function).
+// The Module's properties struct is automatically filled in with the property
+// values specified in the Blueprints file (see Context.RegisterModuleType for more
 // information on this).
 //
+// A Module can be split into multiple Modules by a Mutator.  All existing
+// properties set on the module will be duplicated to the new Module, and then
+// modified as necessary by the Mutator.
+//
 // The Module implementation can access the build configuration as well as any
 // modules on which on which it depends (as defined by the "deps" property
 // specified in the Blueprints file or dynamically added by implementing the
@@ -115,11 +119,15 @@
 type ModuleContext interface {
 	PreModuleContext
 
+	ModuleSubDir() string
+
 	Variable(pctx *PackageContext, name, value string)
 	Rule(pctx *PackageContext, name string, params RuleParams, argNames ...string) Rule
 	Build(pctx *PackageContext, params BuildParams)
 
 	AddNinjaFileDeps(deps ...string)
+
+	PrimaryModule() Module
 }
 
 var _ BaseModuleContext = (*baseModuleContext)(nil)
@@ -188,7 +196,8 @@
 
 type preModuleContext struct {
 	baseModuleContext
-	module *moduleInfo
+	module        *moduleInfo
+	primaryModule *moduleInfo
 }
 
 func (m *preModuleContext) OtherModuleName(module Module) string {
@@ -225,6 +234,10 @@
 	actionDefs    localBuildActions
 }
 
+func (m *moduleContext) ModuleSubDir() string {
+	return m.module.subName()
+}
+
 func (m *moduleContext) Variable(pctx *PackageContext, name, value string) {
 	m.scope.ReparentTo(pctx)
 
@@ -265,3 +278,120 @@
 func (m *moduleContext) AddNinjaFileDeps(deps ...string) {
 	m.ninjaFileDeps = append(m.ninjaFileDeps, deps...)
 }
+
+func (m *moduleContext) PrimaryModule() Module {
+	return m.primaryModule.logicModule
+}
+
+//
+// MutatorContext
+//
+
+type mutatorContext struct {
+	baseModuleContext
+	module               *moduleInfo
+	name                 string
+	dependenciesModified bool
+}
+
+type baseMutatorContext interface {
+	BaseModuleContext
+
+	Module() Module
+}
+
+type TopDownMutatorContext interface {
+	baseMutatorContext
+
+	VisitDirectDeps(visit func(Module))
+	VisitDirectDepsIf(pred func(Module) bool, visit func(Module))
+	VisitDepsDepthFirst(visit func(Module))
+	VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
+}
+
+type BottomUpMutatorContext interface {
+	baseMutatorContext
+
+	AddDependency(module Module, name string)
+	CreateVariants(...string) []Module
+	SetDependencyVariant(string)
+}
+
+// A Mutator function is called for each Module, and can use
+// MutatorContext.CreateSubVariants to split a Module into multiple Modules,
+// modifying properties on the new modules to differentiate them.  It is called
+// after parsing all Blueprint files, but before generating any build rules,
+// and is always called on dependencies before being called on the depending module.
+//
+// The Mutator function should only modify members of properties structs, and not
+// members of the module struct itself, to ensure the modified values are copied
+// if a second Mutator chooses to split the module a second time.
+type TopDownMutator func(mctx TopDownMutatorContext)
+type BottomUpMutator func(mctx BottomUpMutatorContext)
+
+// Split a module into mulitple variants, one for each name in the variantNames
+// parameter.  It returns a list of new modules in the same order as the variantNames
+// list.
+//
+// If any of the dependencies of the module being operated on were already split
+// by calling CreateVariants with the same name, the dependency will automatically
+// be updated to point the matching variant.
+//
+// If a module is split, and then a module depending on the first module is not split
+// when the Mutator is later called on it, the dependency of the depending module will
+// automatically be updated to point to the first variant.
+func (mctx *mutatorContext) CreateVariants(variantNames ...string) []Module {
+	ret := []Module{}
+	modules := mctx.context.createVariants(mctx.module, mctx.name, variantNames)
+
+	for _, module := range modules {
+		ret = append(ret, module.logicModule)
+	}
+
+	if len(ret) != len(variantNames) {
+		panic("oops!")
+	}
+
+	return ret
+}
+
+// Set all dangling dependencies on the current module to point to the variant
+// with given name.
+func (mctx *mutatorContext) SetDependencyVariant(variantName string) {
+	subName := subName{
+		mutatorName: mctx.name,
+		variantName: variantName,
+	}
+	mctx.context.convertDepsToVariant(mctx.module, subName)
+}
+
+func (mctx *mutatorContext) Module() Module {
+	return mctx.module.logicModule
+}
+
+// Add a dependency to the given module.  The depender can be a specific variant
+// of a module, but the dependee must be a module that only has a single variant.
+// Does not affect the ordering of the current mutator pass, but will be ordered
+// correctly for all future mutator passes.
+func (mctx *mutatorContext) AddDependency(module Module, depName string) {
+	mctx.context.addDependency(mctx.context.moduleInfo[module], depName)
+	mctx.dependenciesModified = true
+}
+
+func (mctx *mutatorContext) VisitDirectDeps(visit func(Module)) {
+	mctx.context.visitDirectDeps(mctx.module, visit)
+}
+
+func (mctx *mutatorContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) {
+	mctx.context.visitDirectDepsIf(mctx.module, pred, visit)
+}
+
+func (mctx *mutatorContext) VisitDepsDepthFirst(visit func(Module)) {
+	mctx.context.visitDepsDepthFirst(mctx.module, visit)
+}
+
+func (mctx *mutatorContext) VisitDepsDepthFirstIf(pred func(Module) bool,
+	visit func(Module)) {
+
+	mctx.context.visitDepsDepthFirstIf(mctx.module, pred, visit)
+}