blob: 1f1bec866ad4086518461b49334577c8ff7c6b24 [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"
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090024 "strings"
Fumitoshi Ukai49599e52015-06-26 10:10:24 +090025 "time"
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090026)
27
Fumitoshi Ukai3ec25b52015-06-25 15:39:35 +090028type ninjaGenerator struct {
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +090029 f *os.File
30 nodes []*DepNode
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +090031 exports map[string]bool
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +090032
33 ctx *execContext
34
Fumitoshi Ukai936de102015-06-08 11:21:16 +090035 ruleID int
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +090036 done map[string]bool
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +090037 gomaDir string
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090038}
39
Fumitoshi Ukai65c72332015-06-26 21:32:50 +090040var ccRE = regexp.MustCompile(`^prebuilts/(gcc|clang)/.*(gcc|g\+\+|clang|clang\+\+) .* -c `)
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
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +090088func getDepfile(ss string) (string, error) {
89 // A hack for Android - llvm-rs-cc seems not to emit a dep file.
90 if strings.Contains(ss, "bin/llvm-rs-cc ") {
91 return "", nil
92 }
93
94 r, err := getDepfileImpl(ss)
Shinichiro Hamajib8224f32015-05-28 23:07:59 +090095 if r == "" || err != nil {
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +090096 return r, err
97 }
98
Shinichiro Hamaji3438ae32015-06-05 11:33:20 +090099 // A hack for Makefiles generated by automake.
100 mvCmd := "(mv -f " + r + " "
101 if i := strings.LastIndex(ss, mvCmd); i >= 0 {
102 rest := ss[i+len(mvCmd):]
103 ei := strings.IndexByte(rest, ')')
104 if ei < 0 {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900105 return "", fmt.Errorf("unbalanced parenthes? %s", ss)
Shinichiro Hamaji3438ae32015-06-05 11:33:20 +0900106 }
107 return rest[:ei], nil
108 }
109
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +0900110 // A hack for Android to get .P files instead of .d.
111 p := stripExt(r) + ".P"
112 if strings.Contains(ss, p) {
113 return p, nil
114 }
115
116 // A hack for Android. For .s files, GCC does not use
117 // C preprocessor, so it ignores -MF flag.
118 as := "/" + stripExt(filepath.Base(r)) + ".s"
119 if strings.Contains(ss, as) {
120 return "", nil
121 }
122
123 return r, nil
124}
125
Shinichiro Hamaji246cd232015-05-27 17:08:23 +0900126func stripShellComment(s string) string {
127 if strings.IndexByte(s, '#') < 0 {
128 // Fast path.
129 return s
130 }
131 var escape bool
132 var quote rune
133 for i, c := range s {
134 if quote > 0 {
135 if quote == c && (quote == '\'' || !escape) {
136 quote = 0
137 }
138 } else if !escape {
139 if c == '#' {
140 return s[:i]
141 } else if c == '\'' || c == '"' || c == '`' {
142 quote = c
143 }
144 }
145 if escape {
146 escape = false
147 } else if c == '\\' {
148 escape = true
149 } else {
150 escape = false
151 }
152 }
153 return s
154}
155
Fumitoshi Ukai3ec25b52015-06-25 15:39:35 +0900156func (n *ninjaGenerator) genShellScript(runners []runner) (string, bool) {
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900157 useGomacc := false
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900158 var buf bytes.Buffer
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900159 for i, r := range runners {
160 if i > 0 {
161 if runners[i-1].ignoreError {
162 buf.WriteString(" ; ")
163 } else {
164 buf.WriteString(" && ")
165 }
166 }
Shinichiro Hamaji246cd232015-05-27 17:08:23 +0900167 cmd := stripShellComment(r.cmd)
168 cmd = trimLeftSpace(cmd)
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900169 cmd = strings.Replace(cmd, "\\\n", " ", -1)
170 cmd = strings.TrimRight(cmd, " \t\n;")
171 cmd = strings.Replace(cmd, "$", "$$", -1)
172 cmd = strings.Replace(cmd, "\t", " ", -1)
Shinichiro Hamaji501f68e2015-05-27 17:33:41 +0900173 if cmd == "" {
174 cmd = "true"
175 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900176 if n.gomaDir != "" && ccRE.MatchString(cmd) {
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900177 cmd = fmt.Sprintf("%s/gomacc %s", n.gomaDir, cmd)
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900178 useGomacc = true
179 }
Shinichiro Hamaji13653b22015-05-28 22:45:12 +0900180
181 needsSubShell := i > 0 || len(runners) > 1
182 if cmd[0] == '(' {
183 needsSubShell = false
184 }
185
186 if needsSubShell {
187 buf.WriteByte('(')
188 }
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900189 buf.WriteString(cmd)
190 if i == len(runners)-1 && r.ignoreError {
191 buf.WriteString(" ; true")
192 }
Shinichiro Hamaji13653b22015-05-28 22:45:12 +0900193 if needsSubShell {
194 buf.WriteByte(')')
195 }
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900196 }
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900197 return buf.String(), n.gomaDir != "" && !useGomacc
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900198}
199
Fumitoshi Ukai3ec25b52015-06-25 15:39:35 +0900200func (n *ninjaGenerator) genRuleName() string {
Fumitoshi Ukai936de102015-06-08 11:21:16 +0900201 ruleName := fmt.Sprintf("rule%d", n.ruleID)
202 n.ruleID++
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900203 return ruleName
204}
205
Fumitoshi Ukai3ec25b52015-06-25 15:39:35 +0900206func (n *ninjaGenerator) emitBuild(output, rule, dep string) {
Shinichiro Hamajiea2e0de2015-05-28 15:22:16 +0900207 fmt.Fprintf(n.f, "build %s: %s%s\n", output, rule, dep)
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900208}
209
210func getDepString(node *DepNode) string {
211 var deps []string
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900212 for _, d := range node.Deps {
Fumitoshi Ukaic916ea22015-06-30 09:52:13 +0900213 deps = append(deps, d.Output)
214 }
215 var orderOnlys []string
216 for _, d := range node.OrderOnlys {
217 orderOnlys = append(orderOnlys, d.Output)
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900218 }
219 dep := ""
220 if len(deps) > 0 {
221 dep += fmt.Sprintf(" %s", strings.Join(deps, " "))
222 }
223 if len(orderOnlys) > 0 {
224 dep += fmt.Sprintf(" || %s", strings.Join(orderOnlys, " "))
225 }
226 return dep
227}
228
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900229func (n *ninjaGenerator) emitNode(node *DepNode) error {
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900230 if n.done[node.Output] {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900231 return nil
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900232 }
233 n.done[node.Output] = true
234
Fumitoshi Ukaic916ea22015-06-30 09:52:13 +0900235 if len(node.Cmds) == 0 && len(node.Deps) == 0 && len(node.OrderOnlys) == 0 && !node.IsPhony {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900236 return nil
Shinichiro Hamajifd4aaa02015-05-26 15:40:53 +0900237 }
238
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900239 runners, _, err := createRunners(n.ctx, node)
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900240 if err != nil {
241 return err
242 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900243 ruleName := "phony"
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900244 useLocalPool := false
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900245 if len(runners) > 0 {
246 ruleName = n.genRuleName()
Fumitoshi Ukaic916ea22015-06-30 09:52:13 +0900247 fmt.Fprintf(n.f, "\n# rule for %s\n", node.Output)
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900248 fmt.Fprintf(n.f, "rule %s\n", ruleName)
249 fmt.Fprintf(n.f, " description = build $out\n")
250
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900251 ss, ulp := n.genShellScript(runners)
252 if ulp {
253 useLocalPool = true
254 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900255 depfile, err := getDepfile(ss)
Shinichiro Hamaji0ab9a2f2015-05-27 18:32:46 +0900256 if err != nil {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900257 return err
Shinichiro Hamaji0ab9a2f2015-05-27 18:32:46 +0900258 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900259 if depfile != "" {
260 fmt.Fprintf(n.f, " depfile = %s\n", depfile)
261 }
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900262 // It seems Linux is OK with ~130kB.
263 // TODO: Find this number automatically.
264 ArgLenLimit := 100 * 1000
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900265 if len(ss) > ArgLenLimit {
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900266 fmt.Fprintf(n.f, " rspfile = $out.rsp\n")
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900267 fmt.Fprintf(n.f, " rspfile_content = %s\n", ss)
268 ss = "sh $out.rsp"
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900269 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900270 fmt.Fprintf(n.f, " command = %s\n", ss)
271
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900272 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900273 n.emitBuild(node.Output, ruleName, getDepString(node))
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900274 if useLocalPool {
275 fmt.Fprintf(n.f, " pool = local_pool\n")
276 }
Fumitoshi Ukaic916ea22015-06-30 09:52:13 +0900277 fmt.Fprintf(n.f, "\n")
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900278
279 for _, d := range node.Deps {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900280 err := n.emitNode(d)
281 if err != nil {
282 return err
283 }
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900284 }
Fumitoshi Ukaic916ea22015-06-30 09:52:13 +0900285 for _, d := range node.OrderOnlys {
286 err := n.emitNode(d)
287 if err != nil {
288 return err
289 }
290 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900291 return nil
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900292}
293
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900294func (n *ninjaGenerator) generateShell() (err error) {
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900295 f, err := os.Create("ninja.sh")
296 if err != nil {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900297 return err
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900298 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900299 defer func() {
300 cerr := f.Close()
301 if err == nil {
302 err = cerr
303 }
304 }()
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900305
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900306 fmt.Fprintf(f, "#!%s\n", n.ctx.shell)
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900307 for name, export := range n.exports {
308 if export {
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900309 v, err := n.ctx.ev.EvaluateVar(name)
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900310 if err != nil {
311 return err
312 }
313 fmt.Fprintf(f, "export %s=%s\n", name, v)
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900314 } else {
315 fmt.Fprintf(f, "unset %s\n", name)
316 }
317 }
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900318 if n.gomaDir == "" {
Shinichiro Hamaji096c7e02015-06-04 17:13:26 +0900319 fmt.Fprintln(f, `exec ninja "$@"`)
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900320 } else {
Shinichiro Hamaji096c7e02015-06-04 17:13:26 +0900321 fmt.Fprintln(f, `exec ninja -j300 "$@"`)
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900322 }
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900323
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900324 return f.Chmod(0755)
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900325}
326
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900327func (n *ninjaGenerator) generateNinja() (err error) {
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900328 f, err := os.Create("build.ninja")
329 if err != nil {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900330 return err
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900331 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900332 defer func() {
333 cerr := f.Close()
334 if err == nil {
335 err = cerr
336 }
337 }()
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900338
339 n.f = f
Shinichiro Hamajiea2e0de2015-05-28 15:22:16 +0900340 fmt.Fprintf(n.f, "# Generated by kati\n")
341 fmt.Fprintf(n.f, "\n")
342
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900343 if n.gomaDir != "" {
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900344 fmt.Fprintf(n.f, "pool local_pool\n")
345 fmt.Fprintf(n.f, " depth = %d\n", runtime.NumCPU())
346 }
347
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900348 for _, node := range n.nodes {
349 err := n.emitNode(node)
350 if err != nil {
351 return err
352 }
353 }
354 return nil
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900355}
356
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900357// GenerateNinja generates build.ninja from DepGraph.
358func GenerateNinja(g *DepGraph, gomaDir string) error {
Fumitoshi Ukai49599e52015-06-26 10:10:24 +0900359 startTime := time.Now()
Fumitoshi Ukai3ec25b52015-06-25 15:39:35 +0900360 n := newNinjaGenerator(g, gomaDir)
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900361 err := n.generateShell()
362 if err != nil {
363 return err
364 }
365 err = n.generateNinja()
366 if err != nil {
367 return err
368 }
Fumitoshi Ukai49599e52015-06-26 10:10:24 +0900369 logStats("generate ninja time: %q", time.Since(startTime))
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900370 return nil
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900371}