blob: 985a64488a1f5a9e0f10109088531196322112a7 [file] [log] [blame]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001package blueprint
2
3import (
4 "blueprint/parser"
5 "bytes"
6 "errors"
7 "fmt"
8 "io"
9 "os"
10 "path/filepath"
11 "reflect"
Romain Guy28529652014-08-12 17:50:11 -070012 "runtime"
Jamie Gennis1bc967e2014-05-27 16:34:41 -070013 "sort"
14 "strings"
15 "text/scanner"
16 "text/template"
17)
18
19var ErrBuildActionsNotReady = errors.New("build actions are not ready")
20
21const maxErrors = 10
22
Jamie Gennisd4e10182014-06-12 20:06:50 -070023// A Context contains all the state needed to parse a set of Blueprints files
24// and generate a Ninja file. The process of generating a Ninja file proceeds
25// through a series of four phases. Each phase corresponds with a some methods
26// on the Context object
27//
28// Phase Methods
29// ------------ -------------------------------------------
Jamie Gennis7d5b2f82014-09-24 17:51:52 -070030// 1. Registration RegisterModuleType, RegisterSingletonType
Jamie Gennisd4e10182014-06-12 20:06:50 -070031//
32// 2. Parse ParseBlueprintsFiles, Parse
33//
Jamie Gennis7d5b2f82014-09-24 17:51:52 -070034// 3. Generate ResolveDependencies, PrepareBuildActions
Jamie Gennisd4e10182014-06-12 20:06:50 -070035//
36// 4. Write WriteBuildFile
37//
38// The registration phase prepares the context to process Blueprints files
39// containing various types of modules. The parse phase reads in one or more
40// Blueprints files and validates their contents against the module types that
41// have been registered. The generate phase then analyzes the parsed Blueprints
42// contents to create an internal representation for the build actions that must
43// be performed. This phase also performs validation of the module dependencies
44// and property values defined in the parsed Blueprints files. Finally, the
45// write phase generates the Ninja manifest text based on the generated build
46// actions.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070047type Context struct {
48 // set at instantiation
Jamie Gennis7d5b2f82014-09-24 17:51:52 -070049 moduleFactories map[string]ModuleFactory
50 modules map[string]Module
51 moduleInfo map[Module]*moduleInfo
52 singletonInfo map[string]*singletonInfo
Jamie Gennis1bc967e2014-05-27 16:34:41 -070053
54 dependenciesReady bool // set to true on a successful ResolveDependencies
55 buildActionsReady bool // set to true on a successful PrepareBuildActions
56
57 // set by SetIgnoreUnknownModuleTypes
58 ignoreUnknownModuleTypes bool
59
60 // set during PrepareBuildActions
Jamie Gennis2fb20952014-10-03 02:49:58 -070061 pkgNames map[*PackageContext]string
Jamie Gennis1bc967e2014-05-27 16:34:41 -070062 globalVariables map[Variable]*ninjaString
63 globalPools map[Pool]*poolDef
64 globalRules map[Rule]*ruleDef
65
66 // set during PrepareBuildActions
67 buildDir *ninjaString // The builddir special Ninja variable
68 requiredNinjaMajor int // For the ninja_required_version variable
69 requiredNinjaMinor int // For the ninja_required_version variable
70 requiredNinjaMicro int // For the ninja_required_version variable
Jamie Gennisc15544d2014-09-24 20:26:52 -070071
72 // set lazily by sortedModuleNames
73 cachedSortedModuleNames []string
Jamie Gennis1bc967e2014-05-27 16:34:41 -070074}
75
Jamie Gennisd4e10182014-06-12 20:06:50 -070076// An Error describes a problem that was encountered that is related to a
77// particular location in a Blueprints file.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070078type Error struct {
Jamie Gennisd4e10182014-06-12 20:06:50 -070079 Err error // the error that occurred
80 Pos scanner.Position // the relevant Blueprints file location
Jamie Gennis1bc967e2014-05-27 16:34:41 -070081}
82
83type localBuildActions struct {
84 variables []*localVariable
85 rules []*localRule
86 buildDefs []*buildDef
87}
88
89type moduleInfo struct {
90 // set during Parse
Jamie Gennisec701282014-06-12 20:06:31 -070091 typeName string
Jamie Gennisec701282014-06-12 20:06:31 -070092 relBlueprintsFile string
93 pos scanner.Position
94 propertyPos map[string]scanner.Position
95 properties struct {
Jamie Gennis1174c692014-10-05 07:41:44 -070096 Name string
97 Deps []string
Jamie Gennis1bc967e2014-05-27 16:34:41 -070098 }
99
100 // set during ResolveDependencies
101 directDeps []Module
102
103 // set during PrepareBuildActions
104 actionDefs localBuildActions
105}
106
107type singletonInfo struct {
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700108 // set during RegisterSingletonType
109 factory SingletonFactory
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700110 singleton Singleton
111
112 // set during PrepareBuildActions
113 actionDefs localBuildActions
114}
115
116func (e *Error) Error() string {
117
118 return fmt.Sprintf("%s: %s", e.Pos, e.Err)
119}
120
Jamie Gennisd4e10182014-06-12 20:06:50 -0700121// NewContext creates a new Context object. The created context initially has
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700122// no module or singleton factories registered, so the RegisterModuleFactory and
123// RegisterSingletonFactory methods must be called before it can do anything
124// useful.
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700125func NewContext() *Context {
126 return &Context{
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700127 moduleFactories: make(map[string]ModuleFactory),
128 modules: make(map[string]Module),
129 moduleInfo: make(map[Module]*moduleInfo),
130 singletonInfo: make(map[string]*singletonInfo),
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700131 }
132}
133
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700134// A ModuleFactory function creates a new Module object. See the
135// Context.RegisterModuleType method for details about how a registered
136// ModuleFactory is used by a Context.
137type ModuleFactory func() (m Module, propertyStructs []interface{})
138
Jamie Gennisd4e10182014-06-12 20:06:50 -0700139// RegisterModuleType associates a module type name (which can appear in a
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700140// Blueprints file) with a Module factory function. When the given module type
141// name is encountered in a Blueprints file during parsing, the Module factory
142// is invoked to instantiate a new Module object to handle the build action
Jamie Gennisd4e10182014-06-12 20:06:50 -0700143// generation for the module.
144//
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700145// The module type names given here must be unique for the context. The factory
146// function should be a named function so that its package and name can be
147// included in the generated Ninja file for debugging purposes.
148//
149// The factory function returns two values. The first is the newly created
150// Module object. The second is a slice of pointers to that Module object's
151// properties structs. Each properties struct is examined when parsing a module
152// definition of this type in a Blueprints file. Exported fields of the
153// properties structs are automatically set to the property values specified in
154// the Blueprints file. The properties struct field names determine the name of
155// the Blueprints file properties that are used - the Blueprints property name
156// matches that of the properties struct field name with the first letter
157// converted to lower-case.
158//
159// The fields of the properties struct must be either []string, a string, or
160// bool. The Context will panic if a Module gets instantiated with a properties
161// struct containing a field that is not one these supported types.
162//
163// Any properties that appear in the Blueprints files that are not built-in
164// module properties (such as "name" and "deps") and do not have a corresponding
165// field in the returned module properties struct result in an error during the
166// Context's parse phase.
167//
168// As an example, the follow code:
169//
170// type myModule struct {
171// properties struct {
172// Foo string
173// Bar []string
174// }
175// }
176//
177// func NewMyModule() (blueprint.Module, []interface{}) {
178// module := new(myModule)
179// properties := &module.properties
180// return module, []interface{}{properties}
181// }
182//
183// func main() {
184// ctx := blueprint.NewContext()
185// ctx.RegisterModuleType("my_module", NewMyModule)
186// // ...
187// }
188//
189// would support parsing a module defined in a Blueprints file as follows:
190//
191// my_module {
192// name: "myName",
193// foo: "my foo string",
194// bar: ["my", "bar", "strings"],
195// }
196//
197func (c *Context) RegisterModuleType(name string, factory ModuleFactory) {
198 if _, present := c.moduleFactories[name]; present {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700199 panic(errors.New("module type name is already registered"))
200 }
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700201 c.moduleFactories[name] = factory
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700202}
203
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700204// A SingletonFactory function creates a new Singleton object. See the
205// Context.RegisterSingletonType method for details about how a registered
206// SingletonFactory is used by a Context.
207type SingletonFactory func() Singleton
208
209// RegisterSingletonType registers a singleton type that will be invoked to
210// generate build actions. Each registered singleton type is instantiated and
211// and invoked exactly once as part of the generate phase.
212//
213// The singleton type names given here must be unique for the context. The
214// factory function should be a named function so that its package and name can
215// be included in the generated Ninja file for debugging purposes.
216func (c *Context) RegisterSingletonType(name string, factory SingletonFactory) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700217 if _, present := c.singletonInfo[name]; present {
218 panic(errors.New("singleton name is already registered"))
219 }
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700220
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700221 c.singletonInfo[name] = &singletonInfo{
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700222 factory: factory,
223 singleton: factory(),
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700224 }
225}
226
227func singletonPkgPath(singleton Singleton) string {
228 typ := reflect.TypeOf(singleton)
229 for typ.Kind() == reflect.Ptr {
230 typ = typ.Elem()
231 }
232 return typ.PkgPath()
233}
234
235func singletonTypeName(singleton Singleton) string {
236 typ := reflect.TypeOf(singleton)
237 for typ.Kind() == reflect.Ptr {
238 typ = typ.Elem()
239 }
240 return typ.PkgPath() + "." + typ.Name()
241}
242
Jamie Gennisd4e10182014-06-12 20:06:50 -0700243// SetIgnoreUnknownModuleTypes sets the behavior of the context in the case
244// where it encounters an unknown module type while parsing Blueprints files. By
245// default, the context will report unknown module types as an error. If this
246// method is called with ignoreUnknownModuleTypes set to true then the context
247// will silently ignore unknown module types.
248//
249// This method should generally not be used. It exists to facilitate the
250// bootstrapping process.
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700251func (c *Context) SetIgnoreUnknownModuleTypes(ignoreUnknownModuleTypes bool) {
252 c.ignoreUnknownModuleTypes = ignoreUnknownModuleTypes
253}
254
Jamie Gennisd4e10182014-06-12 20:06:50 -0700255// Parse parses a single Blueprints file from r, creating Module objects for
256// each of the module definitions encountered. If the Blueprints file contains
257// an assignment to the "subdirs" variable, then the subdirectories listed are
258// returned in the subdirs first return value.
259//
260// rootDir specifies the path to the root directory of the source tree, while
261// filename specifies the path to the Blueprints file. These paths are used for
262// error reporting and for determining the module's directory.
263//
264// This method should probably not be used directly. It is provided to simplify
265// testing. Instead ParseBlueprintsFiles should be called to parse a set of
266// Blueprints files starting from a top-level Blueprints file.
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700267func (c *Context) Parse(rootDir, filename string, r io.Reader) (subdirs []string,
268 errs []error) {
269
270 c.dependenciesReady = false
271
Jamie Gennisec701282014-06-12 20:06:31 -0700272 relBlueprintsFile, err := filepath.Rel(rootDir, filename)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700273 if err != nil {
274 return nil, []error{err}
275 }
276
277 defs, errs := parser.Parse(filename, r)
278 if len(errs) > 0 {
279 for i, err := range errs {
280 if parseErr, ok := err.(*parser.ParseError); ok {
281 err = &Error{
282 Err: parseErr.Err,
283 Pos: parseErr.Pos,
284 }
285 errs[i] = err
286 }
287 }
288
289 // If there were any parse errors don't bother trying to interpret the
290 // result.
291 return nil, errs
292 }
293
294 for _, def := range defs {
295 var newErrs []error
296 switch def := def.(type) {
297 case *parser.Module:
Jamie Gennisec701282014-06-12 20:06:31 -0700298 newErrs = c.processModuleDef(def, relBlueprintsFile)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700299
300 case *parser.Assignment:
301 var newSubdirs []string
302 newSubdirs, newErrs = c.processAssignment(def)
303 if newSubdirs != nil {
304 subdirs = newSubdirs
305 }
306
307 default:
308 panic("unknown definition type")
309 }
310
311 if len(newErrs) > 0 {
312 errs = append(errs, newErrs...)
313 if len(errs) > maxErrors {
314 break
315 }
316 }
317 }
318
319 return subdirs, errs
320}
321
Jamie Gennisd4e10182014-06-12 20:06:50 -0700322// ParseBlueprintsFiles parses a set of Blueprints files starting with the file
323// at rootFile. When it encounters a Blueprints file with a set of subdirs
324// listed it recursively parses any Blueprints files found in those
325// subdirectories.
326//
327// If no errors are encountered while parsing the files, the list of paths on
328// which the future output will depend is returned. This list will include both
329// Blueprints file paths as well as directory paths for cases where wildcard
330// subdirs are found.
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700331func (c *Context) ParseBlueprintsFiles(rootFile string) (deps []string,
332 errs []error) {
333
334 rootDir := filepath.Dir(rootFile)
335
336 depsSet := map[string]bool{rootFile: true}
337 blueprints := []string{rootFile}
338
339 var file *os.File
340 defer func() {
341 if file != nil {
342 file.Close()
343 }
344 }()
345
346 var err error
347
348 for i := 0; i < len(blueprints); i++ {
349 if len(errs) > maxErrors {
350 return
351 }
352
353 filename := blueprints[i]
354 dir := filepath.Dir(filename)
355
356 file, err = os.Open(filename)
357 if err != nil {
358 errs = append(errs, &Error{Err: err})
359 continue
360 }
361
362 subdirs, newErrs := c.Parse(rootDir, filename, file)
363 if len(newErrs) > 0 {
364 errs = append(errs, newErrs...)
365 continue
366 }
367
368 err = file.Close()
369 if err != nil {
370 errs = append(errs, &Error{Err: err})
371 continue
372 }
373
374 // Add the subdirs to the list of directories to parse Blueprint files
375 // from.
376 for _, subdir := range subdirs {
377 subdir = filepath.Join(dir, subdir)
378 dirPart, filePart := filepath.Split(subdir)
379 dirPart = filepath.Clean(dirPart)
380
381 if filePart == "*" {
382 foundSubdirs, err := listSubdirs(dirPart)
383 if err != nil {
384 errs = append(errs, &Error{Err: err})
385 continue
386 }
387
388 for _, foundSubdir := range foundSubdirs {
389 subBlueprints := filepath.Join(dirPart, foundSubdir,
390 "Blueprints")
391
392 _, err := os.Stat(subBlueprints)
393 if os.IsNotExist(err) {
394 // There is no Blueprints file in this subdirectory. We
395 // need to add the directory to the list of dependencies
396 // so that if someone adds a Blueprints file in the
397 // future we'll pick it up.
398 depsSet[filepath.Dir(subBlueprints)] = true
399 } else if !depsSet[subBlueprints] {
400 // We haven't seen this Blueprints file before, so add
401 // it to our list.
402 depsSet[subBlueprints] = true
403 blueprints = append(blueprints, subBlueprints)
404 }
405 }
406
407 // We now depend on the directory itself because if any new
408 // subdirectories get added or removed we need to rebuild the
409 // Ninja manifest.
410 depsSet[dirPart] = true
411 } else {
412 subBlueprints := filepath.Join(subdir, "Blueprints")
413 if !depsSet[subBlueprints] {
414 depsSet[subBlueprints] = true
415 blueprints = append(blueprints, subBlueprints)
416 }
417 }
418 }
419 }
420
421 for dep := range depsSet {
422 deps = append(deps, dep)
423 }
424
425 return
426}
427
428func listSubdirs(dir string) ([]string, error) {
429 d, err := os.Open(dir)
430 if err != nil {
431 return nil, err
432 }
433 defer d.Close()
434
435 infos, err := d.Readdir(-1)
436 if err != nil {
437 return nil, err
438 }
439
440 var subdirs []string
441 for _, info := range infos {
Jamie Gennis0c35b2d2014-09-25 13:15:10 -0700442 isDotFile := strings.HasPrefix(info.Name(), ".")
443 if info.IsDir() && !isDotFile {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700444 subdirs = append(subdirs, info.Name())
445 }
446 }
447
448 return subdirs, nil
449}
450
451func (c *Context) processAssignment(
452 assignment *parser.Assignment) (subdirs []string, errs []error) {
453
454 if assignment.Name == "subdirs" {
455 switch assignment.Value.Type {
456 case parser.List:
457 subdirs = make([]string, 0, len(assignment.Value.ListValue))
458
459 for _, value := range assignment.Value.ListValue {
460 if value.Type != parser.String {
461 // The parser should not produce this.
462 panic("non-string value found in list")
463 }
464
465 dirPart, filePart := filepath.Split(value.StringValue)
466 if (filePart != "*" && strings.ContainsRune(filePart, '*')) ||
467 strings.ContainsRune(dirPart, '*') {
468
469 errs = append(errs, &Error{
470 Err: fmt.Errorf("subdirs may only wildcard whole " +
471 "directories"),
472 Pos: value.Pos,
473 })
474
475 continue
476 }
477
478 subdirs = append(subdirs, value.StringValue)
479 }
480
481 if len(errs) > 0 {
482 subdirs = nil
483 }
484
485 return
486
487 case parser.Bool, parser.String:
488 errs = []error{
489 &Error{
490 Err: fmt.Errorf("subdirs must be a list of strings"),
491 Pos: assignment.Pos,
492 },
493 }
494
495 return
496
497 default:
498 panic(fmt.Errorf("unknown value type: %d", assignment.Value.Type))
499 }
500 }
501
502 return nil, []error{
503 &Error{
504 Err: fmt.Errorf("only 'subdirs' assignment is supported"),
505 Pos: assignment.Pos,
506 },
507 }
508}
509
510func (c *Context) processModuleDef(moduleDef *parser.Module,
Jamie Gennisec701282014-06-12 20:06:31 -0700511 relBlueprintsFile string) []error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700512
513 typeName := moduleDef.Type
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700514 factory, ok := c.moduleFactories[typeName]
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700515 if !ok {
516 if c.ignoreUnknownModuleTypes {
517 return nil
518 }
519
Jamie Gennisd4c53d82014-06-22 17:02:55 -0700520 return []error{
521 &Error{
522 Err: fmt.Errorf("unrecognized module type %q", typeName),
523 Pos: moduleDef.Pos,
524 },
525 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700526 }
527
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700528 module, properties := factory()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700529 info := &moduleInfo{
Jamie Gennisec701282014-06-12 20:06:31 -0700530 typeName: typeName,
Jamie Gennisec701282014-06-12 20:06:31 -0700531 relBlueprintsFile: relBlueprintsFile,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700532 }
533
Jamie Gennis87622922014-09-30 11:38:25 -0700534 props := []interface{}{
535 &info.properties,
536 }
537 properties = append(props, properties...)
Jamie Gennisd4c53d82014-06-22 17:02:55 -0700538
Jamie Gennis87622922014-09-30 11:38:25 -0700539 propertyMap, errs := unpackProperties(moduleDef.Properties, properties...)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700540 if len(errs) > 0 {
541 return errs
542 }
543
544 info.pos = moduleDef.Pos
545 info.propertyPos = make(map[string]scanner.Position)
Jamie Gennis87622922014-09-30 11:38:25 -0700546 for name, propertyDef := range propertyMap {
547 info.propertyPos[name] = propertyDef.Pos
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700548 }
549
550 name := info.properties.Name
551 err := validateNinjaName(name)
552 if err != nil {
553 return []error{
554 &Error{
555 Err: fmt.Errorf("invalid module name %q: %s", err),
556 Pos: info.propertyPos["name"],
557 },
558 }
559 }
560
561 if first, present := c.modules[name]; present {
562 errs = append(errs, &Error{
563 Err: fmt.Errorf("module %q already defined", name),
564 Pos: moduleDef.Pos,
565 })
566 errs = append(errs, &Error{
567 Err: fmt.Errorf("<-- previous definition here"),
568 Pos: c.moduleInfo[first].pos,
569 })
Jamie Gennisb282d5c2014-09-24 20:07:08 -0700570 if len(errs) > 0 {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700571 return errs
572 }
573 }
574
575 c.modules[name] = module
576 c.moduleInfo[module] = info
577
578 return nil
579}
580
Jamie Gennisd4e10182014-06-12 20:06:50 -0700581// ResolveDependencies checks that the dependencies specified by all of the
582// modules defined in the parsed Blueprints files are valid. This means that
583// the modules depended upon are defined and that no circular dependencies
584// exist.
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700585//
586// The config argument is made available to all of the DynamicDependerModule
587// objects via the Config method on the DynamicDependerModuleContext objects
588// passed to their DynamicDependencies method.
589func (c *Context) ResolveDependencies(config interface{}) []error {
590 errs := c.resolveDependencies(config)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700591 if len(errs) > 0 {
592 return errs
593 }
594
595 errs = c.checkForDependencyCycles()
596 if len(errs) > 0 {
597 return errs
598 }
599
600 c.dependenciesReady = true
601 return nil
602}
603
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700604// moduleDepNames returns the sorted list of dependency names for a given
605// module. If the module implements the DynamicDependerModule interface then
606// this set consists of the union of those module names listed in its "deps"
607// property and those returned by its DynamicDependencies method. Otherwise it
608// is simply those names listed in its "deps" property.
609func (c *Context) moduleDepNames(info *moduleInfo,
610 config interface{}) ([]string, []error) {
611
612 depNamesSet := make(map[string]bool)
613
614 for _, depName := range info.properties.Deps {
615 depNamesSet[depName] = true
616 }
617
618 module := c.modules[info.properties.Name]
619 dynamicDepender, ok := module.(DynamicDependerModule)
620 if ok {
621 ddmctx := &dynamicDependerModuleContext{
622 context: c,
623 config: config,
624 info: info,
625 }
626
627 dynamicDeps := dynamicDepender.DynamicDependencies(ddmctx)
628
629 if len(ddmctx.errs) > 0 {
630 return nil, ddmctx.errs
631 }
632
633 for _, depName := range dynamicDeps {
634 depNamesSet[depName] = true
635 }
636 }
637
638 // We need to sort the dependency names to ensure deterministic Ninja file
639 // output from one run to the next.
640 depNames := make([]string, 0, len(depNamesSet))
641 for depName := range depNamesSet {
642 depNames = append(depNames, depName)
643 }
644 sort.Strings(depNames)
645
646 return depNames, nil
647}
648
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700649// resolveDependencies populates the moduleInfo.directDeps list for every
650// module. In doing so it checks for missing dependencies and self-dependant
651// modules.
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700652func (c *Context) resolveDependencies(config interface{}) (errs []error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700653 for _, info := range c.moduleInfo {
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700654 depNames, newErrs := c.moduleDepNames(info, config)
655 if len(newErrs) > 0 {
656 errs = append(errs, newErrs...)
657 continue
658 }
659
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700660 info.directDeps = make([]Module, 0, len(depNames))
661 depsPos := info.propertyPos["deps"]
662
663 for _, depName := range depNames {
664 if depName == info.properties.Name {
665 errs = append(errs, &Error{
666 Err: fmt.Errorf("%q depends on itself", depName),
667 Pos: depsPos,
668 })
669 continue
670 }
671
672 dep, ok := c.modules[depName]
673 if !ok {
674 errs = append(errs, &Error{
675 Err: fmt.Errorf("%q depends on undefined module %q",
676 info.properties.Name, depName),
677 Pos: depsPos,
678 })
679 continue
680 }
681
682 info.directDeps = append(info.directDeps, dep)
683 }
684 }
685
686 return
687}
688
689// checkForDependencyCycles recursively walks the module dependency graph and
690// reports errors when it encounters dependency cycles. This should only be
691// called after resolveDependencies.
692func (c *Context) checkForDependencyCycles() (errs []error) {
693 visited := make(map[Module]bool) // modules that were already checked
694 checking := make(map[Module]bool) // modules actively being checked
695
696 var check func(m Module) []Module
697
698 check = func(m Module) []Module {
699 info := c.moduleInfo[m]
700
701 visited[m] = true
702 checking[m] = true
703 defer delete(checking, m)
704
705 for _, dep := range info.directDeps {
706 if checking[dep] {
707 // This is a cycle.
708 return []Module{dep, m}
709 }
710
711 if !visited[dep] {
712 cycle := check(dep)
713 if cycle != nil {
714 if cycle[0] == m {
715 // We are the "start" of the cycle, so we're responsible
716 // for generating the errors. The cycle list is in
717 // reverse order because all the 'check' calls append
718 // their own module to the list.
719 errs = append(errs, &Error{
720 Err: fmt.Errorf("encountered dependency cycle:"),
721 Pos: info.pos,
722 })
723
724 // Iterate backwards through the cycle list.
725 curInfo := info
726 for i := len(cycle) - 1; i >= 0; i-- {
727 nextInfo := c.moduleInfo[cycle[i]]
728 errs = append(errs, &Error{
729 Err: fmt.Errorf(" %q depends on %q",
730 curInfo.properties.Name,
731 nextInfo.properties.Name),
732 Pos: curInfo.propertyPos["deps"],
733 })
734 curInfo = nextInfo
735 }
736
737 // We can continue processing this module's children to
738 // find more cycles. Since all the modules that were
739 // part of the found cycle were marked as visited we
740 // won't run into that cycle again.
741 } else {
742 // We're not the "start" of the cycle, so we just append
743 // our module to the list and return it.
744 return append(cycle, m)
745 }
746 }
747 }
748 }
749
750 return nil
751 }
752
753 for _, module := range c.modules {
754 if !visited[module] {
755 cycle := check(module)
756 if cycle != nil {
757 panic("inconceivable!")
758 }
759 }
760 }
761
762 return
763}
764
Jamie Gennisd4e10182014-06-12 20:06:50 -0700765// PrepareBuildActions generates an internal representation of all the build
766// actions that need to be performed. This process involves invoking the
767// GenerateBuildActions method on each of the Module objects created during the
768// parse phase and then on each of the registered Singleton objects.
769//
770// If the ResolveDependencies method has not already been called it is called
771// automatically by this method.
772//
773// The config argument is made available to all of the Module and Singleton
774// objects via the Config method on the ModuleContext and SingletonContext
775// objects passed to GenerateBuildActions. It is also passed to the functions
776// specified via PoolFunc, RuleFunc, and VariableFunc so that they can compute
777// config-specific values.
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700778//
779// The returned deps is a list of the ninja files dependencies that were added
780// by the modules and singletons via the ModuleContext.AddNinjaFileDeps() and
781// SingletonContext.AddNinjaFileDeps() methods.
782func (c *Context) PrepareBuildActions(config interface{}) (deps []string, errs []error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700783 c.buildActionsReady = false
784
785 if !c.dependenciesReady {
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700786 errs := c.ResolveDependencies(config)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700787 if len(errs) > 0 {
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700788 return nil, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700789 }
790 }
791
792 liveGlobals := newLiveTracker(config)
793
794 c.initSpecialVariables()
795
Colin Crossb2e7b5d2014-11-11 14:18:53 -0800796 errs = c.preGenerateModuleBuildActions(config)
797 if len(errs) > 0 {
798 return nil, errs
799 }
800
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700801 depsModules, errs := c.generateModuleBuildActions(config, liveGlobals)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700802 if len(errs) > 0 {
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700803 return nil, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700804 }
805
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700806 depsSingletons, errs := c.generateSingletonBuildActions(config, liveGlobals)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700807 if len(errs) > 0 {
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700808 return nil, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700809 }
810
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700811 deps = append(depsModules, depsSingletons...)
812
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700813 if c.buildDir != nil {
814 liveGlobals.addNinjaStringDeps(c.buildDir)
815 }
816
817 pkgNames := c.makeUniquePackageNames(liveGlobals)
818
819 // This will panic if it finds a problem since it's a programming error.
820 c.checkForVariableReferenceCycles(liveGlobals.variables, pkgNames)
821
822 c.pkgNames = pkgNames
823 c.globalVariables = liveGlobals.variables
824 c.globalPools = liveGlobals.pools
825 c.globalRules = liveGlobals.rules
826
827 c.buildActionsReady = true
828
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700829 return deps, nil
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700830}
831
832func (c *Context) initSpecialVariables() {
833 c.buildDir = nil
834 c.requiredNinjaMajor = 1
835 c.requiredNinjaMinor = 1
836 c.requiredNinjaMicro = 0
837}
838
Colin Crossb2e7b5d2014-11-11 14:18:53 -0800839func (c *Context) preGenerateModuleBuildActions(config interface{}) (errs []error) {
840
841 visited := make(map[Module]bool)
842
843 var walk func(module Module)
844 walk = func(module Module) {
845 visited[module] = true
846
847 info := c.moduleInfo[module]
848 for _, dep := range info.directDeps {
849 if !visited[dep] {
850 walk(dep)
851 if len(errs) > 0 {
852 return
853 }
854 }
855 }
856
857 if preGenerateModule, ok := module.(preGenerateModule); ok {
858 mctx := &preModuleContext{
859 dynamicDependerModuleContext: dynamicDependerModuleContext{
860 context: c,
861 config: config,
862 info: info,
863 },
864 module: module,
865 }
866
867 preGenerateModule.PreGenerateBuildActions(mctx)
868
869 if len(mctx.errs) > 0 {
870 errs = append(errs, mctx.errs...)
871 return
872 }
873 }
874 }
875
876 for _, module := range c.modules {
877 if !visited[module] {
878 walk(module)
879 if len(errs) > 0 {
880 break
881 }
882 }
883 }
884
885 return
886}
887
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700888func (c *Context) generateModuleBuildActions(config interface{},
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700889 liveGlobals *liveTracker) ([]string, []error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700890
891 visited := make(map[Module]bool)
892
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700893 var deps []string
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700894 var errs []error
895
896 var walk func(module Module)
897 walk = func(module Module) {
898 visited[module] = true
899
900 info := c.moduleInfo[module]
901 for _, dep := range info.directDeps {
902 if !visited[dep] {
903 walk(dep)
Jamie Gennisae4430c2014-07-23 14:37:21 -0700904 if len(errs) > 0 {
905 return
906 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700907 }
908 }
909
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700910 // The parent scope of the moduleContext's local scope gets overridden to be that of the
911 // calling Go package on a per-call basis. Since the initial parent scope doesn't matter we
912 // just set it to nil.
913 scope := newLocalScope(nil, moduleNamespacePrefix(info.properties.Name))
914
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700915 mctx := &moduleContext{
Colin Crossb2e7b5d2014-11-11 14:18:53 -0800916 preModuleContext: preModuleContext{
917 dynamicDependerModuleContext: dynamicDependerModuleContext{
918 context: c,
919 config: config,
920 info: info,
921 },
922 module: module,
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700923 },
Colin Crossb2e7b5d2014-11-11 14:18:53 -0800924 scope: scope,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700925 }
926
927 module.GenerateBuildActions(mctx)
928
929 if len(mctx.errs) > 0 {
930 errs = append(errs, mctx.errs...)
931 return
932 }
933
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700934 deps = append(deps, mctx.ninjaFileDeps...)
935
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700936 newErrs := c.processLocalBuildActions(&info.actionDefs,
937 &mctx.actionDefs, liveGlobals)
938 errs = append(errs, newErrs...)
939 }
940
941 for _, module := range c.modules {
942 if !visited[module] {
943 walk(module)
Jamie Gennisae4430c2014-07-23 14:37:21 -0700944 if len(errs) > 0 {
945 break
946 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700947 }
948 }
949
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700950 return deps, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700951}
952
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700953func (c *Context) generateSingletonBuildActions(config interface{},
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700954 liveGlobals *liveTracker) ([]string, []error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700955
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700956 var deps []string
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700957 var errs []error
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700958
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700959 for name, info := range c.singletonInfo {
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700960 // The parent scope of the singletonContext's local scope gets overridden to be that of the
961 // calling Go package on a per-call basis. Since the initial parent scope doesn't matter we
962 // just set it to nil.
963 scope := newLocalScope(nil, singletonNamespacePrefix(name))
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700964
965 sctx := &singletonContext{
966 context: c,
967 config: config,
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700968 scope: scope,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700969 }
970
971 info.singleton.GenerateBuildActions(sctx)
972
973 if len(sctx.errs) > 0 {
974 errs = append(errs, sctx.errs...)
975 if len(errs) > maxErrors {
976 break
977 }
978 continue
979 }
980
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700981 deps = append(deps, sctx.ninjaFileDeps...)
982
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700983 newErrs := c.processLocalBuildActions(&info.actionDefs,
984 &sctx.actionDefs, liveGlobals)
985 errs = append(errs, newErrs...)
986 if len(errs) > maxErrors {
987 break
988 }
989 }
990
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700991 return deps, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700992}
993
994func (c *Context) processLocalBuildActions(out, in *localBuildActions,
995 liveGlobals *liveTracker) []error {
996
997 var errs []error
998
999 // First we go through and add everything referenced by the module's
1000 // buildDefs to the live globals set. This will end up adding the live
1001 // locals to the set as well, but we'll take them out after.
1002 for _, def := range in.buildDefs {
1003 err := liveGlobals.AddBuildDefDeps(def)
1004 if err != nil {
1005 errs = append(errs, err)
1006 }
1007 }
1008
1009 if len(errs) > 0 {
1010 return errs
1011 }
1012
1013 out.buildDefs = in.buildDefs
1014
1015 // We use the now-incorrect set of live "globals" to determine which local
1016 // definitions are live. As we go through copying those live locals to the
1017 // moduleInfo we remove them from the live globals set.
1018 out.variables = nil
1019 for _, v := range in.variables {
1020 _, isLive := liveGlobals.variables[v]
1021 if isLive {
1022 out.variables = append(out.variables, v)
1023 delete(liveGlobals.variables, v)
1024 }
1025 }
1026
1027 out.rules = nil
1028 for _, r := range in.rules {
1029 _, isLive := liveGlobals.rules[r]
1030 if isLive {
1031 out.rules = append(out.rules, r)
1032 delete(liveGlobals.rules, r)
1033 }
1034 }
1035
1036 return nil
1037}
1038
1039func (c *Context) visitDepsDepthFirst(module Module, visit func(Module)) {
1040 visited := make(map[Module]bool)
1041
1042 var walk func(m Module)
1043 walk = func(m Module) {
1044 info := c.moduleInfo[m]
1045 visited[m] = true
1046 for _, dep := range info.directDeps {
1047 if !visited[dep] {
1048 walk(dep)
1049 }
1050 }
1051 visit(m)
1052 }
1053
1054 info := c.moduleInfo[module]
1055 for _, dep := range info.directDeps {
1056 if !visited[dep] {
1057 walk(dep)
1058 }
1059 }
1060}
1061
1062func (c *Context) visitDepsDepthFirstIf(module Module, pred func(Module) bool,
1063 visit func(Module)) {
1064
1065 visited := make(map[Module]bool)
1066
1067 var walk func(m Module)
1068 walk = func(m Module) {
1069 info := c.moduleInfo[m]
1070 visited[m] = true
Jamie Gennis0bb5d8a2014-11-26 14:45:37 -08001071 for _, dep := range info.directDeps {
1072 if !visited[dep] {
1073 walk(dep)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001074 }
Jamie Gennis0bb5d8a2014-11-26 14:45:37 -08001075 }
1076 if pred(m) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001077 visit(m)
1078 }
1079 }
1080
1081 info := c.moduleInfo[module]
1082 for _, dep := range info.directDeps {
1083 if !visited[dep] {
1084 walk(dep)
1085 }
1086 }
1087}
1088
Jamie Gennisc15544d2014-09-24 20:26:52 -07001089func (c *Context) sortedModuleNames() []string {
1090 if c.cachedSortedModuleNames == nil {
1091 c.cachedSortedModuleNames = make([]string, 0, len(c.modules))
1092 for moduleName := range c.modules {
1093 c.cachedSortedModuleNames = append(c.cachedSortedModuleNames,
1094 moduleName)
1095 }
1096 sort.Strings(c.cachedSortedModuleNames)
1097 }
1098
1099 return c.cachedSortedModuleNames
1100}
1101
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001102func (c *Context) visitAllModules(visit func(Module)) {
Jamie Gennisc15544d2014-09-24 20:26:52 -07001103 for _, moduleName := range c.sortedModuleNames() {
1104 module := c.modules[moduleName]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001105 visit(module)
1106 }
1107}
1108
1109func (c *Context) visitAllModulesIf(pred func(Module) bool,
1110 visit func(Module)) {
1111
Jamie Gennisc15544d2014-09-24 20:26:52 -07001112 for _, moduleName := range c.sortedModuleNames() {
1113 module := c.modules[moduleName]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001114 if pred(module) {
1115 visit(module)
1116 }
1117 }
1118}
1119
1120func (c *Context) requireNinjaVersion(major, minor, micro int) {
1121 if major != 1 {
1122 panic("ninja version with major version != 1 not supported")
1123 }
1124 if c.requiredNinjaMinor < minor {
1125 c.requiredNinjaMinor = minor
1126 c.requiredNinjaMicro = micro
1127 }
1128 if c.requiredNinjaMinor == minor && c.requiredNinjaMicro < micro {
1129 c.requiredNinjaMicro = micro
1130 }
1131}
1132
1133func (c *Context) setBuildDir(value *ninjaString) {
1134 if c.buildDir != nil {
1135 panic("buildDir set multiple times")
1136 }
1137 c.buildDir = value
1138}
1139
1140func (c *Context) makeUniquePackageNames(
Jamie Gennis2fb20952014-10-03 02:49:58 -07001141 liveGlobals *liveTracker) map[*PackageContext]string {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001142
Jamie Gennis2fb20952014-10-03 02:49:58 -07001143 pkgs := make(map[string]*PackageContext)
1144 pkgNames := make(map[*PackageContext]string)
1145 longPkgNames := make(map[*PackageContext]bool)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001146
Jamie Gennis2fb20952014-10-03 02:49:58 -07001147 processPackage := func(pctx *PackageContext) {
1148 if pctx == nil {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001149 // This is a built-in rule and has no package.
1150 return
1151 }
Jamie Gennis2fb20952014-10-03 02:49:58 -07001152 if _, ok := pkgNames[pctx]; ok {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001153 // We've already processed this package.
1154 return
1155 }
1156
Jamie Gennis2fb20952014-10-03 02:49:58 -07001157 otherPkg, present := pkgs[pctx.shortName]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001158 if present {
1159 // Short name collision. Both this package and the one that's
1160 // already there need to use their full names. We leave the short
1161 // name in pkgNames for now so future collisions still get caught.
Jamie Gennis2fb20952014-10-03 02:49:58 -07001162 longPkgNames[pctx] = true
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001163 longPkgNames[otherPkg] = true
1164 } else {
1165 // No collision so far. Tentatively set the package's name to be
1166 // its short name.
Jamie Gennis2fb20952014-10-03 02:49:58 -07001167 pkgNames[pctx] = pctx.shortName
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001168 }
1169 }
1170
1171 // We try to give all packages their short name, but when we get collisions
1172 // we need to use the full unique package name.
1173 for v, _ := range liveGlobals.variables {
Jamie Gennis2fb20952014-10-03 02:49:58 -07001174 processPackage(v.packageContext())
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001175 }
1176 for p, _ := range liveGlobals.pools {
Jamie Gennis2fb20952014-10-03 02:49:58 -07001177 processPackage(p.packageContext())
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001178 }
1179 for r, _ := range liveGlobals.rules {
Jamie Gennis2fb20952014-10-03 02:49:58 -07001180 processPackage(r.packageContext())
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001181 }
1182
1183 // Add the packages that had collisions using their full unique names. This
1184 // will overwrite any short names that were added in the previous step.
Jamie Gennis2fb20952014-10-03 02:49:58 -07001185 for pctx := range longPkgNames {
1186 pkgNames[pctx] = pctx.fullName
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001187 }
1188
1189 return pkgNames
1190}
1191
1192func (c *Context) checkForVariableReferenceCycles(
Jamie Gennis2fb20952014-10-03 02:49:58 -07001193 variables map[Variable]*ninjaString, pkgNames map[*PackageContext]string) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001194
1195 visited := make(map[Variable]bool) // variables that were already checked
1196 checking := make(map[Variable]bool) // variables actively being checked
1197
1198 var check func(v Variable) []Variable
1199
1200 check = func(v Variable) []Variable {
1201 visited[v] = true
1202 checking[v] = true
1203 defer delete(checking, v)
1204
1205 value := variables[v]
1206 for _, dep := range value.variables {
1207 if checking[dep] {
1208 // This is a cycle.
1209 return []Variable{dep, v}
1210 }
1211
1212 if !visited[dep] {
1213 cycle := check(dep)
1214 if cycle != nil {
1215 if cycle[0] == v {
1216 // We are the "start" of the cycle, so we're responsible
1217 // for generating the errors. The cycle list is in
1218 // reverse order because all the 'check' calls append
1219 // their own module to the list.
1220 msgs := []string{"detected variable reference cycle:"}
1221
1222 // Iterate backwards through the cycle list.
1223 curName := v.fullName(pkgNames)
1224 curValue := value.Value(pkgNames)
1225 for i := len(cycle) - 1; i >= 0; i-- {
1226 next := cycle[i]
1227 nextName := next.fullName(pkgNames)
1228 nextValue := variables[next].Value(pkgNames)
1229
1230 msgs = append(msgs, fmt.Sprintf(
1231 " %q depends on %q", curName, nextName))
1232 msgs = append(msgs, fmt.Sprintf(
1233 " [%s = %s]", curName, curValue))
1234
1235 curName = nextName
1236 curValue = nextValue
1237 }
1238
1239 // Variable reference cycles are a programming error,
1240 // not the fault of the Blueprint file authors.
1241 panic(strings.Join(msgs, "\n"))
1242 } else {
1243 // We're not the "start" of the cycle, so we just append
1244 // our module to the list and return it.
1245 return append(cycle, v)
1246 }
1247 }
1248 }
1249 }
1250
1251 return nil
1252 }
1253
1254 for v := range variables {
1255 if !visited[v] {
1256 cycle := check(v)
1257 if cycle != nil {
1258 panic("inconceivable!")
1259 }
1260 }
1261 }
1262}
1263
Jamie Gennisaf435562014-10-27 22:34:56 -07001264// AllTargets returns a map all the build target names to the rule used to build
1265// them. This is the same information that is output by running 'ninja -t
1266// targets all'. If this is called before PrepareBuildActions successfully
1267// completes then ErrbuildActionsNotReady is returned.
1268func (c *Context) AllTargets() (map[string]string, error) {
1269 if !c.buildActionsReady {
1270 return nil, ErrBuildActionsNotReady
1271 }
1272
1273 targets := map[string]string{}
1274
1275 // Collect all the module build targets.
1276 for _, info := range c.moduleInfo {
1277 for _, buildDef := range info.actionDefs.buildDefs {
1278 ruleName := buildDef.Rule.fullName(c.pkgNames)
1279 for _, output := range buildDef.Outputs {
Christian Zander6e2b2322014-11-21 15:12:08 -08001280 outputValue, err := output.Eval(c.globalVariables)
1281 if err != nil {
1282 return nil, err
1283 }
Jamie Gennisaf435562014-10-27 22:34:56 -07001284 targets[outputValue] = ruleName
1285 }
1286 }
1287 }
1288
1289 // Collect all the singleton build targets.
1290 for _, info := range c.singletonInfo {
1291 for _, buildDef := range info.actionDefs.buildDefs {
1292 ruleName := buildDef.Rule.fullName(c.pkgNames)
1293 for _, output := range buildDef.Outputs {
Christian Zander6e2b2322014-11-21 15:12:08 -08001294 outputValue, err := output.Eval(c.globalVariables)
1295 if err != nil {
1296 return nil, err
1297 }
Jamie Gennisaf435562014-10-27 22:34:56 -07001298 targets[outputValue] = ruleName
1299 }
1300 }
1301 }
1302
1303 return targets, nil
1304}
1305
Jamie Gennisd4e10182014-06-12 20:06:50 -07001306// WriteBuildFile writes the Ninja manifeset text for the generated build
1307// actions to w. If this is called before PrepareBuildActions successfully
1308// completes then ErrBuildActionsNotReady is returned.
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001309func (c *Context) WriteBuildFile(w io.Writer) error {
1310 if !c.buildActionsReady {
1311 return ErrBuildActionsNotReady
1312 }
1313
1314 nw := newNinjaWriter(w)
1315
1316 err := c.writeBuildFileHeader(nw)
1317 if err != nil {
1318 return err
1319 }
1320
1321 err = c.writeNinjaRequiredVersion(nw)
1322 if err != nil {
1323 return err
1324 }
1325
1326 // TODO: Group the globals by package.
1327
1328 err = c.writeGlobalVariables(nw)
1329 if err != nil {
1330 return err
1331 }
1332
1333 err = c.writeGlobalPools(nw)
1334 if err != nil {
1335 return err
1336 }
1337
1338 err = c.writeBuildDir(nw)
1339 if err != nil {
1340 return err
1341 }
1342
1343 err = c.writeGlobalRules(nw)
1344 if err != nil {
1345 return err
1346 }
1347
1348 err = c.writeAllModuleActions(nw)
1349 if err != nil {
1350 return err
1351 }
1352
1353 err = c.writeAllSingletonActions(nw)
1354 if err != nil {
1355 return err
1356 }
1357
1358 return nil
1359}
1360
Jamie Gennisc15544d2014-09-24 20:26:52 -07001361type pkgAssociation struct {
1362 PkgName string
1363 PkgPath string
1364}
1365
1366type pkgAssociationSorter struct {
1367 pkgs []pkgAssociation
1368}
1369
1370func (s *pkgAssociationSorter) Len() int {
1371 return len(s.pkgs)
1372}
1373
1374func (s *pkgAssociationSorter) Less(i, j int) bool {
1375 iName := s.pkgs[i].PkgName
1376 jName := s.pkgs[j].PkgName
1377 return iName < jName
1378}
1379
1380func (s *pkgAssociationSorter) Swap(i, j int) {
1381 s.pkgs[i], s.pkgs[j] = s.pkgs[j], s.pkgs[i]
1382}
1383
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001384func (c *Context) writeBuildFileHeader(nw *ninjaWriter) error {
1385 headerTemplate := template.New("fileHeader")
1386 _, err := headerTemplate.Parse(fileHeaderTemplate)
1387 if err != nil {
1388 // This is a programming error.
1389 panic(err)
1390 }
1391
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001392 var pkgs []pkgAssociation
1393 maxNameLen := 0
1394 for pkg, name := range c.pkgNames {
1395 pkgs = append(pkgs, pkgAssociation{
1396 PkgName: name,
1397 PkgPath: pkg.pkgPath,
1398 })
1399 if len(name) > maxNameLen {
1400 maxNameLen = len(name)
1401 }
1402 }
1403
1404 for i := range pkgs {
1405 pkgs[i].PkgName += strings.Repeat(" ", maxNameLen-len(pkgs[i].PkgName))
1406 }
1407
Jamie Gennisc15544d2014-09-24 20:26:52 -07001408 sort.Sort(&pkgAssociationSorter{pkgs})
1409
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001410 params := map[string]interface{}{
1411 "Pkgs": pkgs,
1412 }
1413
1414 buf := bytes.NewBuffer(nil)
1415 err = headerTemplate.Execute(buf, params)
1416 if err != nil {
1417 return err
1418 }
1419
1420 return nw.Comment(buf.String())
1421}
1422
1423func (c *Context) writeNinjaRequiredVersion(nw *ninjaWriter) error {
1424 value := fmt.Sprintf("%d.%d.%d", c.requiredNinjaMajor, c.requiredNinjaMinor,
1425 c.requiredNinjaMicro)
1426
1427 err := nw.Assign("ninja_required_version", value)
1428 if err != nil {
1429 return err
1430 }
1431
1432 return nw.BlankLine()
1433}
1434
1435func (c *Context) writeBuildDir(nw *ninjaWriter) error {
1436 if c.buildDir != nil {
1437 err := nw.Assign("builddir", c.buildDir.Value(c.pkgNames))
1438 if err != nil {
1439 return err
1440 }
1441
1442 err = nw.BlankLine()
1443 if err != nil {
1444 return err
1445 }
1446 }
1447 return nil
1448}
1449
Jamie Gennisc15544d2014-09-24 20:26:52 -07001450type globalEntity interface {
Jamie Gennis2fb20952014-10-03 02:49:58 -07001451 fullName(pkgNames map[*PackageContext]string) string
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001452}
1453
Jamie Gennisc15544d2014-09-24 20:26:52 -07001454type globalEntitySorter struct {
Jamie Gennis2fb20952014-10-03 02:49:58 -07001455 pkgNames map[*PackageContext]string
Jamie Gennisc15544d2014-09-24 20:26:52 -07001456 entities []globalEntity
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001457}
1458
Jamie Gennisc15544d2014-09-24 20:26:52 -07001459func (s *globalEntitySorter) Len() int {
1460 return len(s.entities)
1461}
1462
1463func (s *globalEntitySorter) Less(i, j int) bool {
1464 iName := s.entities[i].fullName(s.pkgNames)
1465 jName := s.entities[j].fullName(s.pkgNames)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001466 return iName < jName
1467}
1468
Jamie Gennisc15544d2014-09-24 20:26:52 -07001469func (s *globalEntitySorter) Swap(i, j int) {
1470 s.entities[i], s.entities[j] = s.entities[j], s.entities[i]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001471}
1472
1473func (c *Context) writeGlobalVariables(nw *ninjaWriter) error {
1474 visited := make(map[Variable]bool)
1475
1476 var walk func(v Variable) error
1477 walk = func(v Variable) error {
1478 visited[v] = true
1479
1480 // First visit variables on which this variable depends.
1481 value := c.globalVariables[v]
1482 for _, dep := range value.variables {
1483 if !visited[dep] {
1484 err := walk(dep)
1485 if err != nil {
1486 return err
1487 }
1488 }
1489 }
1490
1491 err := nw.Assign(v.fullName(c.pkgNames), value.Value(c.pkgNames))
1492 if err != nil {
1493 return err
1494 }
1495
1496 err = nw.BlankLine()
1497 if err != nil {
1498 return err
1499 }
1500
1501 return nil
1502 }
1503
Jamie Gennisc15544d2014-09-24 20:26:52 -07001504 globalVariables := make([]globalEntity, 0, len(c.globalVariables))
1505 for variable := range c.globalVariables {
1506 globalVariables = append(globalVariables, variable)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001507 }
1508
Jamie Gennisc15544d2014-09-24 20:26:52 -07001509 sort.Sort(&globalEntitySorter{c.pkgNames, globalVariables})
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001510
Jamie Gennisc15544d2014-09-24 20:26:52 -07001511 for _, entity := range globalVariables {
1512 v := entity.(Variable)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001513 if !visited[v] {
1514 err := walk(v)
1515 if err != nil {
1516 return nil
1517 }
1518 }
1519 }
1520
1521 return nil
1522}
1523
1524func (c *Context) writeGlobalPools(nw *ninjaWriter) error {
Jamie Gennisc15544d2014-09-24 20:26:52 -07001525 globalPools := make([]globalEntity, 0, len(c.globalPools))
1526 for pool := range c.globalPools {
1527 globalPools = append(globalPools, pool)
1528 }
1529
1530 sort.Sort(&globalEntitySorter{c.pkgNames, globalPools})
1531
1532 for _, entity := range globalPools {
1533 pool := entity.(Pool)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001534 name := pool.fullName(c.pkgNames)
Jamie Gennisc15544d2014-09-24 20:26:52 -07001535 def := c.globalPools[pool]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001536 err := def.WriteTo(nw, name)
1537 if err != nil {
1538 return err
1539 }
1540
1541 err = nw.BlankLine()
1542 if err != nil {
1543 return err
1544 }
1545 }
1546
1547 return nil
1548}
1549
1550func (c *Context) writeGlobalRules(nw *ninjaWriter) error {
Jamie Gennisc15544d2014-09-24 20:26:52 -07001551 globalRules := make([]globalEntity, 0, len(c.globalRules))
1552 for rule := range c.globalRules {
1553 globalRules = append(globalRules, rule)
1554 }
1555
1556 sort.Sort(&globalEntitySorter{c.pkgNames, globalRules})
1557
1558 for _, entity := range globalRules {
1559 rule := entity.(Rule)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001560 name := rule.fullName(c.pkgNames)
Jamie Gennisc15544d2014-09-24 20:26:52 -07001561 def := c.globalRules[rule]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001562 err := def.WriteTo(nw, name, c.pkgNames)
1563 if err != nil {
1564 return err
1565 }
1566
1567 err = nw.BlankLine()
1568 if err != nil {
1569 return err
1570 }
1571 }
1572
1573 return nil
1574}
1575
Jamie Gennis86179fe2014-06-11 16:27:16 -07001576type moduleInfoSorter []*moduleInfo
1577
1578func (s moduleInfoSorter) Len() int {
1579 return len(s)
1580}
1581
1582func (s moduleInfoSorter) Less(i, j int) bool {
1583 iName := s[i].properties.Name
1584 jName := s[j].properties.Name
1585 return iName < jName
1586}
1587
1588func (s moduleInfoSorter) Swap(i, j int) {
1589 s[i], s[j] = s[j], s[i]
1590}
1591
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001592func (c *Context) writeAllModuleActions(nw *ninjaWriter) error {
1593 headerTemplate := template.New("moduleHeader")
1594 _, err := headerTemplate.Parse(moduleHeaderTemplate)
1595 if err != nil {
1596 // This is a programming error.
1597 panic(err)
1598 }
1599
Jamie Gennis86179fe2014-06-11 16:27:16 -07001600 infos := make([]*moduleInfo, 0, len(c.moduleInfo))
1601 for _, info := range c.moduleInfo {
1602 infos = append(infos, info)
1603 }
1604 sort.Sort(moduleInfoSorter(infos))
1605
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001606 buf := bytes.NewBuffer(nil)
1607
Jamie Gennis86179fe2014-06-11 16:27:16 -07001608 for _, info := range infos {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001609 buf.Reset()
Jamie Gennis1ebd3b82014-06-04 15:33:08 -07001610
1611 // In order to make the bootstrap build manifest independent of the
1612 // build dir we need to output the Blueprints file locations in the
1613 // comments as paths relative to the source directory.
1614 relPos := info.pos
Jamie Gennisec701282014-06-12 20:06:31 -07001615 relPos.Filename = info.relBlueprintsFile
Jamie Gennis1ebd3b82014-06-04 15:33:08 -07001616
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001617 // Get the name and location of the factory function for the module.
1618 factory := c.moduleFactories[info.typeName]
1619 factoryFunc := runtime.FuncForPC(reflect.ValueOf(factory).Pointer())
1620 factoryName := factoryFunc.Name()
1621
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001622 infoMap := map[string]interface{}{
1623 "properties": info.properties,
1624 "typeName": info.typeName,
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001625 "goFactory": factoryName,
Jamie Gennis1ebd3b82014-06-04 15:33:08 -07001626 "pos": relPos,
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001627 }
1628 err = headerTemplate.Execute(buf, infoMap)
1629 if err != nil {
1630 return err
1631 }
1632
1633 err = nw.Comment(buf.String())
1634 if err != nil {
1635 return err
1636 }
1637
1638 err = nw.BlankLine()
1639 if err != nil {
1640 return err
1641 }
1642
1643 err = c.writeLocalBuildActions(nw, &info.actionDefs)
1644 if err != nil {
1645 return err
1646 }
1647
1648 err = nw.BlankLine()
1649 if err != nil {
1650 return err
1651 }
1652 }
1653
1654 return nil
1655}
1656
1657func (c *Context) writeAllSingletonActions(nw *ninjaWriter) error {
1658 headerTemplate := template.New("singletonHeader")
1659 _, err := headerTemplate.Parse(singletonHeaderTemplate)
1660 if err != nil {
1661 // This is a programming error.
1662 panic(err)
1663 }
1664
1665 buf := bytes.NewBuffer(nil)
1666
Jamie Gennis86179fe2014-06-11 16:27:16 -07001667 singletonNames := make([]string, 0, len(c.singletonInfo))
1668 for name := range c.singletonInfo {
1669 singletonNames = append(singletonNames, name)
1670 }
1671 sort.Strings(singletonNames)
1672
1673 for _, name := range singletonNames {
1674 info := c.singletonInfo[name]
1675
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001676 // Get the name of the factory function for the module.
1677 factory := info.factory
1678 factoryFunc := runtime.FuncForPC(reflect.ValueOf(factory).Pointer())
1679 factoryName := factoryFunc.Name()
1680
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001681 buf.Reset()
1682 infoMap := map[string]interface{}{
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001683 "name": name,
1684 "goFactory": factoryName,
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001685 }
1686 err = headerTemplate.Execute(buf, infoMap)
1687 if err != nil {
1688 return err
1689 }
1690
1691 err = nw.Comment(buf.String())
1692 if err != nil {
1693 return err
1694 }
1695
1696 err = nw.BlankLine()
1697 if err != nil {
1698 return err
1699 }
1700
1701 err = c.writeLocalBuildActions(nw, &info.actionDefs)
1702 if err != nil {
1703 return err
1704 }
1705
1706 err = nw.BlankLine()
1707 if err != nil {
1708 return err
1709 }
1710 }
1711
1712 return nil
1713}
1714
1715func (c *Context) writeLocalBuildActions(nw *ninjaWriter,
1716 defs *localBuildActions) error {
1717
1718 // Write the local variable assignments.
1719 for _, v := range defs.variables {
1720 // A localVariable doesn't need the package names or config to
1721 // determine its name or value.
1722 name := v.fullName(nil)
1723 value, err := v.value(nil)
1724 if err != nil {
1725 panic(err)
1726 }
1727 err = nw.Assign(name, value.Value(c.pkgNames))
1728 if err != nil {
1729 return err
1730 }
1731 }
1732
1733 if len(defs.variables) > 0 {
1734 err := nw.BlankLine()
1735 if err != nil {
1736 return err
1737 }
1738 }
1739
1740 // Write the local rules.
1741 for _, r := range defs.rules {
1742 // A localRule doesn't need the package names or config to determine
1743 // its name or definition.
1744 name := r.fullName(nil)
1745 def, err := r.def(nil)
1746 if err != nil {
1747 panic(err)
1748 }
1749
1750 err = def.WriteTo(nw, name, c.pkgNames)
1751 if err != nil {
1752 return err
1753 }
1754
1755 err = nw.BlankLine()
1756 if err != nil {
1757 return err
1758 }
1759 }
1760
1761 // Write the build definitions.
1762 for _, buildDef := range defs.buildDefs {
1763 err := buildDef.WriteTo(nw, c.pkgNames)
1764 if err != nil {
1765 return err
1766 }
1767
1768 if len(buildDef.Args) > 0 {
1769 err = nw.BlankLine()
1770 if err != nil {
1771 return err
1772 }
1773 }
1774 }
1775
1776 return nil
1777}
1778
1779var fileHeaderTemplate = `******************************************************************************
1780*** This file is generated and should not be edited ***
1781******************************************************************************
1782{{if .Pkgs}}
1783This file contains variables, rules, and pools with name prefixes indicating
1784they were generated by the following Go packages:
1785{{range .Pkgs}}
1786 {{.PkgName}} [from Go package {{.PkgPath}}]{{end}}{{end}}
1787
1788`
1789
1790var moduleHeaderTemplate = `# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
1791Module: {{.properties.Name}}
1792Type: {{.typeName}}
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001793Factory: {{.goFactory}}
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001794Defined: {{.pos}}
1795`
1796
1797var singletonHeaderTemplate = `# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
1798Singleton: {{.name}}
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001799Factory: {{.goFactory}}
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001800`