blob: 6932e47975d024171ee022f8d4748428d26c6280 [file] [log] [blame]
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +09001package main
2
3import (
4 "bytes"
5 "fmt"
6 "os"
7 "strings"
8)
9
10type NinjaGenerator struct {
11 f *os.File
12 nodes []*DepNode
13 vars Vars
14 ex *Executor
15 ruleId int
16 done map[string]bool
17}
18
19func NewNinjaGenerator(g *DepGraph) *NinjaGenerator {
20 f, err := os.Create("build.ninja")
21 if err != nil {
22 panic(err)
23 }
24 return &NinjaGenerator{
25 f: f,
26 nodes: g.nodes,
27 vars: g.vars,
28 done: make(map[string]bool),
29 }
30}
31
Shinichiro Hamajia5467792015-05-27 16:28:40 +090032func genShellScript(runners []runner) string {
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090033 var buf bytes.Buffer
Shinichiro Hamajia5467792015-05-27 16:28:40 +090034 for i, r := range runners {
35 if i > 0 {
36 if runners[i-1].ignoreError {
37 buf.WriteString(" ; ")
38 } else {
39 buf.WriteString(" && ")
40 }
41 }
42 cmd := trimLeftSpace(r.cmd)
43 cmd = strings.Replace(cmd, "\\\n", " ", -1)
44 cmd = strings.TrimRight(cmd, " \t\n;")
45 cmd = strings.Replace(cmd, "$", "$$", -1)
46 cmd = strings.Replace(cmd, "\t", " ", -1)
47 buf.WriteString(cmd)
48 if i == len(runners)-1 && r.ignoreError {
49 buf.WriteString(" ; true")
50 }
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090051 }
52 return buf.String()
53}
54
Shinichiro Hamajifd872162015-05-26 18:06:48 +090055func (n *NinjaGenerator) genRuleName() string {
56 ruleName := fmt.Sprintf("rule%d", n.ruleId)
57 n.ruleId++
58 return ruleName
59}
60
61func (n *NinjaGenerator) emitBuild(output, rule, dep string) {
62 fmt.Fprintf(n.f, "build %s: %s", output, rule)
63 if dep != "" {
64 fmt.Fprintf(n.f, " %s", dep)
65 }
66 fmt.Fprintf(n.f, "\n")
67}
68
69func getDepString(node *DepNode) string {
70 var deps []string
71 var orderOnlys []string
72 for _, d := range node.Deps {
73 if d.IsOrderOnly {
74 orderOnlys = append(orderOnlys, d.Output)
75 } else {
76 deps = append(deps, d.Output)
77 }
78 }
79 dep := ""
80 if len(deps) > 0 {
81 dep += fmt.Sprintf(" %s", strings.Join(deps, " "))
82 }
83 if len(orderOnlys) > 0 {
84 dep += fmt.Sprintf(" || %s", strings.Join(orderOnlys, " "))
85 }
86 return dep
87}
88
89func genIntermediateTargetName(o string, i int) string {
90 return fmt.Sprintf(".make_targets/%s@%d", o, i)
91}
92
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090093func (n *NinjaGenerator) emitNode(node *DepNode) {
94 if n.done[node.Output] {
95 return
96 }
97 n.done[node.Output] = true
98
Shinichiro Hamajifd4aaa02015-05-26 15:40:53 +090099 if len(node.Cmds) == 0 && len(node.Deps) == 0 && !node.IsPhony {
100 return
101 }
102
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900103 runners, _ := n.ex.createRunners(node, true)
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900104 ruleName := "phony"
105 if len(runners) > 0 {
106 ruleName = n.genRuleName()
107 fmt.Fprintf(n.f, "rule %s\n", ruleName)
108 fmt.Fprintf(n.f, " description = build $out\n")
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900109
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900110 ss := genShellScript(runners)
111 // It seems Linux is OK with ~130kB.
112 // TODO: Find this number automatically.
113 ArgLenLimit := 100 * 1000
114 if len(ss) > ArgLenLimit {
115 fmt.Fprintf(n.f, " rspfile = $out.rsp\n")
116 fmt.Fprintf(n.f, " rspfile_content = %s\n", ss)
117 ss = "sh $out.rsp"
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900118 }
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900119 fmt.Fprintf(n.f, " command = %s\n", ss)
120
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900121 }
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900122 n.emitBuild(node.Output, ruleName, getDepString(node))
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900123
124 for _, d := range node.Deps {
125 n.emitNode(d)
126 }
127}
128
129func (n *NinjaGenerator) run() {
130 n.ex = NewExecutor(n.vars)
131 for _, node := range n.nodes {
132 n.emitNode(node)
133 }
134 n.f.Close()
135}
136
137func GenerateNinja(g *DepGraph) {
138 NewNinjaGenerator(g).run()
139}