blob: 5b4b7df4bccbbcba81029995a42de100a4408541 [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 Hamajied883ef2015-07-31 13:15:04 +090020#include <stdlib.h>
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +090021#include <sys/stat.h>
22#include <unistd.h>
Shinichiro Hamaji5f86e1a2015-06-29 14:25:39 +090023
Shinichiro Hamajied8db982016-01-27 06:12:33 +000024#include <algorithm>
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +090025#include <map>
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090026#include <string>
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +090027#include <unordered_map>
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090028#include <unordered_set>
29
30#include "command.h"
31#include "dep.h"
32#include "eval.h"
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +090033#include "file_cache.h"
Shinichiro Hamajib58bb4b2015-07-30 18:02:51 +090034#include "fileutil.h"
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +090035#include "find.h"
Shinichiro Hamaji087cecd2015-07-06 19:51:58 +090036#include "flags.h"
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +090037#include "func.h"
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +090038#include "io.h"
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090039#include "log.h"
Shinichiro Hamajia8e903f2015-08-06 19:41:01 +090040#include "stats.h"
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090041#include "string_piece.h"
42#include "stringprintf.h"
43#include "strutil.h"
44#include "var.h"
Shinichiro Hamajid821f6d2015-07-14 04:03:27 +090045#include "version.h"
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090046
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +090047static size_t FindCommandLineFlag(StringPiece cmd, StringPiece name) {
48 const size_t found = cmd.find(name);
49 if (found == string::npos || found == 0)
50 return string::npos;
51 return found;
52}
53
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090054static StringPiece FindCommandLineFlagWithArg(StringPiece cmd,
55 StringPiece name) {
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +090056 size_t index = FindCommandLineFlag(cmd, name);
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090057 if (index == string::npos)
58 return StringPiece();
59
60 StringPiece val = TrimLeftSpace(cmd.substr(index + name.size()));
61 index = val.find(name);
62 while (index != string::npos) {
63 val = TrimLeftSpace(val.substr(index + name.size()));
64 index = val.find(name);
65 }
66
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +090067 index = val.find_first_of(" \t");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090068 return val.substr(0, index);
69}
70
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090071static bool StripPrefix(StringPiece p, StringPiece* s) {
72 if (!HasPrefix(*s, p))
73 return false;
74 *s = s->substr(p.size());
75 return true;
76}
77
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090078size_t GetGomaccPosForAndroidCompileCommand(StringPiece cmdline) {
79 size_t index = cmdline.find(' ');
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090080 if (index == string::npos)
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090081 return string::npos;
82 StringPiece cmd = cmdline.substr(0, index);
83 if (HasSuffix(cmd, "ccache")) {
84 index++;
85 size_t pos = GetGomaccPosForAndroidCompileCommand(cmdline.substr(index));
86 return pos == string::npos ? string::npos : pos + index;
87 }
88 if (!StripPrefix("prebuilts/", &cmd))
89 return string::npos;
90 if (!StripPrefix("gcc/", &cmd) && !StripPrefix("clang/", &cmd))
91 return string::npos;
92 if (!HasSuffix(cmd, "gcc") && !HasSuffix(cmd, "g++") &&
93 !HasSuffix(cmd, "clang") && !HasSuffix(cmd, "clang++")) {
94 return string::npos;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090095 }
96
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090097 StringPiece rest = cmdline.substr(index);
98 return rest.find(" -c ") != string::npos ? 0 : string::npos;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090099}
100
101static bool GetDepfileFromCommandImpl(StringPiece cmd, string* out) {
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +0900102 if ((FindCommandLineFlag(cmd, " -MD") == string::npos &&
103 FindCommandLineFlag(cmd, " -MMD") == string::npos) ||
104 FindCommandLineFlag(cmd, " -c") == string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900105 return false;
106 }
107
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +0900108 StringPiece mf = FindCommandLineFlagWithArg(cmd, " -MF");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900109 if (!mf.empty()) {
110 mf.AppendToString(out);
111 return true;
112 }
113
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +0900114 StringPiece o = FindCommandLineFlagWithArg(cmd, " -o");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900115 if (o.empty()) {
116 ERROR("Cannot find the depfile in %s", cmd.as_string().c_str());
117 return false;
118 }
119
120 StripExt(o).AppendToString(out);
121 *out += ".d";
122 return true;
123}
124
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900125bool GetDepfileFromCommand(string* cmd, string* out) {
126 CHECK(!cmd->empty());
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900127 if (!GetDepfileFromCommandImpl(*cmd, out))
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900128 return false;
129
130 // A hack for Android - llvm-rs-cc seems not to emit a dep file.
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900131 if (cmd->find("bin/llvm-rs-cc ") != string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900132 return false;
133 }
134
135 // TODO: A hack for Makefiles generated by automake.
136
137 // A hack for Android to get .P files instead of .d.
138 string p;
139 StripExt(*out).AppendToString(&p);
140 p += ".P";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900141 if (cmd->find(p) != string::npos) {
142 const string rm_f = "; rm -f " + *out;
143 const size_t found = cmd->find(rm_f);
144 if (found == string::npos) {
145 ERROR("Cannot find removal of .d file: %s", cmd->c_str());
146 }
147 cmd->erase(found, rm_f.size());
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900148 return true;
149 }
150
151 // A hack for Android. For .s files, GCC does not use C
152 // preprocessor, so it ignores -MF flag.
153 string as = "/";
154 StripExt(Basename(*out)).AppendToString(&as);
155 as += ".s";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900156 if (cmd->find(as) != string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900157 return false;
158 }
159
Shinichiro Hamajid416e612015-07-18 12:43:34 +0900160 *cmd += "&& cp ";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900161 *cmd += *out;
162 *cmd += ' ';
163 *cmd += *out;
Shinichiro Hamajid416e612015-07-18 12:43:34 +0900164 *cmd += ".tmp ";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900165 *out += ".tmp";
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900166 return true;
167}
168
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900169class NinjaGenerator {
170 public:
Shinichiro Hamajif9869fc2015-09-28 17:18:59 +0900171 NinjaGenerator(Evaluator* ev, double start_time)
Shinichiro Hamaji85e5ed02016-01-20 16:25:32 +0900172 : ce_(ev),
173 ev_(ev),
174 fp_(NULL),
175 rule_id_(0),
176 start_time_(start_time),
Shinichiro Hamaji11347162016-01-20 16:23:22 +0900177 default_target_(NULL) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900178 ev_->set_avoid_io(true);
Colin Crosse890a912015-07-20 13:28:00 -0700179 shell_ = ev->EvalVar(kShellSym);
Shinichiro Hamaji003d06e2015-09-09 18:22:04 +0900180 if (g_flags.goma_dir)
181 gomacc_ = StringPrintf("%s/gomacc ", g_flags.goma_dir);
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900182
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900183 GetExecutablePath(&kati_binary_);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900184 }
185
186 ~NinjaGenerator() {
187 ev_->set_avoid_io(false);
188 }
189
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900190 void Generate(const vector<DepNode*>& nodes,
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900191 const string& orig_args) {
Shinichiro Hamajied8db982016-01-27 06:12:33 +0000192 unlink(GetStampFilename().c_str());
Shinichiro Hamaji9c5e60c2015-09-29 13:26:45 +0900193 GenerateNinja(nodes, orig_args);
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900194 GenerateShell();
Dan Willemsen87417412015-08-24 17:57:12 -0700195 GenerateStamp(orig_args);
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900196 }
197
Shinichiro Hamajied8db982016-01-27 06:12:33 +0000198 static string GetNinjaFilename() {
199 return GetFilename("build%s.ninja");
Colin Cross0850f7d2015-09-14 15:11:45 -0700200 }
201
Shinichiro Hamajied8db982016-01-27 06:12:33 +0000202 static string GetShellScriptFilename() {
203 return GetFilename("ninja%s.sh");
204 }
205
206 static string GetStampFilename() {
207 return GetFilename(".kati_stamp%s");
208 }
209
210 static string GetStampTempFilename() {
211 return GetFilename(".kati_stamp%s.tmp");
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900212 }
213
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900214 private:
215 string GenRuleName() {
216 return StringPrintf("rule%d", rule_id_++);
217 }
218
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900219 StringPiece TranslateCommand(const char* in, string* cmd_buf) {
220 const size_t orig_size = cmd_buf->size();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900221 bool prev_backslash = false;
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900222 // Set space as an initial value so the leading comment will be
223 // stripped out.
224 char prev_char = ' ';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900225 char quote = 0;
Shinichiro Hamajifc14d5f2015-07-28 17:07:57 +0900226 for (; *in; in++) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900227 switch (*in) {
228 case '#':
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900229 if (quote == 0 && isspace(prev_char)) {
Shinichiro Hamajifc14d5f2015-07-28 17:07:57 +0900230 while (in[1] && *in != '\n')
231 in++;
Colin Cross415e4a12015-07-15 18:21:38 -0700232 } else {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900233 *cmd_buf += *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900234 }
Colin Cross415e4a12015-07-15 18:21:38 -0700235 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900236
237 case '\'':
238 case '"':
239 case '`':
240 if (quote) {
241 if (quote == *in)
242 quote = 0;
243 } else if (!prev_backslash) {
244 quote = *in;
245 }
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900246 *cmd_buf += *in;
Shinichiro Hamaji4212e382015-06-29 17:21:04 +0900247 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900248
249 case '$':
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900250 *cmd_buf += "$$";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900251 break;
252
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900253 case '\n':
254 if (prev_backslash) {
Shinichiro Hamajia4dfe752015-07-28 15:54:36 +0900255 cmd_buf->resize(cmd_buf->size()-1);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900256 } else {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900257 *cmd_buf += ' ';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900258 }
Shinichiro Hamaji4d151832015-06-29 18:15:46 +0900259 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900260
261 case '\\':
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900262 *cmd_buf += '\\';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900263 break;
264
265 default:
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900266 *cmd_buf += *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900267 }
Colin Crossf6f1cf42015-07-15 15:25:30 -0700268
269 if (*in == '\\') {
270 prev_backslash = !prev_backslash;
271 } else {
272 prev_backslash = false;
273 }
274
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900275 prev_char = *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900276 }
277
Dan Willemsen45c49cc2015-08-21 12:39:34 -0700278 if (prev_backslash) {
279 cmd_buf->resize(cmd_buf->size()-1);
280 }
281
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900282 while (true) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900283 char c = (*cmd_buf)[cmd_buf->size()-1];
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900284 if (!isspace(c) && c != ';')
285 break;
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900286 cmd_buf->resize(cmd_buf->size() - 1);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900287 }
288
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900289 return StringPiece(cmd_buf->data() + orig_size,
290 cmd_buf->size() - orig_size);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900291 }
292
Colin Cross2e032ba2015-07-15 18:33:37 -0700293 bool GetDescriptionFromCommand(StringPiece cmd, string *out) {
Colin Cross2e032ba2015-07-15 18:33:37 -0700294 if (!HasPrefix(cmd, "echo ")) {
295 return false;
296 }
297 cmd = cmd.substr(5, cmd.size());
298
Colin Cross2e032ba2015-07-15 18:33:37 -0700299 bool prev_backslash = false;
300 char quote = 0;
301 string out_buf;
302
303 // Strip outer quotes, and fail if it is not a single echo command
304 for (StringPiece::iterator in = cmd.begin(); in != cmd.end(); in++) {
305 if (prev_backslash) {
306 prev_backslash = false;
307 out_buf += *in;
308 } else if (*in == '\\') {
309 prev_backslash = true;
310 out_buf += *in;
311 } else if (quote) {
312 if (*in == quote) {
313 quote = 0;
314 } else {
315 out_buf += *in;
316 }
317 } else {
318 switch (*in) {
319 case '\'':
320 case '"':
321 case '`':
322 quote = *in;
323 break;
324
325 case '<':
326 case '>':
327 case '&':
328 case '|':
329 case ';':
330 return false;
331
332 default:
333 out_buf += *in;
334 }
335 }
336 }
337
338 *out = out_buf;
339 return true;
340 }
341
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900342 bool GenShellScript(const vector<Command*>& commands,
343 string* cmd_buf,
344 string* description) {
Shinichiro Hamaji1ce0d142015-11-26 15:21:22 +0900345 // TODO: This is a dirty hack to set local_pool even without
346 // --goma_dir or --remote_num_jobs which are not used in AOSP
347 // anymore. This won't set local_pool for targets which appear
348 // before the first command which uses gomacc. Fortunately, such
349 // command appears soon so almost all build targets have
350 // local_pool appropriately, but it's definitely better to come up
351 // with a more reliable solution.
352 static bool was_gomacc_found = false;
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900353 bool got_descritpion = false;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900354 bool use_gomacc = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900355 bool should_ignore_error = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900356 for (const Command* c : commands) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900357 if (!cmd_buf->empty()) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900358 if (should_ignore_error) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900359 *cmd_buf += " ; ";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900360 } else {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900361 *cmd_buf += " && ";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900362 }
363 }
364 should_ignore_error = c->ignore_error;
365
Shinichiro Hamajifb415ad2015-08-14 17:19:34 +0900366 const char* in = c->cmd.c_str();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900367 while (isspace(*in))
368 in++;
369
370 bool needs_subshell = commands.size() > 1;
371 if (*in == '(') {
372 needs_subshell = false;
373 }
374
375 if (needs_subshell)
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900376 *cmd_buf += '(';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900377
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900378 size_t cmd_start = cmd_buf->size();
379 StringPiece translated = TranslateCommand(in, cmd_buf);
Shinichiro Hamaji003d06e2015-09-09 18:22:04 +0900380 if (g_flags.detect_android_echo && !got_descritpion && !c->echo &&
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900381 GetDescriptionFromCommand(translated, description)) {
382 got_descritpion = true;
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900383 cmd_buf->resize(cmd_start);
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900384 translated.clear();
385 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900386 if (translated.empty()) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900387 *cmd_buf += "true";
Shinichiro Hamaji003d06e2015-09-09 18:22:04 +0900388 } else if (g_flags.goma_dir) {
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +0900389 size_t pos = GetGomaccPosForAndroidCompileCommand(translated);
390 if (pos != string::npos) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900391 cmd_buf->insert(cmd_start + pos, gomacc_);
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +0900392 use_gomacc = true;
393 }
Shinichiro Hamaji4cb8a6f2015-08-17 16:30:49 +0900394 } else if (translated.find("/gomacc") != string::npos) {
395 use_gomacc = true;
Shinichiro Hamaji1ce0d142015-11-26 15:21:22 +0900396 was_gomacc_found = true;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900397 }
398
399 if (c == commands.back() && c->ignore_error) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900400 *cmd_buf += " ; true";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900401 }
402
403 if (needs_subshell)
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900404 *cmd_buf += ')';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900405 }
Shinichiro Hamaji1ce0d142015-11-26 15:21:22 +0900406 return (was_gomacc_found || g_flags.remote_num_jobs ||
407 g_flags.goma_dir) && !use_gomacc;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900408 }
409
Shinichiro Hamaji85e5ed02016-01-20 16:25:32 +0900410 bool GetDepfile(DepNode* node, string* cmd_buf, string* depfile) {
411 if (node->depfile_var) {
412 node->depfile_var->Eval(ev_, depfile);
413 return true;
414 }
415
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900416 *cmd_buf += ' ';
Shinichiro Hamaji85e5ed02016-01-20 16:25:32 +0900417 bool result = GetDepfileFromCommand(cmd_buf, depfile);
Shinichiro Hamaji11347162016-01-20 16:23:22 +0900418 cmd_buf->resize(cmd_buf->size()-1);
Shinichiro Hamaji85e5ed02016-01-20 16:25:32 +0900419 return result;
420 }
421
422 void EmitDepfile(DepNode* node, string* cmd_buf) {
423 string depfile;
424 if (!GetDepfile(node, cmd_buf, &depfile))
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900425 return;
426 fprintf(fp_, " depfile = %s\n", depfile.c_str());
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900427 fprintf(fp_, " deps = gcc\n");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900428 }
429
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900430 void EmitNode(DepNode* node) {
431 auto p = done_.insert(node->output);
432 if (!p.second)
433 return;
434
Shinichiro Hamaji346b20a2015-08-17 14:23:50 +0900435 // A hack to exclude out phony target in Android. If this exists,
436 // "ninja -t clean" tries to remove this directory and fails.
Shinichiro Hamaji003d06e2015-09-09 18:22:04 +0900437 if (g_flags.detect_android_echo && node->output.str() == "out")
Shinichiro Hamaji346b20a2015-08-17 14:23:50 +0900438 return;
439
Dan Willemsenf3a4ced2015-08-25 14:22:35 -0700440 // This node is a leaf node
441 if (!node->has_rule && !node->is_phony) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900442 return;
Shinichiro Hamaji0f39c522015-07-07 13:14:02 +0900443 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900444
445 vector<Command*> commands;
446 ce_.Eval(node, &commands);
447
448 string rule_name = "phony";
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900449 bool use_local_pool = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900450 if (!commands.empty()) {
451 rule_name = GenRuleName();
452 fprintf(fp_, "rule %s\n", rule_name.c_str());
Colin Cross2e032ba2015-07-15 18:33:37 -0700453
454 string description = "build $out";
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900455 string cmd_buf;
456 use_local_pool |= GenShellScript(commands, &cmd_buf, &description);
Colin Cross2e032ba2015-07-15 18:33:37 -0700457 fprintf(fp_, " description = %s\n", description.c_str());
Shinichiro Hamaji85e5ed02016-01-20 16:25:32 +0900458 EmitDepfile(node, &cmd_buf);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900459
Shinichiro Hamajieb0e5f92015-07-17 03:38:23 +0900460 // It seems Linux is OK with ~130kB and Mac's limit is ~250kB.
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900461 // TODO: Find this number automatically.
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900462 if (cmd_buf.size() > 100 * 1000) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900463 fprintf(fp_, " rspfile = $out.rsp\n");
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900464 fprintf(fp_, " rspfile_content = %s\n", cmd_buf.c_str());
Shinichiro Hamajifb415ad2015-08-14 17:19:34 +0900465 fprintf(fp_, " command = %s $out.rsp\n", shell_.c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900466 } else {
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900467 EscapeShell(&cmd_buf);
Shinichiro Hamaji2e04d302015-07-24 09:31:18 +0900468 fprintf(fp_, " command = %s -c \"%s\"\n",
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900469 shell_.c_str(), cmd_buf.c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900470 }
Shinichiro Hamaji3ac2a092015-10-01 18:38:02 +0900471 if (node->is_restat) {
472 fprintf(fp_, " restat = 1\n");
473 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900474 }
475
Colin Cross5b26db32015-09-29 16:51:02 -0700476 EmitBuild(node, rule_name, use_local_pool);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900477
478 for (DepNode* d : node->deps) {
479 EmitNode(d);
480 }
Shinichiro Hamaji3f2cf1e2015-07-06 18:58:18 +0900481 for (DepNode* d : node->order_onlys) {
482 EmitNode(d);
483 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900484 }
485
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900486 string EscapeBuildTarget(Symbol s) const {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900487 if (s.str().find_first_of("$: ") == string::npos)
488 return s.str();
489 string r;
490 for (char c : s.str()) {
491 switch (c) {
492 case '$':
493 case ':':
494 case ' ':
495 r += '$';
496 // fall through.
497 default:
498 r += c;
499 }
500 }
501 return r;
502 }
503
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900504 void EscapeShell(string* s) const {
Shinichiro Hamajic58db992015-10-09 16:12:32 +0900505 if (s->find_first_of("$`\\\"") == string::npos)
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900506 return;
Colin Crosse890a912015-07-20 13:28:00 -0700507 string r;
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900508 bool last_dollar = false;
509 for (char c : *s) {
Colin Crosse890a912015-07-20 13:28:00 -0700510 switch (c) {
511 case '$':
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900512 if (last_dollar) {
Colin Crosse890a912015-07-20 13:28:00 -0700513 r += c;
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900514 last_dollar = false;
Colin Crosse890a912015-07-20 13:28:00 -0700515 } else {
516 r += '\\';
517 r += c;
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900518 last_dollar = true;
Colin Crosse890a912015-07-20 13:28:00 -0700519 }
520 break;
521 case '`':
522 case '"':
Colin Crosse890a912015-07-20 13:28:00 -0700523 case '\\':
524 r += '\\';
525 // fall through.
526 default:
527 r += c;
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900528 last_dollar = false;
Colin Crosse890a912015-07-20 13:28:00 -0700529 }
530 }
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900531 s->swap(r);
Colin Crosse890a912015-07-20 13:28:00 -0700532 }
533
Colin Cross5b26db32015-09-29 16:51:02 -0700534 void EmitBuild(DepNode* node, const string& rule_name, bool use_local_pool) {
535 string target = EscapeBuildTarget(node->output);
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900536 fprintf(fp_, "build %s: %s",
Colin Cross5b26db32015-09-29 16:51:02 -0700537 target.c_str(),
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900538 rule_name.c_str());
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900539 vector<Symbol> order_onlys;
Dan Willemsen3faa60f2015-08-21 13:24:39 -0700540 if (node->is_phony) {
541 fprintf(fp_, " _kati_always_build_");
542 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900543 for (DepNode* d : node->deps) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900544 fprintf(fp_, " %s", EscapeBuildTarget(d->output).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900545 }
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900546 if (!node->order_onlys.empty()) {
Shinichiro Hamajif4820de2015-06-29 17:03:17 +0900547 fprintf(fp_, " ||");
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900548 for (DepNode* d : node->order_onlys) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900549 fprintf(fp_, " %s", EscapeBuildTarget(d->output).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900550 }
551 }
552 fprintf(fp_, "\n");
Colin Cross5b26db32015-09-29 16:51:02 -0700553 if (use_local_pool)
554 fprintf(fp_, " pool = local_pool\n");
555 if (node->is_default_target) {
Shinichiro Hamajia62b02a2015-10-01 14:21:40 +0900556 default_target_ = node;
Colin Cross5b26db32015-09-29 16:51:02 -0700557 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900558 }
559
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900560 void EmitRegenRules(const string& orig_args) {
Shinichiro Hamaji003d06e2015-09-09 18:22:04 +0900561 if (!g_flags.gen_regen_rule)
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900562 return;
563
564 fprintf(fp_, "rule regen_ninja\n");
565 fprintf(fp_, " command = %s\n", orig_args.c_str());
566 fprintf(fp_, " generator = 1\n");
567 fprintf(fp_, " description = Regenerate ninja files due to dependency\n");
568 fprintf(fp_, "build %s: regen_ninja", GetNinjaFilename().c_str());
569 unordered_set<string> makefiles;
570 MakefileCacheManager::Get()->GetAllFilenames(&makefiles);
571 for (const string& makefile : makefiles) {
572 fprintf(fp_, " %.*s", SPF(makefile));
573 }
Shinichiro Hamajib58bb4b2015-07-30 18:02:51 +0900574 fprintf(fp_, " %s", kati_binary_.c_str());
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900575 fprintf(fp_, "\n\n");
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900576 }
577
Shinichiro Hamaji5a5a7f62015-12-12 12:44:00 +0900578 static string GetEnvScriptFilename() {
Colin Cross27df5312015-11-09 13:39:14 -0800579 return GetFilename("env%s.sh");
580 }
581
Shinichiro Hamajied8db982016-01-27 06:12:33 +0000582 static string GetFilename(const char* fmt) {
583 string r = g_flags.ninja_dir ? g_flags.ninja_dir : ".";
584 r += '/';
585 r += StringPrintf(fmt, g_flags.ninja_suffix ? g_flags.ninja_suffix : "");
586 return r;
587 }
588
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900589 void GenerateNinja(const vector<DepNode*>& nodes,
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900590 const string& orig_args) {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900591 fp_ = fopen(GetNinjaFilename().c_str(), "wb");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900592 if (fp_ == NULL)
593 PERROR("fopen(build.ninja) failed");
594
Shinichiro Hamajid821f6d2015-07-14 04:03:27 +0900595 fprintf(fp_, "# Generated by kati %s\n", kGitVersion);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900596 fprintf(fp_, "\n");
597
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900598 if (!used_envs_.empty()) {
Shinichiro Hamaji5163e042015-07-14 03:51:44 +0900599 fprintf(fp_, "# Environment variables used:\n");
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900600 for (const auto& p : used_envs_) {
601 fprintf(fp_, "# %s=%s\n", p.first.c_str(), p.second.c_str());
Shinichiro Hamaji5163e042015-07-14 03:51:44 +0900602 }
603 fprintf(fp_, "\n");
604 }
605
Shinichiro Hamajif9869fc2015-09-28 17:18:59 +0900606 if (g_flags.ninja_dir) {
607 fprintf(fp_, "builddir = %s\n\n", g_flags.ninja_dir);
Dan Willemsen2e762292015-08-19 20:39:41 -0700608 }
609
Shinichiro Hamaji4cb8a6f2015-08-17 16:30:49 +0900610 fprintf(fp_, "pool local_pool\n");
Shinichiro Hamaji003d06e2015-09-09 18:22:04 +0900611 fprintf(fp_, " depth = %d\n\n", g_flags.num_jobs);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900612
Dan Willemsen3faa60f2015-08-21 13:24:39 -0700613 fprintf(fp_, "build _kati_always_build_: phony\n\n");
614
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900615 EmitRegenRules(orig_args);
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900616
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900617 for (DepNode* node : nodes) {
618 EmitNode(node);
619 }
620
Shinichiro Hamaji29e45702015-12-09 15:44:51 +0900621 unordered_set<Symbol> used_env_vars(Vars::used_env_vars());
622 // PATH changes $(shell).
623 used_env_vars.insert(Intern("PATH"));
624 for (Symbol e : used_env_vars) {
Shinichiro Hamajied883ef2015-07-31 13:15:04 +0900625 StringPiece val(getenv(e.c_str()));
626 used_envs_.emplace(e.str(), val.as_string());
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900627 }
628
Shinichiro Hamaji9c5e60c2015-09-29 13:26:45 +0900629 string default_targets;
Shinichiro Hamaji4e3b4212015-11-09 17:08:32 +0900630 if (g_flags.targets.empty() || g_flags.gen_all_targets) {
Shinichiro Hamajia62b02a2015-10-01 14:21:40 +0900631 CHECK(default_target_);
632 default_targets = EscapeBuildTarget(default_target_->output);
Shinichiro Hamaji9c5e60c2015-09-29 13:26:45 +0900633 } else {
634 for (Symbol s : g_flags.targets) {
635 if (!default_targets.empty())
636 default_targets += ' ';
637 default_targets += EscapeBuildTarget(s);
638 }
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900639 }
Shinichiro Hamajia62b02a2015-10-01 14:21:40 +0900640 fprintf(fp_, "\n");
641 fprintf(fp_, "default %s\n", default_targets.c_str());
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900642
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900643 fclose(fp_);
644 }
645
646 void GenerateShell() {
Colin Cross27df5312015-11-09 13:39:14 -0800647 FILE* fp = fopen(GetEnvScriptFilename().c_str(), "wb");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900648 if (fp == NULL)
Colin Cross27df5312015-11-09 13:39:14 -0800649 PERROR("fopen(env.sh) failed");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900650
Shinichiro Hamaji4dd62de2015-07-28 16:06:52 +0900651 fprintf(fp, "#!/bin/sh\n");
Shinichiro Hamajiaf9887a2015-07-17 03:45:14 +0900652 fprintf(fp, "# Generated by kati %s\n", kGitVersion);
653 fprintf(fp, "\n");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900654
655 for (const auto& p : ev_->exports()) {
656 if (p.second) {
Shinichiro Hamajifb415ad2015-08-14 17:19:34 +0900657 const string val = ev_->EvalVar(p.first);
658 fprintf(fp, "export '%s'='%s'\n", p.first.c_str(), val.c_str());
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900659 } else {
Shinichiro Hamajif65e5722015-07-28 16:13:18 +0900660 fprintf(fp, "unset '%s'\n", p.first.c_str());
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900661 }
662 }
663
Colin Cross27df5312015-11-09 13:39:14 -0800664 fclose(fp);
665
Shinichiro Hamajied8db982016-01-27 06:12:33 +0000666 fp = fopen(GetShellScriptFilename().c_str(), "wb");
Colin Cross27df5312015-11-09 13:39:14 -0800667 if (fp == NULL)
668 PERROR("fopen(ninja.sh) failed");
669
670 fprintf(fp, "#!/bin/sh\n");
671 fprintf(fp, "# Generated by kati %s\n", kGitVersion);
672 fprintf(fp, "\n");
673
674 fprintf(fp, ". %s\n", GetEnvScriptFilename().c_str());
675
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900676 fprintf(fp, "exec ninja -f %s ", GetNinjaFilename().c_str());
Shinichiro Hamaji003d06e2015-09-09 18:22:04 +0900677 if (g_flags.remote_num_jobs > 0) {
678 fprintf(fp, "-j%d ", g_flags.remote_num_jobs);
679 } else if (g_flags.goma_dir) {
Shinichiro Hamaji6d7c7b72015-07-18 16:51:39 +0900680 fprintf(fp, "-j500 ");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900681 }
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900682 fprintf(fp, "\"$@\"\n");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900683
Colin Cross27df5312015-11-09 13:39:14 -0800684 fclose(fp);
685
Shinichiro Hamajied8db982016-01-27 06:12:33 +0000686 if (chmod(GetShellScriptFilename().c_str(), 0755) != 0)
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900687 PERROR("chmod ninja.sh failed");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900688 }
689
Dan Willemsen87417412015-08-24 17:57:12 -0700690 void GenerateStamp(const string& orig_args) {
Colin Cross0850f7d2015-09-14 15:11:45 -0700691 FILE* fp = fopen(GetStampTempFilename().c_str(), "wb");
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900692 CHECK(fp);
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900693
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900694 size_t r = fwrite(&start_time_, sizeof(start_time_), 1, fp);
695 CHECK(r == 1);
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900696
697 unordered_set<string> makefiles;
698 MakefileCacheManager::Get()->GetAllFilenames(&makefiles);
699 DumpInt(fp, makefiles.size() + 1);
700 DumpString(fp, kati_binary_);
701 for (const string& makefile : makefiles) {
702 DumpString(fp, makefile);
703 }
704
Shinichiro Hamaji7e708012015-07-31 13:07:34 +0900705 DumpInt(fp, Evaluator::used_undefined_vars().size());
706 for (Symbol v : Evaluator::used_undefined_vars()) {
707 DumpString(fp, v.str());
708 }
709
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900710 DumpInt(fp, used_envs_.size());
711 for (const auto& p : used_envs_) {
712 DumpString(fp, p.first);
713 DumpString(fp, p.second);
714 }
715
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900716 const unordered_map<string, vector<string>*>& globs = GetAllGlobCache();
717 DumpInt(fp, globs.size());
718 for (const auto& p : globs) {
719 DumpString(fp, p.first);
720 const vector<string>& files = *p.second;
Shinichiro Hamajie3c43602015-08-13 13:47:27 +0900721#if 0
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900722 unordered_set<string> dirs;
723 GetReadDirs(p.first, files, &dirs);
724 DumpInt(fp, dirs.size());
725 for (const string& dir : dirs) {
726 DumpString(fp, dir);
727 }
Shinichiro Hamajie3c43602015-08-13 13:47:27 +0900728#endif
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900729 DumpInt(fp, files.size());
730 for (const string& file : files) {
731 DumpString(fp, file);
732 }
733 }
734
Shinichiro Hamaji4db9edc2015-08-13 16:43:02 +0900735 const vector<CommandResult*>& crs = GetShellCommandResults();
736 DumpInt(fp, crs.size());
737 for (CommandResult* cr : crs) {
738 DumpString(fp, cr->cmd);
739 DumpString(fp, cr->result);
740 if (!cr->find.get()) {
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900741 // Always re-run this command.
742 DumpInt(fp, 0);
743 continue;
744 }
745
746 DumpInt(fp, 1);
747
748 vector<string> missing_dirs;
Shinichiro Hamaji4db9edc2015-08-13 16:43:02 +0900749 for (StringPiece fd : cr->find->finddirs) {
750 const string& d = ConcatDir(cr->find->chdir, fd);
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900751 if (!Exists(d))
752 missing_dirs.push_back(d);
753 }
754 DumpInt(fp, missing_dirs.size());
755 for (const string& d : missing_dirs) {
756 DumpString(fp, d);
757 }
758
Shinichiro Hamaji4db9edc2015-08-13 16:43:02 +0900759 DumpInt(fp, cr->find->read_dirs->size());
760 for (StringPiece s : *cr->find->read_dirs) {
761 DumpString(fp, ConcatDir(cr->find->chdir, s));
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900762 }
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +0900763 }
764
Dan Willemsen87417412015-08-24 17:57:12 -0700765 DumpString(fp, orig_args);
766
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900767 fclose(fp);
Colin Cross0850f7d2015-09-14 15:11:45 -0700768
Shinichiro Hamajied8db982016-01-27 06:12:33 +0000769 rename(GetStampTempFilename().c_str(), GetStampFilename().c_str());
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900770 }
771
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900772 CommandEvaluator ce_;
773 Evaluator* ev_;
774 FILE* fp_;
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900775 unordered_set<Symbol> done_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900776 int rule_id_;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900777 string gomacc_;
Shinichiro Hamajifb415ad2015-08-14 17:19:34 +0900778 string shell_;
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900779 map<string, string> used_envs_;
Shinichiro Hamajib58bb4b2015-07-30 18:02:51 +0900780 string kati_binary_;
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900781 double start_time_;
Shinichiro Hamajia62b02a2015-10-01 14:21:40 +0900782 DepNode* default_target_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900783};
784
Shinichiro Hamajif9869fc2015-09-28 17:18:59 +0900785void GenerateNinja(const vector<DepNode*>& nodes,
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900786 Evaluator* ev,
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900787 const string& orig_args,
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900788 double start_time) {
Shinichiro Hamajif9869fc2015-09-28 17:18:59 +0900789 NinjaGenerator ng(ev, start_time);
Shinichiro Hamaji9c5e60c2015-09-29 13:26:45 +0900790 ng.Generate(nodes, orig_args);
Shinichiro Hamaji5f86e1a2015-06-29 14:25:39 +0900791}
Shinichiro Hamajied8db982016-01-27 06:12:33 +0000792
793static bool ShouldIgnoreDirty(StringPiece s) {
794 Pattern pat(g_flags.ignore_dirty_pattern);
795 Pattern nopat(g_flags.no_ignore_dirty_pattern);
796 return pat.Match(s) && !nopat.Match(s);
797}
798
799bool NeedsRegen(double start_time, const string& orig_args) {
800 bool retval = false;
801#define RETURN_TRUE do { \
802 if (g_flags.dump_kati_stamp) \
803 retval = true; \
804 else \
805 return true; \
806 } while (0)
807
808#define LOAD_INT(fp) ({ \
809 int v = LoadInt(fp); \
810 if (v < 0) { \
811 fprintf(stderr, "incomplete kati_stamp, regenerating...\n"); \
812 RETURN_TRUE; \
813 } \
814 v; \
815 })
816
817#define LOAD_STRING(fp, s) ({ \
818 if (!LoadString(fp, s)) { \
819 fprintf(stderr, "incomplete kati_stamp, regenerating...\n"); \
820 RETURN_TRUE; \
821 } \
822 })
823
824 if (!Exists(NinjaGenerator::GetNinjaFilename())) {
825 fprintf(stderr, "%s is missing, regenerating...\n",
826 NinjaGenerator::GetNinjaFilename().c_str());
827 return true;
828 }
829 if (!Exists(NinjaGenerator::GetShellScriptFilename())) {
830 fprintf(stderr, "%s is missing, regenerating...\n",
831 NinjaGenerator::GetShellScriptFilename().c_str());
832 return true;
833 }
834
835 const string& stamp_filename = NinjaGenerator::GetStampFilename();
836 FILE* fp = fopen(stamp_filename.c_str(), "rb+");
837 if (!fp) {
838 if (g_flags.dump_kati_stamp)
839 printf("%s: %s\n", stamp_filename.c_str(), strerror(errno));
840 return true;
841 }
842 ScopedFile sfp(fp);
843
844 double gen_time;
845 size_t r = fread(&gen_time, sizeof(gen_time), 1, fp);
846 if (r != 1) {
847 fprintf(stderr, "incomplete kati_stamp, regenerating...\n");
848 RETURN_TRUE;
849 }
850 if (g_flags.dump_kati_stamp)
851 printf("Generated time: %f\n", gen_time);
852
853 string s, s2;
854 int num_files = LOAD_INT(fp);
855 for (int i = 0; i < num_files; i++) {
856 LOAD_STRING(fp, &s);
857 double ts = GetTimestamp(s);
858 if (gen_time < ts) {
859 if (g_flags.regen_ignoring_kati_binary) {
860 string kati_binary;
861 GetExecutablePath(&kati_binary);
862 if (s == kati_binary) {
863 fprintf(stderr, "%s was modified, ignored.\n", s.c_str());
864 continue;
865 }
866 }
867 if (ShouldIgnoreDirty(s)) {
868 if (g_flags.dump_kati_stamp)
869 printf("file %s: ignored (%f)\n", s.c_str(), ts);
870 continue;
871 }
872 if (g_flags.dump_kati_stamp)
873 printf("file %s: dirty (%f)\n", s.c_str(), ts);
874 else
875 fprintf(stderr, "%s was modified, regenerating...\n", s.c_str());
876 RETURN_TRUE;
877 } else if (g_flags.dump_kati_stamp) {
878 printf("file %s: clean (%f)\n", s.c_str(), ts);
879 }
880 }
881
882 int num_undefineds = LOAD_INT(fp);
883 for (int i = 0; i < num_undefineds; i++) {
884 LOAD_STRING(fp, &s);
885 if (getenv(s.c_str())) {
886 if (g_flags.dump_kati_stamp) {
887 printf("env %s: dirty (unset => %s)\n", s.c_str(), getenv(s.c_str()));
888 } else {
889 fprintf(stderr, "Environment variable %s was set, regenerating...\n",
890 s.c_str());
891 }
892 RETURN_TRUE;
893 } else if (g_flags.dump_kati_stamp) {
894 printf("env %s: clean (unset)\n", s.c_str());
895 }
896 }
897
898 int num_envs = LOAD_INT(fp);
899 for (int i = 0; i < num_envs; i++) {
900 LOAD_STRING(fp, &s);
901 StringPiece val(getenv(s.c_str()));
902 LOAD_STRING(fp, &s2);
903 if (val != s2) {
904 if (g_flags.dump_kati_stamp) {
905 printf("env %s: dirty (%s => %.*s)\n",
906 s.c_str(), s2.c_str(), SPF(val));
907 } else {
908 fprintf(stderr, "Environment variable %s was modified (%s => %.*s), "
909 "regenerating...\n",
910 s.c_str(), s2.c_str(), SPF(val));
911 }
912 RETURN_TRUE;
913 } else if (g_flags.dump_kati_stamp) {
914 printf("env %s: clean (%.*s)\n", s.c_str(), SPF(val));
915 }
916 }
917
918 {
919 int num_globs = LOAD_INT(fp);
920 string pat;
921 for (int i = 0; i < num_globs; i++) {
922 COLLECT_STATS("glob time (regen)");
923 LOAD_STRING(fp, &pat);
924#if 0
925 bool needs_reglob = false;
926 int num_dirs = LOAD_INT(fp);
927 for (int j = 0; j < num_dirs; j++) {
928 LOAD_STRING(fp, &s);
929 // TODO: Handle removed files properly.
930 needs_reglob |= gen_time < GetTimestamp(s);
931 }
932#endif
933 int num_files = LOAD_INT(fp);
934 vector<string>* files;
935 Glob(pat.c_str(), &files);
936 sort(files->begin(), files->end());
937 bool needs_regen = files->size() != static_cast<size_t>(num_files);
938 for (int j = 0; j < num_files; j++) {
939 LOAD_STRING(fp, &s);
940 if (!needs_regen) {
941 if ((*files)[j] != s) {
942 needs_regen = true;
943 break;
944 }
945 }
946 }
947 if (needs_regen) {
948 if (ShouldIgnoreDirty(pat)) {
949 if (g_flags.dump_kati_stamp) {
950 printf("wildcard %s: ignored\n", pat.c_str());
951 }
952 continue;
953 }
954 if (g_flags.dump_kati_stamp) {
955 printf("wildcard %s: dirty\n", pat.c_str());
956 } else {
957 fprintf(stderr, "wildcard(%s) was changed, regenerating...\n",
958 pat.c_str());
959 }
960 RETURN_TRUE;
961 } else if (g_flags.dump_kati_stamp) {
962 printf("wildcard %s: clean\n", pat.c_str());
963 }
964 }
965 }
966
967 int num_crs = LOAD_INT(fp);
968 for (int i = 0; i < num_crs; i++) {
969 string cmd, expected;
970 LOAD_STRING(fp, &cmd);
971 LOAD_STRING(fp, &expected);
972
973 {
974 COLLECT_STATS("stat time (regen)");
975 bool has_condition = LOAD_INT(fp);
976 if (has_condition) {
977 bool should_run_command = false;
978
979 int num_missing_dirs = LOAD_INT(fp);
980 for (int j = 0; j < num_missing_dirs; j++) {
981 LOAD_STRING(fp, &s);
982 should_run_command |= Exists(s);
983 }
984
985 int num_read_dirs = LOAD_INT(fp);
986 for (int j = 0; j < num_read_dirs; j++) {
987 LOAD_STRING(fp, &s);
988 // We assume we rarely do a significant change for the top
989 // directory which affects the results of find command.
990 if (s == "" || s == "." || ShouldIgnoreDirty(s))
991 continue;
992
993 struct stat st;
994 if (lstat(s.c_str(), &st) != 0) {
995 should_run_command = true;
996 continue;
997 }
998 double ts = GetTimestampFromStat(st);
999 if (gen_time < ts) {
1000 should_run_command = true;
1001 continue;
1002 }
1003 if (S_ISLNK(st.st_mode)) {
1004 ts = GetTimestamp(s);
1005 should_run_command |= (ts < 0 || gen_time < ts);
1006 }
1007 }
1008
1009 if (!should_run_command) {
1010 if (g_flags.dump_kati_stamp)
1011 printf("shell %s: clean (no rerun)\n", cmd.c_str());
1012 continue;
1013 }
1014 }
1015 }
1016
1017 FindCommand fc;
1018 if (fc.Parse(cmd) && !fc.chdir.empty() && ShouldIgnoreDirty(fc.chdir)) {
1019 if (g_flags.dump_kati_stamp)
1020 printf("shell %s: ignored\n", cmd.c_str());
1021 continue;
1022 }
1023
1024 {
1025 COLLECT_STATS_WITH_SLOW_REPORT("shell time (regen)", cmd.c_str());
1026 string result;
1027 RunCommand("/bin/sh", cmd, RedirectStderr::DEV_NULL, &result);
1028 FormatForCommandSubstitution(&result);
1029 if (expected != result) {
1030 if (g_flags.dump_kati_stamp) {
1031 printf("shell %s: dirty\n", cmd.c_str());
1032 } else {
1033 fprintf(stderr, "$(shell %s) was changed, regenerating...\n",
1034 cmd.c_str());
1035#if 0
1036 fprintf(stderr, "%s => %s\n",
1037 expected.c_str(), result.c_str());
1038#endif
1039 }
1040 RETURN_TRUE;
1041 } else if (g_flags.dump_kati_stamp) {
1042 printf("shell %s: clean (rerun)\n", cmd.c_str());
1043 }
1044 }
1045 }
1046
1047 LoadString(fp, &s);
1048 if (orig_args != s) {
1049 fprintf(stderr, "arguments changed, regenerating...\n");
1050 RETURN_TRUE;
1051 }
1052
1053 if (!retval) {
1054 if (fseek(fp, 0, SEEK_SET) < 0)
1055 PERROR("fseek");
1056 size_t r = fwrite(&start_time, sizeof(start_time), 1, fp);
1057 CHECK(r == 1);
1058 }
1059
1060 return retval;
1061}