blob: fc35da54989c8f9fecd472f14373e3e57a13c911 [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"
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090027)
28
Fumitoshi Ukai3ec25b52015-06-25 15:39:35 +090029type ninjaGenerator struct {
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +090030 f *os.File
31 nodes []*DepNode
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +090032 exports map[string]bool
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +090033
34 ctx *execContext
35
Fumitoshi Ukaie08e9ee2015-07-13 13:14:41 +090036 ruleID int
37 done map[string]bool
38 shortNames map[string][]string
39 gomaDir string
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090040}
41
Fumitoshi Ukai3ec25b52015-06-25 15:39:35 +090042func newNinjaGenerator(g *DepGraph, gomaDir string) *ninjaGenerator {
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +090043 ctx := newExecContext(g.vars, true)
Fumitoshi Ukai3ec25b52015-06-25 15:39:35 +090044 return &ninjaGenerator{
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +090045 nodes: g.nodes,
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +090046 exports: g.exports,
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +090047 ctx: ctx,
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +090048 done: make(map[string]bool),
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +090049 gomaDir: gomaDir,
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090050 }
51}
52
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +090053func getDepfileImpl(ss string) (string, error) {
Shinichiro Hamaji6dca2202015-05-27 18:00:34 +090054 tss := ss + " "
55 if !strings.Contains(tss, " -MD ") && !strings.Contains(tss, " -MMD ") {
56 return "", nil
57 }
58
59 mfIndex := strings.Index(ss, " -MF ")
60 if mfIndex >= 0 {
61 mf := trimLeftSpace(ss[mfIndex+4:])
62 if strings.Index(mf, " -MF ") >= 0 {
63 return "", fmt.Errorf("Multiple output file candidates in %s", ss)
64 }
65 mfEndIndex := strings.IndexAny(mf, " \t\n")
66 if mfEndIndex >= 0 {
67 mf = mf[:mfEndIndex]
68 }
69
Shinichiro Hamaji6dca2202015-05-27 18:00:34 +090070 return mf, nil
71 }
72
73 outIndex := strings.Index(ss, " -o ")
74 if outIndex < 0 {
75 return "", fmt.Errorf("Cannot find the depfile in %s", ss)
76 }
77 out := trimLeftSpace(ss[outIndex+4:])
78 if strings.Index(out, " -o ") >= 0 {
79 return "", fmt.Errorf("Multiple output file candidates in %s", ss)
80 }
81 outEndIndex := strings.IndexAny(out, " \t\n")
82 if outEndIndex >= 0 {
83 out = out[:outEndIndex]
84 }
85 return stripExt(out) + ".d", nil
86}
87
Fumitoshi Ukai923b1282015-07-13 12:53:30 +090088// getDepfile gets depfile from cmdline, and returns cmdline and depfile.
89func getDepfile(cmdline string) (string, string, error) {
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +090090 // A hack for Android - llvm-rs-cc seems not to emit a dep file.
Fumitoshi Ukai923b1282015-07-13 12:53:30 +090091 if strings.Contains(cmdline, "bin/llvm-rs-cc ") {
92 return cmdline, "", nil
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +090093 }
94
Fumitoshi Ukai923b1282015-07-13 12:53:30 +090095 depfile, err := getDepfileImpl(cmdline)
96 if depfile == "" || err != nil {
97 return cmdline, depfile, err
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +090098 }
99
Shinichiro Hamaji3438ae32015-06-05 11:33:20 +0900100 // A hack for Makefiles generated by automake.
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900101 mvCmd := "(mv -f " + depfile + " "
102 if i := strings.LastIndex(cmdline, mvCmd); i >= 0 {
103 rest := cmdline[i+len(mvCmd):]
Shinichiro Hamaji3438ae32015-06-05 11:33:20 +0900104 ei := strings.IndexByte(rest, ')')
105 if ei < 0 {
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900106 return cmdline, "", fmt.Errorf("unbalanced parenthes? %s", cmdline)
Shinichiro Hamaji3438ae32015-06-05 11:33:20 +0900107 }
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900108 cmdline = cmdline[:i] + "(cp -f " + depfile + " " + rest
109 return cmdline, depfile, nil
Shinichiro Hamaji3438ae32015-06-05 11:33:20 +0900110 }
111
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +0900112 // A hack for Android to get .P files instead of .d.
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900113 p := stripExt(depfile) + ".P"
114 if strings.Contains(cmdline, p) {
115 rmfCmd := "; rm -f " + depfile
116 ncmdline := strings.Replace(cmdline, rmfCmd, "", 1)
117 if ncmdline == cmdline {
118 return cmdline, "", fmt.Errorf("cannot find removal of .d file: %s", cmdline)
119 }
120 return ncmdline, p, nil
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +0900121 }
122
123 // A hack for Android. For .s files, GCC does not use
124 // C preprocessor, so it ignores -MF flag.
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900125 as := "/" + stripExt(filepath.Base(depfile)) + ".s"
126 if strings.Contains(cmdline, as) {
127 return cmdline, "", nil
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +0900128 }
129
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900130 cmdline += fmt.Sprintf(" && cp %s %s.tmp", depfile, depfile)
131 depfile += ".tmp"
132 return cmdline, depfile, nil
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +0900133}
134
Shinichiro Hamaji246cd232015-05-27 17:08:23 +0900135func stripShellComment(s string) string {
136 if strings.IndexByte(s, '#') < 0 {
137 // Fast path.
138 return s
139 }
140 var escape bool
141 var quote rune
142 for i, c := range s {
143 if quote > 0 {
144 if quote == c && (quote == '\'' || !escape) {
145 quote = 0
146 }
147 } else if !escape {
148 if c == '#' {
149 return s[:i]
150 } else if c == '\'' || c == '"' || c == '`' {
151 quote = c
152 }
153 }
154 if escape {
155 escape = false
156 } else if c == '\\' {
157 escape = true
158 } else {
159 escape = false
160 }
161 }
162 return s
163}
164
Fumitoshi Ukai1713c6f2015-07-14 13:08:24 +0900165var ccRE = regexp.MustCompile(`^prebuilts/(gcc|clang)/.*(gcc|g\+\+|clang|clang\+\+) .* ?-c `)
166
167func gomaCmdForAndroidCompileCmd(cmd string) (string, bool) {
168 i := strings.Index(cmd, " ")
169 if i < 0 {
170 return cmd, false
171 }
172 driver := cmd[:i]
173 if strings.HasSuffix(driver, "ccache") {
174 return gomaCmdForAndroidCompileCmd(cmd[i+1:])
175 }
176 return cmd, ccRE.MatchString(cmd)
177}
178
Fumitoshi Ukai3ec25b52015-06-25 15:39:35 +0900179func (n *ninjaGenerator) genShellScript(runners []runner) (string, bool) {
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900180 useGomacc := false
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900181 var buf bytes.Buffer
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900182 for i, r := range runners {
183 if i > 0 {
184 if runners[i-1].ignoreError {
185 buf.WriteString(" ; ")
186 } else {
187 buf.WriteString(" && ")
188 }
189 }
Shinichiro Hamaji246cd232015-05-27 17:08:23 +0900190 cmd := stripShellComment(r.cmd)
191 cmd = trimLeftSpace(cmd)
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900192 cmd = strings.Replace(cmd, "\\\n", " ", -1)
193 cmd = strings.TrimRight(cmd, " \t\n;")
194 cmd = strings.Replace(cmd, "$", "$$", -1)
195 cmd = strings.Replace(cmd, "\t", " ", -1)
Shinichiro Hamaji501f68e2015-05-27 17:33:41 +0900196 if cmd == "" {
197 cmd = "true"
198 }
Fumitoshi Ukai1713c6f2015-07-14 13:08:24 +0900199 if n.gomaDir != "" {
200 rcmd, ok := gomaCmdForAndroidCompileCmd(cmd)
201 if ok {
202 cmd = fmt.Sprintf("%s/gomacc %s", n.gomaDir, rcmd)
203 useGomacc = true
204 }
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900205 }
Shinichiro Hamaji13653b22015-05-28 22:45:12 +0900206
207 needsSubShell := i > 0 || len(runners) > 1
208 if cmd[0] == '(' {
209 needsSubShell = false
210 }
211
212 if needsSubShell {
213 buf.WriteByte('(')
214 }
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900215 buf.WriteString(cmd)
216 if i == len(runners)-1 && r.ignoreError {
217 buf.WriteString(" ; true")
218 }
Shinichiro Hamaji13653b22015-05-28 22:45:12 +0900219 if needsSubShell {
220 buf.WriteByte(')')
221 }
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900222 }
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900223 return buf.String(), n.gomaDir != "" && !useGomacc
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900224}
225
Fumitoshi Ukai3ec25b52015-06-25 15:39:35 +0900226func (n *ninjaGenerator) genRuleName() string {
Fumitoshi Ukai936de102015-06-08 11:21:16 +0900227 ruleName := fmt.Sprintf("rule%d", n.ruleID)
228 n.ruleID++
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900229 return ruleName
230}
231
Fumitoshi Ukai3ec25b52015-06-25 15:39:35 +0900232func (n *ninjaGenerator) emitBuild(output, rule, dep string) {
Shinichiro Hamajiea2e0de2015-05-28 15:22:16 +0900233 fmt.Fprintf(n.f, "build %s: %s%s\n", output, rule, dep)
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900234}
235
236func getDepString(node *DepNode) string {
237 var deps []string
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900238 for _, d := range node.Deps {
Fumitoshi Ukaic916ea22015-06-30 09:52:13 +0900239 deps = append(deps, d.Output)
240 }
241 var orderOnlys []string
242 for _, d := range node.OrderOnlys {
243 orderOnlys = append(orderOnlys, d.Output)
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900244 }
245 dep := ""
246 if len(deps) > 0 {
247 dep += fmt.Sprintf(" %s", strings.Join(deps, " "))
248 }
249 if len(orderOnlys) > 0 {
250 dep += fmt.Sprintf(" || %s", strings.Join(orderOnlys, " "))
251 }
252 return dep
253}
254
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900255func (n *ninjaGenerator) emitNode(node *DepNode) error {
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900256 if n.done[node.Output] {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900257 return nil
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900258 }
259 n.done[node.Output] = true
260
Fumitoshi Ukaic916ea22015-06-30 09:52:13 +0900261 if len(node.Cmds) == 0 && len(node.Deps) == 0 && len(node.OrderOnlys) == 0 && !node.IsPhony {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900262 return nil
Shinichiro Hamajifd4aaa02015-05-26 15:40:53 +0900263 }
264
Fumitoshi Ukaie08e9ee2015-07-13 13:14:41 +0900265 base := filepath.Base(node.Output)
266 if base != node.Output {
267 n.shortNames[base] = append(n.shortNames[base], node.Output)
268 }
269
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900270 runners, _, err := createRunners(n.ctx, node)
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900271 if err != nil {
272 return err
273 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900274 ruleName := "phony"
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900275 useLocalPool := false
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900276 if len(runners) > 0 {
277 ruleName = n.genRuleName()
Fumitoshi Ukaic916ea22015-06-30 09:52:13 +0900278 fmt.Fprintf(n.f, "\n# rule for %s\n", node.Output)
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900279 fmt.Fprintf(n.f, "rule %s\n", ruleName)
280 fmt.Fprintf(n.f, " description = build $out\n")
281
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900282 ss, ulp := n.genShellScript(runners)
283 if ulp {
284 useLocalPool = true
285 }
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900286 cmdline, depfile, err := getDepfile(ss)
Shinichiro Hamaji0ab9a2f2015-05-27 18:32:46 +0900287 if err != nil {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900288 return err
Shinichiro Hamaji0ab9a2f2015-05-27 18:32:46 +0900289 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900290 if depfile != "" {
291 fmt.Fprintf(n.f, " depfile = %s\n", depfile)
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900292 fmt.Fprintf(n.f, " deps = gcc\n")
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900293 }
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900294 // It seems Linux is OK with ~130kB.
295 // TODO: Find this number automatically.
296 ArgLenLimit := 100 * 1000
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900297 if len(ss) > ArgLenLimit {
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900298 fmt.Fprintf(n.f, " rspfile = $out.rsp\n")
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900299 fmt.Fprintf(n.f, " rspfile_content = %s\n", ss)
300 ss = "sh $out.rsp"
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900301 }
Fumitoshi Ukai923b1282015-07-13 12:53:30 +0900302 fmt.Fprintf(n.f, " command = %s\n", cmdline)
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900303
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900304 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900305 n.emitBuild(node.Output, ruleName, getDepString(node))
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900306 if useLocalPool {
307 fmt.Fprintf(n.f, " pool = local_pool\n")
308 }
Fumitoshi Ukaic916ea22015-06-30 09:52:13 +0900309 fmt.Fprintf(n.f, "\n")
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900310
311 for _, d := range node.Deps {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900312 err := n.emitNode(d)
313 if err != nil {
314 return err
315 }
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900316 }
Fumitoshi Ukaic916ea22015-06-30 09:52:13 +0900317 for _, d := range node.OrderOnlys {
318 err := n.emitNode(d)
319 if err != nil {
320 return err
321 }
322 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900323 return nil
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900324}
325
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900326func (n *ninjaGenerator) generateShell() (err error) {
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900327 f, err := os.Create("ninja.sh")
328 if err != nil {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900329 return err
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900330 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900331 defer func() {
332 cerr := f.Close()
333 if err == nil {
334 err = cerr
335 }
336 }()
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900337
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900338 fmt.Fprintf(f, "#!%s\n", n.ctx.shell)
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900339 for name, export := range n.exports {
340 if export {
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900341 v, err := n.ctx.ev.EvaluateVar(name)
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900342 if err != nil {
343 return err
344 }
345 fmt.Fprintf(f, "export %s=%s\n", name, v)
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900346 } else {
347 fmt.Fprintf(f, "unset %s\n", name)
348 }
349 }
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900350 if n.gomaDir == "" {
Shinichiro Hamaji096c7e02015-06-04 17:13:26 +0900351 fmt.Fprintln(f, `exec ninja "$@"`)
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900352 } else {
Shinichiro Hamaji096c7e02015-06-04 17:13:26 +0900353 fmt.Fprintln(f, `exec ninja -j300 "$@"`)
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900354 }
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900355
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900356 return f.Chmod(0755)
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900357}
358
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900359func (n *ninjaGenerator) generateNinja() (err error) {
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900360 f, err := os.Create("build.ninja")
361 if err != nil {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900362 return err
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900363 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900364 defer func() {
365 cerr := f.Close()
366 if err == nil {
367 err = cerr
368 }
369 }()
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900370
371 n.f = f
Shinichiro Hamajiea2e0de2015-05-28 15:22:16 +0900372 fmt.Fprintf(n.f, "# Generated by kati\n")
373 fmt.Fprintf(n.f, "\n")
374
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900375 if n.gomaDir != "" {
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900376 fmt.Fprintf(n.f, "pool local_pool\n")
377 fmt.Fprintf(n.f, " depth = %d\n", runtime.NumCPU())
378 }
379
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900380 for _, node := range n.nodes {
381 err := n.emitNode(node)
382 if err != nil {
383 return err
384 }
385 }
Fumitoshi Ukaie08e9ee2015-07-13 13:14:41 +0900386
387 fmt.Fprintf(n.f, "\n# shortcuts:\n")
388 var names []string
389 for name := range n.shortNames {
390 names = append(names, name)
391 }
392 sort.Strings(names)
393 for _, name := range names {
394 if len(n.shortNames[name]) != 1 {
395 // we generate shortcuts only for targets whose basename are unique.
396 continue
397 }
398 fmt.Fprintf(n.f, "build %s: phony %s\n", name, n.shortNames[name][0])
399 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900400 return nil
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900401}
402
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900403// GenerateNinja generates build.ninja from DepGraph.
404func GenerateNinja(g *DepGraph, gomaDir string) error {
Fumitoshi Ukai49599e52015-06-26 10:10:24 +0900405 startTime := time.Now()
Fumitoshi Ukai3ec25b52015-06-25 15:39:35 +0900406 n := newNinjaGenerator(g, gomaDir)
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900407 err := n.generateShell()
408 if err != nil {
409 return err
410 }
411 err = n.generateNinja()
412 if err != nil {
413 return err
414 }
Fumitoshi Ukai49599e52015-06-26 10:10:24 +0900415 logStats("generate ninja time: %q", time.Since(startTime))
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900416 return nil
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900417}