blob: f100f236ba4910fcfc6d56e5bd3e6497a1178eff [file] [log] [blame]
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +09001package main
2
3import (
4 "bytes"
5 "fmt"
6 "os"
Shinichiro Hamaji11c45842015-05-27 19:02:07 +09007 "path/filepath"
Shinichiro Hamajibcecb502015-06-04 16:20:25 +09008 "regexp"
9 "runtime"
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090010 "strings"
11)
12
13type NinjaGenerator struct {
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +090014 f *os.File
15 nodes []*DepNode
16 vars Vars
17 exports map[string]bool
18 ex *Executor
19 ruleId int
20 done map[string]bool
Shinichiro Hamajibcecb502015-06-04 16:20:25 +090021 ccRe *regexp.Regexp
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090022}
23
24func NewNinjaGenerator(g *DepGraph) *NinjaGenerator {
Shinichiro Hamajibcecb502015-06-04 16:20:25 +090025 ccRe, err := regexp.Compile(`^prebuilts/(gcc|clang)/.*(gcc|g\+\+|clang|clang\+\+) .* -c `)
26 if err != nil {
27 panic(err)
28 }
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090029 return &NinjaGenerator{
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +090030 nodes: g.nodes,
31 vars: g.vars,
32 exports: g.exports,
33 done: make(map[string]bool),
Shinichiro Hamajibcecb502015-06-04 16:20:25 +090034 ccRe: ccRe,
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090035 }
36}
37
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +090038func getDepfileImpl(ss string) (string, error) {
Shinichiro Hamaji6dca2202015-05-27 18:00:34 +090039 tss := ss + " "
40 if !strings.Contains(tss, " -MD ") && !strings.Contains(tss, " -MMD ") {
41 return "", nil
42 }
43
44 mfIndex := strings.Index(ss, " -MF ")
45 if mfIndex >= 0 {
46 mf := trimLeftSpace(ss[mfIndex+4:])
47 if strings.Index(mf, " -MF ") >= 0 {
48 return "", fmt.Errorf("Multiple output file candidates in %s", ss)
49 }
50 mfEndIndex := strings.IndexAny(mf, " \t\n")
51 if mfEndIndex >= 0 {
52 mf = mf[:mfEndIndex]
53 }
54
Shinichiro Hamaji6dca2202015-05-27 18:00:34 +090055 return mf, nil
56 }
57
58 outIndex := strings.Index(ss, " -o ")
59 if outIndex < 0 {
60 return "", fmt.Errorf("Cannot find the depfile in %s", ss)
61 }
62 out := trimLeftSpace(ss[outIndex+4:])
63 if strings.Index(out, " -o ") >= 0 {
64 return "", fmt.Errorf("Multiple output file candidates in %s", ss)
65 }
66 outEndIndex := strings.IndexAny(out, " \t\n")
67 if outEndIndex >= 0 {
68 out = out[:outEndIndex]
69 }
70 return stripExt(out) + ".d", nil
71}
72
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +090073func getDepfile(ss string) (string, error) {
74 // A hack for Android - llvm-rs-cc seems not to emit a dep file.
75 if strings.Contains(ss, "bin/llvm-rs-cc ") {
76 return "", nil
77 }
78
79 r, err := getDepfileImpl(ss)
Shinichiro Hamajib8224f32015-05-28 23:07:59 +090080 if r == "" || err != nil {
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +090081 return r, err
82 }
83
84 // A hack for Android to get .P files instead of .d.
85 p := stripExt(r) + ".P"
86 if strings.Contains(ss, p) {
87 return p, nil
88 }
89
90 // A hack for Android. For .s files, GCC does not use
91 // C preprocessor, so it ignores -MF flag.
92 as := "/" + stripExt(filepath.Base(r)) + ".s"
93 if strings.Contains(ss, as) {
94 return "", nil
95 }
96
97 return r, nil
98}
99
Shinichiro Hamaji246cd232015-05-27 17:08:23 +0900100func stripShellComment(s string) string {
101 if strings.IndexByte(s, '#') < 0 {
102 // Fast path.
103 return s
104 }
105 var escape bool
106 var quote rune
107 for i, c := range s {
108 if quote > 0 {
109 if quote == c && (quote == '\'' || !escape) {
110 quote = 0
111 }
112 } else if !escape {
113 if c == '#' {
114 return s[:i]
115 } else if c == '\'' || c == '"' || c == '`' {
116 quote = c
117 }
118 }
119 if escape {
120 escape = false
121 } else if c == '\\' {
122 escape = true
123 } else {
124 escape = false
125 }
126 }
127 return s
128}
129
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900130func (n *NinjaGenerator) genShellScript(runners []runner) (string, bool) {
131 useGomacc := false
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900132 var buf bytes.Buffer
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900133 for i, r := range runners {
134 if i > 0 {
135 if runners[i-1].ignoreError {
136 buf.WriteString(" ; ")
137 } else {
138 buf.WriteString(" && ")
139 }
140 }
Shinichiro Hamaji246cd232015-05-27 17:08:23 +0900141 cmd := stripShellComment(r.cmd)
142 cmd = trimLeftSpace(cmd)
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900143 cmd = strings.Replace(cmd, "\\\n", " ", -1)
144 cmd = strings.TrimRight(cmd, " \t\n;")
145 cmd = strings.Replace(cmd, "$", "$$", -1)
146 cmd = strings.Replace(cmd, "\t", " ", -1)
Shinichiro Hamaji501f68e2015-05-27 17:33:41 +0900147 if cmd == "" {
148 cmd = "true"
149 }
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900150 if gomaDir != "" && n.ccRe.MatchString(cmd) {
151 cmd = fmt.Sprintf("%s/gomacc %s", gomaDir, cmd)
152 useGomacc = true
153 }
Shinichiro Hamaji13653b22015-05-28 22:45:12 +0900154
155 needsSubShell := i > 0 || len(runners) > 1
156 if cmd[0] == '(' {
157 needsSubShell = false
158 }
159
160 if needsSubShell {
161 buf.WriteByte('(')
162 }
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900163 buf.WriteString(cmd)
164 if i == len(runners)-1 && r.ignoreError {
165 buf.WriteString(" ; true")
166 }
Shinichiro Hamaji13653b22015-05-28 22:45:12 +0900167 if needsSubShell {
168 buf.WriteByte(')')
169 }
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900170 }
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900171 return buf.String(), gomaDir != "" && !useGomacc
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900172}
173
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900174func (n *NinjaGenerator) genRuleName() string {
175 ruleName := fmt.Sprintf("rule%d", n.ruleId)
176 n.ruleId++
177 return ruleName
178}
179
180func (n *NinjaGenerator) emitBuild(output, rule, dep string) {
Shinichiro Hamajiea2e0de2015-05-28 15:22:16 +0900181 fmt.Fprintf(n.f, "build %s: %s%s\n", output, rule, dep)
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900182}
183
184func getDepString(node *DepNode) string {
185 var deps []string
186 var orderOnlys []string
187 for _, d := range node.Deps {
188 if d.IsOrderOnly {
189 orderOnlys = append(orderOnlys, d.Output)
190 } else {
191 deps = append(deps, d.Output)
192 }
193 }
194 dep := ""
195 if len(deps) > 0 {
196 dep += fmt.Sprintf(" %s", strings.Join(deps, " "))
197 }
198 if len(orderOnlys) > 0 {
199 dep += fmt.Sprintf(" || %s", strings.Join(orderOnlys, " "))
200 }
201 return dep
202}
203
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900204func (n *NinjaGenerator) emitNode(node *DepNode) {
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900205 if n.done[node.Output] {
206 return
207 }
208 n.done[node.Output] = true
209
Shinichiro Hamajifd4aaa02015-05-26 15:40:53 +0900210 if len(node.Cmds) == 0 && len(node.Deps) == 0 && !node.IsPhony {
211 return
212 }
213
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900214 runners, _ := n.ex.createRunners(node, true)
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900215 ruleName := "phony"
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900216 useLocalPool := false
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900217 if len(runners) > 0 {
218 ruleName = n.genRuleName()
219 fmt.Fprintf(n.f, "rule %s\n", ruleName)
220 fmt.Fprintf(n.f, " description = build $out\n")
221
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900222 ss, ulp := n.genShellScript(runners)
223 if ulp {
224 useLocalPool = true
225 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900226 depfile, err := getDepfile(ss)
Shinichiro Hamaji0ab9a2f2015-05-27 18:32:46 +0900227 if err != nil {
228 panic(err)
229 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900230 if depfile != "" {
231 fmt.Fprintf(n.f, " depfile = %s\n", depfile)
232 }
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900233 // It seems Linux is OK with ~130kB.
234 // TODO: Find this number automatically.
235 ArgLenLimit := 100 * 1000
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900236 if len(ss) > ArgLenLimit {
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900237 fmt.Fprintf(n.f, " rspfile = $out.rsp\n")
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900238 fmt.Fprintf(n.f, " rspfile_content = %s\n", ss)
239 ss = "sh $out.rsp"
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900240 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900241 fmt.Fprintf(n.f, " command = %s\n", ss)
242
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900243 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900244 n.emitBuild(node.Output, ruleName, getDepString(node))
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900245 if useLocalPool {
246 fmt.Fprintf(n.f, " pool = local_pool\n")
247 }
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900248
249 for _, d := range node.Deps {
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900250 n.emitNode(d)
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900251 }
252}
253
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900254func (n *NinjaGenerator) generateShell() {
255 f, err := os.Create("ninja.sh")
256 if err != nil {
257 panic(err)
258 }
259 defer f.Close()
260
261 ev := newEvaluator(n.vars)
262 shell := ev.EvaluateVar("SHELL")
263 if shell == "" {
264 shell = "/bin/sh"
265 }
266 fmt.Fprintf(f, "#!%s\n", shell)
267 for name, export := range n.exports {
268 if export {
269 fmt.Fprintf(f, "export %s=%s\n", name, ev.EvaluateVar(name))
270 } else {
271 fmt.Fprintf(f, "unset %s\n", name)
272 }
273 }
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900274 if gomaDir == "" {
275 fmt.Fprintf(f, "exec ninja\n")
276 } else {
277 fmt.Fprintf(f, "exec ninja -j300\n")
278 }
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900279
280 err = f.Chmod(0755)
281 if err != nil {
282 panic(err)
283 }
284}
285
286func (n *NinjaGenerator) generateNinja() {
287 f, err := os.Create("build.ninja")
288 if err != nil {
289 panic(err)
290 }
291 defer f.Close()
292
293 n.f = f
Shinichiro Hamajiea2e0de2015-05-28 15:22:16 +0900294 fmt.Fprintf(n.f, "# Generated by kati\n")
295 fmt.Fprintf(n.f, "\n")
296
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900297 if gomaDir != "" {
298 fmt.Fprintf(n.f, "pool local_pool\n")
299 fmt.Fprintf(n.f, " depth = %d\n", runtime.NumCPU())
300 }
301
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900302 n.ex = NewExecutor(n.vars)
303 for _, node := range n.nodes {
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900304 n.emitNode(node)
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900305 }
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900306}
307
308func GenerateNinja(g *DepGraph) {
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900309 n := NewNinjaGenerator(g)
310 n.generateShell()
311 n.generateNinja()
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900312}