| // 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 ( |
| "cmp" |
| "encoding/json" |
| "errors" |
| "fmt" |
| "path/filepath" |
| "slices" |
| "strings" |
| "text/scanner" |
| |
| "github.com/google/blueprint/parser" |
| "github.com/google/blueprint/pathtools" |
| "github.com/google/blueprint/proptools" |
| "github.com/google/blueprint/uniquelist" |
| ) |
| |
| // 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 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) |
| |
| String() string |
| |
| addLoadHook(hook LoadHookWithPriority) |
| getAndClearloadHooks() []LoadHookWithPriority |
| |
| info() *moduleInfo |
| setInfo(*moduleInfo) |
| } |
| |
| type ModuleOrProxy interface { |
| info() *moduleInfo |
| Name() string |
| String() string |
| } |
| |
| var _ ModuleOrProxy = (Module)(nil) |
| var _ ModuleOrProxy = ModuleProxy{} |
| |
| type ModuleBase struct { |
| moduleInfo *moduleInfo |
| loadHooks []LoadHookWithPriority |
| } |
| |
| func (m ModuleBase) info() *moduleInfo { |
| return m.moduleInfo |
| } |
| |
| func (m *ModuleBase) setInfo(moduleInfo *moduleInfo) { |
| m.moduleInfo = moduleInfo |
| } |
| |
| func (m *ModuleBase) addLoadHook(hook LoadHookWithPriority) { |
| m.loadHooks = append(m.loadHooks, hook) |
| } |
| |
| func (m *ModuleBase) getAndClearloadHooks() []LoadHookWithPriority { |
| hooks := m.loadHooks |
| m.loadHooks = nil |
| return hooks |
| } |
| |
| type ModuleProxy struct { |
| moduleInfo *moduleInfo |
| } |
| |
| func (m ModuleProxy) info() *moduleInfo { |
| return m.moduleInfo |
| } |
| |
| func CreateModuleProxy(module Module) ModuleProxy { |
| return ModuleProxy{ |
| moduleInfo: module.info(), |
| } |
| } |
| |
| func (m ModuleProxy) IsNil() bool { |
| return m.moduleInfo == nil |
| } |
| |
| func (m ModuleProxy) Name() string { |
| if m.moduleInfo.logicModule == nil { |
| return m.moduleInfo.cachedName |
| } |
| return m.moduleInfo.logicModule.Name() |
| } |
| |
| func (m ModuleProxy) String() string { |
| if m.moduleInfo.logicModule == nil { |
| return m.moduleInfo.cachedString |
| } |
| return m.moduleInfo.logicModule.String() |
| } |
| |
| // 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 BottomUpMutatorContext.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 |
| |
| // ModuleTags returns the tags for this module that should be passed to |
| // ninja for analysis. For example: |
| // [ |
| // "module_name": "libfoo", |
| // "module_type": "cc_library", |
| // ] |
| ModuleTags() map[string]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{}) |
| |
| // OtherModulePropertyErrorf reports an error at the line number of a property in the given module definition. |
| OtherModulePropertyErrorf(logicModule ModuleOrProxy, property string, format 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 |
| OtherModuleNamespace(ModuleOrProxy) Namespace |
| |
| // ModuleFactories returns a map of all of the global ModuleFactories by name. |
| ModuleFactories() map[string]ModuleFactory |
| |
| // HasMutatorFinished returns true if the given mutator has finished running. |
| // It will panic if given an invalid mutator name. |
| HasMutatorFinished(mutatorName string) bool |
| } |
| |
| 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 |
| |
| GetDirectDepProxyWithTag(name string, tag DependencyTag) ModuleProxy |
| |
| // 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)) |
| |
| VisitDirectDepsProxy(visit func(proxy ModuleProxy)) |
| |
| // 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) |
| |
| WalkDepsProxy(visit func(ModuleProxy, ModuleProxy) 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 |
| |
| // IsPrimaryModule returns if the current module is the first variant. 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. |
| IsPrimaryModule(module ModuleOrProxy) bool |
| |
| // 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 |
| |
| // IsFinalModule returns if the current module is the last variant. 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 is the last one. This can be used to perform |
| // singleton actions that are only done once for all variants of a module. |
| IsFinalModule(module ModuleOrProxy) bool |
| |
| // 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 ModuleOrProxy) 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 ModuleOrProxy) 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 ModuleOrProxy) 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 ModuleOrProxy, 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 ModuleOrProxy) DependencyTag |
| |
| // OtherModuleSubDir returns the string representing the variations of the module. |
| OtherModuleSubDir(m ModuleOrProxy) string |
| |
| // 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. |
| // |
| // WARNING: This should _only_ be used within the context of bp2build, where variants and |
| // dependencies are not created. |
| 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 or the module is nil, it returns nil and false. The value returned may be a deep |
| // copy of the value originally passed to SetProvider. |
| // |
| // This method shouldn't be used directly, prefer the type-safe android.OtherModuleProvider instead. |
| OtherModuleProvider(m ModuleOrProxy, provider AnyProviderKey) (any, bool) |
| |
| OtherModuleHasProvider(m ModuleOrProxy, provider AnyProviderKey) bool |
| |
| // OtherModuleIsAutoGenerated returns true if a module has been generated from another module, |
| // instead of being defined in Android.bp file |
| OtherModuleIsAutoGenerated(m ModuleOrProxy) bool |
| |
| // OtherModuleNamespace returns the namespace of the module. |
| OtherModuleNamespace(m ModuleOrProxy) Namespace |
| |
| // Provider returns the value for a provider for the current module. If the value is |
| // not set it returns nil and false. 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. |
| // |
| // This method shouldn't be used directly, prefer the type-safe android.ModuleProvider instead. |
| Provider(provider AnyProviderKey) (any, 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. |
| // |
| // This method shouldn't be used directly, prefer the type-safe android.SetProvider instead. |
| SetProvider(provider AnyProviderKey, value any) |
| |
| EarlyGetMissingDependencies() []string |
| |
| base() *baseModuleContext |
| } |
| |
| 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 |
| |
| ModuleCacheKey() 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 |
| |
| // FreeModuleAfterGenerateBuildActions marks this module as no longer necessary after the completion of |
| // GenerateBuildActions, i.e. all later accesses to the module will be via ModuleProxy and not direct access |
| // to the Module. |
| FreeModuleAfterGenerateBuildActions() |
| } |
| |
| 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) ModuleTags() map[string]string { |
| return map[string]string{ |
| "module_name": d.ModuleName(), |
| "module_type": d.ModuleType(), |
| } |
| } |
| |
| 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(d.context.moduleErrorf(d.module, format, args...)) |
| } |
| |
| func (d *baseModuleContext) PropertyErrorf(property, format string, |
| args ...interface{}) { |
| |
| d.error(d.context.PropertyErrorf(d.module.logicModule, property, format, args...)) |
| } |
| |
| func (d *baseModuleContext) OtherModulePropertyErrorf(logicModule ModuleOrProxy, property string, format string, |
| args ...interface{}) { |
| |
| d.error(d.context.PropertyErrorf(logicModule, property, format, args...)) |
| } |
| |
| func (d *baseModuleContext) Failed() bool { |
| return len(d.errs) > 0 |
| } |
| |
| func (d *baseModuleContext) GlobWithDeps(pattern string, |
| excludes []string) ([]string, error) { |
| result, err := d.context.glob(pattern, excludes) |
| if err == nil && d.context.incrementalEnabled { |
| hash, err := proptools.CalculateHash(result) |
| if err != nil { |
| panic(newPanicErrorf(err, "failed to calculate hash for glob result: %s", d.ModuleName())) |
| } |
| d.module.globCache = append(d.module.globCache, globResultCache{ |
| Pattern: pattern, |
| Excludes: excludes, |
| Result: hash, |
| }) |
| } |
| return result, err |
| } |
| |
| func (d *baseModuleContext) Fs() pathtools.FileSystem { |
| return d.context.fs |
| } |
| |
| func (d *baseModuleContext) Namespace() Namespace { |
| return d.context.nameInterface.GetNamespace(newNamespaceContext(d.module)) |
| } |
| |
| func (d *baseModuleContext) HasMutatorFinished(mutatorName string) bool { |
| return d.context.HasMutatorFinished(mutatorName) |
| } |
| |
| var _ ModuleContext = (*moduleContext)(nil) |
| |
| type moduleContext struct { |
| baseModuleContext |
| scope *localScope |
| actionDefs localBuildActions |
| handledMissingDeps bool |
| } |
| |
| func EqualModules(m1, m2 ModuleOrProxy) bool { |
| return m1.info() == m2.info() |
| } |
| |
| func (m *baseModuleContext) OtherModuleName(logicModule ModuleOrProxy) string { |
| module := logicModule.info() |
| return module.Name() |
| } |
| |
| func (m *baseModuleContext) OtherModuleDir(logicModule ModuleOrProxy) string { |
| module := logicModule.info() |
| return filepath.Dir(module.relBlueprintsFile) |
| } |
| |
| func (m *baseModuleContext) OtherModuleType(logicModule ModuleOrProxy) string { |
| module := logicModule.info() |
| return module.typeName |
| } |
| |
| func (m *baseModuleContext) OtherModuleErrorf(logicModule ModuleOrProxy, format string, |
| args ...interface{}) { |
| |
| module := logicModule.info() |
| m.errs = append(m.errs, &ModuleError{ |
| BlueprintError: BlueprintError{ |
| Err: fmt.Errorf(format, args...), |
| Pos: module.pos, |
| }, |
| module: module, |
| }) |
| } |
| |
| func (m *baseModuleContext) OtherModuleDependencyTag(logicModule ModuleOrProxy) DependencyTag { |
| // fast path for calling OtherModuleDependencyTag from inside VisitDirectDeps |
| if m.visitingDep.module != nil && logicModule.info() == m.visitingDep.module { |
| return m.visitingDep.tag |
| } |
| |
| if m.visitingParent == nil { |
| return nil |
| } |
| |
| for _, dep := range m.visitingParent.directDeps { |
| if dep.module == logicModule.info() { |
| return dep.tag |
| } |
| } |
| |
| return nil |
| } |
| |
| func (m *baseModuleContext) OtherModuleSubDir(logicModule ModuleOrProxy) string { |
| return m.context.ModuleSubDir(logicModule) |
| } |
| |
| 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] |
| 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, _, errs := m.context.findVariant(m.module, m.config, possibleDeps, variations, false, false) |
| if errs != nil { |
| panic(errors.Join(errs...)) |
| } |
| 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, _, errs := m.context.findVariant(m.module, m.config, possibleDeps, variations, true, false) |
| if errs != nil { |
| panic(errors.Join(errs...)) |
| } |
| return found != nil |
| } |
| |
| func (m *baseModuleContext) OtherModuleReverseDependencyVariantExists(name string) bool { |
| possibleDeps := m.context.moduleGroupFromName(name, m.module.namespace()) |
| if possibleDeps == nil { |
| return false |
| } |
| found, _, errs := m.context.findVariant(m.module, m.config, possibleDeps, nil, false, true) |
| if errs != nil { |
| panic(errors.Join(errs...)) |
| } |
| return found != nil |
| } |
| |
| func (m *baseModuleContext) OtherModuleProvider(logicModule ModuleOrProxy, provider AnyProviderKey) (any, bool) { |
| if logicModule == nil { |
| return nil, false |
| } |
| return m.context.provider(logicModule.info(), provider.provider()) |
| } |
| |
| func (m *baseModuleContext) OtherModuleHasProvider(logicModule ModuleOrProxy, provider AnyProviderKey) bool { |
| if logicModule == nil { |
| return false |
| } |
| return m.context.hasProvider(logicModule.info(), provider.provider()) |
| } |
| |
| func (m *baseModuleContext) Provider(provider AnyProviderKey) (any, bool) { |
| return m.context.provider(m.module, provider.provider()) |
| } |
| |
| func (m *baseModuleContext) SetProvider(provider AnyProviderKey, value interface{}) { |
| m.context.setProvider(m.module, provider.provider(), value) |
| } |
| |
| func (m *moduleContext) restoreModuleBuildActions() bool { |
| // Whether the incremental flag is set and the module type supports |
| // incremental, this will decide weather to cache the data for the module. |
| incrementalEnabled := false |
| // Whether the above conditions are true and we can try to restore from |
| // the cache for this module, i.e., no env, product variables and Soong |
| // code changes. |
| incrementalAnalysis := false |
| var cacheKey *BuildActionCacheKey = nil |
| if m.context.GetIncrementalEnabled() { |
| if im, ok := m.module.logicModule.(Incremental); ok { |
| incrementalEnabled = im.IncrementalSupported() |
| incrementalAnalysis = m.context.GetIncrementalAnalysis() && incrementalEnabled |
| } |
| } |
| if incrementalEnabled { |
| hash, err := proptools.CalculateHash(m.module.properties) |
| if err != nil { |
| panic(newPanicErrorf(err, "failed to calculate properties hash")) |
| } |
| cacheInput := new(BuildActionCacheInput) |
| cacheInput.PropertiesHash = hash |
| var deps []ModuleProxy |
| m.VisitDirectDepsProxy(func(module ModuleProxy) { |
| cacheInput.ProvidersHash = |
| append(cacheInput.ProvidersHash, module.info().providerInitialValueHashes) |
| if m.context.incrementalDebugFile != "" { |
| deps = append(deps, module) |
| } |
| }) |
| hash, err = proptools.CalculateHash(cacheInput) |
| if err != nil { |
| panic(newPanicErrorf(err, "failed to calculate cache input hash")) |
| } |
| cacheKey = &BuildActionCacheKey{ |
| Id: m.ModuleCacheKey(), |
| InputHash: hash, |
| } |
| m.module.buildActionCacheKey = cacheKey |
| if m.context.incrementalDebugFile != "" { |
| m.module.incrementalDebugInfo = incrementalDebugData(m, deps, cacheInput) |
| } |
| } |
| |
| restored := false |
| if incrementalAnalysis && cacheKey != nil { |
| // Try to restore from cache if there is a cache hit |
| data := m.context.getBuildActionsFromCache(cacheKey) |
| if data == nil { |
| return false |
| } |
| for _, glob := range data.GlobCache { |
| result, err := m.context.glob(glob.Pattern, glob.Excludes) |
| if err != nil { |
| panic(newPanicErrorf(err, "failed to glob for cached module: %s %s %v", m.ModuleName(), glob.Pattern, glob.Excludes)) |
| } |
| hash, err := proptools.CalculateHash(result) |
| if err != nil { |
| panic(newPanicErrorf(err, "failed to calculate hash for cached glob result: %s", m.ModuleName())) |
| } |
| if hash != glob.Result { |
| return false |
| } |
| } |
| |
| relPos := m.module.pos |
| relPos.Filename = m.module.relBlueprintsFile |
| if data.Pos != nil && relPos == *data.Pos { |
| for _, provider := range data.Providers { |
| m.context.setProvider(m.module, provider.Id, *provider.Value) |
| } |
| m.module.incrementalRestored = true |
| m.module.orderOnlyStrings = data.OrderOnlyStrings |
| m.module.globCache = data.GlobCache |
| restored = true |
| for _, str := range data.OrderOnlyStrings { |
| if !strings.HasPrefix(str, "dedup-") { |
| continue |
| } |
| orderOnlyStrings, ok := m.context.orderOnlyStringsCache[str] |
| if !ok { |
| panic(fmt.Errorf("no cached value found for order only dep: %s", str)) |
| } |
| key := uniquelist.Make(orderOnlyStrings) |
| if info, loaded := m.context.orderOnlyStrings.LoadOrStore(key, &orderOnlyStringsInfo{ |
| dedup: true, |
| incremental: true, |
| }); loaded { |
| for { |
| cpy := *info |
| cpy.dedup = true |
| cpy.incremental = true |
| if m.context.orderOnlyStrings.CompareAndSwap(key, info, &cpy) { |
| break |
| } |
| if info, loaded = m.context.orderOnlyStrings.Load(key); !loaded { |
| // This shouldn't happen |
| panic("order only string was removed unexpectedly") |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| return restored |
| } |
| |
| type depProviders struct { |
| Name string `json:"dep_name"` |
| Type string `json:"dep_type"` |
| Variant string `json:"dep_variant"` |
| Providers []string `json:"dep_provider_hash"` |
| } |
| |
| func incrementalDebugData(m *moduleContext, deps []ModuleProxy, inputHash *BuildActionCacheInput) []byte { |
| info := struct { |
| Name string `json:"name"` |
| CacheKey string `json:"cache_key"` |
| Type string `json:"type"` |
| Variant string `json:"variant"` |
| PropHash uint64 `json:"properties_hash"` |
| Providers []depProviders `json:"providers"` |
| }{ |
| Name: m.module.logicModule.Name(), |
| CacheKey: m.ModuleCacheKey(), |
| Type: m.module.typeName, |
| Variant: m.module.variant.name, |
| PropHash: inputHash.PropertiesHash, |
| Providers: func() []depProviders { |
| result := make([]depProviders, 0, len(deps)) |
| for _, d := range deps { |
| dep := d.info() |
| dp := depProviders{ |
| Name: dep.Name(), |
| Type: dep.typeName, |
| Variant: dep.variant.name, |
| } |
| for _, p := range providerRegistry { |
| if dep.providerInitialValueHashes[p.id] == 0 { |
| continue |
| } |
| dp.Providers = append(dp.Providers, |
| fmt.Sprintf("%s:%x", p.typ, dep.providerInitialValueHashes[p.id])) |
| } |
| result = append(result, dp) |
| } |
| return result |
| }(), |
| } |
| buf, _ := json.Marshal(info) |
| return buf |
| } |
| |
| 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) GetDirectDepProxyWithTag(name string, tag DependencyTag) ModuleProxy { |
| module := m.GetDirectDepWithTag(name, tag) |
| if module != nil { |
| return ModuleProxy{module.info()} |
| } |
| |
| return ModuleProxy{} |
| } |
| |
| 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 |
| if dep.module.logicModule == nil { |
| panic(fmt.Errorf("VisitDirectDeps visited module %s that called FreeAfterGenerateBuildActions()", dep.module)) |
| } |
| visit(dep.module.logicModule) |
| } |
| |
| m.visitingParent = nil |
| m.visitingDep = depInfo{} |
| } |
| |
| func (m *baseModuleContext) VisitDirectDepsProxy(visit func(proxy ModuleProxy)) { |
| 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(ModuleProxy{dep.module}) |
| } |
| |
| 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 dep.module.logicModule == nil { |
| panic(fmt.Errorf("VisitDirectDepsIf visited module %s that called FreeAfterGenerateBuildActions()", dep.module)) |
| } |
| 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 |
| if dep.module.logicModule == nil { |
| panic(fmt.Errorf("VisitDepsDepthFirst visited module %s that called FreeAfterGenerateBuildActions()", dep.module)) |
| } |
| 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 |
| if dep.module.logicModule == nil { |
| panic(fmt.Errorf("VisitDepsDepthFirstIf visited module %s that called FreeAfterGenerateBuildActions()", dep.module)) |
| } |
| 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 |
| if dep.module.logicModule == nil { |
| panic(fmt.Errorf("WalkDeps visited module %s that called FreeAfterGenerateBuildActions()", dep.module)) |
| } |
| return visit(dep.module.logicModule, parent.logicModule) |
| }, nil) |
| |
| m.visitingParent = nil |
| m.visitingDep = depInfo{} |
| } |
| |
| func (m *baseModuleContext) WalkDepsProxy(visit func(child, parent ModuleProxy) bool) { |
| m.context.walkDeps(m.module, true, func(dep depInfo, parent *moduleInfo) bool { |
| m.visitingParent = parent |
| m.visitingDep = dep |
| return visit(ModuleProxy{dep.module}, ModuleProxy{parent}) |
| }, nil) |
| |
| m.visitingParent = nil |
| m.visitingDep = depInfo{} |
| } |
| |
| func (m *baseModuleContext) PrimaryModule() Module { |
| return m.module.group.modules.firstModule().logicModule |
| } |
| |
| func (m *baseModuleContext) IsPrimaryModule(module ModuleOrProxy) bool { |
| return m.module.group.modules.firstModule() == module.info() |
| } |
| |
| func (m *baseModuleContext) FinalModule() Module { |
| return m.module.group.modules.lastModule().logicModule |
| } |
| |
| func (m *baseModuleContext) IsFinalModule(module ModuleOrProxy) bool { |
| return m.module.group.modules.lastModule() == module.info() |
| } |
| |
| func (m *baseModuleContext) AddNinjaFileDeps(deps ...string) { |
| m.ninjaFileDeps = append(m.ninjaFileDeps, deps...) |
| } |
| |
| func (m *baseModuleContext) ModuleFactories() map[string]ModuleFactory { |
| return m.context.ModuleTypeFactories() |
| } |
| |
| func (m *baseModuleContext) base() *baseModuleContext { |
| return m |
| } |
| |
| func (m *baseModuleContext) OtherModuleIsAutoGenerated(logicModule ModuleOrProxy) bool { |
| module := logicModule.info() |
| if module == nil { |
| panic(fmt.Errorf("Module %s not found in baseModuleContext", logicModule.Name())) |
| } |
| return module.createdBy != nil |
| } |
| |
| func (m *baseModuleContext) OtherModuleNamespace(logicModule ModuleOrProxy) Namespace { |
| return m.context.nameInterface.GetNamespace(newNamespaceContext(logicModule.info())) |
| } |
| |
| func (m *moduleContext) ModuleSubDir() string { |
| return m.module.variant.name |
| } |
| |
| func (m *moduleContext) ModuleCacheKey() string { |
| return m.module.ModuleCacheKey() |
| } |
| |
| 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, m.ModuleTags()) |
| if err != nil { |
| panic(err) |
| } |
| |
| m.actionDefs.buildDefs = append(m.actionDefs.buildDefs, def) |
| if def.OrderOnlyStrings.Len() > 0 { |
| if info, loaded := m.context.orderOnlyStrings.LoadOrStore(def.OrderOnlyStrings, &orderOnlyStringsInfo{ |
| dedup: false, |
| incremental: m.module.buildActionCacheKey != nil, |
| }); loaded { |
| for { |
| cpy := *info |
| cpy.dedup = true |
| cpy.incremental = cpy.incremental || m.module.buildActionCacheKey != nil |
| if m.context.orderOnlyStrings.CompareAndSwap(def.OrderOnlyStrings, info, &cpy) { |
| break |
| } |
| if info, loaded = m.context.orderOnlyStrings.Load(def.OrderOnlyStrings); !loaded { |
| // This shouldn't happen |
| panic("order only string was removed unexpectedly") |
| } |
| } |
| } |
| } |
| } |
| |
| func (m *moduleContext) GetMissingDependencies() []string { |
| m.handledMissingDeps = true |
| return m.module.missingDeps |
| } |
| |
| func (m *moduleContext) FreeModuleAfterGenerateBuildActions() { |
| m.module.freeAfterGenerateBuildActions = true |
| } |
| |
| func (m *baseModuleContext) EarlyGetMissingDependencies() []string { |
| return m.module.missingDeps |
| } |
| |
| // |
| // MutatorContext |
| // |
| |
| type mutatorContext struct { |
| baseModuleContext |
| mutator *mutatorInfo |
| reverseDeps []reverseDep |
| rename []rename |
| replace []replace |
| newVariations moduleList // new variants of existing modules |
| newModules []*moduleInfo // brand new modules |
| defaultVariation *string |
| pauseFunc pauseFunc |
| } |
| |
| type BottomUpMutatorContext interface { |
| BaseModuleContext |
| |
| // 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. |
| // |
| // This method will pause until the new dependencies have had the current mutator called on them. |
| 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. May only be called by mutators that were marked with |
| // UsesReverseDependencies during registration. |
| AddReverseDependency(module Module, tag DependencyTag, name 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. |
| // |
| // |
| // This method will pause until the new dependencies have had the current mutator called on them. |
| AddVariationDependencies([]Variation, DependencyTag, ...string) []Module |
| |
| // AddReverseVariationDependency adds a dependency from the named module to the current |
| // module. The given variations will be added to the current module's varations, and then the |
| // result will be used to find the correct variation of the depending module, which must exist. |
| // |
| // 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. May only be called by mutators that were marked with |
| // UsesReverseDependencies during registration. |
| AddReverseVariationDependency([]Variation, DependencyTag, string) |
| |
| // 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. |
| // |
| // |
| // This method will pause until the new dependencies have had the current mutator called on them. |
| AddFarVariationDependencies([]Variation, DependencyTag, ...string) []Module |
| |
| // ReplaceDependencies finds all the variants of the module with the specified name, then |
| // replaces all dependencies onto those variants with the current variant of this module. |
| // Replacements don't take effect until after the mutator pass is finished. May only |
| // be called by mutators that were marked with UsesReplaceDependencies during registration. |
| ReplaceDependencies(string) |
| |
| // ReplaceDependenciesIf finds all the variants of the module with the specified name, then |
| // replaces all dependencies onto those variants 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. May only |
| // be called by mutators that were marked with UsesReplaceDependencies during registration. |
| ReplaceDependenciesIf(string, ReplaceDependencyPredicate) |
| |
| // 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. May only be called |
| // by mutators that were marked with UsesRename during registration. |
| Rename(name string) |
| |
| // 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. May only |
| // be called by mutators that were marked with UsesCreateModule during registration. |
| CreateModule(ModuleFactory, string, ...interface{}) Module |
| } |
| |
| // A Mutator function is called for each Module, and can modify properties on the modules. |
| // 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 BottomUpMutator func(mctx BottomUpMutatorContext) |
| |
| // 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. |
| type DependencyTag interface { |
| dependencyTag(DependencyTag) |
| } |
| |
| type BaseDependencyTag struct { |
| } |
| |
| func (BaseDependencyTag) dependencyTag(DependencyTag) { |
| } |
| |
| var _ DependencyTag = BaseDependencyTag{} |
| |
| func (mctx *mutatorContext) createVariationsWithTransition(variationNames []string, outgoingTransitions [][]string) []*moduleInfo { |
| depChooser := chooseDepByIndexes(mctx.mutator.name, outgoingTransitions) |
| modules, errs := mctx.context.createVariations(mctx.module, mctx.mutator, depChooser, variationNames) |
| if len(errs) > 0 { |
| mctx.errs = append(mctx.errs, errs...) |
| } |
| |
| if mctx.newVariations != nil { |
| panic("module already has variations from this mutator") |
| } |
| mctx.newVariations = modules |
| |
| if len(modules) != len(variationNames) { |
| panic("oops!") |
| } |
| |
| return modules |
| } |
| |
| 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 := module.info() |
| depInfo, errs := mctx.context.addVariationDependency(modInfo, mctx.mutator, mctx.config, nil, 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 (m *mutatorContext) AddReverseDependency(module Module, tag DependencyTag, name string) { |
| if !m.mutator.usesReverseDependencies { |
| panic(fmt.Errorf("method AddReverseDependency called from mutator that was not marked UsesReverseDependencies")) |
| } |
| |
| if _, ok := tag.(BaseDependencyTag); ok { |
| panic("BaseDependencyTag is not allowed to be used directly!") |
| } |
| |
| if module != m.module.logicModule { |
| panic(fmt.Errorf("AddReverseDependency called with module that is not the current module")) |
| } |
| m.AddReverseVariationDependency(nil, tag, name) |
| } |
| |
| func (mctx *mutatorContext) AddReverseVariationDependency(variations []Variation, tag DependencyTag, name string) { |
| if !mctx.mutator.usesReverseDependencies { |
| panic(fmt.Errorf("method AddReverseVariationDependency called from mutator that was not marked UsesReverseDependencies")) |
| } |
| |
| if _, ok := tag.(BaseDependencyTag); ok { |
| panic("BaseDependencyTag is not allowed to be used directly!") |
| } |
| |
| possibleDeps := mctx.context.moduleGroupFromName(name, mctx.module.namespace()) |
| if possibleDeps == nil { |
| mctx.errs = append(mctx.errs, &BlueprintError{ |
| Err: fmt.Errorf("%q has a reverse dependency on undefined module %q", |
| mctx.module.Name(), name), |
| Pos: mctx.module.pos, |
| }) |
| return |
| } |
| |
| found, newVariant, errs := mctx.context.findVariant(mctx.module, mctx.config, possibleDeps, variations, false, true) |
| if errs != nil { |
| mctx.errs = append(mctx.errs, errs...) |
| return |
| } |
| |
| if found == nil { |
| if mctx.context.allowMissingDependencies { |
| // Allow missing variants. |
| mctx.errs = append(mctx.errs, mctx.context.discoveredMissingDependencies(mctx.module, name, newVariant)...) |
| } else { |
| mctx.errs = append(mctx.errs, &BlueprintError{ |
| Err: fmt.Errorf("reverse dependency %q of %q missing variant:\n %s\navailable variants:\n %s", |
| name, mctx.module.Name(), |
| mctx.context.prettyPrintVariant(newVariant), |
| mctx.context.prettyPrintGroupVariants(possibleDeps)), |
| Pos: mctx.module.pos, |
| }) |
| } |
| return |
| } |
| |
| mctx.reverseDeps = append(mctx.reverseDeps, reverseDep{ |
| found, |
| depInfo{mctx.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, mctx.mutator, mctx.config, 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, mctx.mutator, mctx.config, 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) 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) { |
| if !mctx.mutator.usesReplaceDependencies { |
| panic(fmt.Errorf("method ReplaceDependenciesIf called from mutator that was not marked UsesReplaceDependencies")) |
| } |
| |
| targets := mctx.context.moduleVariantsThatDependOn(name, mctx.module) |
| |
| if len(targets) == 0 { |
| panic(fmt.Errorf("ReplaceDependenciesIf could not find variant of %s that depends on %s variant %s", |
| name, |
| mctx.module.group.name, |
| mctx.context.prettyPrintVariant(mctx.module.variant.variations), |
| )) |
| } |
| |
| for _, target := range targets { |
| mctx.replace = append(mctx.replace, replace{target, mctx.module, predicate}) |
| } |
| } |
| |
| func (mctx *mutatorContext) Rename(name string) { |
| if !mctx.mutator.usesRename { |
| panic(fmt.Errorf("method Rename called from mutator that was not marked UsesRename")) |
| } |
| mctx.rename = append(mctx.rename, rename{mctx.module.group, name}) |
| } |
| |
| func (mctx *mutatorContext) CreateModule(factory ModuleFactory, typeName string, props ...interface{}) Module { |
| if !mctx.mutator.usesCreateModule { |
| panic(fmt.Errorf("method CreateModule called from mutator that was not marked UsesCreateModule")) |
| } |
| |
| module := newModule(factory) |
| |
| module.relBlueprintsFile = mctx.module.relBlueprintsFile |
| module.pos = mctx.module.pos |
| module.propertyPos = mctx.module.propertyPos |
| module.createdBy = mctx.module |
| module.typeName = typeName |
| |
| 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.pauseFunc != nil { |
| if dep != nil { |
| mctx.pauseFunc(dep) |
| } |
| 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 |
| } |
| |
| func (s *SimpleName) String() string { |
| return s.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, string, ...interface{}) Module |
| |
| // CreateModuleInDirectory creates a new module in the specified directory 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. |
| CreateModuleInDirectory(ModuleFactory, string, string, ...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, typeName, moduleDir string, props ...interface{}) Module { |
| module := newModule(factory) |
| |
| module.relBlueprintsFile = moduleDir |
| module.pos = l.module.pos |
| module.propertyPos = l.module.propertyPos |
| module.createdBy = l.module |
| module.typeName = typeName |
| |
| 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) CreateModule(factory ModuleFactory, typeName string, props ...interface{}) Module { |
| return l.createModule(factory, typeName, l.module.relBlueprintsFile, props...) |
| } |
| |
| func (l *loadHookContext) CreateModuleInDirectory(factory ModuleFactory, typeName, moduleDir string, props ...interface{}) Module { |
| if moduleDir != filepath.Clean(moduleDir) { |
| panic(fmt.Errorf("Cannot create a module in %s", moduleDir)) |
| } |
| |
| filePath := filepath.Join(moduleDir, "Android.bp") |
| return l.createModule(factory, typeName, filePath, props...) |
| } |
| |
| 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) |
| |
| // LoadHookWithPriority is a wrapper around LoadHook and allows hooks to be sorted by priority. |
| // hooks with higher value of `priority` run last. |
| // hooks with equal value of `priority` run in the order they were registered. |
| type LoadHookWithPriority struct { |
| priority int |
| loadHook LoadHook |
| } |
| |
| func AddLoadHook(module Module, hook LoadHook) { |
| // default priority is 0 |
| AddLoadHookWithPriority(module, hook, 0) |
| } |
| |
| // AddLoadhHookWithPriority adds a load hook with a specified priority. |
| // Hooks with higher priority run last. |
| // Hooks with equal priority run in the order they were registered. |
| func AddLoadHookWithPriority(module Module, hook LoadHook, priority int) { |
| module.addLoadHook(LoadHookWithPriority{priority, hook}) |
| } |
| |
| func runAndRemoveLoadHooks(ctx *Context, config interface{}, module *moduleInfo, |
| scopedModuleFactories *map[string]ModuleFactory) (newModules []*moduleInfo, deps []string, errs []error) { |
| |
| if hooks := module.logicModule.getAndClearloadHooks(); len(hooks) > 0 { |
| // Sort the hooks by priority. |
| // Use SliceStable so that hooks with equal priority run in the order they were registered. |
| slices.SortStableFunc(hooks, func(i, j LoadHookWithPriority) int { return cmp.Compare(i.priority, j.priority) }) |
| |
| for _, hook := range hooks { |
| mctx := &loadHookContext{ |
| baseModuleContext: baseModuleContext{ |
| context: ctx, |
| config: config, |
| module: module, |
| }, |
| scopedModuleFactories: scopedModuleFactories, |
| } |
| hook.loadHook(mctx) |
| newModules = append(newModules, mctx.newModules...) |
| deps = append(deps, mctx.ninjaFileDeps...) |
| errs = append(errs, mctx.errs...) |
| } |
| |
| 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 { |
| file, errs := parser.Parse(filename, strings.NewReader(contents)) |
| 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 |
| } |
| } |