Merge pull request #5 from colincross/earlymutators
Support for early mutators
This commit is contained in:
commit
96555d687e
6 changed files with 635 additions and 320 deletions
782
context.go
782
context.go
File diff suppressed because it is too large
Load diff
4
doc.go
4
doc.go
|
@ -13,7 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
// Blueprint is a meta-build system that reads in Blueprints files that describe
|
// Blueprint is a meta-build system that reads in Blueprints files that describe
|
||||||
// modules that need to be built, and produces a Ninja
|
// modules that need to be built, and produces a Ninja
|
||||||
// (http://martine.github.io/ninja/) manifest describing the commands that need
|
// (http://martine.github.io/ninja/) manifest describing the commands that need
|
||||||
// to be run and their dependencies. Where most build systems use built-in
|
// to be run and their dependencies. Where most build systems use built-in
|
||||||
// rules or a domain-specific langauge to describe the logic how modules are
|
// rules or a domain-specific langauge to describe the logic how modules are
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
//
|
//
|
||||||
// Blueprint uses a bootstrapping process to allow the code for Blueprint,
|
// Blueprint uses a bootstrapping process to allow the code for Blueprint,
|
||||||
// the code for the build logic, and the code for the project being compiled
|
// the code for the build logic, and the code for the project being compiled
|
||||||
// to all live in the project. Dependencies between the layers are fully
|
// to all live in the project. Dependencies between the layers are fully
|
||||||
// tracked - a change to the logic code will cause the logic to be recompiled,
|
// tracked - a change to the logic code will cause the logic to be recompiled,
|
||||||
// regenerate the project build manifest, and run modified project rules. A
|
// regenerate the project build manifest, and run modified project rules. A
|
||||||
// change to Blueprint itself will cause Blueprint to rebuild, and then rebuild
|
// change to Blueprint itself will cause Blueprint to rebuild, and then rebuild
|
||||||
|
|
|
@ -167,3 +167,25 @@ func (l *liveTracker) addNinjaStringDeps(str *ninjaString) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *liveTracker) RemoveVariableIfLive(v Variable) bool {
|
||||||
|
l.Lock()
|
||||||
|
defer l.Unlock()
|
||||||
|
|
||||||
|
_, isLive := l.variables[v]
|
||||||
|
if isLive {
|
||||||
|
delete(l.variables, v)
|
||||||
|
}
|
||||||
|
return isLive
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *liveTracker) RemoveRuleIfLive(r Rule) bool {
|
||||||
|
l.Lock()
|
||||||
|
defer l.Unlock()
|
||||||
|
|
||||||
|
_, isLive := l.rules[r]
|
||||||
|
if isLive {
|
||||||
|
delete(l.rules, r)
|
||||||
|
}
|
||||||
|
return isLive
|
||||||
|
}
|
||||||
|
|
118
module_ctx.go
118
module_ctx.go
|
@ -118,6 +118,8 @@ type BaseModuleContext interface {
|
||||||
|
|
||||||
type DynamicDependerModuleContext interface {
|
type DynamicDependerModuleContext interface {
|
||||||
BaseModuleContext
|
BaseModuleContext
|
||||||
|
|
||||||
|
AddVariationDependencies([]Variation, ...string)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ModuleContext interface {
|
type ModuleContext interface {
|
||||||
|
@ -149,21 +151,21 @@ var _ BaseModuleContext = (*baseModuleContext)(nil)
|
||||||
type baseModuleContext struct {
|
type baseModuleContext struct {
|
||||||
context *Context
|
context *Context
|
||||||
config interface{}
|
config interface{}
|
||||||
group *moduleGroup
|
module *moduleInfo
|
||||||
errs []error
|
errs []error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *baseModuleContext) ModuleName() string {
|
func (d *baseModuleContext) ModuleName() string {
|
||||||
return d.group.properties.Name
|
return d.module.properties.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *baseModuleContext) ContainsProperty(name string) bool {
|
func (d *baseModuleContext) ContainsProperty(name string) bool {
|
||||||
_, ok := d.group.propertyPos[name]
|
_, ok := d.module.propertyPos[name]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *baseModuleContext) ModuleDir() string {
|
func (d *baseModuleContext) ModuleDir() string {
|
||||||
return filepath.Dir(d.group.relBlueprintsFile)
|
return filepath.Dir(d.module.relBlueprintsFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *baseModuleContext) Config() interface{} {
|
func (d *baseModuleContext) Config() interface{} {
|
||||||
|
@ -184,14 +186,14 @@ func (d *baseModuleContext) ModuleErrorf(format string,
|
||||||
|
|
||||||
d.errs = append(d.errs, &Error{
|
d.errs = append(d.errs, &Error{
|
||||||
Err: fmt.Errorf(format, args...),
|
Err: fmt.Errorf(format, args...),
|
||||||
Pos: d.group.pos,
|
Pos: d.module.pos,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *baseModuleContext) PropertyErrorf(property, format string,
|
func (d *baseModuleContext) PropertyErrorf(property, format string,
|
||||||
args ...interface{}) {
|
args ...interface{}) {
|
||||||
|
|
||||||
pos, ok := d.group.propertyPos[property]
|
pos, ok := d.module.propertyPos[property]
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(fmt.Errorf("property %q was not set for this module", property))
|
panic(fmt.Errorf("property %q was not set for this module", property))
|
||||||
}
|
}
|
||||||
|
@ -210,24 +212,23 @@ var _ ModuleContext = (*moduleContext)(nil)
|
||||||
|
|
||||||
type moduleContext struct {
|
type moduleContext struct {
|
||||||
baseModuleContext
|
baseModuleContext
|
||||||
module *moduleInfo
|
|
||||||
scope *localScope
|
scope *localScope
|
||||||
ninjaFileDeps []string
|
ninjaFileDeps []string
|
||||||
actionDefs localBuildActions
|
actionDefs localBuildActions
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *moduleContext) OtherModuleName(module Module) string {
|
func (m *moduleContext) OtherModuleName(logicModule Module) string {
|
||||||
info := m.context.moduleInfo[module]
|
module := m.context.moduleInfo[logicModule]
|
||||||
return info.group.properties.Name
|
return module.properties.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *moduleContext) OtherModuleErrorf(module Module, format string,
|
func (m *moduleContext) OtherModuleErrorf(logicModule Module, format string,
|
||||||
args ...interface{}) {
|
args ...interface{}) {
|
||||||
|
|
||||||
info := m.context.moduleInfo[module]
|
module := m.context.moduleInfo[logicModule]
|
||||||
m.errs = append(m.errs, &Error{
|
m.errs = append(m.errs, &Error{
|
||||||
Err: fmt.Errorf(format, args...),
|
Err: fmt.Errorf(format, args...),
|
||||||
Pos: info.group.pos,
|
Pos: module.pos,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +251,7 @@ func (m *moduleContext) VisitDepsDepthFirstIf(pred func(Module) bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *moduleContext) ModuleSubDir() string {
|
func (m *moduleContext) ModuleSubDir() string {
|
||||||
return m.module.subName()
|
return m.module.variantName
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *moduleContext) Variable(pctx *PackageContext, name, value string) {
|
func (m *moduleContext) Variable(pctx *PackageContext, name, value string) {
|
||||||
|
@ -308,13 +309,37 @@ func (m *moduleContext) VisitAllModuleVariants(visit func(Module)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// DynamicDependerModuleContext
|
||||||
|
//
|
||||||
|
|
||||||
|
type dynamicDependerModuleContext struct {
|
||||||
|
baseModuleContext
|
||||||
|
|
||||||
|
module *moduleInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddVariationDependencies adds deps as dependencies of the current module, but uses the variations
|
||||||
|
// argument to select which variant of the dependency to use. A variant of the dependency must
|
||||||
|
// exist that matches the all of the non-local variations of the current module, plus the variations
|
||||||
|
// argument.
|
||||||
|
func (mctx *dynamicDependerModuleContext) AddVariationDependencies(variations []Variation,
|
||||||
|
deps ...string) {
|
||||||
|
|
||||||
|
for _, dep := range deps {
|
||||||
|
errs := mctx.context.addVariationDependency(mctx.module, variations, dep)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
mctx.errs = append(mctx.errs, errs...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// MutatorContext
|
// MutatorContext
|
||||||
//
|
//
|
||||||
|
|
||||||
type mutatorContext struct {
|
type mutatorContext struct {
|
||||||
baseModuleContext
|
baseModuleContext
|
||||||
module *moduleInfo
|
|
||||||
name string
|
name string
|
||||||
dependenciesModified bool
|
dependenciesModified bool
|
||||||
}
|
}
|
||||||
|
@ -325,6 +350,13 @@ type baseMutatorContext interface {
|
||||||
Module() Module
|
Module() Module
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EarlyMutatorContext interface {
|
||||||
|
baseMutatorContext
|
||||||
|
|
||||||
|
CreateVariations(...string) []Module
|
||||||
|
CreateLocalVariations(...string) []Module
|
||||||
|
}
|
||||||
|
|
||||||
type TopDownMutatorContext interface {
|
type TopDownMutatorContext interface {
|
||||||
baseMutatorContext
|
baseMutatorContext
|
||||||
|
|
||||||
|
@ -338,12 +370,12 @@ type BottomUpMutatorContext interface {
|
||||||
baseMutatorContext
|
baseMutatorContext
|
||||||
|
|
||||||
AddDependency(module Module, name string)
|
AddDependency(module Module, name string)
|
||||||
CreateVariants(...string) []Module
|
CreateVariations(...string) []Module
|
||||||
SetDependencyVariant(string)
|
SetDependencyVariation(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Mutator function is called for each Module, and can use
|
// A Mutator function is called for each Module, and can use
|
||||||
// MutatorContext.CreateSubVariants to split a Module into multiple Modules,
|
// MutatorContext.CreateVariations to split a Module into multiple Modules,
|
||||||
// modifying properties on the new modules to differentiate them. It is called
|
// modifying properties on the new modules to differentiate them. It is called
|
||||||
// after parsing all Blueprint files, but before generating any build rules,
|
// after parsing all Blueprint files, but before generating any build rules,
|
||||||
// and is always called on dependencies before being called on the depending module.
|
// and is always called on dependencies before being called on the depending module.
|
||||||
|
@ -353,44 +385,59 @@ type BottomUpMutatorContext interface {
|
||||||
// if a second Mutator chooses to split the module a second time.
|
// if a second Mutator chooses to split the module a second time.
|
||||||
type TopDownMutator func(mctx TopDownMutatorContext)
|
type TopDownMutator func(mctx TopDownMutatorContext)
|
||||||
type BottomUpMutator func(mctx BottomUpMutatorContext)
|
type BottomUpMutator func(mctx BottomUpMutatorContext)
|
||||||
|
type EarlyMutator func(mctx EarlyMutatorContext)
|
||||||
|
|
||||||
// Split a module into mulitple variants, one for each name in the variantNames
|
// 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 variantNames
|
// parameter. It returns a list of new modules in the same order as the variationNames
|
||||||
// list.
|
// list.
|
||||||
//
|
//
|
||||||
// If any of the dependencies of the module being operated on were already split
|
// If any of the dependencies of the module being operated on were already split
|
||||||
// by calling CreateVariants with the same name, the dependency will automatically
|
// by calling CreateVariations with the same name, the dependency will automatically
|
||||||
// be updated to point the matching variant.
|
// be updated to point the matching variant.
|
||||||
//
|
//
|
||||||
// If a module is split, and then a module depending on the first module is not split
|
// 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
|
// when the Mutator is later called on it, the dependency of the depending module will
|
||||||
// automatically be updated to point to the first variant.
|
// automatically be updated to point to the first variant.
|
||||||
func (mctx *mutatorContext) CreateVariants(variantNames ...string) []Module {
|
func (mctx *mutatorContext) CreateVariations(variationNames ...string) []Module {
|
||||||
|
return mctx.createVariations(variationNames, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split a module into mulitple variants, one for each name in the variantNames
|
||||||
|
// parameter. It returns a list of new modules in the same order as the variantNames
|
||||||
|
// list.
|
||||||
|
//
|
||||||
|
// Local variations do not affect automatic dependency resolution - dependencies added
|
||||||
|
// to the split module via deps or DynamicDependerModule must exactly match a variant
|
||||||
|
// that contains all the non-local variations.
|
||||||
|
func (mctx *mutatorContext) CreateLocalVariations(variationNames ...string) []Module {
|
||||||
|
return mctx.createVariations(variationNames, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mctx *mutatorContext) createVariations(variationNames []string, local bool) []Module {
|
||||||
ret := []Module{}
|
ret := []Module{}
|
||||||
modules, errs := mctx.context.createVariants(mctx.module, mctx.name, variantNames)
|
modules, errs := mctx.context.createVariations(mctx.module, mctx.name, variationNames)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
mctx.errs = append(mctx.errs, errs...)
|
mctx.errs = append(mctx.errs, errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, module := range modules {
|
for i, module := range modules {
|
||||||
ret = append(ret, module.logicModule)
|
ret = append(ret, module.logicModule)
|
||||||
|
if !local {
|
||||||
|
module.dependencyVariant[mctx.name] = variationNames[i]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ret) != len(variantNames) {
|
if len(ret) != len(variationNames) {
|
||||||
panic("oops!")
|
panic("oops!")
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set all dangling dependencies on the current module to point to the variant
|
// Set all dangling dependencies on the current module to point to the variation
|
||||||
// with given name.
|
// with given name.
|
||||||
func (mctx *mutatorContext) SetDependencyVariant(variantName string) {
|
func (mctx *mutatorContext) SetDependencyVariation(variationName string) {
|
||||||
subName := subName{
|
mctx.context.convertDepsToVariation(mctx.module, mctx.name, variationName)
|
||||||
mutatorName: mctx.name,
|
|
||||||
variantName: variantName,
|
|
||||||
}
|
|
||||||
mctx.context.convertDepsToVariant(mctx.module, subName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mctx *mutatorContext) Module() Module {
|
func (mctx *mutatorContext) Module() Module {
|
||||||
|
@ -398,11 +445,14 @@ func (mctx *mutatorContext) Module() Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a dependency to the given module. The depender can be a specific variant
|
// Add a dependency to the given module. The depender can be a specific variant
|
||||||
// of a module, but the dependee must be a module that only has a single variant.
|
// of a module, but the dependee must be a module that has no variations.
|
||||||
// Does not affect the ordering of the current mutator pass, but will be ordered
|
// Does not affect the ordering of the current mutator pass, but will be ordered
|
||||||
// correctly for all future mutator passes.
|
// correctly for all future mutator passes.
|
||||||
func (mctx *mutatorContext) AddDependency(module Module, depName string) {
|
func (mctx *mutatorContext) AddDependency(module Module, depName string) {
|
||||||
mctx.context.addDependency(mctx.context.moduleInfo[module], depName)
|
errs := mctx.context.addDependency(mctx.context.moduleInfo[module], depName)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
mctx.errs = append(mctx.errs, errs...)
|
||||||
|
}
|
||||||
mctx.dependenciesModified = true
|
mctx.dependenciesModified = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
package blueprint
|
package blueprint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -288,6 +289,26 @@ func validateNinjaName(name string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toNinjaName(name string) string {
|
||||||
|
ret := bytes.Buffer{}
|
||||||
|
ret.Grow(len(name))
|
||||||
|
for _, r := range name {
|
||||||
|
valid := (r >= 'a' && r <= 'z') ||
|
||||||
|
(r >= 'A' && r <= 'Z') ||
|
||||||
|
(r >= '0' && r <= '9') ||
|
||||||
|
(r == '_') ||
|
||||||
|
(r == '-') ||
|
||||||
|
(r == '.')
|
||||||
|
if valid {
|
||||||
|
ret.WriteRune(r)
|
||||||
|
} else {
|
||||||
|
ret.WriteRune('_')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret.String()
|
||||||
|
}
|
||||||
|
|
||||||
var builtinRuleArgs = []string{"out", "in"}
|
var builtinRuleArgs = []string{"out", "in"}
|
||||||
|
|
||||||
func validateArgName(argName string) error {
|
func validateArgName(argName string) error {
|
||||||
|
|
|
@ -72,17 +72,17 @@ func (s *singletonContext) Config() interface{} {
|
||||||
|
|
||||||
func (s *singletonContext) ModuleName(logicModule Module) string {
|
func (s *singletonContext) ModuleName(logicModule Module) string {
|
||||||
module := s.context.moduleInfo[logicModule]
|
module := s.context.moduleInfo[logicModule]
|
||||||
return module.group.properties.Name
|
return module.properties.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *singletonContext) ModuleDir(logicModule Module) string {
|
func (s *singletonContext) ModuleDir(logicModule Module) string {
|
||||||
module := s.context.moduleInfo[logicModule]
|
module := s.context.moduleInfo[logicModule]
|
||||||
return filepath.Dir(module.group.relBlueprintsFile)
|
return filepath.Dir(module.relBlueprintsFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *singletonContext) BlueprintFile(logicModule Module) string {
|
func (s *singletonContext) BlueprintFile(logicModule Module) string {
|
||||||
module := s.context.moduleInfo[logicModule]
|
module := s.context.moduleInfo[logicModule]
|
||||||
return module.group.relBlueprintsFile
|
return module.relBlueprintsFile
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *singletonContext) ModuleErrorf(logicModule Module, format string,
|
func (s *singletonContext) ModuleErrorf(logicModule Module, format string,
|
||||||
|
@ -91,7 +91,7 @@ func (s *singletonContext) ModuleErrorf(logicModule Module, format string,
|
||||||
module := s.context.moduleInfo[logicModule]
|
module := s.context.moduleInfo[logicModule]
|
||||||
s.errs = append(s.errs, &Error{
|
s.errs = append(s.errs, &Error{
|
||||||
Err: fmt.Errorf(format, args...),
|
Err: fmt.Errorf(format, args...),
|
||||||
Pos: module.group.pos,
|
Pos: module.pos,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue