2015-01-23 23:15:10 +01:00
|
|
|
// Copyright 2014 Google Inc. All rights reserved.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
package blueprint
|
|
|
|
|
|
|
|
import (
|
|
|
|
"blueprint/parser"
|
2014-12-19 01:28:54 +01:00
|
|
|
"blueprint/proptools"
|
2014-05-28 01:34:41 +02:00
|
|
|
"bytes"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"reflect"
|
Add support for targets in Blueprints.
The default target selector uses the name of the host OS.
Modules can implement their own target selector by implementing
the context.TargetSelector interface and its unique selectTarget()
method.
Targets are defined this way in Blueprint files:
cc_library {
name: "libmylib",
deps: ["libmath", "libutils"],
zones: ["frontend"],
srcs: ["main.cpp"],
targets: {
darwin: {
moduleCflags: "-framework OpenGL -framework GLUT",
},
linux: {
deps: ["libx11headers", "libglu"],
},
},
}
In this example, a set of C flags are defined on OS X only and on
Linux, two new dependencies are added.
When a target is selected, its properties are merged with the properties
of the modules:
- a list is appended to the original list (see deps above)
- a string is concatenated to the original string
- a bool replaces the original bool
- a map adds or replaces the key/value pairs of the original map
Change-Id: Ic627d47f795d6a4ff56ca5f6f099cad157621af1
2014-08-13 02:50:11 +02:00
|
|
|
"runtime"
|
2014-05-28 01:34:41 +02:00
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
"text/scanner"
|
|
|
|
"text/template"
|
|
|
|
)
|
|
|
|
|
|
|
|
var ErrBuildActionsNotReady = errors.New("build actions are not ready")
|
|
|
|
|
|
|
|
const maxErrors = 10
|
|
|
|
|
2014-06-13 05:06:50 +02:00
|
|
|
// 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
|
|
|
|
// ------------ -------------------------------------------
|
2014-09-25 02:51:52 +02:00
|
|
|
// 1. Registration RegisterModuleType, RegisterSingletonType
|
2014-06-13 05:06:50 +02:00
|
|
|
//
|
|
|
|
// 2. Parse ParseBlueprintsFiles, Parse
|
|
|
|
//
|
2014-09-25 02:51:52 +02:00
|
|
|
// 3. Generate ResolveDependencies, PrepareBuildActions
|
2014-06-13 05:06:50 +02:00
|
|
|
//
|
|
|
|
// 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.
|
2014-05-28 01:34:41 +02:00
|
|
|
type Context struct {
|
|
|
|
// set at instantiation
|
2014-12-18 01:12:41 +01:00
|
|
|
moduleFactories map[string]ModuleFactory
|
|
|
|
moduleGroups map[string]*moduleGroup
|
|
|
|
moduleInfo map[Module]*moduleInfo
|
|
|
|
moduleGroupsSorted []*moduleGroup
|
|
|
|
singletonInfo map[string]*singletonInfo
|
2014-12-19 01:28:54 +01:00
|
|
|
mutatorInfo []*mutatorInfo
|
2014-05-28 01:34:41 +02:00
|
|
|
|
|
|
|
dependenciesReady bool // set to true on a successful ResolveDependencies
|
|
|
|
buildActionsReady bool // set to true on a successful PrepareBuildActions
|
|
|
|
|
|
|
|
// set by SetIgnoreUnknownModuleTypes
|
|
|
|
ignoreUnknownModuleTypes bool
|
|
|
|
|
|
|
|
// set during PrepareBuildActions
|
2014-10-03 11:49:58 +02:00
|
|
|
pkgNames map[*PackageContext]string
|
2014-05-28 01:34:41 +02:00
|
|
|
globalVariables map[Variable]*ninjaString
|
|
|
|
globalPools map[Pool]*poolDef
|
|
|
|
globalRules map[Rule]*ruleDef
|
|
|
|
|
|
|
|
// set during PrepareBuildActions
|
|
|
|
buildDir *ninjaString // The builddir special Ninja variable
|
|
|
|
requiredNinjaMajor int // For the ninja_required_version variable
|
|
|
|
requiredNinjaMinor int // For the ninja_required_version variable
|
|
|
|
requiredNinjaMicro int // For the ninja_required_version variable
|
2014-09-25 05:26:52 +02:00
|
|
|
|
|
|
|
// set lazily by sortedModuleNames
|
|
|
|
cachedSortedModuleNames []string
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-06-13 05:06:50 +02:00
|
|
|
// An Error describes a problem that was encountered that is related to a
|
|
|
|
// particular location in a Blueprints file.
|
2014-05-28 01:34:41 +02:00
|
|
|
type Error struct {
|
2014-06-13 05:06:50 +02:00
|
|
|
Err error // the error that occurred
|
|
|
|
Pos scanner.Position // the relevant Blueprints file location
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type localBuildActions struct {
|
|
|
|
variables []*localVariable
|
|
|
|
rules []*localRule
|
|
|
|
buildDefs []*buildDef
|
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
type moduleGroup struct {
|
2014-05-28 01:34:41 +02:00
|
|
|
// set during Parse
|
2014-06-13 05:06:31 +02:00
|
|
|
typeName string
|
|
|
|
relBlueprintsFile string
|
|
|
|
pos scanner.Position
|
|
|
|
propertyPos map[string]scanner.Position
|
|
|
|
properties struct {
|
2014-10-05 16:41:44 +02:00
|
|
|
Name string
|
|
|
|
Deps []string
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
modules []*moduleInfo
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-08 03:08:56 +01:00
|
|
|
// set during updateDependencies
|
|
|
|
reverseDeps []*moduleGroup
|
|
|
|
depsCount int
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
// set during PrepareBuildActions
|
|
|
|
actionDefs localBuildActions
|
2015-01-08 03:08:56 +01:00
|
|
|
|
|
|
|
// used by parallelVisitAllBottomUp
|
|
|
|
waitingCount int
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
type moduleInfo struct {
|
2014-12-19 01:28:54 +01:00
|
|
|
name []subName
|
|
|
|
logicModule Module
|
|
|
|
group *moduleGroup
|
|
|
|
moduleProperties []interface{}
|
|
|
|
|
|
|
|
// set during ResolveDependencies
|
|
|
|
directDeps []*moduleInfo
|
|
|
|
|
|
|
|
// set during each runMutator
|
|
|
|
splitModules []*moduleInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
type subName struct {
|
|
|
|
mutatorName string
|
|
|
|
variantName string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (module *moduleInfo) subName() string {
|
|
|
|
names := []string{}
|
|
|
|
for _, subName := range module.name {
|
|
|
|
if subName.variantName != "" {
|
|
|
|
names = append(names, subName.variantName)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return strings.Join(names, "_")
|
2014-12-18 01:12:41 +01:00
|
|
|
}
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
type singletonInfo struct {
|
2014-09-25 02:51:52 +02:00
|
|
|
// set during RegisterSingletonType
|
|
|
|
factory SingletonFactory
|
2014-05-28 01:34:41 +02:00
|
|
|
singleton Singleton
|
|
|
|
|
|
|
|
// set during PrepareBuildActions
|
|
|
|
actionDefs localBuildActions
|
|
|
|
}
|
|
|
|
|
2014-12-19 01:28:54 +01:00
|
|
|
type mutatorInfo struct {
|
|
|
|
// set during RegisterMutator
|
2015-01-03 00:19:28 +01:00
|
|
|
topDownMutator TopDownMutator
|
|
|
|
bottomUpMutator BottomUpMutator
|
|
|
|
name string
|
2014-12-19 01:28:54 +01:00
|
|
|
}
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
func (e *Error) Error() string {
|
|
|
|
|
|
|
|
return fmt.Sprintf("%s: %s", e.Pos, e.Err)
|
|
|
|
}
|
|
|
|
|
2014-06-13 05:06:50 +02:00
|
|
|
// NewContext creates a new Context object. The created context initially has
|
2014-09-25 02:51:52 +02:00
|
|
|
// no module or singleton factories registered, so the RegisterModuleFactory and
|
|
|
|
// RegisterSingletonFactory methods must be called before it can do anything
|
|
|
|
// useful.
|
2014-05-28 01:34:41 +02:00
|
|
|
func NewContext() *Context {
|
|
|
|
return &Context{
|
2014-09-25 02:51:52 +02:00
|
|
|
moduleFactories: make(map[string]ModuleFactory),
|
2014-12-18 01:12:41 +01:00
|
|
|
moduleGroups: make(map[string]*moduleGroup),
|
2014-09-25 02:51:52 +02:00
|
|
|
moduleInfo: make(map[Module]*moduleInfo),
|
|
|
|
singletonInfo: make(map[string]*singletonInfo),
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-25 02:51:52 +02:00
|
|
|
// A ModuleFactory function creates a new Module object. See the
|
|
|
|
// Context.RegisterModuleType method for details about how a registered
|
|
|
|
// ModuleFactory is used by a Context.
|
|
|
|
type ModuleFactory func() (m Module, propertyStructs []interface{})
|
|
|
|
|
2014-06-13 05:06:50 +02:00
|
|
|
// RegisterModuleType associates a module type name (which can appear in a
|
2014-09-25 02:51:52 +02:00
|
|
|
// Blueprints file) with a Module factory function. When the given module type
|
|
|
|
// name is encountered in a Blueprints file during parsing, the Module factory
|
|
|
|
// is invoked to instantiate a new Module object to handle the build action
|
2014-12-19 01:28:54 +01:00
|
|
|
// generation for the module. If a Mutator splits a module into multiple variants,
|
|
|
|
// the factory is invoked again to create a new Module for each variant.
|
2014-06-13 05:06:50 +02:00
|
|
|
//
|
2014-09-25 02:51:52 +02:00
|
|
|
// The module type names given here must be unique for the context. The factory
|
|
|
|
// function should be a named function so that its package and name can be
|
|
|
|
// included in the generated Ninja file for debugging purposes.
|
|
|
|
//
|
|
|
|
// The factory function returns two values. The first is the newly created
|
|
|
|
// Module object. The second is a slice of pointers to that Module object's
|
|
|
|
// properties structs. Each properties struct is examined when parsing a module
|
|
|
|
// definition of this type in a Blueprints file. Exported fields of the
|
|
|
|
// properties structs 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 be 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:
|
|
|
|
//
|
|
|
|
// type myModule struct {
|
|
|
|
// properties struct {
|
|
|
|
// Foo string
|
|
|
|
// Bar []string
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// func NewMyModule() (blueprint.Module, []interface{}) {
|
|
|
|
// module := new(myModule)
|
|
|
|
// properties := &module.properties
|
|
|
|
// return module, []interface{}{properties}
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// func main() {
|
|
|
|
// ctx := blueprint.NewContext()
|
|
|
|
// ctx.RegisterModuleType("my_module", NewMyModule)
|
|
|
|
// // ...
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// would support parsing a module defined in a Blueprints file as follows:
|
|
|
|
//
|
|
|
|
// my_module {
|
|
|
|
// name: "myName",
|
|
|
|
// foo: "my foo string",
|
|
|
|
// bar: ["my", "bar", "strings"],
|
|
|
|
// }
|
|
|
|
//
|
2015-01-08 01:22:45 +01:00
|
|
|
// The factory function may be called from multiple goroutines. Any accesses
|
|
|
|
// to global variables must be synchronized.
|
2014-09-25 02:51:52 +02:00
|
|
|
func (c *Context) RegisterModuleType(name string, factory ModuleFactory) {
|
|
|
|
if _, present := c.moduleFactories[name]; present {
|
2014-05-28 01:34:41 +02:00
|
|
|
panic(errors.New("module type name is already registered"))
|
|
|
|
}
|
2014-09-25 02:51:52 +02:00
|
|
|
c.moduleFactories[name] = factory
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-09-25 02:51:52 +02:00
|
|
|
// A SingletonFactory function creates a new Singleton object. See the
|
|
|
|
// Context.RegisterSingletonType method for details about how a registered
|
|
|
|
// SingletonFactory is used by a Context.
|
|
|
|
type SingletonFactory func() Singleton
|
|
|
|
|
|
|
|
// RegisterSingletonType registers a singleton type that will be invoked to
|
|
|
|
// generate build actions. Each registered singleton type is instantiated and
|
|
|
|
// and invoked exactly once as part of the generate phase.
|
|
|
|
//
|
|
|
|
// The singleton type names given here must be unique for the context. The
|
|
|
|
// factory function should be a named function so that its package and name can
|
|
|
|
// be included in the generated Ninja file for debugging purposes.
|
|
|
|
func (c *Context) RegisterSingletonType(name string, factory SingletonFactory) {
|
2014-05-28 01:34:41 +02:00
|
|
|
if _, present := c.singletonInfo[name]; present {
|
|
|
|
panic(errors.New("singleton name is already registered"))
|
|
|
|
}
|
2014-09-25 02:51:52 +02:00
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
c.singletonInfo[name] = &singletonInfo{
|
2014-09-25 02:51:52 +02:00
|
|
|
factory: factory,
|
|
|
|
singleton: factory(),
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func singletonPkgPath(singleton Singleton) string {
|
|
|
|
typ := reflect.TypeOf(singleton)
|
|
|
|
for typ.Kind() == reflect.Ptr {
|
|
|
|
typ = typ.Elem()
|
|
|
|
}
|
|
|
|
return typ.PkgPath()
|
|
|
|
}
|
|
|
|
|
|
|
|
func singletonTypeName(singleton Singleton) string {
|
|
|
|
typ := reflect.TypeOf(singleton)
|
|
|
|
for typ.Kind() == reflect.Ptr {
|
|
|
|
typ = typ.Elem()
|
|
|
|
}
|
|
|
|
return typ.PkgPath() + "." + typ.Name()
|
|
|
|
}
|
|
|
|
|
2014-12-19 01:28:54 +01:00
|
|
|
// RegisterTopDownMutator registers a mutator that will be invoked to propagate
|
|
|
|
// dependency info top-down between Modules. Each registered mutator
|
|
|
|
// is invoked once per Module, and is invoked on a module before being invoked
|
|
|
|
// on any of its dependencies
|
|
|
|
//
|
|
|
|
// The mutator type names given here must be unique for the context.
|
|
|
|
func (c *Context) RegisterTopDownMutator(name string, mutator TopDownMutator) {
|
|
|
|
for _, m := range c.mutatorInfo {
|
|
|
|
if m.name == name && m.topDownMutator != nil {
|
|
|
|
panic(fmt.Errorf("mutator name %s is already registered", name))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c.mutatorInfo = append(c.mutatorInfo, &mutatorInfo{
|
|
|
|
topDownMutator: mutator,
|
2015-01-03 00:19:28 +01:00
|
|
|
name: name,
|
2014-12-19 01:28:54 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// RegisterBottomUpMutator registers a mutator that will be invoked to split
|
|
|
|
// Modules into variants. Each registered mutator is invoked once per Module,
|
|
|
|
// and is invoked on dependencies before being invoked on dependers.
|
|
|
|
//
|
|
|
|
// The mutator type names given here must be unique for the context.
|
|
|
|
func (c *Context) RegisterBottomUpMutator(name string, mutator BottomUpMutator) {
|
|
|
|
for _, m := range c.mutatorInfo {
|
|
|
|
if m.name == name && m.bottomUpMutator != nil {
|
|
|
|
panic(fmt.Errorf("mutator name %s is already registered", name))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c.mutatorInfo = append(c.mutatorInfo, &mutatorInfo{
|
|
|
|
bottomUpMutator: mutator,
|
2015-01-03 00:19:28 +01:00
|
|
|
name: name,
|
2014-12-19 01:28:54 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2014-06-13 05:06:50 +02:00
|
|
|
// 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.
|
2014-05-28 01:34:41 +02:00
|
|
|
func (c *Context) SetIgnoreUnknownModuleTypes(ignoreUnknownModuleTypes bool) {
|
|
|
|
c.ignoreUnknownModuleTypes = ignoreUnknownModuleTypes
|
|
|
|
}
|
|
|
|
|
2014-06-13 05:06:50 +02:00
|
|
|
// 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.
|
2015-01-08 01:22:45 +01:00
|
|
|
func (c *Context) parse(rootDir, filename string, r io.Reader,
|
|
|
|
scope *parser.Scope) (subdirs []string, modules []*moduleInfo, errs []error,
|
|
|
|
outScope *parser.Scope) {
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2014-06-13 05:06:31 +02:00
|
|
|
relBlueprintsFile, err := filepath.Rel(rootDir, filename)
|
2014-05-28 01:34:41 +02:00
|
|
|
if err != nil {
|
2015-01-08 01:22:45 +01:00
|
|
|
return nil, nil, []error{err}, nil
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2015-01-03 00:19:28 +01:00
|
|
|
scope = parser.NewScope(scope)
|
|
|
|
scope.Remove("subdirs")
|
2015-01-08 23:56:03 +01:00
|
|
|
file, errs := parser.Parse(filename, r, scope)
|
2014-05-28 01:34:41 +02:00
|
|
|
if len(errs) > 0 {
|
|
|
|
for i, err := range errs {
|
|
|
|
if parseErr, ok := err.(*parser.ParseError); ok {
|
|
|
|
err = &Error{
|
|
|
|
Err: parseErr.Err,
|
|
|
|
Pos: parseErr.Pos,
|
|
|
|
}
|
|
|
|
errs[i] = err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there were any parse errors don't bother trying to interpret the
|
|
|
|
// result.
|
2015-01-08 01:22:45 +01:00
|
|
|
return nil, nil, errs, nil
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2015-01-08 23:56:03 +01:00
|
|
|
for _, def := range file.Defs {
|
2014-05-28 01:34:41 +02:00
|
|
|
var newErrs []error
|
2015-01-08 01:22:45 +01:00
|
|
|
var newModule *moduleInfo
|
2014-05-28 01:34:41 +02:00
|
|
|
switch def := def.(type) {
|
|
|
|
case *parser.Module:
|
2015-01-08 01:22:45 +01:00
|
|
|
newModule, newErrs = c.processModuleDef(def, relBlueprintsFile)
|
2014-05-28 01:34:41 +02:00
|
|
|
|
|
|
|
case *parser.Assignment:
|
2015-01-03 00:19:28 +01:00
|
|
|
// Already handled via Scope object
|
2014-05-28 01:34:41 +02:00
|
|
|
default:
|
|
|
|
panic("unknown definition type")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(newErrs) > 0 {
|
|
|
|
errs = append(errs, newErrs...)
|
|
|
|
if len(errs) > maxErrors {
|
|
|
|
break
|
|
|
|
}
|
2015-01-08 01:22:45 +01:00
|
|
|
} else if newModule != nil {
|
|
|
|
modules = append(modules, newModule)
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-03 00:19:28 +01:00
|
|
|
subdirs, newErrs := c.processSubdirs(scope)
|
|
|
|
if len(newErrs) > 0 {
|
|
|
|
errs = append(errs, newErrs...)
|
|
|
|
}
|
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
return subdirs, modules, errs, scope
|
2015-01-03 00:19:28 +01:00
|
|
|
}
|
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
type stringAndScope struct {
|
|
|
|
string
|
|
|
|
*parser.Scope
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-06-13 05:06:50 +02:00
|
|
|
// 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.
|
2014-05-28 01:34:41 +02:00
|
|
|
func (c *Context) ParseBlueprintsFiles(rootFile string) (deps []string,
|
|
|
|
errs []error) {
|
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
c.dependenciesReady = false
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
rootDir := filepath.Dir(rootFile)
|
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
blueprintsSet := make(map[string]bool)
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
// Channels to receive data back from parseBlueprintsFile goroutines
|
|
|
|
blueprintsCh := make(chan stringAndScope)
|
|
|
|
errsCh := make(chan []error)
|
|
|
|
modulesCh := make(chan []*moduleInfo)
|
|
|
|
depsCh := make(chan string)
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
// Channel to notify main loop that a parseBlueprintsFile goroutine has finished
|
|
|
|
doneCh := make(chan struct{})
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
// Number of outstanding goroutines to wait for
|
|
|
|
count := 0
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
startParseBlueprintsFile := func(filename string, scope *parser.Scope) {
|
|
|
|
count++
|
|
|
|
go func() {
|
|
|
|
c.parseBlueprintsFile(filename, scope, rootDir,
|
|
|
|
errsCh, modulesCh, blueprintsCh, depsCh)
|
|
|
|
doneCh <- struct{}{}
|
|
|
|
}()
|
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
tooManyErrors := false
|
|
|
|
|
|
|
|
startParseBlueprintsFile(rootFile, nil)
|
|
|
|
|
|
|
|
loop:
|
|
|
|
for {
|
|
|
|
if len(errs) > maxErrors {
|
|
|
|
tooManyErrors = true
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
select {
|
|
|
|
case newErrs := <-errsCh:
|
2014-05-28 01:34:41 +02:00
|
|
|
errs = append(errs, newErrs...)
|
2015-01-08 01:22:45 +01:00
|
|
|
case dep := <-depsCh:
|
|
|
|
deps = append(deps, dep)
|
|
|
|
case modules := <-modulesCh:
|
|
|
|
newErrs := c.addModules(modules)
|
|
|
|
errs = append(errs, newErrs...)
|
|
|
|
case blueprint := <-blueprintsCh:
|
|
|
|
if tooManyErrors {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if blueprintsSet[blueprint.string] {
|
|
|
|
continue
|
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
blueprintsSet[blueprint.string] = true
|
|
|
|
startParseBlueprintsFile(blueprint.string, blueprint.Scope)
|
|
|
|
case <-doneCh:
|
|
|
|
count--
|
|
|
|
if count == 0 {
|
|
|
|
break loop
|
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
2015-01-08 01:22:45 +01:00
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// parseBlueprintFile parses a single Blueprints file, returning any errors through
|
|
|
|
// errsCh, any defined modules through modulesCh, any sub-Blueprints files through
|
|
|
|
// blueprintsCh, and any dependencies on Blueprints files or directories through
|
|
|
|
// depsCh.
|
|
|
|
func (c *Context) parseBlueprintsFile(filename string, scope *parser.Scope, rootDir string,
|
|
|
|
errsCh chan<- []error, modulesCh chan<- []*moduleInfo, blueprintsCh chan<- stringAndScope,
|
|
|
|
depsCh chan<- string) {
|
|
|
|
|
|
|
|
dir := filepath.Dir(filename)
|
|
|
|
|
|
|
|
file, err := os.Open(filename)
|
|
|
|
if err != nil {
|
|
|
|
errsCh <- []error{err}
|
|
|
|
return
|
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
subdirs, modules, errs, subScope := c.parse(rootDir, filename, file, scope)
|
|
|
|
if len(errs) > 0 {
|
|
|
|
errsCh <- errs
|
|
|
|
}
|
|
|
|
|
|
|
|
err = file.Close()
|
|
|
|
if err != nil {
|
|
|
|
errsCh <- []error{err}
|
|
|
|
}
|
|
|
|
|
|
|
|
modulesCh <- modules
|
|
|
|
|
|
|
|
for _, subdir := range subdirs {
|
|
|
|
subdir = filepath.Join(dir, subdir)
|
|
|
|
|
|
|
|
dirPart, filePart := filepath.Split(subdir)
|
|
|
|
dirPart = filepath.Clean(dirPart)
|
|
|
|
|
|
|
|
if filePart == "*" {
|
|
|
|
foundSubdirs, err := listSubdirs(dirPart)
|
|
|
|
if err != nil {
|
|
|
|
errsCh <- []error{err}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, foundSubdir := range foundSubdirs {
|
|
|
|
subBlueprints := filepath.Join(dirPart, foundSubdir,
|
|
|
|
"Blueprints")
|
|
|
|
|
|
|
|
_, err := os.Stat(subBlueprints)
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
// There is no Blueprints file in this subdirectory. We
|
|
|
|
// need to add the directory to the list of dependencies
|
|
|
|
// so that if someone adds a Blueprints file in the
|
|
|
|
// future we'll pick it up.
|
|
|
|
depsCh <- filepath.Dir(subBlueprints)
|
|
|
|
} else {
|
|
|
|
depsCh <- subBlueprints
|
|
|
|
blueprintsCh <- stringAndScope{
|
|
|
|
subBlueprints,
|
|
|
|
subScope,
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
}
|
2015-01-08 01:22:45 +01:00
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
// We now depend on the directory itself because if any new
|
|
|
|
// subdirectories get added or removed we need to rebuild the
|
|
|
|
// Ninja manifest.
|
|
|
|
depsCh <- dirPart
|
|
|
|
} else {
|
|
|
|
subBlueprints := filepath.Join(subdir, "Blueprints")
|
|
|
|
depsCh <- subBlueprints
|
|
|
|
blueprintsCh <- stringAndScope{
|
|
|
|
subBlueprints,
|
|
|
|
subScope,
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func listSubdirs(dir string) ([]string, error) {
|
|
|
|
d, err := os.Open(dir)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer d.Close()
|
|
|
|
|
|
|
|
infos, err := d.Readdir(-1)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var subdirs []string
|
|
|
|
for _, info := range infos {
|
2014-09-25 22:15:10 +02:00
|
|
|
isDotFile := strings.HasPrefix(info.Name(), ".")
|
|
|
|
if info.IsDir() && !isDotFile {
|
2014-05-28 01:34:41 +02:00
|
|
|
subdirs = append(subdirs, info.Name())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return subdirs, nil
|
|
|
|
}
|
|
|
|
|
2015-01-03 00:19:28 +01:00
|
|
|
func (c *Context) processSubdirs(
|
|
|
|
scope *parser.Scope) (subdirs []string, errs []error) {
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-03 00:19:28 +01:00
|
|
|
if assignment, err := scope.Get("subdirs"); err == nil {
|
2014-05-28 01:34:41 +02:00
|
|
|
switch assignment.Value.Type {
|
|
|
|
case parser.List:
|
|
|
|
subdirs = make([]string, 0, len(assignment.Value.ListValue))
|
|
|
|
|
|
|
|
for _, value := range assignment.Value.ListValue {
|
|
|
|
if value.Type != parser.String {
|
|
|
|
// The parser should not produce this.
|
|
|
|
panic("non-string value found in list")
|
|
|
|
}
|
|
|
|
|
|
|
|
dirPart, filePart := filepath.Split(value.StringValue)
|
|
|
|
if (filePart != "*" && strings.ContainsRune(filePart, '*')) ||
|
|
|
|
strings.ContainsRune(dirPart, '*') {
|
|
|
|
|
|
|
|
errs = append(errs, &Error{
|
|
|
|
Err: fmt.Errorf("subdirs may only wildcard whole " +
|
|
|
|
"directories"),
|
|
|
|
Pos: value.Pos,
|
|
|
|
})
|
|
|
|
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
subdirs = append(subdirs, value.StringValue)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(errs) > 0 {
|
|
|
|
subdirs = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
case parser.Bool, parser.String:
|
|
|
|
errs = []error{
|
|
|
|
&Error{
|
|
|
|
Err: fmt.Errorf("subdirs must be a list of strings"),
|
|
|
|
Pos: assignment.Pos,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
default:
|
|
|
|
panic(fmt.Errorf("unknown value type: %d", assignment.Value.Type))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-03 00:19:28 +01:00
|
|
|
return nil, nil
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-12-19 01:28:54 +01:00
|
|
|
func (c *Context) createVariants(origModule *moduleInfo, mutatorName string,
|
2015-03-04 02:37:03 +01:00
|
|
|
variantNames []string) ([]*moduleInfo, []error) {
|
2014-12-19 01:28:54 +01:00
|
|
|
|
|
|
|
newModules := []*moduleInfo{}
|
|
|
|
origVariantName := origModule.name
|
|
|
|
group := origModule.group
|
|
|
|
|
2015-03-04 02:37:03 +01:00
|
|
|
var errs []error
|
|
|
|
|
2014-12-19 01:28:54 +01:00
|
|
|
for i, variantName := range variantNames {
|
|
|
|
typeName := group.typeName
|
|
|
|
factory, ok := c.moduleFactories[typeName]
|
|
|
|
if !ok {
|
|
|
|
panic(fmt.Sprintf("unrecognized module type %q during cloning", typeName))
|
|
|
|
}
|
|
|
|
|
|
|
|
var newLogicModule Module
|
|
|
|
var newProperties []interface{}
|
|
|
|
|
|
|
|
if i == 0 {
|
|
|
|
// Reuse the existing module for the first new variant
|
|
|
|
newLogicModule = origModule.logicModule
|
|
|
|
newProperties = origModule.moduleProperties
|
|
|
|
} else {
|
|
|
|
props := []interface{}{
|
|
|
|
&group.properties,
|
|
|
|
}
|
|
|
|
newLogicModule, newProperties = factory()
|
|
|
|
|
|
|
|
newProperties = append(props, newProperties...)
|
|
|
|
|
|
|
|
if len(newProperties) != len(origModule.moduleProperties) {
|
|
|
|
panic("mismatched properties array length in " + group.properties.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range newProperties {
|
|
|
|
dst := reflect.ValueOf(newProperties[i]).Elem()
|
|
|
|
src := reflect.ValueOf(origModule.moduleProperties[i]).Elem()
|
|
|
|
|
|
|
|
proptools.CopyProperties(dst, src)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
newVariantName := append([]subName(nil), origVariantName...)
|
|
|
|
newSubName := subName{
|
|
|
|
mutatorName: mutatorName,
|
|
|
|
variantName: variantName,
|
|
|
|
}
|
|
|
|
newVariantName = append(newVariantName, newSubName)
|
|
|
|
|
|
|
|
newModule := &moduleInfo{
|
|
|
|
group: group,
|
|
|
|
directDeps: append([]*moduleInfo(nil), origModule.directDeps...),
|
|
|
|
logicModule: newLogicModule,
|
|
|
|
name: newVariantName,
|
|
|
|
moduleProperties: newProperties,
|
|
|
|
}
|
|
|
|
|
|
|
|
newModules = append(newModules, newModule)
|
|
|
|
c.moduleInfo[newModule.logicModule] = newModule
|
|
|
|
|
2015-03-04 02:37:03 +01:00
|
|
|
newErrs := c.convertDepsToVariant(newModule, newSubName)
|
|
|
|
if len(newErrs) > 0 {
|
|
|
|
errs = append(errs, newErrs...)
|
|
|
|
}
|
2014-12-19 01:28:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Mark original variant as invalid. Modules that depend on this module will still
|
|
|
|
// depend on origModule, but we'll fix it when the mutator is called on them.
|
|
|
|
origModule.logicModule = nil
|
|
|
|
origModule.splitModules = newModules
|
|
|
|
|
2015-03-04 02:37:03 +01:00
|
|
|
return newModules, errs
|
2014-12-19 01:28:54 +01:00
|
|
|
}
|
|
|
|
|
2015-03-04 02:37:03 +01:00
|
|
|
func (c *Context) convertDepsToVariant(module *moduleInfo, newSubName subName) (errs []error) {
|
|
|
|
|
2014-12-19 01:28:54 +01:00
|
|
|
for i, dep := range module.directDeps {
|
|
|
|
if dep.logicModule == nil {
|
|
|
|
var newDep *moduleInfo
|
|
|
|
for _, m := range dep.splitModules {
|
|
|
|
if len(m.name) > 0 && m.name[len(m.name)-1] == newSubName {
|
|
|
|
newDep = m
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if newDep == nil {
|
2015-03-04 02:37:03 +01:00
|
|
|
errs = append(errs, &Error{
|
|
|
|
Err: fmt.Errorf("failed to find variant %q for module %q needed by %q",
|
|
|
|
newSubName.variantName, dep.group.properties.Name,
|
|
|
|
module.group.properties.Name),
|
|
|
|
Pos: module.group.pos,
|
|
|
|
})
|
|
|
|
continue
|
2014-12-19 01:28:54 +01:00
|
|
|
}
|
|
|
|
module.directDeps[i] = newDep
|
|
|
|
}
|
|
|
|
}
|
2015-03-04 02:37:03 +01:00
|
|
|
|
|
|
|
return errs
|
2014-12-19 01:28:54 +01:00
|
|
|
}
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
func (c *Context) processModuleDef(moduleDef *parser.Module,
|
2015-01-08 01:22:45 +01:00
|
|
|
relBlueprintsFile string) (*moduleInfo, []error) {
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-08 23:56:03 +01:00
|
|
|
typeName := moduleDef.Type.Name
|
2014-09-25 02:51:52 +02:00
|
|
|
factory, ok := c.moduleFactories[typeName]
|
2014-05-28 01:34:41 +02:00
|
|
|
if !ok {
|
|
|
|
if c.ignoreUnknownModuleTypes {
|
2015-01-08 01:22:45 +01:00
|
|
|
return nil, nil
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
return nil, []error{
|
2014-06-23 02:02:55 +02:00
|
|
|
&Error{
|
|
|
|
Err: fmt.Errorf("unrecognized module type %q", typeName),
|
2015-01-08 23:56:03 +01:00
|
|
|
Pos: moduleDef.Type.Pos,
|
2014-06-23 02:02:55 +02:00
|
|
|
},
|
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
logicModule, properties := factory()
|
|
|
|
group := &moduleGroup{
|
2014-06-13 05:06:31 +02:00
|
|
|
typeName: typeName,
|
|
|
|
relBlueprintsFile: relBlueprintsFile,
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-09-30 20:38:25 +02:00
|
|
|
props := []interface{}{
|
2014-12-18 01:12:41 +01:00
|
|
|
&group.properties,
|
2014-09-30 20:38:25 +02:00
|
|
|
}
|
|
|
|
properties = append(props, properties...)
|
2014-06-23 02:02:55 +02:00
|
|
|
|
2014-09-30 20:38:25 +02:00
|
|
|
propertyMap, errs := unpackProperties(moduleDef.Properties, properties...)
|
2014-05-28 01:34:41 +02:00
|
|
|
if len(errs) > 0 {
|
2015-01-08 01:22:45 +01:00
|
|
|
return nil, errs
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2015-01-08 23:56:03 +01:00
|
|
|
group.pos = moduleDef.Type.Pos
|
2014-12-18 01:12:41 +01:00
|
|
|
group.propertyPos = make(map[string]scanner.Position)
|
2014-09-30 20:38:25 +02:00
|
|
|
for name, propertyDef := range propertyMap {
|
2014-12-18 01:12:41 +01:00
|
|
|
group.propertyPos[name] = propertyDef.Pos
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
name := group.properties.Name
|
2014-05-28 01:34:41 +02:00
|
|
|
err := validateNinjaName(name)
|
|
|
|
if err != nil {
|
2015-01-08 01:22:45 +01:00
|
|
|
return nil, []error{
|
2014-05-28 01:34:41 +02:00
|
|
|
&Error{
|
|
|
|
Err: fmt.Errorf("invalid module name %q: %s", err),
|
2014-12-18 01:12:41 +01:00
|
|
|
Pos: group.propertyPos["name"],
|
2014-05-28 01:34:41 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
module := &moduleInfo{
|
2014-12-19 01:28:54 +01:00
|
|
|
group: group,
|
|
|
|
logicModule: logicModule,
|
|
|
|
moduleProperties: properties,
|
2014-12-18 01:12:41 +01:00
|
|
|
}
|
2015-01-08 01:22:45 +01:00
|
|
|
group.modules = []*moduleInfo{module}
|
2014-12-18 01:12:41 +01:00
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
return module, nil
|
|
|
|
}
|
2014-12-18 01:12:41 +01:00
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
func (c *Context) addModules(modules []*moduleInfo) (errs []error) {
|
|
|
|
for _, module := range modules {
|
|
|
|
name := module.group.properties.Name
|
|
|
|
if first, present := c.moduleGroups[name]; present {
|
|
|
|
errs = append(errs, []error{
|
|
|
|
&Error{
|
|
|
|
Err: fmt.Errorf("module %q already defined", name),
|
|
|
|
Pos: module.group.pos,
|
|
|
|
},
|
|
|
|
&Error{
|
|
|
|
Err: fmt.Errorf("<-- previous definition here"),
|
|
|
|
Pos: first.pos,
|
|
|
|
},
|
|
|
|
}...)
|
|
|
|
continue
|
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-08 01:22:45 +01:00
|
|
|
c.moduleGroups[name] = module.group
|
|
|
|
c.moduleInfo[module.logicModule] = module
|
|
|
|
}
|
|
|
|
|
|
|
|
return errs
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-06-13 05:06:50 +02:00
|
|
|
// 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.
|
2014-09-25 05:28:11 +02:00
|
|
|
//
|
|
|
|
// The config argument is made available to all of the DynamicDependerModule
|
|
|
|
// objects via the Config method on the DynamicDependerModuleContext objects
|
|
|
|
// passed to their DynamicDependencies method.
|
|
|
|
func (c *Context) ResolveDependencies(config interface{}) []error {
|
|
|
|
errs := c.resolveDependencies(config)
|
2014-05-28 01:34:41 +02:00
|
|
|
if len(errs) > 0 {
|
|
|
|
return errs
|
|
|
|
}
|
|
|
|
|
2015-01-08 03:08:56 +01:00
|
|
|
errs = c.updateDependencies()
|
2014-05-28 01:34:41 +02:00
|
|
|
if len(errs) > 0 {
|
|
|
|
return errs
|
|
|
|
}
|
|
|
|
|
|
|
|
c.dependenciesReady = true
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-09-25 05:28:11 +02:00
|
|
|
// moduleDepNames returns the sorted list of dependency names for a given
|
|
|
|
// module. If the module implements the DynamicDependerModule interface then
|
|
|
|
// this set consists of the union of those module names listed in its "deps"
|
|
|
|
// property and those returned by its DynamicDependencies method. Otherwise it
|
|
|
|
// is simply those names listed in its "deps" property.
|
2014-12-18 01:12:41 +01:00
|
|
|
func (c *Context) moduleDepNames(group *moduleGroup,
|
2014-09-25 05:28:11 +02:00
|
|
|
config interface{}) ([]string, []error) {
|
|
|
|
|
|
|
|
depNamesSet := make(map[string]bool)
|
2015-01-13 19:59:52 +01:00
|
|
|
depNames := []string{}
|
2014-09-25 05:28:11 +02:00
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
for _, depName := range group.properties.Deps {
|
2015-01-13 19:59:52 +01:00
|
|
|
if !depNamesSet[depName] {
|
|
|
|
depNamesSet[depName] = true
|
|
|
|
depNames = append(depNames, depName)
|
|
|
|
}
|
2014-09-25 05:28:11 +02:00
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
if len(group.modules) != 1 {
|
|
|
|
panic("expected a single module during moduleDepNames")
|
|
|
|
}
|
|
|
|
logicModule := group.modules[0].logicModule
|
|
|
|
dynamicDepender, ok := logicModule.(DynamicDependerModule)
|
2014-09-25 05:28:11 +02:00
|
|
|
if ok {
|
2014-12-18 20:05:45 +01:00
|
|
|
ddmctx := &baseModuleContext{
|
2014-09-25 05:28:11 +02:00
|
|
|
context: c,
|
|
|
|
config: config,
|
2014-12-18 01:12:41 +01:00
|
|
|
group: group,
|
2014-09-25 05:28:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
dynamicDeps := dynamicDepender.DynamicDependencies(ddmctx)
|
|
|
|
|
|
|
|
if len(ddmctx.errs) > 0 {
|
|
|
|
return nil, ddmctx.errs
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, depName := range dynamicDeps {
|
2015-01-13 19:59:52 +01:00
|
|
|
if !depNamesSet[depName] {
|
|
|
|
depNamesSet[depName] = true
|
|
|
|
depNames = append(depNames, depName)
|
|
|
|
}
|
2014-09-25 05:28:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return depNames, nil
|
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
// resolveDependencies populates the moduleGroup.modules[0].directDeps list for every
|
2014-05-28 01:34:41 +02:00
|
|
|
// module. In doing so it checks for missing dependencies and self-dependant
|
|
|
|
// modules.
|
2014-09-25 05:28:11 +02:00
|
|
|
func (c *Context) resolveDependencies(config interface{}) (errs []error) {
|
2014-12-18 01:12:41 +01:00
|
|
|
for _, group := range c.moduleGroups {
|
|
|
|
depNames, newErrs := c.moduleDepNames(group, config)
|
2014-09-25 05:28:11 +02:00
|
|
|
if len(newErrs) > 0 {
|
|
|
|
errs = append(errs, newErrs...)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
if len(group.modules) != 1 {
|
|
|
|
panic("expected a single module in resolveDependencies")
|
|
|
|
}
|
|
|
|
group.modules[0].directDeps = make([]*moduleInfo, 0, len(depNames))
|
2014-05-28 01:34:41 +02:00
|
|
|
|
|
|
|
for _, depName := range depNames {
|
2014-12-19 01:28:54 +01:00
|
|
|
newErrs := c.addDependency(group.modules[0], depName)
|
|
|
|
if len(newErrs) > 0 {
|
|
|
|
errs = append(errs, newErrs...)
|
2014-05-28 01:34:41 +02:00
|
|
|
continue
|
|
|
|
}
|
2014-12-19 01:28:54 +01:00
|
|
|
}
|
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2014-12-19 01:28:54 +01:00
|
|
|
return
|
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2014-12-19 01:28:54 +01:00
|
|
|
func (c *Context) addDependency(module *moduleInfo, depName string) []error {
|
|
|
|
depsPos := module.group.propertyPos["deps"]
|
2014-12-18 01:12:41 +01:00
|
|
|
|
2014-12-19 01:28:54 +01:00
|
|
|
if depName == module.group.properties.Name {
|
|
|
|
return []error{&Error{
|
|
|
|
Err: fmt.Errorf("%q depends on itself", depName),
|
|
|
|
Pos: depsPos,
|
|
|
|
}}
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-12-19 01:28:54 +01:00
|
|
|
depInfo, ok := c.moduleGroups[depName]
|
|
|
|
if !ok {
|
|
|
|
return []error{&Error{
|
|
|
|
Err: fmt.Errorf("%q depends on undefined module %q",
|
|
|
|
module.group.properties.Name, depName),
|
|
|
|
Pos: depsPos,
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(depInfo.modules) != 1 {
|
|
|
|
panic(fmt.Sprintf("cannot add dependency from %s to %s, it already has multiple variants",
|
|
|
|
module.group.properties.Name, depInfo.properties.Name))
|
|
|
|
}
|
|
|
|
|
|
|
|
module.directDeps = append(module.directDeps, depInfo.modules[0])
|
|
|
|
|
|
|
|
return nil
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2015-03-02 23:03:01 +01:00
|
|
|
func (c *Context) parallelVisitAllBottomUp(visit func(group *moduleGroup) bool) {
|
2015-01-08 03:08:56 +01:00
|
|
|
doneCh := make(chan *moduleGroup)
|
|
|
|
count := 0
|
2015-03-02 23:03:01 +01:00
|
|
|
cancel := false
|
2015-01-08 03:08:56 +01:00
|
|
|
|
|
|
|
for _, group := range c.moduleGroupsSorted {
|
|
|
|
group.waitingCount = group.depsCount
|
|
|
|
}
|
|
|
|
|
|
|
|
visitOne := func(group *moduleGroup) {
|
|
|
|
count++
|
|
|
|
go func() {
|
2015-03-02 23:03:01 +01:00
|
|
|
ret := visit(group)
|
|
|
|
if ret {
|
|
|
|
cancel = true
|
|
|
|
}
|
2015-01-08 03:08:56 +01:00
|
|
|
doneCh <- group
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, group := range c.moduleGroupsSorted {
|
|
|
|
if group.waitingCount == 0 {
|
|
|
|
visitOne(group)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-04 19:41:00 +01:00
|
|
|
for count > 0 {
|
2015-01-08 03:08:56 +01:00
|
|
|
select {
|
|
|
|
case doneGroup := <-doneCh:
|
2015-03-02 23:03:01 +01:00
|
|
|
if !cancel {
|
|
|
|
for _, parent := range doneGroup.reverseDeps {
|
|
|
|
parent.waitingCount--
|
|
|
|
if parent.waitingCount == 0 {
|
|
|
|
visitOne(parent)
|
|
|
|
}
|
2015-01-08 03:08:56 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
count--
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// updateDependencies recursively walks the module dependency graph and updates
|
|
|
|
// additional fields based on the dependencies. It builds a sorted list of modules
|
|
|
|
// such that dependencies of a module always appear first, and populates reverse
|
|
|
|
// dependency links and counts of total dependencies. It also reports errors when
|
|
|
|
// it encounters dependency cycles. This should called after resolveDependencies,
|
|
|
|
// as well as after any mutator pass has called addDependency
|
|
|
|
func (c *Context) updateDependencies() (errs []error) {
|
2014-12-18 01:12:41 +01:00
|
|
|
visited := make(map[*moduleGroup]bool) // modules that were already checked
|
|
|
|
checking := make(map[*moduleGroup]bool) // modules actively being checked
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
sorted := make([]*moduleGroup, 0, len(c.moduleGroups))
|
2014-12-17 23:16:51 +01:00
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
var check func(group *moduleGroup) []*moduleGroup
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
check = func(group *moduleGroup) []*moduleGroup {
|
|
|
|
visited[group] = true
|
|
|
|
checking[group] = true
|
|
|
|
defer delete(checking, group)
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
deps := make(map[*moduleGroup]bool)
|
|
|
|
for _, module := range group.modules {
|
|
|
|
for _, dep := range module.directDeps {
|
|
|
|
deps[dep.group] = true
|
|
|
|
}
|
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-08 03:08:56 +01:00
|
|
|
group.reverseDeps = []*moduleGroup{}
|
|
|
|
group.depsCount = len(deps)
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
for dep := range deps {
|
2014-05-28 01:34:41 +02:00
|
|
|
if checking[dep] {
|
|
|
|
// This is a cycle.
|
2014-12-18 01:12:41 +01:00
|
|
|
return []*moduleGroup{dep, group}
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if !visited[dep] {
|
|
|
|
cycle := check(dep)
|
|
|
|
if cycle != nil {
|
2014-12-18 01:12:41 +01:00
|
|
|
if cycle[0] == group {
|
2014-05-28 01:34:41 +02:00
|
|
|
// We are the "start" of the cycle, so we're responsible
|
|
|
|
// for generating the errors. The cycle list is in
|
|
|
|
// reverse order because all the 'check' calls append
|
|
|
|
// their own module to the list.
|
|
|
|
errs = append(errs, &Error{
|
|
|
|
Err: fmt.Errorf("encountered dependency cycle:"),
|
2014-12-18 01:12:41 +01:00
|
|
|
Pos: group.pos,
|
2014-05-28 01:34:41 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
// Iterate backwards through the cycle list.
|
2014-12-18 01:12:41 +01:00
|
|
|
curGroup := group
|
2014-05-28 01:34:41 +02:00
|
|
|
for i := len(cycle) - 1; i >= 0; i-- {
|
2014-12-18 01:12:41 +01:00
|
|
|
nextGroup := cycle[i]
|
2014-05-28 01:34:41 +02:00
|
|
|
errs = append(errs, &Error{
|
|
|
|
Err: fmt.Errorf(" %q depends on %q",
|
2014-12-18 01:12:41 +01:00
|
|
|
curGroup.properties.Name,
|
|
|
|
nextGroup.properties.Name),
|
|
|
|
Pos: curGroup.propertyPos["deps"],
|
2014-05-28 01:34:41 +02:00
|
|
|
})
|
2014-12-18 01:12:41 +01:00
|
|
|
curGroup = nextGroup
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// We can continue processing this module's children to
|
|
|
|
// find more cycles. Since all the modules that were
|
|
|
|
// part of the found cycle were marked as visited we
|
|
|
|
// won't run into that cycle again.
|
|
|
|
} else {
|
|
|
|
// We're not the "start" of the cycle, so we just append
|
|
|
|
// our module to the list and return it.
|
2014-12-18 01:12:41 +01:00
|
|
|
return append(cycle, group)
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-01-08 03:08:56 +01:00
|
|
|
|
|
|
|
dep.reverseDeps = append(dep.reverseDeps, group)
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
sorted = append(sorted, group)
|
2014-12-17 23:16:51 +01:00
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
for _, group := range c.moduleGroups {
|
|
|
|
if !visited[group] {
|
|
|
|
cycle := check(group)
|
2014-05-28 01:34:41 +02:00
|
|
|
if cycle != nil {
|
|
|
|
panic("inconceivable!")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
c.moduleGroupsSorted = sorted
|
2014-12-17 23:16:51 +01:00
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-06-13 05:06:50 +02:00
|
|
|
// 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.
|
2014-06-26 02:21:54 +02:00
|
|
|
//
|
|
|
|
// The returned deps is a list of the ninja files dependencies that were added
|
|
|
|
// by the modules and singletons via the ModuleContext.AddNinjaFileDeps() and
|
|
|
|
// SingletonContext.AddNinjaFileDeps() methods.
|
|
|
|
func (c *Context) PrepareBuildActions(config interface{}) (deps []string, errs []error) {
|
2014-05-28 01:34:41 +02:00
|
|
|
c.buildActionsReady = false
|
|
|
|
|
|
|
|
if !c.dependenciesReady {
|
2014-09-25 05:28:11 +02:00
|
|
|
errs := c.ResolveDependencies(config)
|
2014-05-28 01:34:41 +02:00
|
|
|
if len(errs) > 0 {
|
2014-06-26 02:21:54 +02:00
|
|
|
return nil, errs
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-19 01:28:54 +01:00
|
|
|
errs = c.runMutators(config)
|
|
|
|
if len(errs) > 0 {
|
|
|
|
return nil, errs
|
|
|
|
}
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
liveGlobals := newLiveTracker(config)
|
|
|
|
|
|
|
|
c.initSpecialVariables()
|
|
|
|
|
2014-06-26 02:21:54 +02:00
|
|
|
depsModules, errs := c.generateModuleBuildActions(config, liveGlobals)
|
2014-05-28 01:34:41 +02:00
|
|
|
if len(errs) > 0 {
|
2014-06-26 02:21:54 +02:00
|
|
|
return nil, errs
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-06-26 02:21:54 +02:00
|
|
|
depsSingletons, errs := c.generateSingletonBuildActions(config, liveGlobals)
|
2014-05-28 01:34:41 +02:00
|
|
|
if len(errs) > 0 {
|
2014-06-26 02:21:54 +02:00
|
|
|
return nil, errs
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-06-26 02:21:54 +02:00
|
|
|
deps = append(depsModules, depsSingletons...)
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
if c.buildDir != nil {
|
|
|
|
liveGlobals.addNinjaStringDeps(c.buildDir)
|
|
|
|
}
|
|
|
|
|
|
|
|
pkgNames := c.makeUniquePackageNames(liveGlobals)
|
|
|
|
|
|
|
|
// This will panic if it finds a problem since it's a programming error.
|
|
|
|
c.checkForVariableReferenceCycles(liveGlobals.variables, pkgNames)
|
|
|
|
|
|
|
|
c.pkgNames = pkgNames
|
|
|
|
c.globalVariables = liveGlobals.variables
|
|
|
|
c.globalPools = liveGlobals.pools
|
|
|
|
c.globalRules = liveGlobals.rules
|
|
|
|
|
|
|
|
c.buildActionsReady = true
|
|
|
|
|
2014-06-26 02:21:54 +02:00
|
|
|
return deps, nil
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-12-19 01:28:54 +01:00
|
|
|
func (c *Context) runMutators(config interface{}) (errs []error) {
|
|
|
|
for _, mutator := range c.mutatorInfo {
|
|
|
|
if mutator.topDownMutator != nil {
|
|
|
|
errs = c.runTopDownMutator(config, mutator.name, mutator.topDownMutator)
|
|
|
|
} else if mutator.bottomUpMutator != nil {
|
|
|
|
errs = c.runBottomUpMutator(config, mutator.name, mutator.bottomUpMutator)
|
|
|
|
} else {
|
|
|
|
panic("no mutator set on " + mutator.name)
|
|
|
|
}
|
|
|
|
if len(errs) > 0 {
|
|
|
|
return errs
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) runTopDownMutator(config interface{},
|
|
|
|
name string, mutator TopDownMutator) (errs []error) {
|
|
|
|
|
|
|
|
for i := 0; i < len(c.moduleGroupsSorted); i++ {
|
|
|
|
group := c.moduleGroupsSorted[len(c.moduleGroupsSorted)-1-i]
|
|
|
|
for _, module := range group.modules {
|
|
|
|
mctx := &mutatorContext{
|
|
|
|
baseModuleContext: baseModuleContext{
|
|
|
|
context: c,
|
|
|
|
config: config,
|
|
|
|
group: group,
|
|
|
|
},
|
|
|
|
module: module,
|
|
|
|
name: name,
|
|
|
|
}
|
|
|
|
|
|
|
|
mutator(mctx)
|
|
|
|
if len(mctx.errs) > 0 {
|
|
|
|
errs = append(errs, mctx.errs...)
|
|
|
|
return errs
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return errs
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) runBottomUpMutator(config interface{},
|
|
|
|
name string, mutator BottomUpMutator) (errs []error) {
|
|
|
|
|
|
|
|
dependenciesModified := false
|
|
|
|
|
|
|
|
for _, group := range c.moduleGroupsSorted {
|
|
|
|
newModules := make([]*moduleInfo, 0, len(group.modules))
|
|
|
|
|
|
|
|
for _, module := range group.modules {
|
|
|
|
mctx := &mutatorContext{
|
|
|
|
baseModuleContext: baseModuleContext{
|
|
|
|
context: c,
|
|
|
|
config: config,
|
|
|
|
group: group,
|
|
|
|
},
|
|
|
|
module: module,
|
|
|
|
name: name,
|
|
|
|
}
|
|
|
|
|
|
|
|
mutator(mctx)
|
|
|
|
if len(mctx.errs) > 0 {
|
|
|
|
errs = append(errs, mctx.errs...)
|
|
|
|
return errs
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fix up any remaining dependencies on modules that were split into variants
|
|
|
|
// by replacing them with the first variant
|
|
|
|
for i, dep := range module.directDeps {
|
|
|
|
if dep.logicModule == nil {
|
|
|
|
module.directDeps[i] = dep.splitModules[0]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if mctx.dependenciesModified {
|
|
|
|
dependenciesModified = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if module.splitModules != nil {
|
|
|
|
newModules = append(newModules, module.splitModules...)
|
|
|
|
} else {
|
|
|
|
newModules = append(newModules, module)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
group.modules = newModules
|
|
|
|
}
|
|
|
|
|
|
|
|
if dependenciesModified {
|
2015-01-08 03:08:56 +01:00
|
|
|
errs = c.updateDependencies()
|
2014-12-19 01:28:54 +01:00
|
|
|
if len(errs) > 0 {
|
|
|
|
return errs
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return errs
|
|
|
|
}
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
func (c *Context) initSpecialVariables() {
|
|
|
|
c.buildDir = nil
|
|
|
|
c.requiredNinjaMajor = 1
|
|
|
|
c.requiredNinjaMinor = 1
|
|
|
|
c.requiredNinjaMicro = 0
|
|
|
|
}
|
|
|
|
|
2014-06-12 03:31:16 +02:00
|
|
|
func (c *Context) generateModuleBuildActions(config interface{},
|
2014-06-26 02:21:54 +02:00
|
|
|
liveGlobals *liveTracker) ([]string, []error) {
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2014-06-26 02:21:54 +02:00
|
|
|
var deps []string
|
2014-05-28 01:34:41 +02:00
|
|
|
var errs []error
|
|
|
|
|
2015-01-08 03:08:56 +01:00
|
|
|
cancelCh := make(chan struct{})
|
|
|
|
errsCh := make(chan []error)
|
|
|
|
depsCh := make(chan []string)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-cancelCh:
|
|
|
|
close(cancelCh)
|
|
|
|
return
|
|
|
|
case newErrs := <-errsCh:
|
|
|
|
errs = append(errs, newErrs...)
|
|
|
|
case newDeps := <-depsCh:
|
|
|
|
deps = append(deps, newDeps...)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2015-03-02 23:03:01 +01:00
|
|
|
c.parallelVisitAllBottomUp(func(group *moduleGroup) bool {
|
2014-09-25 02:51:52 +02:00
|
|
|
// The parent scope of the moduleContext's local scope gets overridden to be that of the
|
|
|
|
// calling Go package on a per-call basis. Since the initial parent scope doesn't matter we
|
|
|
|
// just set it to nil.
|
2014-12-18 01:12:41 +01:00
|
|
|
scope := newLocalScope(nil, moduleNamespacePrefix(group.properties.Name))
|
|
|
|
|
|
|
|
for _, module := range group.modules {
|
|
|
|
mctx := &moduleContext{
|
2014-12-17 22:23:56 +01:00
|
|
|
baseModuleContext: baseModuleContext{
|
|
|
|
context: c,
|
|
|
|
config: config,
|
|
|
|
group: group,
|
2014-11-11 23:18:53 +01:00
|
|
|
},
|
2014-12-17 22:23:56 +01:00
|
|
|
module: module,
|
|
|
|
scope: scope,
|
2014-12-18 01:12:41 +01:00
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
mctx.module.logicModule.GenerateBuildActions(mctx)
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
if len(mctx.errs) > 0 {
|
2015-01-08 03:08:56 +01:00
|
|
|
errsCh <- mctx.errs
|
2015-03-02 23:03:01 +01:00
|
|
|
return true
|
2014-12-18 01:12:41 +01:00
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-08 03:08:56 +01:00
|
|
|
depsCh <- mctx.ninjaFileDeps
|
2014-06-26 02:21:54 +02:00
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
newErrs := c.processLocalBuildActions(&group.actionDefs,
|
|
|
|
&mctx.actionDefs, liveGlobals)
|
|
|
|
if len(newErrs) > 0 {
|
2015-01-08 03:08:56 +01:00
|
|
|
errsCh <- newErrs
|
2015-03-02 23:03:01 +01:00
|
|
|
return true
|
2014-12-18 01:12:41 +01:00
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
2015-03-02 23:03:01 +01:00
|
|
|
return false
|
2015-01-08 03:08:56 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
cancelCh <- struct{}{}
|
|
|
|
<-cancelCh
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2014-06-26 02:21:54 +02:00
|
|
|
return deps, errs
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-06-12 03:31:16 +02:00
|
|
|
func (c *Context) generateSingletonBuildActions(config interface{},
|
2014-06-26 02:21:54 +02:00
|
|
|
liveGlobals *liveTracker) ([]string, []error) {
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2014-06-26 02:21:54 +02:00
|
|
|
var deps []string
|
2014-05-28 01:34:41 +02:00
|
|
|
var errs []error
|
2014-06-26 02:21:54 +02:00
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
for name, info := range c.singletonInfo {
|
2014-09-25 02:51:52 +02:00
|
|
|
// The parent scope of the singletonContext's local scope gets overridden to be that of the
|
|
|
|
// calling Go package on a per-call basis. Since the initial parent scope doesn't matter we
|
|
|
|
// just set it to nil.
|
|
|
|
scope := newLocalScope(nil, singletonNamespacePrefix(name))
|
2014-05-28 01:34:41 +02:00
|
|
|
|
|
|
|
sctx := &singletonContext{
|
|
|
|
context: c,
|
|
|
|
config: config,
|
2014-09-25 02:51:52 +02:00
|
|
|
scope: scope,
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
info.singleton.GenerateBuildActions(sctx)
|
|
|
|
|
|
|
|
if len(sctx.errs) > 0 {
|
|
|
|
errs = append(errs, sctx.errs...)
|
|
|
|
if len(errs) > maxErrors {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2014-06-26 02:21:54 +02:00
|
|
|
deps = append(deps, sctx.ninjaFileDeps...)
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
newErrs := c.processLocalBuildActions(&info.actionDefs,
|
|
|
|
&sctx.actionDefs, liveGlobals)
|
|
|
|
errs = append(errs, newErrs...)
|
|
|
|
if len(errs) > maxErrors {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-26 02:21:54 +02:00
|
|
|
return deps, errs
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) processLocalBuildActions(out, in *localBuildActions,
|
|
|
|
liveGlobals *liveTracker) []error {
|
|
|
|
|
|
|
|
var errs []error
|
|
|
|
|
|
|
|
// First we go through and add everything referenced by the module's
|
|
|
|
// buildDefs to the live globals set. This will end up adding the live
|
|
|
|
// locals to the set as well, but we'll take them out after.
|
|
|
|
for _, def := range in.buildDefs {
|
|
|
|
err := liveGlobals.AddBuildDefDeps(def)
|
|
|
|
if err != nil {
|
|
|
|
errs = append(errs, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(errs) > 0 {
|
|
|
|
return errs
|
|
|
|
}
|
|
|
|
|
2014-12-19 01:28:54 +01:00
|
|
|
out.buildDefs = append(out.buildDefs, in.buildDefs...)
|
2014-05-28 01:34:41 +02:00
|
|
|
|
|
|
|
// We use the now-incorrect set of live "globals" to determine which local
|
|
|
|
// definitions are live. As we go through copying those live locals to the
|
2014-12-19 01:28:54 +01:00
|
|
|
// moduleGroup we remove them from the live globals set.
|
2014-05-28 01:34:41 +02:00
|
|
|
for _, v := range in.variables {
|
|
|
|
_, isLive := liveGlobals.variables[v]
|
|
|
|
if isLive {
|
|
|
|
out.variables = append(out.variables, v)
|
|
|
|
delete(liveGlobals.variables, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, r := range in.rules {
|
|
|
|
_, isLive := liveGlobals.rules[r]
|
|
|
|
if isLive {
|
|
|
|
out.rules = append(out.rules, r)
|
|
|
|
delete(liveGlobals.rules, r)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
func (c *Context) visitDepsDepthFirst(topModule *moduleInfo, visit func(Module)) {
|
|
|
|
visited := make(map[*moduleInfo]bool)
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
var walk func(module *moduleInfo)
|
|
|
|
walk = func(module *moduleInfo) {
|
|
|
|
visited[module] = true
|
|
|
|
for _, moduleDep := range module.directDeps {
|
|
|
|
if !visited[moduleDep] {
|
|
|
|
walk(moduleDep)
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
if module != topModule {
|
|
|
|
visit(module.logicModule)
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
}
|
2014-12-18 01:12:41 +01:00
|
|
|
|
|
|
|
walk(topModule)
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
func (c *Context) visitDepsDepthFirstIf(topModule *moduleInfo, pred func(Module) bool,
|
2014-05-28 01:34:41 +02:00
|
|
|
visit func(Module)) {
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
visited := make(map[*moduleInfo]bool)
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
var walk func(module *moduleInfo)
|
|
|
|
walk = func(module *moduleInfo) {
|
|
|
|
visited[module] = true
|
|
|
|
for _, moduleDep := range module.directDeps {
|
|
|
|
if !visited[moduleDep] {
|
|
|
|
walk(moduleDep)
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
2014-11-26 23:45:37 +01:00
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
if module != topModule {
|
|
|
|
if pred(module.logicModule) {
|
|
|
|
visit(module.logicModule)
|
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
}
|
2014-12-18 01:12:41 +01:00
|
|
|
|
|
|
|
walk(topModule)
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-12-19 01:28:54 +01:00
|
|
|
func (c *Context) visitDirectDeps(module *moduleInfo, visit func(Module)) {
|
|
|
|
for _, dep := range module.directDeps {
|
|
|
|
visit(dep.logicModule)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) visitDirectDepsIf(module *moduleInfo, pred func(Module) bool,
|
|
|
|
visit func(Module)) {
|
|
|
|
|
|
|
|
for _, dep := range module.directDeps {
|
|
|
|
if pred(dep.logicModule) {
|
|
|
|
visit(dep.logicModule)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-25 05:26:52 +02:00
|
|
|
func (c *Context) sortedModuleNames() []string {
|
|
|
|
if c.cachedSortedModuleNames == nil {
|
2014-12-18 01:12:41 +01:00
|
|
|
c.cachedSortedModuleNames = make([]string, 0, len(c.moduleGroups))
|
|
|
|
for moduleName := range c.moduleGroups {
|
2014-09-25 05:26:52 +02:00
|
|
|
c.cachedSortedModuleNames = append(c.cachedSortedModuleNames,
|
|
|
|
moduleName)
|
|
|
|
}
|
|
|
|
sort.Strings(c.cachedSortedModuleNames)
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.cachedSortedModuleNames
|
|
|
|
}
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
func (c *Context) visitAllModules(visit func(Module)) {
|
2014-09-25 05:26:52 +02:00
|
|
|
for _, moduleName := range c.sortedModuleNames() {
|
2014-12-18 01:12:41 +01:00
|
|
|
group := c.moduleGroups[moduleName]
|
|
|
|
for _, module := range group.modules {
|
|
|
|
visit(module.logicModule)
|
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) visitAllModulesIf(pred func(Module) bool,
|
|
|
|
visit func(Module)) {
|
|
|
|
|
2014-09-25 05:26:52 +02:00
|
|
|
for _, moduleName := range c.sortedModuleNames() {
|
2014-12-18 01:12:41 +01:00
|
|
|
group := c.moduleGroups[moduleName]
|
|
|
|
for _, module := range group.modules {
|
|
|
|
if pred(module.logicModule) {
|
|
|
|
visit(module.logicModule)
|
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) requireNinjaVersion(major, minor, micro int) {
|
|
|
|
if major != 1 {
|
|
|
|
panic("ninja version with major version != 1 not supported")
|
|
|
|
}
|
|
|
|
if c.requiredNinjaMinor < minor {
|
|
|
|
c.requiredNinjaMinor = minor
|
|
|
|
c.requiredNinjaMicro = micro
|
|
|
|
}
|
|
|
|
if c.requiredNinjaMinor == minor && c.requiredNinjaMicro < micro {
|
|
|
|
c.requiredNinjaMicro = micro
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) setBuildDir(value *ninjaString) {
|
|
|
|
if c.buildDir != nil {
|
|
|
|
panic("buildDir set multiple times")
|
|
|
|
}
|
|
|
|
c.buildDir = value
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) makeUniquePackageNames(
|
2014-10-03 11:49:58 +02:00
|
|
|
liveGlobals *liveTracker) map[*PackageContext]string {
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2014-10-03 11:49:58 +02:00
|
|
|
pkgs := make(map[string]*PackageContext)
|
|
|
|
pkgNames := make(map[*PackageContext]string)
|
|
|
|
longPkgNames := make(map[*PackageContext]bool)
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2014-10-03 11:49:58 +02:00
|
|
|
processPackage := func(pctx *PackageContext) {
|
|
|
|
if pctx == nil {
|
2014-05-28 01:34:41 +02:00
|
|
|
// This is a built-in rule and has no package.
|
|
|
|
return
|
|
|
|
}
|
2014-10-03 11:49:58 +02:00
|
|
|
if _, ok := pkgNames[pctx]; ok {
|
2014-05-28 01:34:41 +02:00
|
|
|
// We've already processed this package.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-10-03 11:49:58 +02:00
|
|
|
otherPkg, present := pkgs[pctx.shortName]
|
2014-05-28 01:34:41 +02:00
|
|
|
if present {
|
|
|
|
// Short name collision. Both this package and the one that's
|
|
|
|
// already there need to use their full names. We leave the short
|
|
|
|
// name in pkgNames for now so future collisions still get caught.
|
2014-10-03 11:49:58 +02:00
|
|
|
longPkgNames[pctx] = true
|
2014-05-28 01:34:41 +02:00
|
|
|
longPkgNames[otherPkg] = true
|
|
|
|
} else {
|
|
|
|
// No collision so far. Tentatively set the package's name to be
|
|
|
|
// its short name.
|
2014-10-03 11:49:58 +02:00
|
|
|
pkgNames[pctx] = pctx.shortName
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We try to give all packages their short name, but when we get collisions
|
|
|
|
// we need to use the full unique package name.
|
|
|
|
for v, _ := range liveGlobals.variables {
|
2014-10-03 11:49:58 +02:00
|
|
|
processPackage(v.packageContext())
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
for p, _ := range liveGlobals.pools {
|
2014-10-03 11:49:58 +02:00
|
|
|
processPackage(p.packageContext())
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
for r, _ := range liveGlobals.rules {
|
2014-10-03 11:49:58 +02:00
|
|
|
processPackage(r.packageContext())
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add the packages that had collisions using their full unique names. This
|
|
|
|
// will overwrite any short names that were added in the previous step.
|
2014-10-03 11:49:58 +02:00
|
|
|
for pctx := range longPkgNames {
|
|
|
|
pkgNames[pctx] = pctx.fullName
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return pkgNames
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) checkForVariableReferenceCycles(
|
2014-10-03 11:49:58 +02:00
|
|
|
variables map[Variable]*ninjaString, pkgNames map[*PackageContext]string) {
|
2014-05-28 01:34:41 +02:00
|
|
|
|
|
|
|
visited := make(map[Variable]bool) // variables that were already checked
|
|
|
|
checking := make(map[Variable]bool) // variables actively being checked
|
|
|
|
|
|
|
|
var check func(v Variable) []Variable
|
|
|
|
|
|
|
|
check = func(v Variable) []Variable {
|
|
|
|
visited[v] = true
|
|
|
|
checking[v] = true
|
|
|
|
defer delete(checking, v)
|
|
|
|
|
|
|
|
value := variables[v]
|
|
|
|
for _, dep := range value.variables {
|
|
|
|
if checking[dep] {
|
|
|
|
// This is a cycle.
|
|
|
|
return []Variable{dep, v}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !visited[dep] {
|
|
|
|
cycle := check(dep)
|
|
|
|
if cycle != nil {
|
|
|
|
if cycle[0] == v {
|
|
|
|
// We are the "start" of the cycle, so we're responsible
|
|
|
|
// for generating the errors. The cycle list is in
|
|
|
|
// reverse order because all the 'check' calls append
|
|
|
|
// their own module to the list.
|
|
|
|
msgs := []string{"detected variable reference cycle:"}
|
|
|
|
|
|
|
|
// Iterate backwards through the cycle list.
|
|
|
|
curName := v.fullName(pkgNames)
|
|
|
|
curValue := value.Value(pkgNames)
|
|
|
|
for i := len(cycle) - 1; i >= 0; i-- {
|
|
|
|
next := cycle[i]
|
|
|
|
nextName := next.fullName(pkgNames)
|
|
|
|
nextValue := variables[next].Value(pkgNames)
|
|
|
|
|
|
|
|
msgs = append(msgs, fmt.Sprintf(
|
|
|
|
" %q depends on %q", curName, nextName))
|
|
|
|
msgs = append(msgs, fmt.Sprintf(
|
|
|
|
" [%s = %s]", curName, curValue))
|
|
|
|
|
|
|
|
curName = nextName
|
|
|
|
curValue = nextValue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Variable reference cycles are a programming error,
|
|
|
|
// not the fault of the Blueprint file authors.
|
|
|
|
panic(strings.Join(msgs, "\n"))
|
|
|
|
} else {
|
|
|
|
// We're not the "start" of the cycle, so we just append
|
|
|
|
// our module to the list and return it.
|
|
|
|
return append(cycle, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for v := range variables {
|
|
|
|
if !visited[v] {
|
|
|
|
cycle := check(v)
|
|
|
|
if cycle != nil {
|
|
|
|
panic("inconceivable!")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-28 06:34:56 +01:00
|
|
|
// AllTargets returns a map all the build target names to the rule used to build
|
|
|
|
// them. This is the same information that is output by running 'ninja -t
|
|
|
|
// targets all'. If this is called before PrepareBuildActions successfully
|
|
|
|
// completes then ErrbuildActionsNotReady is returned.
|
|
|
|
func (c *Context) AllTargets() (map[string]string, error) {
|
|
|
|
if !c.buildActionsReady {
|
|
|
|
return nil, ErrBuildActionsNotReady
|
|
|
|
}
|
|
|
|
|
|
|
|
targets := map[string]string{}
|
|
|
|
|
|
|
|
// Collect all the module build targets.
|
2014-12-18 01:12:41 +01:00
|
|
|
for _, info := range c.moduleGroups {
|
2014-10-28 06:34:56 +01:00
|
|
|
for _, buildDef := range info.actionDefs.buildDefs {
|
|
|
|
ruleName := buildDef.Rule.fullName(c.pkgNames)
|
|
|
|
for _, output := range buildDef.Outputs {
|
2014-11-22 00:12:08 +01:00
|
|
|
outputValue, err := output.Eval(c.globalVariables)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-10-28 06:34:56 +01:00
|
|
|
targets[outputValue] = ruleName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Collect all the singleton build targets.
|
|
|
|
for _, info := range c.singletonInfo {
|
|
|
|
for _, buildDef := range info.actionDefs.buildDefs {
|
|
|
|
ruleName := buildDef.Rule.fullName(c.pkgNames)
|
|
|
|
for _, output := range buildDef.Outputs {
|
2014-11-22 00:12:08 +01:00
|
|
|
outputValue, err := output.Eval(c.globalVariables)
|
|
|
|
if err != nil {
|
2014-12-31 01:05:02 +01:00
|
|
|
return nil, err
|
2014-11-22 00:12:08 +01:00
|
|
|
}
|
2014-10-28 06:34:56 +01:00
|
|
|
targets[outputValue] = ruleName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return targets, nil
|
|
|
|
}
|
|
|
|
|
2014-06-13 05:06:50 +02:00
|
|
|
// 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.
|
2014-05-28 01:34:41 +02:00
|
|
|
func (c *Context) WriteBuildFile(w io.Writer) error {
|
|
|
|
if !c.buildActionsReady {
|
|
|
|
return ErrBuildActionsNotReady
|
|
|
|
}
|
|
|
|
|
|
|
|
nw := newNinjaWriter(w)
|
|
|
|
|
|
|
|
err := c.writeBuildFileHeader(nw)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = c.writeNinjaRequiredVersion(nw)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Group the globals by package.
|
|
|
|
|
|
|
|
err = c.writeGlobalVariables(nw)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = c.writeGlobalPools(nw)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = c.writeBuildDir(nw)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = c.writeGlobalRules(nw)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = c.writeAllModuleActions(nw)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = c.writeAllSingletonActions(nw)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-09-25 05:26:52 +02:00
|
|
|
type pkgAssociation struct {
|
|
|
|
PkgName string
|
|
|
|
PkgPath string
|
|
|
|
}
|
|
|
|
|
|
|
|
type pkgAssociationSorter struct {
|
|
|
|
pkgs []pkgAssociation
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *pkgAssociationSorter) Len() int {
|
|
|
|
return len(s.pkgs)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *pkgAssociationSorter) Less(i, j int) bool {
|
|
|
|
iName := s.pkgs[i].PkgName
|
|
|
|
jName := s.pkgs[j].PkgName
|
|
|
|
return iName < jName
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *pkgAssociationSorter) Swap(i, j int) {
|
|
|
|
s.pkgs[i], s.pkgs[j] = s.pkgs[j], s.pkgs[i]
|
|
|
|
}
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
func (c *Context) writeBuildFileHeader(nw *ninjaWriter) error {
|
|
|
|
headerTemplate := template.New("fileHeader")
|
|
|
|
_, err := headerTemplate.Parse(fileHeaderTemplate)
|
|
|
|
if err != nil {
|
|
|
|
// This is a programming error.
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var pkgs []pkgAssociation
|
|
|
|
maxNameLen := 0
|
|
|
|
for pkg, name := range c.pkgNames {
|
|
|
|
pkgs = append(pkgs, pkgAssociation{
|
|
|
|
PkgName: name,
|
|
|
|
PkgPath: pkg.pkgPath,
|
|
|
|
})
|
|
|
|
if len(name) > maxNameLen {
|
|
|
|
maxNameLen = len(name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range pkgs {
|
|
|
|
pkgs[i].PkgName += strings.Repeat(" ", maxNameLen-len(pkgs[i].PkgName))
|
|
|
|
}
|
|
|
|
|
2014-09-25 05:26:52 +02:00
|
|
|
sort.Sort(&pkgAssociationSorter{pkgs})
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
params := map[string]interface{}{
|
|
|
|
"Pkgs": pkgs,
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := bytes.NewBuffer(nil)
|
|
|
|
err = headerTemplate.Execute(buf, params)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nw.Comment(buf.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) writeNinjaRequiredVersion(nw *ninjaWriter) error {
|
|
|
|
value := fmt.Sprintf("%d.%d.%d", c.requiredNinjaMajor, c.requiredNinjaMinor,
|
|
|
|
c.requiredNinjaMicro)
|
|
|
|
|
|
|
|
err := nw.Assign("ninja_required_version", value)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nw.BlankLine()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) writeBuildDir(nw *ninjaWriter) error {
|
|
|
|
if c.buildDir != nil {
|
|
|
|
err := nw.Assign("builddir", c.buildDir.Value(c.pkgNames))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nw.BlankLine()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-09-25 05:26:52 +02:00
|
|
|
type globalEntity interface {
|
2014-10-03 11:49:58 +02:00
|
|
|
fullName(pkgNames map[*PackageContext]string) string
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-09-25 05:26:52 +02:00
|
|
|
type globalEntitySorter struct {
|
2014-10-03 11:49:58 +02:00
|
|
|
pkgNames map[*PackageContext]string
|
2014-09-25 05:26:52 +02:00
|
|
|
entities []globalEntity
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-09-25 05:26:52 +02:00
|
|
|
func (s *globalEntitySorter) Len() int {
|
|
|
|
return len(s.entities)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *globalEntitySorter) Less(i, j int) bool {
|
|
|
|
iName := s.entities[i].fullName(s.pkgNames)
|
|
|
|
jName := s.entities[j].fullName(s.pkgNames)
|
2014-05-28 01:34:41 +02:00
|
|
|
return iName < jName
|
|
|
|
}
|
|
|
|
|
2014-09-25 05:26:52 +02:00
|
|
|
func (s *globalEntitySorter) Swap(i, j int) {
|
|
|
|
s.entities[i], s.entities[j] = s.entities[j], s.entities[i]
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) writeGlobalVariables(nw *ninjaWriter) error {
|
|
|
|
visited := make(map[Variable]bool)
|
|
|
|
|
|
|
|
var walk func(v Variable) error
|
|
|
|
walk = func(v Variable) error {
|
|
|
|
visited[v] = true
|
|
|
|
|
|
|
|
// First visit variables on which this variable depends.
|
|
|
|
value := c.globalVariables[v]
|
|
|
|
for _, dep := range value.variables {
|
|
|
|
if !visited[dep] {
|
|
|
|
err := walk(dep)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err := nw.Assign(v.fullName(c.pkgNames), value.Value(c.pkgNames))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nw.BlankLine()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-09-25 05:26:52 +02:00
|
|
|
globalVariables := make([]globalEntity, 0, len(c.globalVariables))
|
|
|
|
for variable := range c.globalVariables {
|
|
|
|
globalVariables = append(globalVariables, variable)
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2014-09-25 05:26:52 +02:00
|
|
|
sort.Sort(&globalEntitySorter{c.pkgNames, globalVariables})
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2014-09-25 05:26:52 +02:00
|
|
|
for _, entity := range globalVariables {
|
|
|
|
v := entity.(Variable)
|
2014-05-28 01:34:41 +02:00
|
|
|
if !visited[v] {
|
|
|
|
err := walk(v)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) writeGlobalPools(nw *ninjaWriter) error {
|
2014-09-25 05:26:52 +02:00
|
|
|
globalPools := make([]globalEntity, 0, len(c.globalPools))
|
|
|
|
for pool := range c.globalPools {
|
|
|
|
globalPools = append(globalPools, pool)
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Sort(&globalEntitySorter{c.pkgNames, globalPools})
|
|
|
|
|
|
|
|
for _, entity := range globalPools {
|
|
|
|
pool := entity.(Pool)
|
2014-05-28 01:34:41 +02:00
|
|
|
name := pool.fullName(c.pkgNames)
|
2014-09-25 05:26:52 +02:00
|
|
|
def := c.globalPools[pool]
|
2014-05-28 01:34:41 +02:00
|
|
|
err := def.WriteTo(nw, name)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nw.BlankLine()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) writeGlobalRules(nw *ninjaWriter) error {
|
2014-09-25 05:26:52 +02:00
|
|
|
globalRules := make([]globalEntity, 0, len(c.globalRules))
|
|
|
|
for rule := range c.globalRules {
|
|
|
|
globalRules = append(globalRules, rule)
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Sort(&globalEntitySorter{c.pkgNames, globalRules})
|
|
|
|
|
|
|
|
for _, entity := range globalRules {
|
|
|
|
rule := entity.(Rule)
|
2014-05-28 01:34:41 +02:00
|
|
|
name := rule.fullName(c.pkgNames)
|
2014-09-25 05:26:52 +02:00
|
|
|
def := c.globalRules[rule]
|
2014-05-28 01:34:41 +02:00
|
|
|
err := def.WriteTo(nw, name, c.pkgNames)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nw.BlankLine()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
type moduleGroupSorter []*moduleGroup
|
2014-06-12 01:27:16 +02:00
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
func (s moduleGroupSorter) Len() int {
|
2014-06-12 01:27:16 +02:00
|
|
|
return len(s)
|
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
func (s moduleGroupSorter) Less(i, j int) bool {
|
2014-06-12 01:27:16 +02:00
|
|
|
iName := s[i].properties.Name
|
|
|
|
jName := s[j].properties.Name
|
|
|
|
return iName < jName
|
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
func (s moduleGroupSorter) Swap(i, j int) {
|
2014-06-12 01:27:16 +02:00
|
|
|
s[i], s[j] = s[j], s[i]
|
|
|
|
}
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
func (c *Context) writeAllModuleActions(nw *ninjaWriter) error {
|
|
|
|
headerTemplate := template.New("moduleHeader")
|
|
|
|
_, err := headerTemplate.Parse(moduleHeaderTemplate)
|
|
|
|
if err != nil {
|
|
|
|
// This is a programming error.
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2014-12-18 01:12:41 +01:00
|
|
|
infos := make([]*moduleGroup, 0, len(c.moduleGroups))
|
|
|
|
for _, info := range c.moduleGroups {
|
2014-06-12 01:27:16 +02:00
|
|
|
infos = append(infos, info)
|
|
|
|
}
|
2014-12-18 01:12:41 +01:00
|
|
|
sort.Sort(moduleGroupSorter(infos))
|
2014-06-12 01:27:16 +02:00
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
buf := bytes.NewBuffer(nil)
|
|
|
|
|
2014-06-12 01:27:16 +02:00
|
|
|
for _, info := range infos {
|
2014-05-28 01:34:41 +02:00
|
|
|
buf.Reset()
|
2014-06-05 00:33:08 +02:00
|
|
|
|
|
|
|
// In order to make the bootstrap build manifest independent of the
|
|
|
|
// build dir we need to output the Blueprints file locations in the
|
|
|
|
// comments as paths relative to the source directory.
|
|
|
|
relPos := info.pos
|
2014-06-13 05:06:31 +02:00
|
|
|
relPos.Filename = info.relBlueprintsFile
|
2014-06-05 00:33:08 +02:00
|
|
|
|
2014-09-25 02:51:52 +02:00
|
|
|
// Get the name and location of the factory function for the module.
|
|
|
|
factory := c.moduleFactories[info.typeName]
|
|
|
|
factoryFunc := runtime.FuncForPC(reflect.ValueOf(factory).Pointer())
|
|
|
|
factoryName := factoryFunc.Name()
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
infoMap := map[string]interface{}{
|
|
|
|
"properties": info.properties,
|
|
|
|
"typeName": info.typeName,
|
2014-09-25 02:51:52 +02:00
|
|
|
"goFactory": factoryName,
|
2014-06-05 00:33:08 +02:00
|
|
|
"pos": relPos,
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
err = headerTemplate.Execute(buf, infoMap)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nw.Comment(buf.String())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nw.BlankLine()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = c.writeLocalBuildActions(nw, &info.actionDefs)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nw.BlankLine()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) writeAllSingletonActions(nw *ninjaWriter) error {
|
|
|
|
headerTemplate := template.New("singletonHeader")
|
|
|
|
_, err := headerTemplate.Parse(singletonHeaderTemplate)
|
|
|
|
if err != nil {
|
|
|
|
// This is a programming error.
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := bytes.NewBuffer(nil)
|
|
|
|
|
2014-06-12 01:27:16 +02:00
|
|
|
singletonNames := make([]string, 0, len(c.singletonInfo))
|
|
|
|
for name := range c.singletonInfo {
|
|
|
|
singletonNames = append(singletonNames, name)
|
|
|
|
}
|
|
|
|
sort.Strings(singletonNames)
|
|
|
|
|
|
|
|
for _, name := range singletonNames {
|
|
|
|
info := c.singletonInfo[name]
|
|
|
|
|
2014-09-25 02:51:52 +02:00
|
|
|
// Get the name of the factory function for the module.
|
|
|
|
factory := info.factory
|
|
|
|
factoryFunc := runtime.FuncForPC(reflect.ValueOf(factory).Pointer())
|
|
|
|
factoryName := factoryFunc.Name()
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
buf.Reset()
|
|
|
|
infoMap := map[string]interface{}{
|
2014-09-25 02:51:52 +02:00
|
|
|
"name": name,
|
|
|
|
"goFactory": factoryName,
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
err = headerTemplate.Execute(buf, infoMap)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nw.Comment(buf.String())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nw.BlankLine()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = c.writeLocalBuildActions(nw, &info.actionDefs)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nw.BlankLine()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) writeLocalBuildActions(nw *ninjaWriter,
|
|
|
|
defs *localBuildActions) error {
|
|
|
|
|
|
|
|
// Write the local variable assignments.
|
|
|
|
for _, v := range defs.variables {
|
|
|
|
// A localVariable doesn't need the package names or config to
|
|
|
|
// determine its name or value.
|
|
|
|
name := v.fullName(nil)
|
|
|
|
value, err := v.value(nil)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
err = nw.Assign(name, value.Value(c.pkgNames))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(defs.variables) > 0 {
|
|
|
|
err := nw.BlankLine()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write the local rules.
|
|
|
|
for _, r := range defs.rules {
|
|
|
|
// A localRule doesn't need the package names or config to determine
|
|
|
|
// its name or definition.
|
|
|
|
name := r.fullName(nil)
|
|
|
|
def, err := r.def(nil)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = def.WriteTo(nw, name, c.pkgNames)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nw.BlankLine()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write the build definitions.
|
|
|
|
for _, buildDef := range defs.buildDefs {
|
|
|
|
err := buildDef.WriteTo(nw, c.pkgNames)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(buildDef.Args) > 0 {
|
|
|
|
err = nw.BlankLine()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var fileHeaderTemplate = `******************************************************************************
|
|
|
|
*** This file is generated and should not be edited ***
|
|
|
|
******************************************************************************
|
|
|
|
{{if .Pkgs}}
|
|
|
|
This file contains variables, rules, and pools with name prefixes indicating
|
|
|
|
they were generated by the following Go packages:
|
|
|
|
{{range .Pkgs}}
|
|
|
|
{{.PkgName}} [from Go package {{.PkgPath}}]{{end}}{{end}}
|
|
|
|
|
|
|
|
`
|
|
|
|
|
|
|
|
var moduleHeaderTemplate = `# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
Module: {{.properties.Name}}
|
|
|
|
Type: {{.typeName}}
|
2014-09-25 02:51:52 +02:00
|
|
|
Factory: {{.goFactory}}
|
2014-05-28 01:34:41 +02:00
|
|
|
Defined: {{.pos}}
|
|
|
|
`
|
|
|
|
|
|
|
|
var singletonHeaderTemplate = `# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
Singleton: {{.name}}
|
2014-09-25 02:51:52 +02:00
|
|
|
Factory: {{.goFactory}}
|
2014-05-28 01:34:41 +02:00
|
|
|
`
|