Document most of the blueprint package APIs.

This change adds docs to all the blueprint package APIs except for the Module
and Singleton types and their corresponding context types.

Change-Id: I74aa48c7743086ad79b3122d5813f5c4823c6519
This commit is contained in:
Jamie Gennis 2014-06-12 20:06:50 -07:00
parent ec7012824a
commit d4e1018e19
3 changed files with 298 additions and 34 deletions

View file

@ -19,6 +19,30 @@ var ErrBuildActionsNotReady = errors.New("build actions are not ready")
const maxErrors = 10
// A Context contains all the state needed to parse a set of Blueprints files
// and generate a Ninja file. The process of generating a Ninja file proceeds
// through a series of four phases. Each phase corresponds with a some methods
// on the Context object
//
// Phase Methods
// ------------ -------------------------------------------
// 1. Registration RegisterModuleType, RegisterSingleton
//
// 2. Parse ParseBlueprintsFiles, Parse
//
// 3. Generate ResovleDependencies, PrepareBuildActions
//
// 4. Write WriteBuildFile
//
// The registration phase prepares the context to process Blueprints files
// containing various types of modules. The parse phase reads in one or more
// Blueprints files and validates their contents against the module types that
// have been registered. The generate phase then analyzes the parsed Blueprints
// contents to create an internal representation for the build actions that must
// be performed. This phase also performs validation of the module dependencies
// and property values defined in the parsed Blueprints files. Finally, the
// write phase generates the Ninja manifest text based on the generated build
// actions.
type Context struct {
// set at instantiation
moduleTypes map[string]ModuleType
@ -45,9 +69,11 @@ type Context struct {
requiredNinjaMicro int // For the ninja_required_version variable
}
// An Error describes a problem that was encountered that is related to a
// particular location in a Blueprints file.
type Error struct {
Err error
Pos scanner.Position
Err error // the error that occurred
Pos scanner.Position // the relevant Blueprints file location
}
type localBuildActions struct {
@ -88,6 +114,9 @@ func (e *Error) Error() string {
return fmt.Sprintf("%s: %s", e.Pos, e.Err)
}
// NewContext creates a new Context object. The created context initially has
// no module types or singletons registered, so the RegisterModuleType and
// RegisterSingleton methods must be called before it can do anything useful.
func NewContext() *Context {
return &Context{
moduleTypes: make(map[string]ModuleType),
@ -97,6 +126,18 @@ func NewContext() *Context {
}
}
// RegisterModuleType associates a module type name (which can appear in a
// Blueprints file) with a ModuleType object. When the given module type name
// is encountered in a Blueprints file during parsing, the ModuleType object
// will be used to instantiate a new Module object to handle the build action
// generation for the module.
//
// The module type names given here must be unique for the context. Note that
// these module type names are different from the name passed to MakeModuleType.
// The name given here is how the module type is referenced in a Blueprints
// file, while the name passed to MakeModuleType indicates the name of the Go
// ModuleType object (i.e. it's used to when reporting build logic problems to
// make finding the problematic code easier).
func (c *Context) RegisterModuleType(name string, typ ModuleType) {
if _, present := c.moduleTypes[name]; present {
panic(errors.New("module type name is already registered"))
@ -104,6 +145,9 @@ func (c *Context) RegisterModuleType(name string, typ ModuleType) {
c.moduleTypes[name] = typ
}
// RegisterSingleton registers a singleton object that will be invoked to
// generate build actions. Each registered singleton is invoked exactly once as
// part of the generate phase.
func (c *Context) RegisterSingleton(name string, singleton Singleton) {
if _, present := c.singletonInfo[name]; present {
panic(errors.New("singleton name is already registered"))
@ -132,10 +176,30 @@ func singletonTypeName(singleton Singleton) string {
return typ.PkgPath() + "." + typ.Name()
}
// SetIgnoreUnknownModuleTypes sets the behavior of the context in the case
// where it encounters an unknown module type while parsing Blueprints files. By
// default, the context will report unknown module types as an error. If this
// method is called with ignoreUnknownModuleTypes set to true then the context
// will silently ignore unknown module types.
//
// This method should generally not be used. It exists to facilitate the
// bootstrapping process.
func (c *Context) SetIgnoreUnknownModuleTypes(ignoreUnknownModuleTypes bool) {
c.ignoreUnknownModuleTypes = ignoreUnknownModuleTypes
}
// Parse parses a single Blueprints file from r, creating Module objects for
// each of the module definitions encountered. If the Blueprints file contains
// an assignment to the "subdirs" variable, then the subdirectories listed are
// returned in the subdirs first return value.
//
// rootDir specifies the path to the root directory of the source tree, while
// filename specifies the path to the Blueprints file. These paths are used for
// error reporting and for determining the module's directory.
//
// This method should probably not be used directly. It is provided to simplify
// testing. Instead ParseBlueprintsFiles should be called to parse a set of
// Blueprints files starting from a top-level Blueprints file.
func (c *Context) Parse(rootDir, filename string, r io.Reader) (subdirs []string,
errs []error) {
@ -191,6 +255,15 @@ func (c *Context) Parse(rootDir, filename string, r io.Reader) (subdirs []string
return subdirs, errs
}
// ParseBlueprintsFiles parses a set of Blueprints files starting with the file
// at rootFile. When it encounters a Blueprints file with a set of subdirs
// listed it recursively parses any Blueprints files found in those
// subdirectories.
//
// If no errors are encountered while parsing the files, the list of paths on
// which the future output will depend is returned. This list will include both
// Blueprints file paths as well as directory paths for cases where wildcard
// subdirs are found.
func (c *Context) ParseBlueprintsFiles(rootFile string) (deps []string,
errs []error) {
@ -433,6 +506,10 @@ func (c *Context) processModuleDef(moduleDef *parser.Module,
return nil
}
// ResolveDependencies checks that the dependencies specified by all of the
// modules defined in the parsed Blueprints files are valid. This means that
// the modules depended upon are defined and that no circular dependencies
// exist.
func (c *Context) ResolveDependencies() []error {
errs := c.resolveDependencies()
if len(errs) > 0 {
@ -559,6 +636,19 @@ func (c *Context) checkForDependencyCycles() (errs []error) {
return
}
// PrepareBuildActions generates an internal representation of all the build
// actions that need to be performed. This process involves invoking the
// GenerateBuildActions method on each of the Module objects created during the
// parse phase and then on each of the registered Singleton objects.
//
// If the ResolveDependencies method has not already been called it is called
// automatically by this method.
//
// The config argument is made available to all of the Module and Singleton
// objects via the Config method on the ModuleContext and SingletonContext
// objects passed to GenerateBuildActions. It is also passed to the functions
// specified via PoolFunc, RuleFunc, and VariableFunc so that they can compute
// config-specific values.
func (c *Context) PrepareBuildActions(config interface{}) []error {
c.buildActionsReady = false
@ -954,6 +1044,9 @@ func (c *Context) checkForVariableReferenceCycles(
}
}
// WriteBuildFile writes the Ninja manifeset text for the generated build
// actions to w. If this is called before PrepareBuildActions successfully
// completes then ErrBuildActionsNotReady is returned.
func (c *Context) WriteBuildFile(w io.Writer) error {
if !c.buildActionsReady {
return ErrBuildActionsNotReady

View file

@ -86,13 +86,15 @@ func callerPackage() *pkg {
return p
}
// Import enables access to the global Ninja rules and variables that are
// exported by another Go package. It may only be called from a Go package's
// init() function. The Go package path passed to Import must have already been
// imported into the Go package using a Go import statement. The imported
// variables may then be accessed from Ninja strings as "${pkg.Variable}", while
// the imported rules can simply be accessed as exported Go variables from the
// package. For example:
// Import enables access to the exported Ninja pools, rules, and variables that
// are defined at the package scope of another Go package. Go's visibility
// rules apply to these references - capitalized names indicate that something
// is exported. It may only be called from a Go package's init() function. The
// Go package path passed to Import must have already been imported into the Go
// package using a Go import statement. The imported variables may then be
// accessed from Ninja strings as "${pkg.Variable}", while the imported rules
// can simply be accessed as exported Go variables from the package. For
// example:
//
// import (
// "blueprint"
@ -111,6 +113,12 @@ func callerPackage() *pkg {
// Outputs: []string{"${bar.SomeVariable}"},
// })
// }
//
// Note that the local name used to refer to the package in Ninja variable names
// is derived from pkgPath by extracting the last path component. This differs
// from Go's import declaration, which derives the local name from the package
// clause in the imported package. By convention these names are made to match,
// but this is not required.
func Import(pkgPath string) {
callerPkg := callerPackage()
@ -125,6 +133,9 @@ func Import(pkgPath string) {
}
}
// ImportAs provides the same functionality as Import, but it allows the local
// name that will be used to refer to the package to be specified explicitly.
// It may only be called from a Go package's init() function.
func ImportAs(as, pkgPath string) {
callerPkg := callerPackage()
@ -150,8 +161,15 @@ type staticVariable struct {
value_ string
}
// StaticVariable returns a Variable that does not depend on any configuration
// information.
// StaticVariable returns a Variable whose value does not depend on any
// configuration information. It may only be called during a Go package's
// initialization - either from the init() function or as part of a package-
// scoped variable's initialization.
//
// This function is usually used to initialize a package-scoped Go variable that
// represents a Ninja variable that will be output. The name argument should
// exactly match the Go variable name, and the value string may reference other
// Ninja variables that are visible within the calling Go package.
func StaticVariable(name, value string) Variable {
err := validateNinjaName(name)
if err != nil {
@ -192,9 +210,19 @@ type variableFunc struct {
}
// VariableFunc returns a Variable whose value is determined by a function that
// takes a interface{} object as input and returns either the variable value or an
// error.
func VariableFunc(name string, f func(interface{}) (string, error)) Variable {
// takes a config object as input and returns either the variable value or an
// error. It may only be called during a Go package's initialization - either
// from the init() function or as part of a package-scoped variable's
// initialization.
//
// This function is usually used to initialize a package-scoped Go variable that
// represents a Ninja variable that will be output. The name argument should
// exactly match the Go variable name, and the value string returned by f may
// reference other Ninja variables that are visible within the calling Go
// package.
func VariableFunc(name string, f func(config interface{}) (string,
error)) Variable {
err := validateNinjaName(name)
if err != nil {
panic(err)
@ -212,8 +240,16 @@ func VariableFunc(name string, f func(interface{}) (string, error)) Variable {
}
// VariableConfigMethod returns a Variable whose value is determined by calling
// a method on the interface{} object. The method must take no arguments and return
// a single string that will be the variable's value.
// a method on the config object. The method must take no arguments and return
// a single string that will be the variable's value. It may only be called
// during a Go package's initialization - either from the init() function or as
// part of a package-scoped variable's initialization.
//
// This function is usually used to initialize a package-scoped Go variable that
// represents a Ninja variable that will be output. The name argument should
// exactly match the Go variable name, and the value string returned by method
// may reference other Ninja variables that are visible within the calling Go
// package.
func VariableConfigMethod(name string, method interface{}) Variable {
err := validateNinjaName(name)
if err != nil {
@ -311,6 +347,15 @@ type staticPool struct {
params PoolParams
}
// StaticPool returns a Pool whose value does not depend on any configuration
// information. It may only be called during a Go package's initialization -
// either from the init() function or as part of a package-scoped Go variable's
// initialization.
//
// This function is usually used to initialize a package-scoped Go variable that
// represents a Ninja pool that will be output. The name argument should
// exactly match the Go variable name, and the params fields may reference other
// Ninja variables that are visible within the calling Go package.
func StaticPool(name string, params PoolParams) Pool {
err := validateNinjaName(name)
if err != nil {
@ -354,6 +399,16 @@ type poolFunc struct {
paramsFunc func(interface{}) (PoolParams, error)
}
// PoolFunc returns a Pool whose value is determined by a function that takes a
// config object as input and returns either the pool parameters or an error. It
// may only be called during a Go package's initialization - either from the
// init() function or as part of a package-scoped variable's initialization.
//
// This function is usually used to initialize a package-scoped Go variable that
// represents a Ninja pool that will be output. The name argument should
// exactly match the Go variable name, and the string fields of the PoolParams
// returned by f may reference other Ninja variables that are visible within the
// calling Go package.
func PoolFunc(name string, f func(interface{}) (PoolParams, error)) Pool {
err := validateNinjaName(name)
if err != nil {
@ -403,6 +458,24 @@ type staticRule struct {
scope_ *scope
}
// StaticRule returns a Rule whose value does not depend on any configuration
// information. It may only be called during a Go package's initialization -
// either from the init() function or as part of a package-scoped Go variable's
// initialization.
//
// This function is usually used to initialize a package-scoped Go variable that
// represents a Ninja rule that will be output. The name argument should
// exactly match the Go variable name, and the params fields may reference other
// Ninja variables that are visible within the calling Go package.
//
// The argNames arguments list Ninja variables that may be overridden by Ninja
// build statements that invoke the rule. These arguments may be referenced in
// any of the string fields of params. Arguments can shadow package-scoped
// variables defined within the caller's Go package, but they may not shadow
// those defined in another package. Shadowing a package-scoped variable
// results in the package-scoped variable's value being used for build
// statements that do not override the argument. For argument names that do not
// shadow package-scoped variables the default value is an empty string.
func StaticRule(name string, params RuleParams, argNames ...string) Rule {
pkg := callerPackage()
@ -474,6 +547,25 @@ type ruleFunc struct {
scope_ *scope
}
// RuleFunc returns a Rule whose value is determined by a function that takes a
// config object as input and returns either the rule parameters or an error. It
// may only be called during a Go package's initialization - either from the
// init() function or as part of a package-scoped variable's initialization.
//
// This function is usually used to initialize a package-scoped Go variable that
// represents a Ninja rule that will be output. The name argument should
// exactly match the Go variable name, and the string fields of the RuleParams
// returned by f may reference other Ninja variables that are visible within the
// calling Go package.
//
// The argNames arguments list Ninja variables that may be overridden by Ninja
// build statements that invoke the rule. These arguments may be referenced in
// any of the string fields of the RuleParams returned by f. Arguments can
// shadow package-scoped variables defined within the caller's Go package, but
// they may not shadow those defined in another package. Shadowing a package-
// scoped variable results in the package-scoped variable's value being used for
// build statements that do not override the argument. For argument names that
// do not shadow package-scoped variables the default value is an empty string.
func RuleFunc(name string, f func(interface{}) (RuleParams, error),
argNames ...string) Rule {
@ -575,6 +667,10 @@ func (r *builtinRule) isArg(argName string) bool {
return false
}
// A ModuleType represents a type of module that can be defined in a Blueprints
// file. In order for it to be used when interpreting Blueprints files, a
// ModuleType must first be registered with a Context object via the
// Context.RegisterModuleType method.
type ModuleType interface {
pkg() *pkg
name() string
@ -587,6 +683,70 @@ type moduleTypeFunc struct {
new_ func() (Module, interface{})
}
// MakeModuleType returns a new ModuleType object that will instantiate new
// Module objects with the given new function. MakeModuleType may only be
// called during a Go package's initialization - either from the init() function
// or as part of a package-scoped variable's initialization.
//
// This function is usually used to initialize a package-scoped Go ModuleType
// variable that can then be passed to Context.RegisterModuleType. The name
// argument should exactly match the Go variable name. Note that this name is
// different than the one passed to Context.RegisterModuleType. This name is
// used to identify the Go object in error messages, making it easier to
// identify problematic build logic code. The name passed to
// Context.RegisterModuleType is the name that appear in Blueprints files to
// instantiate modules of this type.
//
// The new function passed to MakeModuleType returns two values. The first is
// the newly created Module object. The second is a pointer to that Module
// object's properties struct. This properties struct is examined when parsing
// a module definition of this type in a Blueprints file. Exported fields of
// the properties struct are automatically set to the property values specified
// in the Blueprints file. The properties struct field names determine the name
// of the Blueprints file properties that are used - the Blueprints property
// name matches that of the properties struct field name with the first letter
// converted to lower-case.
//
// The fields of the properties struct must either []string, a string, or bool.
// The Context will panic if a Module gets instantiated with a properties struct
// containing a field that is not one these supported types.
//
// Any properties that appear in the Blueprints files that are not built-in
// module properties (such as "name" and "deps") and do not have a corresponding
// field in the returned module properties struct result in an error during the
// Context's parse phase.
//
// As an example, the follow code:
//
// var MyModuleType = blueprint.MakeModuleType("MyModuleType", newMyModule)
//
// type myModule struct {
// properties struct {
// Foo string
// Bar []string
// }
// }
//
// func newMyModule() (blueprint.Module, interface{}) {
// module := new(myModule)
// properties := &module.properties
// return module, properties
// }
//
// func main() {
// ctx := blueprint.NewContext()
// ctx.RegisterModuleType("my_module", MyModuleType)
// // ...
// }
//
// would support parsing a module defined in a Blueprints file as follows:
//
// my_module {
// name: "myName",
// foo: "my foo string",
// bar: ["my", "bar", "strings"],
// }
//
func MakeModuleType(name string,
new func() (m Module, properties interface{})) ModuleType {

View file

@ -6,6 +6,8 @@ import (
"strings"
)
// A Deps value indicates the dependency file format that Ninja should expect to
// be output by a compiler.
type Deps int
const (
@ -27,31 +29,40 @@ func (d Deps) String() string {
}
}
// A PoolParams object contains the set of parameters that make up a Ninja pool
// definition.
type PoolParams struct {
Comment string
Depth int
Comment string // The comment that will appear above the definition.
Depth int // The Ninja pool depth.
}
// A RuleParams object contains the set of parameters that make up a Ninja rule
// definition. Each field except for Comment corresponds with a Ninja variable
// of the same name.
type RuleParams struct {
Comment string
Command string
Depfile string
Deps Deps
Description string
Generator bool
Pool Pool
Restat bool
Rspfile string
RspfileContent string
Comment string // The comment that will appear above the definition.
Command string // The command that Ninja will run for the rule.
Depfile string // The dependency file name.
Deps Deps // The format of the dependency file.
Description string // The description that Ninja will print for the rule.
Generator bool // Whether the rule generates the Ninja manifest file.
Pool Pool // The Ninja pool to which the rule belongs.
Restat bool // Whether Ninja should re-stat the rule's outputs.
Rspfile string // The response file.
RspfileContent string // The response file content.
}
// A BuildParams object contains the set of parameters that make up a Ninja
// build statement. Each field except for Args corresponds with a part of the
// Ninja build statement. The Args field contains variable names and values
// that are set within the build statement's scope in the Ninja file.
type BuildParams struct {
Rule Rule
Outputs []string
Inputs []string
Implicits []string
OrderOnly []string
Args map[string]string
Rule Rule // The rule to invoke.
Outputs []string // The list of output targets.
Inputs []string // The list of explicit input dependencies.
Implicits []string // The list of implicit dependencies.
OrderOnly []string // The list of order-only dependencies.
Args map[string]string // The variable/value pairs to set.
}
// A poolDef describes a pool definition. It does not include the name of the