blob: 05c8455bfbb01712393dbfc4d5d0c39a4f9ed8ab [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:
Colin Crossbb2bba12015-07-20 16:31:18 -0700158 NinjaGenerator(const char* ninja_suffix, const char* ninja_dir, 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 }
Colin Crossbb2bba12015-07-20 16:31:18 -0700167 if (ninja_dir) {
168 ninja_dir_ = ninja_dir;
169 } else {
170 ninja_dir_ = ".";
171 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900172 }
173
174 ~NinjaGenerator() {
175 ev_->set_avoid_io(false);
176 }
177
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900178 void Generate(const vector<DepNode*>& nodes, bool build_all_targets) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900179 GenerateShell();
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900180 GenerateNinja(nodes, build_all_targets);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900181 }
182
183 private:
184 string GenRuleName() {
185 return StringPrintf("rule%d", rule_id_++);
186 }
187
188 StringPiece TranslateCommand(const char* in) {
189 const size_t orig_size = cmd_buf_.size();
190 bool prev_backslash = false;
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900191 // Set space as an initial value so the leading comment will be
192 // stripped out.
193 char prev_char = ' ';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900194 char quote = 0;
195 bool done = false;
196 for (; *in && !done; in++) {
197 switch (*in) {
198 case '#':
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900199 if (quote == 0 && isspace(prev_char)) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900200 done = true;
Colin Cross415e4a12015-07-15 18:21:38 -0700201 } else {
202 cmd_buf_ += *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900203 }
Colin Cross415e4a12015-07-15 18:21:38 -0700204 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900205
206 case '\'':
207 case '"':
208 case '`':
209 if (quote) {
210 if (quote == *in)
211 quote = 0;
212 } else if (!prev_backslash) {
213 quote = *in;
214 }
Shinichiro Hamajif79ca532015-06-29 17:25:54 +0900215 cmd_buf_ += *in;
Shinichiro Hamaji4212e382015-06-29 17:21:04 +0900216 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900217
218 case '$':
219 cmd_buf_ += "$$";
220 break;
221
222 case '\t':
223 cmd_buf_ += ' ';
224 break;
225
226 case '\n':
227 if (prev_backslash) {
228 cmd_buf_[cmd_buf_.size()-1] = ' ';
229 } else {
230 cmd_buf_ += ' ';
231 }
Shinichiro Hamaji4d151832015-06-29 18:15:46 +0900232 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900233
234 case '\\':
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900235 cmd_buf_ += '\\';
236 break;
237
238 default:
239 cmd_buf_ += *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900240 }
Colin Crossf6f1cf42015-07-15 15:25:30 -0700241
242 if (*in == '\\') {
243 prev_backslash = !prev_backslash;
244 } else {
245 prev_backslash = false;
246 }
247
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900248 prev_char = *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900249 }
250
251 while (true) {
252 char c = cmd_buf_[cmd_buf_.size()-1];
253 if (!isspace(c) && c != ';')
254 break;
255 cmd_buf_.resize(cmd_buf_.size() - 1);
256 }
257
258 return StringPiece(cmd_buf_.data() + orig_size,
259 cmd_buf_.size() - orig_size);
260 }
261
Colin Cross2e032ba2015-07-15 18:33:37 -0700262 bool GetDescriptionFromCommand(StringPiece cmd, string *out) {
Colin Cross2e032ba2015-07-15 18:33:37 -0700263 if (!HasPrefix(cmd, "echo ")) {
264 return false;
265 }
266 cmd = cmd.substr(5, cmd.size());
267
Colin Cross2e032ba2015-07-15 18:33:37 -0700268 bool prev_backslash = false;
269 char quote = 0;
270 string out_buf;
271
272 // Strip outer quotes, and fail if it is not a single echo command
273 for (StringPiece::iterator in = cmd.begin(); in != cmd.end(); in++) {
274 if (prev_backslash) {
275 prev_backslash = false;
276 out_buf += *in;
277 } else if (*in == '\\') {
278 prev_backslash = true;
279 out_buf += *in;
280 } else if (quote) {
281 if (*in == quote) {
282 quote = 0;
283 } else {
284 out_buf += *in;
285 }
286 } else {
287 switch (*in) {
288 case '\'':
289 case '"':
290 case '`':
291 quote = *in;
292 break;
293
294 case '<':
295 case '>':
296 case '&':
297 case '|':
298 case ';':
299 return false;
300
301 default:
302 out_buf += *in;
303 }
304 }
305 }
306
307 *out = out_buf;
308 return true;
309 }
310
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900311 bool GenShellScript(const vector<Command*>& commands, string* description) {
312 bool got_descritpion = false;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900313 bool use_gomacc = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900314 bool should_ignore_error = false;
315 cmd_buf_.clear();
316 for (const Command* c : commands) {
317 if (!cmd_buf_.empty()) {
318 if (should_ignore_error) {
319 cmd_buf_ += " ; ";
320 } else {
321 cmd_buf_ += " && ";
322 }
323 }
324 should_ignore_error = c->ignore_error;
325
326 const char* in = c->cmd->c_str();
327 while (isspace(*in))
328 in++;
329
330 bool needs_subshell = commands.size() > 1;
331 if (*in == '(') {
332 needs_subshell = false;
333 }
334
335 if (needs_subshell)
336 cmd_buf_ += '(';
337
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900338 size_t cmd_start = cmd_buf_.size();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900339 StringPiece translated = TranslateCommand(in);
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900340 if (g_detect_android_echo && !got_descritpion && !c->echo &&
341 GetDescriptionFromCommand(translated, description)) {
342 got_descritpion = true;
343 cmd_buf_.resize(cmd_start);
344 translated.clear();
345 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900346 if (translated.empty()) {
347 cmd_buf_ += "true";
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +0900348 } else if (g_goma_dir) {
349 size_t pos = GetGomaccPosForAndroidCompileCommand(translated);
350 if (pos != string::npos) {
351 cmd_buf_.insert(cmd_start + pos, gomacc_);
352 use_gomacc = true;
353 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900354 }
355
356 if (c == commands.back() && c->ignore_error) {
357 cmd_buf_ += " ; true";
358 }
359
360 if (needs_subshell)
361 cmd_buf_ += ')';
362 }
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900363 return g_goma_dir && !use_gomacc;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900364 }
365
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900366 void EmitDepfile() {
367 cmd_buf_ += ' ';
368 string depfile;
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900369 bool result = GetDepfileFromCommand(&cmd_buf_, &depfile);
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900370 cmd_buf_.resize(cmd_buf_.size()-1);
371 if (!result)
372 return;
373 fprintf(fp_, " depfile = %s\n", depfile.c_str());
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900374 fprintf(fp_, " deps = gcc\n");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900375 }
376
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900377 void EmitNode(DepNode* node) {
378 auto p = done_.insert(node->output);
379 if (!p.second)
380 return;
381
Shinichiro Hamaji0f39c522015-07-07 13:14:02 +0900382 if (node->cmds.empty() &&
383 node->deps.empty() && node->order_onlys.empty() && !node->is_phony) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900384 return;
Shinichiro Hamaji0f39c522015-07-07 13:14:02 +0900385 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900386
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900387 StringPiece base = Basename(node->output.str());
388 if (base != node->output.str()) {
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900389 auto p = short_names_.emplace(Intern(base), node->output);
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900390 if (!p.second) {
391 // We generate shortcuts only for targets whose basename are unique.
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900392 p.first->second = kEmptySym;
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900393 }
394 }
395
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900396 vector<Command*> commands;
397 ce_.Eval(node, &commands);
398
399 string rule_name = "phony";
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900400 bool use_local_pool = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900401 if (!commands.empty()) {
402 rule_name = GenRuleName();
403 fprintf(fp_, "rule %s\n", rule_name.c_str());
Colin Cross2e032ba2015-07-15 18:33:37 -0700404
405 string description = "build $out";
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900406 use_local_pool |= GenShellScript(commands, &description);
Colin Cross2e032ba2015-07-15 18:33:37 -0700407 fprintf(fp_, " description = %s\n", description.c_str());
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900408 EmitDepfile();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900409
Shinichiro Hamajieb0e5f92015-07-17 03:38:23 +0900410 // It seems Linux is OK with ~130kB and Mac's limit is ~250kB.
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900411 // TODO: Find this number automatically.
412 if (cmd_buf_.size() > 100 * 1000) {
413 fprintf(fp_, " rspfile = $out.rsp\n");
414 fprintf(fp_, " rspfile_content = %s\n", cmd_buf_.c_str());
Colin Crosse890a912015-07-20 13:28:00 -0700415 fprintf(fp_, " command = %s $out.rsp\n", shell_->c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900416 } else {
Colin Crosse890a912015-07-20 13:28:00 -0700417 fprintf(fp_, " command = %s -c \"%s\"\n", shell_->c_str(), EscapeShell(cmd_buf_).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900418 }
419 }
420
421 EmitBuild(node, rule_name);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900422 if (use_local_pool)
423 fprintf(fp_, " pool = local_pool\n");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900424
425 for (DepNode* d : node->deps) {
426 EmitNode(d);
427 }
Shinichiro Hamaji3f2cf1e2015-07-06 18:58:18 +0900428 for (DepNode* d : node->order_onlys) {
429 EmitNode(d);
430 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900431 }
432
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900433 string EscapeBuildTarget(Symbol s) {
434 if (s.str().find_first_of("$: ") == string::npos)
435 return s.str();
436 string r;
437 for (char c : s.str()) {
438 switch (c) {
439 case '$':
440 case ':':
441 case ' ':
442 r += '$';
443 // fall through.
444 default:
445 r += c;
446 }
447 }
448 return r;
449 }
450
Colin Crosse890a912015-07-20 13:28:00 -0700451 string EscapeShell(string s) {
452 if (s.find_first_of("$`!\\\"") == string::npos)
453 return s;
454 string r;
455 bool lastDollar = false;
456 for (char c : s) {
457 switch (c) {
458 case '$':
459 if (lastDollar) {
460 r += c;
461 lastDollar = false;
462 } else {
463 r += '\\';
464 r += c;
465 lastDollar = true;
466 }
467 break;
468 case '`':
469 case '"':
470 case '!':
471 case '\\':
472 r += '\\';
473 // fall through.
474 default:
475 r += c;
476 lastDollar = false;
477 }
478 }
479 return r;
480 }
481
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900482 void EmitBuild(DepNode* node, const string& rule_name) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900483 fprintf(fp_, "build %s: %s",
484 EscapeBuildTarget(node->output).c_str(),
485 rule_name.c_str());
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900486 vector<Symbol> order_onlys;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900487 for (DepNode* d : node->deps) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900488 fprintf(fp_, " %s", EscapeBuildTarget(d->output).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900489 }
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900490 if (!node->order_onlys.empty()) {
Shinichiro Hamajif4820de2015-06-29 17:03:17 +0900491 fprintf(fp_, " ||");
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900492 for (DepNode* d : node->order_onlys) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900493 fprintf(fp_, " %s", EscapeBuildTarget(d->output).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900494 }
495 }
496 fprintf(fp_, "\n");
497 }
498
Colin Crossbb2bba12015-07-20 16:31:18 -0700499 string GetNinjaDirectory() const {
500 return ninja_dir_;
501 }
502
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900503 string GetNinjaFilename() const {
Colin Crossbb2bba12015-07-20 16:31:18 -0700504 return StringPrintf("%s/build%s.ninja", ninja_dir_.c_str(), ninja_suffix_.c_str());
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900505 }
506
507 string GetShellScriptFilename() const {
Colin Crossbb2bba12015-07-20 16:31:18 -0700508 return StringPrintf("%s/ninja%s.sh", ninja_dir_.c_str(), ninja_suffix_.c_str());
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900509 }
510
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900511 void GenerateNinja(const vector<DepNode*>& nodes, bool build_all_targets) {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900512 fp_ = fopen(GetNinjaFilename().c_str(), "wb");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900513 if (fp_ == NULL)
514 PERROR("fopen(build.ninja) failed");
515
Shinichiro Hamajid821f6d2015-07-14 04:03:27 +0900516 fprintf(fp_, "# Generated by kati %s\n", kGitVersion);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900517 fprintf(fp_, "\n");
518
Shinichiro Hamaji5163e042015-07-14 03:51:44 +0900519 if (!Vars::used_env_vars().empty()) {
520 fprintf(fp_, "# Environment variables used:\n");
521 for (Symbol e : Vars::used_env_vars()) {
522 shared_ptr<string> val = ev_->EvalVar(e);
523 fprintf(fp_, "# %s=%s\n", e.c_str(), val->c_str());
524 }
525 fprintf(fp_, "\n");
526 }
527
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900528 if (g_goma_dir) {
529 fprintf(fp_, "pool local_pool\n");
Shinichiro Hamaji087cecd2015-07-06 19:51:58 +0900530 fprintf(fp_, " depth = %d\n", g_num_jobs);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900531 }
532
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900533 for (DepNode* node : nodes) {
534 EmitNode(node);
535 }
536
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900537 if (!build_all_targets) {
538 CHECK(!nodes.empty());
539 fprintf(fp_, "\ndefault %s\n", nodes.front()->output.c_str());
540 }
541
Shinichiro Hamaji15fe11d2015-07-11 06:32:37 +0900542 fprintf(fp_, "\n# shortcuts:\n");
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900543 for (auto p : short_names_) {
Shinichiro Hamaji10bb4ab2015-07-16 06:04:48 +0900544 if (!p.second.empty() && !done_.count(p.first))
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900545 fprintf(fp_, "build %s: phony %s\n", p.first.c_str(), p.second.c_str());
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900546 }
547
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900548 fclose(fp_);
549 }
550
551 void GenerateShell() {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900552 FILE* fp = fopen(GetShellScriptFilename().c_str(), "wb");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900553 if (fp == NULL)
554 PERROR("fopen(ninja.sh) failed");
555
Shinichiro Hamaji94d6f2a2015-07-05 05:32:25 +0900556 shared_ptr<string> shell = ev_->EvalVar(kShellSym);
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900557 if (shell->empty())
558 shell = make_shared<string>("/bin/sh");
559 fprintf(fp, "#!%s\n", shell->c_str());
Shinichiro Hamajiaf9887a2015-07-17 03:45:14 +0900560 fprintf(fp, "# Generated by kati %s\n", kGitVersion);
561 fprintf(fp, "\n");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900562
563 for (const auto& p : ev_->exports()) {
564 if (p.second) {
Shinichiro Hamaji94d6f2a2015-07-05 05:32:25 +0900565 shared_ptr<string> val = ev_->EvalVar(p.first);
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900566 fprintf(fp, "export %s=%s\n", p.first.c_str(), val->c_str());
567 } else {
568 fprintf(fp, "unset %s\n", p.first.c_str());
569 }
570 }
571
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900572 fprintf(fp, "exec ninja -f %s ", GetNinjaFilename().c_str());
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900573 if (g_goma_dir) {
Shinichiro Hamaji6d7c7b72015-07-18 16:51:39 +0900574 fprintf(fp, "-j500 ");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900575 }
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900576 fprintf(fp, "\"$@\"\n");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900577
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900578 if (chmod(GetShellScriptFilename().c_str(), 0755) != 0)
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900579 PERROR("chmod ninja.sh failed");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900580 }
581
582 CommandEvaluator ce_;
583 Evaluator* ev_;
584 FILE* fp_;
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900585 unordered_set<Symbol> done_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900586 int rule_id_;
587 string cmd_buf_;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900588 string gomacc_;
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900589 string ninja_suffix_;
Colin Crossbb2bba12015-07-20 16:31:18 -0700590 string ninja_dir_;
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900591 unordered_map<Symbol, Symbol> short_names_;
Colin Crosse890a912015-07-20 13:28:00 -0700592 shared_ptr<string> shell_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900593};
594
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900595void GenerateNinja(const char* ninja_suffix,
Colin Crossbb2bba12015-07-20 16:31:18 -0700596 const char* ninja_dir,
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900597 const vector<DepNode*>& nodes,
598 Evaluator* ev,
599 bool build_all_targets) {
Colin Crossbb2bba12015-07-20 16:31:18 -0700600 NinjaGenerator ng(ninja_suffix, ninja_dir, ev);
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900601 ng.Generate(nodes, build_all_targets);
Shinichiro Hamaji5f86e1a2015-06-29 14:25:39 +0900602}