blob: e9d8733f960c67497fbcb33a924f7da57d89b6b8 [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 Hamajia6e06f82015-07-18 14:08:49 +090023#include <map>
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090024#include <memory>
25#include <string>
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +090026#include <unordered_map>
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090027#include <unordered_set>
28
29#include "command.h"
30#include "dep.h"
31#include "eval.h"
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +090032#include "file_cache.h"
Shinichiro Hamaji087cecd2015-07-06 19:51:58 +090033#include "flags.h"
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090034#include "log.h"
35#include "string_piece.h"
36#include "stringprintf.h"
37#include "strutil.h"
38#include "var.h"
Shinichiro Hamajid821f6d2015-07-14 04:03:27 +090039#include "version.h"
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090040
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090041static StringPiece FindCommandLineFlagWithArg(StringPiece cmd,
42 StringPiece name) {
43 size_t index = cmd.find(name);
44 if (index == string::npos)
45 return StringPiece();
46
47 StringPiece val = TrimLeftSpace(cmd.substr(index + name.size()));
48 index = val.find(name);
49 while (index != string::npos) {
50 val = TrimLeftSpace(val.substr(index + name.size()));
51 index = val.find(name);
52 }
53
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090054 index = val.find(' ');
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090055 CHECK(index != string::npos);
56 return val.substr(0, index);
57}
58
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090059static bool StripPrefix(StringPiece p, StringPiece* s) {
60 if (!HasPrefix(*s, p))
61 return false;
62 *s = s->substr(p.size());
63 return true;
64}
65
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090066size_t GetGomaccPosForAndroidCompileCommand(StringPiece cmdline) {
67 size_t index = cmdline.find(' ');
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090068 if (index == string::npos)
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090069 return string::npos;
70 StringPiece cmd = cmdline.substr(0, index);
71 if (HasSuffix(cmd, "ccache")) {
72 index++;
73 size_t pos = GetGomaccPosForAndroidCompileCommand(cmdline.substr(index));
74 return pos == string::npos ? string::npos : pos + index;
75 }
76 if (!StripPrefix("prebuilts/", &cmd))
77 return string::npos;
78 if (!StripPrefix("gcc/", &cmd) && !StripPrefix("clang/", &cmd))
79 return string::npos;
80 if (!HasSuffix(cmd, "gcc") && !HasSuffix(cmd, "g++") &&
81 !HasSuffix(cmd, "clang") && !HasSuffix(cmd, "clang++")) {
82 return string::npos;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090083 }
84
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090085 StringPiece rest = cmdline.substr(index);
86 return rest.find(" -c ") != string::npos ? 0 : string::npos;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090087}
88
89static bool GetDepfileFromCommandImpl(StringPiece cmd, string* out) {
Shinichiro Hamajid416e612015-07-18 12:43:34 +090090 if ((cmd.find(StringPiece(" -MD ")) == string::npos &&
91 cmd.find(StringPiece(" -MMD ")) == string::npos) ||
92 cmd.find(StringPiece(" -c ")) == string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090093 return false;
94 }
95
Shinichiro Hamaji388e8582015-07-03 16:51:46 +090096 StringPiece mf = FindCommandLineFlagWithArg(cmd, StringPiece(" -MF "));
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090097 if (!mf.empty()) {
98 mf.AppendToString(out);
99 return true;
100 }
101
Shinichiro Hamaji388e8582015-07-03 16:51:46 +0900102 StringPiece o = FindCommandLineFlagWithArg(cmd, StringPiece(" -o "));
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900103 if (o.empty()) {
104 ERROR("Cannot find the depfile in %s", cmd.as_string().c_str());
105 return false;
106 }
107
108 StripExt(o).AppendToString(out);
109 *out += ".d";
110 return true;
111}
112
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900113bool GetDepfileFromCommand(string* cmd, string* out) {
114 CHECK(!cmd->empty());
115 CHECK((*cmd)[cmd->size()-1] == ' ');
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900116
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900117 if (!GetDepfileFromCommandImpl(*cmd, out))
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900118 return false;
119
120 // A hack for Android - llvm-rs-cc seems not to emit a dep file.
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900121 if (cmd->find("bin/llvm-rs-cc ") != string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900122 return false;
123 }
124
125 // TODO: A hack for Makefiles generated by automake.
126
127 // A hack for Android to get .P files instead of .d.
128 string p;
129 StripExt(*out).AppendToString(&p);
130 p += ".P";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900131 if (cmd->find(p) != string::npos) {
132 const string rm_f = "; rm -f " + *out;
133 const size_t found = cmd->find(rm_f);
134 if (found == string::npos) {
135 ERROR("Cannot find removal of .d file: %s", cmd->c_str());
136 }
137 cmd->erase(found, rm_f.size());
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900138 return true;
139 }
140
141 // A hack for Android. For .s files, GCC does not use C
142 // preprocessor, so it ignores -MF flag.
143 string as = "/";
144 StripExt(Basename(*out)).AppendToString(&as);
145 as += ".s";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900146 if (cmd->find(as) != string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900147 return false;
148 }
149
Shinichiro Hamajid416e612015-07-18 12:43:34 +0900150 *cmd += "&& cp ";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900151 *cmd += *out;
152 *cmd += ' ';
153 *cmd += *out;
Shinichiro Hamajid416e612015-07-18 12:43:34 +0900154 *cmd += ".tmp ";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900155 *out += ".tmp";
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900156 return true;
157}
158
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900159class NinjaGenerator {
160 public:
Colin Crossbb2bba12015-07-20 16:31:18 -0700161 NinjaGenerator(const char* ninja_suffix, const char* ninja_dir, Evaluator* ev)
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900162 : ce_(ev), ev_(ev), fp_(NULL), rule_id_(0) {
163 ev_->set_avoid_io(true);
Colin Crosse890a912015-07-20 13:28:00 -0700164 shell_ = ev->EvalVar(kShellSym);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900165 if (g_goma_dir)
166 gomacc_ = StringPrintf("%s/gomacc ", g_goma_dir);
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900167 if (ninja_suffix) {
168 ninja_suffix_ = ninja_suffix;
169 }
Colin Crossbb2bba12015-07-20 16:31:18 -0700170 if (ninja_dir) {
171 ninja_dir_ = ninja_dir;
172 } else {
173 ninja_dir_ = ".";
174 }
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900175
176 for (Symbol e : Vars::used_env_vars()) {
177 shared_ptr<string> val = ev_->EvalVar(e);
178 used_envs_.emplace(e.str(), *val);
179 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900180 }
181
182 ~NinjaGenerator() {
183 ev_->set_avoid_io(false);
184 }
185
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900186 void Generate(const vector<DepNode*>& nodes,
187 bool build_all_targets,
188 const string& orig_args) {
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900189 GenerateEnvlist();
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900190 GenerateNinja(nodes, build_all_targets, orig_args);
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900191 GenerateShell();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900192 }
193
194 private:
195 string GenRuleName() {
196 return StringPrintf("rule%d", rule_id_++);
197 }
198
199 StringPiece TranslateCommand(const char* in) {
200 const size_t orig_size = cmd_buf_.size();
201 bool prev_backslash = false;
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900202 // Set space as an initial value so the leading comment will be
203 // stripped out.
204 char prev_char = ' ';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900205 char quote = 0;
206 bool done = false;
207 for (; *in && !done; in++) {
208 switch (*in) {
209 case '#':
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900210 if (quote == 0 && isspace(prev_char)) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900211 done = true;
Colin Cross415e4a12015-07-15 18:21:38 -0700212 } else {
213 cmd_buf_ += *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900214 }
Colin Cross415e4a12015-07-15 18:21:38 -0700215 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900216
217 case '\'':
218 case '"':
219 case '`':
220 if (quote) {
221 if (quote == *in)
222 quote = 0;
223 } else if (!prev_backslash) {
224 quote = *in;
225 }
Shinichiro Hamajif79ca532015-06-29 17:25:54 +0900226 cmd_buf_ += *in;
Shinichiro Hamaji4212e382015-06-29 17:21:04 +0900227 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900228
229 case '$':
230 cmd_buf_ += "$$";
231 break;
232
233 case '\t':
234 cmd_buf_ += ' ';
235 break;
236
237 case '\n':
238 if (prev_backslash) {
239 cmd_buf_[cmd_buf_.size()-1] = ' ';
240 } else {
241 cmd_buf_ += ' ';
242 }
Shinichiro Hamaji4d151832015-06-29 18:15:46 +0900243 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900244
245 case '\\':
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900246 cmd_buf_ += '\\';
247 break;
248
249 default:
250 cmd_buf_ += *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900251 }
Colin Crossf6f1cf42015-07-15 15:25:30 -0700252
253 if (*in == '\\') {
254 prev_backslash = !prev_backslash;
255 } else {
256 prev_backslash = false;
257 }
258
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900259 prev_char = *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900260 }
261
262 while (true) {
263 char c = cmd_buf_[cmd_buf_.size()-1];
264 if (!isspace(c) && c != ';')
265 break;
266 cmd_buf_.resize(cmd_buf_.size() - 1);
267 }
268
269 return StringPiece(cmd_buf_.data() + orig_size,
270 cmd_buf_.size() - orig_size);
271 }
272
Colin Cross2e032ba2015-07-15 18:33:37 -0700273 bool GetDescriptionFromCommand(StringPiece cmd, string *out) {
Colin Cross2e032ba2015-07-15 18:33:37 -0700274 if (!HasPrefix(cmd, "echo ")) {
275 return false;
276 }
277 cmd = cmd.substr(5, cmd.size());
278
Colin Cross2e032ba2015-07-15 18:33:37 -0700279 bool prev_backslash = false;
280 char quote = 0;
281 string out_buf;
282
283 // Strip outer quotes, and fail if it is not a single echo command
284 for (StringPiece::iterator in = cmd.begin(); in != cmd.end(); in++) {
285 if (prev_backslash) {
286 prev_backslash = false;
287 out_buf += *in;
288 } else if (*in == '\\') {
289 prev_backslash = true;
290 out_buf += *in;
291 } else if (quote) {
292 if (*in == quote) {
293 quote = 0;
294 } else {
295 out_buf += *in;
296 }
297 } else {
298 switch (*in) {
299 case '\'':
300 case '"':
301 case '`':
302 quote = *in;
303 break;
304
305 case '<':
306 case '>':
307 case '&':
308 case '|':
309 case ';':
310 return false;
311
312 default:
313 out_buf += *in;
314 }
315 }
316 }
317
318 *out = out_buf;
319 return true;
320 }
321
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900322 bool GenShellScript(const vector<Command*>& commands, string* description) {
323 bool got_descritpion = false;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900324 bool use_gomacc = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900325 bool should_ignore_error = false;
326 cmd_buf_.clear();
327 for (const Command* c : commands) {
328 if (!cmd_buf_.empty()) {
329 if (should_ignore_error) {
330 cmd_buf_ += " ; ";
331 } else {
332 cmd_buf_ += " && ";
333 }
334 }
335 should_ignore_error = c->ignore_error;
336
337 const char* in = c->cmd->c_str();
338 while (isspace(*in))
339 in++;
340
341 bool needs_subshell = commands.size() > 1;
342 if (*in == '(') {
343 needs_subshell = false;
344 }
345
346 if (needs_subshell)
347 cmd_buf_ += '(';
348
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900349 size_t cmd_start = cmd_buf_.size();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900350 StringPiece translated = TranslateCommand(in);
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900351 if (g_detect_android_echo && !got_descritpion && !c->echo &&
352 GetDescriptionFromCommand(translated, description)) {
353 got_descritpion = true;
354 cmd_buf_.resize(cmd_start);
355 translated.clear();
356 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900357 if (translated.empty()) {
358 cmd_buf_ += "true";
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +0900359 } else if (g_goma_dir) {
360 size_t pos = GetGomaccPosForAndroidCompileCommand(translated);
361 if (pos != string::npos) {
362 cmd_buf_.insert(cmd_start + pos, gomacc_);
363 use_gomacc = true;
364 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900365 }
366
367 if (c == commands.back() && c->ignore_error) {
368 cmd_buf_ += " ; true";
369 }
370
371 if (needs_subshell)
372 cmd_buf_ += ')';
373 }
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900374 return g_goma_dir && !use_gomacc;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900375 }
376
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900377 void EmitDepfile() {
378 cmd_buf_ += ' ';
379 string depfile;
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900380 bool result = GetDepfileFromCommand(&cmd_buf_, &depfile);
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900381 cmd_buf_.resize(cmd_buf_.size()-1);
382 if (!result)
383 return;
384 fprintf(fp_, " depfile = %s\n", depfile.c_str());
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900385 fprintf(fp_, " deps = gcc\n");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900386 }
387
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900388 void EmitNode(DepNode* node) {
389 auto p = done_.insert(node->output);
390 if (!p.second)
391 return;
392
Shinichiro Hamaji0f39c522015-07-07 13:14:02 +0900393 if (node->cmds.empty() &&
394 node->deps.empty() && node->order_onlys.empty() && !node->is_phony) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900395 return;
Shinichiro Hamaji0f39c522015-07-07 13:14:02 +0900396 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900397
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900398 StringPiece base = Basename(node->output.str());
399 if (base != node->output.str()) {
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900400 auto p = short_names_.emplace(Intern(base), node->output);
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900401 if (!p.second) {
402 // We generate shortcuts only for targets whose basename are unique.
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900403 p.first->second = kEmptySym;
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900404 }
405 }
406
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900407 vector<Command*> commands;
408 ce_.Eval(node, &commands);
409
410 string rule_name = "phony";
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900411 bool use_local_pool = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900412 if (!commands.empty()) {
413 rule_name = GenRuleName();
414 fprintf(fp_, "rule %s\n", rule_name.c_str());
Colin Cross2e032ba2015-07-15 18:33:37 -0700415
416 string description = "build $out";
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900417 use_local_pool |= GenShellScript(commands, &description);
Colin Cross2e032ba2015-07-15 18:33:37 -0700418 fprintf(fp_, " description = %s\n", description.c_str());
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900419 EmitDepfile();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900420
Shinichiro Hamajieb0e5f92015-07-17 03:38:23 +0900421 // It seems Linux is OK with ~130kB and Mac's limit is ~250kB.
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900422 // TODO: Find this number automatically.
423 if (cmd_buf_.size() > 100 * 1000) {
424 fprintf(fp_, " rspfile = $out.rsp\n");
425 fprintf(fp_, " rspfile_content = %s\n", cmd_buf_.c_str());
Colin Crosse890a912015-07-20 13:28:00 -0700426 fprintf(fp_, " command = %s $out.rsp\n", shell_->c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900427 } else {
Colin Crosse890a912015-07-20 13:28:00 -0700428 fprintf(fp_, " command = %s -c \"%s\"\n", shell_->c_str(), EscapeShell(cmd_buf_).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900429 }
430 }
431
432 EmitBuild(node, rule_name);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900433 if (use_local_pool)
434 fprintf(fp_, " pool = local_pool\n");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900435
436 for (DepNode* d : node->deps) {
437 EmitNode(d);
438 }
Shinichiro Hamaji3f2cf1e2015-07-06 18:58:18 +0900439 for (DepNode* d : node->order_onlys) {
440 EmitNode(d);
441 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900442 }
443
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900444 string EscapeBuildTarget(Symbol s) {
445 if (s.str().find_first_of("$: ") == string::npos)
446 return s.str();
447 string r;
448 for (char c : s.str()) {
449 switch (c) {
450 case '$':
451 case ':':
452 case ' ':
453 r += '$';
454 // fall through.
455 default:
456 r += c;
457 }
458 }
459 return r;
460 }
461
Colin Crosse890a912015-07-20 13:28:00 -0700462 string EscapeShell(string s) {
463 if (s.find_first_of("$`!\\\"") == string::npos)
464 return s;
465 string r;
466 bool lastDollar = false;
467 for (char c : s) {
468 switch (c) {
469 case '$':
470 if (lastDollar) {
471 r += c;
472 lastDollar = false;
473 } else {
474 r += '\\';
475 r += c;
476 lastDollar = true;
477 }
478 break;
479 case '`':
480 case '"':
481 case '!':
482 case '\\':
483 r += '\\';
484 // fall through.
485 default:
486 r += c;
487 lastDollar = false;
488 }
489 }
490 return r;
491 }
492
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900493 void EmitBuild(DepNode* node, const string& rule_name) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900494 fprintf(fp_, "build %s: %s",
495 EscapeBuildTarget(node->output).c_str(),
496 rule_name.c_str());
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900497 vector<Symbol> order_onlys;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900498 for (DepNode* d : node->deps) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900499 fprintf(fp_, " %s", EscapeBuildTarget(d->output).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900500 }
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900501 if (!node->order_onlys.empty()) {
Shinichiro Hamajif4820de2015-06-29 17:03:17 +0900502 fprintf(fp_, " ||");
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900503 for (DepNode* d : node->order_onlys) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900504 fprintf(fp_, " %s", EscapeBuildTarget(d->output).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900505 }
506 }
507 fprintf(fp_, "\n");
508 }
509
Colin Crossbb2bba12015-07-20 16:31:18 -0700510 string GetNinjaDirectory() const {
511 return ninja_dir_;
512 }
513
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900514 void EmitRegenRules(const string& orig_args) {
515 if (!g_gen_regen_rule)
516 return;
517
518 fprintf(fp_, "rule regen_ninja\n");
519 fprintf(fp_, " command = %s\n", orig_args.c_str());
520 fprintf(fp_, " generator = 1\n");
521 fprintf(fp_, " description = Regenerate ninja files due to dependency\n");
522 fprintf(fp_, "build %s: regen_ninja", GetNinjaFilename().c_str());
523 unordered_set<string> makefiles;
524 MakefileCacheManager::Get()->GetAllFilenames(&makefiles);
525 for (const string& makefile : makefiles) {
526 fprintf(fp_, " %.*s", SPF(makefile));
527 }
Shinichiro Hamaji3ed61482015-07-18 14:16:32 +0900528 // TODO: Add dependencies to directories read by $(wildcard)
529 // or $(shell find).
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900530 if (!used_envs_.empty())
531 fprintf(fp_, " %s", GetEnvlistFilename().c_str());
532 fprintf(fp_, "\n\n");
533
534 if (used_envs_.empty())
535 return;
536
537 fprintf(fp_, "build .always_build: phony\n");
538 fprintf(fp_, "rule regen_envlist\n");
539 fprintf(fp_, " command = rm -f $out.tmp");
540 for (const auto& p : used_envs_) {
541 fprintf(fp_, " && echo %s=$$%s >> $out.tmp",
542 p.first.c_str(), p.first.c_str());
543 }
Shinichiro Hamajid556e622015-07-18 15:33:58 +0900544 if (g_error_on_env_change) {
545 fprintf(fp_, " && (diff $out.tmp $out || (echo Environment variable changes are detected && false))\n");
546 } else {
547 fprintf(fp_, " && (diff $out.tmp $out || mv $out.tmp $out)\n");
548 }
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900549 fprintf(fp_, " restat = 1\n");
Shinichiro Hamaji8bd580e2015-07-18 14:22:29 +0900550 fprintf(fp_, " generator = 1\n");
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900551 fprintf(fp_, " description = Update $out\n");
552 fprintf(fp_, "build %s: regen_envlist .always_build\n\n",
553 GetEnvlistFilename().c_str());
554 }
555
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900556 string GetNinjaFilename() const {
Colin Crossbb2bba12015-07-20 16:31:18 -0700557 return StringPrintf("%s/build%s.ninja", ninja_dir_.c_str(), ninja_suffix_.c_str());
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900558 }
559
560 string GetShellScriptFilename() const {
Colin Crossbb2bba12015-07-20 16:31:18 -0700561 return StringPrintf("%s/ninja%s.sh", ninja_dir_.c_str(), ninja_suffix_.c_str());
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900562 }
563
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900564 string GetEnvlistFilename() const {
565 return StringPrintf(".kati_env%s", ninja_suffix_.c_str());
566 }
567
Shinichiro Hamajid556e622015-07-18 15:33:58 +0900568 string GetLunchFilename() const {
569 return StringPrintf(".kati_lunch%s", ninja_suffix_.c_str());
570 }
571
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900572 void GenerateNinja(const vector<DepNode*>& nodes,
573 bool build_all_targets,
574 const string& orig_args) {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900575 fp_ = fopen(GetNinjaFilename().c_str(), "wb");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900576 if (fp_ == NULL)
577 PERROR("fopen(build.ninja) failed");
578
Shinichiro Hamajid821f6d2015-07-14 04:03:27 +0900579 fprintf(fp_, "# Generated by kati %s\n", kGitVersion);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900580 fprintf(fp_, "\n");
581
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900582 if (!used_envs_.empty()) {
Shinichiro Hamaji5163e042015-07-14 03:51:44 +0900583 fprintf(fp_, "# Environment variables used:\n");
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900584 for (const auto& p : used_envs_) {
585 fprintf(fp_, "# %s=%s\n", p.first.c_str(), p.second.c_str());
Shinichiro Hamaji5163e042015-07-14 03:51:44 +0900586 }
587 fprintf(fp_, "\n");
588 }
589
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900590 if (g_goma_dir) {
591 fprintf(fp_, "pool local_pool\n");
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900592 fprintf(fp_, " depth = %d\n\n", g_num_jobs);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900593 }
594
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900595 EmitRegenRules(orig_args);
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900596
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900597 for (DepNode* node : nodes) {
598 EmitNode(node);
599 }
600
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900601 if (!build_all_targets) {
602 CHECK(!nodes.empty());
603 fprintf(fp_, "\ndefault %s\n", nodes.front()->output.c_str());
604 }
605
Shinichiro Hamaji15fe11d2015-07-11 06:32:37 +0900606 fprintf(fp_, "\n# shortcuts:\n");
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900607 for (auto p : short_names_) {
Shinichiro Hamaji10bb4ab2015-07-16 06:04:48 +0900608 if (!p.second.empty() && !done_.count(p.first))
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900609 fprintf(fp_, "build %s: phony %s\n", p.first.c_str(), p.second.c_str());
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900610 }
611
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900612 fclose(fp_);
613 }
614
615 void GenerateShell() {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900616 FILE* fp = fopen(GetShellScriptFilename().c_str(), "wb");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900617 if (fp == NULL)
618 PERROR("fopen(ninja.sh) failed");
619
Shinichiro Hamaji94d6f2a2015-07-05 05:32:25 +0900620 shared_ptr<string> shell = ev_->EvalVar(kShellSym);
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900621 if (shell->empty())
622 shell = make_shared<string>("/bin/sh");
623 fprintf(fp, "#!%s\n", shell->c_str());
Shinichiro Hamajiaf9887a2015-07-17 03:45:14 +0900624 fprintf(fp, "# Generated by kati %s\n", kGitVersion);
625 fprintf(fp, "\n");
Shinichiro Hamajic7629e02015-07-22 02:41:57 +0900626 if (ninja_dir_ == ".")
627 fprintf(fp, "cd $(dirname \"$0\")\n");
Shinichiro Hamajid556e622015-07-18 15:33:58 +0900628 if (!ninja_suffix_.empty()) {
629 fprintf(fp, "if [ -f %s ]; then\n export $(cat %s)\nfi\n",
630 GetEnvlistFilename().c_str(), GetEnvlistFilename().c_str());
631 fprintf(fp, "if [ -f %s ]; then\n export $(cat %s)\nfi\n",
632 GetLunchFilename().c_str(), GetLunchFilename().c_str());
633 }
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900634
635 for (const auto& p : ev_->exports()) {
636 if (p.second) {
Shinichiro Hamaji94d6f2a2015-07-05 05:32:25 +0900637 shared_ptr<string> val = ev_->EvalVar(p.first);
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900638 fprintf(fp, "export %s=%s\n", p.first.c_str(), val->c_str());
639 } else {
640 fprintf(fp, "unset %s\n", p.first.c_str());
641 }
642 }
643
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900644 fprintf(fp, "exec ninja -f %s ", GetNinjaFilename().c_str());
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900645 if (g_goma_dir) {
Shinichiro Hamaji6d7c7b72015-07-18 16:51:39 +0900646 fprintf(fp, "-j500 ");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900647 }
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900648 fprintf(fp, "\"$@\"\n");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900649
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900650 if (chmod(GetShellScriptFilename().c_str(), 0755) != 0)
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900651 PERROR("chmod ninja.sh failed");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900652 }
653
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900654 void GenerateEnvlist() {
655 if (used_envs_.empty())
656 return;
657 FILE* fp = fopen(GetEnvlistFilename().c_str(), "wb");
658 for (const auto& p : used_envs_) {
659 fprintf(fp, "%s=%s\n", p.first.c_str(), p.second.c_str());
660 }
661 fclose(fp);
662 }
663
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900664 CommandEvaluator ce_;
665 Evaluator* ev_;
666 FILE* fp_;
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900667 unordered_set<Symbol> done_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900668 int rule_id_;
669 string cmd_buf_;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900670 string gomacc_;
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900671 string ninja_suffix_;
Colin Crossbb2bba12015-07-20 16:31:18 -0700672 string ninja_dir_;
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900673 unordered_map<Symbol, Symbol> short_names_;
Colin Crosse890a912015-07-20 13:28:00 -0700674 shared_ptr<string> shell_;
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900675 map<string, string> used_envs_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900676};
677
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900678void GenerateNinja(const char* ninja_suffix,
Colin Crossbb2bba12015-07-20 16:31:18 -0700679 const char* ninja_dir,
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900680 const vector<DepNode*>& nodes,
681 Evaluator* ev,
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900682 bool build_all_targets,
683 const string& orig_args) {
Colin Crossbb2bba12015-07-20 16:31:18 -0700684 NinjaGenerator ng(ninja_suffix, ninja_dir, ev);
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900685 ng.Generate(nodes, build_all_targets, orig_args);
Shinichiro Hamaji5f86e1a2015-06-29 14:25:39 +0900686}