blob: 8c7edb84f6f5a47170cd005eb2c9300ef49e5f88 [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 Hamajia09ed282015-07-31 12:21:54 +090024#include <algorithm>
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +090025#include <map>
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090026#include <memory>
27#include <string>
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +090028#include <unordered_map>
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090029#include <unordered_set>
30
31#include "command.h"
32#include "dep.h"
33#include "eval.h"
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +090034#include "file_cache.h"
Shinichiro Hamajib58bb4b2015-07-30 18:02:51 +090035#include "fileutil.h"
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +090036#include "find.h"
Shinichiro Hamaji087cecd2015-07-06 19:51:58 +090037#include "flags.h"
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +090038#include "func.h"
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +090039#include "io.h"
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090040#include "log.h"
Shinichiro Hamajia8e903f2015-08-06 19:41:01 +090041#include "stats.h"
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090042#include "string_piece.h"
43#include "stringprintf.h"
44#include "strutil.h"
45#include "var.h"
Shinichiro Hamajid821f6d2015-07-14 04:03:27 +090046#include "version.h"
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090047
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +090048static size_t FindCommandLineFlag(StringPiece cmd, StringPiece name) {
49 const size_t found = cmd.find(name);
50 if (found == string::npos || found == 0)
51 return string::npos;
52 return found;
53}
54
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090055static StringPiece FindCommandLineFlagWithArg(StringPiece cmd,
56 StringPiece name) {
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +090057 size_t index = FindCommandLineFlag(cmd, name);
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090058 if (index == string::npos)
59 return StringPiece();
60
61 StringPiece val = TrimLeftSpace(cmd.substr(index + name.size()));
62 index = val.find(name);
63 while (index != string::npos) {
64 val = TrimLeftSpace(val.substr(index + name.size()));
65 index = val.find(name);
66 }
67
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +090068 index = val.find_first_of(" \t");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090069 return val.substr(0, index);
70}
71
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090072static bool StripPrefix(StringPiece p, StringPiece* s) {
73 if (!HasPrefix(*s, p))
74 return false;
75 *s = s->substr(p.size());
76 return true;
77}
78
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090079size_t GetGomaccPosForAndroidCompileCommand(StringPiece cmdline) {
80 size_t index = cmdline.find(' ');
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090081 if (index == string::npos)
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090082 return string::npos;
83 StringPiece cmd = cmdline.substr(0, index);
84 if (HasSuffix(cmd, "ccache")) {
85 index++;
86 size_t pos = GetGomaccPosForAndroidCompileCommand(cmdline.substr(index));
87 return pos == string::npos ? string::npos : pos + index;
88 }
89 if (!StripPrefix("prebuilts/", &cmd))
90 return string::npos;
91 if (!StripPrefix("gcc/", &cmd) && !StripPrefix("clang/", &cmd))
92 return string::npos;
93 if (!HasSuffix(cmd, "gcc") && !HasSuffix(cmd, "g++") &&
94 !HasSuffix(cmd, "clang") && !HasSuffix(cmd, "clang++")) {
95 return string::npos;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090096 }
97
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090098 StringPiece rest = cmdline.substr(index);
99 return rest.find(" -c ") != string::npos ? 0 : string::npos;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900100}
101
102static bool GetDepfileFromCommandImpl(StringPiece cmd, string* out) {
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +0900103 if ((FindCommandLineFlag(cmd, " -MD") == string::npos &&
104 FindCommandLineFlag(cmd, " -MMD") == string::npos) ||
105 FindCommandLineFlag(cmd, " -c") == string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900106 return false;
107 }
108
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +0900109 StringPiece mf = FindCommandLineFlagWithArg(cmd, " -MF");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900110 if (!mf.empty()) {
111 mf.AppendToString(out);
112 return true;
113 }
114
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +0900115 StringPiece o = FindCommandLineFlagWithArg(cmd, " -o");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900116 if (o.empty()) {
117 ERROR("Cannot find the depfile in %s", cmd.as_string().c_str());
118 return false;
119 }
120
121 StripExt(o).AppendToString(out);
122 *out += ".d";
123 return true;
124}
125
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900126bool GetDepfileFromCommand(string* cmd, string* out) {
127 CHECK(!cmd->empty());
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900128 if (!GetDepfileFromCommandImpl(*cmd, out))
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900129 return false;
130
131 // A hack for Android - llvm-rs-cc seems not to emit a dep file.
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900132 if (cmd->find("bin/llvm-rs-cc ") != string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900133 return false;
134 }
135
136 // TODO: A hack for Makefiles generated by automake.
137
138 // A hack for Android to get .P files instead of .d.
139 string p;
140 StripExt(*out).AppendToString(&p);
141 p += ".P";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900142 if (cmd->find(p) != string::npos) {
143 const string rm_f = "; rm -f " + *out;
144 const size_t found = cmd->find(rm_f);
145 if (found == string::npos) {
146 ERROR("Cannot find removal of .d file: %s", cmd->c_str());
147 }
148 cmd->erase(found, rm_f.size());
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900149 return true;
150 }
151
152 // A hack for Android. For .s files, GCC does not use C
153 // preprocessor, so it ignores -MF flag.
154 string as = "/";
155 StripExt(Basename(*out)).AppendToString(&as);
156 as += ".s";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900157 if (cmd->find(as) != string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900158 return false;
159 }
160
Shinichiro Hamajid416e612015-07-18 12:43:34 +0900161 *cmd += "&& cp ";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900162 *cmd += *out;
163 *cmd += ' ';
164 *cmd += *out;
Shinichiro Hamajid416e612015-07-18 12:43:34 +0900165 *cmd += ".tmp ";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900166 *out += ".tmp";
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900167 return true;
168}
169
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900170class NinjaGenerator {
171 public:
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900172 NinjaGenerator(const char* ninja_suffix, const char* ninja_dir, Evaluator* ev,
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900173 double start_time)
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900174 : ce_(ev), ev_(ev), fp_(NULL), rule_id_(0), start_time_(start_time) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900175 ev_->set_avoid_io(true);
Colin Crosse890a912015-07-20 13:28:00 -0700176 shell_ = ev->EvalVar(kShellSym);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900177 if (g_goma_dir)
178 gomacc_ = StringPrintf("%s/gomacc ", g_goma_dir);
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900179 if (ninja_suffix) {
180 ninja_suffix_ = ninja_suffix;
181 }
Colin Crossbb2bba12015-07-20 16:31:18 -0700182 if (ninja_dir) {
183 ninja_dir_ = ninja_dir;
184 } else {
185 ninja_dir_ = ".";
186 }
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900187
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900188 GetExecutablePath(&kati_binary_);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900189 }
190
191 ~NinjaGenerator() {
192 ev_->set_avoid_io(false);
193 }
194
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900195 void Generate(const vector<DepNode*>& nodes,
196 bool build_all_targets,
197 const string& orig_args) {
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900198 GenerateEnvlist();
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900199 GenerateNinja(nodes, build_all_targets, orig_args);
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900200 GenerateShell();
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900201 GenerateStamp();
202 }
203
204 static string GetStampFilename(const char* ninja_dir,
205 const char* ninja_suffix) {
206 return StringPrintf("%s/.kati_stamp%s",
207 ninja_dir ? ninja_dir : ".",
208 ninja_suffix ? ninja_suffix : "");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900209 }
210
211 private:
212 string GenRuleName() {
213 return StringPrintf("rule%d", rule_id_++);
214 }
215
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900216 StringPiece TranslateCommand(const char* in, string* cmd_buf) {
217 const size_t orig_size = cmd_buf->size();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900218 bool prev_backslash = false;
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900219 // Set space as an initial value so the leading comment will be
220 // stripped out.
221 char prev_char = ' ';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900222 char quote = 0;
Shinichiro Hamajifc14d5f2015-07-28 17:07:57 +0900223 for (; *in; in++) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900224 switch (*in) {
225 case '#':
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900226 if (quote == 0 && isspace(prev_char)) {
Shinichiro Hamajifc14d5f2015-07-28 17:07:57 +0900227 while (in[1] && *in != '\n')
228 in++;
Colin Cross415e4a12015-07-15 18:21:38 -0700229 } else {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900230 *cmd_buf += *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900231 }
Colin Cross415e4a12015-07-15 18:21:38 -0700232 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900233
234 case '\'':
235 case '"':
236 case '`':
237 if (quote) {
238 if (quote == *in)
239 quote = 0;
240 } else if (!prev_backslash) {
241 quote = *in;
242 }
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900243 *cmd_buf += *in;
Shinichiro Hamaji4212e382015-06-29 17:21:04 +0900244 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900245
246 case '$':
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900247 *cmd_buf += "$$";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900248 break;
249
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900250 case '\n':
251 if (prev_backslash) {
Shinichiro Hamajia4dfe752015-07-28 15:54:36 +0900252 cmd_buf->resize(cmd_buf->size()-1);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900253 } else {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900254 *cmd_buf += ' ';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900255 }
Shinichiro Hamaji4d151832015-06-29 18:15:46 +0900256 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900257
258 case '\\':
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900259 *cmd_buf += '\\';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900260 break;
261
262 default:
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900263 *cmd_buf += *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900264 }
Colin Crossf6f1cf42015-07-15 15:25:30 -0700265
266 if (*in == '\\') {
267 prev_backslash = !prev_backslash;
268 } else {
269 prev_backslash = false;
270 }
271
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900272 prev_char = *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900273 }
274
275 while (true) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900276 char c = (*cmd_buf)[cmd_buf->size()-1];
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900277 if (!isspace(c) && c != ';')
278 break;
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900279 cmd_buf->resize(cmd_buf->size() - 1);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900280 }
281
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900282 return StringPiece(cmd_buf->data() + orig_size,
283 cmd_buf->size() - orig_size);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900284 }
285
Colin Cross2e032ba2015-07-15 18:33:37 -0700286 bool GetDescriptionFromCommand(StringPiece cmd, string *out) {
Colin Cross2e032ba2015-07-15 18:33:37 -0700287 if (!HasPrefix(cmd, "echo ")) {
288 return false;
289 }
290 cmd = cmd.substr(5, cmd.size());
291
Colin Cross2e032ba2015-07-15 18:33:37 -0700292 bool prev_backslash = false;
293 char quote = 0;
294 string out_buf;
295
296 // Strip outer quotes, and fail if it is not a single echo command
297 for (StringPiece::iterator in = cmd.begin(); in != cmd.end(); in++) {
298 if (prev_backslash) {
299 prev_backslash = false;
300 out_buf += *in;
301 } else if (*in == '\\') {
302 prev_backslash = true;
303 out_buf += *in;
304 } else if (quote) {
305 if (*in == quote) {
306 quote = 0;
307 } else {
308 out_buf += *in;
309 }
310 } else {
311 switch (*in) {
312 case '\'':
313 case '"':
314 case '`':
315 quote = *in;
316 break;
317
318 case '<':
319 case '>':
320 case '&':
321 case '|':
322 case ';':
323 return false;
324
325 default:
326 out_buf += *in;
327 }
328 }
329 }
330
331 *out = out_buf;
332 return true;
333 }
334
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900335 bool GenShellScript(const vector<Command*>& commands,
336 string* cmd_buf,
337 string* description) {
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900338 bool got_descritpion = false;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900339 bool use_gomacc = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900340 bool should_ignore_error = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900341 for (const Command* c : commands) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900342 if (!cmd_buf->empty()) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900343 if (should_ignore_error) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900344 *cmd_buf += " ; ";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900345 } else {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900346 *cmd_buf += " && ";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900347 }
348 }
349 should_ignore_error = c->ignore_error;
350
351 const char* in = c->cmd->c_str();
352 while (isspace(*in))
353 in++;
354
355 bool needs_subshell = commands.size() > 1;
356 if (*in == '(') {
357 needs_subshell = false;
358 }
359
360 if (needs_subshell)
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900361 *cmd_buf += '(';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900362
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900363 size_t cmd_start = cmd_buf->size();
364 StringPiece translated = TranslateCommand(in, cmd_buf);
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900365 if (g_detect_android_echo && !got_descritpion && !c->echo &&
366 GetDescriptionFromCommand(translated, description)) {
367 got_descritpion = true;
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900368 cmd_buf->resize(cmd_start);
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900369 translated.clear();
370 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900371 if (translated.empty()) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900372 *cmd_buf += "true";
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +0900373 } else if (g_goma_dir) {
374 size_t pos = GetGomaccPosForAndroidCompileCommand(translated);
375 if (pos != string::npos) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900376 cmd_buf->insert(cmd_start + pos, gomacc_);
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +0900377 use_gomacc = true;
378 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900379 }
380
381 if (c == commands.back() && c->ignore_error) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900382 *cmd_buf += " ; true";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900383 }
384
385 if (needs_subshell)
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900386 *cmd_buf += ')';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900387 }
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900388 return g_goma_dir && !use_gomacc;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900389 }
390
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900391 void EmitDepfile(string* cmd_buf) {
392 *cmd_buf += ' ';
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900393 string depfile;
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900394 bool result = GetDepfileFromCommand(cmd_buf, &depfile);
395 cmd_buf->resize(cmd_buf->size()-1);
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900396 if (!result)
397 return;
398 fprintf(fp_, " depfile = %s\n", depfile.c_str());
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900399 fprintf(fp_, " deps = gcc\n");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900400 }
401
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900402 void EmitNode(DepNode* node) {
403 auto p = done_.insert(node->output);
404 if (!p.second)
405 return;
406
Shinichiro Hamaji90216592015-07-28 14:36:31 +0900407 // Removing this will fix auto_vars.mk, build_once.mk, and
408 // command_vars.mk. However, this change will make
409 // ninja_normalized_path2.mk fail and cause a lot of warnings for
410 // Android build.
Shinichiro Hamaji0f39c522015-07-07 13:14:02 +0900411 if (node->cmds.empty() &&
412 node->deps.empty() && node->order_onlys.empty() && !node->is_phony) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900413 return;
Shinichiro Hamaji0f39c522015-07-07 13:14:02 +0900414 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900415
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900416 StringPiece base = Basename(node->output.str());
417 if (base != node->output.str()) {
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900418 auto p = short_names_.emplace(Intern(base), node->output);
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900419 if (!p.second) {
420 // We generate shortcuts only for targets whose basename are unique.
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900421 p.first->second = kEmptySym;
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900422 }
423 }
424
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900425 vector<Command*> commands;
426 ce_.Eval(node, &commands);
427
428 string rule_name = "phony";
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900429 bool use_local_pool = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900430 if (!commands.empty()) {
431 rule_name = GenRuleName();
432 fprintf(fp_, "rule %s\n", rule_name.c_str());
Colin Cross2e032ba2015-07-15 18:33:37 -0700433
434 string description = "build $out";
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900435 string cmd_buf;
436 use_local_pool |= GenShellScript(commands, &cmd_buf, &description);
Colin Cross2e032ba2015-07-15 18:33:37 -0700437 fprintf(fp_, " description = %s\n", description.c_str());
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900438 EmitDepfile(&cmd_buf);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900439
Shinichiro Hamajieb0e5f92015-07-17 03:38:23 +0900440 // It seems Linux is OK with ~130kB and Mac's limit is ~250kB.
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900441 // TODO: Find this number automatically.
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900442 if (cmd_buf.size() > 100 * 1000) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900443 fprintf(fp_, " rspfile = $out.rsp\n");
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900444 fprintf(fp_, " rspfile_content = %s\n", cmd_buf.c_str());
Colin Crosse890a912015-07-20 13:28:00 -0700445 fprintf(fp_, " command = %s $out.rsp\n", shell_->c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900446 } else {
Shinichiro Hamaji2e04d302015-07-24 09:31:18 +0900447 fprintf(fp_, " command = %s -c \"%s\"\n",
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900448 shell_->c_str(), EscapeShell(cmd_buf).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900449 }
450 }
451
452 EmitBuild(node, rule_name);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900453 if (use_local_pool)
454 fprintf(fp_, " pool = local_pool\n");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900455
456 for (DepNode* d : node->deps) {
457 EmitNode(d);
458 }
Shinichiro Hamaji3f2cf1e2015-07-06 18:58:18 +0900459 for (DepNode* d : node->order_onlys) {
460 EmitNode(d);
461 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900462 }
463
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900464 string EscapeBuildTarget(Symbol s) {
465 if (s.str().find_first_of("$: ") == string::npos)
466 return s.str();
467 string r;
468 for (char c : s.str()) {
469 switch (c) {
470 case '$':
471 case ':':
472 case ' ':
473 r += '$';
474 // fall through.
475 default:
476 r += c;
477 }
478 }
479 return r;
480 }
481
Colin Crosse890a912015-07-20 13:28:00 -0700482 string EscapeShell(string s) {
483 if (s.find_first_of("$`!\\\"") == string::npos)
484 return s;
485 string r;
486 bool lastDollar = false;
487 for (char c : s) {
488 switch (c) {
489 case '$':
490 if (lastDollar) {
491 r += c;
492 lastDollar = false;
493 } else {
494 r += '\\';
495 r += c;
496 lastDollar = true;
497 }
498 break;
499 case '`':
500 case '"':
501 case '!':
502 case '\\':
503 r += '\\';
504 // fall through.
505 default:
506 r += c;
507 lastDollar = false;
508 }
509 }
510 return r;
511 }
512
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900513 void EmitBuild(DepNode* node, const string& rule_name) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900514 fprintf(fp_, "build %s: %s",
515 EscapeBuildTarget(node->output).c_str(),
516 rule_name.c_str());
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900517 vector<Symbol> order_onlys;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900518 for (DepNode* d : node->deps) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900519 fprintf(fp_, " %s", EscapeBuildTarget(d->output).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900520 }
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900521 if (!node->order_onlys.empty()) {
Shinichiro Hamajif4820de2015-06-29 17:03:17 +0900522 fprintf(fp_, " ||");
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900523 for (DepNode* d : node->order_onlys) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900524 fprintf(fp_, " %s", EscapeBuildTarget(d->output).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900525 }
526 }
527 fprintf(fp_, "\n");
528 }
529
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900530 void EmitRegenRules(const string& orig_args) {
531 if (!g_gen_regen_rule)
532 return;
533
534 fprintf(fp_, "rule regen_ninja\n");
535 fprintf(fp_, " command = %s\n", orig_args.c_str());
536 fprintf(fp_, " generator = 1\n");
537 fprintf(fp_, " description = Regenerate ninja files due to dependency\n");
538 fprintf(fp_, "build %s: regen_ninja", GetNinjaFilename().c_str());
539 unordered_set<string> makefiles;
540 MakefileCacheManager::Get()->GetAllFilenames(&makefiles);
541 for (const string& makefile : makefiles) {
542 fprintf(fp_, " %.*s", SPF(makefile));
543 }
Shinichiro Hamajib58bb4b2015-07-30 18:02:51 +0900544 fprintf(fp_, " %s", kati_binary_.c_str());
Shinichiro Hamaji3ed61482015-07-18 14:16:32 +0900545 // TODO: Add dependencies to directories read by $(wildcard)
546 // or $(shell find).
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900547 if (!used_envs_.empty())
548 fprintf(fp_, " %s", GetEnvlistFilename().c_str());
549 fprintf(fp_, "\n\n");
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900550 }
551
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900552 string GetNinjaFilename() const {
Shinichiro Hamaji2e04d302015-07-24 09:31:18 +0900553 return StringPrintf("%s/build%s.ninja",
554 ninja_dir_.c_str(), ninja_suffix_.c_str());
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900555 }
556
557 string GetShellScriptFilename() const {
Shinichiro Hamaji2e04d302015-07-24 09:31:18 +0900558 return StringPrintf("%s/ninja%s.sh",
559 ninja_dir_.c_str(), ninja_suffix_.c_str());
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900560 }
561
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900562 string GetEnvlistFilename() const {
Shinichiro Hamaji41957e02015-07-24 10:27:14 +0900563 return StringPrintf("%s/.kati_env%s",
564 ninja_dir_.c_str(), ninja_suffix_.c_str());
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900565 }
566
Shinichiro Hamajid556e622015-07-18 15:33:58 +0900567 string GetLunchFilename() const {
Shinichiro Hamaji41957e02015-07-24 10:27:14 +0900568 return StringPrintf("%s/.kati_lunch%s",
569 ninja_dir_.c_str(), ninja_suffix_.c_str());
Shinichiro Hamajid556e622015-07-18 15:33:58 +0900570 }
571
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900572 string GetStampFilename() const {
573 return GetStampFilename(ninja_dir_.c_str(), ninja_suffix_.c_str());
574 }
575
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900576 void GenerateNinja(const vector<DepNode*>& nodes,
577 bool build_all_targets,
578 const string& orig_args) {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900579 fp_ = fopen(GetNinjaFilename().c_str(), "wb");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900580 if (fp_ == NULL)
581 PERROR("fopen(build.ninja) failed");
582
Shinichiro Hamajid821f6d2015-07-14 04:03:27 +0900583 fprintf(fp_, "# Generated by kati %s\n", kGitVersion);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900584 fprintf(fp_, "\n");
585
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900586 if (!used_envs_.empty()) {
Shinichiro Hamaji5163e042015-07-14 03:51:44 +0900587 fprintf(fp_, "# Environment variables used:\n");
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900588 for (const auto& p : used_envs_) {
589 fprintf(fp_, "# %s=%s\n", p.first.c_str(), p.second.c_str());
Shinichiro Hamaji5163e042015-07-14 03:51:44 +0900590 }
591 fprintf(fp_, "\n");
592 }
593
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900594 if (g_goma_dir) {
595 fprintf(fp_, "pool local_pool\n");
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900596 fprintf(fp_, " depth = %d\n\n", g_num_jobs);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900597 }
598
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900599 EmitRegenRules(orig_args);
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900600
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900601 for (DepNode* node : nodes) {
602 EmitNode(node);
603 }
604
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900605 for (Symbol e : Vars::used_env_vars()) {
Shinichiro Hamajied883ef2015-07-31 13:15:04 +0900606 StringPiece val(getenv(e.c_str()));
607 used_envs_.emplace(e.str(), val.as_string());
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900608 }
609
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900610 if (!build_all_targets) {
611 CHECK(!nodes.empty());
Shinichiro Hamaji865597b2015-07-28 16:16:58 +0900612 fprintf(fp_, "\ndefault %s\n",
613 EscapeBuildTarget(nodes.front()->output).c_str());
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900614 }
615
Shinichiro Hamaji15fe11d2015-07-11 06:32:37 +0900616 fprintf(fp_, "\n# shortcuts:\n");
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900617 for (auto p : short_names_) {
Shinichiro Hamaji10bb4ab2015-07-16 06:04:48 +0900618 if (!p.second.empty() && !done_.count(p.first))
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900619 fprintf(fp_, "build %s: phony %s\n", p.first.c_str(), p.second.c_str());
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900620 }
621
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900622 fclose(fp_);
623 }
624
625 void GenerateShell() {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900626 FILE* fp = fopen(GetShellScriptFilename().c_str(), "wb");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900627 if (fp == NULL)
628 PERROR("fopen(ninja.sh) failed");
629
Shinichiro Hamaji4dd62de2015-07-28 16:06:52 +0900630 fprintf(fp, "#!/bin/sh\n");
Shinichiro Hamajiaf9887a2015-07-17 03:45:14 +0900631 fprintf(fp, "# Generated by kati %s\n", kGitVersion);
632 fprintf(fp, "\n");
Shinichiro Hamajic7629e02015-07-22 02:41:57 +0900633 if (ninja_dir_ == ".")
634 fprintf(fp, "cd $(dirname \"$0\")\n");
Shinichiro Hamajid556e622015-07-18 15:33:58 +0900635 if (!ninja_suffix_.empty()) {
636 fprintf(fp, "if [ -f %s ]; then\n export $(cat %s)\nfi\n",
637 GetEnvlistFilename().c_str(), GetEnvlistFilename().c_str());
638 fprintf(fp, "if [ -f %s ]; then\n export $(cat %s)\nfi\n",
639 GetLunchFilename().c_str(), GetLunchFilename().c_str());
640 }
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900641
642 for (const auto& p : ev_->exports()) {
643 if (p.second) {
Shinichiro Hamaji94d6f2a2015-07-05 05:32:25 +0900644 shared_ptr<string> val = ev_->EvalVar(p.first);
Shinichiro Hamajif65e5722015-07-28 16:13:18 +0900645 fprintf(fp, "export '%s'='%s'\n", p.first.c_str(), val->c_str());
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900646 } else {
Shinichiro Hamajif65e5722015-07-28 16:13:18 +0900647 fprintf(fp, "unset '%s'\n", p.first.c_str());
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900648 }
649 }
650
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900651 fprintf(fp, "exec ninja -f %s ", GetNinjaFilename().c_str());
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900652 if (g_goma_dir) {
Shinichiro Hamaji6d7c7b72015-07-18 16:51:39 +0900653 fprintf(fp, "-j500 ");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900654 }
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900655 fprintf(fp, "\"$@\"\n");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900656
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900657 if (chmod(GetShellScriptFilename().c_str(), 0755) != 0)
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900658 PERROR("chmod ninja.sh failed");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900659 }
660
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900661 void GenerateEnvlist() {
662 if (used_envs_.empty())
663 return;
664 FILE* fp = fopen(GetEnvlistFilename().c_str(), "wb");
665 for (const auto& p : used_envs_) {
666 fprintf(fp, "%s=%s\n", p.first.c_str(), p.second.c_str());
667 }
668 fclose(fp);
669 }
670
Shinichiro Hamajie3c43602015-08-13 13:47:27 +0900671#if 0
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900672 void GetCommonPrefixDir(const vector<string>& files, string* o) {
673 for (const string& file : files) {
674 size_t l = min(file.size(), o->size());
675 for (size_t i = 0; i < l; i++) {
676 if (file[i] != (*o)[i]) {
677 size_t index = o->rfind('/', i);
678 if (index == string::npos)
679 index = 0;
680 o->resize(index);
681 break;
682 }
683 }
684 }
685 }
686
687 void GetReadDirs(const string& pat,
688 const vector<string>& files,
689 unordered_set<string>* dirs) {
690 string prefix_dir = Dirname(pat).as_string();
691 size_t index = prefix_dir.find_first_of("?*[\\");
692 if (index != string::npos) {
693 index = prefix_dir.rfind('/', index);
694 if (index == string::npos) {
695 prefix_dir = "";
696 } else {
697 prefix_dir = prefix_dir.substr(0, index);
698 }
699 }
700
701 GetCommonPrefixDir(files, &prefix_dir);
702 if (prefix_dir.empty())
703 prefix_dir = ".";
704 dirs->insert(prefix_dir);
705 for (const string& file : files) {
706 StringPiece dir = Dirname(file);
707 while (dir != prefix_dir) {
708 dirs->insert(dir.as_string());
709 dir = Dirname(dir);
710 }
711 }
712 }
Shinichiro Hamajie3c43602015-08-13 13:47:27 +0900713#endif
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900714
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900715 void GenerateStamp() {
716 FILE* fp = fopen(GetStampFilename().c_str(), "wb");
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900717 CHECK(fp);
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900718
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900719 size_t r = fwrite(&start_time_, sizeof(start_time_), 1, fp);
720 CHECK(r == 1);
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900721
722 unordered_set<string> makefiles;
723 MakefileCacheManager::Get()->GetAllFilenames(&makefiles);
724 DumpInt(fp, makefiles.size() + 1);
725 DumpString(fp, kati_binary_);
726 for (const string& makefile : makefiles) {
727 DumpString(fp, makefile);
728 }
729
Shinichiro Hamaji7e708012015-07-31 13:07:34 +0900730 DumpInt(fp, Evaluator::used_undefined_vars().size());
731 for (Symbol v : Evaluator::used_undefined_vars()) {
732 DumpString(fp, v.str());
733 }
734
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900735 DumpInt(fp, used_envs_.size());
736 for (const auto& p : used_envs_) {
737 DumpString(fp, p.first);
738 DumpString(fp, p.second);
739 }
740
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900741 const unordered_map<string, vector<string>*>& globs = GetAllGlobCache();
742 DumpInt(fp, globs.size());
743 for (const auto& p : globs) {
744 DumpString(fp, p.first);
745 const vector<string>& files = *p.second;
Shinichiro Hamajie3c43602015-08-13 13:47:27 +0900746#if 0
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900747 unordered_set<string> dirs;
748 GetReadDirs(p.first, files, &dirs);
749 DumpInt(fp, dirs.size());
750 for (const string& dir : dirs) {
751 DumpString(fp, dir);
752 }
Shinichiro Hamajie3c43602015-08-13 13:47:27 +0900753#endif
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900754 DumpInt(fp, files.size());
755 for (const string& file : files) {
756 DumpString(fp, file);
757 }
758 }
759
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +0900760 const vector<FileListCommand*>& flcs = GetFileListCommmands();
761 DumpInt(fp, flcs.size());
762 for (FileListCommand* flc : flcs) {
763 DumpString(fp, flc->cmd);
764 DumpString(fp, flc->result);
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900765 if (!flc->find.get()) {
766 // Always re-run this command.
767 DumpInt(fp, 0);
768 continue;
769 }
770
771 DumpInt(fp, 1);
772
773 vector<string> missing_dirs;
774 for (StringPiece fd : flc->find->finddirs) {
775 const string& d = ConcatDir(flc->find->chdir, fd);
776 if (!Exists(d))
777 missing_dirs.push_back(d);
778 }
779 DumpInt(fp, missing_dirs.size());
780 for (const string& d : missing_dirs) {
781 DumpString(fp, d);
782 }
783
784 DumpInt(fp, flc->find->read_dirs->size());
785 for (StringPiece s : *flc->find->read_dirs) {
Shinichiro Hamajiede1f2f2015-08-07 14:24:44 +0900786 DumpString(fp, ConcatDir(flc->find->chdir, s));
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900787 }
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +0900788 }
789
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900790 fclose(fp);
791 }
792
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900793 CommandEvaluator ce_;
794 Evaluator* ev_;
795 FILE* fp_;
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900796 unordered_set<Symbol> done_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900797 int rule_id_;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900798 string gomacc_;
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900799 string ninja_suffix_;
Colin Crossbb2bba12015-07-20 16:31:18 -0700800 string ninja_dir_;
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900801 unordered_map<Symbol, Symbol> short_names_;
Colin Crosse890a912015-07-20 13:28:00 -0700802 shared_ptr<string> shell_;
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900803 map<string, string> used_envs_;
Shinichiro Hamajib58bb4b2015-07-30 18:02:51 +0900804 string kati_binary_;
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900805 double start_time_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900806};
807
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900808void GenerateNinja(const char* ninja_suffix,
Colin Crossbb2bba12015-07-20 16:31:18 -0700809 const char* ninja_dir,
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900810 const vector<DepNode*>& nodes,
811 Evaluator* ev,
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900812 bool build_all_targets,
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900813 const string& orig_args,
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900814 double start_time) {
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900815 NinjaGenerator ng(ninja_suffix, ninja_dir, ev, start_time);
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900816 ng.Generate(nodes, build_all_targets, orig_args);
Shinichiro Hamaji5f86e1a2015-06-29 14:25:39 +0900817}
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900818
Shinichiro Hamajie67d54e2015-08-13 14:39:11 +0900819static bool ShouldIgnoreDirty(StringPiece s) {
820 return (g_ignore_dirty_pattern &&
821 Pattern(g_ignore_dirty_pattern).Match(s));
822}
823
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900824bool NeedsRegen(const char* ninja_suffix,
Shinichiro Hamaji81958cb2015-08-06 19:36:55 +0900825 const char* ninja_dir,
Shinichiro Hamajif6df3342015-08-11 14:59:36 +0900826 bool ignore_kati_binary,
827 bool dump_kati_stamp) {
828 bool retval = false;
829#define RETURN_TRUE do { \
830 if (dump_kati_stamp) \
831 retval = true; \
832 else \
833 return true; \
834 } while (0)
835
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900836 const string& stamp_filename =
837 NinjaGenerator::GetStampFilename(ninja_suffix, ninja_dir);
838 FILE* fp = fopen(stamp_filename.c_str(), "rb");
839 if (!fp)
Shinichiro Hamajif6df3342015-08-11 14:59:36 +0900840 RETURN_TRUE;
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900841 ScopedFile sfp(fp);
842
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900843 double gen_time;
844 size_t r = fread(&gen_time, sizeof(gen_time), 1, fp);
845 CHECK(r == 1);
Shinichiro Hamajif6df3342015-08-11 14:59:36 +0900846 if (dump_kati_stamp)
847 printf("Generated time: %f\n", gen_time);
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900848
849 string s, s2;
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900850 int num_files = LoadInt(fp);
851 for (int i = 0; i < num_files; i++) {
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900852 LoadString(fp, &s);
Shinichiro Hamajif6df3342015-08-11 14:59:36 +0900853 double ts = GetTimestamp(s);
854 if (gen_time < ts) {
Shinichiro Hamaji81958cb2015-08-06 19:36:55 +0900855 if (ignore_kati_binary) {
856 string kati_binary;
857 GetExecutablePath(&kati_binary);
858 if (s == kati_binary) {
859 fprintf(stderr, "%s was modified, ignored.\n", s.c_str());
860 continue;
861 }
862 }
Shinichiro Hamajie67d54e2015-08-13 14:39:11 +0900863 if (ShouldIgnoreDirty(s)) {
864 if (dump_kati_stamp)
865 printf("file %s: ignored (%f)\n", s.c_str(), ts);
866 continue;
867 }
Shinichiro Hamajif6df3342015-08-11 14:59:36 +0900868 if (dump_kati_stamp)
869 printf("file %s: dirty (%f)\n", s.c_str(), ts);
870 else
871 fprintf(stderr, "%s was modified, regenerating...\n", s.c_str());
872 RETURN_TRUE;
873 } else if (dump_kati_stamp) {
874 printf("file %s: clean (%f)\n", s.c_str(), ts);
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900875 }
876 }
877
Shinichiro Hamaji7e708012015-07-31 13:07:34 +0900878 int num_undefineds = LoadInt(fp);
879 for (int i = 0; i < num_undefineds; i++) {
880 LoadString(fp, &s);
881 if (getenv(s.c_str())) {
Shinichiro Hamajif6df3342015-08-11 14:59:36 +0900882 if (dump_kati_stamp) {
883 printf("env %s: dirty (unset => %s)\n", s.c_str(), getenv(s.c_str()));
884 } else {
885 fprintf(stderr, "Environment variable %s was set, regenerating...\n",
886 s.c_str());
887 }
888 RETURN_TRUE;
889 } else if (dump_kati_stamp) {
890 printf("env %s: clean (unset)\n", s.c_str());
Shinichiro Hamaji7e708012015-07-31 13:07:34 +0900891 }
892 }
893
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900894 int num_envs = LoadInt(fp);
895 for (int i = 0; i < num_envs; i++) {
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900896 LoadString(fp, &s);
897 StringPiece val(getenv(s.c_str()));
898 LoadString(fp, &s2);
899 if (val != s2) {
Shinichiro Hamajif6df3342015-08-11 14:59:36 +0900900 if (dump_kati_stamp) {
Shinichiro Hamajiad62e172015-08-13 13:54:24 +0900901 printf("env %s: dirty (%s => %.*s)\n",
Shinichiro Hamajif6df3342015-08-11 14:59:36 +0900902 s.c_str(), s2.c_str(), SPF(val));
903 } else {
904 fprintf(stderr, "Environment variable %s was modified (%s => %.*s), "
905 "regenerating...\n",
906 s.c_str(), s2.c_str(), SPF(val));
907 }
908 RETURN_TRUE;
909 } else if (dump_kati_stamp) {
910 printf("env %s: clean (%.*s)\n", s.c_str(), SPF(val));
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900911 }
912 }
913
Shinichiro Hamajia8e903f2015-08-06 19:41:01 +0900914 {
Shinichiro Hamajia8e903f2015-08-06 19:41:01 +0900915 int num_globs = LoadInt(fp);
916 string pat;
917 for (int i = 0; i < num_globs; i++) {
Shinichiro Hamajie3c43602015-08-13 13:47:27 +0900918 COLLECT_STATS("glob time (regen)");
Shinichiro Hamajia8e903f2015-08-06 19:41:01 +0900919 LoadString(fp, &pat);
Shinichiro Hamajie3c43602015-08-13 13:47:27 +0900920#if 0
Shinichiro Hamajia8e903f2015-08-06 19:41:01 +0900921 bool needs_reglob = false;
922 int num_dirs = LoadInt(fp);
923 for (int j = 0; j < num_dirs; j++) {
924 LoadString(fp, &s);
925 // TODO: Handle removed files properly.
926 needs_reglob |= gen_time < GetTimestamp(s);
927 }
Shinichiro Hamajie3c43602015-08-13 13:47:27 +0900928#endif
Shinichiro Hamajia8e903f2015-08-06 19:41:01 +0900929 int num_files = LoadInt(fp);
Shinichiro Hamajie3c43602015-08-13 13:47:27 +0900930 vector<string>* files;
931 Glob(pat.c_str(), &files);
932 sort(files->begin(), files->end());
933 bool needs_regen = files->size() != static_cast<size_t>(num_files);
934 if (!needs_regen) {
935 for (int j = 0; j < num_files; j++) {
Shinichiro Hamajia8e903f2015-08-06 19:41:01 +0900936 LoadString(fp, &s);
Shinichiro Hamajie3c43602015-08-13 13:47:27 +0900937 if ((*files)[j] != s) {
938 needs_regen = true;
939 break;
940 }
941 }
942 }
943 if (needs_regen) {
Shinichiro Hamajie67d54e2015-08-13 14:39:11 +0900944 if (ShouldIgnoreDirty(pat)) {
Shinichiro Hamajie3c43602015-08-13 13:47:27 +0900945 if (dump_kati_stamp) {
946 printf("wildcard %s: ignored\n", pat.c_str());
947 }
948 continue;
949 }
950 if (dump_kati_stamp) {
951 printf("wildcard %s: dirty\n", pat.c_str());
952 } else {
953 fprintf(stderr, "wildcard(%s) was changed, regenerating...\n",
954 pat.c_str());
955 }
956 RETURN_TRUE;
957 } else if (dump_kati_stamp) {
958 printf("wildcard %s: clean\n", pat.c_str());
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900959 }
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900960 }
961 }
962
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +0900963 int num_flcs = LoadInt(fp);
964 for (int i = 0; i < num_flcs; i++) {
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900965 string cmd, expected;
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +0900966 LoadString(fp, &cmd);
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900967 LoadString(fp, &expected);
968
Shinichiro Hamajia8e903f2015-08-06 19:41:01 +0900969 {
970 COLLECT_STATS("stat time (regen)");
971 bool has_condition = LoadInt(fp);
972 if (has_condition) {
973 bool should_run_command = false;
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900974
Shinichiro Hamajia8e903f2015-08-06 19:41:01 +0900975 int num_missing_dirs = LoadInt(fp);
976 for (int j = 0; j < num_missing_dirs; j++) {
977 LoadString(fp, &s);
978 should_run_command |= Exists(s);
979 }
980
981 int num_read_dirs = LoadInt(fp);
982 for (int j = 0; j < num_read_dirs; j++) {
983 LoadString(fp, &s);
Shinichiro Hamajif6671e42015-08-07 14:37:49 +0900984 // We assume we rarely do a significant change for the top
985 // directory which affects the results of find command.
Shinichiro Hamajie67d54e2015-08-13 14:39:11 +0900986 if (s == "" || s == "." || ShouldIgnoreDirty(s))
Shinichiro Hamajiede1f2f2015-08-07 14:24:44 +0900987 continue;
Shinichiro Hamajia8e903f2015-08-06 19:41:01 +0900988 double ts = GetTimestamp(s);
989 should_run_command |= (ts < 0 || gen_time < ts);
990 }
991
Shinichiro Hamajif6df3342015-08-11 14:59:36 +0900992 if (!should_run_command) {
993 if (dump_kati_stamp)
994 printf("shell %s: clean (no rerun)\n", cmd.c_str());
Shinichiro Hamajia8e903f2015-08-06 19:41:01 +0900995 continue;
Shinichiro Hamajif6df3342015-08-11 14:59:36 +0900996 }
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900997 }
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900998 }
999
Shinichiro Hamajie67d54e2015-08-13 14:39:11 +09001000 FindCommand fc;
1001 if (fc.Parse(cmd) && !fc.chdir.empty() && ShouldIgnoreDirty(fc.chdir)) {
1002 if (dump_kati_stamp)
1003 printf("shell %s: ignored\n", cmd.c_str());
1004 continue;
1005 }
1006
Shinichiro Hamajia8e903f2015-08-06 19:41:01 +09001007 {
1008 COLLECT_STATS_WITH_SLOW_REPORT("shell time (regen)", cmd.c_str());
1009 string result;
1010 RunCommand("/bin/sh", cmd, RedirectStderr::DEV_NULL, &result);
1011 FormatForCommandSubstitution(&result);
1012 if (expected != result) {
Shinichiro Hamajif6df3342015-08-11 14:59:36 +09001013 if (dump_kati_stamp) {
1014 printf("shell %s: dirty\n", cmd.c_str());
1015 } else {
1016 fprintf(stderr, "$(shell %s) was changed, regenerating...\n",
1017 cmd.c_str());
1018#if 0
1019 fprintf(stderr, "%s => %s\n",
1020 expected.c_str(), result.c_str());
1021#endif
1022 }
1023 RETURN_TRUE;
1024 } else if (dump_kati_stamp) {
1025 printf("shell %s: clean (rerun)\n", cmd.c_str());
Shinichiro Hamajia8e903f2015-08-06 19:41:01 +09001026 }
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +09001027 }
1028 }
1029
Shinichiro Hamajif6df3342015-08-11 14:59:36 +09001030 return retval;
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +09001031}