Add TopDownMutatorContext.CreateModule

Allow a module to create other modules as if they were specified
in a blueprint file.  CreateModule takes the name of a module type,
calls the factory function the module type, and then inserts
properties into the property structs returned by the factory.

Bug: 64161749
Test: TestCreateModule
Change-Id: Ic1903305d6b08eae1edc7f0fb4137e85544aac0f
This commit is contained in:
Colin Cross 2017-07-28 14:32:36 -07:00
parent 05b3607c37
commit af4fd215eb
3 changed files with 148 additions and 32 deletions

View file

@ -157,6 +157,7 @@ type moduleGroup struct {
type moduleInfo struct {
// set during Parse
typeName string
factory ModuleFactory
relBlueprintsFile string
pos scanner.Position
propertyPos map[string]scanner.Position
@ -260,12 +261,8 @@ type mutatorInfo struct {
parallel bool
}
// NewContext creates a new Context object. The created context initially has
// no module or singleton factories registered, so the RegisterModuleFactory and
// RegisterSingletonFactory methods must be called before it can do anything
// useful.
func NewContext() *Context {
ctx := &Context{
func newContext() *Context {
return &Context{
moduleFactories: make(map[string]ModuleFactory),
moduleNames: make(map[string]*moduleGroup),
moduleInfo: make(map[Module]*moduleInfo),
@ -273,6 +270,14 @@ func NewContext() *Context {
globs: make(map[string]GlobPath),
fs: pathtools.OsFs,
}
}
// NewContext creates a new Context object. The created context initially has
// no module or singleton factories registered, so the RegisterModuleFactory and
// RegisterSingletonFactory methods must be called before it can do anything
// useful.
func NewContext() *Context {
ctx := newContext()
ctx.RegisterBottomUpMutator("blueprint_deps", blueprintDepsMutator)
@ -940,13 +945,7 @@ func getStringFromScope(scope *parser.Scope, v string) (string, scanner.Position
// property values. Any values stored in the module object that are not stored in properties
// structs will be lost.
func (c *Context) cloneLogicModule(origModule *moduleInfo) (Module, []interface{}) {
typeName := origModule.typeName
factory, ok := c.moduleFactories[typeName]
if !ok {
panic(fmt.Sprintf("unrecognized module type %q during cloning", typeName))
}
newLogicModule, newProperties := factory()
newLogicModule, newProperties := origModule.factory()
if len(newProperties) != len(origModule.properties) {
panic("mismatched properties array length in " + origModule.Name())
@ -1062,6 +1061,19 @@ func (c *Context) prettyPrintVariant(variant variationMap) string {
return strings.Join(names, ", ")
}
func (c *Context) newModule(factory ModuleFactory) *moduleInfo {
logicModule, properties := factory()
module := &moduleInfo{
logicModule: logicModule,
factory: factory,
}
module.properties = properties
return module
}
func (c *Context) processModuleDef(moduleDef *parser.Module,
relBlueprintsFile string) (*moduleInfo, []error) {
@ -1079,17 +1091,12 @@ func (c *Context) processModuleDef(moduleDef *parser.Module,
}
}
logicModule, properties := factory()
module := c.newModule(factory)
module.typeName = moduleDef.Type
module := &moduleInfo{
logicModule: logicModule,
typeName: moduleDef.Type,
relBlueprintsFile: relBlueprintsFile,
}
module.relBlueprintsFile = relBlueprintsFile
module.properties = properties
propertyMap, errs := unpackProperties(moduleDef.Properties, properties...)
propertyMap, errs := unpackProperties(moduleDef.Properties, module.properties...)
if len(errs) > 0 {
return nil, errs
}
@ -1744,14 +1751,16 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo,
}
type globalStateChange struct {
reverse []reverseDep
rename []rename
replace []replace
reverse []reverseDep
rename []rename
replace []replace
newModules []*moduleInfo
}
reverseDeps := make(map[*moduleInfo][]depInfo)
var rename []rename
var replace []replace
var newModules []*moduleInfo
errsCh := make(chan []error)
globalStateCh := make(chan globalStateChange)
@ -1798,11 +1807,12 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo,
newVariationsCh <- mctx.newVariations
}
if len(mctx.reverseDeps) > 0 || len(mctx.replace) > 0 || len(mctx.rename) > 0 {
if len(mctx.reverseDeps) > 0 || len(mctx.replace) > 0 || len(mctx.rename) > 0 || len(mctx.newModules) > 0 {
globalStateCh <- globalStateChange{
reverse: mctx.reverseDeps,
replace: mctx.replace,
rename: mctx.rename,
reverse: mctx.reverseDeps,
replace: mctx.replace,
rename: mctx.rename,
newModules: mctx.newModules,
}
}
@ -1821,6 +1831,7 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo,
}
replace = append(replace, globalStateChange.replace...)
rename = append(rename, globalStateChange.rename...)
newModules = append(newModules, globalStateChange.newModules...)
case newVariations := <-newVariationsCh:
for _, m := range newVariations {
newModuleInfo[m.logicModule] = m
@ -1870,6 +1881,14 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo,
c.depsModified++
}
for _, module := range newModules {
errs = c.addModule(module)
if len(errs) > 0 {
return errs
}
atomic.AddUint32(&c.depsModified, 1)
}
errs = c.handleRenames(rename)
if len(errs) > 0 {
return errs
@ -3007,8 +3026,7 @@ func (c *Context) writeAllModuleActions(nw *ninjaWriter) error {
relPos.Filename = module.relBlueprintsFile
// Get the name and location of the factory function for the module.
factory := c.moduleFactories[module.typeName]
factoryFunc := runtime.FuncForPC(reflect.ValueOf(factory).Pointer())
factoryFunc := runtime.FuncForPC(reflect.ValueOf(module.factory).Pointer())
factoryName := factoryFunc.Name()
infoMap := map[string]interface{}{

View file

@ -16,6 +16,7 @@ package blueprint
import (
"bytes"
"strings"
"testing"
)
@ -201,3 +202,81 @@ func TestWalkDeps(t *testing.T) {
t.Fatalf("unexpected walkDeps behaviour: %s\nup should be: GFC", outputUp)
}
}
func TestCreateModule(t *testing.T) {
ctx := newContext()
ctx.MockFileSystem(map[string][]byte{
"Blueprints": []byte(`
foo_module {
name: "A",
deps: ["B", "C"],
}
`),
})
ctx.RegisterTopDownMutator("create", createTestMutator)
ctx.RegisterBottomUpMutator("deps", blueprintDepsMutator)
ctx.RegisterModuleType("foo_module", newFooModule)
ctx.RegisterModuleType("bar_module", newBarModule)
_, errs := ctx.ParseBlueprintsFiles("Blueprints")
if len(errs) > 0 {
t.Errorf("unexpected parse errors:")
for _, err := range errs {
t.Errorf(" %s", err)
}
t.FailNow()
}
errs = ctx.ResolveDependencies(nil)
if len(errs) > 0 {
t.Errorf("unexpected dep errors:")
for _, err := range errs {
t.Errorf(" %s", err)
}
t.FailNow()
}
a := ctx.modulesFromName("A")[0].logicModule.(*fooModule)
b := ctx.modulesFromName("B")[0].logicModule.(*barModule)
c := ctx.modulesFromName("C")[0].logicModule.(*barModule)
d := ctx.modulesFromName("D")[0].logicModule.(*fooModule)
checkDeps := func(m Module, expected string) {
var deps []string
ctx.VisitDirectDeps(m, func(m Module) {
deps = append(deps, ctx.ModuleName(m))
})
got := strings.Join(deps, ",")
if got != expected {
t.Errorf("unexpected %q dependencies, got %q expected %q",
ctx.ModuleName(m), got, expected)
}
}
checkDeps(a, "B,C")
checkDeps(b, "D")
checkDeps(c, "D")
checkDeps(d, "")
}
func createTestMutator(ctx TopDownMutatorContext) {
type props struct {
Name string
Deps []string
}
ctx.CreateModule(newBarModule, &props{
Name: "B",
Deps: []string{"D"},
})
ctx.CreateModule(newBarModule, &props{
Name: "C",
Deps: []string{"D"},
})
ctx.CreateModule(newFooModule, &props{
Name: "D",
})
}

View file

@ -20,6 +20,7 @@ import (
"text/scanner"
"github.com/google/blueprint/pathtools"
"github.com/google/blueprint/proptools"
)
// A Module handles generating all of the Ninja build actions needed to build a
@ -502,7 +503,8 @@ type mutatorContext struct {
reverseDeps []reverseDep
rename []rename
replace []replace
newVariations []*moduleInfo
newVariations []*moduleInfo // new variants of existing modules
newModules []*moduleInfo // brand new modules
}
type baseMutatorContext interface {
@ -527,6 +529,8 @@ type TopDownMutatorContext interface {
OtherModuleErrorf(m Module, fmt string, args ...interface{})
OtherModuleDependencyTag(m Module) DependencyTag
CreateModule(ModuleFactory, ...interface{})
GetDirectDepWithTag(name string, tag DependencyTag) Module
GetDirectDep(name string) (Module, DependencyTag)
@ -737,6 +741,21 @@ func (mctx *mutatorContext) Rename(name string) {
mctx.rename = append(mctx.rename, rename{mctx.module.group, name})
}
// Create a new module by calling the factory method for the specified moduleType, and apply
// the specified property structs to it as if the properties were set in a blueprint file.
func (mctx *mutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) {
module := mctx.context.newModule(factory)
for _, p := range props {
err := proptools.AppendMatchingProperties(module.properties, p, nil)
if err != nil {
panic(err)
}
}
mctx.newModules = append(mctx.newModules, module)
}
// SimpleName is an embeddable object to implement the ModuleContext.Name method using a property
// called "name". Modules that embed it must also add SimpleName.Properties to their property
// structure list.