blob: e9fa84b2430f8652e47f98fd27571a5269dad74b [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
61 pkgNames map[*pkg]string
62 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
71}
72
Jamie Gennisd4e10182014-06-12 20:06:50 -070073// An Error describes a problem that was encountered that is related to a
74// particular location in a Blueprints file.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070075type Error struct {
Jamie Gennisd4e10182014-06-12 20:06:50 -070076 Err error // the error that occurred
77 Pos scanner.Position // the relevant Blueprints file location
Jamie Gennis1bc967e2014-05-27 16:34:41 -070078}
79
80type localBuildActions struct {
81 variables []*localVariable
82 rules []*localRule
83 buildDefs []*buildDef
84}
85
86type moduleInfo struct {
87 // set during Parse
Jamie Gennisec701282014-06-12 20:06:31 -070088 typeName string
Jamie Gennisec701282014-06-12 20:06:31 -070089 relBlueprintsFile string
90 pos scanner.Position
91 propertyPos map[string]scanner.Position
92 properties struct {
Romain Guy28529652014-08-12 17:50:11 -070093 Name string
94 Deps []string
95 Targets map[string][]*parser.Property
Jamie Gennis1bc967e2014-05-27 16:34:41 -070096 }
97
98 // set during ResolveDependencies
99 directDeps []Module
100
101 // set during PrepareBuildActions
102 actionDefs localBuildActions
103}
104
105type singletonInfo struct {
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700106 // set during RegisterSingletonType
107 factory SingletonFactory
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700108 singleton Singleton
109
110 // set during PrepareBuildActions
111 actionDefs localBuildActions
112}
113
Romain Guy28529652014-08-12 17:50:11 -0700114type TargetSelector interface {
115 SelectTarget() string
116}
117
118// Default target selector that simply returns the host OS name
119type goosTargetSelector struct {
120}
121
122func (g *goosTargetSelector) SelectTarget() string {
123 return runtime.GOOS
124}
125
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700126func (e *Error) Error() string {
127
128 return fmt.Sprintf("%s: %s", e.Pos, e.Err)
129}
130
Jamie Gennisd4e10182014-06-12 20:06:50 -0700131// NewContext creates a new Context object. The created context initially has
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700132// no module or singleton factories registered, so the RegisterModuleFactory and
133// RegisterSingletonFactory methods must be called before it can do anything
134// useful.
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700135func NewContext() *Context {
136 return &Context{
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700137 moduleFactories: make(map[string]ModuleFactory),
138 modules: make(map[string]Module),
139 moduleInfo: make(map[Module]*moduleInfo),
140 singletonInfo: make(map[string]*singletonInfo),
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700141 }
142}
143
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700144// A ModuleFactory function creates a new Module object. See the
145// Context.RegisterModuleType method for details about how a registered
146// ModuleFactory is used by a Context.
147type ModuleFactory func() (m Module, propertyStructs []interface{})
148
Jamie Gennisd4e10182014-06-12 20:06:50 -0700149// RegisterModuleType associates a module type name (which can appear in a
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700150// Blueprints file) with a Module factory function. When the given module type
151// name is encountered in a Blueprints file during parsing, the Module factory
152// is invoked to instantiate a new Module object to handle the build action
Jamie Gennisd4e10182014-06-12 20:06:50 -0700153// generation for the module.
154//
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700155// The module type names given here must be unique for the context. The factory
156// function should be a named function so that its package and name can be
157// included in the generated Ninja file for debugging purposes.
158//
159// The factory function returns two values. The first is the newly created
160// Module object. The second is a slice of pointers to that Module object's
161// properties structs. Each properties struct is examined when parsing a module
162// definition of this type in a Blueprints file. Exported fields of the
163// properties structs are automatically set to the property values specified in
164// the Blueprints file. The properties struct field names determine the name of
165// the Blueprints file properties that are used - the Blueprints property name
166// matches that of the properties struct field name with the first letter
167// converted to lower-case.
168//
169// The fields of the properties struct must be either []string, a string, or
170// bool. The Context will panic if a Module gets instantiated with a properties
171// struct containing a field that is not one these supported types.
172//
173// Any properties that appear in the Blueprints files that are not built-in
174// module properties (such as "name" and "deps") and do not have a corresponding
175// field in the returned module properties struct result in an error during the
176// Context's parse phase.
177//
178// As an example, the follow code:
179//
180// type myModule struct {
181// properties struct {
182// Foo string
183// Bar []string
184// }
185// }
186//
187// func NewMyModule() (blueprint.Module, []interface{}) {
188// module := new(myModule)
189// properties := &module.properties
190// return module, []interface{}{properties}
191// }
192//
193// func main() {
194// ctx := blueprint.NewContext()
195// ctx.RegisterModuleType("my_module", NewMyModule)
196// // ...
197// }
198//
199// would support parsing a module defined in a Blueprints file as follows:
200//
201// my_module {
202// name: "myName",
203// foo: "my foo string",
204// bar: ["my", "bar", "strings"],
205// }
206//
207func (c *Context) RegisterModuleType(name string, factory ModuleFactory) {
208 if _, present := c.moduleFactories[name]; present {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700209 panic(errors.New("module type name is already registered"))
210 }
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700211 c.moduleFactories[name] = factory
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700212}
213
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700214// A SingletonFactory function creates a new Singleton object. See the
215// Context.RegisterSingletonType method for details about how a registered
216// SingletonFactory is used by a Context.
217type SingletonFactory func() Singleton
218
219// RegisterSingletonType registers a singleton type that will be invoked to
220// generate build actions. Each registered singleton type is instantiated and
221// and invoked exactly once as part of the generate phase.
222//
223// The singleton type names given here must be unique for the context. The
224// factory function should be a named function so that its package and name can
225// be included in the generated Ninja file for debugging purposes.
226func (c *Context) RegisterSingletonType(name string, factory SingletonFactory) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700227 if _, present := c.singletonInfo[name]; present {
228 panic(errors.New("singleton name is already registered"))
229 }
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700230
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700231 c.singletonInfo[name] = &singletonInfo{
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700232 factory: factory,
233 singleton: factory(),
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700234 }
235}
236
237func singletonPkgPath(singleton Singleton) string {
238 typ := reflect.TypeOf(singleton)
239 for typ.Kind() == reflect.Ptr {
240 typ = typ.Elem()
241 }
242 return typ.PkgPath()
243}
244
245func singletonTypeName(singleton Singleton) string {
246 typ := reflect.TypeOf(singleton)
247 for typ.Kind() == reflect.Ptr {
248 typ = typ.Elem()
249 }
250 return typ.PkgPath() + "." + typ.Name()
251}
252
Jamie Gennisd4e10182014-06-12 20:06:50 -0700253// SetIgnoreUnknownModuleTypes sets the behavior of the context in the case
254// where it encounters an unknown module type while parsing Blueprints files. By
255// default, the context will report unknown module types as an error. If this
256// method is called with ignoreUnknownModuleTypes set to true then the context
257// will silently ignore unknown module types.
258//
259// This method should generally not be used. It exists to facilitate the
260// bootstrapping process.
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700261func (c *Context) SetIgnoreUnknownModuleTypes(ignoreUnknownModuleTypes bool) {
262 c.ignoreUnknownModuleTypes = ignoreUnknownModuleTypes
263}
264
Jamie Gennisd4e10182014-06-12 20:06:50 -0700265// Parse parses a single Blueprints file from r, creating Module objects for
266// each of the module definitions encountered. If the Blueprints file contains
267// an assignment to the "subdirs" variable, then the subdirectories listed are
268// returned in the subdirs first return value.
269//
270// rootDir specifies the path to the root directory of the source tree, while
271// filename specifies the path to the Blueprints file. These paths are used for
272// error reporting and for determining the module's directory.
273//
274// This method should probably not be used directly. It is provided to simplify
275// testing. Instead ParseBlueprintsFiles should be called to parse a set of
276// Blueprints files starting from a top-level Blueprints file.
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700277func (c *Context) Parse(rootDir, filename string, r io.Reader) (subdirs []string,
278 errs []error) {
279
280 c.dependenciesReady = false
281
Jamie Gennisec701282014-06-12 20:06:31 -0700282 relBlueprintsFile, err := filepath.Rel(rootDir, filename)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700283 if err != nil {
284 return nil, []error{err}
285 }
286
287 defs, errs := parser.Parse(filename, r)
288 if len(errs) > 0 {
289 for i, err := range errs {
290 if parseErr, ok := err.(*parser.ParseError); ok {
291 err = &Error{
292 Err: parseErr.Err,
293 Pos: parseErr.Pos,
294 }
295 errs[i] = err
296 }
297 }
298
299 // If there were any parse errors don't bother trying to interpret the
300 // result.
301 return nil, errs
302 }
303
304 for _, def := range defs {
305 var newErrs []error
306 switch def := def.(type) {
307 case *parser.Module:
Jamie Gennisec701282014-06-12 20:06:31 -0700308 newErrs = c.processModuleDef(def, relBlueprintsFile)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700309
310 case *parser.Assignment:
311 var newSubdirs []string
312 newSubdirs, newErrs = c.processAssignment(def)
313 if newSubdirs != nil {
314 subdirs = newSubdirs
315 }
316
317 default:
318 panic("unknown definition type")
319 }
320
321 if len(newErrs) > 0 {
322 errs = append(errs, newErrs...)
323 if len(errs) > maxErrors {
324 break
325 }
326 }
327 }
328
329 return subdirs, errs
330}
331
Jamie Gennisd4e10182014-06-12 20:06:50 -0700332// ParseBlueprintsFiles parses a set of Blueprints files starting with the file
333// at rootFile. When it encounters a Blueprints file with a set of subdirs
334// listed it recursively parses any Blueprints files found in those
335// subdirectories.
336//
337// If no errors are encountered while parsing the files, the list of paths on
338// which the future output will depend is returned. This list will include both
339// Blueprints file paths as well as directory paths for cases where wildcard
340// subdirs are found.
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700341func (c *Context) ParseBlueprintsFiles(rootFile string) (deps []string,
342 errs []error) {
343
344 rootDir := filepath.Dir(rootFile)
345
346 depsSet := map[string]bool{rootFile: true}
347 blueprints := []string{rootFile}
348
349 var file *os.File
350 defer func() {
351 if file != nil {
352 file.Close()
353 }
354 }()
355
356 var err error
357
358 for i := 0; i < len(blueprints); i++ {
359 if len(errs) > maxErrors {
360 return
361 }
362
363 filename := blueprints[i]
364 dir := filepath.Dir(filename)
365
366 file, err = os.Open(filename)
367 if err != nil {
368 errs = append(errs, &Error{Err: err})
369 continue
370 }
371
372 subdirs, newErrs := c.Parse(rootDir, filename, file)
373 if len(newErrs) > 0 {
374 errs = append(errs, newErrs...)
375 continue
376 }
377
378 err = file.Close()
379 if err != nil {
380 errs = append(errs, &Error{Err: err})
381 continue
382 }
383
384 // Add the subdirs to the list of directories to parse Blueprint files
385 // from.
386 for _, subdir := range subdirs {
387 subdir = filepath.Join(dir, subdir)
388 dirPart, filePart := filepath.Split(subdir)
389 dirPart = filepath.Clean(dirPart)
390
391 if filePart == "*" {
392 foundSubdirs, err := listSubdirs(dirPart)
393 if err != nil {
394 errs = append(errs, &Error{Err: err})
395 continue
396 }
397
398 for _, foundSubdir := range foundSubdirs {
399 subBlueprints := filepath.Join(dirPart, foundSubdir,
400 "Blueprints")
401
402 _, err := os.Stat(subBlueprints)
403 if os.IsNotExist(err) {
404 // There is no Blueprints file in this subdirectory. We
405 // need to add the directory to the list of dependencies
406 // so that if someone adds a Blueprints file in the
407 // future we'll pick it up.
408 depsSet[filepath.Dir(subBlueprints)] = true
409 } else if !depsSet[subBlueprints] {
410 // We haven't seen this Blueprints file before, so add
411 // it to our list.
412 depsSet[subBlueprints] = true
413 blueprints = append(blueprints, subBlueprints)
414 }
415 }
416
417 // We now depend on the directory itself because if any new
418 // subdirectories get added or removed we need to rebuild the
419 // Ninja manifest.
420 depsSet[dirPart] = true
421 } else {
422 subBlueprints := filepath.Join(subdir, "Blueprints")
423 if !depsSet[subBlueprints] {
424 depsSet[subBlueprints] = true
425 blueprints = append(blueprints, subBlueprints)
426 }
427 }
428 }
429 }
430
431 for dep := range depsSet {
432 deps = append(deps, dep)
433 }
434
435 return
436}
437
438func listSubdirs(dir string) ([]string, error) {
439 d, err := os.Open(dir)
440 if err != nil {
441 return nil, err
442 }
443 defer d.Close()
444
445 infos, err := d.Readdir(-1)
446 if err != nil {
447 return nil, err
448 }
449
450 var subdirs []string
451 for _, info := range infos {
452 if info.IsDir() {
453 subdirs = append(subdirs, info.Name())
454 }
455 }
456
457 return subdirs, nil
458}
459
460func (c *Context) processAssignment(
461 assignment *parser.Assignment) (subdirs []string, errs []error) {
462
463 if assignment.Name == "subdirs" {
464 switch assignment.Value.Type {
465 case parser.List:
466 subdirs = make([]string, 0, len(assignment.Value.ListValue))
467
468 for _, value := range assignment.Value.ListValue {
469 if value.Type != parser.String {
470 // The parser should not produce this.
471 panic("non-string value found in list")
472 }
473
474 dirPart, filePart := filepath.Split(value.StringValue)
475 if (filePart != "*" && strings.ContainsRune(filePart, '*')) ||
476 strings.ContainsRune(dirPart, '*') {
477
478 errs = append(errs, &Error{
479 Err: fmt.Errorf("subdirs may only wildcard whole " +
480 "directories"),
481 Pos: value.Pos,
482 })
483
484 continue
485 }
486
487 subdirs = append(subdirs, value.StringValue)
488 }
489
490 if len(errs) > 0 {
491 subdirs = nil
492 }
493
494 return
495
496 case parser.Bool, parser.String:
497 errs = []error{
498 &Error{
499 Err: fmt.Errorf("subdirs must be a list of strings"),
500 Pos: assignment.Pos,
501 },
502 }
503
504 return
505
506 default:
507 panic(fmt.Errorf("unknown value type: %d", assignment.Value.Type))
508 }
509 }
510
511 return nil, []error{
512 &Error{
513 Err: fmt.Errorf("only 'subdirs' assignment is supported"),
514 Pos: assignment.Pos,
515 },
516 }
517}
518
519func (c *Context) processModuleDef(moduleDef *parser.Module,
Jamie Gennisec701282014-06-12 20:06:31 -0700520 relBlueprintsFile string) []error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700521
522 typeName := moduleDef.Type
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700523 factory, ok := c.moduleFactories[typeName]
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700524 if !ok {
525 if c.ignoreUnknownModuleTypes {
526 return nil
527 }
528
Jamie Gennisd4c53d82014-06-22 17:02:55 -0700529 return []error{
530 &Error{
531 Err: fmt.Errorf("unrecognized module type %q", typeName),
532 Pos: moduleDef.Pos,
533 },
534 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700535 }
536
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700537 module, properties := factory()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700538 info := &moduleInfo{
Jamie Gennisec701282014-06-12 20:06:31 -0700539 typeName: typeName,
Jamie Gennisec701282014-06-12 20:06:31 -0700540 relBlueprintsFile: relBlueprintsFile,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700541 }
542
Jamie Gennisd4c53d82014-06-22 17:02:55 -0700543 properties = append(properties, &info.properties)
544
545 errs := unpackProperties(moduleDef.Properties, properties...)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700546 if len(errs) > 0 {
547 return errs
548 }
549
Romain Guy28529652014-08-12 17:50:11 -0700550 var targetName string
551 if selector, ok := module.(TargetSelector); ok {
552 targetName = selector.SelectTarget()
553 } else {
554 defaultSelector := goosTargetSelector{}
555 targetName = defaultSelector.SelectTarget()
556 }
557
558 if targetProperties, ok := info.properties.Targets[targetName]; ok {
559 errs = mergeProperties(targetProperties, properties...)
560 if len(errs) > 0 {
561 return errs
562 }
563 }
564
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700565 info.pos = moduleDef.Pos
566 info.propertyPos = make(map[string]scanner.Position)
567 for _, propertyDef := range moduleDef.Properties {
568 info.propertyPos[propertyDef.Name] = propertyDef.Pos
569 }
570
571 name := info.properties.Name
572 err := validateNinjaName(name)
573 if err != nil {
574 return []error{
575 &Error{
576 Err: fmt.Errorf("invalid module name %q: %s", err),
577 Pos: info.propertyPos["name"],
578 },
579 }
580 }
581
582 if first, present := c.modules[name]; present {
583 errs = append(errs, &Error{
584 Err: fmt.Errorf("module %q already defined", name),
585 Pos: moduleDef.Pos,
586 })
587 errs = append(errs, &Error{
588 Err: fmt.Errorf("<-- previous definition here"),
589 Pos: c.moduleInfo[first].pos,
590 })
591 if len(errs) >= maxErrors {
592 return errs
593 }
594 }
595
596 c.modules[name] = module
597 c.moduleInfo[module] = info
598
599 return nil
600}
601
Jamie Gennisd4e10182014-06-12 20:06:50 -0700602// ResolveDependencies checks that the dependencies specified by all of the
603// modules defined in the parsed Blueprints files are valid. This means that
604// the modules depended upon are defined and that no circular dependencies
605// exist.
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700606func (c *Context) ResolveDependencies() []error {
607 errs := c.resolveDependencies()
608 if len(errs) > 0 {
609 return errs
610 }
611
612 errs = c.checkForDependencyCycles()
613 if len(errs) > 0 {
614 return errs
615 }
616
617 c.dependenciesReady = true
618 return nil
619}
620
621// resolveDependencies populates the moduleInfo.directDeps list for every
622// module. In doing so it checks for missing dependencies and self-dependant
623// modules.
624func (c *Context) resolveDependencies() (errs []error) {
625 for _, info := range c.moduleInfo {
626 depNames := info.properties.Deps
627 info.directDeps = make([]Module, 0, len(depNames))
628 depsPos := info.propertyPos["deps"]
629
630 for _, depName := range depNames {
631 if depName == info.properties.Name {
632 errs = append(errs, &Error{
633 Err: fmt.Errorf("%q depends on itself", depName),
634 Pos: depsPos,
635 })
636 continue
637 }
638
639 dep, ok := c.modules[depName]
640 if !ok {
641 errs = append(errs, &Error{
642 Err: fmt.Errorf("%q depends on undefined module %q",
643 info.properties.Name, depName),
644 Pos: depsPos,
645 })
646 continue
647 }
648
649 info.directDeps = append(info.directDeps, dep)
650 }
651 }
652
653 return
654}
655
656// checkForDependencyCycles recursively walks the module dependency graph and
657// reports errors when it encounters dependency cycles. This should only be
658// called after resolveDependencies.
659func (c *Context) checkForDependencyCycles() (errs []error) {
660 visited := make(map[Module]bool) // modules that were already checked
661 checking := make(map[Module]bool) // modules actively being checked
662
663 var check func(m Module) []Module
664
665 check = func(m Module) []Module {
666 info := c.moduleInfo[m]
667
668 visited[m] = true
669 checking[m] = true
670 defer delete(checking, m)
671
672 for _, dep := range info.directDeps {
673 if checking[dep] {
674 // This is a cycle.
675 return []Module{dep, m}
676 }
677
678 if !visited[dep] {
679 cycle := check(dep)
680 if cycle != nil {
681 if cycle[0] == m {
682 // We are the "start" of the cycle, so we're responsible
683 // for generating the errors. The cycle list is in
684 // reverse order because all the 'check' calls append
685 // their own module to the list.
686 errs = append(errs, &Error{
687 Err: fmt.Errorf("encountered dependency cycle:"),
688 Pos: info.pos,
689 })
690
691 // Iterate backwards through the cycle list.
692 curInfo := info
693 for i := len(cycle) - 1; i >= 0; i-- {
694 nextInfo := c.moduleInfo[cycle[i]]
695 errs = append(errs, &Error{
696 Err: fmt.Errorf(" %q depends on %q",
697 curInfo.properties.Name,
698 nextInfo.properties.Name),
699 Pos: curInfo.propertyPos["deps"],
700 })
701 curInfo = nextInfo
702 }
703
704 // We can continue processing this module's children to
705 // find more cycles. Since all the modules that were
706 // part of the found cycle were marked as visited we
707 // won't run into that cycle again.
708 } else {
709 // We're not the "start" of the cycle, so we just append
710 // our module to the list and return it.
711 return append(cycle, m)
712 }
713 }
714 }
715 }
716
717 return nil
718 }
719
720 for _, module := range c.modules {
721 if !visited[module] {
722 cycle := check(module)
723 if cycle != nil {
724 panic("inconceivable!")
725 }
726 }
727 }
728
729 return
730}
731
Jamie Gennisd4e10182014-06-12 20:06:50 -0700732// PrepareBuildActions generates an internal representation of all the build
733// actions that need to be performed. This process involves invoking the
734// GenerateBuildActions method on each of the Module objects created during the
735// parse phase and then on each of the registered Singleton objects.
736//
737// If the ResolveDependencies method has not already been called it is called
738// automatically by this method.
739//
740// The config argument is made available to all of the Module and Singleton
741// objects via the Config method on the ModuleContext and SingletonContext
742// objects passed to GenerateBuildActions. It is also passed to the functions
743// specified via PoolFunc, RuleFunc, and VariableFunc so that they can compute
744// config-specific values.
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700745//
746// The returned deps is a list of the ninja files dependencies that were added
747// by the modules and singletons via the ModuleContext.AddNinjaFileDeps() and
748// SingletonContext.AddNinjaFileDeps() methods.
749func (c *Context) PrepareBuildActions(config interface{}) (deps []string, errs []error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700750 c.buildActionsReady = false
751
752 if !c.dependenciesReady {
753 errs := c.ResolveDependencies()
754 if len(errs) > 0 {
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700755 return nil, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700756 }
757 }
758
759 liveGlobals := newLiveTracker(config)
760
761 c.initSpecialVariables()
762
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700763 depsModules, errs := c.generateModuleBuildActions(config, liveGlobals)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700764 if len(errs) > 0 {
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700765 return nil, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700766 }
767
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700768 depsSingletons, errs := c.generateSingletonBuildActions(config, liveGlobals)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700769 if len(errs) > 0 {
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700770 return nil, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700771 }
772
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700773 deps = append(depsModules, depsSingletons...)
774
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700775 if c.buildDir != nil {
776 liveGlobals.addNinjaStringDeps(c.buildDir)
777 }
778
779 pkgNames := c.makeUniquePackageNames(liveGlobals)
780
781 // This will panic if it finds a problem since it's a programming error.
782 c.checkForVariableReferenceCycles(liveGlobals.variables, pkgNames)
783
784 c.pkgNames = pkgNames
785 c.globalVariables = liveGlobals.variables
786 c.globalPools = liveGlobals.pools
787 c.globalRules = liveGlobals.rules
788
789 c.buildActionsReady = true
790
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700791 return deps, nil
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700792}
793
794func (c *Context) initSpecialVariables() {
795 c.buildDir = nil
796 c.requiredNinjaMajor = 1
797 c.requiredNinjaMinor = 1
798 c.requiredNinjaMicro = 0
799}
800
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700801func (c *Context) generateModuleBuildActions(config interface{},
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700802 liveGlobals *liveTracker) ([]string, []error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700803
804 visited := make(map[Module]bool)
805
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700806 var deps []string
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700807 var errs []error
808
809 var walk func(module Module)
810 walk = func(module Module) {
811 visited[module] = true
812
813 info := c.moduleInfo[module]
814 for _, dep := range info.directDeps {
815 if !visited[dep] {
816 walk(dep)
Jamie Gennisae4430c2014-07-23 14:37:21 -0700817 if len(errs) > 0 {
818 return
819 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700820 }
821 }
822
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700823 // The parent scope of the moduleContext's local scope gets overridden to be that of the
824 // calling Go package on a per-call basis. Since the initial parent scope doesn't matter we
825 // just set it to nil.
826 scope := newLocalScope(nil, moduleNamespacePrefix(info.properties.Name))
827
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700828 mctx := &moduleContext{
829 context: c,
830 config: config,
831 module: module,
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700832 scope: scope,
833 info: info,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700834 }
835
836 module.GenerateBuildActions(mctx)
837
838 if len(mctx.errs) > 0 {
839 errs = append(errs, mctx.errs...)
840 return
841 }
842
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700843 deps = append(deps, mctx.ninjaFileDeps...)
844
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700845 newErrs := c.processLocalBuildActions(&info.actionDefs,
846 &mctx.actionDefs, liveGlobals)
847 errs = append(errs, newErrs...)
848 }
849
850 for _, module := range c.modules {
851 if !visited[module] {
852 walk(module)
Jamie Gennisae4430c2014-07-23 14:37:21 -0700853 if len(errs) > 0 {
854 break
855 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700856 }
857 }
858
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700859 return deps, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700860}
861
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700862func (c *Context) generateSingletonBuildActions(config interface{},
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700863 liveGlobals *liveTracker) ([]string, []error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700864
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700865 var deps []string
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700866 var errs []error
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700867
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700868 for name, info := range c.singletonInfo {
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700869 // The parent scope of the singletonContext's local scope gets overridden to be that of the
870 // calling Go package on a per-call basis. Since the initial parent scope doesn't matter we
871 // just set it to nil.
872 scope := newLocalScope(nil, singletonNamespacePrefix(name))
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700873
874 sctx := &singletonContext{
875 context: c,
876 config: config,
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700877 scope: scope,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700878 }
879
880 info.singleton.GenerateBuildActions(sctx)
881
882 if len(sctx.errs) > 0 {
883 errs = append(errs, sctx.errs...)
884 if len(errs) > maxErrors {
885 break
886 }
887 continue
888 }
889
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700890 deps = append(deps, sctx.ninjaFileDeps...)
891
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700892 newErrs := c.processLocalBuildActions(&info.actionDefs,
893 &sctx.actionDefs, liveGlobals)
894 errs = append(errs, newErrs...)
895 if len(errs) > maxErrors {
896 break
897 }
898 }
899
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700900 return deps, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700901}
902
903func (c *Context) processLocalBuildActions(out, in *localBuildActions,
904 liveGlobals *liveTracker) []error {
905
906 var errs []error
907
908 // First we go through and add everything referenced by the module's
909 // buildDefs to the live globals set. This will end up adding the live
910 // locals to the set as well, but we'll take them out after.
911 for _, def := range in.buildDefs {
912 err := liveGlobals.AddBuildDefDeps(def)
913 if err != nil {
914 errs = append(errs, err)
915 }
916 }
917
918 if len(errs) > 0 {
919 return errs
920 }
921
922 out.buildDefs = in.buildDefs
923
924 // We use the now-incorrect set of live "globals" to determine which local
925 // definitions are live. As we go through copying those live locals to the
926 // moduleInfo we remove them from the live globals set.
927 out.variables = nil
928 for _, v := range in.variables {
929 _, isLive := liveGlobals.variables[v]
930 if isLive {
931 out.variables = append(out.variables, v)
932 delete(liveGlobals.variables, v)
933 }
934 }
935
936 out.rules = nil
937 for _, r := range in.rules {
938 _, isLive := liveGlobals.rules[r]
939 if isLive {
940 out.rules = append(out.rules, r)
941 delete(liveGlobals.rules, r)
942 }
943 }
944
945 return nil
946}
947
948func (c *Context) visitDepsDepthFirst(module Module, visit func(Module)) {
949 visited := make(map[Module]bool)
950
951 var walk func(m Module)
952 walk = func(m Module) {
953 info := c.moduleInfo[m]
954 visited[m] = true
955 for _, dep := range info.directDeps {
956 if !visited[dep] {
957 walk(dep)
958 }
959 }
960 visit(m)
961 }
962
963 info := c.moduleInfo[module]
964 for _, dep := range info.directDeps {
965 if !visited[dep] {
966 walk(dep)
967 }
968 }
969}
970
971func (c *Context) visitDepsDepthFirstIf(module Module, pred func(Module) bool,
972 visit func(Module)) {
973
974 visited := make(map[Module]bool)
975
976 var walk func(m Module)
977 walk = func(m Module) {
978 info := c.moduleInfo[m]
979 visited[m] = true
980 if pred(m) {
981 for _, dep := range info.directDeps {
982 if !visited[dep] {
983 walk(dep)
984 }
985 }
986 visit(m)
987 }
988 }
989
990 info := c.moduleInfo[module]
991 for _, dep := range info.directDeps {
992 if !visited[dep] {
993 walk(dep)
994 }
995 }
996}
997
998func (c *Context) visitAllModules(visit func(Module)) {
999 for _, module := range c.modules {
1000 visit(module)
1001 }
1002}
1003
1004func (c *Context) visitAllModulesIf(pred func(Module) bool,
1005 visit func(Module)) {
1006
1007 for _, module := range c.modules {
1008 if pred(module) {
1009 visit(module)
1010 }
1011 }
1012}
1013
1014func (c *Context) requireNinjaVersion(major, minor, micro int) {
1015 if major != 1 {
1016 panic("ninja version with major version != 1 not supported")
1017 }
1018 if c.requiredNinjaMinor < minor {
1019 c.requiredNinjaMinor = minor
1020 c.requiredNinjaMicro = micro
1021 }
1022 if c.requiredNinjaMinor == minor && c.requiredNinjaMicro < micro {
1023 c.requiredNinjaMicro = micro
1024 }
1025}
1026
1027func (c *Context) setBuildDir(value *ninjaString) {
1028 if c.buildDir != nil {
1029 panic("buildDir set multiple times")
1030 }
1031 c.buildDir = value
1032}
1033
1034func (c *Context) makeUniquePackageNames(
1035 liveGlobals *liveTracker) map[*pkg]string {
1036
1037 pkgs := make(map[string]*pkg)
1038 pkgNames := make(map[*pkg]string)
1039 longPkgNames := make(map[*pkg]bool)
1040
1041 processPackage := func(pkg *pkg) {
1042 if pkg == nil {
1043 // This is a built-in rule and has no package.
1044 return
1045 }
1046 if _, ok := pkgNames[pkg]; ok {
1047 // We've already processed this package.
1048 return
1049 }
1050
1051 otherPkg, present := pkgs[pkg.shortName]
1052 if present {
1053 // Short name collision. Both this package and the one that's
1054 // already there need to use their full names. We leave the short
1055 // name in pkgNames for now so future collisions still get caught.
1056 longPkgNames[pkg] = true
1057 longPkgNames[otherPkg] = true
1058 } else {
1059 // No collision so far. Tentatively set the package's name to be
1060 // its short name.
1061 pkgNames[pkg] = pkg.shortName
1062 }
1063 }
1064
1065 // We try to give all packages their short name, but when we get collisions
1066 // we need to use the full unique package name.
1067 for v, _ := range liveGlobals.variables {
1068 processPackage(v.pkg())
1069 }
1070 for p, _ := range liveGlobals.pools {
1071 processPackage(p.pkg())
1072 }
1073 for r, _ := range liveGlobals.rules {
1074 processPackage(r.pkg())
1075 }
1076
1077 // Add the packages that had collisions using their full unique names. This
1078 // will overwrite any short names that were added in the previous step.
1079 for pkg := range longPkgNames {
1080 pkgNames[pkg] = pkg.fullName
1081 }
1082
1083 return pkgNames
1084}
1085
1086func (c *Context) checkForVariableReferenceCycles(
1087 variables map[Variable]*ninjaString, pkgNames map[*pkg]string) {
1088
1089 visited := make(map[Variable]bool) // variables that were already checked
1090 checking := make(map[Variable]bool) // variables actively being checked
1091
1092 var check func(v Variable) []Variable
1093
1094 check = func(v Variable) []Variable {
1095 visited[v] = true
1096 checking[v] = true
1097 defer delete(checking, v)
1098
1099 value := variables[v]
1100 for _, dep := range value.variables {
1101 if checking[dep] {
1102 // This is a cycle.
1103 return []Variable{dep, v}
1104 }
1105
1106 if !visited[dep] {
1107 cycle := check(dep)
1108 if cycle != nil {
1109 if cycle[0] == v {
1110 // We are the "start" of the cycle, so we're responsible
1111 // for generating the errors. The cycle list is in
1112 // reverse order because all the 'check' calls append
1113 // their own module to the list.
1114 msgs := []string{"detected variable reference cycle:"}
1115
1116 // Iterate backwards through the cycle list.
1117 curName := v.fullName(pkgNames)
1118 curValue := value.Value(pkgNames)
1119 for i := len(cycle) - 1; i >= 0; i-- {
1120 next := cycle[i]
1121 nextName := next.fullName(pkgNames)
1122 nextValue := variables[next].Value(pkgNames)
1123
1124 msgs = append(msgs, fmt.Sprintf(
1125 " %q depends on %q", curName, nextName))
1126 msgs = append(msgs, fmt.Sprintf(
1127 " [%s = %s]", curName, curValue))
1128
1129 curName = nextName
1130 curValue = nextValue
1131 }
1132
1133 // Variable reference cycles are a programming error,
1134 // not the fault of the Blueprint file authors.
1135 panic(strings.Join(msgs, "\n"))
1136 } else {
1137 // We're not the "start" of the cycle, so we just append
1138 // our module to the list and return it.
1139 return append(cycle, v)
1140 }
1141 }
1142 }
1143 }
1144
1145 return nil
1146 }
1147
1148 for v := range variables {
1149 if !visited[v] {
1150 cycle := check(v)
1151 if cycle != nil {
1152 panic("inconceivable!")
1153 }
1154 }
1155 }
1156}
1157
Jamie Gennisd4e10182014-06-12 20:06:50 -07001158// WriteBuildFile writes the Ninja manifeset text for the generated build
1159// actions to w. If this is called before PrepareBuildActions successfully
1160// completes then ErrBuildActionsNotReady is returned.
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001161func (c *Context) WriteBuildFile(w io.Writer) error {
1162 if !c.buildActionsReady {
1163 return ErrBuildActionsNotReady
1164 }
1165
1166 nw := newNinjaWriter(w)
1167
1168 err := c.writeBuildFileHeader(nw)
1169 if err != nil {
1170 return err
1171 }
1172
1173 err = c.writeNinjaRequiredVersion(nw)
1174 if err != nil {
1175 return err
1176 }
1177
1178 // TODO: Group the globals by package.
1179
1180 err = c.writeGlobalVariables(nw)
1181 if err != nil {
1182 return err
1183 }
1184
1185 err = c.writeGlobalPools(nw)
1186 if err != nil {
1187 return err
1188 }
1189
1190 err = c.writeBuildDir(nw)
1191 if err != nil {
1192 return err
1193 }
1194
1195 err = c.writeGlobalRules(nw)
1196 if err != nil {
1197 return err
1198 }
1199
1200 err = c.writeAllModuleActions(nw)
1201 if err != nil {
1202 return err
1203 }
1204
1205 err = c.writeAllSingletonActions(nw)
1206 if err != nil {
1207 return err
1208 }
1209
1210 return nil
1211}
1212
1213func (c *Context) writeBuildFileHeader(nw *ninjaWriter) error {
1214 headerTemplate := template.New("fileHeader")
1215 _, err := headerTemplate.Parse(fileHeaderTemplate)
1216 if err != nil {
1217 // This is a programming error.
1218 panic(err)
1219 }
1220
1221 type pkgAssociation struct {
1222 PkgName string
1223 PkgPath string
1224 }
1225
1226 var pkgs []pkgAssociation
1227 maxNameLen := 0
1228 for pkg, name := range c.pkgNames {
1229 pkgs = append(pkgs, pkgAssociation{
1230 PkgName: name,
1231 PkgPath: pkg.pkgPath,
1232 })
1233 if len(name) > maxNameLen {
1234 maxNameLen = len(name)
1235 }
1236 }
1237
1238 for i := range pkgs {
1239 pkgs[i].PkgName += strings.Repeat(" ", maxNameLen-len(pkgs[i].PkgName))
1240 }
1241
1242 params := map[string]interface{}{
1243 "Pkgs": pkgs,
1244 }
1245
1246 buf := bytes.NewBuffer(nil)
1247 err = headerTemplate.Execute(buf, params)
1248 if err != nil {
1249 return err
1250 }
1251
1252 return nw.Comment(buf.String())
1253}
1254
1255func (c *Context) writeNinjaRequiredVersion(nw *ninjaWriter) error {
1256 value := fmt.Sprintf("%d.%d.%d", c.requiredNinjaMajor, c.requiredNinjaMinor,
1257 c.requiredNinjaMicro)
1258
1259 err := nw.Assign("ninja_required_version", value)
1260 if err != nil {
1261 return err
1262 }
1263
1264 return nw.BlankLine()
1265}
1266
1267func (c *Context) writeBuildDir(nw *ninjaWriter) error {
1268 if c.buildDir != nil {
1269 err := nw.Assign("builddir", c.buildDir.Value(c.pkgNames))
1270 if err != nil {
1271 return err
1272 }
1273
1274 err = nw.BlankLine()
1275 if err != nil {
1276 return err
1277 }
1278 }
1279 return nil
1280}
1281
1282type variableSorter struct {
1283 pkgNames map[*pkg]string
1284 variables []Variable
1285}
1286
1287func (v *variableSorter) Len() int {
1288 return len(v.variables)
1289}
1290
1291func (v *variableSorter) Less(i, j int) bool {
1292 iName := v.variables[i].fullName(v.pkgNames)
1293 jName := v.variables[j].fullName(v.pkgNames)
1294 return iName < jName
1295}
1296
1297func (v *variableSorter) Swap(i, j int) {
1298 v.variables[i], v.variables[j] = v.variables[j], v.variables[i]
1299}
1300
1301func (c *Context) writeGlobalVariables(nw *ninjaWriter) error {
1302 visited := make(map[Variable]bool)
1303
1304 var walk func(v Variable) error
1305 walk = func(v Variable) error {
1306 visited[v] = true
1307
1308 // First visit variables on which this variable depends.
1309 value := c.globalVariables[v]
1310 for _, dep := range value.variables {
1311 if !visited[dep] {
1312 err := walk(dep)
1313 if err != nil {
1314 return err
1315 }
1316 }
1317 }
1318
1319 err := nw.Assign(v.fullName(c.pkgNames), value.Value(c.pkgNames))
1320 if err != nil {
1321 return err
1322 }
1323
1324 err = nw.BlankLine()
1325 if err != nil {
1326 return err
1327 }
1328
1329 return nil
1330 }
1331
1332 globalVariables := make([]Variable, 0, len(c.globalVariables))
1333 for v := range c.globalVariables {
1334 globalVariables = append(globalVariables, v)
1335 }
1336
1337 sort.Sort(&variableSorter{c.pkgNames, globalVariables})
1338
1339 for _, v := range globalVariables {
1340 if !visited[v] {
1341 err := walk(v)
1342 if err != nil {
1343 return nil
1344 }
1345 }
1346 }
1347
1348 return nil
1349}
1350
1351func (c *Context) writeGlobalPools(nw *ninjaWriter) error {
1352 for pool, def := range c.globalPools {
1353 name := pool.fullName(c.pkgNames)
1354 err := def.WriteTo(nw, name)
1355 if err != nil {
1356 return err
1357 }
1358
1359 err = nw.BlankLine()
1360 if err != nil {
1361 return err
1362 }
1363 }
1364
1365 return nil
1366}
1367
1368func (c *Context) writeGlobalRules(nw *ninjaWriter) error {
1369 for rule, def := range c.globalRules {
1370 name := rule.fullName(c.pkgNames)
1371 err := def.WriteTo(nw, name, c.pkgNames)
1372 if err != nil {
1373 return err
1374 }
1375
1376 err = nw.BlankLine()
1377 if err != nil {
1378 return err
1379 }
1380 }
1381
1382 return nil
1383}
1384
Jamie Gennis86179fe2014-06-11 16:27:16 -07001385type moduleInfoSorter []*moduleInfo
1386
1387func (s moduleInfoSorter) Len() int {
1388 return len(s)
1389}
1390
1391func (s moduleInfoSorter) Less(i, j int) bool {
1392 iName := s[i].properties.Name
1393 jName := s[j].properties.Name
1394 return iName < jName
1395}
1396
1397func (s moduleInfoSorter) Swap(i, j int) {
1398 s[i], s[j] = s[j], s[i]
1399}
1400
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001401func (c *Context) writeAllModuleActions(nw *ninjaWriter) error {
1402 headerTemplate := template.New("moduleHeader")
1403 _, err := headerTemplate.Parse(moduleHeaderTemplate)
1404 if err != nil {
1405 // This is a programming error.
1406 panic(err)
1407 }
1408
Jamie Gennis86179fe2014-06-11 16:27:16 -07001409 infos := make([]*moduleInfo, 0, len(c.moduleInfo))
1410 for _, info := range c.moduleInfo {
1411 infos = append(infos, info)
1412 }
1413 sort.Sort(moduleInfoSorter(infos))
1414
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001415 buf := bytes.NewBuffer(nil)
1416
Jamie Gennis86179fe2014-06-11 16:27:16 -07001417 for _, info := range infos {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001418 buf.Reset()
Jamie Gennis1ebd3b82014-06-04 15:33:08 -07001419
1420 // In order to make the bootstrap build manifest independent of the
1421 // build dir we need to output the Blueprints file locations in the
1422 // comments as paths relative to the source directory.
1423 relPos := info.pos
Jamie Gennisec701282014-06-12 20:06:31 -07001424 relPos.Filename = info.relBlueprintsFile
Jamie Gennis1ebd3b82014-06-04 15:33:08 -07001425
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001426 // Get the name and location of the factory function for the module.
1427 factory := c.moduleFactories[info.typeName]
1428 factoryFunc := runtime.FuncForPC(reflect.ValueOf(factory).Pointer())
1429 factoryName := factoryFunc.Name()
1430
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001431 infoMap := map[string]interface{}{
1432 "properties": info.properties,
1433 "typeName": info.typeName,
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001434 "goFactory": factoryName,
Jamie Gennis1ebd3b82014-06-04 15:33:08 -07001435 "pos": relPos,
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001436 }
1437 err = headerTemplate.Execute(buf, infoMap)
1438 if err != nil {
1439 return err
1440 }
1441
1442 err = nw.Comment(buf.String())
1443 if err != nil {
1444 return err
1445 }
1446
1447 err = nw.BlankLine()
1448 if err != nil {
1449 return err
1450 }
1451
1452 err = c.writeLocalBuildActions(nw, &info.actionDefs)
1453 if err != nil {
1454 return err
1455 }
1456
1457 err = nw.BlankLine()
1458 if err != nil {
1459 return err
1460 }
1461 }
1462
1463 return nil
1464}
1465
1466func (c *Context) writeAllSingletonActions(nw *ninjaWriter) error {
1467 headerTemplate := template.New("singletonHeader")
1468 _, err := headerTemplate.Parse(singletonHeaderTemplate)
1469 if err != nil {
1470 // This is a programming error.
1471 panic(err)
1472 }
1473
1474 buf := bytes.NewBuffer(nil)
1475
Jamie Gennis86179fe2014-06-11 16:27:16 -07001476 singletonNames := make([]string, 0, len(c.singletonInfo))
1477 for name := range c.singletonInfo {
1478 singletonNames = append(singletonNames, name)
1479 }
1480 sort.Strings(singletonNames)
1481
1482 for _, name := range singletonNames {
1483 info := c.singletonInfo[name]
1484
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001485 // Get the name of the factory function for the module.
1486 factory := info.factory
1487 factoryFunc := runtime.FuncForPC(reflect.ValueOf(factory).Pointer())
1488 factoryName := factoryFunc.Name()
1489
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001490 buf.Reset()
1491 infoMap := map[string]interface{}{
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001492 "name": name,
1493 "goFactory": factoryName,
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001494 }
1495 err = headerTemplate.Execute(buf, infoMap)
1496 if err != nil {
1497 return err
1498 }
1499
1500 err = nw.Comment(buf.String())
1501 if err != nil {
1502 return err
1503 }
1504
1505 err = nw.BlankLine()
1506 if err != nil {
1507 return err
1508 }
1509
1510 err = c.writeLocalBuildActions(nw, &info.actionDefs)
1511 if err != nil {
1512 return err
1513 }
1514
1515 err = nw.BlankLine()
1516 if err != nil {
1517 return err
1518 }
1519 }
1520
1521 return nil
1522}
1523
1524func (c *Context) writeLocalBuildActions(nw *ninjaWriter,
1525 defs *localBuildActions) error {
1526
1527 // Write the local variable assignments.
1528 for _, v := range defs.variables {
1529 // A localVariable doesn't need the package names or config to
1530 // determine its name or value.
1531 name := v.fullName(nil)
1532 value, err := v.value(nil)
1533 if err != nil {
1534 panic(err)
1535 }
1536 err = nw.Assign(name, value.Value(c.pkgNames))
1537 if err != nil {
1538 return err
1539 }
1540 }
1541
1542 if len(defs.variables) > 0 {
1543 err := nw.BlankLine()
1544 if err != nil {
1545 return err
1546 }
1547 }
1548
1549 // Write the local rules.
1550 for _, r := range defs.rules {
1551 // A localRule doesn't need the package names or config to determine
1552 // its name or definition.
1553 name := r.fullName(nil)
1554 def, err := r.def(nil)
1555 if err != nil {
1556 panic(err)
1557 }
1558
1559 err = def.WriteTo(nw, name, c.pkgNames)
1560 if err != nil {
1561 return err
1562 }
1563
1564 err = nw.BlankLine()
1565 if err != nil {
1566 return err
1567 }
1568 }
1569
1570 // Write the build definitions.
1571 for _, buildDef := range defs.buildDefs {
1572 err := buildDef.WriteTo(nw, c.pkgNames)
1573 if err != nil {
1574 return err
1575 }
1576
1577 if len(buildDef.Args) > 0 {
1578 err = nw.BlankLine()
1579 if err != nil {
1580 return err
1581 }
1582 }
1583 }
1584
1585 return nil
1586}
1587
1588var fileHeaderTemplate = `******************************************************************************
1589*** This file is generated and should not be edited ***
1590******************************************************************************
1591{{if .Pkgs}}
1592This file contains variables, rules, and pools with name prefixes indicating
1593they were generated by the following Go packages:
1594{{range .Pkgs}}
1595 {{.PkgName}} [from Go package {{.PkgPath}}]{{end}}{{end}}
1596
1597`
1598
1599var moduleHeaderTemplate = `# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
1600Module: {{.properties.Name}}
1601Type: {{.typeName}}
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001602Factory: {{.goFactory}}
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001603Defined: {{.pos}}
1604`
1605
1606var singletonHeaderTemplate = `# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
1607Singleton: {{.name}}
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001608Factory: {{.goFactory}}
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001609`