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:
parent
05b3607c37
commit
af4fd215eb
3 changed files with 148 additions and 32 deletions
80
context.go
80
context.go
|
@ -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{}{
|
||||
|
|
|
@ -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",
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue