blob: 96946d411cf37d5987ffad70849f1afd79a0f90e [file] [log] [blame]
Colin Cross8e0c5112015-01-23 14:15:10 -08001// Copyright 2014 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Jamie Gennis1bc967e2014-05-27 16:34:41 -070015package blueprint
16
17import (
18 "blueprint/parser"
Colin Crossc9028482014-12-18 16:28:54 -080019 "blueprint/proptools"
Jamie Gennis1bc967e2014-05-27 16:34:41 -070020 "bytes"
21 "errors"
22 "fmt"
23 "io"
24 "os"
25 "path/filepath"
26 "reflect"
Romain Guy28529652014-08-12 17:50:11 -070027 "runtime"
Jamie Gennis1bc967e2014-05-27 16:34:41 -070028 "sort"
Colin Cross6134a5c2015-02-10 11:26:26 -080029 "strconv"
Jamie Gennis1bc967e2014-05-27 16:34:41 -070030 "strings"
31 "text/scanner"
32 "text/template"
33)
34
35var ErrBuildActionsNotReady = errors.New("build actions are not ready")
36
37const maxErrors = 10
38
Jamie Gennisd4e10182014-06-12 20:06:50 -070039// A Context contains all the state needed to parse a set of Blueprints files
40// and generate a Ninja file. The process of generating a Ninja file proceeds
41// through a series of four phases. Each phase corresponds with a some methods
42// on the Context object
43//
44// Phase Methods
45// ------------ -------------------------------------------
Jamie Gennis7d5b2f82014-09-24 17:51:52 -070046// 1. Registration RegisterModuleType, RegisterSingletonType
Jamie Gennisd4e10182014-06-12 20:06:50 -070047//
48// 2. Parse ParseBlueprintsFiles, Parse
49//
Jamie Gennis7d5b2f82014-09-24 17:51:52 -070050// 3. Generate ResolveDependencies, PrepareBuildActions
Jamie Gennisd4e10182014-06-12 20:06:50 -070051//
52// 4. Write WriteBuildFile
53//
54// The registration phase prepares the context to process Blueprints files
55// containing various types of modules. The parse phase reads in one or more
56// Blueprints files and validates their contents against the module types that
57// have been registered. The generate phase then analyzes the parsed Blueprints
58// contents to create an internal representation for the build actions that must
59// be performed. This phase also performs validation of the module dependencies
60// and property values defined in the parsed Blueprints files. Finally, the
61// write phase generates the Ninja manifest text based on the generated build
62// actions.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070063type Context struct {
64 // set at instantiation
Colin Crossbbfa51a2014-12-17 16:12:41 -080065 moduleFactories map[string]ModuleFactory
66 moduleGroups map[string]*moduleGroup
67 moduleInfo map[Module]*moduleInfo
68 moduleGroupsSorted []*moduleGroup
69 singletonInfo map[string]*singletonInfo
Colin Crossc9028482014-12-18 16:28:54 -080070 mutatorInfo []*mutatorInfo
Colin Cross6134a5c2015-02-10 11:26:26 -080071 moduleNinjaNames map[string]*moduleGroup
Jamie Gennis1bc967e2014-05-27 16:34:41 -070072
73 dependenciesReady bool // set to true on a successful ResolveDependencies
74 buildActionsReady bool // set to true on a successful PrepareBuildActions
75
76 // set by SetIgnoreUnknownModuleTypes
77 ignoreUnknownModuleTypes bool
78
79 // set during PrepareBuildActions
Jamie Gennis2fb20952014-10-03 02:49:58 -070080 pkgNames map[*PackageContext]string
Jamie Gennis1bc967e2014-05-27 16:34:41 -070081 globalVariables map[Variable]*ninjaString
82 globalPools map[Pool]*poolDef
83 globalRules map[Rule]*ruleDef
84
85 // set during PrepareBuildActions
86 buildDir *ninjaString // The builddir special Ninja variable
87 requiredNinjaMajor int // For the ninja_required_version variable
88 requiredNinjaMinor int // For the ninja_required_version variable
89 requiredNinjaMicro int // For the ninja_required_version variable
Jamie Gennisc15544d2014-09-24 20:26:52 -070090
91 // set lazily by sortedModuleNames
92 cachedSortedModuleNames []string
Jamie Gennis1bc967e2014-05-27 16:34:41 -070093}
94
Jamie Gennisd4e10182014-06-12 20:06:50 -070095// An Error describes a problem that was encountered that is related to a
96// particular location in a Blueprints file.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070097type Error struct {
Jamie Gennisd4e10182014-06-12 20:06:50 -070098 Err error // the error that occurred
99 Pos scanner.Position // the relevant Blueprints file location
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700100}
101
102type localBuildActions struct {
103 variables []*localVariable
104 rules []*localRule
105 buildDefs []*buildDef
106}
107
Colin Crossbbfa51a2014-12-17 16:12:41 -0800108type moduleGroup struct {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700109 // set during Parse
Jamie Gennisec701282014-06-12 20:06:31 -0700110 typeName string
Colin Cross6134a5c2015-02-10 11:26:26 -0800111 ninjaName string
Jamie Gennisec701282014-06-12 20:06:31 -0700112 relBlueprintsFile string
113 pos scanner.Position
114 propertyPos map[string]scanner.Position
115 properties struct {
Jamie Gennis1174c692014-10-05 07:41:44 -0700116 Name string
117 Deps []string
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700118 }
119
Colin Crossbbfa51a2014-12-17 16:12:41 -0800120 modules []*moduleInfo
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700121
Colin Cross691a60d2015-01-07 18:08:56 -0800122 // set during updateDependencies
123 reverseDeps []*moduleGroup
124 depsCount int
125
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700126 // set during PrepareBuildActions
127 actionDefs localBuildActions
Colin Cross691a60d2015-01-07 18:08:56 -0800128
129 // used by parallelVisitAllBottomUp
130 waitingCount int
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700131}
132
Colin Crossbbfa51a2014-12-17 16:12:41 -0800133type moduleInfo struct {
Colin Crossc9028482014-12-18 16:28:54 -0800134 name []subName
135 logicModule Module
136 group *moduleGroup
137 moduleProperties []interface{}
138
139 // set during ResolveDependencies
140 directDeps []*moduleInfo
141
142 // set during each runMutator
143 splitModules []*moduleInfo
144}
145
146type subName struct {
147 mutatorName string
148 variantName string
149}
150
151func (module *moduleInfo) subName() string {
152 names := []string{}
153 for _, subName := range module.name {
154 if subName.variantName != "" {
155 names = append(names, subName.variantName)
156 }
157 }
158 return strings.Join(names, "_")
Colin Crossbbfa51a2014-12-17 16:12:41 -0800159}
160
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700161type singletonInfo struct {
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700162 // set during RegisterSingletonType
163 factory SingletonFactory
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700164 singleton Singleton
165
166 // set during PrepareBuildActions
167 actionDefs localBuildActions
168}
169
Colin Crossc9028482014-12-18 16:28:54 -0800170type mutatorInfo struct {
171 // set during RegisterMutator
Colin Crossc0dbc552015-01-02 15:19:28 -0800172 topDownMutator TopDownMutator
173 bottomUpMutator BottomUpMutator
174 name string
Colin Crossc9028482014-12-18 16:28:54 -0800175}
176
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700177func (e *Error) Error() string {
178
179 return fmt.Sprintf("%s: %s", e.Pos, e.Err)
180}
181
Jamie Gennisd4e10182014-06-12 20:06:50 -0700182// NewContext creates a new Context object. The created context initially has
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700183// no module or singleton factories registered, so the RegisterModuleFactory and
184// RegisterSingletonFactory methods must be called before it can do anything
185// useful.
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700186func NewContext() *Context {
187 return &Context{
Colin Cross6134a5c2015-02-10 11:26:26 -0800188 moduleFactories: make(map[string]ModuleFactory),
189 moduleGroups: make(map[string]*moduleGroup),
190 moduleInfo: make(map[Module]*moduleInfo),
191 singletonInfo: make(map[string]*singletonInfo),
192 moduleNinjaNames: make(map[string]*moduleGroup),
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700193 }
194}
195
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700196// A ModuleFactory function creates a new Module object. See the
197// Context.RegisterModuleType method for details about how a registered
198// ModuleFactory is used by a Context.
199type ModuleFactory func() (m Module, propertyStructs []interface{})
200
Jamie Gennisd4e10182014-06-12 20:06:50 -0700201// RegisterModuleType associates a module type name (which can appear in a
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700202// Blueprints file) with a Module factory function. When the given module type
203// name is encountered in a Blueprints file during parsing, the Module factory
204// is invoked to instantiate a new Module object to handle the build action
Colin Crossc9028482014-12-18 16:28:54 -0800205// generation for the module. If a Mutator splits a module into multiple variants,
206// the factory is invoked again to create a new Module for each variant.
Jamie Gennisd4e10182014-06-12 20:06:50 -0700207//
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700208// The module type names given here must be unique for the context. The factory
209// function should be a named function so that its package and name can be
210// included in the generated Ninja file for debugging purposes.
211//
212// The factory function returns two values. The first is the newly created
213// Module object. The second is a slice of pointers to that Module object's
214// properties structs. Each properties struct is examined when parsing a module
215// definition of this type in a Blueprints file. Exported fields of the
216// properties structs are automatically set to the property values specified in
217// the Blueprints file. The properties struct field names determine the name of
218// the Blueprints file properties that are used - the Blueprints property name
219// matches that of the properties struct field name with the first letter
220// converted to lower-case.
221//
222// The fields of the properties struct must be either []string, a string, or
223// bool. The Context will panic if a Module gets instantiated with a properties
224// struct containing a field that is not one these supported types.
225//
226// Any properties that appear in the Blueprints files that are not built-in
227// module properties (such as "name" and "deps") and do not have a corresponding
228// field in the returned module properties struct result in an error during the
229// Context's parse phase.
230//
231// As an example, the follow code:
232//
233// type myModule struct {
234// properties struct {
235// Foo string
236// Bar []string
237// }
238// }
239//
240// func NewMyModule() (blueprint.Module, []interface{}) {
241// module := new(myModule)
242// properties := &module.properties
243// return module, []interface{}{properties}
244// }
245//
246// func main() {
247// ctx := blueprint.NewContext()
248// ctx.RegisterModuleType("my_module", NewMyModule)
249// // ...
250// }
251//
252// would support parsing a module defined in a Blueprints file as follows:
253//
254// my_module {
255// name: "myName",
256// foo: "my foo string",
257// bar: ["my", "bar", "strings"],
258// }
259//
Colin Cross7ad621c2015-01-07 16:22:45 -0800260// The factory function may be called from multiple goroutines. Any accesses
261// to global variables must be synchronized.
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700262func (c *Context) RegisterModuleType(name string, factory ModuleFactory) {
263 if _, present := c.moduleFactories[name]; present {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700264 panic(errors.New("module type name is already registered"))
265 }
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700266 c.moduleFactories[name] = factory
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700267}
268
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700269// A SingletonFactory function creates a new Singleton object. See the
270// Context.RegisterSingletonType method for details about how a registered
271// SingletonFactory is used by a Context.
272type SingletonFactory func() Singleton
273
274// RegisterSingletonType registers a singleton type that will be invoked to
275// generate build actions. Each registered singleton type is instantiated and
276// and invoked exactly once as part of the generate phase.
277//
278// The singleton type names given here must be unique for the context. The
279// factory function should be a named function so that its package and name can
280// be included in the generated Ninja file for debugging purposes.
281func (c *Context) RegisterSingletonType(name string, factory SingletonFactory) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700282 if _, present := c.singletonInfo[name]; present {
283 panic(errors.New("singleton name is already registered"))
284 }
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700285
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700286 c.singletonInfo[name] = &singletonInfo{
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700287 factory: factory,
288 singleton: factory(),
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700289 }
290}
291
292func singletonPkgPath(singleton Singleton) string {
293 typ := reflect.TypeOf(singleton)
294 for typ.Kind() == reflect.Ptr {
295 typ = typ.Elem()
296 }
297 return typ.PkgPath()
298}
299
300func singletonTypeName(singleton Singleton) string {
301 typ := reflect.TypeOf(singleton)
302 for typ.Kind() == reflect.Ptr {
303 typ = typ.Elem()
304 }
305 return typ.PkgPath() + "." + typ.Name()
306}
307
Colin Crossc9028482014-12-18 16:28:54 -0800308// RegisterTopDownMutator registers a mutator that will be invoked to propagate
309// dependency info top-down between Modules. Each registered mutator
310// is invoked once per Module, and is invoked on a module before being invoked
311// on any of its dependencies
312//
313// The mutator type names given here must be unique for the context.
314func (c *Context) RegisterTopDownMutator(name string, mutator TopDownMutator) {
315 for _, m := range c.mutatorInfo {
316 if m.name == name && m.topDownMutator != nil {
317 panic(fmt.Errorf("mutator name %s is already registered", name))
318 }
319 }
320
321 c.mutatorInfo = append(c.mutatorInfo, &mutatorInfo{
322 topDownMutator: mutator,
Colin Crossc0dbc552015-01-02 15:19:28 -0800323 name: name,
Colin Crossc9028482014-12-18 16:28:54 -0800324 })
325}
326
327// RegisterBottomUpMutator registers a mutator that will be invoked to split
328// Modules into variants. Each registered mutator is invoked once per Module,
329// and is invoked on dependencies before being invoked on dependers.
330//
331// The mutator type names given here must be unique for the context.
332func (c *Context) RegisterBottomUpMutator(name string, mutator BottomUpMutator) {
333 for _, m := range c.mutatorInfo {
334 if m.name == name && m.bottomUpMutator != nil {
335 panic(fmt.Errorf("mutator name %s is already registered", name))
336 }
337 }
338
339 c.mutatorInfo = append(c.mutatorInfo, &mutatorInfo{
340 bottomUpMutator: mutator,
Colin Crossc0dbc552015-01-02 15:19:28 -0800341 name: name,
Colin Crossc9028482014-12-18 16:28:54 -0800342 })
343}
344
Jamie Gennisd4e10182014-06-12 20:06:50 -0700345// SetIgnoreUnknownModuleTypes sets the behavior of the context in the case
346// where it encounters an unknown module type while parsing Blueprints files. By
347// default, the context will report unknown module types as an error. If this
348// method is called with ignoreUnknownModuleTypes set to true then the context
349// will silently ignore unknown module types.
350//
351// This method should generally not be used. It exists to facilitate the
352// bootstrapping process.
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700353func (c *Context) SetIgnoreUnknownModuleTypes(ignoreUnknownModuleTypes bool) {
354 c.ignoreUnknownModuleTypes = ignoreUnknownModuleTypes
355}
356
Jamie Gennisd4e10182014-06-12 20:06:50 -0700357// Parse parses a single Blueprints file from r, creating Module objects for
358// each of the module definitions encountered. If the Blueprints file contains
359// an assignment to the "subdirs" variable, then the subdirectories listed are
360// returned in the subdirs first return value.
361//
362// rootDir specifies the path to the root directory of the source tree, while
363// filename specifies the path to the Blueprints file. These paths are used for
364// error reporting and for determining the module's directory.
Colin Cross7ad621c2015-01-07 16:22:45 -0800365func (c *Context) parse(rootDir, filename string, r io.Reader,
366 scope *parser.Scope) (subdirs []string, modules []*moduleInfo, errs []error,
367 outScope *parser.Scope) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700368
Jamie Gennisec701282014-06-12 20:06:31 -0700369 relBlueprintsFile, err := filepath.Rel(rootDir, filename)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700370 if err != nil {
Colin Cross7ad621c2015-01-07 16:22:45 -0800371 return nil, nil, []error{err}, nil
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700372 }
373
Colin Crossc0dbc552015-01-02 15:19:28 -0800374 scope = parser.NewScope(scope)
375 scope.Remove("subdirs")
Colin Crossd1facc12015-01-08 14:56:03 -0800376 file, errs := parser.Parse(filename, r, scope)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700377 if len(errs) > 0 {
378 for i, err := range errs {
379 if parseErr, ok := err.(*parser.ParseError); ok {
380 err = &Error{
381 Err: parseErr.Err,
382 Pos: parseErr.Pos,
383 }
384 errs[i] = err
385 }
386 }
387
388 // If there were any parse errors don't bother trying to interpret the
389 // result.
Colin Cross7ad621c2015-01-07 16:22:45 -0800390 return nil, nil, errs, nil
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700391 }
392
Colin Crossd1facc12015-01-08 14:56:03 -0800393 for _, def := range file.Defs {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700394 var newErrs []error
Colin Cross7ad621c2015-01-07 16:22:45 -0800395 var newModule *moduleInfo
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700396 switch def := def.(type) {
397 case *parser.Module:
Colin Cross7ad621c2015-01-07 16:22:45 -0800398 newModule, newErrs = c.processModuleDef(def, relBlueprintsFile)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700399
400 case *parser.Assignment:
Colin Crossc0dbc552015-01-02 15:19:28 -0800401 // Already handled via Scope object
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700402 default:
403 panic("unknown definition type")
404 }
405
406 if len(newErrs) > 0 {
407 errs = append(errs, newErrs...)
408 if len(errs) > maxErrors {
409 break
410 }
Colin Cross7ad621c2015-01-07 16:22:45 -0800411 } else if newModule != nil {
412 modules = append(modules, newModule)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700413 }
414 }
415
Colin Crossc0dbc552015-01-02 15:19:28 -0800416 subdirs, newErrs := c.processSubdirs(scope)
417 if len(newErrs) > 0 {
418 errs = append(errs, newErrs...)
419 }
420
Colin Cross7ad621c2015-01-07 16:22:45 -0800421 return subdirs, modules, errs, scope
Colin Crossc0dbc552015-01-02 15:19:28 -0800422}
423
Colin Cross7ad621c2015-01-07 16:22:45 -0800424type stringAndScope struct {
425 string
426 *parser.Scope
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700427}
428
Jamie Gennisd4e10182014-06-12 20:06:50 -0700429// ParseBlueprintsFiles parses a set of Blueprints files starting with the file
430// at rootFile. When it encounters a Blueprints file with a set of subdirs
431// listed it recursively parses any Blueprints files found in those
432// subdirectories.
433//
434// If no errors are encountered while parsing the files, the list of paths on
435// which the future output will depend is returned. This list will include both
436// Blueprints file paths as well as directory paths for cases where wildcard
437// subdirs are found.
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700438func (c *Context) ParseBlueprintsFiles(rootFile string) (deps []string,
439 errs []error) {
440
Colin Cross7ad621c2015-01-07 16:22:45 -0800441 c.dependenciesReady = false
442
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700443 rootDir := filepath.Dir(rootFile)
444
Colin Cross7ad621c2015-01-07 16:22:45 -0800445 blueprintsSet := make(map[string]bool)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700446
Colin Cross7ad621c2015-01-07 16:22:45 -0800447 // Channels to receive data back from parseBlueprintsFile goroutines
448 blueprintsCh := make(chan stringAndScope)
449 errsCh := make(chan []error)
450 modulesCh := make(chan []*moduleInfo)
451 depsCh := make(chan string)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700452
Colin Cross7ad621c2015-01-07 16:22:45 -0800453 // Channel to notify main loop that a parseBlueprintsFile goroutine has finished
454 doneCh := make(chan struct{})
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700455
Colin Cross7ad621c2015-01-07 16:22:45 -0800456 // Number of outstanding goroutines to wait for
457 count := 0
458
459 startParseBlueprintsFile := func(filename string, scope *parser.Scope) {
460 count++
461 go func() {
462 c.parseBlueprintsFile(filename, scope, rootDir,
463 errsCh, modulesCh, blueprintsCh, depsCh)
464 doneCh <- struct{}{}
465 }()
466 }
467
468 tooManyErrors := false
469
470 startParseBlueprintsFile(rootFile, nil)
471
472loop:
473 for {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700474 if len(errs) > maxErrors {
Colin Cross7ad621c2015-01-07 16:22:45 -0800475 tooManyErrors = true
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700476 }
477
Colin Cross7ad621c2015-01-07 16:22:45 -0800478 select {
479 case newErrs := <-errsCh:
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700480 errs = append(errs, newErrs...)
Colin Cross7ad621c2015-01-07 16:22:45 -0800481 case dep := <-depsCh:
482 deps = append(deps, dep)
483 case modules := <-modulesCh:
484 newErrs := c.addModules(modules)
485 errs = append(errs, newErrs...)
486 case blueprint := <-blueprintsCh:
487 if tooManyErrors {
488 continue
489 }
490 if blueprintsSet[blueprint.string] {
491 continue
492 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700493
Colin Cross7ad621c2015-01-07 16:22:45 -0800494 blueprintsSet[blueprint.string] = true
495 startParseBlueprintsFile(blueprint.string, blueprint.Scope)
496 case <-doneCh:
497 count--
498 if count == 0 {
499 break loop
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700500 }
501 }
502 }
503
Colin Cross7ad621c2015-01-07 16:22:45 -0800504 return
505}
506
507// parseBlueprintFile parses a single Blueprints file, returning any errors through
508// errsCh, any defined modules through modulesCh, any sub-Blueprints files through
509// blueprintsCh, and any dependencies on Blueprints files or directories through
510// depsCh.
511func (c *Context) parseBlueprintsFile(filename string, scope *parser.Scope, rootDir string,
512 errsCh chan<- []error, modulesCh chan<- []*moduleInfo, blueprintsCh chan<- stringAndScope,
513 depsCh chan<- string) {
514
515 dir := filepath.Dir(filename)
516
517 file, err := os.Open(filename)
518 if err != nil {
519 errsCh <- []error{err}
520 return
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700521 }
522
Colin Cross7ad621c2015-01-07 16:22:45 -0800523 subdirs, modules, errs, subScope := c.parse(rootDir, filename, file, scope)
524 if len(errs) > 0 {
525 errsCh <- errs
526 }
527
528 err = file.Close()
529 if err != nil {
530 errsCh <- []error{err}
531 }
532
533 modulesCh <- modules
534
535 for _, subdir := range subdirs {
536 subdir = filepath.Join(dir, subdir)
537
538 dirPart, filePart := filepath.Split(subdir)
539 dirPart = filepath.Clean(dirPart)
540
541 if filePart == "*" {
542 foundSubdirs, err := listSubdirs(dirPart)
543 if err != nil {
544 errsCh <- []error{err}
545 return
546 }
547
548 for _, foundSubdir := range foundSubdirs {
549 subBlueprints := filepath.Join(dirPart, foundSubdir,
550 "Blueprints")
551
552 _, err := os.Stat(subBlueprints)
553 if os.IsNotExist(err) {
554 // There is no Blueprints file in this subdirectory. We
555 // need to add the directory to the list of dependencies
556 // so that if someone adds a Blueprints file in the
557 // future we'll pick it up.
558 depsCh <- filepath.Dir(subBlueprints)
559 } else {
560 depsCh <- subBlueprints
561 blueprintsCh <- stringAndScope{
562 subBlueprints,
563 subScope,
564 }
565 }
566 }
567
568 // We now depend on the directory itself because if any new
569 // subdirectories get added or removed we need to rebuild the
570 // Ninja manifest.
571 depsCh <- dirPart
572 } else {
573 subBlueprints := filepath.Join(subdir, "Blueprints")
574 depsCh <- subBlueprints
575 blueprintsCh <- stringAndScope{
576 subBlueprints,
577 subScope,
578 }
579
580 }
581 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700582}
583
584func listSubdirs(dir string) ([]string, error) {
585 d, err := os.Open(dir)
586 if err != nil {
587 return nil, err
588 }
589 defer d.Close()
590
591 infos, err := d.Readdir(-1)
592 if err != nil {
593 return nil, err
594 }
595
596 var subdirs []string
597 for _, info := range infos {
Jamie Gennis0c35b2d2014-09-25 13:15:10 -0700598 isDotFile := strings.HasPrefix(info.Name(), ".")
599 if info.IsDir() && !isDotFile {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700600 subdirs = append(subdirs, info.Name())
601 }
602 }
603
604 return subdirs, nil
605}
606
Colin Crossc0dbc552015-01-02 15:19:28 -0800607func (c *Context) processSubdirs(
608 scope *parser.Scope) (subdirs []string, errs []error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700609
Colin Crossc0dbc552015-01-02 15:19:28 -0800610 if assignment, err := scope.Get("subdirs"); err == nil {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700611 switch assignment.Value.Type {
612 case parser.List:
613 subdirs = make([]string, 0, len(assignment.Value.ListValue))
614
615 for _, value := range assignment.Value.ListValue {
616 if value.Type != parser.String {
617 // The parser should not produce this.
618 panic("non-string value found in list")
619 }
620
621 dirPart, filePart := filepath.Split(value.StringValue)
622 if (filePart != "*" && strings.ContainsRune(filePart, '*')) ||
623 strings.ContainsRune(dirPart, '*') {
624
625 errs = append(errs, &Error{
626 Err: fmt.Errorf("subdirs may only wildcard whole " +
627 "directories"),
628 Pos: value.Pos,
629 })
630
631 continue
632 }
633
634 subdirs = append(subdirs, value.StringValue)
635 }
636
637 if len(errs) > 0 {
638 subdirs = nil
639 }
640
641 return
642
643 case parser.Bool, parser.String:
644 errs = []error{
645 &Error{
646 Err: fmt.Errorf("subdirs must be a list of strings"),
647 Pos: assignment.Pos,
648 },
649 }
650
651 return
652
653 default:
654 panic(fmt.Errorf("unknown value type: %d", assignment.Value.Type))
655 }
656 }
657
Colin Crossc0dbc552015-01-02 15:19:28 -0800658 return nil, nil
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700659}
660
Colin Crossc9028482014-12-18 16:28:54 -0800661func (c *Context) createVariants(origModule *moduleInfo, mutatorName string,
Colin Cross174ae052015-03-03 17:37:03 -0800662 variantNames []string) ([]*moduleInfo, []error) {
Colin Crossc9028482014-12-18 16:28:54 -0800663
664 newModules := []*moduleInfo{}
665 origVariantName := origModule.name
666 group := origModule.group
667
Colin Cross174ae052015-03-03 17:37:03 -0800668 var errs []error
669
Colin Crossc9028482014-12-18 16:28:54 -0800670 for i, variantName := range variantNames {
671 typeName := group.typeName
672 factory, ok := c.moduleFactories[typeName]
673 if !ok {
674 panic(fmt.Sprintf("unrecognized module type %q during cloning", typeName))
675 }
676
677 var newLogicModule Module
678 var newProperties []interface{}
679
680 if i == 0 {
681 // Reuse the existing module for the first new variant
682 newLogicModule = origModule.logicModule
683 newProperties = origModule.moduleProperties
684 } else {
685 props := []interface{}{
686 &group.properties,
687 }
688 newLogicModule, newProperties = factory()
689
690 newProperties = append(props, newProperties...)
691
692 if len(newProperties) != len(origModule.moduleProperties) {
693 panic("mismatched properties array length in " + group.properties.Name)
694 }
695
696 for i := range newProperties {
697 dst := reflect.ValueOf(newProperties[i]).Elem()
698 src := reflect.ValueOf(origModule.moduleProperties[i]).Elem()
699
700 proptools.CopyProperties(dst, src)
701 }
702 }
703
704 newVariantName := append([]subName(nil), origVariantName...)
705 newSubName := subName{
706 mutatorName: mutatorName,
707 variantName: variantName,
708 }
709 newVariantName = append(newVariantName, newSubName)
710
711 newModule := &moduleInfo{
712 group: group,
713 directDeps: append([]*moduleInfo(nil), origModule.directDeps...),
714 logicModule: newLogicModule,
715 name: newVariantName,
716 moduleProperties: newProperties,
717 }
718
719 newModules = append(newModules, newModule)
720 c.moduleInfo[newModule.logicModule] = newModule
721
Colin Cross174ae052015-03-03 17:37:03 -0800722 newErrs := c.convertDepsToVariant(newModule, newSubName)
723 if len(newErrs) > 0 {
724 errs = append(errs, newErrs...)
725 }
Colin Crossc9028482014-12-18 16:28:54 -0800726 }
727
728 // Mark original variant as invalid. Modules that depend on this module will still
729 // depend on origModule, but we'll fix it when the mutator is called on them.
730 origModule.logicModule = nil
731 origModule.splitModules = newModules
732
Colin Cross174ae052015-03-03 17:37:03 -0800733 return newModules, errs
Colin Crossc9028482014-12-18 16:28:54 -0800734}
735
Colin Cross174ae052015-03-03 17:37:03 -0800736func (c *Context) convertDepsToVariant(module *moduleInfo, newSubName subName) (errs []error) {
737
Colin Crossc9028482014-12-18 16:28:54 -0800738 for i, dep := range module.directDeps {
739 if dep.logicModule == nil {
740 var newDep *moduleInfo
741 for _, m := range dep.splitModules {
742 if len(m.name) > 0 && m.name[len(m.name)-1] == newSubName {
743 newDep = m
744 break
745 }
746 }
747 if newDep == nil {
Colin Cross174ae052015-03-03 17:37:03 -0800748 errs = append(errs, &Error{
749 Err: fmt.Errorf("failed to find variant %q for module %q needed by %q",
750 newSubName.variantName, dep.group.properties.Name,
751 module.group.properties.Name),
752 Pos: module.group.pos,
753 })
754 continue
Colin Crossc9028482014-12-18 16:28:54 -0800755 }
756 module.directDeps[i] = newDep
757 }
758 }
Colin Cross174ae052015-03-03 17:37:03 -0800759
760 return errs
Colin Crossc9028482014-12-18 16:28:54 -0800761}
762
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700763func (c *Context) processModuleDef(moduleDef *parser.Module,
Colin Cross7ad621c2015-01-07 16:22:45 -0800764 relBlueprintsFile string) (*moduleInfo, []error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700765
Colin Crossd1facc12015-01-08 14:56:03 -0800766 typeName := moduleDef.Type.Name
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700767 factory, ok := c.moduleFactories[typeName]
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700768 if !ok {
769 if c.ignoreUnknownModuleTypes {
Colin Cross7ad621c2015-01-07 16:22:45 -0800770 return nil, nil
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700771 }
772
Colin Cross7ad621c2015-01-07 16:22:45 -0800773 return nil, []error{
Jamie Gennisd4c53d82014-06-22 17:02:55 -0700774 &Error{
775 Err: fmt.Errorf("unrecognized module type %q", typeName),
Colin Crossd1facc12015-01-08 14:56:03 -0800776 Pos: moduleDef.Type.Pos,
Jamie Gennisd4c53d82014-06-22 17:02:55 -0700777 },
778 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700779 }
780
Colin Crossbbfa51a2014-12-17 16:12:41 -0800781 logicModule, properties := factory()
782 group := &moduleGroup{
Jamie Gennisec701282014-06-12 20:06:31 -0700783 typeName: typeName,
Jamie Gennisec701282014-06-12 20:06:31 -0700784 relBlueprintsFile: relBlueprintsFile,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700785 }
786
Jamie Gennis87622922014-09-30 11:38:25 -0700787 props := []interface{}{
Colin Crossbbfa51a2014-12-17 16:12:41 -0800788 &group.properties,
Jamie Gennis87622922014-09-30 11:38:25 -0700789 }
790 properties = append(props, properties...)
Jamie Gennisd4c53d82014-06-22 17:02:55 -0700791
Jamie Gennis87622922014-09-30 11:38:25 -0700792 propertyMap, errs := unpackProperties(moduleDef.Properties, properties...)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700793 if len(errs) > 0 {
Colin Cross7ad621c2015-01-07 16:22:45 -0800794 return nil, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700795 }
796
Colin Cross6134a5c2015-02-10 11:26:26 -0800797 ninjaName := toNinjaName(group.properties.Name)
798
799 // The sanitizing in toNinjaName can result in collisions, uniquify the name if it
800 // already exists
801 for i := 0; c.moduleNinjaNames[ninjaName] != nil; i++ {
802 ninjaName = toNinjaName(group.properties.Name) + strconv.Itoa(i)
803 }
804
805 c.moduleNinjaNames[ninjaName] = group
806 group.ninjaName = ninjaName
807
Colin Crossd1facc12015-01-08 14:56:03 -0800808 group.pos = moduleDef.Type.Pos
Colin Crossbbfa51a2014-12-17 16:12:41 -0800809 group.propertyPos = make(map[string]scanner.Position)
Jamie Gennis87622922014-09-30 11:38:25 -0700810 for name, propertyDef := range propertyMap {
Colin Crossbbfa51a2014-12-17 16:12:41 -0800811 group.propertyPos[name] = propertyDef.Pos
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700812 }
813
Colin Crossbbfa51a2014-12-17 16:12:41 -0800814 module := &moduleInfo{
Colin Crossc9028482014-12-18 16:28:54 -0800815 group: group,
816 logicModule: logicModule,
817 moduleProperties: properties,
Colin Crossbbfa51a2014-12-17 16:12:41 -0800818 }
Colin Crossbbfa51a2014-12-17 16:12:41 -0800819 group.modules = []*moduleInfo{module}
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700820
Colin Cross7ad621c2015-01-07 16:22:45 -0800821 return module, nil
822}
823
824func (c *Context) addModules(modules []*moduleInfo) (errs []error) {
825 for _, module := range modules {
826 name := module.group.properties.Name
827 if first, present := c.moduleGroups[name]; present {
828 errs = append(errs, []error{
829 &Error{
830 Err: fmt.Errorf("module %q already defined", name),
831 Pos: module.group.pos,
832 },
833 &Error{
834 Err: fmt.Errorf("<-- previous definition here"),
835 Pos: first.pos,
836 },
837 }...)
838 continue
839 }
840
841 c.moduleGroups[name] = module.group
842 c.moduleInfo[module.logicModule] = module
843 }
844
845 return errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700846}
847
Jamie Gennisd4e10182014-06-12 20:06:50 -0700848// ResolveDependencies checks that the dependencies specified by all of the
849// modules defined in the parsed Blueprints files are valid. This means that
850// the modules depended upon are defined and that no circular dependencies
851// exist.
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700852//
853// The config argument is made available to all of the DynamicDependerModule
854// objects via the Config method on the DynamicDependerModuleContext objects
855// passed to their DynamicDependencies method.
856func (c *Context) ResolveDependencies(config interface{}) []error {
857 errs := c.resolveDependencies(config)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700858 if len(errs) > 0 {
859 return errs
860 }
861
Colin Cross691a60d2015-01-07 18:08:56 -0800862 errs = c.updateDependencies()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700863 if len(errs) > 0 {
864 return errs
865 }
866
867 c.dependenciesReady = true
868 return nil
869}
870
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700871// moduleDepNames returns the sorted list of dependency names for a given
872// module. If the module implements the DynamicDependerModule interface then
873// this set consists of the union of those module names listed in its "deps"
874// property and those returned by its DynamicDependencies method. Otherwise it
875// is simply those names listed in its "deps" property.
Colin Crossbbfa51a2014-12-17 16:12:41 -0800876func (c *Context) moduleDepNames(group *moduleGroup,
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700877 config interface{}) ([]string, []error) {
878
879 depNamesSet := make(map[string]bool)
Colin Crossa434b3f2015-01-13 10:59:52 -0800880 depNames := []string{}
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700881
Colin Crossbbfa51a2014-12-17 16:12:41 -0800882 for _, depName := range group.properties.Deps {
Colin Crossa434b3f2015-01-13 10:59:52 -0800883 if !depNamesSet[depName] {
884 depNamesSet[depName] = true
885 depNames = append(depNames, depName)
886 }
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700887 }
888
Colin Crossbbfa51a2014-12-17 16:12:41 -0800889 if len(group.modules) != 1 {
890 panic("expected a single module during moduleDepNames")
891 }
892 logicModule := group.modules[0].logicModule
893 dynamicDepender, ok := logicModule.(DynamicDependerModule)
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700894 if ok {
Colin Crossbe1a9a12014-12-18 11:05:45 -0800895 ddmctx := &baseModuleContext{
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700896 context: c,
897 config: config,
Colin Crossbbfa51a2014-12-17 16:12:41 -0800898 group: group,
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700899 }
900
901 dynamicDeps := dynamicDepender.DynamicDependencies(ddmctx)
902
903 if len(ddmctx.errs) > 0 {
904 return nil, ddmctx.errs
905 }
906
907 for _, depName := range dynamicDeps {
Colin Crossa434b3f2015-01-13 10:59:52 -0800908 if !depNamesSet[depName] {
909 depNamesSet[depName] = true
910 depNames = append(depNames, depName)
911 }
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700912 }
913 }
914
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700915 return depNames, nil
916}
917
Colin Crossbbfa51a2014-12-17 16:12:41 -0800918// resolveDependencies populates the moduleGroup.modules[0].directDeps list for every
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700919// module. In doing so it checks for missing dependencies and self-dependant
920// modules.
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700921func (c *Context) resolveDependencies(config interface{}) (errs []error) {
Colin Crossbbfa51a2014-12-17 16:12:41 -0800922 for _, group := range c.moduleGroups {
923 depNames, newErrs := c.moduleDepNames(group, config)
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700924 if len(newErrs) > 0 {
925 errs = append(errs, newErrs...)
926 continue
927 }
928
Colin Crossbbfa51a2014-12-17 16:12:41 -0800929 if len(group.modules) != 1 {
930 panic("expected a single module in resolveDependencies")
931 }
932 group.modules[0].directDeps = make([]*moduleInfo, 0, len(depNames))
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700933
934 for _, depName := range depNames {
Colin Crossc9028482014-12-18 16:28:54 -0800935 newErrs := c.addDependency(group.modules[0], depName)
936 if len(newErrs) > 0 {
937 errs = append(errs, newErrs...)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700938 continue
939 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700940 }
941 }
942
943 return
944}
945
Colin Crossc9028482014-12-18 16:28:54 -0800946func (c *Context) addDependency(module *moduleInfo, depName string) []error {
947 depsPos := module.group.propertyPos["deps"]
948
949 if depName == module.group.properties.Name {
950 return []error{&Error{
951 Err: fmt.Errorf("%q depends on itself", depName),
952 Pos: depsPos,
953 }}
954 }
955
956 depInfo, ok := c.moduleGroups[depName]
957 if !ok {
958 return []error{&Error{
959 Err: fmt.Errorf("%q depends on undefined module %q",
960 module.group.properties.Name, depName),
961 Pos: depsPos,
962 }}
963 }
964
965 if len(depInfo.modules) != 1 {
966 panic(fmt.Sprintf("cannot add dependency from %s to %s, it already has multiple variants",
967 module.group.properties.Name, depInfo.properties.Name))
968 }
969
970 module.directDeps = append(module.directDeps, depInfo.modules[0])
971
972 return nil
973}
974
Colin Cross8900e9b2015-03-02 14:03:01 -0800975func (c *Context) parallelVisitAllBottomUp(visit func(group *moduleGroup) bool) {
Colin Cross691a60d2015-01-07 18:08:56 -0800976 doneCh := make(chan *moduleGroup)
977 count := 0
Colin Cross8900e9b2015-03-02 14:03:01 -0800978 cancel := false
Colin Cross691a60d2015-01-07 18:08:56 -0800979
980 for _, group := range c.moduleGroupsSorted {
981 group.waitingCount = group.depsCount
982 }
983
984 visitOne := func(group *moduleGroup) {
985 count++
986 go func() {
Colin Cross8900e9b2015-03-02 14:03:01 -0800987 ret := visit(group)
988 if ret {
989 cancel = true
990 }
Colin Cross691a60d2015-01-07 18:08:56 -0800991 doneCh <- group
992 }()
993 }
994
995 for _, group := range c.moduleGroupsSorted {
996 if group.waitingCount == 0 {
997 visitOne(group)
998 }
999 }
1000
Colin Cross11e3b0d2015-02-04 10:41:00 -08001001 for count > 0 {
Colin Cross691a60d2015-01-07 18:08:56 -08001002 select {
1003 case doneGroup := <-doneCh:
Colin Cross8900e9b2015-03-02 14:03:01 -08001004 if !cancel {
1005 for _, parent := range doneGroup.reverseDeps {
1006 parent.waitingCount--
1007 if parent.waitingCount == 0 {
1008 visitOne(parent)
1009 }
Colin Cross691a60d2015-01-07 18:08:56 -08001010 }
1011 }
1012 count--
Colin Cross691a60d2015-01-07 18:08:56 -08001013 }
1014 }
1015}
1016
1017// updateDependencies recursively walks the module dependency graph and updates
1018// additional fields based on the dependencies. It builds a sorted list of modules
1019// such that dependencies of a module always appear first, and populates reverse
1020// dependency links and counts of total dependencies. It also reports errors when
1021// it encounters dependency cycles. This should called after resolveDependencies,
1022// as well as after any mutator pass has called addDependency
1023func (c *Context) updateDependencies() (errs []error) {
Colin Crossbbfa51a2014-12-17 16:12:41 -08001024 visited := make(map[*moduleGroup]bool) // modules that were already checked
1025 checking := make(map[*moduleGroup]bool) // modules actively being checked
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001026
Colin Crossbbfa51a2014-12-17 16:12:41 -08001027 sorted := make([]*moduleGroup, 0, len(c.moduleGroups))
Colin Cross573a2fd2014-12-17 14:16:51 -08001028
Colin Crossbbfa51a2014-12-17 16:12:41 -08001029 var check func(group *moduleGroup) []*moduleGroup
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001030
Colin Cross10b54db2015-03-11 14:40:30 -07001031 cycleError := func(cycle []*moduleGroup) {
1032 // We are the "start" of the cycle, so we're responsible
1033 // for generating the errors. The cycle list is in
1034 // reverse order because all the 'check' calls append
1035 // their own module to the list.
1036 errs = append(errs, &Error{
1037 Err: fmt.Errorf("encountered dependency cycle:"),
1038 Pos: cycle[len(cycle)-1].pos,
1039 })
1040
1041 // Iterate backwards through the cycle list.
1042 curGroup := cycle[len(cycle)-1]
1043 for i := len(cycle) - 1; i >= 0; i-- {
1044 nextGroup := cycle[i]
1045 errs = append(errs, &Error{
1046 Err: fmt.Errorf(" %q depends on %q",
1047 curGroup.properties.Name,
1048 nextGroup.properties.Name),
1049 Pos: curGroup.propertyPos["deps"],
1050 })
1051 curGroup = nextGroup
1052 }
1053 }
1054
Colin Crossbbfa51a2014-12-17 16:12:41 -08001055 check = func(group *moduleGroup) []*moduleGroup {
1056 visited[group] = true
1057 checking[group] = true
1058 defer delete(checking, group)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001059
Colin Crossbbfa51a2014-12-17 16:12:41 -08001060 deps := make(map[*moduleGroup]bool)
1061 for _, module := range group.modules {
1062 for _, dep := range module.directDeps {
1063 deps[dep.group] = true
1064 }
1065 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001066
Colin Cross691a60d2015-01-07 18:08:56 -08001067 group.reverseDeps = []*moduleGroup{}
1068 group.depsCount = len(deps)
1069
Colin Crossbbfa51a2014-12-17 16:12:41 -08001070 for dep := range deps {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001071 if checking[dep] {
1072 // This is a cycle.
Colin Crossbbfa51a2014-12-17 16:12:41 -08001073 return []*moduleGroup{dep, group}
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001074 }
1075
1076 if !visited[dep] {
1077 cycle := check(dep)
1078 if cycle != nil {
Colin Crossbbfa51a2014-12-17 16:12:41 -08001079 if cycle[0] == group {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001080 // We are the "start" of the cycle, so we're responsible
1081 // for generating the errors. The cycle list is in
1082 // reverse order because all the 'check' calls append
1083 // their own module to the list.
Colin Cross10b54db2015-03-11 14:40:30 -07001084 cycleError(cycle)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001085
1086 // We can continue processing this module's children to
1087 // find more cycles. Since all the modules that were
1088 // part of the found cycle were marked as visited we
1089 // won't run into that cycle again.
1090 } else {
1091 // We're not the "start" of the cycle, so we just append
1092 // our module to the list and return it.
Colin Crossbbfa51a2014-12-17 16:12:41 -08001093 return append(cycle, group)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001094 }
1095 }
1096 }
Colin Cross691a60d2015-01-07 18:08:56 -08001097
1098 dep.reverseDeps = append(dep.reverseDeps, group)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001099 }
1100
Colin Crossbbfa51a2014-12-17 16:12:41 -08001101 sorted = append(sorted, group)
Colin Cross573a2fd2014-12-17 14:16:51 -08001102
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001103 return nil
1104 }
1105
Colin Crossbbfa51a2014-12-17 16:12:41 -08001106 for _, group := range c.moduleGroups {
1107 if !visited[group] {
1108 cycle := check(group)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001109 if cycle != nil {
Colin Cross10b54db2015-03-11 14:40:30 -07001110 if cycle[len(cycle)-1] != group {
1111 panic("inconceivable!")
1112 }
1113 cycleError(cycle)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001114 }
1115 }
1116 }
1117
Colin Crossbbfa51a2014-12-17 16:12:41 -08001118 c.moduleGroupsSorted = sorted
Colin Cross573a2fd2014-12-17 14:16:51 -08001119
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001120 return
1121}
1122
Jamie Gennisd4e10182014-06-12 20:06:50 -07001123// PrepareBuildActions generates an internal representation of all the build
1124// actions that need to be performed. This process involves invoking the
1125// GenerateBuildActions method on each of the Module objects created during the
1126// parse phase and then on each of the registered Singleton objects.
1127//
1128// If the ResolveDependencies method has not already been called it is called
1129// automatically by this method.
1130//
1131// The config argument is made available to all of the Module and Singleton
1132// objects via the Config method on the ModuleContext and SingletonContext
1133// objects passed to GenerateBuildActions. It is also passed to the functions
1134// specified via PoolFunc, RuleFunc, and VariableFunc so that they can compute
1135// config-specific values.
Mathias Agopian5b8477d2014-06-25 17:21:54 -07001136//
1137// The returned deps is a list of the ninja files dependencies that were added
1138// by the modules and singletons via the ModuleContext.AddNinjaFileDeps() and
1139// SingletonContext.AddNinjaFileDeps() methods.
1140func (c *Context) PrepareBuildActions(config interface{}) (deps []string, errs []error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001141 c.buildActionsReady = false
1142
1143 if !c.dependenciesReady {
Jamie Gennisb9e87f62014-09-24 20:28:11 -07001144 errs := c.ResolveDependencies(config)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001145 if len(errs) > 0 {
Mathias Agopian5b8477d2014-06-25 17:21:54 -07001146 return nil, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001147 }
1148 }
1149
Colin Crossc9028482014-12-18 16:28:54 -08001150 errs = c.runMutators(config)
1151 if len(errs) > 0 {
1152 return nil, errs
1153 }
1154
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001155 liveGlobals := newLiveTracker(config)
1156
1157 c.initSpecialVariables()
1158
Mathias Agopian5b8477d2014-06-25 17:21:54 -07001159 depsModules, errs := c.generateModuleBuildActions(config, liveGlobals)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001160 if len(errs) > 0 {
Mathias Agopian5b8477d2014-06-25 17:21:54 -07001161 return nil, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001162 }
1163
Mathias Agopian5b8477d2014-06-25 17:21:54 -07001164 depsSingletons, errs := c.generateSingletonBuildActions(config, liveGlobals)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001165 if len(errs) > 0 {
Mathias Agopian5b8477d2014-06-25 17:21:54 -07001166 return nil, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001167 }
1168
Mathias Agopian5b8477d2014-06-25 17:21:54 -07001169 deps = append(depsModules, depsSingletons...)
1170
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001171 if c.buildDir != nil {
1172 liveGlobals.addNinjaStringDeps(c.buildDir)
1173 }
1174
1175 pkgNames := c.makeUniquePackageNames(liveGlobals)
1176
1177 // This will panic if it finds a problem since it's a programming error.
1178 c.checkForVariableReferenceCycles(liveGlobals.variables, pkgNames)
1179
1180 c.pkgNames = pkgNames
1181 c.globalVariables = liveGlobals.variables
1182 c.globalPools = liveGlobals.pools
1183 c.globalRules = liveGlobals.rules
1184
1185 c.buildActionsReady = true
1186
Mathias Agopian5b8477d2014-06-25 17:21:54 -07001187 return deps, nil
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001188}
1189
Colin Crossc9028482014-12-18 16:28:54 -08001190func (c *Context) runMutators(config interface{}) (errs []error) {
1191 for _, mutator := range c.mutatorInfo {
1192 if mutator.topDownMutator != nil {
1193 errs = c.runTopDownMutator(config, mutator.name, mutator.topDownMutator)
1194 } else if mutator.bottomUpMutator != nil {
1195 errs = c.runBottomUpMutator(config, mutator.name, mutator.bottomUpMutator)
1196 } else {
1197 panic("no mutator set on " + mutator.name)
1198 }
1199 if len(errs) > 0 {
1200 return errs
1201 }
1202 }
1203
1204 return nil
1205}
1206
1207func (c *Context) runTopDownMutator(config interface{},
1208 name string, mutator TopDownMutator) (errs []error) {
1209
1210 for i := 0; i < len(c.moduleGroupsSorted); i++ {
1211 group := c.moduleGroupsSorted[len(c.moduleGroupsSorted)-1-i]
1212 for _, module := range group.modules {
1213 mctx := &mutatorContext{
1214 baseModuleContext: baseModuleContext{
1215 context: c,
1216 config: config,
1217 group: group,
1218 },
1219 module: module,
1220 name: name,
1221 }
1222
1223 mutator(mctx)
1224 if len(mctx.errs) > 0 {
1225 errs = append(errs, mctx.errs...)
1226 return errs
1227 }
1228 }
1229 }
1230
1231 return errs
1232}
1233
1234func (c *Context) runBottomUpMutator(config interface{},
1235 name string, mutator BottomUpMutator) (errs []error) {
1236
1237 dependenciesModified := false
1238
1239 for _, group := range c.moduleGroupsSorted {
1240 newModules := make([]*moduleInfo, 0, len(group.modules))
1241
1242 for _, module := range group.modules {
1243 mctx := &mutatorContext{
1244 baseModuleContext: baseModuleContext{
1245 context: c,
1246 config: config,
1247 group: group,
1248 },
1249 module: module,
1250 name: name,
1251 }
1252
1253 mutator(mctx)
1254 if len(mctx.errs) > 0 {
1255 errs = append(errs, mctx.errs...)
1256 return errs
1257 }
1258
1259 // Fix up any remaining dependencies on modules that were split into variants
1260 // by replacing them with the first variant
1261 for i, dep := range module.directDeps {
1262 if dep.logicModule == nil {
1263 module.directDeps[i] = dep.splitModules[0]
1264 }
1265 }
1266
1267 if mctx.dependenciesModified {
1268 dependenciesModified = true
1269 }
1270
1271 if module.splitModules != nil {
1272 newModules = append(newModules, module.splitModules...)
1273 } else {
1274 newModules = append(newModules, module)
1275 }
1276 }
1277
1278 group.modules = newModules
1279 }
1280
1281 if dependenciesModified {
Colin Cross691a60d2015-01-07 18:08:56 -08001282 errs = c.updateDependencies()
Colin Crossc9028482014-12-18 16:28:54 -08001283 if len(errs) > 0 {
1284 return errs
1285 }
1286 }
1287
1288 return errs
1289}
1290
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001291func (c *Context) initSpecialVariables() {
1292 c.buildDir = nil
1293 c.requiredNinjaMajor = 1
1294 c.requiredNinjaMinor = 1
1295 c.requiredNinjaMicro = 0
1296}
1297
Jamie Gennis6eb4d242014-06-11 18:31:16 -07001298func (c *Context) generateModuleBuildActions(config interface{},
Mathias Agopian5b8477d2014-06-25 17:21:54 -07001299 liveGlobals *liveTracker) ([]string, []error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001300
Mathias Agopian5b8477d2014-06-25 17:21:54 -07001301 var deps []string
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001302 var errs []error
1303
Colin Cross691a60d2015-01-07 18:08:56 -08001304 cancelCh := make(chan struct{})
1305 errsCh := make(chan []error)
1306 depsCh := make(chan []string)
1307
1308 go func() {
1309 for {
1310 select {
1311 case <-cancelCh:
1312 close(cancelCh)
1313 return
1314 case newErrs := <-errsCh:
1315 errs = append(errs, newErrs...)
1316 case newDeps := <-depsCh:
1317 deps = append(deps, newDeps...)
1318
1319 }
1320 }
1321 }()
1322
Colin Cross8900e9b2015-03-02 14:03:01 -08001323 c.parallelVisitAllBottomUp(func(group *moduleGroup) bool {
Colin Crossbbfa51a2014-12-17 16:12:41 -08001324 for _, module := range group.modules {
Colin Cross6134a5c2015-02-10 11:26:26 -08001325 // The parent scope of the moduleContext's local scope gets overridden to be that of the
1326 // calling Go package on a per-call basis. Since the initial parent scope doesn't matter we
1327 // just set it to nil.
1328 prefix := moduleNamespacePrefix(group.ninjaName + "_" + module.subName())
1329 scope := newLocalScope(nil, prefix)
1330
Colin Crossbbfa51a2014-12-17 16:12:41 -08001331 mctx := &moduleContext{
Colin Cross1455a0f2014-12-17 13:23:56 -08001332 baseModuleContext: baseModuleContext{
1333 context: c,
1334 config: config,
1335 group: group,
Colin Crossb2e7b5d2014-11-11 14:18:53 -08001336 },
Colin Cross1455a0f2014-12-17 13:23:56 -08001337 module: module,
1338 scope: scope,
Colin Crossbbfa51a2014-12-17 16:12:41 -08001339 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001340
Colin Crossbbfa51a2014-12-17 16:12:41 -08001341 mctx.module.logicModule.GenerateBuildActions(mctx)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001342
Colin Crossbbfa51a2014-12-17 16:12:41 -08001343 if len(mctx.errs) > 0 {
Colin Cross691a60d2015-01-07 18:08:56 -08001344 errsCh <- mctx.errs
Colin Cross8900e9b2015-03-02 14:03:01 -08001345 return true
Colin Crossbbfa51a2014-12-17 16:12:41 -08001346 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001347
Colin Cross691a60d2015-01-07 18:08:56 -08001348 depsCh <- mctx.ninjaFileDeps
Mathias Agopian5b8477d2014-06-25 17:21:54 -07001349
Colin Crossbbfa51a2014-12-17 16:12:41 -08001350 newErrs := c.processLocalBuildActions(&group.actionDefs,
1351 &mctx.actionDefs, liveGlobals)
1352 if len(newErrs) > 0 {
Colin Cross691a60d2015-01-07 18:08:56 -08001353 errsCh <- newErrs
Colin Cross8900e9b2015-03-02 14:03:01 -08001354 return true
Colin Crossbbfa51a2014-12-17 16:12:41 -08001355 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001356 }
Colin Cross8900e9b2015-03-02 14:03:01 -08001357 return false
Colin Cross691a60d2015-01-07 18:08:56 -08001358 })
1359
1360 cancelCh <- struct{}{}
1361 <-cancelCh
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001362
Mathias Agopian5b8477d2014-06-25 17:21:54 -07001363 return deps, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001364}
1365
Jamie Gennis6eb4d242014-06-11 18:31:16 -07001366func (c *Context) generateSingletonBuildActions(config interface{},
Mathias Agopian5b8477d2014-06-25 17:21:54 -07001367 liveGlobals *liveTracker) ([]string, []error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001368
Mathias Agopian5b8477d2014-06-25 17:21:54 -07001369 var deps []string
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001370 var errs []error
Mathias Agopian5b8477d2014-06-25 17:21:54 -07001371
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001372 for name, info := range c.singletonInfo {
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001373 // The parent scope of the singletonContext's local scope gets overridden to be that of the
1374 // calling Go package on a per-call basis. Since the initial parent scope doesn't matter we
1375 // just set it to nil.
1376 scope := newLocalScope(nil, singletonNamespacePrefix(name))
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001377
1378 sctx := &singletonContext{
1379 context: c,
1380 config: config,
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001381 scope: scope,
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001382 }
1383
1384 info.singleton.GenerateBuildActions(sctx)
1385
1386 if len(sctx.errs) > 0 {
1387 errs = append(errs, sctx.errs...)
1388 if len(errs) > maxErrors {
1389 break
1390 }
1391 continue
1392 }
1393
Mathias Agopian5b8477d2014-06-25 17:21:54 -07001394 deps = append(deps, sctx.ninjaFileDeps...)
1395
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001396 newErrs := c.processLocalBuildActions(&info.actionDefs,
1397 &sctx.actionDefs, liveGlobals)
1398 errs = append(errs, newErrs...)
1399 if len(errs) > maxErrors {
1400 break
1401 }
1402 }
1403
Mathias Agopian5b8477d2014-06-25 17:21:54 -07001404 return deps, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001405}
1406
1407func (c *Context) processLocalBuildActions(out, in *localBuildActions,
1408 liveGlobals *liveTracker) []error {
1409
1410 var errs []error
1411
1412 // First we go through and add everything referenced by the module's
1413 // buildDefs to the live globals set. This will end up adding the live
1414 // locals to the set as well, but we'll take them out after.
1415 for _, def := range in.buildDefs {
1416 err := liveGlobals.AddBuildDefDeps(def)
1417 if err != nil {
1418 errs = append(errs, err)
1419 }
1420 }
1421
1422 if len(errs) > 0 {
1423 return errs
1424 }
1425
Colin Crossc9028482014-12-18 16:28:54 -08001426 out.buildDefs = append(out.buildDefs, in.buildDefs...)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001427
1428 // We use the now-incorrect set of live "globals" to determine which local
1429 // definitions are live. As we go through copying those live locals to the
Colin Crossc9028482014-12-18 16:28:54 -08001430 // moduleGroup we remove them from the live globals set.
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001431 for _, v := range in.variables {
1432 _, isLive := liveGlobals.variables[v]
1433 if isLive {
1434 out.variables = append(out.variables, v)
1435 delete(liveGlobals.variables, v)
1436 }
1437 }
1438
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001439 for _, r := range in.rules {
1440 _, isLive := liveGlobals.rules[r]
1441 if isLive {
1442 out.rules = append(out.rules, r)
1443 delete(liveGlobals.rules, r)
1444 }
1445 }
1446
1447 return nil
1448}
1449
Colin Crossbbfa51a2014-12-17 16:12:41 -08001450func (c *Context) visitDepsDepthFirst(topModule *moduleInfo, visit func(Module)) {
1451 visited := make(map[*moduleInfo]bool)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001452
Colin Crossbbfa51a2014-12-17 16:12:41 -08001453 var walk func(module *moduleInfo)
1454 walk = func(module *moduleInfo) {
1455 visited[module] = true
1456 for _, moduleDep := range module.directDeps {
1457 if !visited[moduleDep] {
1458 walk(moduleDep)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001459 }
1460 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001461
Colin Crossbbfa51a2014-12-17 16:12:41 -08001462 if module != topModule {
1463 visit(module.logicModule)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001464 }
1465 }
Colin Crossbbfa51a2014-12-17 16:12:41 -08001466
1467 walk(topModule)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001468}
1469
Colin Crossbbfa51a2014-12-17 16:12:41 -08001470func (c *Context) visitDepsDepthFirstIf(topModule *moduleInfo, pred func(Module) bool,
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001471 visit func(Module)) {
1472
Colin Crossbbfa51a2014-12-17 16:12:41 -08001473 visited := make(map[*moduleInfo]bool)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001474
Colin Crossbbfa51a2014-12-17 16:12:41 -08001475 var walk func(module *moduleInfo)
1476 walk = func(module *moduleInfo) {
1477 visited[module] = true
1478 for _, moduleDep := range module.directDeps {
1479 if !visited[moduleDep] {
1480 walk(moduleDep)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001481 }
Jamie Gennis0bb5d8a2014-11-26 14:45:37 -08001482 }
Colin Crossbbfa51a2014-12-17 16:12:41 -08001483
1484 if module != topModule {
1485 if pred(module.logicModule) {
1486 visit(module.logicModule)
1487 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001488 }
1489 }
1490
Colin Crossbbfa51a2014-12-17 16:12:41 -08001491 walk(topModule)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001492}
1493
Colin Crossc9028482014-12-18 16:28:54 -08001494func (c *Context) visitDirectDeps(module *moduleInfo, visit func(Module)) {
1495 for _, dep := range module.directDeps {
1496 visit(dep.logicModule)
1497 }
1498}
1499
1500func (c *Context) visitDirectDepsIf(module *moduleInfo, pred func(Module) bool,
1501 visit func(Module)) {
1502
1503 for _, dep := range module.directDeps {
1504 if pred(dep.logicModule) {
1505 visit(dep.logicModule)
1506 }
1507 }
1508}
1509
Jamie Gennisc15544d2014-09-24 20:26:52 -07001510func (c *Context) sortedModuleNames() []string {
1511 if c.cachedSortedModuleNames == nil {
Colin Crossbbfa51a2014-12-17 16:12:41 -08001512 c.cachedSortedModuleNames = make([]string, 0, len(c.moduleGroups))
1513 for moduleName := range c.moduleGroups {
Jamie Gennisc15544d2014-09-24 20:26:52 -07001514 c.cachedSortedModuleNames = append(c.cachedSortedModuleNames,
1515 moduleName)
1516 }
1517 sort.Strings(c.cachedSortedModuleNames)
1518 }
1519
1520 return c.cachedSortedModuleNames
1521}
1522
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001523func (c *Context) visitAllModules(visit func(Module)) {
Jamie Gennisc15544d2014-09-24 20:26:52 -07001524 for _, moduleName := range c.sortedModuleNames() {
Colin Crossbbfa51a2014-12-17 16:12:41 -08001525 group := c.moduleGroups[moduleName]
1526 for _, module := range group.modules {
1527 visit(module.logicModule)
1528 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001529 }
1530}
1531
1532func (c *Context) visitAllModulesIf(pred func(Module) bool,
1533 visit func(Module)) {
1534
Jamie Gennisc15544d2014-09-24 20:26:52 -07001535 for _, moduleName := range c.sortedModuleNames() {
Colin Crossbbfa51a2014-12-17 16:12:41 -08001536 group := c.moduleGroups[moduleName]
1537 for _, module := range group.modules {
1538 if pred(module.logicModule) {
1539 visit(module.logicModule)
1540 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001541 }
1542 }
1543}
1544
1545func (c *Context) requireNinjaVersion(major, minor, micro int) {
1546 if major != 1 {
1547 panic("ninja version with major version != 1 not supported")
1548 }
1549 if c.requiredNinjaMinor < minor {
1550 c.requiredNinjaMinor = minor
1551 c.requiredNinjaMicro = micro
1552 }
1553 if c.requiredNinjaMinor == minor && c.requiredNinjaMicro < micro {
1554 c.requiredNinjaMicro = micro
1555 }
1556}
1557
1558func (c *Context) setBuildDir(value *ninjaString) {
1559 if c.buildDir != nil {
1560 panic("buildDir set multiple times")
1561 }
1562 c.buildDir = value
1563}
1564
1565func (c *Context) makeUniquePackageNames(
Jamie Gennis2fb20952014-10-03 02:49:58 -07001566 liveGlobals *liveTracker) map[*PackageContext]string {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001567
Jamie Gennis2fb20952014-10-03 02:49:58 -07001568 pkgs := make(map[string]*PackageContext)
1569 pkgNames := make(map[*PackageContext]string)
1570 longPkgNames := make(map[*PackageContext]bool)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001571
Jamie Gennis2fb20952014-10-03 02:49:58 -07001572 processPackage := func(pctx *PackageContext) {
1573 if pctx == nil {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001574 // This is a built-in rule and has no package.
1575 return
1576 }
Jamie Gennis2fb20952014-10-03 02:49:58 -07001577 if _, ok := pkgNames[pctx]; ok {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001578 // We've already processed this package.
1579 return
1580 }
1581
Jamie Gennis2fb20952014-10-03 02:49:58 -07001582 otherPkg, present := pkgs[pctx.shortName]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001583 if present {
1584 // Short name collision. Both this package and the one that's
1585 // already there need to use their full names. We leave the short
1586 // name in pkgNames for now so future collisions still get caught.
Jamie Gennis2fb20952014-10-03 02:49:58 -07001587 longPkgNames[pctx] = true
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001588 longPkgNames[otherPkg] = true
1589 } else {
1590 // No collision so far. Tentatively set the package's name to be
1591 // its short name.
Jamie Gennis2fb20952014-10-03 02:49:58 -07001592 pkgNames[pctx] = pctx.shortName
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001593 }
1594 }
1595
1596 // We try to give all packages their short name, but when we get collisions
1597 // we need to use the full unique package name.
1598 for v, _ := range liveGlobals.variables {
Jamie Gennis2fb20952014-10-03 02:49:58 -07001599 processPackage(v.packageContext())
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001600 }
1601 for p, _ := range liveGlobals.pools {
Jamie Gennis2fb20952014-10-03 02:49:58 -07001602 processPackage(p.packageContext())
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001603 }
1604 for r, _ := range liveGlobals.rules {
Jamie Gennis2fb20952014-10-03 02:49:58 -07001605 processPackage(r.packageContext())
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001606 }
1607
1608 // Add the packages that had collisions using their full unique names. This
1609 // will overwrite any short names that were added in the previous step.
Jamie Gennis2fb20952014-10-03 02:49:58 -07001610 for pctx := range longPkgNames {
1611 pkgNames[pctx] = pctx.fullName
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001612 }
1613
1614 return pkgNames
1615}
1616
1617func (c *Context) checkForVariableReferenceCycles(
Jamie Gennis2fb20952014-10-03 02:49:58 -07001618 variables map[Variable]*ninjaString, pkgNames map[*PackageContext]string) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001619
1620 visited := make(map[Variable]bool) // variables that were already checked
1621 checking := make(map[Variable]bool) // variables actively being checked
1622
1623 var check func(v Variable) []Variable
1624
1625 check = func(v Variable) []Variable {
1626 visited[v] = true
1627 checking[v] = true
1628 defer delete(checking, v)
1629
1630 value := variables[v]
1631 for _, dep := range value.variables {
1632 if checking[dep] {
1633 // This is a cycle.
1634 return []Variable{dep, v}
1635 }
1636
1637 if !visited[dep] {
1638 cycle := check(dep)
1639 if cycle != nil {
1640 if cycle[0] == v {
1641 // We are the "start" of the cycle, so we're responsible
1642 // for generating the errors. The cycle list is in
1643 // reverse order because all the 'check' calls append
1644 // their own module to the list.
1645 msgs := []string{"detected variable reference cycle:"}
1646
1647 // Iterate backwards through the cycle list.
1648 curName := v.fullName(pkgNames)
1649 curValue := value.Value(pkgNames)
1650 for i := len(cycle) - 1; i >= 0; i-- {
1651 next := cycle[i]
1652 nextName := next.fullName(pkgNames)
1653 nextValue := variables[next].Value(pkgNames)
1654
1655 msgs = append(msgs, fmt.Sprintf(
1656 " %q depends on %q", curName, nextName))
1657 msgs = append(msgs, fmt.Sprintf(
1658 " [%s = %s]", curName, curValue))
1659
1660 curName = nextName
1661 curValue = nextValue
1662 }
1663
1664 // Variable reference cycles are a programming error,
1665 // not the fault of the Blueprint file authors.
1666 panic(strings.Join(msgs, "\n"))
1667 } else {
1668 // We're not the "start" of the cycle, so we just append
1669 // our module to the list and return it.
1670 return append(cycle, v)
1671 }
1672 }
1673 }
1674 }
1675
1676 return nil
1677 }
1678
1679 for v := range variables {
1680 if !visited[v] {
1681 cycle := check(v)
1682 if cycle != nil {
1683 panic("inconceivable!")
1684 }
1685 }
1686 }
1687}
1688
Jamie Gennisaf435562014-10-27 22:34:56 -07001689// AllTargets returns a map all the build target names to the rule used to build
1690// them. This is the same information that is output by running 'ninja -t
1691// targets all'. If this is called before PrepareBuildActions successfully
1692// completes then ErrbuildActionsNotReady is returned.
1693func (c *Context) AllTargets() (map[string]string, error) {
1694 if !c.buildActionsReady {
1695 return nil, ErrBuildActionsNotReady
1696 }
1697
1698 targets := map[string]string{}
1699
1700 // Collect all the module build targets.
Colin Crossbbfa51a2014-12-17 16:12:41 -08001701 for _, info := range c.moduleGroups {
Jamie Gennisaf435562014-10-27 22:34:56 -07001702 for _, buildDef := range info.actionDefs.buildDefs {
1703 ruleName := buildDef.Rule.fullName(c.pkgNames)
1704 for _, output := range buildDef.Outputs {
Christian Zander6e2b2322014-11-21 15:12:08 -08001705 outputValue, err := output.Eval(c.globalVariables)
1706 if err != nil {
1707 return nil, err
1708 }
Jamie Gennisaf435562014-10-27 22:34:56 -07001709 targets[outputValue] = ruleName
1710 }
1711 }
1712 }
1713
1714 // Collect all the singleton build targets.
1715 for _, info := range c.singletonInfo {
1716 for _, buildDef := range info.actionDefs.buildDefs {
1717 ruleName := buildDef.Rule.fullName(c.pkgNames)
1718 for _, output := range buildDef.Outputs {
Christian Zander6e2b2322014-11-21 15:12:08 -08001719 outputValue, err := output.Eval(c.globalVariables)
1720 if err != nil {
Colin Crossfea2b752014-12-30 16:05:02 -08001721 return nil, err
Christian Zander6e2b2322014-11-21 15:12:08 -08001722 }
Jamie Gennisaf435562014-10-27 22:34:56 -07001723 targets[outputValue] = ruleName
1724 }
1725 }
1726 }
1727
1728 return targets, nil
1729}
1730
Jamie Gennisd4e10182014-06-12 20:06:50 -07001731// WriteBuildFile writes the Ninja manifeset text for the generated build
1732// actions to w. If this is called before PrepareBuildActions successfully
1733// completes then ErrBuildActionsNotReady is returned.
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001734func (c *Context) WriteBuildFile(w io.Writer) error {
1735 if !c.buildActionsReady {
1736 return ErrBuildActionsNotReady
1737 }
1738
1739 nw := newNinjaWriter(w)
1740
1741 err := c.writeBuildFileHeader(nw)
1742 if err != nil {
1743 return err
1744 }
1745
1746 err = c.writeNinjaRequiredVersion(nw)
1747 if err != nil {
1748 return err
1749 }
1750
1751 // TODO: Group the globals by package.
1752
1753 err = c.writeGlobalVariables(nw)
1754 if err != nil {
1755 return err
1756 }
1757
1758 err = c.writeGlobalPools(nw)
1759 if err != nil {
1760 return err
1761 }
1762
1763 err = c.writeBuildDir(nw)
1764 if err != nil {
1765 return err
1766 }
1767
1768 err = c.writeGlobalRules(nw)
1769 if err != nil {
1770 return err
1771 }
1772
1773 err = c.writeAllModuleActions(nw)
1774 if err != nil {
1775 return err
1776 }
1777
1778 err = c.writeAllSingletonActions(nw)
1779 if err != nil {
1780 return err
1781 }
1782
1783 return nil
1784}
1785
Jamie Gennisc15544d2014-09-24 20:26:52 -07001786type pkgAssociation struct {
1787 PkgName string
1788 PkgPath string
1789}
1790
1791type pkgAssociationSorter struct {
1792 pkgs []pkgAssociation
1793}
1794
1795func (s *pkgAssociationSorter) Len() int {
1796 return len(s.pkgs)
1797}
1798
1799func (s *pkgAssociationSorter) Less(i, j int) bool {
1800 iName := s.pkgs[i].PkgName
1801 jName := s.pkgs[j].PkgName
1802 return iName < jName
1803}
1804
1805func (s *pkgAssociationSorter) Swap(i, j int) {
1806 s.pkgs[i], s.pkgs[j] = s.pkgs[j], s.pkgs[i]
1807}
1808
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001809func (c *Context) writeBuildFileHeader(nw *ninjaWriter) error {
1810 headerTemplate := template.New("fileHeader")
1811 _, err := headerTemplate.Parse(fileHeaderTemplate)
1812 if err != nil {
1813 // This is a programming error.
1814 panic(err)
1815 }
1816
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001817 var pkgs []pkgAssociation
1818 maxNameLen := 0
1819 for pkg, name := range c.pkgNames {
1820 pkgs = append(pkgs, pkgAssociation{
1821 PkgName: name,
1822 PkgPath: pkg.pkgPath,
1823 })
1824 if len(name) > maxNameLen {
1825 maxNameLen = len(name)
1826 }
1827 }
1828
1829 for i := range pkgs {
1830 pkgs[i].PkgName += strings.Repeat(" ", maxNameLen-len(pkgs[i].PkgName))
1831 }
1832
Jamie Gennisc15544d2014-09-24 20:26:52 -07001833 sort.Sort(&pkgAssociationSorter{pkgs})
1834
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001835 params := map[string]interface{}{
1836 "Pkgs": pkgs,
1837 }
1838
1839 buf := bytes.NewBuffer(nil)
1840 err = headerTemplate.Execute(buf, params)
1841 if err != nil {
1842 return err
1843 }
1844
1845 return nw.Comment(buf.String())
1846}
1847
1848func (c *Context) writeNinjaRequiredVersion(nw *ninjaWriter) error {
1849 value := fmt.Sprintf("%d.%d.%d", c.requiredNinjaMajor, c.requiredNinjaMinor,
1850 c.requiredNinjaMicro)
1851
1852 err := nw.Assign("ninja_required_version", value)
1853 if err != nil {
1854 return err
1855 }
1856
1857 return nw.BlankLine()
1858}
1859
1860func (c *Context) writeBuildDir(nw *ninjaWriter) error {
1861 if c.buildDir != nil {
1862 err := nw.Assign("builddir", c.buildDir.Value(c.pkgNames))
1863 if err != nil {
1864 return err
1865 }
1866
1867 err = nw.BlankLine()
1868 if err != nil {
1869 return err
1870 }
1871 }
1872 return nil
1873}
1874
Jamie Gennisc15544d2014-09-24 20:26:52 -07001875type globalEntity interface {
Jamie Gennis2fb20952014-10-03 02:49:58 -07001876 fullName(pkgNames map[*PackageContext]string) string
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001877}
1878
Jamie Gennisc15544d2014-09-24 20:26:52 -07001879type globalEntitySorter struct {
Jamie Gennis2fb20952014-10-03 02:49:58 -07001880 pkgNames map[*PackageContext]string
Jamie Gennisc15544d2014-09-24 20:26:52 -07001881 entities []globalEntity
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001882}
1883
Jamie Gennisc15544d2014-09-24 20:26:52 -07001884func (s *globalEntitySorter) Len() int {
1885 return len(s.entities)
1886}
1887
1888func (s *globalEntitySorter) Less(i, j int) bool {
1889 iName := s.entities[i].fullName(s.pkgNames)
1890 jName := s.entities[j].fullName(s.pkgNames)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001891 return iName < jName
1892}
1893
Jamie Gennisc15544d2014-09-24 20:26:52 -07001894func (s *globalEntitySorter) Swap(i, j int) {
1895 s.entities[i], s.entities[j] = s.entities[j], s.entities[i]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001896}
1897
1898func (c *Context) writeGlobalVariables(nw *ninjaWriter) error {
1899 visited := make(map[Variable]bool)
1900
1901 var walk func(v Variable) error
1902 walk = func(v Variable) error {
1903 visited[v] = true
1904
1905 // First visit variables on which this variable depends.
1906 value := c.globalVariables[v]
1907 for _, dep := range value.variables {
1908 if !visited[dep] {
1909 err := walk(dep)
1910 if err != nil {
1911 return err
1912 }
1913 }
1914 }
1915
1916 err := nw.Assign(v.fullName(c.pkgNames), value.Value(c.pkgNames))
1917 if err != nil {
1918 return err
1919 }
1920
1921 err = nw.BlankLine()
1922 if err != nil {
1923 return err
1924 }
1925
1926 return nil
1927 }
1928
Jamie Gennisc15544d2014-09-24 20:26:52 -07001929 globalVariables := make([]globalEntity, 0, len(c.globalVariables))
1930 for variable := range c.globalVariables {
1931 globalVariables = append(globalVariables, variable)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001932 }
1933
Jamie Gennisc15544d2014-09-24 20:26:52 -07001934 sort.Sort(&globalEntitySorter{c.pkgNames, globalVariables})
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001935
Jamie Gennisc15544d2014-09-24 20:26:52 -07001936 for _, entity := range globalVariables {
1937 v := entity.(Variable)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001938 if !visited[v] {
1939 err := walk(v)
1940 if err != nil {
1941 return nil
1942 }
1943 }
1944 }
1945
1946 return nil
1947}
1948
1949func (c *Context) writeGlobalPools(nw *ninjaWriter) error {
Jamie Gennisc15544d2014-09-24 20:26:52 -07001950 globalPools := make([]globalEntity, 0, len(c.globalPools))
1951 for pool := range c.globalPools {
1952 globalPools = append(globalPools, pool)
1953 }
1954
1955 sort.Sort(&globalEntitySorter{c.pkgNames, globalPools})
1956
1957 for _, entity := range globalPools {
1958 pool := entity.(Pool)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001959 name := pool.fullName(c.pkgNames)
Jamie Gennisc15544d2014-09-24 20:26:52 -07001960 def := c.globalPools[pool]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001961 err := def.WriteTo(nw, name)
1962 if err != nil {
1963 return err
1964 }
1965
1966 err = nw.BlankLine()
1967 if err != nil {
1968 return err
1969 }
1970 }
1971
1972 return nil
1973}
1974
1975func (c *Context) writeGlobalRules(nw *ninjaWriter) error {
Jamie Gennisc15544d2014-09-24 20:26:52 -07001976 globalRules := make([]globalEntity, 0, len(c.globalRules))
1977 for rule := range c.globalRules {
1978 globalRules = append(globalRules, rule)
1979 }
1980
1981 sort.Sort(&globalEntitySorter{c.pkgNames, globalRules})
1982
1983 for _, entity := range globalRules {
1984 rule := entity.(Rule)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001985 name := rule.fullName(c.pkgNames)
Jamie Gennisc15544d2014-09-24 20:26:52 -07001986 def := c.globalRules[rule]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001987 err := def.WriteTo(nw, name, c.pkgNames)
1988 if err != nil {
1989 return err
1990 }
1991
1992 err = nw.BlankLine()
1993 if err != nil {
1994 return err
1995 }
1996 }
1997
1998 return nil
1999}
2000
Colin Crossbbfa51a2014-12-17 16:12:41 -08002001type moduleGroupSorter []*moduleGroup
Jamie Gennis86179fe2014-06-11 16:27:16 -07002002
Colin Crossbbfa51a2014-12-17 16:12:41 -08002003func (s moduleGroupSorter) Len() int {
Jamie Gennis86179fe2014-06-11 16:27:16 -07002004 return len(s)
2005}
2006
Colin Crossbbfa51a2014-12-17 16:12:41 -08002007func (s moduleGroupSorter) Less(i, j int) bool {
Jamie Gennis86179fe2014-06-11 16:27:16 -07002008 iName := s[i].properties.Name
2009 jName := s[j].properties.Name
2010 return iName < jName
2011}
2012
Colin Crossbbfa51a2014-12-17 16:12:41 -08002013func (s moduleGroupSorter) Swap(i, j int) {
Jamie Gennis86179fe2014-06-11 16:27:16 -07002014 s[i], s[j] = s[j], s[i]
2015}
2016
Jamie Gennis1bc967e2014-05-27 16:34:41 -07002017func (c *Context) writeAllModuleActions(nw *ninjaWriter) error {
2018 headerTemplate := template.New("moduleHeader")
2019 _, err := headerTemplate.Parse(moduleHeaderTemplate)
2020 if err != nil {
2021 // This is a programming error.
2022 panic(err)
2023 }
2024
Colin Crossbbfa51a2014-12-17 16:12:41 -08002025 infos := make([]*moduleGroup, 0, len(c.moduleGroups))
2026 for _, info := range c.moduleGroups {
Jamie Gennis86179fe2014-06-11 16:27:16 -07002027 infos = append(infos, info)
2028 }
Colin Crossbbfa51a2014-12-17 16:12:41 -08002029 sort.Sort(moduleGroupSorter(infos))
Jamie Gennis86179fe2014-06-11 16:27:16 -07002030
Jamie Gennis1bc967e2014-05-27 16:34:41 -07002031 buf := bytes.NewBuffer(nil)
2032
Jamie Gennis86179fe2014-06-11 16:27:16 -07002033 for _, info := range infos {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07002034 buf.Reset()
Jamie Gennis1ebd3b82014-06-04 15:33:08 -07002035
2036 // In order to make the bootstrap build manifest independent of the
2037 // build dir we need to output the Blueprints file locations in the
2038 // comments as paths relative to the source directory.
2039 relPos := info.pos
Jamie Gennisec701282014-06-12 20:06:31 -07002040 relPos.Filename = info.relBlueprintsFile
Jamie Gennis1ebd3b82014-06-04 15:33:08 -07002041
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07002042 // Get the name and location of the factory function for the module.
2043 factory := c.moduleFactories[info.typeName]
2044 factoryFunc := runtime.FuncForPC(reflect.ValueOf(factory).Pointer())
2045 factoryName := factoryFunc.Name()
2046
Jamie Gennis1bc967e2014-05-27 16:34:41 -07002047 infoMap := map[string]interface{}{
2048 "properties": info.properties,
2049 "typeName": info.typeName,
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07002050 "goFactory": factoryName,
Jamie Gennis1ebd3b82014-06-04 15:33:08 -07002051 "pos": relPos,
Jamie Gennis1bc967e2014-05-27 16:34:41 -07002052 }
2053 err = headerTemplate.Execute(buf, infoMap)
2054 if err != nil {
2055 return err
2056 }
2057
2058 err = nw.Comment(buf.String())
2059 if err != nil {
2060 return err
2061 }
2062
2063 err = nw.BlankLine()
2064 if err != nil {
2065 return err
2066 }
2067
2068 err = c.writeLocalBuildActions(nw, &info.actionDefs)
2069 if err != nil {
2070 return err
2071 }
2072
2073 err = nw.BlankLine()
2074 if err != nil {
2075 return err
2076 }
2077 }
2078
2079 return nil
2080}
2081
2082func (c *Context) writeAllSingletonActions(nw *ninjaWriter) error {
2083 headerTemplate := template.New("singletonHeader")
2084 _, err := headerTemplate.Parse(singletonHeaderTemplate)
2085 if err != nil {
2086 // This is a programming error.
2087 panic(err)
2088 }
2089
2090 buf := bytes.NewBuffer(nil)
2091
Jamie Gennis86179fe2014-06-11 16:27:16 -07002092 singletonNames := make([]string, 0, len(c.singletonInfo))
2093 for name := range c.singletonInfo {
2094 singletonNames = append(singletonNames, name)
2095 }
2096 sort.Strings(singletonNames)
2097
2098 for _, name := range singletonNames {
2099 info := c.singletonInfo[name]
2100
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07002101 // Get the name of the factory function for the module.
2102 factory := info.factory
2103 factoryFunc := runtime.FuncForPC(reflect.ValueOf(factory).Pointer())
2104 factoryName := factoryFunc.Name()
2105
Jamie Gennis1bc967e2014-05-27 16:34:41 -07002106 buf.Reset()
2107 infoMap := map[string]interface{}{
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07002108 "name": name,
2109 "goFactory": factoryName,
Jamie Gennis1bc967e2014-05-27 16:34:41 -07002110 }
2111 err = headerTemplate.Execute(buf, infoMap)
2112 if err != nil {
2113 return err
2114 }
2115
2116 err = nw.Comment(buf.String())
2117 if err != nil {
2118 return err
2119 }
2120
2121 err = nw.BlankLine()
2122 if err != nil {
2123 return err
2124 }
2125
2126 err = c.writeLocalBuildActions(nw, &info.actionDefs)
2127 if err != nil {
2128 return err
2129 }
2130
2131 err = nw.BlankLine()
2132 if err != nil {
2133 return err
2134 }
2135 }
2136
2137 return nil
2138}
2139
2140func (c *Context) writeLocalBuildActions(nw *ninjaWriter,
2141 defs *localBuildActions) error {
2142
2143 // Write the local variable assignments.
2144 for _, v := range defs.variables {
2145 // A localVariable doesn't need the package names or config to
2146 // determine its name or value.
2147 name := v.fullName(nil)
2148 value, err := v.value(nil)
2149 if err != nil {
2150 panic(err)
2151 }
2152 err = nw.Assign(name, value.Value(c.pkgNames))
2153 if err != nil {
2154 return err
2155 }
2156 }
2157
2158 if len(defs.variables) > 0 {
2159 err := nw.BlankLine()
2160 if err != nil {
2161 return err
2162 }
2163 }
2164
2165 // Write the local rules.
2166 for _, r := range defs.rules {
2167 // A localRule doesn't need the package names or config to determine
2168 // its name or definition.
2169 name := r.fullName(nil)
2170 def, err := r.def(nil)
2171 if err != nil {
2172 panic(err)
2173 }
2174
2175 err = def.WriteTo(nw, name, c.pkgNames)
2176 if err != nil {
2177 return err
2178 }
2179
2180 err = nw.BlankLine()
2181 if err != nil {
2182 return err
2183 }
2184 }
2185
2186 // Write the build definitions.
2187 for _, buildDef := range defs.buildDefs {
2188 err := buildDef.WriteTo(nw, c.pkgNames)
2189 if err != nil {
2190 return err
2191 }
2192
2193 if len(buildDef.Args) > 0 {
2194 err = nw.BlankLine()
2195 if err != nil {
2196 return err
2197 }
2198 }
2199 }
2200
2201 return nil
2202}
2203
2204var fileHeaderTemplate = `******************************************************************************
2205*** This file is generated and should not be edited ***
2206******************************************************************************
2207{{if .Pkgs}}
2208This file contains variables, rules, and pools with name prefixes indicating
2209they were generated by the following Go packages:
2210{{range .Pkgs}}
2211 {{.PkgName}} [from Go package {{.PkgPath}}]{{end}}{{end}}
2212
2213`
2214
2215var moduleHeaderTemplate = `# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
2216Module: {{.properties.Name}}
2217Type: {{.typeName}}
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07002218Factory: {{.goFactory}}
Jamie Gennis1bc967e2014-05-27 16:34:41 -07002219Defined: {{.pos}}
2220`
2221
2222var singletonHeaderTemplate = `# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
2223Singleton: {{.name}}
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07002224Factory: {{.goFactory}}
Jamie Gennis1bc967e2014-05-27 16:34:41 -07002225`