| // 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" |
| "strings" |
| "sync" |
| "text/scanner" |
| |
| "github.com/google/blueprint/parser" |
| "github.com/google/blueprint/pathtools" |
| "github.com/google/blueprint/proptools" |
| ) |
| |
| // 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, dynamically added by implementing the |
| // (deprecated) DynamicDependerModule interface, or dynamically added by a |
| // BottomUpMutator) 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 { |
| // Name returns a string used to uniquely identify each module. The return |
| // value must be unique across all modules. It is only called once, during |
| // initial blueprint parsing. To change the name later a mutator must call |
| // MutatorContext.Rename |
| // |
| // In most cases, Name should return the contents of a "name:" property from |
| // the blueprint file. An embeddable SimpleName object can be used for this |
| // case. |
| Name() string |
| |
| // 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. |
| // |
| // Deprecated, use a BottomUpMutator instead |
| 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 EarlyModuleContext interface { |
| // Module returns the current module as a Module. It should rarely be necessary, as the module already has a |
| // reference to itself. |
| Module() Module |
| |
| // ModuleName returns the name of the module. This is generally the value that was returned by Module.Name() when |
| // the module was created, but may have been modified by calls to BaseMutatorContext.Rename. |
| ModuleName() string |
| |
| // ModuleDir returns the path to the directory that contains the definition of the module. |
| ModuleDir() string |
| |
| // ModuleType returns the name of the module type that was used to create the module, as specified in |
| // Context.RegisterModuleType(). |
| ModuleType() string |
| |
| // BlueprintsFile returns the name of the blueprint file that contains the definition of this |
| // module. |
| BlueprintsFile() string |
| |
| // Config returns the config object that was passed to Context.PrepareBuildActions. |
| Config() interface{} |
| |
| // ContainsProperty returns true if the specified property name was set in the module definition. |
| ContainsProperty(name string) bool |
| |
| // Errorf reports an error at the specified position of the module definition file. |
| Errorf(pos scanner.Position, fmt string, args ...interface{}) |
| |
| // ModuleErrorf reports an error at the line number of the module type in the module definition. |
| ModuleErrorf(fmt string, args ...interface{}) |
| |
| // PropertyErrorf reports an error at the line number of a property in the module definition. |
| PropertyErrorf(property, fmt string, args ...interface{}) |
| |
| // Failed returns true if any errors have been reported. In most cases the module can continue with generating |
| // build rules after an error, allowing it to report additional errors in a single run, but in cases where the error |
| // has prevented the module from creating necessary data it can return early when Failed returns true. |
| Failed() bool |
| |
| // GlobWithDeps returns a list of files and directories that match the |
| // specified pattern but do not match any of the patterns in excludes. |
| // Any directories will have a '/' suffix. It also adds efficient |
| // dependencies to rerun the primary builder whenever a file matching |
| // the pattern as added or removed, without rerunning if a file that |
| // does not match the pattern is added to a searched directory. |
| GlobWithDeps(pattern string, excludes []string) ([]string, error) |
| |
| // Fs returns a pathtools.Filesystem that can be used to interact with files. Using the Filesystem interface allows |
| // the module to be used in build system tests that run against a mock filesystem. |
| Fs() pathtools.FileSystem |
| |
| // AddNinjaFileDeps adds dependencies on the specified files to the rule that creates the ninja manifest. The |
| // primary builder will be rerun whenever the specified files are modified. |
| AddNinjaFileDeps(deps ...string) |
| |
| moduleInfo() *moduleInfo |
| error(err error) |
| |
| // Namespace returns the Namespace object provided by the NameInterface set by Context.SetNameInterface, or the |
| // default SimpleNameInterface if Context.SetNameInterface was not called. |
| Namespace() Namespace |
| |
| // ModuleFactories returns a map of all of the global ModuleFactories by name. |
| ModuleFactories() map[string]ModuleFactory |
| } |
| |
| type BaseModuleContext interface { |
| EarlyModuleContext |
| |
| // GetDirectDepWithTag returns the Module the direct dependency with the specified name, or nil if |
| // none exists. It panics if the dependency does not have the specified tag. |
| GetDirectDepWithTag(name string, tag DependencyTag) Module |
| |
| // GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified |
| // name, or nil if none exists. If there are multiple dependencies on the same module it returns |
| // the first DependencyTag. |
| GetDirectDep(name string) (Module, DependencyTag) |
| |
| // VisitDirectDeps calls visit for each direct dependency. If there are multiple direct dependencies on the same |
| // module visit will be called multiple times on that module and OtherModuleDependencyTag will return a different |
| // tag for each. |
| // |
| // The Module passed to the visit function should not be retained outside of the visit function, it may be |
| // invalidated by future mutators. |
| VisitDirectDeps(visit func(Module)) |
| |
| // VisitDirectDepsIf calls pred for each direct dependency, and if pred returns true calls visit. If there are |
| // multiple direct dependencies on the same module pred and visit will be called multiple times on that module and |
| // OtherModuleDependencyTag will return a different tag for each. |
| // |
| // The Module passed to the visit function should not be retained outside of the visit function, it may be |
| // invalidated by future mutators. |
| VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) |
| |
| // VisitDepsDepthFirst calls visit for each transitive dependency, traversing the dependency tree in depth first |
| // order. visit will only be called once for any given module, even if there are multiple paths through the |
| // dependency tree to the module or multiple direct dependencies with different tags. OtherModuleDependencyTag will |
| // return the tag for the first path found to the module. |
| // |
| // The Module passed to the visit function should not be retained outside of the visit function, it may be |
| // invalidated by future mutators. |
| VisitDepsDepthFirst(visit func(Module)) |
| |
| // VisitDepsDepthFirstIf calls pred for each transitive dependency, and if pred returns true calls visit, traversing |
| // the dependency tree in depth first order. visit will only be called once for any given module, even if there are |
| // multiple paths through the dependency tree to the module or multiple direct dependencies with different tags. |
| // OtherModuleDependencyTag will return the tag for the first path found to the module. The return value of pred |
| // does not affect which branches of the tree are traversed. |
| // |
| // The Module passed to the visit function should not be retained outside of the visit function, it may be |
| // invalidated by future mutators. |
| VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) |
| |
| // WalkDeps calls visit for each transitive dependency, traversing the dependency tree in top down order. visit may |
| // be called multiple times for the same (child, parent) pair if there are multiple direct dependencies between the |
| // child and parent with different tags. OtherModuleDependencyTag will return the tag for the currently visited |
| // (child, parent) pair. If visit returns false WalkDeps will not continue recursing down to child. |
| // |
| // The Modules passed to the visit function should not be retained outside of the visit function, they may be |
| // invalidated by future mutators. |
| WalkDeps(visit func(Module, Module) bool) |
| |
| // PrimaryModule returns the first variant of the current module. Variants of a module are always visited in |
| // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from the |
| // Module returned by PrimaryModule without data races. This can be used to perform singleton actions that are |
| // only done once for all variants of a module. |
| PrimaryModule() Module |
| |
| // FinalModule returns the last variant of the current module. Variants of a module are always visited in |
| // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from all |
| // variants using VisitAllModuleVariants if the current module == FinalModule(). This can be used to perform |
| // singleton actions that are only done once for all variants of a module. |
| FinalModule() Module |
| |
| // VisitAllModuleVariants calls visit for each variant of the current module. Variants of a module are always |
| // visited in order by mutators and GenerateBuildActions, so the data created by the current mutator can be read |
| // from all variants if the current module == FinalModule(). Otherwise, care must be taken to not access any |
| // data modified by the current mutator. |
| VisitAllModuleVariants(visit func(Module)) |
| |
| // OtherModuleName returns the name of another Module. See BaseModuleContext.ModuleName for more information. |
| // It is intended for use inside the visit functions of Visit* and WalkDeps. |
| OtherModuleName(m Module) string |
| |
| // OtherModuleDir returns the directory of another Module. See BaseModuleContext.ModuleDir for more information. |
| // It is intended for use inside the visit functions of Visit* and WalkDeps. |
| OtherModuleDir(m Module) string |
| |
| // OtherModuleSubDir returns the unique subdirectory name of another Module. See ModuleContext.ModuleSubDir for |
| // more information. |
| // It is intended for use inside the visit functions of Visit* and WalkDeps. |
| OtherModuleSubDir(m Module) string |
| |
| // OtherModuleType returns the type of another Module. See BaseModuleContext.ModuleType for more information. |
| // It is intended for use inside the visit functions of Visit* and WalkDeps. |
| OtherModuleType(m Module) string |
| |
| // OtherModuleErrorf reports an error on another Module. See BaseModuleContext.ModuleErrorf for more information. |
| // It is intended for use inside the visit functions of Visit* and WalkDeps. |
| OtherModuleErrorf(m Module, fmt string, args ...interface{}) |
| |
| // OtherModuleDependencyTag returns the dependency tag used to depend on a module, or nil if there is no dependency |
| // on the module. When called inside a Visit* method with current module being visited, and there are multiple |
| // dependencies on the module being visited, it returns the dependency tag used for the current dependency. |
| OtherModuleDependencyTag(m Module) DependencyTag |
| |
| // OtherModuleExists returns true if a module with the specified name exists, as determined by the NameInterface |
| // passed to Context.SetNameInterface, or SimpleNameInterface if it was not called. |
| OtherModuleExists(name string) bool |
| |
| // ModuleFromName returns (module, true) if a module exists by the given name and same context namespace, |
| // or (nil, false) if it does not exist. It panics if there is either more than one |
| // module of the given name, or if the given name refers to an alias instead of a module. |
| // There are no guarantees about which variant of the module will be returned. |
| // Prefer retrieving the module using GetDirectDep or a visit function, when possible, as |
| // this will guarantee the appropriate module-variant dependency is returned. |
| ModuleFromName(name string) (Module, bool) |
| |
| // OtherModuleDependencyVariantExists returns true if a module with the |
| // specified name and variant exists. The variant must match the given |
| // variations. It must also match all the non-local variations of the current |
| // module. In other words, it checks for the module that AddVariationDependencies |
| // would add a dependency on with the same arguments. |
| OtherModuleDependencyVariantExists(variations []Variation, name string) bool |
| |
| // OtherModuleFarDependencyVariantExists returns true if a module with the |
| // specified name and variant exists. The variant must match the given |
| // variations, but not the non-local variations of the current module. In |
| // other words, it checks for the module that AddFarVariationDependencies |
| // would add a dependency on with the same arguments. |
| OtherModuleFarDependencyVariantExists(variations []Variation, name string) bool |
| |
| // OtherModuleReverseDependencyVariantExists returns true if a module with the |
| // specified name exists with the same variations as the current module. In |
| // other words, it checks for the module that AddReverseDependency would add a |
| // dependency on with the same argument. |
| OtherModuleReverseDependencyVariantExists(name string) bool |
| |
| // OtherModuleProvider returns the value for a provider for the given module. If the value is |
| // not set it returns the zero value of the type of the provider, so the return value can always |
| // be type asserted to the type of the provider. The value returned may be a deep copy of the |
| // value originally passed to SetProvider. |
| OtherModuleProvider(m Module, provider ProviderKey) interface{} |
| |
| // OtherModuleHasProvider returns true if the provider for the given module has been set. |
| OtherModuleHasProvider(m Module, provider ProviderKey) bool |
| |
| // Provider returns the value for a provider for the current module. If the value is |
| // not set it returns the zero value of the type of the provider, so the return value can always |
| // be type asserted to the type of the provider. It panics if called before the appropriate |
| // mutator or GenerateBuildActions pass for the provider. The value returned may be a deep |
| // copy of the value originally passed to SetProvider. |
| Provider(provider ProviderKey) interface{} |
| |
| // HasProvider returns true if the provider for the current module has been set. |
| HasProvider(provider ProviderKey) bool |
| |
| // SetProvider sets the value for a provider for the current module. It panics if not called |
| // during the appropriate mutator or GenerateBuildActions pass for the provider, if the value |
| // is not of the appropriate type, or if the value has already been set. The value should not |
| // be modified after being passed to SetProvider. |
| SetProvider(provider ProviderKey, value interface{}) |
| } |
| |
| type DynamicDependerModuleContext BottomUpMutatorContext |
| |
| type ModuleContext interface { |
| BaseModuleContext |
| |
| // ModuleSubDir returns a unique name for the current variant of a module that can be used as part of the path |
| // to ensure that each variant of a module gets its own intermediates directory to write to. |
| ModuleSubDir() string |
| |
| // Variable creates a new ninja variable scoped to the module. It can be referenced by calls to Rule and Build |
| // in the same module. |
| Variable(pctx PackageContext, name, value string) |
| |
| // Rule creates a new ninja rule scoped to the module. It can be referenced by calls to Build in the same module. |
| Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule |
| |
| // Build creates a new ninja build statement. |
| Build(pctx PackageContext, params BuildParams) |
| |
| // GetMissingDependencies returns the list of dependencies that were passed to AddDependencies or related methods, |
| // but do not exist. It can be used with Context.SetAllowMissingDependencies to allow the primary builder to |
| // handle missing dependencies on its own instead of having Blueprint treat them as an error. |
| GetMissingDependencies() []string |
| } |
| |
| var _ BaseModuleContext = (*baseModuleContext)(nil) |
| |
| type baseModuleContext struct { |
| context *Context |
| config interface{} |
| module *moduleInfo |
| errs []error |
| visitingParent *moduleInfo |
| visitingDep depInfo |
| ninjaFileDeps []string |
| } |
| |
| func (d *baseModuleContext) moduleInfo() *moduleInfo { |
| return d.module |
| } |
| |
| func (d *baseModuleContext) Module() Module { |
| return d.module.logicModule |
| } |
| |
| func (d *baseModuleContext) ModuleName() string { |
| return d.module.Name() |
| } |
| |
| func (d *baseModuleContext) ModuleType() string { |
| return d.module.typeName |
| } |
| |
| 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) BlueprintsFile() string { |
| return d.module.relBlueprintsFile |
| } |
| |
| func (d *baseModuleContext) Config() interface{} { |
| return d.config |
| } |
| |
| func (d *baseModuleContext) error(err error) { |
| if err != nil { |
| d.errs = append(d.errs, err) |
| } |
| } |
| |
| func (d *baseModuleContext) Errorf(pos scanner.Position, |
| format string, args ...interface{}) { |
| |
| d.error(&BlueprintError{ |
| Err: fmt.Errorf(format, args...), |
| Pos: pos, |
| }) |
| } |
| |
| func (d *baseModuleContext) ModuleErrorf(format string, |
| args ...interface{}) { |
| |
| d.error(&ModuleError{ |
| BlueprintError: BlueprintError{ |
| Err: fmt.Errorf(format, args...), |
| Pos: d.module.pos, |
| }, |
| module: d.module, |
| }) |
| } |
| |
| func (d *baseModuleContext) PropertyErrorf(property, format string, |
| args ...interface{}) { |
| |
| pos := d.module.propertyPos[property] |
| |
| if !pos.IsValid() { |
| pos = d.module.pos |
| } |
| |
| d.error(&PropertyError{ |
| ModuleError: ModuleError{ |
| BlueprintError: BlueprintError{ |
| Err: fmt.Errorf(format, args...), |
| Pos: pos, |
| }, |
| module: d.module, |
| }, |
| property: property, |
| }) |
| } |
| |
| func (d *baseModuleContext) Failed() bool { |
| return len(d.errs) > 0 |
| } |
| |
| func (d *baseModuleContext) GlobWithDeps(pattern string, |
| excludes []string) ([]string, error) { |
| return d.context.glob(pattern, excludes) |
| } |
| |
| func (d *baseModuleContext) Fs() pathtools.FileSystem { |
| return d.context.fs |
| } |
| |
| func (d *baseModuleContext) Namespace() Namespace { |
| return d.context.nameInterface.GetNamespace(newNamespaceContext(d.module)) |
| } |
| |
| var _ ModuleContext = (*moduleContext)(nil) |
| |
| type moduleContext struct { |
| baseModuleContext |
| scope *localScope |
| actionDefs localBuildActions |
| handledMissingDeps bool |
| } |
| |
| func (m *baseModuleContext) OtherModuleName(logicModule Module) string { |
| module := m.context.moduleInfo[logicModule] |
| return module.Name() |
| } |
| |
| func (m *baseModuleContext) OtherModuleDir(logicModule Module) string { |
| module := m.context.moduleInfo[logicModule] |
| return filepath.Dir(module.relBlueprintsFile) |
| } |
| |
| func (m *baseModuleContext) OtherModuleSubDir(logicModule Module) string { |
| module := m.context.moduleInfo[logicModule] |
| return module.variant.name |
| } |
| |
| func (m *baseModuleContext) OtherModuleType(logicModule Module) string { |
| module := m.context.moduleInfo[logicModule] |
| return module.typeName |
| } |
| |
| func (m *baseModuleContext) OtherModuleErrorf(logicModule Module, format string, |
| args ...interface{}) { |
| |
| module := m.context.moduleInfo[logicModule] |
| m.errs = append(m.errs, &ModuleError{ |
| BlueprintError: BlueprintError{ |
| Err: fmt.Errorf(format, args...), |
| Pos: module.pos, |
| }, |
| module: module, |
| }) |
| } |
| |
| func (m *baseModuleContext) OtherModuleDependencyTag(logicModule Module) DependencyTag { |
| // fast path for calling OtherModuleDependencyTag from inside VisitDirectDeps |
| if logicModule == m.visitingDep.module.logicModule { |
| return m.visitingDep.tag |
| } |
| |
| for _, dep := range m.visitingParent.directDeps { |
| if dep.module.logicModule == logicModule { |
| return dep.tag |
| } |
| } |
| |
| return nil |
| } |
| |
| func (m *baseModuleContext) ModuleFromName(name string) (Module, bool) { |
| moduleGroup, exists := m.context.nameInterface.ModuleFromName(name, m.module.namespace()) |
| if exists { |
| if len(moduleGroup.modules) != 1 { |
| panic(fmt.Errorf("Expected exactly one module named %q, but got %d", name, len(moduleGroup.modules))) |
| } |
| moduleInfo := moduleGroup.modules[0].module() |
| if moduleInfo != nil { |
| return moduleInfo.logicModule, true |
| } else { |
| panic(fmt.Errorf(`Expected actual module named %q, but group did not contain a module. |
| There may instead be an alias by that name.`, name)) |
| } |
| } |
| return nil, exists |
| } |
| |
| func (m *baseModuleContext) OtherModuleExists(name string) bool { |
| _, exists := m.context.nameInterface.ModuleFromName(name, m.module.namespace()) |
| return exists |
| } |
| |
| func (m *baseModuleContext) OtherModuleDependencyVariantExists(variations []Variation, name string) bool { |
| possibleDeps := m.context.moduleGroupFromName(name, m.module.namespace()) |
| if possibleDeps == nil { |
| return false |
| } |
| found, _ := findVariant(m.module, possibleDeps, variations, false, false) |
| return found != nil |
| } |
| |
| func (m *baseModuleContext) OtherModuleFarDependencyVariantExists(variations []Variation, name string) bool { |
| possibleDeps := m.context.moduleGroupFromName(name, m.module.namespace()) |
| if possibleDeps == nil { |
| return false |
| } |
| found, _ := findVariant(m.module, possibleDeps, variations, true, false) |
| return found != nil |
| } |
| |
| func (m *baseModuleContext) OtherModuleReverseDependencyVariantExists(name string) bool { |
| possibleDeps := m.context.moduleGroupFromName(name, m.module.namespace()) |
| if possibleDeps == nil { |
| return false |
| } |
| found, _ := findVariant(m.module, possibleDeps, nil, false, true) |
| return found != nil |
| } |
| |
| func (m *baseModuleContext) OtherModuleProvider(logicModule Module, provider ProviderKey) interface{} { |
| module := m.context.moduleInfo[logicModule] |
| value, _ := m.context.provider(module, provider) |
| return value |
| } |
| |
| func (m *baseModuleContext) OtherModuleHasProvider(logicModule Module, provider ProviderKey) bool { |
| module := m.context.moduleInfo[logicModule] |
| _, ok := m.context.provider(module, provider) |
| return ok |
| } |
| |
| func (m *baseModuleContext) Provider(provider ProviderKey) interface{} { |
| value, _ := m.context.provider(m.module, provider) |
| return value |
| } |
| |
| func (m *baseModuleContext) HasProvider(provider ProviderKey) bool { |
| _, ok := m.context.provider(m.module, provider) |
| return ok |
| } |
| |
| func (m *baseModuleContext) SetProvider(provider ProviderKey, value interface{}) { |
| m.context.setProvider(m.module, provider, value) |
| } |
| |
| func (m *baseModuleContext) GetDirectDep(name string) (Module, DependencyTag) { |
| for _, dep := range m.module.directDeps { |
| if dep.module.Name() == name { |
| return dep.module.logicModule, dep.tag |
| } |
| } |
| |
| return nil, nil |
| } |
| |
| func (m *baseModuleContext) GetDirectDepWithTag(name string, tag DependencyTag) Module { |
| var deps []depInfo |
| for _, dep := range m.module.directDeps { |
| if dep.module.Name() == name { |
| if dep.tag == tag { |
| return dep.module.logicModule |
| } |
| deps = append(deps, dep) |
| } |
| } |
| |
| if len(deps) != 0 { |
| panic(fmt.Errorf("Unable to find dependency %q with requested tag %#v. Found: %#v", deps[0].module, tag, deps)) |
| } |
| |
| return nil |
| } |
| |
| func (m *baseModuleContext) VisitDirectDeps(visit func(Module)) { |
| defer func() { |
| if r := recover(); r != nil { |
| panic(newPanicErrorf(r, "VisitDirectDeps(%s, %s) for dependency %s", |
| m.module, funcName(visit), m.visitingDep.module)) |
| } |
| }() |
| |
| m.visitingParent = m.module |
| |
| for _, dep := range m.module.directDeps { |
| m.visitingDep = dep |
| visit(dep.module.logicModule) |
| } |
| |
| m.visitingParent = nil |
| m.visitingDep = depInfo{} |
| } |
| |
| func (m *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) { |
| defer func() { |
| if r := recover(); r != nil { |
| panic(newPanicErrorf(r, "VisitDirectDepsIf(%s, %s, %s) for dependency %s", |
| m.module, funcName(pred), funcName(visit), m.visitingDep.module)) |
| } |
| }() |
| |
| m.visitingParent = m.module |
| |
| for _, dep := range m.module.directDeps { |
| m.visitingDep = dep |
| if pred(dep.module.logicModule) { |
| visit(dep.module.logicModule) |
| } |
| } |
| |
| m.visitingParent = nil |
| m.visitingDep = depInfo{} |
| } |
| |
| func (m *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) { |
| defer func() { |
| if r := recover(); r != nil { |
| panic(newPanicErrorf(r, "VisitDepsDepthFirst(%s, %s) for dependency %s", |
| m.module, funcName(visit), m.visitingDep.module)) |
| } |
| }() |
| |
| m.context.walkDeps(m.module, false, nil, func(dep depInfo, parent *moduleInfo) { |
| m.visitingParent = parent |
| m.visitingDep = dep |
| visit(dep.module.logicModule) |
| }) |
| |
| m.visitingParent = nil |
| m.visitingDep = depInfo{} |
| } |
| |
| func (m *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, |
| visit func(Module)) { |
| |
| defer func() { |
| if r := recover(); r != nil { |
| panic(newPanicErrorf(r, "VisitDepsDepthFirstIf(%s, %s, %s) for dependency %s", |
| m.module, funcName(pred), funcName(visit), m.visitingDep.module)) |
| } |
| }() |
| |
| m.context.walkDeps(m.module, false, nil, func(dep depInfo, parent *moduleInfo) { |
| if pred(dep.module.logicModule) { |
| m.visitingParent = parent |
| m.visitingDep = dep |
| visit(dep.module.logicModule) |
| } |
| }) |
| |
| m.visitingParent = nil |
| m.visitingDep = depInfo{} |
| } |
| |
| func (m *baseModuleContext) WalkDeps(visit func(child, parent Module) bool) { |
| m.context.walkDeps(m.module, true, func(dep depInfo, parent *moduleInfo) bool { |
| m.visitingParent = parent |
| m.visitingDep = dep |
| return visit(dep.module.logicModule, parent.logicModule) |
| }, nil) |
| |
| m.visitingParent = nil |
| m.visitingDep = depInfo{} |
| } |
| |
| func (m *baseModuleContext) PrimaryModule() Module { |
| return m.module.group.modules.firstModule().logicModule |
| } |
| |
| func (m *baseModuleContext) FinalModule() Module { |
| return m.module.group.modules.lastModule().logicModule |
| } |
| |
| func (m *baseModuleContext) VisitAllModuleVariants(visit func(Module)) { |
| m.context.visitAllModuleVariants(m.module, visit) |
| } |
| |
| func (m *baseModuleContext) AddNinjaFileDeps(deps ...string) { |
| m.ninjaFileDeps = append(m.ninjaFileDeps, deps...) |
| } |
| |
| func (m *baseModuleContext) ModuleFactories() map[string]ModuleFactory { |
| ret := make(map[string]ModuleFactory) |
| for k, v := range m.context.moduleFactories { |
| ret[k] = v |
| } |
| return ret |
| } |
| |
| func (m *moduleContext) ModuleSubDir() string { |
| return m.module.variant.name |
| } |
| |
| 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) GetMissingDependencies() []string { |
| m.handledMissingDeps = true |
| return m.module.missingDeps |
| } |
| |
| // |
| // MutatorContext |
| // |
| |
| type mutatorContext struct { |
| baseModuleContext |
| name string |
| reverseDeps []reverseDep |
| rename []rename |
| replace []replace |
| newVariations modulesOrAliases // new variants of existing modules |
| newModules []*moduleInfo // brand new modules |
| defaultVariation *string |
| pauseCh chan<- pauseSpec |
| } |
| |
| type BaseMutatorContext interface { |
| BaseModuleContext |
| |
| // Rename all variants of a module. The new name is not visible to calls to ModuleName, |
| // AddDependency or OtherModuleName until after this mutator pass is complete. |
| Rename(name string) |
| |
| // MutatorName returns the name that this mutator was registered with. |
| MutatorName() string |
| } |
| |
| type EarlyMutatorContext interface { |
| BaseMutatorContext |
| |
| // CreateVariations splits a module into multiple 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. |
| CreateVariations(...string) []Module |
| |
| // CreateLocalVariations splits a module into multiple 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. |
| CreateLocalVariations(...string) []Module |
| } |
| |
| type TopDownMutatorContext interface { |
| BaseMutatorContext |
| |
| // CreateModule creates a new module by calling the factory method for the specified moduleType, and applies |
| // the specified property structs to it as if the properties were set in a blueprint file. |
| CreateModule(ModuleFactory, ...interface{}) Module |
| } |
| |
| type BottomUpMutatorContext interface { |
| BaseMutatorContext |
| |
| // AddDependency adds a dependency to the given module. It returns a slice of modules for each |
| // dependency (some entries may be nil). Does not affect the ordering of the current mutator |
| // pass, but will be ordered correctly for all future mutator passes. |
| // |
| // If the mutator is parallel (see MutatorHandle.Parallel), this method will pause until the |
| // new dependencies have had the current mutator called on them. If the mutator is not |
| // parallel this method does not affect the ordering of the current mutator pass, but will |
| // be ordered correctly for all future mutator passes. |
| AddDependency(module Module, tag DependencyTag, name ...string) []Module |
| |
| // AddReverseDependency adds a dependency from the destination to the given module. |
| // Does not affect the ordering of the current mutator pass, but will be ordered |
| // correctly for all future mutator passes. All reverse dependencies for a destination module are |
| // collected until the end of the mutator pass, sorted by name, and then appended to the destination |
| // module's dependency list. |
| AddReverseDependency(module Module, tag DependencyTag, name string) |
| |
| // CreateVariations splits a module into multiple 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. |
| CreateVariations(variationNames ...string) []Module |
| |
| // CreateLocalVariations splits a module into multiple variants, one for each name in the variationNames |
| // 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. |
| CreateLocalVariations(variationNames ...string) []Module |
| |
| // SetDependencyVariation sets all dangling dependencies on the current module to point to the variation |
| // with given name. This function ignores the default variation set by SetDefaultDependencyVariation. |
| SetDependencyVariation(string) |
| |
| // SetDefaultDependencyVariation sets the default variation when a dangling reference is detected |
| // during the subsequent calls on Create*Variations* functions. To reset, set it to nil. |
| SetDefaultDependencyVariation(*string) |
| |
| // AddVariationDependencies adds deps as dependencies of the current module, but uses the variations |
| // argument to select which variant of the dependency to use. It returns a slice of modules for |
| // each dependency (some entries may be nil). A variant of the dependency must exist that matches |
| // the all of the non-local variations of the current module, plus the variations argument. |
| // |
| // If the mutator is parallel (see MutatorHandle.Parallel), this method will pause until the |
| // new dependencies have had the current mutator called on them. If the mutator is not |
| // parallel this method does not affect the ordering of the current mutator pass, but will |
| // be ordered correctly for all future mutator passes. |
| AddVariationDependencies([]Variation, DependencyTag, ...string) []Module |
| |
| // AddFarVariationDependencies adds deps as dependencies of the current module, but uses the |
| // variations argument to select which variant of the dependency to use. It returns a slice of |
| // modules for each dependency (some entries may be nil). A variant of the dependency must |
| // exist that matches the variations argument, but may also have other variations. |
| // For any unspecified variation the first variant will be used. |
| // |
| // Unlike AddVariationDependencies, the variations of the current module are ignored - the |
| // dependency only needs to match the supplied variations. |
| // |
| // If the mutator is parallel (see MutatorHandle.Parallel), this method will pause until the |
| // new dependencies have had the current mutator called on them. If the mutator is not |
| // parallel this method does not affect the ordering of the current mutator pass, but will |
| // be ordered correctly for all future mutator passes. |
| AddFarVariationDependencies([]Variation, DependencyTag, ...string) []Module |
| |
| // AddInterVariantDependency adds a dependency between two variants of the same module. Variants are always |
| // ordered in the same order as they were listed in CreateVariations, and AddInterVariantDependency does not change |
| // that ordering, but it associates a DependencyTag with the dependency and makes it visible to VisitDirectDeps, |
| // WalkDeps, etc. |
| AddInterVariantDependency(tag DependencyTag, from, to Module) |
| |
| // ReplaceDependencies replaces all dependencies on the identical variant of the module with the |
| // specified name with the current variant of this module. Replacements don't take effect until |
| // after the mutator pass is finished. |
| ReplaceDependencies(string) |
| |
| // ReplaceDependenciesIf replaces all dependencies on the identical variant of the module with the |
| // specified name with the current variant of this module as long as the supplied predicate returns |
| // true. |
| // |
| // Replacements don't take effect until after the mutator pass is finished. |
| ReplaceDependenciesIf(string, ReplaceDependencyPredicate) |
| |
| // AliasVariation takes a variationName that was passed to CreateVariations for this module, |
| // and creates an alias from the current variant (before the mutator has run) to the new |
| // variant. The alias will be valid until the next time a mutator calls CreateVariations or |
| // CreateLocalVariations on this module without also calling AliasVariation. The alias can |
| // be used to add dependencies on the newly created variant using the variant map from |
| // before CreateVariations was run. |
| AliasVariation(variationName string) |
| |
| // CreateAliasVariation takes a toVariationName that was passed to CreateVariations for this |
| // module, and creates an alias from a new fromVariationName variant the toVariationName |
| // variant. The alias will be valid until the next time a mutator calls CreateVariations or |
| // CreateLocalVariations on this module without also calling AliasVariation. The alias can |
| // be used to add dependencies on the toVariationName variant using the fromVariationName |
| // variant. |
| CreateAliasVariation(fromVariationName, toVariationName string) |
| |
| // SetVariationProvider sets the value for a provider for the given newly created variant of |
| // the current module, i.e. one of the Modules returned by CreateVariations.. It panics if |
| // not called during the appropriate mutator or GenerateBuildActions pass for the provider, |
| // if the value is not of the appropriate type, or if the module is not a newly created |
| // variant of the current module. The value should not be modified after being passed to |
| // SetVariationProvider. |
| SetVariationProvider(module Module, provider ProviderKey, value interface{}) |
| } |
| |
| // 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) |
| |
| // DependencyTag is an interface to an arbitrary object that embeds BaseDependencyTag. It can be |
| // used to transfer information on a dependency between the mutator that called AddDependency |
| // and the GenerateBuildActions method. Variants created by CreateVariations have a copy of the |
| // interface (pointing to the same concrete object) from their original module. |
| type DependencyTag interface { |
| dependencyTag(DependencyTag) |
| } |
| |
| type BaseDependencyTag struct { |
| } |
| |
| func (BaseDependencyTag) dependencyTag(DependencyTag) { |
| } |
| |
| var _ DependencyTag = BaseDependencyTag{} |
| |
| func (mctx *mutatorContext) MutatorName() string { |
| return mctx.name |
| } |
| |
| func (mctx *mutatorContext) CreateVariations(variationNames ...string) []Module { |
| return mctx.createVariations(variationNames, false) |
| } |
| |
| func (mctx *mutatorContext) CreateLocalVariations(variationNames ...string) []Module { |
| return mctx.createVariations(variationNames, true) |
| } |
| |
| func (mctx *mutatorContext) SetVariationProvider(module Module, provider ProviderKey, value interface{}) { |
| for _, variant := range mctx.newVariations { |
| if m := variant.module(); m != nil && m.logicModule == module { |
| mctx.context.setProvider(m, provider, value) |
| return |
| } |
| } |
| panic(fmt.Errorf("module %q is not a newly created variant of %q", module, mctx.module)) |
| } |
| |
| func (mctx *mutatorContext) createVariations(variationNames []string, local bool) []Module { |
| var ret []Module |
| modules, errs := mctx.context.createVariations(mctx.module, mctx.name, mctx.defaultVariation, variationNames, local) |
| if len(errs) > 0 { |
| mctx.errs = append(mctx.errs, errs...) |
| } |
| |
| for _, module := range modules { |
| ret = append(ret, module.module().logicModule) |
| } |
| |
| if mctx.newVariations != nil { |
| panic("module already has variations from this mutator") |
| } |
| mctx.newVariations = modules |
| |
| if len(ret) != len(variationNames) { |
| panic("oops!") |
| } |
| |
| return ret |
| } |
| |
| func (mctx *mutatorContext) AliasVariation(variationName string) { |
| for _, moduleOrAlias := range mctx.module.splitModules { |
| if alias := moduleOrAlias.alias(); alias != nil { |
| if alias.variant.variations.equal(mctx.module.variant.variations) { |
| panic(fmt.Errorf("AliasVariation already called")) |
| } |
| } |
| } |
| |
| for _, variant := range mctx.newVariations { |
| if variant.moduleOrAliasVariant().variations[mctx.name] == variationName { |
| alias := &moduleAlias{ |
| variant: mctx.module.variant, |
| target: variant.moduleOrAliasTarget(), |
| } |
| // Prepend the alias so that AddFarVariationDependencies subset match matches |
| // the alias before matching the first variation. |
| mctx.module.splitModules = append(modulesOrAliases{alias}, mctx.module.splitModules...) |
| return |
| } |
| } |
| |
| var foundVariations []string |
| for _, variant := range mctx.newVariations { |
| foundVariations = append(foundVariations, variant.moduleOrAliasVariant().variations[mctx.name]) |
| } |
| panic(fmt.Errorf("no %q variation in module variations %q", variationName, foundVariations)) |
| } |
| |
| func (mctx *mutatorContext) CreateAliasVariation(aliasVariationName, targetVariationName string) { |
| newVariant := newVariant(mctx.module, mctx.name, aliasVariationName, false) |
| |
| for _, moduleOrAlias := range mctx.module.splitModules { |
| if moduleOrAlias.moduleOrAliasVariant().variations.equal(newVariant.variations) { |
| if alias := moduleOrAlias.alias(); alias != nil { |
| panic(fmt.Errorf("can't alias %q to %q, already aliased to %q", aliasVariationName, targetVariationName, alias.target.variant.name)) |
| } else { |
| panic(fmt.Errorf("can't alias %q to %q, there is already a variant with that name", aliasVariationName, targetVariationName)) |
| } |
| } |
| } |
| |
| for _, variant := range mctx.newVariations { |
| if variant.moduleOrAliasVariant().variations[mctx.name] == targetVariationName { |
| // Append the alias here so that it comes after any aliases created by AliasVariation. |
| mctx.module.splitModules = append(mctx.module.splitModules, &moduleAlias{ |
| variant: newVariant, |
| target: variant.moduleOrAliasTarget(), |
| }) |
| return |
| } |
| } |
| |
| var foundVariations []string |
| for _, variant := range mctx.newVariations { |
| foundVariations = append(foundVariations, variant.moduleOrAliasVariant().variations[mctx.name]) |
| } |
| panic(fmt.Errorf("no %q variation in module variations %q", targetVariationName, foundVariations)) |
| } |
| |
| func (mctx *mutatorContext) SetDependencyVariation(variationName string) { |
| mctx.context.convertDepsToVariation(mctx.module, mctx.name, variationName, nil) |
| } |
| |
| func (mctx *mutatorContext) SetDefaultDependencyVariation(variationName *string) { |
| mctx.defaultVariation = variationName |
| } |
| |
| func (mctx *mutatorContext) Module() Module { |
| return mctx.module.logicModule |
| } |
| |
| func (mctx *mutatorContext) AddDependency(module Module, tag DependencyTag, deps ...string) []Module { |
| depInfos := make([]Module, 0, len(deps)) |
| for _, dep := range deps { |
| modInfo := mctx.context.moduleInfo[module] |
| depInfo, errs := mctx.context.addDependency(modInfo, tag, dep) |
| if len(errs) > 0 { |
| mctx.errs = append(mctx.errs, errs...) |
| } |
| if !mctx.pause(depInfo) { |
| // Pausing not supported by this mutator, new dependencies can't be returned. |
| depInfo = nil |
| } |
| depInfos = append(depInfos, maybeLogicModule(depInfo)) |
| } |
| return depInfos |
| } |
| |
| func (mctx *mutatorContext) AddReverseDependency(module Module, tag DependencyTag, destName string) { |
| if _, ok := tag.(BaseDependencyTag); ok { |
| panic("BaseDependencyTag is not allowed to be used directly!") |
| } |
| |
| destModule, errs := mctx.context.findReverseDependency(mctx.context.moduleInfo[module], destName) |
| if len(errs) > 0 { |
| mctx.errs = append(mctx.errs, errs...) |
| return |
| } |
| |
| mctx.reverseDeps = append(mctx.reverseDeps, reverseDep{ |
| destModule, |
| depInfo{mctx.context.moduleInfo[module], tag}, |
| }) |
| } |
| |
| func (mctx *mutatorContext) AddVariationDependencies(variations []Variation, tag DependencyTag, |
| deps ...string) []Module { |
| |
| depInfos := make([]Module, 0, len(deps)) |
| for _, dep := range deps { |
| depInfo, errs := mctx.context.addVariationDependency(mctx.module, variations, tag, dep, false) |
| if len(errs) > 0 { |
| mctx.errs = append(mctx.errs, errs...) |
| } |
| if !mctx.pause(depInfo) { |
| // Pausing not supported by this mutator, new dependencies can't be returned. |
| depInfo = nil |
| } |
| depInfos = append(depInfos, maybeLogicModule(depInfo)) |
| } |
| return depInfos |
| } |
| |
| func (mctx *mutatorContext) AddFarVariationDependencies(variations []Variation, tag DependencyTag, |
| deps ...string) []Module { |
| |
| depInfos := make([]Module, 0, len(deps)) |
| for _, dep := range deps { |
| depInfo, errs := mctx.context.addVariationDependency(mctx.module, variations, tag, dep, true) |
| if len(errs) > 0 { |
| mctx.errs = append(mctx.errs, errs...) |
| } |
| if !mctx.pause(depInfo) { |
| // Pausing not supported by this mutator, new dependencies can't be returned. |
| depInfo = nil |
| } |
| depInfos = append(depInfos, maybeLogicModule(depInfo)) |
| } |
| return depInfos |
| } |
| |
| func (mctx *mutatorContext) AddInterVariantDependency(tag DependencyTag, from, to Module) { |
| mctx.context.addInterVariantDependency(mctx.module, tag, from, to) |
| } |
| |
| func (mctx *mutatorContext) ReplaceDependencies(name string) { |
| mctx.ReplaceDependenciesIf(name, nil) |
| } |
| |
| type ReplaceDependencyPredicate func(from Module, tag DependencyTag, to Module) bool |
| |
| func (mctx *mutatorContext) ReplaceDependenciesIf(name string, predicate ReplaceDependencyPredicate) { |
| target := mctx.context.moduleMatchingVariant(mctx.module, name) |
| |
| if target == nil { |
| panic(fmt.Errorf("ReplaceDependencies could not find identical variant {%s} for module %s\n"+ |
| "available variants:\n %s", |
| mctx.context.prettyPrintVariant(mctx.module.variant.variations), |
| name, |
| mctx.context.prettyPrintGroupVariants(mctx.context.moduleGroupFromName(name, mctx.module.namespace())))) |
| } |
| |
| mctx.replace = append(mctx.replace, replace{target, mctx.module, predicate}) |
| } |
| |
| func (mctx *mutatorContext) Rename(name string) { |
| mctx.rename = append(mctx.rename, rename{mctx.module.group, name}) |
| } |
| |
| func (mctx *mutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) Module { |
| module := newModule(factory) |
| |
| module.relBlueprintsFile = mctx.module.relBlueprintsFile |
| module.pos = mctx.module.pos |
| module.propertyPos = mctx.module.propertyPos |
| module.createdBy = mctx.module |
| |
| for _, p := range props { |
| err := proptools.AppendMatchingProperties(module.properties, p, nil) |
| if err != nil { |
| panic(err) |
| } |
| } |
| |
| mctx.newModules = append(mctx.newModules, module) |
| |
| return module.logicModule |
| } |
| |
| // pause waits until the given dependency has been visited by the mutator's parallelVisit call. |
| // It returns true if the pause was supported, false if the pause was not supported and did not |
| // occur, which will happen when the mutator is not parallelizable. If the dependency is nil |
| // it returns true if pausing is supported or false if it is not. |
| func (mctx *mutatorContext) pause(dep *moduleInfo) bool { |
| if mctx.pauseCh != nil { |
| if dep != nil { |
| unpause := make(unpause) |
| mctx.pauseCh <- pauseSpec{ |
| paused: mctx.module, |
| until: dep, |
| unpause: unpause, |
| } |
| <-unpause |
| } |
| return true |
| } |
| return false |
| } |
| |
| // 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. |
| type SimpleName struct { |
| Properties struct { |
| Name string |
| } |
| } |
| |
| func (s *SimpleName) Name() string { |
| return s.Properties.Name |
| } |
| |
| // Load Hooks |
| |
| type LoadHookContext interface { |
| EarlyModuleContext |
| |
| // CreateModule creates a new module by calling the factory method for the specified moduleType, and applies |
| // the specified property structs to it as if the properties were set in a blueprint file. |
| CreateModule(ModuleFactory, ...interface{}) Module |
| |
| // RegisterScopedModuleType creates a new module type that is scoped to the current Blueprints |
| // file. |
| RegisterScopedModuleType(name string, factory ModuleFactory) |
| } |
| |
| func (l *loadHookContext) CreateModule(factory ModuleFactory, props ...interface{}) Module { |
| module := newModule(factory) |
| |
| module.relBlueprintsFile = l.module.relBlueprintsFile |
| module.pos = l.module.pos |
| module.propertyPos = l.module.propertyPos |
| module.createdBy = l.module |
| |
| for _, p := range props { |
| err := proptools.AppendMatchingProperties(module.properties, p, nil) |
| if err != nil { |
| panic(err) |
| } |
| } |
| |
| l.newModules = append(l.newModules, module) |
| |
| return module.logicModule |
| } |
| |
| func (l *loadHookContext) RegisterScopedModuleType(name string, factory ModuleFactory) { |
| if _, exists := l.context.moduleFactories[name]; exists { |
| panic(fmt.Errorf("A global module type named %q already exists", name)) |
| } |
| |
| if _, exists := (*l.scopedModuleFactories)[name]; exists { |
| panic(fmt.Errorf("A module type named %q already exists in this scope", name)) |
| } |
| |
| if *l.scopedModuleFactories == nil { |
| *l.scopedModuleFactories = make(map[string]ModuleFactory) |
| } |
| |
| (*l.scopedModuleFactories)[name] = factory |
| } |
| |
| type loadHookContext struct { |
| baseModuleContext |
| newModules []*moduleInfo |
| scopedModuleFactories *map[string]ModuleFactory |
| } |
| |
| type LoadHook func(ctx LoadHookContext) |
| |
| // Load hooks need to be added by module factories, which don't have any parameter to get to the |
| // Context, and only produce a Module interface with no base implementation, so the load hooks |
| // must be stored in a global map. The key is a pointer allocated by the module factory, so there |
| // is no chance of collisions even if tests are running in parallel with multiple contexts. The |
| // contents should be short-lived, they are added during a module factory and removed immediately |
| // after the module factory returns. |
| var pendingHooks sync.Map |
| |
| func AddLoadHook(module Module, hook LoadHook) { |
| // Only one goroutine can be processing a given module, so no additional locking is required |
| // for the slice stored in the sync.Map. |
| v, exists := pendingHooks.Load(module) |
| if !exists { |
| v, _ = pendingHooks.LoadOrStore(module, new([]LoadHook)) |
| } |
| hooks := v.(*[]LoadHook) |
| *hooks = append(*hooks, hook) |
| } |
| |
| func runAndRemoveLoadHooks(ctx *Context, config interface{}, module *moduleInfo, |
| scopedModuleFactories *map[string]ModuleFactory) (newModules []*moduleInfo, deps []string, errs []error) { |
| |
| if v, exists := pendingHooks.Load(module.logicModule); exists { |
| hooks := v.(*[]LoadHook) |
| |
| for _, hook := range *hooks { |
| mctx := &loadHookContext{ |
| baseModuleContext: baseModuleContext{ |
| context: ctx, |
| config: config, |
| module: module, |
| }, |
| scopedModuleFactories: scopedModuleFactories, |
| } |
| hook(mctx) |
| newModules = append(newModules, mctx.newModules...) |
| deps = append(deps, mctx.ninjaFileDeps...) |
| errs = append(errs, mctx.errs...) |
| } |
| pendingHooks.Delete(module.logicModule) |
| |
| return newModules, deps, errs |
| } |
| |
| return nil, nil, nil |
| } |
| |
| // Check the syntax of a generated blueprint file. |
| // |
| // This is intended to perform a quick syntactic check for generated blueprint |
| // code, where syntactically correct means: |
| // * No variable definitions. |
| // * Valid module types. |
| // * Valid property names. |
| // * Valid values for the property type. |
| // |
| // It does not perform any semantic checking of properties, existence of referenced |
| // files, or dependencies. |
| // |
| // At a low level it: |
| // * Parses the contents. |
| // * Invokes relevant factory to create Module instances. |
| // * Unpacks the properties into the Module. |
| // * Does not invoke load hooks or any mutators. |
| // |
| // The filename is only used for reporting errors. |
| func CheckBlueprintSyntax(moduleFactories map[string]ModuleFactory, filename string, contents string) []error { |
| scope := parser.NewScope(nil) |
| file, errs := parser.Parse(filename, strings.NewReader(contents), scope) |
| if len(errs) != 0 { |
| return errs |
| } |
| |
| for _, def := range file.Defs { |
| switch def := def.(type) { |
| case *parser.Module: |
| _, moduleErrs := processModuleDef(def, filename, moduleFactories, nil, false) |
| errs = append(errs, moduleErrs...) |
| |
| default: |
| panic(fmt.Errorf("unknown definition type: %T", def)) |
| } |
| } |
| |
| return errs |
| } |
| |
| func maybeLogicModule(module *moduleInfo) Module { |
| if module != nil { |
| return module.logicModule |
| } else { |
| return nil |
| } |
| } |