| // Copyright 2014 Google Inc. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package blueprint |
| |
| import ( |
| "fmt" |
| "path/filepath" |
| "text/scanner" |
| ) |
| |
| // A Module handles generating all of the Ninja build actions needed to build a |
| // 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 |
| // DynamicDependerModule interface) using the ModuleContext passed to |
| // GenerateBuildActions. This ModuleContext is also used to create Ninja build |
| // actions and to report errors to the user. |
| // |
| // In addition to implementing the GenerateBuildActions method, a Module should |
| // implement methods that provide dependant modules and singletons information |
| // they need to generate their build actions. These methods will only be called |
| // after GenerateBuildActions is called because the Context calls |
| // GenerateBuildActions in dependency-order (and singletons are invoked after |
| // all the Modules). The set of methods a Module supports will determine how |
| // dependant Modules interact with it. |
| // |
| // For example, consider a Module that is responsible for generating a library |
| // that other modules can link against. The library Module might implement the |
| // following interface: |
| // |
| // type LibraryProducer interface { |
| // LibraryFileName() string |
| // } |
| // |
| // func IsLibraryProducer(module blueprint.Module) { |
| // _, ok := module.(LibraryProducer) |
| // return ok |
| // } |
| // |
| // A binary-producing Module that depends on the library Module could then do: |
| // |
| // func (m *myBinaryModule) GenerateBuildActions(ctx blueprint.ModuleContext) { |
| // ... |
| // var libraryFiles []string |
| // ctx.VisitDepsDepthFirstIf(IsLibraryProducer, |
| // func(module blueprint.Module) { |
| // libProducer := module.(LibraryProducer) |
| // libraryFiles = append(libraryFiles, libProducer.LibraryFileName()) |
| // }) |
| // ... |
| // } |
| // |
| // to build the list of library file names that should be included in its link |
| // command. |
| // |
| // GenerateBuildActions may be called from multiple threads. It is guaranteed to |
| // be called after it has finished being called on all dependencies and on all |
| // variants of that appear earlier in the ModuleContext.VisitAllModuleVariants list. |
| // Any accesses to global variables or to Module objects that are not dependencies |
| // or variants of the current Module must be synchronized by the implementation of |
| // GenerateBuildActions. |
| type Module interface { |
| // GenerateBuildActions is called by the Context that created the Module |
| // during its generate phase. This call should generate all Ninja build |
| // actions (rules, pools, and build statements) needed to build the module. |
| GenerateBuildActions(ModuleContext) |
| } |
| |
| // A DynamicDependerModule is a Module that may add dependencies that do not |
| // appear in its "deps" property. Any Module that implements this interface |
| // will have its DynamicDependencies method called by the Context that created |
| // it during generate phase. |
| type DynamicDependerModule interface { |
| Module |
| |
| // DynamicDependencies is called by the Context that created the |
| // DynamicDependerModule during its generate phase. This call should return |
| // the list of module names that the DynamicDependerModule depends on |
| // dynamically. Module names that already appear in the "deps" property may |
| // but do not need to be included in the returned list. |
| DynamicDependencies(DynamicDependerModuleContext) []string |
| } |
| |
| type BaseModuleContext interface { |
| ModuleName() string |
| ModuleDir() string |
| Config() interface{} |
| |
| ContainsProperty(name string) bool |
| Errorf(pos scanner.Position, fmt string, args ...interface{}) |
| ModuleErrorf(fmt string, args ...interface{}) |
| PropertyErrorf(property, fmt string, args ...interface{}) |
| Failed() bool |
| } |
| |
| type DynamicDependerModuleContext interface { |
| BaseModuleContext |
| |
| AddVariationDependencies([]Variation, ...string) |
| } |
| |
| type ModuleContext interface { |
| BaseModuleContext |
| |
| OtherModuleName(m Module) string |
| OtherModuleErrorf(m Module, fmt string, args ...interface{}) |
| |
| VisitDirectDeps(visit func(Module)) |
| VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) |
| VisitDepsDepthFirst(visit func(Module)) |
| VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) |
| |
| 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 |
| FinalModule() Module |
| VisitAllModuleVariants(visit func(Module)) |
| } |
| |
| var _ BaseModuleContext = (*baseModuleContext)(nil) |
| |
| type baseModuleContext struct { |
| context *Context |
| config interface{} |
| module *moduleInfo |
| errs []error |
| } |
| |
| func (d *baseModuleContext) ModuleName() string { |
| return d.module.properties.Name |
| } |
| |
| func (d *baseModuleContext) ContainsProperty(name string) bool { |
| _, ok := d.module.propertyPos[name] |
| return ok |
| } |
| |
| func (d *baseModuleContext) ModuleDir() string { |
| return filepath.Dir(d.module.relBlueprintsFile) |
| } |
| |
| func (d *baseModuleContext) Config() interface{} { |
| return d.config |
| } |
| |
| func (d *baseModuleContext) Errorf(pos scanner.Position, |
| format string, args ...interface{}) { |
| |
| d.errs = append(d.errs, &Error{ |
| Err: fmt.Errorf(format, args...), |
| Pos: pos, |
| }) |
| } |
| |
| func (d *baseModuleContext) ModuleErrorf(format string, |
| args ...interface{}) { |
| |
| d.errs = append(d.errs, &Error{ |
| Err: fmt.Errorf(format, args...), |
| Pos: d.module.pos, |
| }) |
| } |
| |
| func (d *baseModuleContext) PropertyErrorf(property, format string, |
| args ...interface{}) { |
| |
| pos, ok := d.module.propertyPos[property] |
| if !ok { |
| panic(fmt.Errorf("property %q was not set for this module", property)) |
| } |
| |
| d.errs = append(d.errs, &Error{ |
| Err: fmt.Errorf(format, args...), |
| Pos: pos, |
| }) |
| } |
| |
| func (d *baseModuleContext) Failed() bool { |
| return len(d.errs) > 0 |
| } |
| |
| var _ ModuleContext = (*moduleContext)(nil) |
| |
| type moduleContext struct { |
| baseModuleContext |
| scope *localScope |
| ninjaFileDeps []string |
| actionDefs localBuildActions |
| } |
| |
| func (m *moduleContext) OtherModuleName(logicModule Module) string { |
| module := m.context.moduleInfo[logicModule] |
| return module.properties.Name |
| } |
| |
| func (m *moduleContext) OtherModuleErrorf(logicModule Module, format string, |
| args ...interface{}) { |
| |
| module := m.context.moduleInfo[logicModule] |
| m.errs = append(m.errs, &Error{ |
| Err: fmt.Errorf(format, args...), |
| Pos: module.pos, |
| }) |
| } |
| |
| func (m *moduleContext) VisitDirectDeps(visit func(Module)) { |
| m.context.visitDirectDeps(m.module, visit) |
| } |
| |
| func (m *moduleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) { |
| m.context.visitDirectDepsIf(m.module, pred, visit) |
| } |
| |
| func (m *moduleContext) VisitDepsDepthFirst(visit func(Module)) { |
| m.context.visitDepsDepthFirst(m.module, visit) |
| } |
| |
| func (m *moduleContext) VisitDepsDepthFirstIf(pred func(Module) bool, |
| visit func(Module)) { |
| |
| m.context.visitDepsDepthFirstIf(m.module, pred, visit) |
| } |
| |
| func (m *moduleContext) ModuleSubDir() string { |
| return m.module.variantName |
| } |
| |
| func (m *moduleContext) Variable(pctx *PackageContext, name, value string) { |
| m.scope.ReparentTo(pctx) |
| |
| v, err := m.scope.AddLocalVariable(name, value) |
| if err != nil { |
| panic(err) |
| } |
| |
| m.actionDefs.variables = append(m.actionDefs.variables, v) |
| } |
| |
| func (m *moduleContext) Rule(pctx *PackageContext, name string, |
| params RuleParams, argNames ...string) Rule { |
| |
| m.scope.ReparentTo(pctx) |
| |
| r, err := m.scope.AddLocalRule(name, ¶ms, argNames...) |
| if err != nil { |
| panic(err) |
| } |
| |
| m.actionDefs.rules = append(m.actionDefs.rules, r) |
| |
| return r |
| } |
| |
| func (m *moduleContext) Build(pctx *PackageContext, params BuildParams) { |
| m.scope.ReparentTo(pctx) |
| |
| def, err := parseBuildParams(m.scope, ¶ms) |
| if err != nil { |
| panic(err) |
| } |
| |
| m.actionDefs.buildDefs = append(m.actionDefs.buildDefs, def) |
| } |
| |
| func (m *moduleContext) AddNinjaFileDeps(deps ...string) { |
| m.ninjaFileDeps = append(m.ninjaFileDeps, deps...) |
| } |
| |
| func (m *moduleContext) PrimaryModule() Module { |
| return m.module.group.modules[0].logicModule |
| } |
| |
| func (m *moduleContext) FinalModule() Module { |
| return m.module.group.modules[len(m.module.group.modules)-1].logicModule |
| } |
| |
| func (m *moduleContext) VisitAllModuleVariants(visit func(Module)) { |
| for _, module := range m.module.group.modules { |
| visit(module.logicModule) |
| } |
| } |
| |
| // |
| // DynamicDependerModuleContext |
| // |
| |
| type dynamicDependerModuleContext struct { |
| baseModuleContext |
| |
| module *moduleInfo |
| } |
| |
| // AddVariationDependencies adds deps as dependencies of the current module, but uses the variations |
| // argument to select which variant of the dependency to use. A variant of the dependency must |
| // exist that matches the all of the non-local variations of the current module, plus the variations |
| // argument. |
| func (mctx *dynamicDependerModuleContext) AddVariationDependencies(variations []Variation, |
| deps ...string) { |
| |
| for _, dep := range deps { |
| errs := mctx.context.addVariationDependency(mctx.module, variations, dep) |
| if len(errs) > 0 { |
| mctx.errs = append(mctx.errs, errs...) |
| } |
| } |
| } |
| |
| // |
| // MutatorContext |
| // |
| |
| type mutatorContext struct { |
| baseModuleContext |
| name string |
| dependenciesModified bool |
| } |
| |
| type baseMutatorContext interface { |
| BaseModuleContext |
| |
| Module() Module |
| } |
| |
| type EarlyMutatorContext interface { |
| baseMutatorContext |
| |
| CreateVariations(...string) []Module |
| CreateLocalVariations(...string) []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) |
| CreateVariations(...string) []Module |
| SetDependencyVariation(string) |
| } |
| |
| // A Mutator function is called for each Module, and can use |
| // MutatorContext.CreateVariations 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) |
| type EarlyMutator func(mctx EarlyMutatorContext) |
| |
| // Split a module into mulitple variants, one for each name in the variationNames |
| // parameter. It returns a list of new modules in the same order as the variationNames |
| // list. |
| // |
| // If any of the dependencies of the module being operated on were already split |
| // by calling CreateVariations 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) CreateVariations(variationNames ...string) []Module { |
| return mctx.createVariations(variationNames, 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 variations 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 variations. |
| func (mctx *mutatorContext) CreateLocalVariations(variationNames ...string) []Module { |
| return mctx.createVariations(variationNames, true) |
| } |
| |
| func (mctx *mutatorContext) createVariations(variationNames []string, local bool) []Module { |
| ret := []Module{} |
| modules, errs := mctx.context.createVariations(mctx.module, mctx.name, variationNames) |
| if len(errs) > 0 { |
| mctx.errs = append(mctx.errs, errs...) |
| } |
| |
| for i, module := range modules { |
| ret = append(ret, module.logicModule) |
| if !local { |
| module.dependencyVariant[mctx.name] = variationNames[i] |
| } |
| } |
| |
| if len(ret) != len(variationNames) { |
| panic("oops!") |
| } |
| |
| return ret |
| } |
| |
| // Set all dangling dependencies on the current module to point to the variation |
| // with given name. |
| func (mctx *mutatorContext) SetDependencyVariation(variationName string) { |
| mctx.context.convertDepsToVariation(mctx.module, mctx.name, variationName) |
| } |
| |
| 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 has no variations. |
| // 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) { |
| errs := mctx.context.addDependency(mctx.context.moduleInfo[module], depName) |
| if len(errs) > 0 { |
| mctx.errs = append(mctx.errs, errs...) |
| } |
| 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) |
| } |