blob: ac9748fc64b9d916e170120d8e109868a04fd1db [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
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090015package main
16
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"
25)
26
27type NinjaGenerator struct {
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +090028 f *os.File
29 nodes []*DepNode
30 vars Vars
31 exports map[string]bool
32 ex *Executor
Fumitoshi Ukai936de102015-06-08 11:21:16 +090033 ruleID int
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +090034 done map[string]bool
Shinichiro Hamajibcecb502015-06-04 16:20:25 +090035 ccRe *regexp.Regexp
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090036}
37
38func NewNinjaGenerator(g *DepGraph) *NinjaGenerator {
Shinichiro Hamajibcecb502015-06-04 16:20:25 +090039 ccRe, err := regexp.Compile(`^prebuilts/(gcc|clang)/.*(gcc|g\+\+|clang|clang\+\+) .* -c `)
40 if err != nil {
41 panic(err)
42 }
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090043 return &NinjaGenerator{
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +090044 nodes: g.nodes,
45 vars: g.vars,
46 exports: g.exports,
47 done: make(map[string]bool),
Shinichiro Hamajibcecb502015-06-04 16:20:25 +090048 ccRe: ccRe,
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090049 }
50}
51
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +090052func getDepfileImpl(ss string) (string, error) {
Shinichiro Hamaji6dca2202015-05-27 18:00:34 +090053 tss := ss + " "
54 if !strings.Contains(tss, " -MD ") && !strings.Contains(tss, " -MMD ") {
55 return "", nil
56 }
57
58 mfIndex := strings.Index(ss, " -MF ")
59 if mfIndex >= 0 {
60 mf := trimLeftSpace(ss[mfIndex+4:])
61 if strings.Index(mf, " -MF ") >= 0 {
62 return "", fmt.Errorf("Multiple output file candidates in %s", ss)
63 }
64 mfEndIndex := strings.IndexAny(mf, " \t\n")
65 if mfEndIndex >= 0 {
66 mf = mf[:mfEndIndex]
67 }
68
Shinichiro Hamaji6dca2202015-05-27 18:00:34 +090069 return mf, nil
70 }
71
72 outIndex := strings.Index(ss, " -o ")
73 if outIndex < 0 {
74 return "", fmt.Errorf("Cannot find the depfile in %s", ss)
75 }
76 out := trimLeftSpace(ss[outIndex+4:])
77 if strings.Index(out, " -o ") >= 0 {
78 return "", fmt.Errorf("Multiple output file candidates in %s", ss)
79 }
80 outEndIndex := strings.IndexAny(out, " \t\n")
81 if outEndIndex >= 0 {
82 out = out[:outEndIndex]
83 }
84 return stripExt(out) + ".d", nil
85}
86
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +090087func getDepfile(ss string) (string, error) {
88 // A hack for Android - llvm-rs-cc seems not to emit a dep file.
89 if strings.Contains(ss, "bin/llvm-rs-cc ") {
90 return "", nil
91 }
92
93 r, err := getDepfileImpl(ss)
Shinichiro Hamajib8224f32015-05-28 23:07:59 +090094 if r == "" || err != nil {
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +090095 return r, err
96 }
97
Shinichiro Hamaji3438ae32015-06-05 11:33:20 +090098 // A hack for Makefiles generated by automake.
99 mvCmd := "(mv -f " + r + " "
100 if i := strings.LastIndex(ss, mvCmd); i >= 0 {
101 rest := ss[i+len(mvCmd):]
102 ei := strings.IndexByte(rest, ')')
103 if ei < 0 {
104 panic(ss)
105 }
106 return rest[:ei], nil
107 }
108
Shinichiro Hamaji74ab4b62015-05-28 22:56:24 +0900109 // A hack for Android to get .P files instead of .d.
110 p := stripExt(r) + ".P"
111 if strings.Contains(ss, p) {
112 return p, nil
113 }
114
115 // A hack for Android. For .s files, GCC does not use
116 // C preprocessor, so it ignores -MF flag.
117 as := "/" + stripExt(filepath.Base(r)) + ".s"
118 if strings.Contains(ss, as) {
119 return "", nil
120 }
121
122 return r, nil
123}
124
Shinichiro Hamaji246cd232015-05-27 17:08:23 +0900125func stripShellComment(s string) string {
126 if strings.IndexByte(s, '#') < 0 {
127 // Fast path.
128 return s
129 }
130 var escape bool
131 var quote rune
132 for i, c := range s {
133 if quote > 0 {
134 if quote == c && (quote == '\'' || !escape) {
135 quote = 0
136 }
137 } else if !escape {
138 if c == '#' {
139 return s[:i]
140 } else if c == '\'' || c == '"' || c == '`' {
141 quote = c
142 }
143 }
144 if escape {
145 escape = false
146 } else if c == '\\' {
147 escape = true
148 } else {
149 escape = false
150 }
151 }
152 return s
153}
154
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900155func (n *NinjaGenerator) genShellScript(runners []runner) (string, bool) {
156 useGomacc := false
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900157 var buf bytes.Buffer
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900158 for i, r := range runners {
159 if i > 0 {
160 if runners[i-1].ignoreError {
161 buf.WriteString(" ; ")
162 } else {
163 buf.WriteString(" && ")
164 }
165 }
Shinichiro Hamaji246cd232015-05-27 17:08:23 +0900166 cmd := stripShellComment(r.cmd)
167 cmd = trimLeftSpace(cmd)
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900168 cmd = strings.Replace(cmd, "\\\n", " ", -1)
169 cmd = strings.TrimRight(cmd, " \t\n;")
170 cmd = strings.Replace(cmd, "$", "$$", -1)
171 cmd = strings.Replace(cmd, "\t", " ", -1)
Shinichiro Hamaji501f68e2015-05-27 17:33:41 +0900172 if cmd == "" {
173 cmd = "true"
174 }
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900175 if gomaDir != "" && n.ccRe.MatchString(cmd) {
176 cmd = fmt.Sprintf("%s/gomacc %s", gomaDir, cmd)
177 useGomacc = true
178 }
Shinichiro Hamaji13653b22015-05-28 22:45:12 +0900179
180 needsSubShell := i > 0 || len(runners) > 1
181 if cmd[0] == '(' {
182 needsSubShell = false
183 }
184
185 if needsSubShell {
186 buf.WriteByte('(')
187 }
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900188 buf.WriteString(cmd)
189 if i == len(runners)-1 && r.ignoreError {
190 buf.WriteString(" ; true")
191 }
Shinichiro Hamaji13653b22015-05-28 22:45:12 +0900192 if needsSubShell {
193 buf.WriteByte(')')
194 }
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900195 }
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900196 return buf.String(), gomaDir != "" && !useGomacc
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900197}
198
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900199func (n *NinjaGenerator) genRuleName() string {
Fumitoshi Ukai936de102015-06-08 11:21:16 +0900200 ruleName := fmt.Sprintf("rule%d", n.ruleID)
201 n.ruleID++
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900202 return ruleName
203}
204
205func (n *NinjaGenerator) emitBuild(output, rule, dep string) {
Shinichiro Hamajiea2e0de2015-05-28 15:22:16 +0900206 fmt.Fprintf(n.f, "build %s: %s%s\n", output, rule, dep)
Shinichiro Hamajifd872162015-05-26 18:06:48 +0900207}
208
209func getDepString(node *DepNode) string {
210 var deps []string
211 var orderOnlys []string
212 for _, d := range node.Deps {
213 if d.IsOrderOnly {
214 orderOnlys = append(orderOnlys, d.Output)
215 } else {
216 deps = append(deps, d.Output)
217 }
218 }
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
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900229func (n *NinjaGenerator) emitNode(node *DepNode) {
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900230 if n.done[node.Output] {
231 return
232 }
233 n.done[node.Output] = true
234
Shinichiro Hamajifd4aaa02015-05-26 15:40:53 +0900235 if len(node.Cmds) == 0 && len(node.Deps) == 0 && !node.IsPhony {
236 return
237 }
238
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900239 runners, _ := n.ex.createRunners(node, true)
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900240 ruleName := "phony"
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900241 useLocalPool := false
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900242 if len(runners) > 0 {
243 ruleName = n.genRuleName()
244 fmt.Fprintf(n.f, "rule %s\n", ruleName)
245 fmt.Fprintf(n.f, " description = build $out\n")
246
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900247 ss, ulp := n.genShellScript(runners)
248 if ulp {
249 useLocalPool = true
250 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900251 depfile, err := getDepfile(ss)
Shinichiro Hamaji0ab9a2f2015-05-27 18:32:46 +0900252 if err != nil {
253 panic(err)
254 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900255 if depfile != "" {
256 fmt.Fprintf(n.f, " depfile = %s\n", depfile)
257 }
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900258 // It seems Linux is OK with ~130kB.
259 // TODO: Find this number automatically.
260 ArgLenLimit := 100 * 1000
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900261 if len(ss) > ArgLenLimit {
Shinichiro Hamajia5467792015-05-27 16:28:40 +0900262 fmt.Fprintf(n.f, " rspfile = $out.rsp\n")
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900263 fmt.Fprintf(n.f, " rspfile_content = %s\n", ss)
264 ss = "sh $out.rsp"
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900265 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900266 fmt.Fprintf(n.f, " command = %s\n", ss)
267
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900268 }
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900269 n.emitBuild(node.Output, ruleName, getDepString(node))
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900270 if useLocalPool {
271 fmt.Fprintf(n.f, " pool = local_pool\n")
272 }
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900273
274 for _, d := range node.Deps {
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900275 n.emitNode(d)
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900276 }
277}
278
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900279func (n *NinjaGenerator) generateShell() {
280 f, err := os.Create("ninja.sh")
281 if err != nil {
282 panic(err)
283 }
284 defer f.Close()
285
286 ev := newEvaluator(n.vars)
287 shell := ev.EvaluateVar("SHELL")
288 if shell == "" {
289 shell = "/bin/sh"
290 }
291 fmt.Fprintf(f, "#!%s\n", shell)
292 for name, export := range n.exports {
293 if export {
294 fmt.Fprintf(f, "export %s=%s\n", name, ev.EvaluateVar(name))
295 } else {
296 fmt.Fprintf(f, "unset %s\n", name)
297 }
298 }
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900299 if gomaDir == "" {
Shinichiro Hamaji096c7e02015-06-04 17:13:26 +0900300 fmt.Fprintln(f, `exec ninja "$@"`)
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900301 } else {
Shinichiro Hamaji096c7e02015-06-04 17:13:26 +0900302 fmt.Fprintln(f, `exec ninja -j300 "$@"`)
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900303 }
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900304
305 err = f.Chmod(0755)
306 if err != nil {
307 panic(err)
308 }
309}
310
311func (n *NinjaGenerator) generateNinja() {
312 f, err := os.Create("build.ninja")
313 if err != nil {
314 panic(err)
315 }
316 defer f.Close()
317
318 n.f = f
Shinichiro Hamajiea2e0de2015-05-28 15:22:16 +0900319 fmt.Fprintf(n.f, "# Generated by kati\n")
320 fmt.Fprintf(n.f, "\n")
321
Shinichiro Hamajibcecb502015-06-04 16:20:25 +0900322 if gomaDir != "" {
323 fmt.Fprintf(n.f, "pool local_pool\n")
324 fmt.Fprintf(n.f, " depth = %d\n", runtime.NumCPU())
325 }
326
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900327 n.ex = NewExecutor(n.vars)
328 for _, node := range n.nodes {
Shinichiro Hamajibd59ebb2015-05-28 17:07:17 +0900329 n.emitNode(node)
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900330 }
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900331}
332
333func GenerateNinja(g *DepGraph) {
Shinichiro Hamaji30a02ad2015-05-29 14:56:43 +0900334 n := NewNinjaGenerator(g)
335 n.generateShell()
336 n.generateNinja()
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +0900337}