From af4fd215ebbe3c3b93fccd24f82943ff8c4596ca Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 28 Jul 2017 14:32:36 -0700 Subject: [PATCH] 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 --- context.go | 80 ++++++++++++++++++++++++++++++------------------- context_test.go | 79 ++++++++++++++++++++++++++++++++++++++++++++++++ module_ctx.go | 21 ++++++++++++- 3 files changed, 148 insertions(+), 32 deletions(-) diff --git a/context.go b/context.go index 93268d6..2ccc6fd 100644 --- a/context.go +++ b/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{}{ diff --git a/context_test.go b/context_test.go index b05c541..6070c99 100644 --- a/context_test.go +++ b/context_test.go @@ -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", + }) +} diff --git a/module_ctx.go b/module_ctx.go index 9a3a1d3..7e9a436 100644 --- a/module_ctx.go +++ b/module_ctx.go @@ -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.