b2e7b5d036
Call PreGenerateBuildActions on each module that implments it after resolving dependencies but before calling GenerateBuildActions on any module. This allows the build logic to propagate top-down information, for example what variants of a library need to be compiled for the binaries it is used in. Change-Id: I2737504fa9d1a2b42ef747497de32c1c5129233d
263 lines
7.7 KiB
Go
263 lines
7.7 KiB
Go
package blueprint
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"text/scanner"
|
|
)
|
|
|
|
// A Module handles generating all of the Ninja build actions needed to build a
|
|
// single module that is defined in a Blueprints file. Module objects are
|
|
// 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).
|
|
//
|
|
// The Module implementation can access the build configuration as well as any
|
|
// modules on which on which it depends (as defined by the "deps" property
|
|
// specified in the Blueprints file or dynamically added by implementing the
|
|
// DynamicDependerModule interface) 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.
|
|
type Module interface {
|
|
// 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)
|
|
}
|
|
|
|
type preGenerateModule interface {
|
|
// PreGenerateBuildActions is called by the Context that created the Module
|
|
// during its generate phase, before calling GenerateBuildActions on
|
|
// any module. It should not touch any Ninja build actions.
|
|
PreGenerateBuildActions(PreModuleContext)
|
|
}
|
|
|
|
// 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.
|
|
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 DynamicDependerModuleContext interface {
|
|
ModuleName() string
|
|
ModuleDir() string
|
|
Config() interface{}
|
|
|
|
ContainsProperty(name string) bool
|
|
Errorf(pos scanner.Position, fmt string, args ...interface{})
|
|
ModuleErrorf(fmt string, args ...interface{})
|
|
PropertyErrorf(property, fmt string, args ...interface{})
|
|
Failed() bool
|
|
}
|
|
|
|
type PreModuleContext interface {
|
|
DynamicDependerModuleContext
|
|
|
|
OtherModuleName(m Module) string
|
|
OtherModuleErrorf(m Module, fmt string, args ...interface{})
|
|
|
|
VisitDepsDepthFirst(visit func(Module))
|
|
VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
|
|
}
|
|
|
|
type ModuleContext interface {
|
|
PreModuleContext
|
|
|
|
Variable(pctx *PackageContext, name, value string)
|
|
Rule(pctx *PackageContext, name string, params RuleParams, argNames ...string) Rule
|
|
Build(pctx *PackageContext, params BuildParams)
|
|
|
|
AddNinjaFileDeps(deps ...string)
|
|
}
|
|
|
|
var _ DynamicDependerModuleContext = (*dynamicDependerModuleContext)(nil)
|
|
|
|
type dynamicDependerModuleContext struct {
|
|
context *Context
|
|
config interface{}
|
|
info *moduleInfo
|
|
errs []error
|
|
}
|
|
|
|
func (d *dynamicDependerModuleContext) ModuleName() string {
|
|
return d.info.properties.Name
|
|
}
|
|
|
|
func (d *dynamicDependerModuleContext) ContainsProperty(name string) bool {
|
|
_, ok := d.info.propertyPos[name]
|
|
return ok
|
|
}
|
|
|
|
func (d *dynamicDependerModuleContext) ModuleDir() string {
|
|
return filepath.Dir(d.info.relBlueprintsFile)
|
|
}
|
|
|
|
func (d *dynamicDependerModuleContext) Config() interface{} {
|
|
return d.config
|
|
}
|
|
|
|
func (d *dynamicDependerModuleContext) Errorf(pos scanner.Position,
|
|
format string, args ...interface{}) {
|
|
|
|
d.errs = append(d.errs, &Error{
|
|
Err: fmt.Errorf(format, args...),
|
|
Pos: pos,
|
|
})
|
|
}
|
|
|
|
func (d *dynamicDependerModuleContext) ModuleErrorf(format string,
|
|
args ...interface{}) {
|
|
|
|
d.errs = append(d.errs, &Error{
|
|
Err: fmt.Errorf(format, args...),
|
|
Pos: d.info.pos,
|
|
})
|
|
}
|
|
|
|
func (d *dynamicDependerModuleContext) PropertyErrorf(property, format string,
|
|
args ...interface{}) {
|
|
|
|
pos, ok := d.info.propertyPos[property]
|
|
if !ok {
|
|
panic(fmt.Errorf("property %q was not set for this module", property))
|
|
}
|
|
|
|
d.errs = append(d.errs, &Error{
|
|
Err: fmt.Errorf(format, args...),
|
|
Pos: pos,
|
|
})
|
|
}
|
|
|
|
func (d *dynamicDependerModuleContext) Failed() bool {
|
|
return len(d.errs) > 0
|
|
}
|
|
|
|
var _ PreModuleContext = (*preModuleContext)(nil)
|
|
|
|
type preModuleContext struct {
|
|
dynamicDependerModuleContext
|
|
module Module
|
|
}
|
|
|
|
func (m *preModuleContext) OtherModuleName(module Module) string {
|
|
info := m.context.moduleInfo[module]
|
|
return info.properties.Name
|
|
}
|
|
|
|
func (m *preModuleContext) OtherModuleErrorf(module Module, format string,
|
|
args ...interface{}) {
|
|
|
|
info := m.context.moduleInfo[module]
|
|
m.errs = append(m.errs, &Error{
|
|
Err: fmt.Errorf(format, args...),
|
|
Pos: info.pos,
|
|
})
|
|
}
|
|
|
|
func (m *preModuleContext) VisitDepsDepthFirst(visit func(Module)) {
|
|
m.context.visitDepsDepthFirst(m.module, visit)
|
|
}
|
|
|
|
func (m *preModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool,
|
|
visit func(Module)) {
|
|
|
|
m.context.visitDepsDepthFirstIf(m.module, pred, visit)
|
|
}
|
|
|
|
var _ ModuleContext = (*moduleContext)(nil)
|
|
|
|
type moduleContext struct {
|
|
preModuleContext
|
|
scope *localScope
|
|
ninjaFileDeps []string
|
|
actionDefs localBuildActions
|
|
}
|
|
|
|
func (m *moduleContext) Variable(pctx *PackageContext, name, value string) {
|
|
m.scope.ReparentTo(pctx)
|
|
|
|
v, err := m.scope.AddLocalVariable(name, value)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
m.actionDefs.variables = append(m.actionDefs.variables, v)
|
|
}
|
|
|
|
func (m *moduleContext) Rule(pctx *PackageContext, name string,
|
|
params RuleParams, argNames ...string) Rule {
|
|
|
|
m.scope.ReparentTo(pctx)
|
|
|
|
r, err := m.scope.AddLocalRule(name, ¶ms, argNames...)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
m.actionDefs.rules = append(m.actionDefs.rules, r)
|
|
|
|
return r
|
|
}
|
|
|
|
func (m *moduleContext) Build(pctx *PackageContext, params BuildParams) {
|
|
m.scope.ReparentTo(pctx)
|
|
|
|
def, err := parseBuildParams(m.scope, ¶ms)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
m.actionDefs.buildDefs = append(m.actionDefs.buildDefs, def)
|
|
}
|
|
|
|
func (m *moduleContext) AddNinjaFileDeps(deps ...string) {
|
|
m.ninjaFileDeps = append(m.ninjaFileDeps, deps...)
|
|
}
|