blob: f90ff24cfb890a2b9ef8636cf9764b9d6e8124c8 [file] [log] [blame]
Shinichiro Hamajib69bf8a2015-06-10 14:52:06 +09001// Copyright 2015 Google Inc. All rights reserved
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +090015package kati
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090016
17import (
18 "bytes"
19 "fmt"
20 "os"
Shinichiro Hamaji11c45842015-05-27 19:02:07 +090021 "path/filepath"
Shinichiro Hamajibcecb502015-06-04 16:20:25 +090022 "regexp"
23 "runtime"
Fumitoshi Ukaie08e9ee2015-07-13 13:14:41 +090024 "sort"
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090025 "strings"
Fumitoshi Ukai49599e52015-06-26 10:10:24 +090026 "time"
Fumitoshi Ukaif90f73c2015-07-28 14:37:02 +090027
28 "github.com/golang/glog"
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090029)
30
Fumitoshi Ukaib816ffb2015-07-28 17:46:04 +090031type nodeState int
32
33const (
Fumitoshi Ukai786846d2015-07-28 18:08:47 +090034 nodeInit nodeState = iota // not visited
35 nodeVisit // visited
36 nodeFile // visited & file exists
37 nodeAlias // visited & alias for other target
38 nodeMissing // visited & no target for this output
39 nodeBuild // visited & build emitted
Fumitoshi Ukaib816ffb2015-07-28 17:46:04 +090040)
41
42func (s nodeState) String() string {
43 switch s {
44 case nodeInit:
45 return "node-init"
46 case nodeVisit:
47 return "node-visit"
48 case nodeFile:
49 return "node-file"
50 case nodeAlias:
51 return "node-alias"
Fumitoshi Ukai786846d2015-07-28 18:08:47 +090052 case nodeMissing:
53 return "node-missing"
Fumitoshi Ukaib816ffb2015-07-28 17:46:04 +090054 case nodeBuild:
55 return "node-build"
56 default:
57 return fmt.Sprintf("node-unknown[%d]", int(s))
58 }
59}
60
Fumitoshi Ukaib79b2902015-07-16 16:40:09 +090061// NinjaGenerator generates ninja build files from DepGraph.
62type NinjaGenerator struct {
Fumitoshi Ukaid4a016e2015-07-27 16:50:00 +090063 // Args is original arguments to generate the ninja file.
64 Args []string
65 // Suffix is suffix for generated files.
66 Suffix string
Fumitoshi Ukaib79b2902015-07-16 16:40:09 +090067 // GomaDir is goma directory. If empty, goma will not be used.
68 GomaDir string
Fumitoshi Ukaib92f0282015-07-21 14:05:51 +090069 // DetectAndroidEcho detects echo as description.
70 DetectAndroidEcho bool
Fumitoshi Ukaib79b2902015-07-16 16:40:09 +090071
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +090072 f *os.File
73 nodes []*DepNode
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +090074 exports map[string]bool
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +090075
76 ctx *execContext
77
Fumitoshi Ukaib816ffb2015-07-28 17:46:04 +090078 ruleID int
79 done map[string]nodeState
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090080}
81
Fumitoshi Ukaib79b2902015-07-16 16:40:09 +090082func (n *NinjaGenerator) init(g *DepGraph) {
Fumitoshi Ukai8a1110d2015-07-29 13:18:40 +090083 g.resolveVPATH()
Fumitoshi Ukaib79b2902015-07-16 16:40:09 +090084 n.nodes = g.nodes
85 n.exports = g.exports
86 n.ctx = newExecContext(g.vars, g.vpaths, true)
Fumitoshi Ukaib816ffb2015-07-28 17:46:04 +090087 n.done = make(map[string]nodeState)
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090088}
89
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +090090func getDepfileImpl(ss string) (string, error) {
Shinichiro Hamaji6dca2202015-05-27 18:00:34 +090091 tss := ss + " "
Fumitoshi Ukaib8b80502015-07-21 14:28:26 +090092 if (!strings.Contains(tss, " -MD ") && !strings.Contains(tss, " -MMD ")) || !strings.Contains(tss, " -c ") {
Shinichiro Hamaji6dca2202015-05-27 18:00:34 +090093 return "", nil
94 }
95
96 mfIndex := strings.Index(ss, " -MF ")
97 if mfIndex >= 0 {
98 mf := trimLeftSpace(ss[mfIndex+4:])
99 if strings.Index(mf, " -MF ") >= 0 {
100 return "", fmt.Errorf("Multiple output file candidates in %s", ss)
101 }
102 mfEndIndex := strings.IndexAny(mf, " \t\n")
103 if mfEndIndex >= 0 {
104 mf = mf[:mfEndIndex]
105 }
106
Shinichiro Hamaji6dca2202015-05-27 18:00:34 +0900107 return mf, nil
108 }
109
110 outIndex := strings.Index(ss, " -o ")
111 if outIndex < 0 {
112 return "", fmt.Errorf("Cannot find the depfile in %s", ss)
113 }
114 out := trimLeftSpace(ss[outIndex+4:])
115 if strings.Index(out, " -o ") >= 0 {
116 return "", fmt.Errorf("Multiple output file candidates in %s", ss)
117 }
118 outEndIndex := strings.IndexAny(out, " \t\n")
119 if outEndIndex >= 0 {
120 out = out[:outEndIndex]
121 }
122 return stripExt(out) + ".d", nil
123}
124
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900125// getDepfile gets depfile from cmdline, and returns cmdline and depfile.
126func getDepfile(cmdline string) (string, string, error) {
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +0900127 // A hack for Android - llvm-rs-cc seems not to emit a dep file.
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900128 if strings.Contains(cmdline, "bin/llvm-rs-cc ") {
129 return cmdline, "", nil
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +0900130 }
131
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900132 depfile, err := getDepfileImpl(cmdline)
133 if depfile == "" || err != nil {
134 return cmdline, depfile, err
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +0900135 }
136
Shinichiro Hamaji3438ae32015-06-05 11:33:20 +0900137 // A hack for Makefiles generated by automake.
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900138 mvCmd := "(mv -f " + depfile + " "
139 if i := strings.LastIndex(cmdline, mvCmd); i >= 0 {
140 rest := cmdline[i+len(mvCmd):]
Shinichiro Hamaji3438ae32015-06-05 11:33:20 +0900141 ei := strings.IndexByte(rest, ')')
142 if ei < 0 {
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900143 return cmdline, "", fmt.Errorf("unbalanced parenthes? %s", cmdline)
Shinichiro Hamaji3438ae32015-06-05 11:33:20 +0900144 }
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900145 cmdline = cmdline[:i] + "(cp -f " + depfile + " " + rest
146 return cmdline, depfile, nil
Shinichiro Hamaji3438ae32015-06-05 11:33:20 +0900147 }
148
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +0900149 // A hack for Android to get .P files instead of .d.
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900150 p := stripExt(depfile) + ".P"
151 if strings.Contains(cmdline, p) {
152 rmfCmd := "; rm -f " + depfile
153 ncmdline := strings.Replace(cmdline, rmfCmd, "", 1)
154 if ncmdline == cmdline {
155 return cmdline, "", fmt.Errorf("cannot find removal of .d file: %s", cmdline)
156 }
157 return ncmdline, p, nil
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +0900158 }
159
160 // A hack for Android. For .s files, GCC does not use
161 // C preprocessor, so it ignores -MF flag.
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900162 as := "/" + stripExt(filepath.Base(depfile)) + ".s"
163 if strings.Contains(cmdline, as) {
164 return cmdline, "", nil
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +0900165 }
166
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900167 cmdline += fmt.Sprintf(" && cp %s %s.tmp", depfile, depfile)
168 depfile += ".tmp"
169 return cmdline, depfile, nil
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +0900170}
171
Fumitoshi Ukai60f92c02015-07-29 10:09:36 +0900172func trimTailingSlash(s string) string {
173 if s == "" {
174 return s
175 }
176 if s[len(s)-1] != '\\' {
177 return s
178 }
179 // drop single trailing slash - multiline_arg.mk
180 if len(s) > 2 && s[len(s)-2] != '\\' {
181 return s[:len(s)-1]
182 }
183 // preserve two trailing slash - escaped_backslash.mk
184 return s
185}
186
Shinichiro Hamaji246cd232015-05-27 17:08:23 +0900187func stripShellComment(s string) string {
188 if strings.IndexByte(s, '#') < 0 {
189 // Fast path.
190 return s
191 }
Fumitoshi Ukai43b56662015-07-15 10:41:47 +0900192 // set space as an initial value so the leading comment will be
193 // stripped out.
194 lastch := rune(' ')
Shinichiro Hamaji246cd232015-05-27 17:08:23 +0900195 var escape bool
196 var quote rune
Fumitoshi Ukai03e9ea62015-07-29 11:39:00 +0900197 var skip rune
198 var cmdsubst []rune
199 var buf bytes.Buffer
200Loop:
201 for _, c := range s {
202 if skip != 0 {
203 if skip != c {
204 continue Loop
205 }
206 if len(cmdsubst) > 0 && cmdsubst[len(cmdsubst)-1] == skip {
207 cmdsubst = cmdsubst[:len(cmdsubst)-1]
208 }
209 skip = 0
210 }
Fumitoshi Ukaib92f0282015-07-21 14:05:51 +0900211 if quote != 0 {
Shinichiro Hamaji246cd232015-05-27 17:08:23 +0900212 if quote == c && (quote == '\'' || !escape) {
213 quote = 0
214 }
215 } else if !escape {
Fumitoshi Ukai43b56662015-07-15 10:41:47 +0900216 if c == '#' && isWhitespace(lastch) {
Fumitoshi Ukai03e9ea62015-07-29 11:39:00 +0900217 if len(cmdsubst) == 0 {
218 // strip comment until the end of line.
219 skip = '\n'
220 continue Loop
221 }
222 // strip comment until the end of command subst.
223 skip = cmdsubst[len(cmdsubst)-1]
224 continue Loop
225 } else if c == '\'' || c == '"' {
Shinichiro Hamaji246cd232015-05-27 17:08:23 +0900226 quote = c
Fumitoshi Ukai03e9ea62015-07-29 11:39:00 +0900227 } else if lastch == '$' && c == '(' {
228 cmdsubst = append(cmdsubst, ')')
229 } else if c == '`' {
230 cmdsubst = append(cmdsubst, '`')
Shinichiro Hamaji246cd232015-05-27 17:08:23 +0900231 }
232 }
233 if escape {
234 escape = false
235 } else if c == '\\' {
236 escape = true
237 } else {
238 escape = false
239 }
Fumitoshi Ukai43b56662015-07-15 10:41:47 +0900240 lastch = c
Fumitoshi Ukai03e9ea62015-07-29 11:39:00 +0900241 buf.WriteRune(c)
Shinichiro Hamaji246cd232015-05-27 17:08:23 +0900242 }
Fumitoshi Ukai03e9ea62015-07-29 11:39:00 +0900243 return buf.String()
Shinichiro Hamaji246cd232015-05-27 17:08:23 +0900244}
245
Fumitoshi Ukai1713c6f2015-07-14 13:08:24 +0900246var ccRE = regexp.MustCompile(`^prebuilts/(gcc|clang)/.*(gcc|g\+\+|clang|clang\+\+) .* ?-c `)
247
248func gomaCmdForAndroidCompileCmd(cmd string) (string, bool) {
249 i := strings.Index(cmd, " ")
250 if i < 0 {
251 return cmd, false
252 }
253 driver := cmd[:i]
254 if strings.HasSuffix(driver, "ccache") {
255 return gomaCmdForAndroidCompileCmd(cmd[i+1:])
256 }
257 return cmd, ccRE.MatchString(cmd)
258}
259
Fumitoshi Ukaib92f0282015-07-21 14:05:51 +0900260func descriptionFromCmd(cmd string) (string, bool) {
261 if !strings.HasPrefix(cmd, "echo") || !isWhitespace(rune(cmd[4])) {
262 return "", false
263 }
264 echoarg := cmd[5:]
265
266 // strip outer quotes, and fail if it is not a single echo command.
267 var buf bytes.Buffer
268 var escape bool
269 var quote rune
270 for _, c := range echoarg {
271 if escape {
272 escape = false
273 buf.WriteRune(c)
274 continue
275 }
276 if c == '\\' {
277 escape = true
278 buf.WriteRune(c)
279 continue
280 }
281 if quote != 0 {
282 if c == quote {
283 quote = 0
284 continue
285 }
286 buf.WriteRune(c)
287 continue
288 }
289 switch c {
290 case '\'', '"', '`':
291 quote = c
292 case '<', '>', '&', '|', ';':
293 return "", false
294 default:
295 buf.WriteRune(c)
296 }
297 }
298 return buf.String(), true
299}
300
301func (n *NinjaGenerator) genShellScript(runners []runner) (cmd string, desc string, useLocalPool bool) {
302 const defaultDesc = "build $out"
303 var useGomacc bool
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900304 var buf bytes.Buffer
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900305 for i, r := range runners {
306 if i > 0 {
307 if runners[i-1].ignoreError {
308 buf.WriteString(" ; ")
309 } else {
310 buf.WriteString(" && ")
311 }
312 }
Fumitoshi Ukai60f92c02015-07-29 10:09:36 +0900313 cmd := trimTailingSlash(r.cmd)
314 cmd = stripShellComment(cmd)
Shinichiro Hamaji246cd232015-05-27 17:08:23 +0900315 cmd = trimLeftSpace(cmd)
Fumitoshi Ukai5af212f2015-07-28 17:57:17 +0900316 cmd = strings.Replace(cmd, "\\\n\t", "", -1)
Fumitoshi Ukai74663592015-07-17 17:53:11 +0900317 cmd = strings.Replace(cmd, "\\\n", "", -1)
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900318 cmd = strings.TrimRight(cmd, " \t\n;")
Fumitoshi Ukai701b0e02015-07-28 14:06:06 +0900319 cmd = escapeNinja(cmd)
Shinichiro Hamaji501f68e2015-05-27 17:33:41 +0900320 if cmd == "" {
321 cmd = "true"
322 }
Fumitoshi Ukai5af212f2015-07-28 17:57:17 +0900323 glog.V(2).Infof("cmd %q=>%q", r.cmd, cmd)
Fumitoshi Ukaib79b2902015-07-16 16:40:09 +0900324 if n.GomaDir != "" {
Fumitoshi Ukai1713c6f2015-07-14 13:08:24 +0900325 rcmd, ok := gomaCmdForAndroidCompileCmd(cmd)
326 if ok {
Fumitoshi Ukaib79b2902015-07-16 16:40:09 +0900327 cmd = fmt.Sprintf("%s/gomacc %s", n.GomaDir, rcmd)
Fumitoshi Ukai1713c6f2015-07-14 13:08:24 +0900328 useGomacc = true
329 }
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900330 }
Fumitoshi Ukaib92f0282015-07-21 14:05:51 +0900331 if n.DetectAndroidEcho && desc == "" {
332 d, ok := descriptionFromCmd(cmd)
333 if ok {
334 desc = d
335 cmd = "true"
336 }
337 }
Shinichiro Hamaji13653b22015-05-28 22:45:12 +0900338 needsSubShell := i > 0 || len(runners) > 1
339 if cmd[0] == '(' {
340 needsSubShell = false
341 }
342
343 if needsSubShell {
344 buf.WriteByte('(')
345 }
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900346 buf.WriteString(cmd)
347 if i == len(runners)-1 && r.ignoreError {
348 buf.WriteString(" ; true")
349 }
Shinichiro Hamaji13653b22015-05-28 22:45:12 +0900350 if needsSubShell {
351 buf.WriteByte(')')
352 }
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900353 }
Fumitoshi Ukaib92f0282015-07-21 14:05:51 +0900354 if desc == "" {
355 desc = defaultDesc
356 }
357 return buf.String(), desc, n.GomaDir != "" && !useGomacc
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900358}
359
Fumitoshi Ukaib79b2902015-07-16 16:40:09 +0900360func (n *NinjaGenerator) genRuleName() string {
Fumitoshi Ukai936de102015-06-08 11:21:16 +0900361 ruleName := fmt.Sprintf("rule%d", n.ruleID)
362 n.ruleID++
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900363 return ruleName
364}
365
Fumitoshi Ukai3dae8042015-07-22 13:49:23 +0900366func (n *NinjaGenerator) emitBuild(output, rule, inputs, orderOnlys string) {
367 fmt.Fprintf(n.f, "build %s: %s", escapeBuildTarget(output), rule)
368 if inputs != "" {
369 fmt.Fprintf(n.f, " %s", inputs)
370 }
371 if orderOnlys != "" {
372 fmt.Fprintf(n.f, " || %s", orderOnlys)
373 }
Fumitoshi Ukai26e83812015-07-21 14:38:33 +0900374}
375
376func escapeBuildTarget(s string) string {
Fumitoshi Ukai48ac8ae2015-07-29 11:52:28 +0900377 i := strings.IndexAny(s, "$: \\")
Fumitoshi Ukai26e83812015-07-21 14:38:33 +0900378 if i < 0 {
379 return s
380 }
Fumitoshi Ukai48ac8ae2015-07-29 11:52:28 +0900381 // unescapeInput only "\ ", "\=" unescape as " ", "=".
382 // TODO(ukai): which char should unescape, which should not here?
383 var esc rune
Fumitoshi Ukai26e83812015-07-21 14:38:33 +0900384 var buf bytes.Buffer
385 for _, c := range s {
386 switch c {
Fumitoshi Ukai48ac8ae2015-07-29 11:52:28 +0900387 case '\\':
388 esc = c
389 continue
Fumitoshi Ukai26e83812015-07-21 14:38:33 +0900390 case '$', ':', ' ':
Fumitoshi Ukai48ac8ae2015-07-29 11:52:28 +0900391 esc = 0
Fumitoshi Ukai26e83812015-07-21 14:38:33 +0900392 buf.WriteByte('$')
393 }
Fumitoshi Ukai48ac8ae2015-07-29 11:52:28 +0900394 if esc != 0 {
395 buf.WriteRune(esc)
396 esc = 0
397 }
Fumitoshi Ukai26e83812015-07-21 14:38:33 +0900398 buf.WriteRune(c)
399 }
Fumitoshi Ukai48ac8ae2015-07-29 11:52:28 +0900400 if esc != 0 {
401 buf.WriteRune(esc)
402 }
Fumitoshi Ukai26e83812015-07-21 14:38:33 +0900403 return buf.String()
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900404}
405
Fumitoshi Ukai8a1110d2015-07-29 13:18:40 +0900406func (n *NinjaGenerator) dependency(node *DepNode) (string, string) {
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900407 var deps []string
Fumitoshi Ukai3dae8042015-07-22 13:49:23 +0900408 seen := make(map[string]bool)
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900409 for _, d := range node.Deps {
Fumitoshi Ukai3dae8042015-07-22 13:49:23 +0900410 t := escapeBuildTarget(d.Output)
411 if seen[t] {
412 continue
413 }
414 deps = append(deps, t)
415 seen[t] = true
Fumitoshi Ukaic916ea22015-06-30 09:52:13 +0900416 }
417 var orderOnlys []string
418 for _, d := range node.OrderOnlys {
Fumitoshi Ukai3dae8042015-07-22 13:49:23 +0900419 t := escapeBuildTarget(d.Output)
420 if seen[t] {
421 continue
422 }
423 orderOnlys = append(orderOnlys, t)
424 seen[t] = true
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900425 }
Fumitoshi Ukai3dae8042015-07-22 13:49:23 +0900426 return strings.Join(deps, " "), strings.Join(orderOnlys, " ")
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900427}
428
Fumitoshi Ukai701b0e02015-07-28 14:06:06 +0900429func escapeNinja(s string) string {
430 return strings.Replace(s, "$", "$$", -1)
431}
432
Fumitoshi Ukaicf6c6c22015-07-21 15:24:35 +0900433func escapeShell(s string) string {
434 i := strings.IndexAny(s, "$`!\\\"")
435 if i < 0 {
436 return s
437 }
438 var buf bytes.Buffer
439 var lastDollar bool
440 for _, c := range s {
441 switch c {
442 case '$':
443 if lastDollar {
444 buf.WriteRune(c)
445 lastDollar = false
446 continue
447 }
448 buf.WriteString(`\$`)
449 lastDollar = true
450 continue
451 case '`', '"', '!', '\\':
452 buf.WriteByte('\\')
453 }
454 buf.WriteRune(c)
455 lastDollar = false
456 }
457 return buf.String()
458}
459
Fumitoshi Ukaib6adb392015-07-28 16:46:11 +0900460func (n *NinjaGenerator) ninjaVars(s string, nv [][]string, esc func(string) string) string {
461 for _, v := range nv {
462 k, v := v[0], v[1]
463 if v == "" {
464 continue
465 }
Fumitoshi Ukai53c880d2015-07-28 17:10:15 +0900466 if strings.Contains(v, "/./") || strings.Contains(v, "/../") || strings.Contains(v, "$") {
467 // ninja will normalize paths (/./, /../), so keep it as is
468 // ninja will emit quoted string for $
Fumitoshi Ukaib6adb392015-07-28 16:46:11 +0900469 continue
470 }
471 if esc != nil {
472 v = esc(v)
473 }
474 s = strings.Replace(s, v, k, -1)
475 }
476 return s
477}
478
Fumitoshi Ukaib79b2902015-07-16 16:40:09 +0900479func (n *NinjaGenerator) emitNode(node *DepNode) error {
Fumitoshi Ukai8a1110d2015-07-29 13:18:40 +0900480 output := node.Output
481 if _, found := n.done[output]; found {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900482 return nil
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900483 }
Fumitoshi Ukai8a1110d2015-07-29 13:18:40 +0900484 n.done[output] = nodeVisit
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900485
Fumitoshi Ukaic916ea22015-06-30 09:52:13 +0900486 if len(node.Cmds) == 0 && len(node.Deps) == 0 && len(node.OrderOnlys) == 0 && !node.IsPhony {
Fumitoshi Ukai8a1110d2015-07-29 13:18:40 +0900487 if _, ok := n.ctx.vpaths.exists(output); ok {
488 n.done[output] = nodeFile
Fumitoshi Ukai3857a242015-07-17 17:15:12 +0900489 return nil
490 }
Fumitoshi Ukai8a1110d2015-07-29 13:18:40 +0900491 o := filepath.Clean(output)
492 if o != output {
Fumitoshi Ukaib816ffb2015-07-28 17:46:04 +0900493 // if normalized target has been done, it marks as alias.
494 if s, found := n.done[o]; found {
495 glog.V(1).Infof("node %s=%s => %s=alias", o, s, node.Output)
Fumitoshi Ukai8a1110d2015-07-29 13:18:40 +0900496 n.done[output] = nodeAlias
Fumitoshi Ukaib816ffb2015-07-28 17:46:04 +0900497 return nil
498 }
499 }
Fumitoshi Ukai786846d2015-07-28 18:08:47 +0900500 if node.Filename == "" {
Fumitoshi Ukai8a1110d2015-07-29 13:18:40 +0900501 n.done[output] = nodeMissing
Fumitoshi Ukai786846d2015-07-28 18:08:47 +0900502 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900503 return nil
Shinichiro Hamajifd4aaa02015-05-26 15:40:53 +0900504 }
505
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900506 runners, _, err := createRunners(n.ctx, node)
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900507 if err != nil {
508 return err
509 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900510 ruleName := "phony"
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900511 useLocalPool := false
Fumitoshi Ukai8a1110d2015-07-29 13:18:40 +0900512 inputs, orderOnlys := n.dependency(node)
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900513 if len(runners) > 0 {
514 ruleName = n.genRuleName()
Fumitoshi Ukai701b0e02015-07-28 14:06:06 +0900515 fmt.Fprintf(n.f, "\n# rule for %q\n", node.Output)
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900516 fmt.Fprintf(n.f, "rule %s\n", ruleName)
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900517
Fumitoshi Ukaib92f0282015-07-21 14:05:51 +0900518 ss, desc, ulp := n.genShellScript(runners)
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900519 if ulp {
520 useLocalPool = true
521 }
Fumitoshi Ukaib92f0282015-07-21 14:05:51 +0900522 fmt.Fprintf(n.f, " description = %s\n", desc)
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900523 cmdline, depfile, err := getDepfile(ss)
Shinichiro Hamaji0ab9a2f2015-05-27 18:32:46 +0900524 if err != nil {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900525 return err
Shinichiro Hamaji0ab9a2f2015-05-27 18:32:46 +0900526 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900527 if depfile != "" {
528 fmt.Fprintf(n.f, " depfile = %s\n", depfile)
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900529 fmt.Fprintf(n.f, " deps = gcc\n")
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900530 }
Fumitoshi Ukaib6adb392015-07-28 16:46:11 +0900531 nv := [][]string{
532 []string{"${in}", inputs},
Fumitoshi Ukai8a1110d2015-07-29 13:18:40 +0900533 []string{"${out}", escapeNinja(output)},
Fumitoshi Ukaib6adb392015-07-28 16:46:11 +0900534 }
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900535 // It seems Linux is OK with ~130kB.
536 // TODO: Find this number automatically.
537 ArgLenLimit := 100 * 1000
Fumitoshi Ukaicf6c6c22015-07-21 15:24:35 +0900538 if len(cmdline) > ArgLenLimit {
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900539 fmt.Fprintf(n.f, " rspfile = $out.rsp\n")
Fumitoshi Ukaib6adb392015-07-28 16:46:11 +0900540 cmdline = n.ninjaVars(cmdline, nv, nil)
Fumitoshi Ukaicf6c6c22015-07-21 15:24:35 +0900541 fmt.Fprintf(n.f, " rspfile_content = %s\n", cmdline)
542 fmt.Fprintf(n.f, " command = %s $out.rsp\n", n.ctx.shell)
543 } else {
Fumitoshi Ukai3dae8042015-07-22 13:49:23 +0900544 cmdline = escapeShell(cmdline)
Fumitoshi Ukaib6adb392015-07-28 16:46:11 +0900545 cmdline = n.ninjaVars(cmdline, nv, escapeShell)
Fumitoshi Ukai3dae8042015-07-22 13:49:23 +0900546 fmt.Fprintf(n.f, " command = %s -c \"%s\"\n", n.ctx.shell, cmdline)
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900547 }
548 }
Fumitoshi Ukai8a1110d2015-07-29 13:18:40 +0900549 n.emitBuild(output, ruleName, inputs, orderOnlys)
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900550 if useLocalPool {
551 fmt.Fprintf(n.f, " pool = local_pool\n")
552 }
Fumitoshi Ukaic916ea22015-06-30 09:52:13 +0900553 fmt.Fprintf(n.f, "\n")
Fumitoshi Ukai8a1110d2015-07-29 13:18:40 +0900554 n.done[output] = nodeBuild
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900555
556 for _, d := range node.Deps {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900557 err := n.emitNode(d)
558 if err != nil {
559 return err
560 }
Fumitoshi Ukaib816ffb2015-07-28 17:46:04 +0900561 glog.V(1).Infof("node %s dep node %q %s", node.Output, d.Output, n.done[d.Output])
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900562 }
Fumitoshi Ukaic916ea22015-06-30 09:52:13 +0900563 for _, d := range node.OrderOnlys {
564 err := n.emitNode(d)
565 if err != nil {
566 return err
567 }
Fumitoshi Ukaib816ffb2015-07-28 17:46:04 +0900568 glog.V(1).Infof("node %s order node %q %s", node.Output, d.Output, n.done[d.Output])
Fumitoshi Ukaic916ea22015-06-30 09:52:13 +0900569 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900570 return nil
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900571}
572
Fumitoshi Ukaid4a016e2015-07-27 16:50:00 +0900573func (n *NinjaGenerator) emitRegenRules() error {
574 if len(n.Args) == 0 {
575 return nil
576 }
577 mkfiles, err := n.ctx.ev.EvaluateVar("MAKEFILE_LIST")
578 if err != nil {
579 return err
580 }
581 fmt.Fprintf(n.f, `
582rule regen_ninja
583 description = Regenerate ninja files due to dependency
584 generator=1
585 command=%s
586`, strings.Join(n.Args, " "))
587 fmt.Fprintf(n.f, "build %s: regen_ninja %s", n.ninjaName(), mkfiles)
588 // TODO: Add dependencies to directories read by $(wildcard) or
589 // $(shell find).
590 if len(usedEnvs) > 0 {
591 fmt.Fprintf(n.f, " %s", n.envlistName())
592 }
593 fmt.Fprintf(n.f, "\n\n")
Fumitoshi Ukaid4a016e2015-07-27 16:50:00 +0900594 return nil
Fumitoshi Ukai861c9f22015-07-22 10:54:01 +0900595}
596
Fumitoshi Ukaid4a016e2015-07-27 16:50:00 +0900597func (n *NinjaGenerator) shName() string {
598 return fmt.Sprintf("ninja%s.sh", n.Suffix)
Fumitoshi Ukai861c9f22015-07-22 10:54:01 +0900599}
600
Fumitoshi Ukaid4a016e2015-07-27 16:50:00 +0900601func (n *NinjaGenerator) ninjaName() string {
602 return fmt.Sprintf("build%s.ninja", n.Suffix)
603}
604
605func (n *NinjaGenerator) envlistName() string {
606 return fmt.Sprintf(".kati_env%s", n.Suffix)
607}
608
609func (n *NinjaGenerator) generateEnvlist() (err error) {
610 f, err := os.Create(n.envlistName())
611 if err != nil {
612 return err
613 }
614 defer func() {
615 cerr := f.Close()
616 if err == nil {
617 err = cerr
618 }
619 }()
620 for k := range usedEnvs {
621 v, err := n.ctx.ev.EvaluateVar(k)
622 if err != nil {
623 return err
624 }
Fumitoshi Ukaid5570262015-07-28 12:44:31 +0900625 fmt.Fprintf(f, "%q=%q\n", k, v)
Fumitoshi Ukaid4a016e2015-07-27 16:50:00 +0900626 }
627 return nil
628}
629
630func (n *NinjaGenerator) generateShell() (err error) {
631 f, err := os.Create(n.shName())
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900632 if err != nil {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900633 return err
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900634 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900635 defer func() {
636 cerr := f.Close()
637 if err == nil {
638 err = cerr
639 }
640 }()
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900641
Fumitoshi Ukaibe9104d2015-07-28 14:20:43 +0900642 fmt.Fprintf(f, "#!/bin/bash\n")
Fumitoshi Ukai08db53a2015-07-17 14:45:57 +0900643 fmt.Fprintf(f, "# Generated by kati %s\n", gitVersion)
644 fmt.Fprintln(f)
645 fmt.Fprintln(f, `cd $(dirname "$0")`)
Fumitoshi Ukaid4a016e2015-07-27 16:50:00 +0900646 if n.Suffix != "" {
647 fmt.Fprintf(f, "if [ -f %s ]; then\n export $(cat %s)\nfi\n", n.envlistName(), n.envlistName())
648 }
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900649 for name, export := range n.exports {
Fumitoshi Ukaif90f73c2015-07-28 14:37:02 +0900650 // export "a b"=c will error on bash
651 // bash: export `a b=c': not a valid identifier
652 if strings.ContainsAny(name, " \t\n\r") {
653 glog.V(1).Infof("ignore export %q (export:%t)", name, export)
654 continue
655 }
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900656 if export {
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900657 v, err := n.ctx.ev.EvaluateVar(name)
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900658 if err != nil {
659 return err
660 }
Fumitoshi Ukaid5570262015-07-28 12:44:31 +0900661 fmt.Fprintf(f, "export %q=%q\n", name, v)
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900662 } else {
Fumitoshi Ukaid5570262015-07-28 12:44:31 +0900663 fmt.Fprintf(f, "unset %q\n", name)
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900664 }
665 }
Fumitoshi Ukaib79b2902015-07-16 16:40:09 +0900666 if n.GomaDir == "" {
Fumitoshi Ukaid4a016e2015-07-27 16:50:00 +0900667 fmt.Fprintf(f, `exec ninja -f %s "$@"`+"\n", n.ninjaName())
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900668 } else {
Fumitoshi Ukaid4a016e2015-07-27 16:50:00 +0900669 fmt.Fprintf(f, `exec ninja -f %s -j500 "$@"`+"\n", n.ninjaName())
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900670 }
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900671
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900672 return f.Chmod(0755)
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900673}
674
Fumitoshi Ukaid4a016e2015-07-27 16:50:00 +0900675func (n *NinjaGenerator) generateNinja(defaultTarget string) (err error) {
676 f, err := os.Create(n.ninjaName())
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900677 if err != nil {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900678 return err
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900679 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900680 defer func() {
681 cerr := f.Close()
682 if err == nil {
683 err = cerr
684 }
685 }()
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900686
687 n.f = f
Fumitoshi Ukai9dd48a92015-07-14 13:39:03 +0900688 fmt.Fprintf(n.f, "# Generated by kati %s\n", gitVersion)
Shinichiro Hamajiea2e0de2015-05-28 15:22:16 +0900689 fmt.Fprintf(n.f, "\n")
690
Fumitoshi Ukaib5612a82015-07-14 13:22:17 +0900691 if len(usedEnvs) > 0 {
692 fmt.Fprintln(n.f, "# Environment variables used:")
693 var names []string
694 for name := range usedEnvs {
695 names = append(names, name)
696 }
697 sort.Strings(names)
698 for _, name := range names {
699 v, err := n.ctx.ev.EvaluateVar(name)
700 if err != nil {
701 return err
702 }
Fumitoshi Ukaid5570262015-07-28 12:44:31 +0900703 fmt.Fprintf(n.f, "# %q=%q\n", name, v)
Fumitoshi Ukaib5612a82015-07-14 13:22:17 +0900704 }
705 fmt.Fprintf(n.f, "\n")
706 }
707
Fumitoshi Ukaib79b2902015-07-16 16:40:09 +0900708 if n.GomaDir != "" {
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900709 fmt.Fprintf(n.f, "pool local_pool\n")
Fumitoshi Ukaid4a016e2015-07-27 16:50:00 +0900710 fmt.Fprintf(n.f, " depth = %d\n\n", runtime.NumCPU())
711 }
712
713 err = n.emitRegenRules()
714 if err != nil {
715 return err
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900716 }
717
Fumitoshi Ukai3dae8042015-07-22 13:49:23 +0900718 // defining $out for $@ and $in for $^ here doesn't work well,
719 // because these texts will be processed in escapeShell...
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900720 for _, node := range n.nodes {
721 err := n.emitNode(node)
722 if err != nil {
723 return err
724 }
Fumitoshi Ukaib816ffb2015-07-28 17:46:04 +0900725 glog.V(1).Infof("node %q %s", node.Output, n.done[node.Output])
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900726 }
Fumitoshi Ukaie08e9ee2015-07-13 13:14:41 +0900727
Fumitoshi Ukaib816ffb2015-07-28 17:46:04 +0900728 // emit phony targets for visited nodes that are
729 // - not existing file
730 // - not alias for other targets.
731 var nodes []string
732 for node, state := range n.done {
733 if state != nodeVisit {
734 continue
735 }
736 nodes = append(nodes, node)
737 }
738 if len(nodes) > 0 {
739 fmt.Fprintln(n.f)
740 sort.Strings(nodes)
741 for _, node := range nodes {
742 n.emitBuild(node, "phony", "", "")
743 fmt.Fprintln(n.f)
744 n.done[node] = nodeBuild
745 }
746 }
747
748 // emit default if the target was emitted.
749 if defaultTarget != "" && n.done[defaultTarget] == nodeBuild {
Fumitoshi Ukai701b0e02015-07-28 14:06:06 +0900750 fmt.Fprintf(n.f, "\ndefault %s\n", escapeNinja(defaultTarget))
Fumitoshi Ukaib79b2902015-07-16 16:40:09 +0900751 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900752 return nil
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900753}
754
Fumitoshi Ukaib79b2902015-07-16 16:40:09 +0900755// Save generates build.ninja from DepGraph.
Fumitoshi Ukaid4a016e2015-07-27 16:50:00 +0900756func (n *NinjaGenerator) Save(g *DepGraph, name string, targets []string) error {
Fumitoshi Ukai49599e52015-06-26 10:10:24 +0900757 startTime := time.Now()
Fumitoshi Ukaib79b2902015-07-16 16:40:09 +0900758 n.init(g)
Fumitoshi Ukaid4a016e2015-07-27 16:50:00 +0900759 err := n.generateEnvlist()
760 if err != nil {
761 return err
762 }
763 err = n.generateShell()
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900764 if err != nil {
765 return err
766 }
Fumitoshi Ukaib79b2902015-07-16 16:40:09 +0900767 var defaultTarget string
768 if len(targets) == 0 && len(g.nodes) > 0 {
769 defaultTarget = g.nodes[0].Output
770 }
Fumitoshi Ukaid4a016e2015-07-27 16:50:00 +0900771 err = n.generateNinja(defaultTarget)
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900772 if err != nil {
773 return err
774 }
Fumitoshi Ukai49599e52015-06-26 10:10:24 +0900775 logStats("generate ninja time: %q", time.Since(startTime))
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900776 return nil
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900777}