blob: 9ecaef12fee3d9d131284a1da717c7313e450ca7 [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"
12 "sort"
13 "strings"
14 "text/scanner"
15 "text/template"
16)
17
18var ErrBuildActionsNotReady = errors.New("build actions are not ready")
19
20const maxErrors = 10
21
22type Context struct {
23 // set at instantiation
24 moduleTypes map[string]ModuleType
25 modules map[string]Module
26 moduleInfo map[Module]*moduleInfo
27 singletonInfo map[string]*singletonInfo
28
29 dependenciesReady bool // set to true on a successful ResolveDependencies
30 buildActionsReady bool // set to true on a successful PrepareBuildActions
31
32 // set by SetIgnoreUnknownModuleTypes
33 ignoreUnknownModuleTypes bool
34
35 // set during PrepareBuildActions
36 pkgNames map[*pkg]string
37 globalVariables map[Variable]*ninjaString
38 globalPools map[Pool]*poolDef
39 globalRules map[Rule]*ruleDef
40
41 // set during PrepareBuildActions
42 buildDir *ninjaString // The builddir special Ninja variable
43 requiredNinjaMajor int // For the ninja_required_version variable
44 requiredNinjaMinor int // For the ninja_required_version variable
45 requiredNinjaMicro int // For the ninja_required_version variable
46}
47
48// A Config contains build configuration information that can affect the
49// contents of the Ninja build file is that will be generated. The specific
50// representation of this configuration information is not defined here.
51type Config interface{}
52
53type Error struct {
54 Err error
55 Pos scanner.Position
56}
57
58type localBuildActions struct {
59 variables []*localVariable
60 rules []*localRule
61 buildDefs []*buildDef
62}
63
64type moduleInfo struct {
65 // set during Parse
66 typeName string
67 typ ModuleType
68 relBlueprintFile string
69 pos scanner.Position
70 propertyPos map[string]scanner.Position
71 properties struct {
72 Name string
73 Deps []string
74 }
75
76 // set during ResolveDependencies
77 directDeps []Module
78
79 // set during PrepareBuildActions
80 actionDefs localBuildActions
81}
82
83type singletonInfo struct {
84 // set during RegisterSingleton
85 singleton Singleton
86
87 // set during PrepareBuildActions
88 actionDefs localBuildActions
89}
90
91func (e *Error) Error() string {
92
93 return fmt.Sprintf("%s: %s", e.Pos, e.Err)
94}
95
96func NewContext() *Context {
97 return &Context{
98 moduleTypes: make(map[string]ModuleType),
99 modules: make(map[string]Module),
100 moduleInfo: make(map[Module]*moduleInfo),
101 singletonInfo: make(map[string]*singletonInfo),
102 }
103}
104
105func (c *Context) RegisterModuleType(name string, typ ModuleType) {
106 if _, present := c.moduleTypes[name]; present {
107 panic(errors.New("module type name is already registered"))
108 }
109 c.moduleTypes[name] = typ
110}
111
112func (c *Context) RegisterSingleton(name string, singleton Singleton) {
113 if _, present := c.singletonInfo[name]; present {
114 panic(errors.New("singleton name is already registered"))
115 }
116 if singletonPkgPath(singleton) == "" {
117 panic(errors.New("singleton types must be a named type"))
118 }
119 c.singletonInfo[name] = &singletonInfo{
120 singleton: singleton,
121 }
122}
123
124func singletonPkgPath(singleton Singleton) string {
125 typ := reflect.TypeOf(singleton)
126 for typ.Kind() == reflect.Ptr {
127 typ = typ.Elem()
128 }
129 return typ.PkgPath()
130}
131
132func singletonTypeName(singleton Singleton) string {
133 typ := reflect.TypeOf(singleton)
134 for typ.Kind() == reflect.Ptr {
135 typ = typ.Elem()
136 }
137 return typ.PkgPath() + "." + typ.Name()
138}
139
140func (c *Context) SetIgnoreUnknownModuleTypes(ignoreUnknownModuleTypes bool) {
141 c.ignoreUnknownModuleTypes = ignoreUnknownModuleTypes
142}
143
144func (c *Context) Parse(rootDir, filename string, r io.Reader) (subdirs []string,
145 errs []error) {
146
147 c.dependenciesReady = false
148
149 relBlueprintFile, err := filepath.Rel(rootDir, filename)
150 if err != nil {
151 return nil, []error{err}
152 }
153
154 defs, errs := parser.Parse(filename, r)
155 if len(errs) > 0 {
156 for i, err := range errs {
157 if parseErr, ok := err.(*parser.ParseError); ok {
158 err = &Error{
159 Err: parseErr.Err,
160 Pos: parseErr.Pos,
161 }
162 errs[i] = err
163 }
164 }
165
166 // If there were any parse errors don't bother trying to interpret the
167 // result.
168 return nil, errs
169 }
170
171 for _, def := range defs {
172 var newErrs []error
173 switch def := def.(type) {
174 case *parser.Module:
175 newErrs = c.processModuleDef(def, relBlueprintFile)
176
177 case *parser.Assignment:
178 var newSubdirs []string
179 newSubdirs, newErrs = c.processAssignment(def)
180 if newSubdirs != nil {
181 subdirs = newSubdirs
182 }
183
184 default:
185 panic("unknown definition type")
186 }
187
188 if len(newErrs) > 0 {
189 errs = append(errs, newErrs...)
190 if len(errs) > maxErrors {
191 break
192 }
193 }
194 }
195
196 return subdirs, errs
197}
198
199func (c *Context) ParseBlueprintsFiles(rootFile string) (deps []string,
200 errs []error) {
201
202 rootDir := filepath.Dir(rootFile)
203
204 depsSet := map[string]bool{rootFile: true}
205 blueprints := []string{rootFile}
206
207 var file *os.File
208 defer func() {
209 if file != nil {
210 file.Close()
211 }
212 }()
213
214 var err error
215
216 for i := 0; i < len(blueprints); i++ {
217 if len(errs) > maxErrors {
218 return
219 }
220
221 filename := blueprints[i]
222 dir := filepath.Dir(filename)
223
224 file, err = os.Open(filename)
225 if err != nil {
226 errs = append(errs, &Error{Err: err})
227 continue
228 }
229
230 subdirs, newErrs := c.Parse(rootDir, filename, file)
231 if len(newErrs) > 0 {
232 errs = append(errs, newErrs...)
233 continue
234 }
235
236 err = file.Close()
237 if err != nil {
238 errs = append(errs, &Error{Err: err})
239 continue
240 }
241
242 // Add the subdirs to the list of directories to parse Blueprint files
243 // from.
244 for _, subdir := range subdirs {
245 subdir = filepath.Join(dir, subdir)
246 dirPart, filePart := filepath.Split(subdir)
247 dirPart = filepath.Clean(dirPart)
248
249 if filePart == "*" {
250 foundSubdirs, err := listSubdirs(dirPart)
251 if err != nil {
252 errs = append(errs, &Error{Err: err})
253 continue
254 }
255
256 for _, foundSubdir := range foundSubdirs {
257 subBlueprints := filepath.Join(dirPart, foundSubdir,
258 "Blueprints")
259
260 _, err := os.Stat(subBlueprints)
261 if os.IsNotExist(err) {
262 // There is no Blueprints file in this subdirectory. We
263 // need to add the directory to the list of dependencies
264 // so that if someone adds a Blueprints file in the
265 // future we'll pick it up.
266 depsSet[filepath.Dir(subBlueprints)] = true
267 } else if !depsSet[subBlueprints] {
268 // We haven't seen this Blueprints file before, so add
269 // it to our list.
270 depsSet[subBlueprints] = true
271 blueprints = append(blueprints, subBlueprints)
272 }
273 }
274
275 // We now depend on the directory itself because if any new
276 // subdirectories get added or removed we need to rebuild the
277 // Ninja manifest.
278 depsSet[dirPart] = true
279 } else {
280 subBlueprints := filepath.Join(subdir, "Blueprints")
281 if !depsSet[subBlueprints] {
282 depsSet[subBlueprints] = true
283 blueprints = append(blueprints, subBlueprints)
284 }
285 }
286 }
287 }
288
289 for dep := range depsSet {
290 deps = append(deps, dep)
291 }
292
293 return
294}
295
296func listSubdirs(dir string) ([]string, error) {
297 d, err := os.Open(dir)
298 if err != nil {
299 return nil, err
300 }
301 defer d.Close()
302
303 infos, err := d.Readdir(-1)
304 if err != nil {
305 return nil, err
306 }
307
308 var subdirs []string
309 for _, info := range infos {
310 if info.IsDir() {
311 subdirs = append(subdirs, info.Name())
312 }
313 }
314
315 return subdirs, nil
316}
317
318func (c *Context) processAssignment(
319 assignment *parser.Assignment) (subdirs []string, errs []error) {
320
321 if assignment.Name == "subdirs" {
322 switch assignment.Value.Type {
323 case parser.List:
324 subdirs = make([]string, 0, len(assignment.Value.ListValue))
325
326 for _, value := range assignment.Value.ListValue {
327 if value.Type != parser.String {
328 // The parser should not produce this.
329 panic("non-string value found in list")
330 }
331
332 dirPart, filePart := filepath.Split(value.StringValue)
333 if (filePart != "*" && strings.ContainsRune(filePart, '*')) ||
334 strings.ContainsRune(dirPart, '*') {
335
336 errs = append(errs, &Error{
337 Err: fmt.Errorf("subdirs may only wildcard whole " +
338 "directories"),
339 Pos: value.Pos,
340 })
341
342 continue
343 }
344
345 subdirs = append(subdirs, value.StringValue)
346 }
347
348 if len(errs) > 0 {
349 subdirs = nil
350 }
351
352 return
353
354 case parser.Bool, parser.String:
355 errs = []error{
356 &Error{
357 Err: fmt.Errorf("subdirs must be a list of strings"),
358 Pos: assignment.Pos,
359 },
360 }
361
362 return
363
364 default:
365 panic(fmt.Errorf("unknown value type: %d", assignment.Value.Type))
366 }
367 }
368
369 return nil, []error{
370 &Error{
371 Err: fmt.Errorf("only 'subdirs' assignment is supported"),
372 Pos: assignment.Pos,
373 },
374 }
375}
376
377func (c *Context) processModuleDef(moduleDef *parser.Module,
378 relBlueprintFile string) []error {
379
380 typeName := moduleDef.Type
381 typ, ok := c.moduleTypes[typeName]
382 if !ok {
383 if c.ignoreUnknownModuleTypes {
384 return nil
385 }
386
387 err := fmt.Errorf("unrecognized module type %q", typeName)
388 return []error{err}
389 }
390
391 module, properties := typ.new()
392 info := &moduleInfo{
393 typeName: typeName,
394 typ: typ,
395 relBlueprintFile: relBlueprintFile,
396 }
397
398 errs := unpackProperties(moduleDef.Properties, &info.properties,
399 properties)
400 if len(errs) > 0 {
401 return errs
402 }
403
404 info.pos = moduleDef.Pos
405 info.propertyPos = make(map[string]scanner.Position)
406 for _, propertyDef := range moduleDef.Properties {
407 info.propertyPos[propertyDef.Name] = propertyDef.Pos
408 }
409
410 name := info.properties.Name
411 err := validateNinjaName(name)
412 if err != nil {
413 return []error{
414 &Error{
415 Err: fmt.Errorf("invalid module name %q: %s", err),
416 Pos: info.propertyPos["name"],
417 },
418 }
419 }
420
421 if first, present := c.modules[name]; present {
422 errs = append(errs, &Error{
423 Err: fmt.Errorf("module %q already defined", name),
424 Pos: moduleDef.Pos,
425 })
426 errs = append(errs, &Error{
427 Err: fmt.Errorf("<-- previous definition here"),
428 Pos: c.moduleInfo[first].pos,
429 })
430 if len(errs) >= maxErrors {
431 return errs
432 }
433 }
434
435 c.modules[name] = module
436 c.moduleInfo[module] = info
437
438 return nil
439}
440
441func (c *Context) ResolveDependencies() []error {
442 errs := c.resolveDependencies()
443 if len(errs) > 0 {
444 return errs
445 }
446
447 errs = c.checkForDependencyCycles()
448 if len(errs) > 0 {
449 return errs
450 }
451
452 c.dependenciesReady = true
453 return nil
454}
455
456// resolveDependencies populates the moduleInfo.directDeps list for every
457// module. In doing so it checks for missing dependencies and self-dependant
458// modules.
459func (c *Context) resolveDependencies() (errs []error) {
460 for _, info := range c.moduleInfo {
461 depNames := info.properties.Deps
462 info.directDeps = make([]Module, 0, len(depNames))
463 depsPos := info.propertyPos["deps"]
464
465 for _, depName := range depNames {
466 if depName == info.properties.Name {
467 errs = append(errs, &Error{
468 Err: fmt.Errorf("%q depends on itself", depName),
469 Pos: depsPos,
470 })
471 continue
472 }
473
474 dep, ok := c.modules[depName]
475 if !ok {
476 errs = append(errs, &Error{
477 Err: fmt.Errorf("%q depends on undefined module %q",
478 info.properties.Name, depName),
479 Pos: depsPos,
480 })
481 continue
482 }
483
484 info.directDeps = append(info.directDeps, dep)
485 }
486 }
487
488 return
489}
490
491// checkForDependencyCycles recursively walks the module dependency graph and
492// reports errors when it encounters dependency cycles. This should only be
493// called after resolveDependencies.
494func (c *Context) checkForDependencyCycles() (errs []error) {
495 visited := make(map[Module]bool) // modules that were already checked
496 checking := make(map[Module]bool) // modules actively being checked
497
498 var check func(m Module) []Module
499
500 check = func(m Module) []Module {
501 info := c.moduleInfo[m]
502
503 visited[m] = true
504 checking[m] = true
505 defer delete(checking, m)
506
507 for _, dep := range info.directDeps {
508 if checking[dep] {
509 // This is a cycle.
510 return []Module{dep, m}
511 }
512
513 if !visited[dep] {
514 cycle := check(dep)
515 if cycle != nil {
516 if cycle[0] == m {
517 // We are the "start" of the cycle, so we're responsible
518 // for generating the errors. The cycle list is in
519 // reverse order because all the 'check' calls append
520 // their own module to the list.
521 errs = append(errs, &Error{
522 Err: fmt.Errorf("encountered dependency cycle:"),
523 Pos: info.pos,
524 })
525
526 // Iterate backwards through the cycle list.
527 curInfo := info
528 for i := len(cycle) - 1; i >= 0; i-- {
529 nextInfo := c.moduleInfo[cycle[i]]
530 errs = append(errs, &Error{
531 Err: fmt.Errorf(" %q depends on %q",
532 curInfo.properties.Name,
533 nextInfo.properties.Name),
534 Pos: curInfo.propertyPos["deps"],
535 })
536 curInfo = nextInfo
537 }
538
539 // We can continue processing this module's children to
540 // find more cycles. Since all the modules that were
541 // part of the found cycle were marked as visited we
542 // won't run into that cycle again.
543 } else {
544 // We're not the "start" of the cycle, so we just append
545 // our module to the list and return it.
546 return append(cycle, m)
547 }
548 }
549 }
550 }
551
552 return nil
553 }
554
555 for _, module := range c.modules {
556 if !visited[module] {
557 cycle := check(module)
558 if cycle != nil {
559 panic("inconceivable!")
560 }
561 }
562 }
563
564 return
565}
566
567func (c *Context) PrepareBuildActions(config Config) []error {
568 c.buildActionsReady = false
569
570 if !c.dependenciesReady {
571 errs := c.ResolveDependencies()
572 if len(errs) > 0 {
573 return errs
574 }
575 }
576
577 liveGlobals := newLiveTracker(config)
578
579 c.initSpecialVariables()
580
581 errs := c.generateModuleBuildActions(config, liveGlobals)
582 if len(errs) > 0 {
583 return errs
584 }
585
586 errs = c.generateSingletonBuildActions(config, liveGlobals)
587 if len(errs) > 0 {
588 return errs
589 }
590
591 if c.buildDir != nil {
592 liveGlobals.addNinjaStringDeps(c.buildDir)
593 }
594
595 pkgNames := c.makeUniquePackageNames(liveGlobals)
596
597 // This will panic if it finds a problem since it's a programming error.
598 c.checkForVariableReferenceCycles(liveGlobals.variables, pkgNames)
599
600 c.pkgNames = pkgNames
601 c.globalVariables = liveGlobals.variables
602 c.globalPools = liveGlobals.pools
603 c.globalRules = liveGlobals.rules
604
605 c.buildActionsReady = true
606
607 return nil
608}
609
610func (c *Context) initSpecialVariables() {
611 c.buildDir = nil
612 c.requiredNinjaMajor = 1
613 c.requiredNinjaMinor = 1
614 c.requiredNinjaMicro = 0
615}
616
617func (c *Context) generateModuleBuildActions(config Config,
618 liveGlobals *liveTracker) []error {
619
620 visited := make(map[Module]bool)
621
622 var errs []error
623
624 var walk func(module Module)
625 walk = func(module Module) {
626 visited[module] = true
627
628 info := c.moduleInfo[module]
629 for _, dep := range info.directDeps {
630 if !visited[dep] {
631 walk(dep)
632 }
633 }
634
635 mctx := &moduleContext{
636 context: c,
637 config: config,
638 module: module,
639 scope: newLocalScope(info.typ.pkg().scope,
640 moduleNamespacePrefix(info.properties.Name)),
641 info: info,
642 }
643
644 module.GenerateBuildActions(mctx)
645
646 if len(mctx.errs) > 0 {
647 errs = append(errs, mctx.errs...)
648 return
649 }
650
651 newErrs := c.processLocalBuildActions(&info.actionDefs,
652 &mctx.actionDefs, liveGlobals)
653 errs = append(errs, newErrs...)
654 }
655
656 for _, module := range c.modules {
657 if !visited[module] {
658 walk(module)
659 }
660 }
661
662 return errs
663}
664
665func (c *Context) generateSingletonBuildActions(config Config,
666 liveGlobals *liveTracker) []error {
667
668 var errs []error
669 for name, info := range c.singletonInfo {
670 // If the package to which the singleton type belongs has not defined
671 // any Ninja globals and has not called Import() then we won't have an
672 // entry for it in the pkgs map. If that's the case then the
673 // singleton's scope's parent should just be nil.
674 var singletonScope *scope
675 if pkg := pkgs[singletonPkgPath(info.singleton)]; pkg != nil {
676 singletonScope = pkg.scope
677 }
678
679 sctx := &singletonContext{
680 context: c,
681 config: config,
682 scope: newLocalScope(singletonScope,
683 singletonNamespacePrefix(name)),
684 }
685
686 info.singleton.GenerateBuildActions(sctx)
687
688 if len(sctx.errs) > 0 {
689 errs = append(errs, sctx.errs...)
690 if len(errs) > maxErrors {
691 break
692 }
693 continue
694 }
695
696 newErrs := c.processLocalBuildActions(&info.actionDefs,
697 &sctx.actionDefs, liveGlobals)
698 errs = append(errs, newErrs...)
699 if len(errs) > maxErrors {
700 break
701 }
702 }
703
704 return errs
705}
706
707func (c *Context) processLocalBuildActions(out, in *localBuildActions,
708 liveGlobals *liveTracker) []error {
709
710 var errs []error
711
712 // First we go through and add everything referenced by the module's
713 // buildDefs to the live globals set. This will end up adding the live
714 // locals to the set as well, but we'll take them out after.
715 for _, def := range in.buildDefs {
716 err := liveGlobals.AddBuildDefDeps(def)
717 if err != nil {
718 errs = append(errs, err)
719 }
720 }
721
722 if len(errs) > 0 {
723 return errs
724 }
725
726 out.buildDefs = in.buildDefs
727
728 // We use the now-incorrect set of live "globals" to determine which local
729 // definitions are live. As we go through copying those live locals to the
730 // moduleInfo we remove them from the live globals set.
731 out.variables = nil
732 for _, v := range in.variables {
733 _, isLive := liveGlobals.variables[v]
734 if isLive {
735 out.variables = append(out.variables, v)
736 delete(liveGlobals.variables, v)
737 }
738 }
739
740 out.rules = nil
741 for _, r := range in.rules {
742 _, isLive := liveGlobals.rules[r]
743 if isLive {
744 out.rules = append(out.rules, r)
745 delete(liveGlobals.rules, r)
746 }
747 }
748
749 return nil
750}
751
752func (c *Context) visitDepsDepthFirst(module Module, visit func(Module)) {
753 visited := make(map[Module]bool)
754
755 var walk func(m Module)
756 walk = func(m Module) {
757 info := c.moduleInfo[m]
758 visited[m] = true
759 for _, dep := range info.directDeps {
760 if !visited[dep] {
761 walk(dep)
762 }
763 }
764 visit(m)
765 }
766
767 info := c.moduleInfo[module]
768 for _, dep := range info.directDeps {
769 if !visited[dep] {
770 walk(dep)
771 }
772 }
773}
774
775func (c *Context) visitDepsDepthFirstIf(module Module, pred func(Module) bool,
776 visit func(Module)) {
777
778 visited := make(map[Module]bool)
779
780 var walk func(m Module)
781 walk = func(m Module) {
782 info := c.moduleInfo[m]
783 visited[m] = true
784 if pred(m) {
785 for _, dep := range info.directDeps {
786 if !visited[dep] {
787 walk(dep)
788 }
789 }
790 visit(m)
791 }
792 }
793
794 info := c.moduleInfo[module]
795 for _, dep := range info.directDeps {
796 if !visited[dep] {
797 walk(dep)
798 }
799 }
800}
801
802func (c *Context) visitAllModules(visit func(Module)) {
803 for _, module := range c.modules {
804 visit(module)
805 }
806}
807
808func (c *Context) visitAllModulesIf(pred func(Module) bool,
809 visit func(Module)) {
810
811 for _, module := range c.modules {
812 if pred(module) {
813 visit(module)
814 }
815 }
816}
817
818func (c *Context) requireNinjaVersion(major, minor, micro int) {
819 if major != 1 {
820 panic("ninja version with major version != 1 not supported")
821 }
822 if c.requiredNinjaMinor < minor {
823 c.requiredNinjaMinor = minor
824 c.requiredNinjaMicro = micro
825 }
826 if c.requiredNinjaMinor == minor && c.requiredNinjaMicro < micro {
827 c.requiredNinjaMicro = micro
828 }
829}
830
831func (c *Context) setBuildDir(value *ninjaString) {
832 if c.buildDir != nil {
833 panic("buildDir set multiple times")
834 }
835 c.buildDir = value
836}
837
838func (c *Context) makeUniquePackageNames(
839 liveGlobals *liveTracker) map[*pkg]string {
840
841 pkgs := make(map[string]*pkg)
842 pkgNames := make(map[*pkg]string)
843 longPkgNames := make(map[*pkg]bool)
844
845 processPackage := func(pkg *pkg) {
846 if pkg == nil {
847 // This is a built-in rule and has no package.
848 return
849 }
850 if _, ok := pkgNames[pkg]; ok {
851 // We've already processed this package.
852 return
853 }
854
855 otherPkg, present := pkgs[pkg.shortName]
856 if present {
857 // Short name collision. Both this package and the one that's
858 // already there need to use their full names. We leave the short
859 // name in pkgNames for now so future collisions still get caught.
860 longPkgNames[pkg] = true
861 longPkgNames[otherPkg] = true
862 } else {
863 // No collision so far. Tentatively set the package's name to be
864 // its short name.
865 pkgNames[pkg] = pkg.shortName
866 }
867 }
868
869 // We try to give all packages their short name, but when we get collisions
870 // we need to use the full unique package name.
871 for v, _ := range liveGlobals.variables {
872 processPackage(v.pkg())
873 }
874 for p, _ := range liveGlobals.pools {
875 processPackage(p.pkg())
876 }
877 for r, _ := range liveGlobals.rules {
878 processPackage(r.pkg())
879 }
880
881 // Add the packages that had collisions using their full unique names. This
882 // will overwrite any short names that were added in the previous step.
883 for pkg := range longPkgNames {
884 pkgNames[pkg] = pkg.fullName
885 }
886
887 return pkgNames
888}
889
890func (c *Context) checkForVariableReferenceCycles(
891 variables map[Variable]*ninjaString, pkgNames map[*pkg]string) {
892
893 visited := make(map[Variable]bool) // variables that were already checked
894 checking := make(map[Variable]bool) // variables actively being checked
895
896 var check func(v Variable) []Variable
897
898 check = func(v Variable) []Variable {
899 visited[v] = true
900 checking[v] = true
901 defer delete(checking, v)
902
903 value := variables[v]
904 for _, dep := range value.variables {
905 if checking[dep] {
906 // This is a cycle.
907 return []Variable{dep, v}
908 }
909
910 if !visited[dep] {
911 cycle := check(dep)
912 if cycle != nil {
913 if cycle[0] == v {
914 // We are the "start" of the cycle, so we're responsible
915 // for generating the errors. The cycle list is in
916 // reverse order because all the 'check' calls append
917 // their own module to the list.
918 msgs := []string{"detected variable reference cycle:"}
919
920 // Iterate backwards through the cycle list.
921 curName := v.fullName(pkgNames)
922 curValue := value.Value(pkgNames)
923 for i := len(cycle) - 1; i >= 0; i-- {
924 next := cycle[i]
925 nextName := next.fullName(pkgNames)
926 nextValue := variables[next].Value(pkgNames)
927
928 msgs = append(msgs, fmt.Sprintf(
929 " %q depends on %q", curName, nextName))
930 msgs = append(msgs, fmt.Sprintf(
931 " [%s = %s]", curName, curValue))
932
933 curName = nextName
934 curValue = nextValue
935 }
936
937 // Variable reference cycles are a programming error,
938 // not the fault of the Blueprint file authors.
939 panic(strings.Join(msgs, "\n"))
940 } else {
941 // We're not the "start" of the cycle, so we just append
942 // our module to the list and return it.
943 return append(cycle, v)
944 }
945 }
946 }
947 }
948
949 return nil
950 }
951
952 for v := range variables {
953 if !visited[v] {
954 cycle := check(v)
955 if cycle != nil {
956 panic("inconceivable!")
957 }
958 }
959 }
960}
961
962func (c *Context) WriteBuildFile(w io.Writer) error {
963 if !c.buildActionsReady {
964 return ErrBuildActionsNotReady
965 }
966
967 nw := newNinjaWriter(w)
968
969 err := c.writeBuildFileHeader(nw)
970 if err != nil {
971 return err
972 }
973
974 err = c.writeNinjaRequiredVersion(nw)
975 if err != nil {
976 return err
977 }
978
979 // TODO: Group the globals by package.
980
981 err = c.writeGlobalVariables(nw)
982 if err != nil {
983 return err
984 }
985
986 err = c.writeGlobalPools(nw)
987 if err != nil {
988 return err
989 }
990
991 err = c.writeBuildDir(nw)
992 if err != nil {
993 return err
994 }
995
996 err = c.writeGlobalRules(nw)
997 if err != nil {
998 return err
999 }
1000
1001 err = c.writeAllModuleActions(nw)
1002 if err != nil {
1003 return err
1004 }
1005
1006 err = c.writeAllSingletonActions(nw)
1007 if err != nil {
1008 return err
1009 }
1010
1011 return nil
1012}
1013
1014func (c *Context) writeBuildFileHeader(nw *ninjaWriter) error {
1015 headerTemplate := template.New("fileHeader")
1016 _, err := headerTemplate.Parse(fileHeaderTemplate)
1017 if err != nil {
1018 // This is a programming error.
1019 panic(err)
1020 }
1021
1022 type pkgAssociation struct {
1023 PkgName string
1024 PkgPath string
1025 }
1026
1027 var pkgs []pkgAssociation
1028 maxNameLen := 0
1029 for pkg, name := range c.pkgNames {
1030 pkgs = append(pkgs, pkgAssociation{
1031 PkgName: name,
1032 PkgPath: pkg.pkgPath,
1033 })
1034 if len(name) > maxNameLen {
1035 maxNameLen = len(name)
1036 }
1037 }
1038
1039 for i := range pkgs {
1040 pkgs[i].PkgName += strings.Repeat(" ", maxNameLen-len(pkgs[i].PkgName))
1041 }
1042
1043 params := map[string]interface{}{
1044 "Pkgs": pkgs,
1045 }
1046
1047 buf := bytes.NewBuffer(nil)
1048 err = headerTemplate.Execute(buf, params)
1049 if err != nil {
1050 return err
1051 }
1052
1053 return nw.Comment(buf.String())
1054}
1055
1056func (c *Context) writeNinjaRequiredVersion(nw *ninjaWriter) error {
1057 value := fmt.Sprintf("%d.%d.%d", c.requiredNinjaMajor, c.requiredNinjaMinor,
1058 c.requiredNinjaMicro)
1059
1060 err := nw.Assign("ninja_required_version", value)
1061 if err != nil {
1062 return err
1063 }
1064
1065 return nw.BlankLine()
1066}
1067
1068func (c *Context) writeBuildDir(nw *ninjaWriter) error {
1069 if c.buildDir != nil {
1070 err := nw.Assign("builddir", c.buildDir.Value(c.pkgNames))
1071 if err != nil {
1072 return err
1073 }
1074
1075 err = nw.BlankLine()
1076 if err != nil {
1077 return err
1078 }
1079 }
1080 return nil
1081}
1082
1083type variableSorter struct {
1084 pkgNames map[*pkg]string
1085 variables []Variable
1086}
1087
1088func (v *variableSorter) Len() int {
1089 return len(v.variables)
1090}
1091
1092func (v *variableSorter) Less(i, j int) bool {
1093 iName := v.variables[i].fullName(v.pkgNames)
1094 jName := v.variables[j].fullName(v.pkgNames)
1095 return iName < jName
1096}
1097
1098func (v *variableSorter) Swap(i, j int) {
1099 v.variables[i], v.variables[j] = v.variables[j], v.variables[i]
1100}
1101
1102func (c *Context) writeGlobalVariables(nw *ninjaWriter) error {
1103 visited := make(map[Variable]bool)
1104
1105 var walk func(v Variable) error
1106 walk = func(v Variable) error {
1107 visited[v] = true
1108
1109 // First visit variables on which this variable depends.
1110 value := c.globalVariables[v]
1111 for _, dep := range value.variables {
1112 if !visited[dep] {
1113 err := walk(dep)
1114 if err != nil {
1115 return err
1116 }
1117 }
1118 }
1119
1120 err := nw.Assign(v.fullName(c.pkgNames), value.Value(c.pkgNames))
1121 if err != nil {
1122 return err
1123 }
1124
1125 err = nw.BlankLine()
1126 if err != nil {
1127 return err
1128 }
1129
1130 return nil
1131 }
1132
1133 globalVariables := make([]Variable, 0, len(c.globalVariables))
1134 for v := range c.globalVariables {
1135 globalVariables = append(globalVariables, v)
1136 }
1137
1138 sort.Sort(&variableSorter{c.pkgNames, globalVariables})
1139
1140 for _, v := range globalVariables {
1141 if !visited[v] {
1142 err := walk(v)
1143 if err != nil {
1144 return nil
1145 }
1146 }
1147 }
1148
1149 return nil
1150}
1151
1152func (c *Context) writeGlobalPools(nw *ninjaWriter) error {
1153 for pool, def := range c.globalPools {
1154 name := pool.fullName(c.pkgNames)
1155 err := def.WriteTo(nw, name)
1156 if err != nil {
1157 return err
1158 }
1159
1160 err = nw.BlankLine()
1161 if err != nil {
1162 return err
1163 }
1164 }
1165
1166 return nil
1167}
1168
1169func (c *Context) writeGlobalRules(nw *ninjaWriter) error {
1170 for rule, def := range c.globalRules {
1171 name := rule.fullName(c.pkgNames)
1172 err := def.WriteTo(nw, name, c.pkgNames)
1173 if err != nil {
1174 return err
1175 }
1176
1177 err = nw.BlankLine()
1178 if err != nil {
1179 return err
1180 }
1181 }
1182
1183 return nil
1184}
1185
Jamie Gennis86179fe2014-06-11 16:27:16 -07001186type moduleInfoSorter []*moduleInfo
1187
1188func (s moduleInfoSorter) Len() int {
1189 return len(s)
1190}
1191
1192func (s moduleInfoSorter) Less(i, j int) bool {
1193 iName := s[i].properties.Name
1194 jName := s[j].properties.Name
1195 return iName < jName
1196}
1197
1198func (s moduleInfoSorter) Swap(i, j int) {
1199 s[i], s[j] = s[j], s[i]
1200}
1201
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001202func (c *Context) writeAllModuleActions(nw *ninjaWriter) error {
1203 headerTemplate := template.New("moduleHeader")
1204 _, err := headerTemplate.Parse(moduleHeaderTemplate)
1205 if err != nil {
1206 // This is a programming error.
1207 panic(err)
1208 }
1209
Jamie Gennis86179fe2014-06-11 16:27:16 -07001210 infos := make([]*moduleInfo, 0, len(c.moduleInfo))
1211 for _, info := range c.moduleInfo {
1212 infos = append(infos, info)
1213 }
1214 sort.Sort(moduleInfoSorter(infos))
1215
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001216 buf := bytes.NewBuffer(nil)
1217
Jamie Gennis86179fe2014-06-11 16:27:16 -07001218 for _, info := range infos {
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001219 buf.Reset()
Jamie Gennis1ebd3b82014-06-04 15:33:08 -07001220
1221 // In order to make the bootstrap build manifest independent of the
1222 // build dir we need to output the Blueprints file locations in the
1223 // comments as paths relative to the source directory.
1224 relPos := info.pos
1225 relPos.Filename = info.relBlueprintFile
1226
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001227 infoMap := map[string]interface{}{
1228 "properties": info.properties,
1229 "typeName": info.typeName,
1230 "goTypeName": info.typ.name(),
Jamie Gennis1ebd3b82014-06-04 15:33:08 -07001231 "pos": relPos,
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001232 }
1233 err = headerTemplate.Execute(buf, infoMap)
1234 if err != nil {
1235 return err
1236 }
1237
1238 err = nw.Comment(buf.String())
1239 if err != nil {
1240 return err
1241 }
1242
1243 err = nw.BlankLine()
1244 if err != nil {
1245 return err
1246 }
1247
1248 err = c.writeLocalBuildActions(nw, &info.actionDefs)
1249 if err != nil {
1250 return err
1251 }
1252
1253 err = nw.BlankLine()
1254 if err != nil {
1255 return err
1256 }
1257 }
1258
1259 return nil
1260}
1261
1262func (c *Context) writeAllSingletonActions(nw *ninjaWriter) error {
1263 headerTemplate := template.New("singletonHeader")
1264 _, err := headerTemplate.Parse(singletonHeaderTemplate)
1265 if err != nil {
1266 // This is a programming error.
1267 panic(err)
1268 }
1269
1270 buf := bytes.NewBuffer(nil)
1271
Jamie Gennis86179fe2014-06-11 16:27:16 -07001272 singletonNames := make([]string, 0, len(c.singletonInfo))
1273 for name := range c.singletonInfo {
1274 singletonNames = append(singletonNames, name)
1275 }
1276 sort.Strings(singletonNames)
1277
1278 for _, name := range singletonNames {
1279 info := c.singletonInfo[name]
1280
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001281 buf.Reset()
1282 infoMap := map[string]interface{}{
1283 "name": name,
1284 "goTypeName": singletonTypeName(info.singleton),
1285 }
1286 err = headerTemplate.Execute(buf, infoMap)
1287 if err != nil {
1288 return err
1289 }
1290
1291 err = nw.Comment(buf.String())
1292 if err != nil {
1293 return err
1294 }
1295
1296 err = nw.BlankLine()
1297 if err != nil {
1298 return err
1299 }
1300
1301 err = c.writeLocalBuildActions(nw, &info.actionDefs)
1302 if err != nil {
1303 return err
1304 }
1305
1306 err = nw.BlankLine()
1307 if err != nil {
1308 return err
1309 }
1310 }
1311
1312 return nil
1313}
1314
1315func (c *Context) writeLocalBuildActions(nw *ninjaWriter,
1316 defs *localBuildActions) error {
1317
1318 // Write the local variable assignments.
1319 for _, v := range defs.variables {
1320 // A localVariable doesn't need the package names or config to
1321 // determine its name or value.
1322 name := v.fullName(nil)
1323 value, err := v.value(nil)
1324 if err != nil {
1325 panic(err)
1326 }
1327 err = nw.Assign(name, value.Value(c.pkgNames))
1328 if err != nil {
1329 return err
1330 }
1331 }
1332
1333 if len(defs.variables) > 0 {
1334 err := nw.BlankLine()
1335 if err != nil {
1336 return err
1337 }
1338 }
1339
1340 // Write the local rules.
1341 for _, r := range defs.rules {
1342 // A localRule doesn't need the package names or config to determine
1343 // its name or definition.
1344 name := r.fullName(nil)
1345 def, err := r.def(nil)
1346 if err != nil {
1347 panic(err)
1348 }
1349
1350 err = def.WriteTo(nw, name, c.pkgNames)
1351 if err != nil {
1352 return err
1353 }
1354
1355 err = nw.BlankLine()
1356 if err != nil {
1357 return err
1358 }
1359 }
1360
1361 // Write the build definitions.
1362 for _, buildDef := range defs.buildDefs {
1363 err := buildDef.WriteTo(nw, c.pkgNames)
1364 if err != nil {
1365 return err
1366 }
1367
1368 if len(buildDef.Args) > 0 {
1369 err = nw.BlankLine()
1370 if err != nil {
1371 return err
1372 }
1373 }
1374 }
1375
1376 return nil
1377}
1378
1379var fileHeaderTemplate = `******************************************************************************
1380*** This file is generated and should not be edited ***
1381******************************************************************************
1382{{if .Pkgs}}
1383This file contains variables, rules, and pools with name prefixes indicating
1384they were generated by the following Go packages:
1385{{range .Pkgs}}
1386 {{.PkgName}} [from Go package {{.PkgPath}}]{{end}}{{end}}
1387
1388`
1389
1390var moduleHeaderTemplate = `# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
1391Module: {{.properties.Name}}
1392Type: {{.typeName}}
1393GoType: {{.goTypeName}}
1394Defined: {{.pos}}
1395`
1396
1397var singletonHeaderTemplate = `# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
1398Singleton: {{.name}}
1399GoType: {{.goTypeName}}
1400`