Merge pull request #247 from colincross/mutator_ctx

Improve module, mutator and singleton contexts
This commit is contained in:
colincross 2019-05-21 11:26:19 -07:00 committed by GitHub
commit abd66e6c82
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 302 additions and 121 deletions

View file

@ -173,6 +173,7 @@ type moduleInfo struct {
relBlueprintsFile string
pos scanner.Position
propertyPos map[string]scanner.Position
createdBy *moduleInfo
variantName string
variant variationMap
@ -183,12 +184,13 @@ type moduleInfo struct {
properties []interface{}
// set during ResolveDependencies
directDeps []depInfo
missingDeps []string
missingDeps []string
newDirectDeps []depInfo
// set during updateDependencies
reverseDeps []*moduleInfo
forwardDeps []*moduleInfo
directDeps []depInfo
// used by parallelVisitAllBottomUp
waitingCount int
@ -214,6 +216,10 @@ func (module *moduleInfo) String() string {
if module.variantName != "" {
s += fmt.Sprintf(" variant %q", module.variantName)
}
if module.createdBy != nil {
s += fmt.Sprintf(" (created by %s)", module.createdBy)
}
return s
}
@ -1427,7 +1433,7 @@ func (c *Context) addDependency(module *moduleInfo, tag DependencyTag, depName s
}
if m := c.findMatchingVariant(module, possibleDeps); m != nil {
module.directDeps = append(module.directDeps, depInfo{m, tag})
module.newDirectDeps = append(module.newDirectDeps, depInfo{m, tag})
atomic.AddUint32(&c.depsModified, 1)
return nil
}
@ -1530,7 +1536,7 @@ func (c *Context) addVariationDependency(module *moduleInfo, variations []Variat
Pos: module.pos,
}}
}
module.directDeps = append(module.directDeps, depInfo{m, tag})
module.newDirectDeps = append(module.newDirectDeps, depInfo{m, tag})
atomic.AddUint32(&c.depsModified, 1)
return nil
}
@ -1575,7 +1581,7 @@ func (c *Context) addInterVariantDependency(origModule *moduleInfo, tag Dependen
origModule.Name()))
}
fromInfo.directDeps = append(fromInfo.directDeps, depInfo{toInfo, tag})
fromInfo.newDirectDeps = append(fromInfo.newDirectDeps, depInfo{toInfo, tag})
atomic.AddUint32(&c.depsModified, 1)
}
@ -2149,9 +2155,18 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo,
module.directDeps[j].module = dep.module.splitModules[0]
}
}
if module.createdBy != nil && module.createdBy.logicModule == nil {
module.createdBy = module.createdBy.splitModules[0]
}
// Add in any new direct dependencies that were added by the mutator
module.directDeps = append(module.directDeps, module.newDirectDeps...)
module.newDirectDeps = nil
}
}
// Add in any new reverse dependencies that were added by the mutator
for module, deps := range reverseDeps {
sort.Sort(depSorter(deps))
module.directDeps = append(module.directDeps, deps...)

View file

@ -121,15 +121,39 @@ type DynamicDependerModule interface {
}
type BaseModuleContext 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 defintion of the module.
ModuleDir() string
// ModuleType returns the name of the module type that was used to create the module, as specified in
// RegisterModuleType.
ModuleType() 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
@ -140,13 +164,103 @@ type BaseModuleContext interface {
// 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
// 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))
// VisitDepsDepthFirst 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)
// 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
}
type DynamicDependerModuleContext BottomUpMutatorContext
@ -154,32 +268,41 @@ type DynamicDependerModuleContext BottomUpMutatorContext
type ModuleContext interface {
BaseModuleContext
OtherModuleName(m Module) string
OtherModuleDir(m Module) string
OtherModuleSubDir(m Module) string
OtherModuleType(m Module) string
OtherModuleErrorf(m Module, fmt string, args ...interface{})
OtherModuleDependencyTag(m Module) DependencyTag
GetDirectDepWithTag(name string, tag DependencyTag) Module
GetDirectDep(name string) (Module, DependencyTag)
VisitDirectDeps(visit func(Module))
VisitDirectDepsIf(pred func(Module) bool, visit func(Module))
VisitDepsDepthFirst(visit func(Module))
VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
WalkDeps(visit func(child, parent Module) bool)
// 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)
// 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))
// 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
}
@ -199,6 +322,10 @@ 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()
}
@ -327,9 +454,6 @@ func (m *baseModuleContext) OtherModuleErrorf(logicModule Module, format string,
})
}
// 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.
func (m *baseModuleContext) OtherModuleDependencyTag(logicModule Module) DependencyTag {
// fast path for calling OtherModuleDependencyTag from inside VisitDirectDeps
if logicModule == m.visitingDep.module.logicModule {
@ -345,9 +469,11 @@ func (m *baseModuleContext) OtherModuleDependencyTag(logicModule Module) Depende
return nil
}
// 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.
func (m *baseModuleContext) OtherModuleExists(name string) bool {
_, exists := m.context.nameInterface.ModuleFromName(name, m.module.namespace())
return exists
}
func (m *baseModuleContext) GetDirectDep(name string) (Module, DependencyTag) {
for _, dep := range m.module.directDeps {
if dep.module.Name() == name {
@ -358,8 +484,6 @@ func (m *baseModuleContext) GetDirectDep(name string) (Module, DependencyTag) {
return nil, nil
}
// 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.
func (m *baseModuleContext) GetDirectDepWithTag(name string, tag DependencyTag) Module {
var deps []depInfo
for _, dep := range m.module.directDeps {
@ -378,8 +502,6 @@ func (m *baseModuleContext) GetDirectDepWithTag(name string, tag DependencyTag)
return nil
}
// 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.
func (m *baseModuleContext) VisitDirectDeps(visit func(Module)) {
defer func() {
if r := recover(); r != nil {
@ -399,9 +521,6 @@ func (m *baseModuleContext) VisitDirectDeps(visit func(Module)) {
m.visitingDep = depInfo{}
}
// 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.
func (m *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) {
defer func() {
if r := recover(); r != nil {
@ -423,10 +542,6 @@ func (m *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func
m.visitingDep = depInfo{}
}
// 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.
func (m *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) {
defer func() {
if r := recover(); r != nil {
@ -445,11 +560,6 @@ func (m *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) {
m.visitingDep = depInfo{}
}
// VisitDepsDepthFirst 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.
func (m *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool,
visit func(Module)) {
@ -472,10 +582,6 @@ func (m *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool,
m.visitingDep = depInfo{}
}
// 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.
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
@ -563,54 +669,113 @@ type mutatorContext struct {
newModules []*moduleInfo // brand new modules
}
type baseMutatorContext interface {
type BaseMutatorContext interface {
BaseModuleContext
OtherModuleExists(name string) bool
// 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)
Module() Module
}
type EarlyMutatorContext interface {
baseMutatorContext
BaseMutatorContext
// CreateVariations splits a module into mulitple variants, one for each name in the variationNames
// parameter. It returns a list of new modules in the same order as the variationNames
// list.
//
// If any of the dependencies of the module being operated on were already split
// by calling CreateVariations with the same name, the dependency will automatically
// be updated to point the matching variant.
//
// If a module is split, and then a module depending on the first module is not split
// when the Mutator is later called on it, the dependency of the depending module will
// automatically be updated to point to the first variant.
CreateVariations(...string) []Module
// CreateLocationVariations splits a module into mulitple variants, one for each name in the variantNames
// parameter. It returns a list of new modules in the same order as the variantNames
// list.
//
// Local variations do not affect automatic dependency resolution - dependencies added
// to the split module via deps or DynamicDependerModule must exactly match a variant
// that contains all the non-local variations.
CreateLocalVariations(...string) []Module
}
type TopDownMutatorContext interface {
baseMutatorContext
OtherModuleName(m Module) string
OtherModuleDir(m Module) string
OtherModuleSubDir(m Module) string
OtherModuleType(m Module) string
OtherModuleErrorf(m Module, fmt string, args ...interface{})
OtherModuleDependencyTag(m Module) DependencyTag
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{})
GetDirectDepWithTag(name string, tag DependencyTag) Module
GetDirectDep(name string) (Module, DependencyTag)
VisitDirectDeps(visit func(Module))
VisitDirectDepsIf(pred func(Module) bool, visit func(Module))
VisitDepsDepthFirst(visit func(Module))
VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
WalkDeps(visit func(Module, Module) bool)
}
type BottomUpMutatorContext interface {
baseMutatorContext
BaseMutatorContext
// AddDependency adds a dependency to the given module.
// 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)
// 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 mulitple variants, one for each name in the variationNames
// parameter. It returns a list of new modules in the same order as the variationNames
// list.
//
// If any of the dependencies of the module being operated on were already split
// by calling CreateVariations with the same name, the dependency will automatically
// be updated to point the matching variant.
//
// If a module is split, and then a module depending on the first module is not split
// when the Mutator is later called on it, the dependency of the depending module will
// automatically be updated to point to the first variant.
CreateVariations(...string) []Module
// CreateLocationVariations splits a module into mulitple variants, one for each name in the variantNames
// parameter. It returns a list of new modules in the same order as the variantNames
// list.
//
// Local variations do not affect automatic dependency resolution - dependencies added
// to the split module via deps or DynamicDependerModule must exactly match a variant
// that contains all the non-local variations.
CreateLocalVariations(...string) []Module
// SetDependencyVariation sets all dangling dependencies on the current module to point to the variation
// with given name.
SetDependencyVariation(string)
// AddVariationDependencies adds deps as dependencies of the current module, but uses the variations
// argument to select which variant of the dependency to use. A variant of the dependency must
// exist that matches the all of the non-local variations of the current module, plus the variations
// argument.
AddVariationDependencies([]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. 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.
AddFarVariationDependencies([]Variation, DependencyTag, ...string)
// AddInterVariantDependency adds a dependency between two variants of the same module. Variants are always
// ordered in the same orderas 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)
}
@ -643,28 +808,10 @@ func (BaseDependencyTag) dependencyTag(DependencyTag) {
var _ DependencyTag = BaseDependencyTag{}
// Split a module into mulitple variants, one for each name in the variationNames
// parameter. It returns a list of new modules in the same order as the variationNames
// list.
//
// If any of the dependencies of the module being operated on were already split
// by calling CreateVariations with the same name, the dependency will automatically
// be updated to point the matching variant.
//
// If a module is split, and then a module depending on the first module is not split
// when the Mutator is later called on it, the dependency of the depending module will
// automatically be updated to point to the first variant.
func (mctx *mutatorContext) CreateVariations(variationNames ...string) []Module {
return mctx.createVariations(variationNames, false)
}
// Split a module into mulitple variants, one for each name in the variantNames
// parameter. It returns a list of new modules in the same order as the variantNames
// list.
//
// Local variations do not affect automatic dependency resolution - dependencies added
// to the split module via deps or DynamicDependerModule must exactly match a variant
// that contains all the non-local variations.
func (mctx *mutatorContext) CreateLocalVariations(variationNames ...string) []Module {
return mctx.createVariations(variationNames, true)
}
@ -695,8 +842,6 @@ func (mctx *mutatorContext) createVariations(variationNames []string, local bool
return ret
}
// Set all dangling dependencies on the current module to point to the variation
// with given name.
func (mctx *mutatorContext) SetDependencyVariation(variationName string) {
mctx.context.convertDepsToVariation(mctx.module, mctx.name, variationName)
}
@ -705,9 +850,6 @@ func (mctx *mutatorContext) Module() Module {
return mctx.module.logicModule
}
// Add a dependency to the given module.
// Does not affect the ordering of the current mutator pass, but will be ordered
// correctly for all future mutator passes.
func (mctx *mutatorContext) AddDependency(module Module, tag DependencyTag, deps ...string) {
for _, dep := range deps {
modInfo := mctx.context.moduleInfo[module]
@ -718,11 +860,6 @@ func (mctx *mutatorContext) AddDependency(module Module, tag DependencyTag, deps
}
}
// Add 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.
func (mctx *mutatorContext) AddReverseDependency(module Module, tag DependencyTag, destName string) {
if _, ok := tag.(BaseDependencyTag); ok {
panic("BaseDependencyTag is not allowed to be used directly!")
@ -740,10 +877,6 @@ func (mctx *mutatorContext) AddReverseDependency(module Module, tag DependencyTa
})
}
// AddVariationDependencies adds deps as dependencies of the current module, but uses the variations
// argument to select which variant of the dependency to use. A variant of the dependency must
// exist that matches the all of the non-local variations of the current module, plus the variations
// argument.
func (mctx *mutatorContext) AddVariationDependencies(variations []Variation, tag DependencyTag,
deps ...string) {
@ -755,13 +888,6 @@ func (mctx *mutatorContext) AddVariationDependencies(variations []Variation, tag
}
}
// AddFarVariationDependencies adds deps as dependencies of the current module, but uses the
// variations argument to select which variant of the dependency to use. A variant of the
// dependency must exist that matches the 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
// depdendency only needs to match the supplied variations.
func (mctx *mutatorContext) AddFarVariationDependencies(variations []Variation, tag DependencyTag,
deps ...string) {
@ -777,9 +903,6 @@ func (mctx *mutatorContext) AddInterVariantDependency(tag DependencyTag, from, t
mctx.context.addInterVariantDependency(mctx.module, tag, from, to)
}
// 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.
func (mctx *mutatorContext) ReplaceDependencies(name string) {
target := mctx.context.moduleMatchingVariant(mctx.module, name)
@ -791,24 +914,17 @@ func (mctx *mutatorContext) ReplaceDependencies(name string) {
mctx.replace = append(mctx.replace, replace{target, mctx.module})
}
func (mctx *mutatorContext) OtherModuleExists(name string) bool {
_, exists := mctx.context.nameInterface.ModuleFromName(name, mctx.module.namespace())
return exists
}
// 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.
func (mctx *mutatorContext) Rename(name string) {
mctx.rename = append(mctx.rename, rename{mctx.module.group, name})
}
// Create a new module by calling the factory method for the specified moduleType, and apply
// the specified property structs to it as if the properties were set in a blueprint file.
func (mctx *mutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) {
module := mctx.context.newModule(factory)
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)

View file

@ -25,23 +25,51 @@ type Singleton interface {
}
type SingletonContext interface {
// Config returns the config object that was passed to Context.PrepareBuildActions.
Config() interface{}
// Name returns the name of the current singleton passed to Context.RegisterSingletonType
Name() string
// ModuleName returns the name of the given Module. See BaseModuleContext.ModuleName for more information.
ModuleName(module Module) string
// ModuleDir returns the directory of the given Module. See BaseModuleContext.ModuleDir for more information.
ModuleDir(module Module) string
// ModuleSubDir returns the unique subdirectory name of the given Module. See ModuleContext.ModuleSubDir for
// more information.
ModuleSubDir(module Module) string
// ModuleType returns the type of the given Module. See BaseModuleContext.ModuleType for more information.
ModuleType(module Module) string
// BlueprintFile returns the path of the Blueprint file that defined the given module.
BlueprintFile(module Module) string
// ModuleErrorf reports an error at the line number of the module type in the module definition.
ModuleErrorf(module Module, format string, args ...interface{})
// Errorf reports an error at the specified position of the module definition file.
Errorf(format string, args ...interface{})
// Failed returns true if any errors have been reported. In most cases the singleton 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 singleton from creating necessary data it can return early when Failed returns true.
Failed() bool
// Variable creates a new ninja variable scoped to the singleton. It can be referenced by calls to Rule and Build
// in the same singleton.
Variable(pctx PackageContext, name, value string)
// Rule creates a new ninja rule scoped to the singleton. It can be referenced by calls to Build in the same
// singleton.
Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule
// Build creates a new ninja build statement.
Build(pctx PackageContext, params BuildParams)
// RequireNinjaVersion sets the generated ninja manifest to require at least the specified version of ninja.
RequireNinjaVersion(major, minor, micro int)
// SetNinjaBuildDir sets the value of the top-level "builddir" Ninja variable
@ -58,17 +86,37 @@ type SingletonContext interface {
// are expanded in the scope of the PackageContext.
Eval(pctx PackageContext, ninjaStr string) (string, error)
// VisitAllModules calls visit for each defined variant of each module in an unspecified order.
VisitAllModules(visit func(Module))
// VisitAllModules calls pred for each defined variant of each module in an unspecified order, and if pred returns
// true calls visit.
VisitAllModulesIf(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.
VisitDepsDepthFirst(module Module, visit func(Module))
// VisitDepsDepthFirst 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.
VisitDepsDepthFirstIf(module Module, pred func(Module) bool,
visit func(Module))
// VisitAllModuleVariants calls visit for each variant of the given module.
VisitAllModuleVariants(module Module, visit func(Module))
// PrimaryModule returns the first variant of the given module. This can be used to perform
// // singleton actions that are only done once for all variants of a module.
PrimaryModule(module Module) Module
// FinalModule returns the last variant of the given module. This can be used to perform
// singleton actions that are only done once for all variants of a module.
FinalModule(module Module) Module
// 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)
// GlobWithDeps returns a list of files and directories that match the
@ -79,6 +127,8 @@ type SingletonContext interface {
// 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 singleton to be used in build system tests that run against a mock filesystem.
Fs() pathtools.FileSystem
}
@ -223,7 +273,7 @@ func (s *singletonContext) VisitAllModules(visit func(Module)) {
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "VisitAllModules(%s) for module %s",
funcName(visit), visitingModule))
funcName(visit), s.context.moduleInfo[visitingModule]))
}
}()