blob: 5282bc9c0125d5b59de67cd65863b12bba33da70 [file] [log] [blame]
Shinichiro Hamaji5f86e1a2015-06-29 14:25:39 +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
15// +build ignore
16
17#include "ninja.h"
18
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090019#include <stdio.h>
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +090020#include <sys/stat.h>
21#include <unistd.h>
Shinichiro Hamaji5f86e1a2015-06-29 14:25:39 +090022
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090023#include <memory>
24#include <string>
25#include <unordered_set>
26
27#include "command.h"
28#include "dep.h"
29#include "eval.h"
30#include "log.h"
31#include "string_piece.h"
32#include "stringprintf.h"
33#include "strutil.h"
34#include "var.h"
35
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090036static StringPiece FindCommandLineFlagWithArg(StringPiece cmd,
37 StringPiece name) {
38 size_t index = cmd.find(name);
39 if (index == string::npos)
40 return StringPiece();
41
42 StringPiece val = TrimLeftSpace(cmd.substr(index + name.size()));
43 index = val.find(name);
44 while (index != string::npos) {
45 val = TrimLeftSpace(val.substr(index + name.size()));
46 index = val.find(name);
47 }
48
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090049 index = val.find(' ');
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090050 CHECK(index != string::npos);
51 return val.substr(0, index);
52}
53
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090054static bool StripPrefix(StringPiece p, StringPiece* s) {
55 if (!HasPrefix(*s, p))
56 return false;
57 *s = s->substr(p.size());
58 return true;
59}
60
61static bool IsAndroidCompileCommand(StringPiece cmd) {
62 if (!StripPrefix("prebuilts/", &cmd))
63 return false;
64 if (!StripPrefix("gcc/", &cmd) && !StripPrefix("clang/", &cmd))
65 return false;
66
67 size_t index = cmd.find(' ');
68 if (index == string::npos)
69 return false;
70 StringPiece cc = cmd.substr(0, index);
71 if (!HasSuffix(cc, "gcc") && !HasSuffix(cc, "g++") &&
72 !HasSuffix(cc, "clang") && !HasSuffix(cc, "clang++")) {
73 return false;
74 }
75
76 cmd = cmd.substr(index);
77 return cmd.find(" -c ") != string::npos;
78}
79
80static bool GetDepfileFromCommandImpl(StringPiece cmd, string* out) {
Shinichiro Hamaji388e8582015-07-03 16:51:46 +090081 if (cmd.find(StringPiece(" -MD ")) == string::npos &&
82 cmd.find(StringPiece(" -MMD ")) == string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090083 return false;
84 }
85
Shinichiro Hamaji388e8582015-07-03 16:51:46 +090086 StringPiece mf = FindCommandLineFlagWithArg(cmd, StringPiece(" -MF "));
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090087 if (!mf.empty()) {
88 mf.AppendToString(out);
89 return true;
90 }
91
Shinichiro Hamaji388e8582015-07-03 16:51:46 +090092 StringPiece o = FindCommandLineFlagWithArg(cmd, StringPiece(" -o "));
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090093 if (o.empty()) {
94 ERROR("Cannot find the depfile in %s", cmd.as_string().c_str());
95 return false;
96 }
97
98 StripExt(o).AppendToString(out);
99 *out += ".d";
100 return true;
101}
102
103bool GetDepfileFromCommand(StringPiece cmd, string* out) {
104 CHECK(cmd.get(cmd.size()-1) == ' ');
105
106 if (!GetDepfileFromCommandImpl(cmd, out))
107 return false;
108
109 // A hack for Android - llvm-rs-cc seems not to emit a dep file.
110 if (cmd.find("bin/llvm-rs-cc ") != string::npos) {
111 return false;
112 }
113
114 // TODO: A hack for Makefiles generated by automake.
115
116 // A hack for Android to get .P files instead of .d.
117 string p;
118 StripExt(*out).AppendToString(&p);
119 p += ".P";
120 if (cmd.find(p) != string::npos) {
121 *out = p;
122 return true;
123 }
124
125 // A hack for Android. For .s files, GCC does not use C
126 // preprocessor, so it ignores -MF flag.
127 string as = "/";
128 StripExt(Basename(*out)).AppendToString(&as);
129 as += ".s";
130 if (cmd.find(as) != string::npos) {
131 return false;
132 }
133
134 return true;
135}
136
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900137class NinjaGenerator {
138 public:
139 explicit NinjaGenerator(Evaluator* ev)
140 : ce_(ev), ev_(ev), fp_(NULL), rule_id_(0) {
141 ev_->set_avoid_io(true);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900142 if (g_goma_dir)
143 gomacc_ = StringPrintf("%s/gomacc ", g_goma_dir);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900144 }
145
146 ~NinjaGenerator() {
147 ev_->set_avoid_io(false);
148 }
149
150 void Generate(const vector<DepNode*>& nodes) {
151 GenerateShell();
152 GenerateNinja(nodes);
153 }
154
155 private:
156 string GenRuleName() {
157 return StringPrintf("rule%d", rule_id_++);
158 }
159
160 StringPiece TranslateCommand(const char* in) {
161 const size_t orig_size = cmd_buf_.size();
162 bool prev_backslash = false;
163 char quote = 0;
164 bool done = false;
165 for (; *in && !done; in++) {
166 switch (*in) {
167 case '#':
168 if (quote == 0 && !prev_backslash) {
169 done = true;
170 break;
171 }
172
173 case '\'':
174 case '"':
175 case '`':
176 if (quote) {
177 if (quote == *in)
178 quote = 0;
179 } else if (!prev_backslash) {
180 quote = *in;
181 }
Shinichiro Hamajif79ca532015-06-29 17:25:54 +0900182 cmd_buf_ += *in;
Shinichiro Hamaji4212e382015-06-29 17:21:04 +0900183 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900184
185 case '$':
186 cmd_buf_ += "$$";
187 break;
188
189 case '\t':
190 cmd_buf_ += ' ';
191 break;
192
193 case '\n':
194 if (prev_backslash) {
195 cmd_buf_[cmd_buf_.size()-1] = ' ';
196 } else {
197 cmd_buf_ += ' ';
198 }
Shinichiro Hamaji4d151832015-06-29 18:15:46 +0900199 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900200
201 case '\\':
202 prev_backslash = !prev_backslash;
203 cmd_buf_ += '\\';
204 break;
205
206 default:
207 cmd_buf_ += *in;
208 prev_backslash = false;
209 }
210 }
211
212 while (true) {
213 char c = cmd_buf_[cmd_buf_.size()-1];
214 if (!isspace(c) && c != ';')
215 break;
216 cmd_buf_.resize(cmd_buf_.size() - 1);
217 }
218
219 return StringPiece(cmd_buf_.data() + orig_size,
220 cmd_buf_.size() - orig_size);
221 }
222
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900223 bool GenShellScript(const vector<Command*>& commands) {
224 bool use_gomacc = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900225 bool should_ignore_error = false;
226 cmd_buf_.clear();
227 for (const Command* c : commands) {
228 if (!cmd_buf_.empty()) {
229 if (should_ignore_error) {
230 cmd_buf_ += " ; ";
231 } else {
232 cmd_buf_ += " && ";
233 }
234 }
235 should_ignore_error = c->ignore_error;
236
237 const char* in = c->cmd->c_str();
238 while (isspace(*in))
239 in++;
240
241 bool needs_subshell = commands.size() > 1;
242 if (*in == '(') {
243 needs_subshell = false;
244 }
245
246 if (needs_subshell)
247 cmd_buf_ += '(';
248
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900249 size_t cmd_start = cmd_buf_.size();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900250 StringPiece translated = TranslateCommand(in);
251 if (translated.empty()) {
252 cmd_buf_ += "true";
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900253 } else if (g_goma_dir && IsAndroidCompileCommand(translated)) {
254 cmd_buf_.insert(cmd_start, gomacc_);
255 use_gomacc = true;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900256 }
257
258 if (c == commands.back() && c->ignore_error) {
259 cmd_buf_ += " ; true";
260 }
261
262 if (needs_subshell)
263 cmd_buf_ += ')';
264 }
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900265 return g_goma_dir && !use_gomacc;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900266 }
267
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900268 void EmitDepfile() {
269 cmd_buf_ += ' ';
270 string depfile;
271 bool result = GetDepfileFromCommand(cmd_buf_, &depfile);
272 cmd_buf_.resize(cmd_buf_.size()-1);
273 if (!result)
274 return;
275 fprintf(fp_, " depfile = %s\n", depfile.c_str());
276 }
277
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900278 void EmitNode(DepNode* node) {
279 auto p = done_.insert(node->output);
280 if (!p.second)
281 return;
282
283 if (node->cmds.empty() && node->deps.empty() && !node->is_phony)
284 return;
285
286 vector<Command*> commands;
287 ce_.Eval(node, &commands);
288
289 string rule_name = "phony";
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900290 bool use_local_pool = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900291 if (!commands.empty()) {
292 rule_name = GenRuleName();
293 fprintf(fp_, "rule %s\n", rule_name.c_str());
294 fprintf(fp_, " description = build $out\n");
295
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900296 use_local_pool |= GenShellScript(commands);
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900297 EmitDepfile();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900298
299 // It seems Linux is OK with ~130kB.
300 // TODO: Find this number automatically.
301 if (cmd_buf_.size() > 100 * 1000) {
302 fprintf(fp_, " rspfile = $out.rsp\n");
303 fprintf(fp_, " rspfile_content = %s\n", cmd_buf_.c_str());
304 fprintf(fp_, " command = sh $out.rsp\n");
305 } else {
306 fprintf(fp_, " command = %s\n", cmd_buf_.c_str());
307 }
308 }
309
310 EmitBuild(node, rule_name);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900311 if (use_local_pool)
312 fprintf(fp_, " pool = local_pool\n");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900313
314 for (DepNode* d : node->deps) {
315 EmitNode(d);
316 }
Shinichiro Hamaji3f2cf1e2015-07-06 18:58:18 +0900317 for (DepNode* d : node->order_onlys) {
318 EmitNode(d);
319 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900320 }
321
322 void EmitBuild(DepNode* node, const string& rule_name) {
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900323 fprintf(fp_, "build %s: %s", node->output.c_str(), rule_name.c_str());
324 vector<Symbol> order_onlys;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900325 for (DepNode* d : node->deps) {
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900326 fprintf(fp_, " %s", d->output.c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900327 }
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900328 if (!node->order_onlys.empty()) {
Shinichiro Hamajif4820de2015-06-29 17:03:17 +0900329 fprintf(fp_, " ||");
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900330 for (DepNode* d : node->order_onlys) {
331 fprintf(fp_, " %s", d->output.c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900332 }
333 }
334 fprintf(fp_, "\n");
335 }
336
337 void GenerateNinja(const vector<DepNode*>& nodes) {
338 fp_ = fopen("build.ninja", "wb");
339 if (fp_ == NULL)
340 PERROR("fopen(build.ninja) failed");
341
342 fprintf(fp_, "# Generated by kati\n");
343 fprintf(fp_, "\n");
344
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900345 if (g_goma_dir) {
346 fprintf(fp_, "pool local_pool\n");
347 // TODO: Decide the appropriate number based on the number of cores.
Shinichiro Hamaji99e43262015-07-03 20:47:05 +0900348 fprintf(fp_, " depth = %d\n", 32);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900349 }
350
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900351 for (DepNode* node : nodes) {
352 EmitNode(node);
353 }
354
355 fclose(fp_);
356 }
357
358 void GenerateShell() {
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900359 FILE* fp = fopen("ninja.sh", "wb");
360 if (fp == NULL)
361 PERROR("fopen(ninja.sh) failed");
362
Shinichiro Hamaji94d6f2a2015-07-05 05:32:25 +0900363 shared_ptr<string> shell = ev_->EvalVar(kShellSym);
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900364 if (shell->empty())
365 shell = make_shared<string>("/bin/sh");
366 fprintf(fp, "#!%s\n", shell->c_str());
367
368 for (const auto& p : ev_->exports()) {
369 if (p.second) {
Shinichiro Hamaji94d6f2a2015-07-05 05:32:25 +0900370 shared_ptr<string> val = ev_->EvalVar(p.first);
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900371 fprintf(fp, "export %s=%s\n", p.first.c_str(), val->c_str());
372 } else {
373 fprintf(fp, "unset %s\n", p.first.c_str());
374 }
375 }
376
377 if (g_goma_dir) {
378 fprintf(fp, "exec ninja -j300 \"$@\"\n");
379 } else {
380 fprintf(fp, "exec ninja \"$@\"\n");
381 }
382
383 if (chmod("ninja.sh", 0755) != 0)
384 PERROR("chmod ninja.sh failed");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900385 }
386
387 CommandEvaluator ce_;
388 Evaluator* ev_;
389 FILE* fp_;
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900390 unordered_set<Symbol> done_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900391 int rule_id_;
392 string cmd_buf_;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900393 string gomacc_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900394};
395
396void GenerateNinja(const vector<DepNode*>& nodes, Evaluator* ev) {
397 NinjaGenerator ng(ev);
398 ng.Generate(nodes);
Shinichiro Hamaji5f86e1a2015-06-29 14:25:39 +0900399}