blob: 9ddcced04598944df0e1b0b6bd80bdda442d015f [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"
41#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 Hamajia4bb7742015-07-31 11:27:30 +0900171 NinjaGenerator(const char* ninja_suffix, const char* ninja_dir, Evaluator* ev,
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900172 double start_time)
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900173 : ce_(ev), ev_(ev), fp_(NULL), rule_id_(0), start_time_(start_time) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900174 ev_->set_avoid_io(true);
Colin Crosse890a912015-07-20 13:28:00 -0700175 shell_ = ev->EvalVar(kShellSym);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900176 if (g_goma_dir)
177 gomacc_ = StringPrintf("%s/gomacc ", g_goma_dir);
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900178 if (ninja_suffix) {
179 ninja_suffix_ = ninja_suffix;
180 }
Colin Crossbb2bba12015-07-20 16:31:18 -0700181 if (ninja_dir) {
182 ninja_dir_ = ninja_dir;
183 } else {
184 ninja_dir_ = ".";
185 }
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900186
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900187 GetExecutablePath(&kati_binary_);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900188 }
189
190 ~NinjaGenerator() {
191 ev_->set_avoid_io(false);
192 }
193
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900194 void Generate(const vector<DepNode*>& nodes,
195 bool build_all_targets,
196 const string& orig_args) {
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900197 GenerateEnvlist();
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900198 GenerateNinja(nodes, build_all_targets, orig_args);
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900199 GenerateShell();
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900200 GenerateStamp();
201 }
202
203 static string GetStampFilename(const char* ninja_dir,
204 const char* ninja_suffix) {
205 return StringPrintf("%s/.kati_stamp%s",
206 ninja_dir ? ninja_dir : ".",
207 ninja_suffix ? ninja_suffix : "");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900208 }
209
210 private:
211 string GenRuleName() {
212 return StringPrintf("rule%d", rule_id_++);
213 }
214
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900215 StringPiece TranslateCommand(const char* in, string* cmd_buf) {
216 const size_t orig_size = cmd_buf->size();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900217 bool prev_backslash = false;
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900218 // Set space as an initial value so the leading comment will be
219 // stripped out.
220 char prev_char = ' ';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900221 char quote = 0;
Shinichiro Hamajifc14d5f2015-07-28 17:07:57 +0900222 for (; *in; in++) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900223 switch (*in) {
224 case '#':
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900225 if (quote == 0 && isspace(prev_char)) {
Shinichiro Hamajifc14d5f2015-07-28 17:07:57 +0900226 while (in[1] && *in != '\n')
227 in++;
Colin Cross415e4a12015-07-15 18:21:38 -0700228 } else {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900229 *cmd_buf += *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900230 }
Colin Cross415e4a12015-07-15 18:21:38 -0700231 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900232
233 case '\'':
234 case '"':
235 case '`':
236 if (quote) {
237 if (quote == *in)
238 quote = 0;
239 } else if (!prev_backslash) {
240 quote = *in;
241 }
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900242 *cmd_buf += *in;
Shinichiro Hamaji4212e382015-06-29 17:21:04 +0900243 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900244
245 case '$':
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900246 *cmd_buf += "$$";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900247 break;
248
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900249 case '\n':
250 if (prev_backslash) {
Shinichiro Hamajia4dfe752015-07-28 15:54:36 +0900251 cmd_buf->resize(cmd_buf->size()-1);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900252 } else {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900253 *cmd_buf += ' ';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900254 }
Shinichiro Hamaji4d151832015-06-29 18:15:46 +0900255 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900256
257 case '\\':
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900258 *cmd_buf += '\\';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900259 break;
260
261 default:
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900262 *cmd_buf += *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900263 }
Colin Crossf6f1cf42015-07-15 15:25:30 -0700264
265 if (*in == '\\') {
266 prev_backslash = !prev_backslash;
267 } else {
268 prev_backslash = false;
269 }
270
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900271 prev_char = *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900272 }
273
274 while (true) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900275 char c = (*cmd_buf)[cmd_buf->size()-1];
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900276 if (!isspace(c) && c != ';')
277 break;
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900278 cmd_buf->resize(cmd_buf->size() - 1);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900279 }
280
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900281 return StringPiece(cmd_buf->data() + orig_size,
282 cmd_buf->size() - orig_size);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900283 }
284
Colin Cross2e032ba2015-07-15 18:33:37 -0700285 bool GetDescriptionFromCommand(StringPiece cmd, string *out) {
Colin Cross2e032ba2015-07-15 18:33:37 -0700286 if (!HasPrefix(cmd, "echo ")) {
287 return false;
288 }
289 cmd = cmd.substr(5, cmd.size());
290
Colin Cross2e032ba2015-07-15 18:33:37 -0700291 bool prev_backslash = false;
292 char quote = 0;
293 string out_buf;
294
295 // Strip outer quotes, and fail if it is not a single echo command
296 for (StringPiece::iterator in = cmd.begin(); in != cmd.end(); in++) {
297 if (prev_backslash) {
298 prev_backslash = false;
299 out_buf += *in;
300 } else if (*in == '\\') {
301 prev_backslash = true;
302 out_buf += *in;
303 } else if (quote) {
304 if (*in == quote) {
305 quote = 0;
306 } else {
307 out_buf += *in;
308 }
309 } else {
310 switch (*in) {
311 case '\'':
312 case '"':
313 case '`':
314 quote = *in;
315 break;
316
317 case '<':
318 case '>':
319 case '&':
320 case '|':
321 case ';':
322 return false;
323
324 default:
325 out_buf += *in;
326 }
327 }
328 }
329
330 *out = out_buf;
331 return true;
332 }
333
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900334 bool GenShellScript(const vector<Command*>& commands,
335 string* cmd_buf,
336 string* description) {
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900337 bool got_descritpion = false;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900338 bool use_gomacc = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900339 bool should_ignore_error = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900340 for (const Command* c : commands) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900341 if (!cmd_buf->empty()) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900342 if (should_ignore_error) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900343 *cmd_buf += " ; ";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900344 } else {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900345 *cmd_buf += " && ";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900346 }
347 }
348 should_ignore_error = c->ignore_error;
349
350 const char* in = c->cmd->c_str();
351 while (isspace(*in))
352 in++;
353
354 bool needs_subshell = commands.size() > 1;
355 if (*in == '(') {
356 needs_subshell = false;
357 }
358
359 if (needs_subshell)
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900360 *cmd_buf += '(';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900361
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900362 size_t cmd_start = cmd_buf->size();
363 StringPiece translated = TranslateCommand(in, cmd_buf);
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900364 if (g_detect_android_echo && !got_descritpion && !c->echo &&
365 GetDescriptionFromCommand(translated, description)) {
366 got_descritpion = true;
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900367 cmd_buf->resize(cmd_start);
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900368 translated.clear();
369 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900370 if (translated.empty()) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900371 *cmd_buf += "true";
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +0900372 } else if (g_goma_dir) {
373 size_t pos = GetGomaccPosForAndroidCompileCommand(translated);
374 if (pos != string::npos) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900375 cmd_buf->insert(cmd_start + pos, gomacc_);
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +0900376 use_gomacc = true;
377 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900378 }
379
380 if (c == commands.back() && c->ignore_error) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900381 *cmd_buf += " ; true";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900382 }
383
384 if (needs_subshell)
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900385 *cmd_buf += ')';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900386 }
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900387 return g_goma_dir && !use_gomacc;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900388 }
389
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900390 void EmitDepfile(string* cmd_buf) {
391 *cmd_buf += ' ';
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900392 string depfile;
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900393 bool result = GetDepfileFromCommand(cmd_buf, &depfile);
394 cmd_buf->resize(cmd_buf->size()-1);
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900395 if (!result)
396 return;
397 fprintf(fp_, " depfile = %s\n", depfile.c_str());
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900398 fprintf(fp_, " deps = gcc\n");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900399 }
400
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900401 void EmitNode(DepNode* node) {
402 auto p = done_.insert(node->output);
403 if (!p.second)
404 return;
405
Shinichiro Hamaji90216592015-07-28 14:36:31 +0900406 // Removing this will fix auto_vars.mk, build_once.mk, and
407 // command_vars.mk. However, this change will make
408 // ninja_normalized_path2.mk fail and cause a lot of warnings for
409 // Android build.
Shinichiro Hamaji0f39c522015-07-07 13:14:02 +0900410 if (node->cmds.empty() &&
411 node->deps.empty() && node->order_onlys.empty() && !node->is_phony) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900412 return;
Shinichiro Hamaji0f39c522015-07-07 13:14:02 +0900413 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900414
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900415 StringPiece base = Basename(node->output.str());
416 if (base != node->output.str()) {
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900417 auto p = short_names_.emplace(Intern(base), node->output);
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900418 if (!p.second) {
419 // We generate shortcuts only for targets whose basename are unique.
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900420 p.first->second = kEmptySym;
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900421 }
422 }
423
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900424 vector<Command*> commands;
425 ce_.Eval(node, &commands);
426
427 string rule_name = "phony";
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900428 bool use_local_pool = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900429 if (!commands.empty()) {
430 rule_name = GenRuleName();
431 fprintf(fp_, "rule %s\n", rule_name.c_str());
Colin Cross2e032ba2015-07-15 18:33:37 -0700432
433 string description = "build $out";
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900434 string cmd_buf;
435 use_local_pool |= GenShellScript(commands, &cmd_buf, &description);
Colin Cross2e032ba2015-07-15 18:33:37 -0700436 fprintf(fp_, " description = %s\n", description.c_str());
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900437 EmitDepfile(&cmd_buf);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900438
Shinichiro Hamajieb0e5f92015-07-17 03:38:23 +0900439 // It seems Linux is OK with ~130kB and Mac's limit is ~250kB.
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900440 // TODO: Find this number automatically.
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900441 if (cmd_buf.size() > 100 * 1000) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900442 fprintf(fp_, " rspfile = $out.rsp\n");
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900443 fprintf(fp_, " rspfile_content = %s\n", cmd_buf.c_str());
Colin Crosse890a912015-07-20 13:28:00 -0700444 fprintf(fp_, " command = %s $out.rsp\n", shell_->c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900445 } else {
Shinichiro Hamaji2e04d302015-07-24 09:31:18 +0900446 fprintf(fp_, " command = %s -c \"%s\"\n",
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900447 shell_->c_str(), EscapeShell(cmd_buf).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900448 }
449 }
450
451 EmitBuild(node, rule_name);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900452 if (use_local_pool)
453 fprintf(fp_, " pool = local_pool\n");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900454
455 for (DepNode* d : node->deps) {
456 EmitNode(d);
457 }
Shinichiro Hamaji3f2cf1e2015-07-06 18:58:18 +0900458 for (DepNode* d : node->order_onlys) {
459 EmitNode(d);
460 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900461 }
462
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900463 string EscapeBuildTarget(Symbol s) {
464 if (s.str().find_first_of("$: ") == string::npos)
465 return s.str();
466 string r;
467 for (char c : s.str()) {
468 switch (c) {
469 case '$':
470 case ':':
471 case ' ':
472 r += '$';
473 // fall through.
474 default:
475 r += c;
476 }
477 }
478 return r;
479 }
480
Colin Crosse890a912015-07-20 13:28:00 -0700481 string EscapeShell(string s) {
482 if (s.find_first_of("$`!\\\"") == string::npos)
483 return s;
484 string r;
485 bool lastDollar = false;
486 for (char c : s) {
487 switch (c) {
488 case '$':
489 if (lastDollar) {
490 r += c;
491 lastDollar = false;
492 } else {
493 r += '\\';
494 r += c;
495 lastDollar = true;
496 }
497 break;
498 case '`':
499 case '"':
500 case '!':
501 case '\\':
502 r += '\\';
503 // fall through.
504 default:
505 r += c;
506 lastDollar = false;
507 }
508 }
509 return r;
510 }
511
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900512 void EmitBuild(DepNode* node, const string& rule_name) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900513 fprintf(fp_, "build %s: %s",
514 EscapeBuildTarget(node->output).c_str(),
515 rule_name.c_str());
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900516 vector<Symbol> order_onlys;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900517 for (DepNode* d : node->deps) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900518 fprintf(fp_, " %s", EscapeBuildTarget(d->output).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900519 }
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900520 if (!node->order_onlys.empty()) {
Shinichiro Hamajif4820de2015-06-29 17:03:17 +0900521 fprintf(fp_, " ||");
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900522 for (DepNode* d : node->order_onlys) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900523 fprintf(fp_, " %s", EscapeBuildTarget(d->output).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900524 }
525 }
526 fprintf(fp_, "\n");
527 }
528
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900529 void EmitRegenRules(const string& orig_args) {
530 if (!g_gen_regen_rule)
531 return;
532
533 fprintf(fp_, "rule regen_ninja\n");
534 fprintf(fp_, " command = %s\n", orig_args.c_str());
535 fprintf(fp_, " generator = 1\n");
536 fprintf(fp_, " description = Regenerate ninja files due to dependency\n");
537 fprintf(fp_, "build %s: regen_ninja", GetNinjaFilename().c_str());
538 unordered_set<string> makefiles;
539 MakefileCacheManager::Get()->GetAllFilenames(&makefiles);
540 for (const string& makefile : makefiles) {
541 fprintf(fp_, " %.*s", SPF(makefile));
542 }
Shinichiro Hamajib58bb4b2015-07-30 18:02:51 +0900543 fprintf(fp_, " %s", kati_binary_.c_str());
Shinichiro Hamaji3ed61482015-07-18 14:16:32 +0900544 // TODO: Add dependencies to directories read by $(wildcard)
545 // or $(shell find).
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900546 if (!used_envs_.empty())
547 fprintf(fp_, " %s", GetEnvlistFilename().c_str());
548 fprintf(fp_, "\n\n");
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900549 }
550
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900551 string GetNinjaFilename() const {
Shinichiro Hamaji2e04d302015-07-24 09:31:18 +0900552 return StringPrintf("%s/build%s.ninja",
553 ninja_dir_.c_str(), ninja_suffix_.c_str());
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900554 }
555
556 string GetShellScriptFilename() const {
Shinichiro Hamaji2e04d302015-07-24 09:31:18 +0900557 return StringPrintf("%s/ninja%s.sh",
558 ninja_dir_.c_str(), ninja_suffix_.c_str());
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900559 }
560
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900561 string GetEnvlistFilename() const {
Shinichiro Hamaji41957e02015-07-24 10:27:14 +0900562 return StringPrintf("%s/.kati_env%s",
563 ninja_dir_.c_str(), ninja_suffix_.c_str());
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900564 }
565
Shinichiro Hamajid556e622015-07-18 15:33:58 +0900566 string GetLunchFilename() const {
Shinichiro Hamaji41957e02015-07-24 10:27:14 +0900567 return StringPrintf("%s/.kati_lunch%s",
568 ninja_dir_.c_str(), ninja_suffix_.c_str());
Shinichiro Hamajid556e622015-07-18 15:33:58 +0900569 }
570
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900571 string GetStampFilename() const {
572 return GetStampFilename(ninja_dir_.c_str(), ninja_suffix_.c_str());
573 }
574
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900575 void GenerateNinja(const vector<DepNode*>& nodes,
576 bool build_all_targets,
577 const string& orig_args) {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900578 fp_ = fopen(GetNinjaFilename().c_str(), "wb");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900579 if (fp_ == NULL)
580 PERROR("fopen(build.ninja) failed");
581
Shinichiro Hamajid821f6d2015-07-14 04:03:27 +0900582 fprintf(fp_, "# Generated by kati %s\n", kGitVersion);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900583 fprintf(fp_, "\n");
584
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900585 if (!used_envs_.empty()) {
Shinichiro Hamaji5163e042015-07-14 03:51:44 +0900586 fprintf(fp_, "# Environment variables used:\n");
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900587 for (const auto& p : used_envs_) {
588 fprintf(fp_, "# %s=%s\n", p.first.c_str(), p.second.c_str());
Shinichiro Hamaji5163e042015-07-14 03:51:44 +0900589 }
590 fprintf(fp_, "\n");
591 }
592
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900593 if (g_goma_dir) {
594 fprintf(fp_, "pool local_pool\n");
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900595 fprintf(fp_, " depth = %d\n\n", g_num_jobs);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900596 }
597
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900598 EmitRegenRules(orig_args);
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900599
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900600 for (DepNode* node : nodes) {
601 EmitNode(node);
602 }
603
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900604 for (Symbol e : Vars::used_env_vars()) {
Shinichiro Hamajied883ef2015-07-31 13:15:04 +0900605 StringPiece val(getenv(e.c_str()));
606 used_envs_.emplace(e.str(), val.as_string());
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900607 }
608
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900609 if (!build_all_targets) {
610 CHECK(!nodes.empty());
Shinichiro Hamaji865597b2015-07-28 16:16:58 +0900611 fprintf(fp_, "\ndefault %s\n",
612 EscapeBuildTarget(nodes.front()->output).c_str());
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900613 }
614
Shinichiro Hamaji15fe11d2015-07-11 06:32:37 +0900615 fprintf(fp_, "\n# shortcuts:\n");
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900616 for (auto p : short_names_) {
Shinichiro Hamaji10bb4ab2015-07-16 06:04:48 +0900617 if (!p.second.empty() && !done_.count(p.first))
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900618 fprintf(fp_, "build %s: phony %s\n", p.first.c_str(), p.second.c_str());
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900619 }
620
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900621 fclose(fp_);
622 }
623
624 void GenerateShell() {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900625 FILE* fp = fopen(GetShellScriptFilename().c_str(), "wb");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900626 if (fp == NULL)
627 PERROR("fopen(ninja.sh) failed");
628
Shinichiro Hamaji4dd62de2015-07-28 16:06:52 +0900629 fprintf(fp, "#!/bin/sh\n");
Shinichiro Hamajiaf9887a2015-07-17 03:45:14 +0900630 fprintf(fp, "# Generated by kati %s\n", kGitVersion);
631 fprintf(fp, "\n");
Shinichiro Hamajic7629e02015-07-22 02:41:57 +0900632 if (ninja_dir_ == ".")
633 fprintf(fp, "cd $(dirname \"$0\")\n");
Shinichiro Hamajid556e622015-07-18 15:33:58 +0900634 if (!ninja_suffix_.empty()) {
635 fprintf(fp, "if [ -f %s ]; then\n export $(cat %s)\nfi\n",
636 GetEnvlistFilename().c_str(), GetEnvlistFilename().c_str());
637 fprintf(fp, "if [ -f %s ]; then\n export $(cat %s)\nfi\n",
638 GetLunchFilename().c_str(), GetLunchFilename().c_str());
639 }
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900640
641 for (const auto& p : ev_->exports()) {
642 if (p.second) {
Shinichiro Hamaji94d6f2a2015-07-05 05:32:25 +0900643 shared_ptr<string> val = ev_->EvalVar(p.first);
Shinichiro Hamajif65e5722015-07-28 16:13:18 +0900644 fprintf(fp, "export '%s'='%s'\n", p.first.c_str(), val->c_str());
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900645 } else {
Shinichiro Hamajif65e5722015-07-28 16:13:18 +0900646 fprintf(fp, "unset '%s'\n", p.first.c_str());
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900647 }
648 }
649
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900650 fprintf(fp, "exec ninja -f %s ", GetNinjaFilename().c_str());
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900651 if (g_goma_dir) {
Shinichiro Hamaji6d7c7b72015-07-18 16:51:39 +0900652 fprintf(fp, "-j500 ");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900653 }
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900654 fprintf(fp, "\"$@\"\n");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900655
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900656 if (chmod(GetShellScriptFilename().c_str(), 0755) != 0)
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900657 PERROR("chmod ninja.sh failed");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900658 }
659
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900660 void GenerateEnvlist() {
661 if (used_envs_.empty())
662 return;
663 FILE* fp = fopen(GetEnvlistFilename().c_str(), "wb");
664 for (const auto& p : used_envs_) {
665 fprintf(fp, "%s=%s\n", p.first.c_str(), p.second.c_str());
666 }
667 fclose(fp);
668 }
669
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900670 void GetCommonPrefixDir(const vector<string>& files, string* o) {
671 for (const string& file : files) {
672 size_t l = min(file.size(), o->size());
673 for (size_t i = 0; i < l; i++) {
674 if (file[i] != (*o)[i]) {
675 size_t index = o->rfind('/', i);
676 if (index == string::npos)
677 index = 0;
678 o->resize(index);
679 break;
680 }
681 }
682 }
683 }
684
685 void GetReadDirs(const string& pat,
686 const vector<string>& files,
687 unordered_set<string>* dirs) {
688 string prefix_dir = Dirname(pat).as_string();
689 size_t index = prefix_dir.find_first_of("?*[\\");
690 if (index != string::npos) {
691 index = prefix_dir.rfind('/', index);
692 if (index == string::npos) {
693 prefix_dir = "";
694 } else {
695 prefix_dir = prefix_dir.substr(0, index);
696 }
697 }
698
699 GetCommonPrefixDir(files, &prefix_dir);
700 if (prefix_dir.empty())
701 prefix_dir = ".";
702 dirs->insert(prefix_dir);
703 for (const string& file : files) {
704 StringPiece dir = Dirname(file);
705 while (dir != prefix_dir) {
706 dirs->insert(dir.as_string());
707 dir = Dirname(dir);
708 }
709 }
710 }
711
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900712 void GenerateStamp() {
713 FILE* fp = fopen(GetStampFilename().c_str(), "wb");
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900714 CHECK(fp);
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900715
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900716 size_t r = fwrite(&start_time_, sizeof(start_time_), 1, fp);
717 CHECK(r == 1);
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900718
719 unordered_set<string> makefiles;
720 MakefileCacheManager::Get()->GetAllFilenames(&makefiles);
721 DumpInt(fp, makefiles.size() + 1);
722 DumpString(fp, kati_binary_);
723 for (const string& makefile : makefiles) {
724 DumpString(fp, makefile);
725 }
726
Shinichiro Hamaji7e708012015-07-31 13:07:34 +0900727 DumpInt(fp, Evaluator::used_undefined_vars().size());
728 for (Symbol v : Evaluator::used_undefined_vars()) {
729 DumpString(fp, v.str());
730 }
731
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900732 DumpInt(fp, used_envs_.size());
733 for (const auto& p : used_envs_) {
734 DumpString(fp, p.first);
735 DumpString(fp, p.second);
736 }
737
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900738 const unordered_map<string, vector<string>*>& globs = GetAllGlobCache();
739 DumpInt(fp, globs.size());
740 for (const auto& p : globs) {
741 DumpString(fp, p.first);
742 const vector<string>& files = *p.second;
743 unordered_set<string> dirs;
744 GetReadDirs(p.first, files, &dirs);
745 DumpInt(fp, dirs.size());
746 for (const string& dir : dirs) {
747 DumpString(fp, dir);
748 }
749 DumpInt(fp, files.size());
750 for (const string& file : files) {
751 DumpString(fp, file);
752 }
753 }
754
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +0900755 const vector<FileListCommand*>& flcs = GetFileListCommmands();
756 DumpInt(fp, flcs.size());
757 for (FileListCommand* flc : flcs) {
758 DumpString(fp, flc->cmd);
759 DumpString(fp, flc->result);
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900760 if (!flc->find.get()) {
761 // Always re-run this command.
762 DumpInt(fp, 0);
763 continue;
764 }
765
766 DumpInt(fp, 1);
767
768 vector<string> missing_dirs;
769 for (StringPiece fd : flc->find->finddirs) {
770 const string& d = ConcatDir(flc->find->chdir, fd);
771 if (!Exists(d))
772 missing_dirs.push_back(d);
773 }
774 DumpInt(fp, missing_dirs.size());
775 for (const string& d : missing_dirs) {
776 DumpString(fp, d);
777 }
778
779 DumpInt(fp, flc->find->read_dirs->size());
780 for (StringPiece s : *flc->find->read_dirs) {
781 DumpString(fp, s);
782 }
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +0900783 }
784
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900785 fclose(fp);
786 }
787
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900788 CommandEvaluator ce_;
789 Evaluator* ev_;
790 FILE* fp_;
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900791 unordered_set<Symbol> done_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900792 int rule_id_;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900793 string gomacc_;
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900794 string ninja_suffix_;
Colin Crossbb2bba12015-07-20 16:31:18 -0700795 string ninja_dir_;
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900796 unordered_map<Symbol, Symbol> short_names_;
Colin Crosse890a912015-07-20 13:28:00 -0700797 shared_ptr<string> shell_;
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900798 map<string, string> used_envs_;
Shinichiro Hamajib58bb4b2015-07-30 18:02:51 +0900799 string kati_binary_;
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900800 double start_time_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900801};
802
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900803void GenerateNinja(const char* ninja_suffix,
Colin Crossbb2bba12015-07-20 16:31:18 -0700804 const char* ninja_dir,
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900805 const vector<DepNode*>& nodes,
806 Evaluator* ev,
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900807 bool build_all_targets,
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900808 const string& orig_args,
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900809 double start_time) {
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900810 NinjaGenerator ng(ninja_suffix, ninja_dir, ev, start_time);
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900811 ng.Generate(nodes, build_all_targets, orig_args);
Shinichiro Hamaji5f86e1a2015-06-29 14:25:39 +0900812}
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900813
814bool NeedsRegen(const char* ninja_suffix,
Shinichiro Hamaji81958cb2015-08-06 19:36:55 +0900815 const char* ninja_dir,
816 bool ignore_kati_binary) {
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900817 const string& stamp_filename =
818 NinjaGenerator::GetStampFilename(ninja_suffix, ninja_dir);
819 FILE* fp = fopen(stamp_filename.c_str(), "rb");
820 if (!fp)
821 return true;
822 ScopedFile sfp(fp);
823
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900824 double gen_time;
825 size_t r = fread(&gen_time, sizeof(gen_time), 1, fp);
826 CHECK(r == 1);
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900827
828 string s, s2;
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900829 int num_files = LoadInt(fp);
830 for (int i = 0; i < num_files; i++) {
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900831 LoadString(fp, &s);
832 if (gen_time < GetTimestamp(s)) {
Shinichiro Hamaji81958cb2015-08-06 19:36:55 +0900833 if (ignore_kati_binary) {
834 string kati_binary;
835 GetExecutablePath(&kati_binary);
836 if (s == kati_binary) {
837 fprintf(stderr, "%s was modified, ignored.\n", s.c_str());
838 continue;
839 }
840 }
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900841 fprintf(stderr, "%s was modified, regenerating...\n", s.c_str());
842 return true;
843 }
844 }
845
Shinichiro Hamaji7e708012015-07-31 13:07:34 +0900846 int num_undefineds = LoadInt(fp);
847 for (int i = 0; i < num_undefineds; i++) {
848 LoadString(fp, &s);
849 if (getenv(s.c_str())) {
850 fprintf(stderr, "Environment variable %s was set, regenerating...\n",
851 s.c_str());
852 return true;
853 }
854 }
855
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900856 int num_envs = LoadInt(fp);
857 for (int i = 0; i < num_envs; i++) {
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900858 LoadString(fp, &s);
859 StringPiece val(getenv(s.c_str()));
860 LoadString(fp, &s2);
861 if (val != s2) {
862 fprintf(stderr, "Environment variable %s was modified (%s => %.*s), "
863 "regenerating...\n",
Shinichiro Hamaji7e708012015-07-31 13:07:34 +0900864 s.c_str(), s2.c_str(), SPF(val));
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900865 return true;
866 }
867 }
868
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900869 int num_globs = LoadInt(fp);
870 string pat;
871 for (int i = 0; i < num_globs; i++) {
872 LoadString(fp, &pat);
873 bool needs_reglob = false;
874 int num_dirs = LoadInt(fp);
875 for (int j = 0; j < num_dirs; j++) {
876 LoadString(fp, &s);
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900877 // TODO: Handle removed files properly.
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900878 needs_reglob |= gen_time < GetTimestamp(s);
879 }
880
881 int num_files = LoadInt(fp);
882 if (needs_reglob) {
883 vector<string>* files;
884 Glob(pat.c_str(), &files);
885 sort(files->begin(), files->end());
886 bool needs_regen = files->size() != static_cast<size_t>(num_files);
887 if (!needs_regen) {
888 for (int j = 0; j < num_files; j++) {
889 LoadString(fp, &s);
890 if ((*files)[j] != s) {
891 needs_regen = true;
892 break;
893 }
894 }
895 }
896 if (needs_regen) {
897 fprintf(stderr, "wildcard(%s) was changed, regenerating...\n",
898 pat.c_str());
899 return true;
900 }
901 } else {
902 for (int j = 0; j < num_files; j++)
903 LoadString(fp, &s);
904 }
905 }
906
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +0900907 int num_flcs = LoadInt(fp);
908 for (int i = 0; i < num_flcs; i++) {
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900909 string cmd, expected;
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +0900910 LoadString(fp, &cmd);
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900911 LoadString(fp, &expected);
912
913 bool has_condition = LoadInt(fp);
914 if (has_condition) {
915 bool should_run_command = false;
916
917 int num_missing_dirs = LoadInt(fp);
918 for (int j = 0; j < num_missing_dirs; j++) {
919 LoadString(fp, &s);
920 should_run_command |= Exists(s);
921 }
922
923 int num_read_dirs = LoadInt(fp);
924 for (int j = 0; j < num_read_dirs; j++) {
925 LoadString(fp, &s);
926 double ts = GetTimestamp(s);
927 should_run_command |= (ts < 0 || gen_time < ts);
928 }
929
930 if (!should_run_command)
931 continue;
932 }
933
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +0900934 string result;
935 RunCommand("/bin/sh", cmd, RedirectStderr::DEV_NULL, &result);
936 FormatForCommandSubstitution(&result);
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900937 if (expected != result) {
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +0900938 fprintf(stderr, "$(shell %s) was changed, regenerating...\n",
939 cmd.c_str());
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900940 fprintf(stderr, "%s => %s\n",
941 expected.c_str(), result.c_str());
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +0900942 return true;
943 }
944 }
945
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900946 return false;
947}