Merge pull request #5 from colincross/earlymutators

Support for early mutators
This commit is contained in:
colincross 2015-03-13 19:56:52 -07:00
commit 96555d687e
6 changed files with 635 additions and 320 deletions

File diff suppressed because it is too large Load diff

4
doc.go
View file

@ -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

View file

@ -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
}

View file

@ -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
} }

View file

@ -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 {

View file

@ -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,
}) })
} }