blob: 9fdcd4a0178655238493873796381ba0a4cda12d [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
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700796 depsModules, errs := c.generateModuleBuildActions(config, liveGlobals)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700797 if len(errs) > 0 {
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700798 return nil, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700799 }
800
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700801 depsSingletons, errs := c.generateSingletonBuildActions(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 deps = append(depsModules, depsSingletons...)
807
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700808 if c.buildDir != nil {
809 liveGlobals.addNinjaStringDeps(c.buildDir)
810 }
811
812 pkgNames := c.makeUniquePackageNames(liveGlobals)
813
814 // This will panic if it finds a problem since it's a programming error.
815 c.checkForVariableReferenceCycles(liveGlobals.variables, pkgNames)
816
817 c.pkgNames = pkgNames
818 c.globalVariables = liveGlobals.variables
819 c.globalPools = liveGlobals.pools
820 c.globalRules = liveGlobals.rules
821
822 c.buildActionsReady = true
823
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700824 return deps, nil
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700825}
826
827func (c *Context) initSpecialVariables() {
828 c.buildDir = nil
829 c.requiredNinjaMajor = 1
830 c.requiredNinjaMinor = 1
831 c.requiredNinjaMicro = 0
832}
833
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700834func (c *Context) generateModuleBuildActions(config interface{},
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700835 liveGlobals *liveTracker) ([]string, []error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700836
837 visited := make(map[Module]bool)
838
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700839 var deps []string
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700840 var errs []error
841
842 var walk func(module Module)
843 walk = func(module Module) {
844 visited[module] = true
845
846 info := c.moduleInfo[module]
847 for _, dep := range info.directDeps {
848 if !visited[dep] {
849 walk(dep)
Jamie Gennisae4430c2014-07-23 14:37:21 -0700850 if len(errs) > 0 {
851 return
852 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700853 }
854 }
855
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700856 // The parent scope of the moduleContext's local scope gets overridden to be that of the
857 // calling Go package on a per-call basis. Since the initial parent scope doesn't matter we
858 // just set it to nil.
859 scope := newLocalScope(nil, moduleNamespacePrefix(info.properties.Name))
860
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700861 mctx := &moduleContext{
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700862 dynamicDependerModuleContext: dynamicDependerModuleContext{
863 context: c,
864 config: config,
865 info: info,
866 },
867 module: module,
868 scope: scope,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700869 }
870
871 module.GenerateBuildActions(mctx)
872
873 if len(mctx.errs) > 0 {
874 errs = append(errs, mctx.errs...)
875 return
876 }
877
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700878 deps = append(deps, mctx.ninjaFileDeps...)
879
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700880 newErrs := c.processLocalBuildActions(&info.actionDefs,
881 &mctx.actionDefs, liveGlobals)
882 errs = append(errs, newErrs...)
883 }
884
885 for _, module := range c.modules {
886 if !visited[module] {
887 walk(module)
Jamie Gennisae4430c2014-07-23 14:37:21 -0700888 if len(errs) > 0 {
889 break
890 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700891 }
892 }
893
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700894 return deps, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700895}
896
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700897func (c *Context) generateSingletonBuildActions(config interface{},
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700898 liveGlobals *liveTracker) ([]string, []error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700899
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700900 var deps []string
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700901 var errs []error
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700902
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700903 for name, info := range c.singletonInfo {
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700904 // The parent scope of the singletonContext's local scope gets overridden to be that of the
905 // calling Go package on a per-call basis. Since the initial parent scope doesn't matter we
906 // just set it to nil.
907 scope := newLocalScope(nil, singletonNamespacePrefix(name))
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700908
909 sctx := &singletonContext{
910 context: c,
911 config: config,
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700912 scope: scope,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700913 }
914
915 info.singleton.GenerateBuildActions(sctx)
916
917 if len(sctx.errs) > 0 {
918 errs = append(errs, sctx.errs...)
919 if len(errs) > maxErrors {
920 break
921 }
922 continue
923 }
924
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700925 deps = append(deps, sctx.ninjaFileDeps...)
926
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700927 newErrs := c.processLocalBuildActions(&info.actionDefs,
928 &sctx.actionDefs, liveGlobals)
929 errs = append(errs, newErrs...)
930 if len(errs) > maxErrors {
931 break
932 }
933 }
934
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700935 return deps, errs
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700936}
937
938func (c *Context) processLocalBuildActions(out, in *localBuildActions,
939 liveGlobals *liveTracker) []error {
940
941 var errs []error
942
943 // First we go through and add everything referenced by the module's
944 // buildDefs to the live globals set. This will end up adding the live
945 // locals to the set as well, but we'll take them out after.
946 for _, def := range in.buildDefs {
947 err := liveGlobals.AddBuildDefDeps(def)
948 if err != nil {
949 errs = append(errs, err)
950 }
951 }
952
953 if len(errs) > 0 {
954 return errs
955 }
956
957 out.buildDefs = in.buildDefs
958
959 // We use the now-incorrect set of live "globals" to determine which local
960 // definitions are live. As we go through copying those live locals to the
961 // moduleInfo we remove them from the live globals set.
962 out.variables = nil
963 for _, v := range in.variables {
964 _, isLive := liveGlobals.variables[v]
965 if isLive {
966 out.variables = append(out.variables, v)
967 delete(liveGlobals.variables, v)
968 }
969 }
970
971 out.rules = nil
972 for _, r := range in.rules {
973 _, isLive := liveGlobals.rules[r]
974 if isLive {
975 out.rules = append(out.rules, r)
976 delete(liveGlobals.rules, r)
977 }
978 }
979
980 return nil
981}
982
983func (c *Context) visitDepsDepthFirst(module Module, visit func(Module)) {
984 visited := make(map[Module]bool)
985
986 var walk func(m Module)
987 walk = func(m Module) {
988 info := c.moduleInfo[m]
989 visited[m] = true
990 for _, dep := range info.directDeps {
991 if !visited[dep] {
992 walk(dep)
993 }
994 }
995 visit(m)
996 }
997
998 info := c.moduleInfo[module]
999 for _, dep := range info.directDeps {
1000 if !visited[dep] {
1001 walk(dep)
1002 }
1003 }
1004}
1005
1006func (c *Context) visitDepsDepthFirstIf(module Module, pred func(Module) bool,
1007 visit func(Module)) {
1008
1009 visited := make(map[Module]bool)
1010
1011 var walk func(m Module)
1012 walk = func(m Module) {
1013 info := c.moduleInfo[m]
1014 visited[m] = true
1015 if pred(m) {
1016 for _, dep := range info.directDeps {
1017 if !visited[dep] {
1018 walk(dep)
1019 }
1020 }
1021 visit(m)
1022 }
1023 }
1024
1025 info := c.moduleInfo[module]
1026 for _, dep := range info.directDeps {
1027 if !visited[dep] {
1028 walk(dep)
1029 }
1030 }
1031}
1032
Jamie Gennisc15544d2014-09-24 20:26:52 -07001033func (c *Context) sortedModuleNames() []string {
1034 if c.cachedSortedModuleNames == nil {
1035 c.cachedSortedModuleNames = make([]string, 0, len(c.modules))
1036 for moduleName := range c.modules {
1037 c.cachedSortedModuleNames = append(c.cachedSortedModuleNames,
1038 moduleName)
1039 }
1040 sort.Strings(c.cachedSortedModuleNames)
1041 }
1042
1043 return c.cachedSortedModuleNames
1044}
1045
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001046func (c *Context) visitAllModules(visit func(Module)) {
Jamie Gennisc15544d2014-09-24 20:26:52 -07001047 for _, moduleName := range c.sortedModuleNames() {
1048 module := c.modules[moduleName]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001049 visit(module)
1050 }
1051}
1052
1053func (c *Context) visitAllModulesIf(pred func(Module) bool,
1054 visit func(Module)) {
1055
Jamie Gennisc15544d2014-09-24 20:26:52 -07001056 for _, moduleName := range c.sortedModuleNames() {
1057 module := c.modules[moduleName]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001058 if pred(module) {
1059 visit(module)
1060 }
1061 }
1062}
1063
1064func (c *Context) requireNinjaVersion(major, minor, micro int) {
1065 if major != 1 {
1066 panic("ninja version with major version != 1 not supported")
1067 }
1068 if c.requiredNinjaMinor < minor {
1069 c.requiredNinjaMinor = minor
1070 c.requiredNinjaMicro = micro
1071 }
1072 if c.requiredNinjaMinor == minor && c.requiredNinjaMicro < micro {
1073 c.requiredNinjaMicro = micro
1074 }
1075}
1076
1077func (c *Context) setBuildDir(value *ninjaString) {
1078 if c.buildDir != nil {
1079 panic("buildDir set multiple times")
1080 }
1081 c.buildDir = value
1082}
1083
1084func (c *Context) makeUniquePackageNames(
Jamie Gennis2fb20952014-10-03 02:49:58 -07001085 liveGlobals *liveTracker) map[*PackageContext]string {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001086
Jamie Gennis2fb20952014-10-03 02:49:58 -07001087 pkgs := make(map[string]*PackageContext)
1088 pkgNames := make(map[*PackageContext]string)
1089 longPkgNames := make(map[*PackageContext]bool)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001090
Jamie Gennis2fb20952014-10-03 02:49:58 -07001091 processPackage := func(pctx *PackageContext) {
1092 if pctx == nil {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001093 // This is a built-in rule and has no package.
1094 return
1095 }
Jamie Gennis2fb20952014-10-03 02:49:58 -07001096 if _, ok := pkgNames[pctx]; ok {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001097 // We've already processed this package.
1098 return
1099 }
1100
Jamie Gennis2fb20952014-10-03 02:49:58 -07001101 otherPkg, present := pkgs[pctx.shortName]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001102 if present {
1103 // Short name collision. Both this package and the one that's
1104 // already there need to use their full names. We leave the short
1105 // name in pkgNames for now so future collisions still get caught.
Jamie Gennis2fb20952014-10-03 02:49:58 -07001106 longPkgNames[pctx] = true
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001107 longPkgNames[otherPkg] = true
1108 } else {
1109 // No collision so far. Tentatively set the package's name to be
1110 // its short name.
Jamie Gennis2fb20952014-10-03 02:49:58 -07001111 pkgNames[pctx] = pctx.shortName
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001112 }
1113 }
1114
1115 // We try to give all packages their short name, but when we get collisions
1116 // we need to use the full unique package name.
1117 for v, _ := range liveGlobals.variables {
Jamie Gennis2fb20952014-10-03 02:49:58 -07001118 processPackage(v.packageContext())
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001119 }
1120 for p, _ := range liveGlobals.pools {
Jamie Gennis2fb20952014-10-03 02:49:58 -07001121 processPackage(p.packageContext())
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001122 }
1123 for r, _ := range liveGlobals.rules {
Jamie Gennis2fb20952014-10-03 02:49:58 -07001124 processPackage(r.packageContext())
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001125 }
1126
1127 // Add the packages that had collisions using their full unique names. This
1128 // will overwrite any short names that were added in the previous step.
Jamie Gennis2fb20952014-10-03 02:49:58 -07001129 for pctx := range longPkgNames {
1130 pkgNames[pctx] = pctx.fullName
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001131 }
1132
1133 return pkgNames
1134}
1135
1136func (c *Context) checkForVariableReferenceCycles(
Jamie Gennis2fb20952014-10-03 02:49:58 -07001137 variables map[Variable]*ninjaString, pkgNames map[*PackageContext]string) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001138
1139 visited := make(map[Variable]bool) // variables that were already checked
1140 checking := make(map[Variable]bool) // variables actively being checked
1141
1142 var check func(v Variable) []Variable
1143
1144 check = func(v Variable) []Variable {
1145 visited[v] = true
1146 checking[v] = true
1147 defer delete(checking, v)
1148
1149 value := variables[v]
1150 for _, dep := range value.variables {
1151 if checking[dep] {
1152 // This is a cycle.
1153 return []Variable{dep, v}
1154 }
1155
1156 if !visited[dep] {
1157 cycle := check(dep)
1158 if cycle != nil {
1159 if cycle[0] == v {
1160 // We are the "start" of the cycle, so we're responsible
1161 // for generating the errors. The cycle list is in
1162 // reverse order because all the 'check' calls append
1163 // their own module to the list.
1164 msgs := []string{"detected variable reference cycle:"}
1165
1166 // Iterate backwards through the cycle list.
1167 curName := v.fullName(pkgNames)
1168 curValue := value.Value(pkgNames)
1169 for i := len(cycle) - 1; i >= 0; i-- {
1170 next := cycle[i]
1171 nextName := next.fullName(pkgNames)
1172 nextValue := variables[next].Value(pkgNames)
1173
1174 msgs = append(msgs, fmt.Sprintf(
1175 " %q depends on %q", curName, nextName))
1176 msgs = append(msgs, fmt.Sprintf(
1177 " [%s = %s]", curName, curValue))
1178
1179 curName = nextName
1180 curValue = nextValue
1181 }
1182
1183 // Variable reference cycles are a programming error,
1184 // not the fault of the Blueprint file authors.
1185 panic(strings.Join(msgs, "\n"))
1186 } else {
1187 // We're not the "start" of the cycle, so we just append
1188 // our module to the list and return it.
1189 return append(cycle, v)
1190 }
1191 }
1192 }
1193 }
1194
1195 return nil
1196 }
1197
1198 for v := range variables {
1199 if !visited[v] {
1200 cycle := check(v)
1201 if cycle != nil {
1202 panic("inconceivable!")
1203 }
1204 }
1205 }
1206}
1207
Jamie Gennisaf435562014-10-27 22:34:56 -07001208// AllTargets returns a map all the build target names to the rule used to build
1209// them. This is the same information that is output by running 'ninja -t
1210// targets all'. If this is called before PrepareBuildActions successfully
1211// completes then ErrbuildActionsNotReady is returned.
1212func (c *Context) AllTargets() (map[string]string, error) {
1213 if !c.buildActionsReady {
1214 return nil, ErrBuildActionsNotReady
1215 }
1216
1217 targets := map[string]string{}
1218
1219 // Collect all the module build targets.
1220 for _, info := range c.moduleInfo {
1221 for _, buildDef := range info.actionDefs.buildDefs {
1222 ruleName := buildDef.Rule.fullName(c.pkgNames)
1223 for _, output := range buildDef.Outputs {
1224 outputValue := output.Value(c.pkgNames)
1225 targets[outputValue] = ruleName
1226 }
1227 }
1228 }
1229
1230 // Collect all the singleton build targets.
1231 for _, info := range c.singletonInfo {
1232 for _, buildDef := range info.actionDefs.buildDefs {
1233 ruleName := buildDef.Rule.fullName(c.pkgNames)
1234 for _, output := range buildDef.Outputs {
1235 outputValue := output.Value(c.pkgNames)
1236 targets[outputValue] = ruleName
1237 }
1238 }
1239 }
1240
1241 return targets, nil
1242}
1243
Jamie Gennisd4e10182014-06-12 20:06:50 -07001244// WriteBuildFile writes the Ninja manifeset text for the generated build
1245// actions to w. If this is called before PrepareBuildActions successfully
1246// completes then ErrBuildActionsNotReady is returned.
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001247func (c *Context) WriteBuildFile(w io.Writer) error {
1248 if !c.buildActionsReady {
1249 return ErrBuildActionsNotReady
1250 }
1251
1252 nw := newNinjaWriter(w)
1253
1254 err := c.writeBuildFileHeader(nw)
1255 if err != nil {
1256 return err
1257 }
1258
1259 err = c.writeNinjaRequiredVersion(nw)
1260 if err != nil {
1261 return err
1262 }
1263
1264 // TODO: Group the globals by package.
1265
1266 err = c.writeGlobalVariables(nw)
1267 if err != nil {
1268 return err
1269 }
1270
1271 err = c.writeGlobalPools(nw)
1272 if err != nil {
1273 return err
1274 }
1275
1276 err = c.writeBuildDir(nw)
1277 if err != nil {
1278 return err
1279 }
1280
1281 err = c.writeGlobalRules(nw)
1282 if err != nil {
1283 return err
1284 }
1285
1286 err = c.writeAllModuleActions(nw)
1287 if err != nil {
1288 return err
1289 }
1290
1291 err = c.writeAllSingletonActions(nw)
1292 if err != nil {
1293 return err
1294 }
1295
1296 return nil
1297}
1298
Jamie Gennisc15544d2014-09-24 20:26:52 -07001299type pkgAssociation struct {
1300 PkgName string
1301 PkgPath string
1302}
1303
1304type pkgAssociationSorter struct {
1305 pkgs []pkgAssociation
1306}
1307
1308func (s *pkgAssociationSorter) Len() int {
1309 return len(s.pkgs)
1310}
1311
1312func (s *pkgAssociationSorter) Less(i, j int) bool {
1313 iName := s.pkgs[i].PkgName
1314 jName := s.pkgs[j].PkgName
1315 return iName < jName
1316}
1317
1318func (s *pkgAssociationSorter) Swap(i, j int) {
1319 s.pkgs[i], s.pkgs[j] = s.pkgs[j], s.pkgs[i]
1320}
1321
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001322func (c *Context) writeBuildFileHeader(nw *ninjaWriter) error {
1323 headerTemplate := template.New("fileHeader")
1324 _, err := headerTemplate.Parse(fileHeaderTemplate)
1325 if err != nil {
1326 // This is a programming error.
1327 panic(err)
1328 }
1329
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001330 var pkgs []pkgAssociation
1331 maxNameLen := 0
1332 for pkg, name := range c.pkgNames {
1333 pkgs = append(pkgs, pkgAssociation{
1334 PkgName: name,
1335 PkgPath: pkg.pkgPath,
1336 })
1337 if len(name) > maxNameLen {
1338 maxNameLen = len(name)
1339 }
1340 }
1341
1342 for i := range pkgs {
1343 pkgs[i].PkgName += strings.Repeat(" ", maxNameLen-len(pkgs[i].PkgName))
1344 }
1345
Jamie Gennisc15544d2014-09-24 20:26:52 -07001346 sort.Sort(&pkgAssociationSorter{pkgs})
1347
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001348 params := map[string]interface{}{
1349 "Pkgs": pkgs,
1350 }
1351
1352 buf := bytes.NewBuffer(nil)
1353 err = headerTemplate.Execute(buf, params)
1354 if err != nil {
1355 return err
1356 }
1357
1358 return nw.Comment(buf.String())
1359}
1360
1361func (c *Context) writeNinjaRequiredVersion(nw *ninjaWriter) error {
1362 value := fmt.Sprintf("%d.%d.%d", c.requiredNinjaMajor, c.requiredNinjaMinor,
1363 c.requiredNinjaMicro)
1364
1365 err := nw.Assign("ninja_required_version", value)
1366 if err != nil {
1367 return err
1368 }
1369
1370 return nw.BlankLine()
1371}
1372
1373func (c *Context) writeBuildDir(nw *ninjaWriter) error {
1374 if c.buildDir != nil {
1375 err := nw.Assign("builddir", c.buildDir.Value(c.pkgNames))
1376 if err != nil {
1377 return err
1378 }
1379
1380 err = nw.BlankLine()
1381 if err != nil {
1382 return err
1383 }
1384 }
1385 return nil
1386}
1387
Jamie Gennisc15544d2014-09-24 20:26:52 -07001388type globalEntity interface {
Jamie Gennis2fb20952014-10-03 02:49:58 -07001389 fullName(pkgNames map[*PackageContext]string) string
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001390}
1391
Jamie Gennisc15544d2014-09-24 20:26:52 -07001392type globalEntitySorter struct {
Jamie Gennis2fb20952014-10-03 02:49:58 -07001393 pkgNames map[*PackageContext]string
Jamie Gennisc15544d2014-09-24 20:26:52 -07001394 entities []globalEntity
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001395}
1396
Jamie Gennisc15544d2014-09-24 20:26:52 -07001397func (s *globalEntitySorter) Len() int {
1398 return len(s.entities)
1399}
1400
1401func (s *globalEntitySorter) Less(i, j int) bool {
1402 iName := s.entities[i].fullName(s.pkgNames)
1403 jName := s.entities[j].fullName(s.pkgNames)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001404 return iName < jName
1405}
1406
Jamie Gennisc15544d2014-09-24 20:26:52 -07001407func (s *globalEntitySorter) Swap(i, j int) {
1408 s.entities[i], s.entities[j] = s.entities[j], s.entities[i]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001409}
1410
1411func (c *Context) writeGlobalVariables(nw *ninjaWriter) error {
1412 visited := make(map[Variable]bool)
1413
1414 var walk func(v Variable) error
1415 walk = func(v Variable) error {
1416 visited[v] = true
1417
1418 // First visit variables on which this variable depends.
1419 value := c.globalVariables[v]
1420 for _, dep := range value.variables {
1421 if !visited[dep] {
1422 err := walk(dep)
1423 if err != nil {
1424 return err
1425 }
1426 }
1427 }
1428
1429 err := nw.Assign(v.fullName(c.pkgNames), value.Value(c.pkgNames))
1430 if err != nil {
1431 return err
1432 }
1433
1434 err = nw.BlankLine()
1435 if err != nil {
1436 return err
1437 }
1438
1439 return nil
1440 }
1441
Jamie Gennisc15544d2014-09-24 20:26:52 -07001442 globalVariables := make([]globalEntity, 0, len(c.globalVariables))
1443 for variable := range c.globalVariables {
1444 globalVariables = append(globalVariables, variable)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001445 }
1446
Jamie Gennisc15544d2014-09-24 20:26:52 -07001447 sort.Sort(&globalEntitySorter{c.pkgNames, globalVariables})
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001448
Jamie Gennisc15544d2014-09-24 20:26:52 -07001449 for _, entity := range globalVariables {
1450 v := entity.(Variable)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001451 if !visited[v] {
1452 err := walk(v)
1453 if err != nil {
1454 return nil
1455 }
1456 }
1457 }
1458
1459 return nil
1460}
1461
1462func (c *Context) writeGlobalPools(nw *ninjaWriter) error {
Jamie Gennisc15544d2014-09-24 20:26:52 -07001463 globalPools := make([]globalEntity, 0, len(c.globalPools))
1464 for pool := range c.globalPools {
1465 globalPools = append(globalPools, pool)
1466 }
1467
1468 sort.Sort(&globalEntitySorter{c.pkgNames, globalPools})
1469
1470 for _, entity := range globalPools {
1471 pool := entity.(Pool)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001472 name := pool.fullName(c.pkgNames)
Jamie Gennisc15544d2014-09-24 20:26:52 -07001473 def := c.globalPools[pool]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001474 err := def.WriteTo(nw, name)
1475 if err != nil {
1476 return err
1477 }
1478
1479 err = nw.BlankLine()
1480 if err != nil {
1481 return err
1482 }
1483 }
1484
1485 return nil
1486}
1487
1488func (c *Context) writeGlobalRules(nw *ninjaWriter) error {
Jamie Gennisc15544d2014-09-24 20:26:52 -07001489 globalRules := make([]globalEntity, 0, len(c.globalRules))
1490 for rule := range c.globalRules {
1491 globalRules = append(globalRules, rule)
1492 }
1493
1494 sort.Sort(&globalEntitySorter{c.pkgNames, globalRules})
1495
1496 for _, entity := range globalRules {
1497 rule := entity.(Rule)
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001498 name := rule.fullName(c.pkgNames)
Jamie Gennisc15544d2014-09-24 20:26:52 -07001499 def := c.globalRules[rule]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001500 err := def.WriteTo(nw, name, c.pkgNames)
1501 if err != nil {
1502 return err
1503 }
1504
1505 err = nw.BlankLine()
1506 if err != nil {
1507 return err
1508 }
1509 }
1510
1511 return nil
1512}
1513
Jamie Gennis86179fe2014-06-11 16:27:16 -07001514type moduleInfoSorter []*moduleInfo
1515
1516func (s moduleInfoSorter) Len() int {
1517 return len(s)
1518}
1519
1520func (s moduleInfoSorter) Less(i, j int) bool {
1521 iName := s[i].properties.Name
1522 jName := s[j].properties.Name
1523 return iName < jName
1524}
1525
1526func (s moduleInfoSorter) Swap(i, j int) {
1527 s[i], s[j] = s[j], s[i]
1528}
1529
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001530func (c *Context) writeAllModuleActions(nw *ninjaWriter) error {
1531 headerTemplate := template.New("moduleHeader")
1532 _, err := headerTemplate.Parse(moduleHeaderTemplate)
1533 if err != nil {
1534 // This is a programming error.
1535 panic(err)
1536 }
1537
Jamie Gennis86179fe2014-06-11 16:27:16 -07001538 infos := make([]*moduleInfo, 0, len(c.moduleInfo))
1539 for _, info := range c.moduleInfo {
1540 infos = append(infos, info)
1541 }
1542 sort.Sort(moduleInfoSorter(infos))
1543
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001544 buf := bytes.NewBuffer(nil)
1545
Jamie Gennis86179fe2014-06-11 16:27:16 -07001546 for _, info := range infos {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001547 buf.Reset()
Jamie Gennis1ebd3b82014-06-04 15:33:08 -07001548
1549 // In order to make the bootstrap build manifest independent of the
1550 // build dir we need to output the Blueprints file locations in the
1551 // comments as paths relative to the source directory.
1552 relPos := info.pos
Jamie Gennisec701282014-06-12 20:06:31 -07001553 relPos.Filename = info.relBlueprintsFile
Jamie Gennis1ebd3b82014-06-04 15:33:08 -07001554
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001555 // Get the name and location of the factory function for the module.
1556 factory := c.moduleFactories[info.typeName]
1557 factoryFunc := runtime.FuncForPC(reflect.ValueOf(factory).Pointer())
1558 factoryName := factoryFunc.Name()
1559
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001560 infoMap := map[string]interface{}{
1561 "properties": info.properties,
1562 "typeName": info.typeName,
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001563 "goFactory": factoryName,
Jamie Gennis1ebd3b82014-06-04 15:33:08 -07001564 "pos": relPos,
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001565 }
1566 err = headerTemplate.Execute(buf, infoMap)
1567 if err != nil {
1568 return err
1569 }
1570
1571 err = nw.Comment(buf.String())
1572 if err != nil {
1573 return err
1574 }
1575
1576 err = nw.BlankLine()
1577 if err != nil {
1578 return err
1579 }
1580
1581 err = c.writeLocalBuildActions(nw, &info.actionDefs)
1582 if err != nil {
1583 return err
1584 }
1585
1586 err = nw.BlankLine()
1587 if err != nil {
1588 return err
1589 }
1590 }
1591
1592 return nil
1593}
1594
1595func (c *Context) writeAllSingletonActions(nw *ninjaWriter) error {
1596 headerTemplate := template.New("singletonHeader")
1597 _, err := headerTemplate.Parse(singletonHeaderTemplate)
1598 if err != nil {
1599 // This is a programming error.
1600 panic(err)
1601 }
1602
1603 buf := bytes.NewBuffer(nil)
1604
Jamie Gennis86179fe2014-06-11 16:27:16 -07001605 singletonNames := make([]string, 0, len(c.singletonInfo))
1606 for name := range c.singletonInfo {
1607 singletonNames = append(singletonNames, name)
1608 }
1609 sort.Strings(singletonNames)
1610
1611 for _, name := range singletonNames {
1612 info := c.singletonInfo[name]
1613
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001614 // Get the name of the factory function for the module.
1615 factory := info.factory
1616 factoryFunc := runtime.FuncForPC(reflect.ValueOf(factory).Pointer())
1617 factoryName := factoryFunc.Name()
1618
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001619 buf.Reset()
1620 infoMap := map[string]interface{}{
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001621 "name": name,
1622 "goFactory": factoryName,
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001623 }
1624 err = headerTemplate.Execute(buf, infoMap)
1625 if err != nil {
1626 return err
1627 }
1628
1629 err = nw.Comment(buf.String())
1630 if err != nil {
1631 return err
1632 }
1633
1634 err = nw.BlankLine()
1635 if err != nil {
1636 return err
1637 }
1638
1639 err = c.writeLocalBuildActions(nw, &info.actionDefs)
1640 if err != nil {
1641 return err
1642 }
1643
1644 err = nw.BlankLine()
1645 if err != nil {
1646 return err
1647 }
1648 }
1649
1650 return nil
1651}
1652
1653func (c *Context) writeLocalBuildActions(nw *ninjaWriter,
1654 defs *localBuildActions) error {
1655
1656 // Write the local variable assignments.
1657 for _, v := range defs.variables {
1658 // A localVariable doesn't need the package names or config to
1659 // determine its name or value.
1660 name := v.fullName(nil)
1661 value, err := v.value(nil)
1662 if err != nil {
1663 panic(err)
1664 }
1665 err = nw.Assign(name, value.Value(c.pkgNames))
1666 if err != nil {
1667 return err
1668 }
1669 }
1670
1671 if len(defs.variables) > 0 {
1672 err := nw.BlankLine()
1673 if err != nil {
1674 return err
1675 }
1676 }
1677
1678 // Write the local rules.
1679 for _, r := range defs.rules {
1680 // A localRule doesn't need the package names or config to determine
1681 // its name or definition.
1682 name := r.fullName(nil)
1683 def, err := r.def(nil)
1684 if err != nil {
1685 panic(err)
1686 }
1687
1688 err = def.WriteTo(nw, name, c.pkgNames)
1689 if err != nil {
1690 return err
1691 }
1692
1693 err = nw.BlankLine()
1694 if err != nil {
1695 return err
1696 }
1697 }
1698
1699 // Write the build definitions.
1700 for _, buildDef := range defs.buildDefs {
1701 err := buildDef.WriteTo(nw, c.pkgNames)
1702 if err != nil {
1703 return err
1704 }
1705
1706 if len(buildDef.Args) > 0 {
1707 err = nw.BlankLine()
1708 if err != nil {
1709 return err
1710 }
1711 }
1712 }
1713
1714 return nil
1715}
1716
1717var fileHeaderTemplate = `******************************************************************************
1718*** This file is generated and should not be edited ***
1719******************************************************************************
1720{{if .Pkgs}}
1721This file contains variables, rules, and pools with name prefixes indicating
1722they were generated by the following Go packages:
1723{{range .Pkgs}}
1724 {{.PkgName}} [from Go package {{.PkgPath}}]{{end}}{{end}}
1725
1726`
1727
1728var moduleHeaderTemplate = `# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
1729Module: {{.properties.Name}}
1730Type: {{.typeName}}
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001731Factory: {{.goFactory}}
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001732Defined: {{.pos}}
1733`
1734
1735var singletonHeaderTemplate = `# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
1736Singleton: {{.name}}
Jamie Gennis7d5b2f82014-09-24 17:51:52 -07001737Factory: {{.goFactory}}
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001738`