Add early mutators
The mutators that run after dependencies are resolved can be too late to support build logic that needs to vary the dependencies based on the mutated axis, for example architecture. This patch provides an EarlyMutator interface that can be used to mutate modules before any dependencies have been resolved. In order for dependencies to be satisifed in a later pass, all dependencies of a module must either have an identical variant, must have a single variant, or must be inserted using DynamicDependencyModuleContext.AddVariantDependency. Change-Id: Ic6ae57e98edfd6c8c09a7788983128d3e4e992f0
This commit is contained in:
parent
ab6d790165
commit
65569e4375
2 changed files with 282 additions and 56 deletions
284
context.go
284
context.go
|
@ -62,13 +62,15 @@ const maxErrors = 10
|
|||
// actions.
|
||||
type Context struct {
|
||||
// set at instantiation
|
||||
moduleFactories map[string]ModuleFactory
|
||||
moduleGroups map[string]*moduleGroup
|
||||
moduleInfo map[Module]*moduleInfo
|
||||
modulesSorted []*moduleInfo
|
||||
singletonInfo map[string]*singletonInfo
|
||||
mutatorInfo []*mutatorInfo
|
||||
moduleNinjaNames map[string]*moduleGroup
|
||||
moduleFactories map[string]ModuleFactory
|
||||
moduleGroups map[string]*moduleGroup
|
||||
moduleInfo map[Module]*moduleInfo
|
||||
modulesSorted []*moduleInfo
|
||||
singletonInfo map[string]*singletonInfo
|
||||
mutatorInfo []*mutatorInfo
|
||||
earlyMutatorInfo []*earlyMutatorInfo
|
||||
variantMutatorNames []string
|
||||
moduleNinjaNames map[string]*moduleGroup
|
||||
|
||||
dependenciesReady bool // set to true on a successful ResolveDependencies
|
||||
buildActionsReady bool // set to true on a successful PrepareBuildActions
|
||||
|
@ -123,8 +125,9 @@ type moduleInfo struct {
|
|||
Deps []string
|
||||
}
|
||||
|
||||
variantName string
|
||||
variants variantMap
|
||||
variantName string
|
||||
variants variantMap
|
||||
dependencyVariants variantMap
|
||||
|
||||
logicModule Module
|
||||
group *moduleGroup
|
||||
|
@ -147,6 +150,11 @@ type moduleInfo struct {
|
|||
actionDefs localBuildActions
|
||||
}
|
||||
|
||||
type Variant struct {
|
||||
Mutator string
|
||||
Variant string
|
||||
}
|
||||
|
||||
type variantMap map[string]string
|
||||
|
||||
func (vm variantMap) clone() variantMap {
|
||||
|
@ -178,6 +186,12 @@ type mutatorInfo struct {
|
|||
name string
|
||||
}
|
||||
|
||||
type earlyMutatorInfo struct {
|
||||
// set during RegisterEarlyMutator
|
||||
mutator EarlyMutator
|
||||
name string
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
|
||||
return fmt.Sprintf("%s: %s", e.Pos, e.Err)
|
||||
|
@ -311,10 +325,12 @@ func singletonTypeName(singleton Singleton) string {
|
|||
|
||||
// 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
|
||||
// is invoked in registration order (mixing TopDownMutators and BottomUpMutators)
|
||||
// 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.
|
||||
// The mutator type names given here must be unique to all top down mutators in
|
||||
// the Context.
|
||||
func (c *Context) RegisterTopDownMutator(name string, mutator TopDownMutator) {
|
||||
for _, m := range c.mutatorInfo {
|
||||
if m.name == name && m.topDownMutator != nil {
|
||||
|
@ -329,13 +345,15 @@ func (c *Context) RegisterTopDownMutator(name string, mutator TopDownMutator) {
|
|||
}
|
||||
|
||||
// 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.
|
||||
// Modules into variants. Each registered mutator is invoked in registration
|
||||
// order (mixing TopDownMutators and BottomUpMutators) 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.
|
||||
// The mutator type names given here must be unique to all bottom up or early
|
||||
// mutators in the Context.
|
||||
func (c *Context) RegisterBottomUpMutator(name string, mutator BottomUpMutator) {
|
||||
for _, m := range c.mutatorInfo {
|
||||
if m.name == name && m.bottomUpMutator != nil {
|
||||
for _, m := range c.variantMutatorNames {
|
||||
if m == name {
|
||||
panic(fmt.Errorf("mutator name %s is already registered", name))
|
||||
}
|
||||
}
|
||||
|
@ -344,6 +362,35 @@ func (c *Context) RegisterBottomUpMutator(name string, mutator BottomUpMutator)
|
|||
bottomUpMutator: mutator,
|
||||
name: name,
|
||||
})
|
||||
|
||||
c.variantMutatorNames = append(c.variantMutatorNames, name)
|
||||
}
|
||||
|
||||
// RegisterEarlyMutator registers a mutator that will be invoked to split
|
||||
// Modules into multiple variant Modules before any dependencies have been
|
||||
// created. Each registered mutator is invoked in registration order once
|
||||
// per Module (including each variant from previous early mutators). Module
|
||||
// order is unpredictable.
|
||||
//
|
||||
// In order for dependencies to be satisifed in a later pass, all dependencies
|
||||
// of a module either must have an identical variant or must have a single
|
||||
// variant.
|
||||
//
|
||||
// The mutator type names given here must be unique to all bottom up or early
|
||||
// mutators in the Context.
|
||||
func (c *Context) RegisterEarlyMutator(name string, mutator EarlyMutator) {
|
||||
for _, m := range c.variantMutatorNames {
|
||||
if m == name {
|
||||
panic(fmt.Errorf("mutator name %s is already registered", name))
|
||||
}
|
||||
}
|
||||
|
||||
c.earlyMutatorInfo = append(c.earlyMutatorInfo, &earlyMutatorInfo{
|
||||
mutator: mutator,
|
||||
name: name,
|
||||
})
|
||||
|
||||
c.variantMutatorNames = append(c.variantMutatorNames, name)
|
||||
}
|
||||
|
||||
// SetIgnoreUnknownModuleTypes sets the behavior of the context in the case
|
||||
|
@ -711,6 +758,7 @@ func (c *Context) createVariants(origModule *moduleInfo, mutatorName string,
|
|||
newModule.directDeps = append([]*moduleInfo(nil), origModule.directDeps...)
|
||||
newModule.logicModule = newLogicModule
|
||||
newModule.variants = newVariants
|
||||
newModule.dependencyVariants = origModule.dependencyVariants.clone()
|
||||
newModule.moduleProperties = newProperties
|
||||
|
||||
if newModule.variantName == "" {
|
||||
|
@ -763,6 +811,17 @@ func (c *Context) convertDepsToVariant(module *moduleInfo,
|
|||
return errs
|
||||
}
|
||||
|
||||
func (c *Context) prettyPrintVariant(variant variantMap) string {
|
||||
names := make([]string, 0, len(variant))
|
||||
for _, m := range c.variantMutatorNames {
|
||||
if v, ok := variant[m]; ok {
|
||||
names = append(names, m+":"+v)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(names, ", ")
|
||||
}
|
||||
|
||||
func (c *Context) processModuleDef(moduleDef *parser.Module,
|
||||
relBlueprintsFile string) (*moduleInfo, []error) {
|
||||
|
||||
|
@ -873,13 +932,14 @@ func (c *Context) ResolveDependencies(config interface{}) []error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// moduleDepNames returns the sorted list of dependency names for a given
|
||||
// module. If the module implements the DynamicDependerModule interface then
|
||||
// this set consists of the union of those module names listed in its "deps"
|
||||
// property and those returned by its DynamicDependencies method. Otherwise it
|
||||
// moduleDeps adds dependencies to a module. If the module implements the
|
||||
// DynamicDependerModule interface then this set consists of the union of those
|
||||
// module names listed in its "deps" property, those returned by its
|
||||
// DynamicDependencies method, and those added by calling AddDependencies or
|
||||
// AddVariantDependencies on DynamicDependencyModuleContext. Otherwise it
|
||||
// is simply those names listed in its "deps" property.
|
||||
func (c *Context) moduleDepNames(module *moduleInfo,
|
||||
config interface{}) ([]string, []error) {
|
||||
func (c *Context) moduleDeps(module *moduleInfo,
|
||||
config interface{}) (errs []error) {
|
||||
|
||||
depNamesSet := make(map[string]bool)
|
||||
depNames := []string{}
|
||||
|
@ -891,19 +951,21 @@ func (c *Context) moduleDepNames(module *moduleInfo,
|
|||
}
|
||||
}
|
||||
|
||||
logicModule := module.logicModule
|
||||
dynamicDepender, ok := logicModule.(DynamicDependerModule)
|
||||
dynamicDepender, ok := module.logicModule.(DynamicDependerModule)
|
||||
if ok {
|
||||
ddmctx := &baseModuleContext{
|
||||
context: c,
|
||||
config: config,
|
||||
module: module,
|
||||
ddmctx := &dynamicDependerModuleContext{
|
||||
baseModuleContext: baseModuleContext{
|
||||
context: c,
|
||||
config: config,
|
||||
module: module,
|
||||
},
|
||||
module: module,
|
||||
}
|
||||
|
||||
dynamicDeps := dynamicDepender.DynamicDependencies(ddmctx)
|
||||
|
||||
if len(ddmctx.errs) > 0 {
|
||||
return nil, ddmctx.errs
|
||||
return ddmctx.errs
|
||||
}
|
||||
|
||||
for _, depName := range dynamicDeps {
|
||||
|
@ -914,29 +976,25 @@ func (c *Context) moduleDepNames(module *moduleInfo,
|
|||
}
|
||||
}
|
||||
|
||||
return depNames, nil
|
||||
for _, depName := range depNames {
|
||||
newErrs := c.addDependency(module, depName)
|
||||
if len(newErrs) > 0 {
|
||||
errs = append(errs, newErrs...)
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// resolveDependencies populates the moduleGroup.modules[0].directDeps list for every
|
||||
// module. In doing so it checks for missing dependencies and self-dependant
|
||||
// modules.
|
||||
// resolveDependencies populates the directDeps list for every module. In doing so it checks for
|
||||
// missing dependencies and self-dependant modules.
|
||||
func (c *Context) resolveDependencies(config interface{}) (errs []error) {
|
||||
for _, group := range c.moduleGroups {
|
||||
for _, module := range group.modules {
|
||||
depNames, newErrs := c.moduleDepNames(module, config)
|
||||
module.directDeps = make([]*moduleInfo, 0, len(module.properties.Deps))
|
||||
|
||||
newErrs := c.moduleDeps(module, config)
|
||||
if len(newErrs) > 0 {
|
||||
errs = append(errs, newErrs...)
|
||||
continue
|
||||
}
|
||||
|
||||
module.directDeps = make([]*moduleInfo, 0, len(depNames))
|
||||
|
||||
for _, depName := range depNames {
|
||||
newErrs := c.addDependency(module, depName)
|
||||
if len(newErrs) > 0 {
|
||||
errs = append(errs, newErrs...)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -963,14 +1021,76 @@ func (c *Context) addDependency(module *moduleInfo, depName string) []error {
|
|||
}}
|
||||
}
|
||||
|
||||
if len(depInfo.modules) != 1 {
|
||||
panic(fmt.Sprintf("cannot add dependency from %s to %s, it already has multiple variants",
|
||||
module.properties.Name, depInfo.modules[0].properties.Name))
|
||||
for _, m := range module.directDeps {
|
||||
if m.group == depInfo {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
module.directDeps = append(module.directDeps, depInfo.modules[0])
|
||||
if len(depInfo.modules) == 1 {
|
||||
module.directDeps = append(module.directDeps, depInfo.modules[0])
|
||||
return nil
|
||||
} else {
|
||||
for _, m := range depInfo.modules {
|
||||
if m.variants.equal(module.dependencyVariants) {
|
||||
module.directDeps = append(module.directDeps, m)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return []error{&Error{
|
||||
Err: fmt.Errorf("dependency %q of %q missing variant %q",
|
||||
depInfo.modules[0].properties.Name, module.properties.Name,
|
||||
c.prettyPrintVariant(module.dependencyVariants)),
|
||||
Pos: depsPos,
|
||||
}}
|
||||
}
|
||||
|
||||
func (c *Context) addVariantDependency(module *moduleInfo, variant []Variant,
|
||||
depName string) []error {
|
||||
|
||||
depsPos := module.propertyPos["deps"]
|
||||
|
||||
depInfo, ok := c.moduleGroups[depName]
|
||||
if !ok {
|
||||
return []error{&Error{
|
||||
Err: fmt.Errorf("%q depends on undefined module %q",
|
||||
module.properties.Name, depName),
|
||||
Pos: depsPos,
|
||||
}}
|
||||
}
|
||||
|
||||
// We can't just append variant.Variant to module.dependencyVariants.variantName and
|
||||
// compare the strings because the result won't be in mutator registration order.
|
||||
// Create a new map instead, and then deep compare the maps.
|
||||
newVariants := module.dependencyVariants.clone()
|
||||
for _, v := range variant {
|
||||
newVariants[v.Mutator] = v.Variant
|
||||
}
|
||||
|
||||
for _, m := range depInfo.modules {
|
||||
if newVariants.equal(m.variants) {
|
||||
// AddVariantDependency allows adding a dependency on itself, but only if
|
||||
// that module is earlier in the module list than this one, since we always
|
||||
// run the generator in order for the variants of a module
|
||||
if depInfo == module.group && beforeInModuleList(module, m, module.group.modules) {
|
||||
return []error{&Error{
|
||||
Err: fmt.Errorf("%q depends on later version of itself", depName),
|
||||
Pos: depsPos,
|
||||
}}
|
||||
}
|
||||
module.directDeps = append(module.directDeps, m)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return []error{&Error{
|
||||
Err: fmt.Errorf("dependency %q of %q missing variant %q",
|
||||
depInfo.modules[0].properties.Name, module.properties.Name,
|
||||
c.prettyPrintVariant(newVariants)),
|
||||
Pos: depsPos,
|
||||
}}
|
||||
}
|
||||
|
||||
func (c *Context) parallelVisitAllBottomUp(visit func(group *moduleInfo) bool) {
|
||||
|
@ -1148,6 +1268,11 @@ func (c *Context) updateDependencies() (errs []error) {
|
|||
func (c *Context) PrepareBuildActions(config interface{}) (deps []string, errs []error) {
|
||||
c.buildActionsReady = false
|
||||
|
||||
errs = c.runEarlyMutators(config)
|
||||
if len(errs) > 0 {
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
if !c.dependenciesReady {
|
||||
errs := c.ResolveDependencies(config)
|
||||
if len(errs) > 0 {
|
||||
|
@ -1195,6 +1320,40 @@ func (c *Context) PrepareBuildActions(config interface{}) (deps []string, errs [
|
|||
return deps, nil
|
||||
}
|
||||
|
||||
func (c *Context) runEarlyMutators(config interface{}) (errs []error) {
|
||||
for _, mutator := range c.earlyMutatorInfo {
|
||||
for _, group := range c.moduleGroups {
|
||||
newModules := make([]*moduleInfo, 0, len(group.modules))
|
||||
|
||||
for _, module := range group.modules {
|
||||
mctx := &mutatorContext{
|
||||
baseModuleContext: baseModuleContext{
|
||||
context: c,
|
||||
config: config,
|
||||
module: module,
|
||||
},
|
||||
name: mutator.name,
|
||||
}
|
||||
mutator.mutator(mctx)
|
||||
if len(mctx.errs) > 0 {
|
||||
errs = append(errs, mctx.errs...)
|
||||
return errs
|
||||
}
|
||||
|
||||
if module.splitModules != nil {
|
||||
newModules = append(newModules, module.splitModules...)
|
||||
} else {
|
||||
newModules = append(newModules, module)
|
||||
}
|
||||
}
|
||||
|
||||
group.modules = newModules
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Context) runMutators(config interface{}) (errs []error) {
|
||||
for _, mutator := range c.mutatorInfo {
|
||||
if mutator.topDownMutator != nil {
|
||||
|
@ -2037,8 +2196,8 @@ func (s moduleSorter) Less(i, j int) bool {
|
|||
iName := s[i].properties.Name
|
||||
jName := s[j].properties.Name
|
||||
if iName == jName {
|
||||
iName = s[i].subName()
|
||||
jName = s[j].subName()
|
||||
iName = s[i].variantName
|
||||
jName = s[j].variantName
|
||||
}
|
||||
return iName < jName
|
||||
}
|
||||
|
@ -2082,7 +2241,7 @@ func (c *Context) writeAllModuleActions(nw *ninjaWriter) error {
|
|||
"typeName": module.typeName,
|
||||
"goFactory": factoryName,
|
||||
"pos": relPos,
|
||||
"variant": module.subName(),
|
||||
"variant": module.variantName,
|
||||
}
|
||||
err = headerTemplate.Execute(buf, infoMap)
|
||||
if err != nil {
|
||||
|
@ -2235,6 +2394,23 @@ func (c *Context) writeLocalBuildActions(nw *ninjaWriter,
|
|||
return nil
|
||||
}
|
||||
|
||||
func beforeInModuleList(a, b *moduleInfo, list []*moduleInfo) bool {
|
||||
found := false
|
||||
for _, l := range list {
|
||||
if l == a {
|
||||
found = true
|
||||
} else if l == b {
|
||||
return found
|
||||
}
|
||||
}
|
||||
|
||||
missing := a
|
||||
if found {
|
||||
missing = b
|
||||
}
|
||||
panic(fmt.Errorf("element %v not found in list %v", missing, list))
|
||||
}
|
||||
|
||||
var fileHeaderTemplate = `******************************************************************************
|
||||
*** This file is generated and should not be edited ***
|
||||
******************************************************************************
|
||||
|
|
|
@ -118,6 +118,8 @@ type BaseModuleContext interface {
|
|||
|
||||
type DynamicDependerModuleContext interface {
|
||||
BaseModuleContext
|
||||
|
||||
AddVariantDependencies([]Variant, ...string)
|
||||
}
|
||||
|
||||
type ModuleContext interface {
|
||||
|
@ -307,6 +309,25 @@ func (m *moduleContext) VisitAllModuleVariants(visit func(Module)) {
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// DynamicDependerModuleContext
|
||||
//
|
||||
|
||||
type dynamicDependerModuleContext struct {
|
||||
baseModuleContext
|
||||
|
||||
module *moduleInfo
|
||||
}
|
||||
|
||||
func (mctx *dynamicDependerModuleContext) AddVariantDependencies(variant []Variant, deps ...string) {
|
||||
for _, dep := range deps {
|
||||
errs := mctx.context.addVariantDependency(mctx.module, variant, dep)
|
||||
if len(errs) > 0 {
|
||||
mctx.errs = append(mctx.errs, errs...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// MutatorContext
|
||||
//
|
||||
|
@ -323,6 +344,13 @@ type baseMutatorContext interface {
|
|||
Module() Module
|
||||
}
|
||||
|
||||
type EarlyMutatorContext interface {
|
||||
baseMutatorContext
|
||||
|
||||
CreateVariants(...string) []Module
|
||||
CreateLocalVariants(...string) []Module
|
||||
}
|
||||
|
||||
type TopDownMutatorContext interface {
|
||||
baseMutatorContext
|
||||
|
||||
|
@ -351,6 +379,7 @@ type BottomUpMutatorContext interface {
|
|||
// if a second Mutator chooses to split the module a second time.
|
||||
type TopDownMutator func(mctx TopDownMutatorContext)
|
||||
type BottomUpMutator func(mctx BottomUpMutatorContext)
|
||||
type EarlyMutator func(mctx EarlyMutatorContext)
|
||||
|
||||
// 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
|
||||
|
@ -364,14 +393,32 @@ type BottomUpMutator func(mctx BottomUpMutatorContext)
|
|||
// 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 {
|
||||
return mctx.createVariants(variantNames, false)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Local variants do not affect automatic dependency resolution - dependencies added
|
||||
// to the split module via deps or DynamicDependerModule must exactly match a variant
|
||||
// that contains all the non-local variants.
|
||||
func (mctx *mutatorContext) CreateLocalVariants(variantNames ...string) []Module {
|
||||
return mctx.createVariants(variantNames, true)
|
||||
}
|
||||
|
||||
func (mctx *mutatorContext) createVariants(variantNames []string, local bool) []Module {
|
||||
ret := []Module{}
|
||||
modules, errs := mctx.context.createVariants(mctx.module, mctx.name, variantNames)
|
||||
if len(errs) > 0 {
|
||||
mctx.errs = append(mctx.errs, errs...)
|
||||
}
|
||||
|
||||
for _, module := range modules {
|
||||
for i, module := range modules {
|
||||
ret = append(ret, module.logicModule)
|
||||
if !local {
|
||||
module.dependencyVariants[mctx.name] = variantNames[i]
|
||||
}
|
||||
}
|
||||
|
||||
if len(ret) != len(variantNames) {
|
||||
|
@ -396,7 +443,10 @@ func (mctx *mutatorContext) Module() Module {
|
|||
// 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)
|
||||
errs := mctx.context.addDependency(mctx.context.moduleInfo[module], depName)
|
||||
if len(errs) > 0 {
|
||||
mctx.errs = append(mctx.errs, errs...)
|
||||
}
|
||||
mctx.dependenciesModified = true
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue