blob: 2408ebf89e0303b4d382656f0365b8eede7cec69 [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 Hamaji246cd232015-05-27 17:08:23 +090032func stripShellComment(s string) string {
33 if strings.IndexByte(s, '#') < 0 {
34 // Fast path.
35 return s
36 }
37 var escape bool
38 var quote rune
39 for i, c := range s {
40 if quote > 0 {
41 if quote == c && (quote == '\'' || !escape) {
42 quote = 0
43 }
44 } else if !escape {
45 if c == '#' {
46 return s[:i]
47 } else if c == '\'' || c == '"' || c == '`' {
48 quote = c
49 }
50 }
51 if escape {
52 escape = false
53 } else if c == '\\' {
54 escape = true
55 } else {
56 escape = false
57 }
58 }
59 return s
60}
61
Shinichiro Hamajia5467792015-05-27 16:28:40 +090062func genShellScript(runners []runner) string {
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090063 var buf bytes.Buffer
Shinichiro Hamajia5467792015-05-27 16:28:40 +090064 for i, r := range runners {
65 if i > 0 {
66 if runners[i-1].ignoreError {
67 buf.WriteString(" ; ")
68 } else {
69 buf.WriteString(" && ")
70 }
71 }
Shinichiro Hamaji246cd232015-05-27 17:08:23 +090072 cmd := stripShellComment(r.cmd)
73 cmd = trimLeftSpace(cmd)
Shinichiro Hamajia5467792015-05-27 16:28:40 +090074 cmd = strings.Replace(cmd, "\\\n", " ", -1)
75 cmd = strings.TrimRight(cmd, " \t\n;")
76 cmd = strings.Replace(cmd, "$", "$$", -1)
77 cmd = strings.Replace(cmd, "\t", " ", -1)
Shinichiro Hamaji501f68e2015-05-27 17:33:41 +090078 if cmd == "" {
79 cmd = "true"
80 }
Shinichiro Hamajia5467792015-05-27 16:28:40 +090081 buf.WriteString(cmd)
82 if i == len(runners)-1 && r.ignoreError {
83 buf.WriteString(" ; true")
84 }
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090085 }
86 return buf.String()
87}
88
Shinichiro Hamajifd872162015-05-26 18:06:48 +090089func (n *NinjaGenerator) genRuleName() string {
90 ruleName := fmt.Sprintf("rule%d", n.ruleId)
91 n.ruleId++
92 return ruleName
93}
94
95func (n *NinjaGenerator) emitBuild(output, rule, dep string) {
96 fmt.Fprintf(n.f, "build %s: %s", output, rule)
97 if dep != "" {
98 fmt.Fprintf(n.f, " %s", dep)
99 }
100 fmt.Fprintf(n.f, "\n")
101}
102
103func getDepString(node *DepNode) string {
104 var deps []string
105 var orderOnlys []string
106 for _, d := range node.Deps {
107 if d.IsOrderOnly {
108 orderOnlys = append(orderOnlys, d.Output)
109 } else {
110 deps = append(deps, d.Output)
111 }
112 }
113 dep := ""
114 if len(deps) > 0 {
115 dep += fmt.Sprintf(" %s", strings.Join(deps, " "))
116 }
117 if len(orderOnlys) > 0 {
118 dep += fmt.Sprintf(" || %s", strings.Join(orderOnlys, " "))
119 }
120 return dep
121}
122
123func genIntermediateTargetName(o string, i int) string {
124 return fmt.Sprintf(".make_targets/%s@%d", o, i)
125}
126
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900127func (n *NinjaGenerator) emitNode(node *DepNode) {
128 if n.done[node.Output] {
129 return
130 }
131 n.done[node.Output] = true
132
Shinichiro Hamajifd4aaa02015-05-26 15:40:53 +0900133 if len(node.Cmds) == 0 && len(node.Deps) == 0 && !node.IsPhony {
134 return
135 }
136
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900137 runners, _ := n.ex.createRunners(node, true)
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900138 ruleName := "phony"
139 if len(runners) > 0 {
140 ruleName = n.genRuleName()
141 fmt.Fprintf(n.f, "rule %s\n", ruleName)
142 fmt.Fprintf(n.f, " description = build $out\n")
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900143
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900144 ss := genShellScript(runners)
145 // It seems Linux is OK with ~130kB.
146 // TODO: Find this number automatically.
147 ArgLenLimit := 100 * 1000
148 if len(ss) > ArgLenLimit {
149 fmt.Fprintf(n.f, " rspfile = $out.rsp\n")
150 fmt.Fprintf(n.f, " rspfile_content = %s\n", ss)
151 ss = "sh $out.rsp"
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900152 }
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900153 fmt.Fprintf(n.f, " command = %s\n", ss)
154
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900155 }
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900156 n.emitBuild(node.Output, ruleName, getDepString(node))
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900157
158 for _, d := range node.Deps {
159 n.emitNode(d)
160 }
161}
162
163func (n *NinjaGenerator) run() {
164 n.ex = NewExecutor(n.vars)
165 for _, node := range n.nodes {
166 n.emitNode(node)
167 }
168 n.f.Close()
169}
170
171func GenerateNinja(g *DepGraph) {
172 NewNinjaGenerator(g).run()
173}