blob: 5e009a389817977ee2a90029546a0f2155aa96b5 [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"
Shinichiro Hamaji087cecd2015-07-06 19:51:58 +090030#include "flags.h"
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090031#include "log.h"
32#include "string_piece.h"
33#include "stringprintf.h"
34#include "strutil.h"
35#include "var.h"
Shinichiro Hamajid821f6d2015-07-14 04:03:27 +090036#include "version.h"
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090037
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090038static StringPiece FindCommandLineFlagWithArg(StringPiece cmd,
39 StringPiece name) {
40 size_t index = cmd.find(name);
41 if (index == string::npos)
42 return StringPiece();
43
44 StringPiece val = TrimLeftSpace(cmd.substr(index + name.size()));
45 index = val.find(name);
46 while (index != string::npos) {
47 val = TrimLeftSpace(val.substr(index + name.size()));
48 index = val.find(name);
49 }
50
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090051 index = val.find(' ');
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090052 CHECK(index != string::npos);
53 return val.substr(0, index);
54}
55
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090056static bool StripPrefix(StringPiece p, StringPiece* s) {
57 if (!HasPrefix(*s, p))
58 return false;
59 *s = s->substr(p.size());
60 return true;
61}
62
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090063size_t GetGomaccPosForAndroidCompileCommand(StringPiece cmdline) {
64 size_t index = cmdline.find(' ');
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090065 if (index == string::npos)
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090066 return string::npos;
67 StringPiece cmd = cmdline.substr(0, index);
68 if (HasSuffix(cmd, "ccache")) {
69 index++;
70 size_t pos = GetGomaccPosForAndroidCompileCommand(cmdline.substr(index));
71 return pos == string::npos ? string::npos : pos + index;
72 }
73 if (!StripPrefix("prebuilts/", &cmd))
74 return string::npos;
75 if (!StripPrefix("gcc/", &cmd) && !StripPrefix("clang/", &cmd))
76 return string::npos;
77 if (!HasSuffix(cmd, "gcc") && !HasSuffix(cmd, "g++") &&
78 !HasSuffix(cmd, "clang") && !HasSuffix(cmd, "clang++")) {
79 return string::npos;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090080 }
81
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090082 StringPiece rest = cmdline.substr(index);
83 return rest.find(" -c ") != string::npos ? 0 : string::npos;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090084}
85
86static bool GetDepfileFromCommandImpl(StringPiece cmd, string* out) {
Shinichiro Hamajid416e612015-07-18 12:43:34 +090087 if ((cmd.find(StringPiece(" -MD ")) == string::npos &&
88 cmd.find(StringPiece(" -MMD ")) == string::npos) ||
89 cmd.find(StringPiece(" -c ")) == string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090090 return false;
91 }
92
Shinichiro Hamaji388e8582015-07-03 16:51:46 +090093 StringPiece mf = FindCommandLineFlagWithArg(cmd, StringPiece(" -MF "));
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090094 if (!mf.empty()) {
95 mf.AppendToString(out);
96 return true;
97 }
98
Shinichiro Hamaji388e8582015-07-03 16:51:46 +090099 StringPiece o = FindCommandLineFlagWithArg(cmd, StringPiece(" -o "));
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900100 if (o.empty()) {
101 ERROR("Cannot find the depfile in %s", cmd.as_string().c_str());
102 return false;
103 }
104
105 StripExt(o).AppendToString(out);
106 *out += ".d";
107 return true;
108}
109
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900110bool GetDepfileFromCommand(string* cmd, string* out) {
111 CHECK(!cmd->empty());
112 CHECK((*cmd)[cmd->size()-1] == ' ');
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900113
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900114 if (!GetDepfileFromCommandImpl(*cmd, out))
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900115 return false;
116
117 // A hack for Android - llvm-rs-cc seems not to emit a dep file.
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900118 if (cmd->find("bin/llvm-rs-cc ") != string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900119 return false;
120 }
121
122 // TODO: A hack for Makefiles generated by automake.
123
124 // A hack for Android to get .P files instead of .d.
125 string p;
126 StripExt(*out).AppendToString(&p);
127 p += ".P";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900128 if (cmd->find(p) != string::npos) {
129 const string rm_f = "; rm -f " + *out;
130 const size_t found = cmd->find(rm_f);
131 if (found == string::npos) {
132 ERROR("Cannot find removal of .d file: %s", cmd->c_str());
133 }
134 cmd->erase(found, rm_f.size());
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900135 return true;
136 }
137
138 // A hack for Android. For .s files, GCC does not use C
139 // preprocessor, so it ignores -MF flag.
140 string as = "/";
141 StripExt(Basename(*out)).AppendToString(&as);
142 as += ".s";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900143 if (cmd->find(as) != string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900144 return false;
145 }
146
Shinichiro Hamajid416e612015-07-18 12:43:34 +0900147 *cmd += "&& cp ";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900148 *cmd += *out;
149 *cmd += ' ';
150 *cmd += *out;
Shinichiro Hamajid416e612015-07-18 12:43:34 +0900151 *cmd += ".tmp ";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900152 *out += ".tmp";
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900153 return true;
154}
155
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900156class NinjaGenerator {
157 public:
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900158 NinjaGenerator(const char* ninja_suffix, Evaluator* ev)
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900159 : ce_(ev), ev_(ev), fp_(NULL), rule_id_(0) {
160 ev_->set_avoid_io(true);
Colin Crosse890a912015-07-20 13:28:00 -0700161 shell_ = ev->EvalVar(kShellSym);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900162 if (g_goma_dir)
163 gomacc_ = StringPrintf("%s/gomacc ", g_goma_dir);
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900164 if (ninja_suffix) {
165 ninja_suffix_ = ninja_suffix;
166 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900167 }
168
169 ~NinjaGenerator() {
170 ev_->set_avoid_io(false);
171 }
172
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900173 void Generate(const vector<DepNode*>& nodes, bool build_all_targets) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900174 GenerateShell();
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900175 GenerateNinja(nodes, build_all_targets);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900176 }
177
178 private:
179 string GenRuleName() {
180 return StringPrintf("rule%d", rule_id_++);
181 }
182
183 StringPiece TranslateCommand(const char* in) {
184 const size_t orig_size = cmd_buf_.size();
185 bool prev_backslash = false;
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900186 // Set space as an initial value so the leading comment will be
187 // stripped out.
188 char prev_char = ' ';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900189 char quote = 0;
190 bool done = false;
191 for (; *in && !done; in++) {
192 switch (*in) {
193 case '#':
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900194 if (quote == 0 && isspace(prev_char)) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900195 done = true;
Colin Cross415e4a12015-07-15 18:21:38 -0700196 } else {
197 cmd_buf_ += *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900198 }
Colin Cross415e4a12015-07-15 18:21:38 -0700199 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900200
201 case '\'':
202 case '"':
203 case '`':
204 if (quote) {
205 if (quote == *in)
206 quote = 0;
207 } else if (!prev_backslash) {
208 quote = *in;
209 }
Shinichiro Hamajif79ca532015-06-29 17:25:54 +0900210 cmd_buf_ += *in;
Shinichiro Hamaji4212e382015-06-29 17:21:04 +0900211 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900212
213 case '$':
214 cmd_buf_ += "$$";
215 break;
216
217 case '\t':
218 cmd_buf_ += ' ';
219 break;
220
221 case '\n':
222 if (prev_backslash) {
223 cmd_buf_[cmd_buf_.size()-1] = ' ';
224 } else {
225 cmd_buf_ += ' ';
226 }
Shinichiro Hamaji4d151832015-06-29 18:15:46 +0900227 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900228
229 case '\\':
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900230 cmd_buf_ += '\\';
231 break;
232
233 default:
234 cmd_buf_ += *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900235 }
Colin Crossf6f1cf42015-07-15 15:25:30 -0700236
237 if (*in == '\\') {
238 prev_backslash = !prev_backslash;
239 } else {
240 prev_backslash = false;
241 }
242
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900243 prev_char = *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900244 }
245
246 while (true) {
247 char c = cmd_buf_[cmd_buf_.size()-1];
248 if (!isspace(c) && c != ';')
249 break;
250 cmd_buf_.resize(cmd_buf_.size() - 1);
251 }
252
253 return StringPiece(cmd_buf_.data() + orig_size,
254 cmd_buf_.size() - orig_size);
255 }
256
Colin Cross2e032ba2015-07-15 18:33:37 -0700257 bool GetDescriptionFromCommand(StringPiece cmd, string *out) {
Colin Cross2e032ba2015-07-15 18:33:37 -0700258 if (!HasPrefix(cmd, "echo ")) {
259 return false;
260 }
261 cmd = cmd.substr(5, cmd.size());
262
Colin Cross2e032ba2015-07-15 18:33:37 -0700263 bool prev_backslash = false;
264 char quote = 0;
265 string out_buf;
266
267 // Strip outer quotes, and fail if it is not a single echo command
268 for (StringPiece::iterator in = cmd.begin(); in != cmd.end(); in++) {
269 if (prev_backslash) {
270 prev_backslash = false;
271 out_buf += *in;
272 } else if (*in == '\\') {
273 prev_backslash = true;
274 out_buf += *in;
275 } else if (quote) {
276 if (*in == quote) {
277 quote = 0;
278 } else {
279 out_buf += *in;
280 }
281 } else {
282 switch (*in) {
283 case '\'':
284 case '"':
285 case '`':
286 quote = *in;
287 break;
288
289 case '<':
290 case '>':
291 case '&':
292 case '|':
293 case ';':
294 return false;
295
296 default:
297 out_buf += *in;
298 }
299 }
300 }
301
302 *out = out_buf;
303 return true;
304 }
305
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900306 bool GenShellScript(const vector<Command*>& commands, string* description) {
307 bool got_descritpion = false;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900308 bool use_gomacc = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900309 bool should_ignore_error = false;
310 cmd_buf_.clear();
311 for (const Command* c : commands) {
312 if (!cmd_buf_.empty()) {
313 if (should_ignore_error) {
314 cmd_buf_ += " ; ";
315 } else {
316 cmd_buf_ += " && ";
317 }
318 }
319 should_ignore_error = c->ignore_error;
320
321 const char* in = c->cmd->c_str();
322 while (isspace(*in))
323 in++;
324
325 bool needs_subshell = commands.size() > 1;
326 if (*in == '(') {
327 needs_subshell = false;
328 }
329
330 if (needs_subshell)
331 cmd_buf_ += '(';
332
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900333 size_t cmd_start = cmd_buf_.size();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900334 StringPiece translated = TranslateCommand(in);
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900335 if (g_detect_android_echo && !got_descritpion && !c->echo &&
336 GetDescriptionFromCommand(translated, description)) {
337 got_descritpion = true;
338 cmd_buf_.resize(cmd_start);
339 translated.clear();
340 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900341 if (translated.empty()) {
342 cmd_buf_ += "true";
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +0900343 } else if (g_goma_dir) {
344 size_t pos = GetGomaccPosForAndroidCompileCommand(translated);
345 if (pos != string::npos) {
346 cmd_buf_.insert(cmd_start + pos, gomacc_);
347 use_gomacc = true;
348 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900349 }
350
351 if (c == commands.back() && c->ignore_error) {
352 cmd_buf_ += " ; true";
353 }
354
355 if (needs_subshell)
356 cmd_buf_ += ')';
357 }
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900358 return g_goma_dir && !use_gomacc;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900359 }
360
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900361 void EmitDepfile() {
362 cmd_buf_ += ' ';
363 string depfile;
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900364 bool result = GetDepfileFromCommand(&cmd_buf_, &depfile);
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900365 cmd_buf_.resize(cmd_buf_.size()-1);
366 if (!result)
367 return;
368 fprintf(fp_, " depfile = %s\n", depfile.c_str());
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900369 fprintf(fp_, " deps = gcc\n");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900370 }
371
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900372 void EmitNode(DepNode* node) {
373 auto p = done_.insert(node->output);
374 if (!p.second)
375 return;
376
Shinichiro Hamaji0f39c522015-07-07 13:14:02 +0900377 if (node->cmds.empty() &&
378 node->deps.empty() && node->order_onlys.empty() && !node->is_phony) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900379 return;
Shinichiro Hamaji0f39c522015-07-07 13:14:02 +0900380 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900381
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900382 StringPiece base = Basename(node->output.str());
383 if (base != node->output.str()) {
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900384 auto p = short_names_.emplace(Intern(base), node->output);
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900385 if (!p.second) {
386 // We generate shortcuts only for targets whose basename are unique.
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900387 p.first->second = kEmptySym;
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900388 }
389 }
390
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900391 vector<Command*> commands;
392 ce_.Eval(node, &commands);
393
394 string rule_name = "phony";
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900395 bool use_local_pool = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900396 if (!commands.empty()) {
397 rule_name = GenRuleName();
398 fprintf(fp_, "rule %s\n", rule_name.c_str());
Colin Cross2e032ba2015-07-15 18:33:37 -0700399
400 string description = "build $out";
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900401 use_local_pool |= GenShellScript(commands, &description);
Colin Cross2e032ba2015-07-15 18:33:37 -0700402 fprintf(fp_, " description = %s\n", description.c_str());
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900403 EmitDepfile();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900404
Shinichiro Hamajieb0e5f92015-07-17 03:38:23 +0900405 // It seems Linux is OK with ~130kB and Mac's limit is ~250kB.
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900406 // TODO: Find this number automatically.
407 if (cmd_buf_.size() > 100 * 1000) {
408 fprintf(fp_, " rspfile = $out.rsp\n");
409 fprintf(fp_, " rspfile_content = %s\n", cmd_buf_.c_str());
Colin Crosse890a912015-07-20 13:28:00 -0700410 fprintf(fp_, " command = %s $out.rsp\n", shell_->c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900411 } else {
Colin Crosse890a912015-07-20 13:28:00 -0700412 fprintf(fp_, " command = %s -c \"%s\"\n", shell_->c_str(), EscapeShell(cmd_buf_).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900413 }
414 }
415
416 EmitBuild(node, rule_name);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900417 if (use_local_pool)
418 fprintf(fp_, " pool = local_pool\n");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900419
420 for (DepNode* d : node->deps) {
421 EmitNode(d);
422 }
Shinichiro Hamaji3f2cf1e2015-07-06 18:58:18 +0900423 for (DepNode* d : node->order_onlys) {
424 EmitNode(d);
425 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900426 }
427
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900428 string EscapeBuildTarget(Symbol s) {
429 if (s.str().find_first_of("$: ") == string::npos)
430 return s.str();
431 string r;
432 for (char c : s.str()) {
433 switch (c) {
434 case '$':
435 case ':':
436 case ' ':
437 r += '$';
438 // fall through.
439 default:
440 r += c;
441 }
442 }
443 return r;
444 }
445
Colin Crosse890a912015-07-20 13:28:00 -0700446 string EscapeShell(string s) {
447 if (s.find_first_of("$`!\\\"") == string::npos)
448 return s;
449 string r;
450 bool lastDollar = false;
451 for (char c : s) {
452 switch (c) {
453 case '$':
454 if (lastDollar) {
455 r += c;
456 lastDollar = false;
457 } else {
458 r += '\\';
459 r += c;
460 lastDollar = true;
461 }
462 break;
463 case '`':
464 case '"':
465 case '!':
466 case '\\':
467 r += '\\';
468 // fall through.
469 default:
470 r += c;
471 lastDollar = false;
472 }
473 }
474 return r;
475 }
476
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900477 void EmitBuild(DepNode* node, const string& rule_name) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900478 fprintf(fp_, "build %s: %s",
479 EscapeBuildTarget(node->output).c_str(),
480 rule_name.c_str());
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900481 vector<Symbol> order_onlys;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900482 for (DepNode* d : node->deps) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900483 fprintf(fp_, " %s", EscapeBuildTarget(d->output).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900484 }
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900485 if (!node->order_onlys.empty()) {
Shinichiro Hamajif4820de2015-06-29 17:03:17 +0900486 fprintf(fp_, " ||");
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900487 for (DepNode* d : node->order_onlys) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900488 fprintf(fp_, " %s", EscapeBuildTarget(d->output).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900489 }
490 }
491 fprintf(fp_, "\n");
492 }
493
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900494 string GetNinjaFilename() const {
495 return StringPrintf("build%s.ninja", ninja_suffix_.c_str());
496 }
497
498 string GetShellScriptFilename() const {
499 return StringPrintf("ninja%s.sh", ninja_suffix_.c_str());
500 }
501
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900502 void GenerateNinja(const vector<DepNode*>& nodes, bool build_all_targets) {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900503 fp_ = fopen(GetNinjaFilename().c_str(), "wb");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900504 if (fp_ == NULL)
505 PERROR("fopen(build.ninja) failed");
506
Shinichiro Hamajid821f6d2015-07-14 04:03:27 +0900507 fprintf(fp_, "# Generated by kati %s\n", kGitVersion);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900508 fprintf(fp_, "\n");
509
Shinichiro Hamaji5163e042015-07-14 03:51:44 +0900510 if (!Vars::used_env_vars().empty()) {
511 fprintf(fp_, "# Environment variables used:\n");
512 for (Symbol e : Vars::used_env_vars()) {
513 shared_ptr<string> val = ev_->EvalVar(e);
514 fprintf(fp_, "# %s=%s\n", e.c_str(), val->c_str());
515 }
516 fprintf(fp_, "\n");
517 }
518
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900519 if (g_goma_dir) {
520 fprintf(fp_, "pool local_pool\n");
Shinichiro Hamaji087cecd2015-07-06 19:51:58 +0900521 fprintf(fp_, " depth = %d\n", g_num_jobs);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900522 }
523
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900524 for (DepNode* node : nodes) {
525 EmitNode(node);
526 }
527
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900528 if (!build_all_targets) {
529 CHECK(!nodes.empty());
530 fprintf(fp_, "\ndefault %s\n", nodes.front()->output.c_str());
531 }
532
Shinichiro Hamaji15fe11d2015-07-11 06:32:37 +0900533 fprintf(fp_, "\n# shortcuts:\n");
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900534 for (auto p : short_names_) {
Shinichiro Hamaji10bb4ab2015-07-16 06:04:48 +0900535 if (!p.second.empty() && !done_.count(p.first))
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900536 fprintf(fp_, "build %s: phony %s\n", p.first.c_str(), p.second.c_str());
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900537 }
538
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900539 fclose(fp_);
540 }
541
542 void GenerateShell() {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900543 FILE* fp = fopen(GetShellScriptFilename().c_str(), "wb");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900544 if (fp == NULL)
545 PERROR("fopen(ninja.sh) failed");
546
Shinichiro Hamaji94d6f2a2015-07-05 05:32:25 +0900547 shared_ptr<string> shell = ev_->EvalVar(kShellSym);
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900548 if (shell->empty())
549 shell = make_shared<string>("/bin/sh");
550 fprintf(fp, "#!%s\n", shell->c_str());
Shinichiro Hamajiaf9887a2015-07-17 03:45:14 +0900551 fprintf(fp, "# Generated by kati %s\n", kGitVersion);
552 fprintf(fp, "\n");
553 fprintf(fp, "cd $(dirname \"$0\")\n");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900554
555 for (const auto& p : ev_->exports()) {
556 if (p.second) {
Shinichiro Hamaji94d6f2a2015-07-05 05:32:25 +0900557 shared_ptr<string> val = ev_->EvalVar(p.first);
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900558 fprintf(fp, "export %s=%s\n", p.first.c_str(), val->c_str());
559 } else {
560 fprintf(fp, "unset %s\n", p.first.c_str());
561 }
562 }
563
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900564 fprintf(fp, "exec ninja -f %s ", GetNinjaFilename().c_str());
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900565 if (g_goma_dir) {
Shinichiro Hamaji6d7c7b72015-07-18 16:51:39 +0900566 fprintf(fp, "-j500 ");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900567 }
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900568 fprintf(fp, "\"$@\"\n");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900569
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900570 if (chmod(GetShellScriptFilename().c_str(), 0755) != 0)
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900571 PERROR("chmod ninja.sh failed");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900572 }
573
574 CommandEvaluator ce_;
575 Evaluator* ev_;
576 FILE* fp_;
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900577 unordered_set<Symbol> done_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900578 int rule_id_;
579 string cmd_buf_;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900580 string gomacc_;
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900581 string ninja_suffix_;
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900582 unordered_map<Symbol, Symbol> short_names_;
Colin Crosse890a912015-07-20 13:28:00 -0700583 shared_ptr<string> shell_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900584};
585
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900586void GenerateNinja(const char* ninja_suffix,
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900587 const vector<DepNode*>& nodes,
588 Evaluator* ev,
589 bool build_all_targets) {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900590 NinjaGenerator ng(ninja_suffix, ev);
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900591 ng.Generate(nodes, build_all_targets);
Shinichiro Hamaji5f86e1a2015-06-29 14:25:39 +0900592}