blob: bbae7c8a9b0d17c4fd9dfb00fa1841ed81473e3e [file] [log] [blame]
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +09001// Copyright 2016 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#include "regen.h"
16
17#include <sys/stat.h>
18
19#include <algorithm>
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +090020#include <memory>
Shinichiro Hamaji8380fb82016-02-26 16:51:50 +090021#include <mutex>
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +090022#include <vector>
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +090023
24#include "fileutil.h"
25#include "find.h"
Dan Willemsenf06d8012016-10-03 00:16:07 -070026#include "func.h"
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +090027#include "io.h"
28#include "log.h"
29#include "ninja.h"
30#include "stats.h"
31#include "strutil.h"
Shinichiro Hamaji702befc2016-01-27 17:21:39 +090032#include "thread_pool.h"
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +090033
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +090034namespace {
35
36#define RETURN_TRUE do { \
37 if (g_flags.dump_kati_stamp) \
38 needs_regen_ = true; \
39 else \
40 return true; \
41 } while (0)
42
43bool ShouldIgnoreDirty(StringPiece s) {
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +090044 Pattern pat(g_flags.ignore_dirty_pattern);
45 Pattern nopat(g_flags.no_ignore_dirty_pattern);
46 return pat.Match(s) && !nopat.Match(s);
47}
48
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +090049class StampChecker {
50 struct GlobResult {
51 string pat;
52 vector<string> result;
53 };
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +090054
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +090055 struct ShellResult {
Dan Willemsenf06d8012016-10-03 00:16:07 -070056 CommandOp op;
Stefan Beckerd4f28712016-04-07 13:29:36 +030057 string shell;
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +090058 string cmd;
59 string result;
60 vector<string> missing_dirs;
61 vector<string> read_dirs;
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +090062 };
63
64 public:
65 StampChecker()
66 : needs_regen_(false) {
67 }
68
69 ~StampChecker() {
70 for (GlobResult* gr : globs_) {
71 delete gr;
72 }
73 for (ShellResult* sr : commands_) {
74 delete sr;
75 }
76 }
77
78 bool NeedsRegen(double start_time, const string& orig_args) {
79 if (IsMissingOutputs())
80 RETURN_TRUE;
81
82 if (CheckStep1(orig_args))
83 RETURN_TRUE;
84
85 if (CheckStep2())
86 RETURN_TRUE;
87
88 if (!needs_regen_) {
89 FILE* fp = fopen(GetNinjaStampFilename().c_str(), "rb+");
90 if (!fp)
91 return true;
92 ScopedFile sfp(fp);
93 if (fseek(fp, 0, SEEK_SET) < 0)
94 PERROR("fseek");
95 size_t r = fwrite(&start_time, sizeof(start_time), 1, fp);
96 CHECK(r == 1);
97 }
98 return needs_regen_;
99 }
100
101 private:
102 bool IsMissingOutputs() {
103 if (!Exists(GetNinjaFilename())) {
104 fprintf(stderr, "%s is missing, regenerating...\n",
105 GetNinjaFilename().c_str());
106 return true;
107 }
108 if (!Exists(GetNinjaShellScriptFilename())) {
109 fprintf(stderr, "%s is missing, regenerating...\n",
110 GetNinjaShellScriptFilename().c_str());
111 return true;
112 }
113 return false;
114 }
115
116 bool CheckStep1(const string& orig_args) {
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900117#define LOAD_INT(fp) ({ \
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900118 int v = LoadInt(fp); \
119 if (v < 0) { \
120 fprintf(stderr, "incomplete kati_stamp, regenerating...\n"); \
121 RETURN_TRUE; \
122 } \
123 v; \
124 })
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900125
126#define LOAD_STRING(fp, s) ({ \
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900127 if (!LoadString(fp, s)) { \
128 fprintf(stderr, "incomplete kati_stamp, regenerating...\n"); \
129 RETURN_TRUE; \
130 } \
131 })
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900132
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900133 const string& stamp_filename = GetNinjaStampFilename();
134 FILE* fp = fopen(stamp_filename.c_str(), "rb");
135 if (!fp) {
Dan Willemsenf6486ce2016-09-16 19:21:36 -0700136 if (g_flags.regen_debug)
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900137 printf("%s: %s\n", stamp_filename.c_str(), strerror(errno));
138 return true;
139 }
140 ScopedFile sfp(fp);
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900141
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900142 double gen_time;
143 size_t r = fread(&gen_time, sizeof(gen_time), 1, fp);
144 gen_time_ = gen_time;
145 if (r != 1) {
146 fprintf(stderr, "incomplete kati_stamp, regenerating...\n");
147 RETURN_TRUE;
148 }
Dan Willemsenf6486ce2016-09-16 19:21:36 -0700149 if (g_flags.regen_debug)
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900150 printf("Generated time: %f\n", gen_time);
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900151
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900152 string s, s2;
153 int num_files = LOAD_INT(fp);
154 for (int i = 0; i < num_files; i++) {
155 LOAD_STRING(fp, &s);
156 double ts = GetTimestamp(s);
157 if (gen_time < ts) {
158 if (g_flags.regen_ignoring_kati_binary) {
159 string kati_binary;
160 GetExecutablePath(&kati_binary);
161 if (s == kati_binary) {
162 fprintf(stderr, "%s was modified, ignored.\n", s.c_str());
163 continue;
164 }
165 }
166 if (ShouldIgnoreDirty(s)) {
Dan Willemsenf6486ce2016-09-16 19:21:36 -0700167 if (g_flags.regen_debug)
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900168 printf("file %s: ignored (%f)\n", s.c_str(), ts);
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900169 continue;
170 }
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900171 if (g_flags.dump_kati_stamp)
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900172 printf("file %s: dirty (%f)\n", s.c_str(), ts);
173 else
174 fprintf(stderr, "%s was modified, regenerating...\n", s.c_str());
175 RETURN_TRUE;
176 } else if (g_flags.dump_kati_stamp) {
177 printf("file %s: clean (%f)\n", s.c_str(), ts);
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900178 }
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900179 }
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900180
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900181 int num_undefineds = LOAD_INT(fp);
182 for (int i = 0; i < num_undefineds; i++) {
183 LOAD_STRING(fp, &s);
184 if (getenv(s.c_str())) {
185 if (g_flags.dump_kati_stamp) {
186 printf("env %s: dirty (unset => %s)\n", s.c_str(), getenv(s.c_str()));
187 } else {
188 fprintf(stderr, "Environment variable %s was set, regenerating...\n",
189 s.c_str());
190 }
191 RETURN_TRUE;
192 } else if (g_flags.dump_kati_stamp) {
193 printf("env %s: clean (unset)\n", s.c_str());
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900194 }
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900195 }
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900196
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900197 int num_envs = LOAD_INT(fp);
198 for (int i = 0; i < num_envs; i++) {
199 LOAD_STRING(fp, &s);
200 StringPiece val(getenv(s.c_str()));
201 LOAD_STRING(fp, &s2);
202 if (val != s2) {
203 if (g_flags.dump_kati_stamp) {
204 printf("env %s: dirty (%s => %.*s)\n",
205 s.c_str(), s2.c_str(), SPF(val));
206 } else {
207 fprintf(stderr, "Environment variable %s was modified (%s => %.*s), "
208 "regenerating...\n",
209 s.c_str(), s2.c_str(), SPF(val));
210 }
211 RETURN_TRUE;
212 } else if (g_flags.dump_kati_stamp) {
213 printf("env %s: clean (%.*s)\n", s.c_str(), SPF(val));
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900214 }
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900215 }
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900216
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900217 int num_globs = LOAD_INT(fp);
218 string pat;
219 for (int i = 0; i < num_globs; i++) {
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900220 GlobResult* gr = new GlobResult;
221 globs_.push_back(gr);
222
223 LOAD_STRING(fp, &gr->pat);
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900224 int num_files = LOAD_INT(fp);
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900225 gr->result.resize(num_files);
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900226 for (int j = 0; j < num_files; j++) {
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900227 LOAD_STRING(fp, &gr->result[j]);
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900228 }
229 }
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900230
231 int num_crs = LOAD_INT(fp);
232 for (int i = 0; i < num_crs; i++) {
233 ShellResult* sr = new ShellResult;
234 commands_.push_back(sr);
Dan Willemsenf06d8012016-10-03 00:16:07 -0700235 sr->op = static_cast<CommandOp>(LOAD_INT(fp));
Stefan Beckerd4f28712016-04-07 13:29:36 +0300236 LOAD_STRING(fp, &sr->shell);
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900237 LOAD_STRING(fp, &sr->cmd);
238 LOAD_STRING(fp, &sr->result);
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900239
Dan Willemsenf06d8012016-10-03 00:16:07 -0700240 if (sr->op == CommandOp::FIND) {
241 int num_missing_dirs = LOAD_INT(fp);
242 for (int j = 0; j < num_missing_dirs; j++) {
243 LOAD_STRING(fp, &s);
244 sr->missing_dirs.push_back(s);
245 }
246 int num_read_dirs = LOAD_INT(fp);
247 for (int j = 0; j < num_read_dirs; j++) {
248 LOAD_STRING(fp, &s);
249 sr->read_dirs.push_back(s);
250 }
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900251 }
252 }
253
254 LoadString(fp, &s);
255 if (orig_args != s) {
256 fprintf(stderr, "arguments changed, regenerating...\n");
257 RETURN_TRUE;
258 }
259
260 return needs_regen_;
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900261 }
262
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900263 bool CheckGlobResult(const GlobResult* gr, string* err) {
264 COLLECT_STATS("glob time (regen)");
265 vector<string>* files;
266 Glob(gr->pat.c_str(), &files);
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900267 bool needs_regen = files->size() != gr->result.size();
268 for (size_t i = 0; i < gr->result.size(); i++) {
269 if (!needs_regen) {
270 if ((*files)[i] != gr->result[i]) {
271 needs_regen = true;
272 break;
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900273 }
274 }
275 }
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900276 if (needs_regen) {
277 if (ShouldIgnoreDirty(gr->pat)) {
278 if (g_flags.dump_kati_stamp) {
279 printf("wildcard %s: ignored\n", gr->pat.c_str());
280 }
281 return false;
282 }
283 if (g_flags.dump_kati_stamp) {
284 printf("wildcard %s: dirty\n", gr->pat.c_str());
285 } else {
286 *err = StringPrintf("wildcard(%s) was changed, regenerating...\n",
287 gr->pat.c_str());
288 }
289 } else if (g_flags.dump_kati_stamp) {
290 printf("wildcard %s: clean\n", gr->pat.c_str());
291 }
292 return needs_regen;
293 }
294
295 bool ShouldRunCommand(const ShellResult* sr) {
Dan Willemsenf06d8012016-10-03 00:16:07 -0700296 if (sr->op != CommandOp::FIND)
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900297 return true;
298
299 COLLECT_STATS("stat time (regen)");
300 for (const string& dir : sr->missing_dirs) {
301 if (Exists(dir))
302 return true;
303 }
304 for (const string& dir : sr->read_dirs) {
305 // We assume we rarely do a significant change for the top
306 // directory which affects the results of find command.
307 if (dir == "" || dir == "." || ShouldIgnoreDirty(dir))
308 continue;
309
310 struct stat st;
311 if (lstat(dir.c_str(), &st) != 0) {
312 return true;
313 }
314 double ts = GetTimestampFromStat(st);
315 if (gen_time_ < ts) {
316 return true;
317 }
318 if (S_ISLNK(st.st_mode)) {
319 ts = GetTimestamp(dir);
320 if (ts < 0 || gen_time_ < ts)
321 return true;
322 }
323 }
324 return false;
325 }
326
327 bool CheckShellResult(const ShellResult* sr, string* err) {
Dan Willemsenf06d8012016-10-03 00:16:07 -0700328 if (sr->op == CommandOp::READ_MISSING) {
329 if (Exists(sr->cmd)) {
330 if (g_flags.dump_kati_stamp)
331 printf("file %s: dirty\n", sr->cmd.c_str());
332 else
333 *err = StringPrintf("$(file <%s) was changed, regenerating...\n",
334 sr->cmd.c_str());
335 return true;
336 }
337 if (g_flags.dump_kati_stamp)
338 printf("file %s: clean\n", sr->cmd.c_str());
339 return false;
340 }
341
342 if (sr->op == CommandOp::READ) {
343 double ts = GetTimestamp(sr->cmd);
344 if (gen_time_ < ts) {
345 if (g_flags.dump_kati_stamp)
346 printf("file %s: dirty\n", sr->cmd.c_str());
347 else
348 *err = StringPrintf("$(file <%s) was changed, regenerating...\n",
349 sr->cmd.c_str());
350 return true;
351 }
352 if (g_flags.dump_kati_stamp)
353 printf("file %s: clean\n", sr->cmd.c_str());
354 return false;
355 }
356
357 if (sr->op == CommandOp::WRITE || sr->op == CommandOp::APPEND) {
358 FILE* f = fopen(sr->cmd.c_str(), (sr->op == CommandOp::WRITE) ? "wb" : "ab");
359 if (f == NULL) {
360 PERROR("fopen");
361 }
362
363 if (fwrite(&sr->result[0], sr->result.size(), 1, f) != 1) {
364 PERROR("fwrite");
365 }
366
367 if (fclose(f) != 0) {
368 PERROR("fclose");
369 }
370
371 if (g_flags.dump_kati_stamp)
372 printf("file %s: clean (write)\n", sr->cmd.c_str());
373 return false;
374 }
375
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900376 if (!ShouldRunCommand(sr)) {
Dan Willemsenf6486ce2016-09-16 19:21:36 -0700377 if (g_flags.regen_debug)
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900378 printf("shell %s: clean (no rerun)\n", sr->cmd.c_str());
379 return false;
380 }
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900381
382 FindCommand fc;
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900383 if (fc.Parse(sr->cmd) &&
384 !fc.chdir.empty() && ShouldIgnoreDirty(fc.chdir)) {
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900385 if (g_flags.dump_kati_stamp)
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900386 printf("shell %s: ignored\n", sr->cmd.c_str());
387 return false;
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900388 }
389
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900390 COLLECT_STATS_WITH_SLOW_REPORT("shell time (regen)", sr->cmd.c_str());
391 string result;
Stefan Beckerd4f28712016-04-07 13:29:36 +0300392 RunCommand(sr->shell, sr->cmd, RedirectStderr::DEV_NULL, &result);
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900393 FormatForCommandSubstitution(&result);
394 if (sr->result != result) {
395 if (g_flags.dump_kati_stamp) {
396 printf("shell %s: dirty\n", sr->cmd.c_str());
397 } else {
398 *err = StringPrintf("$(shell %s) was changed, regenerating...\n",
399 sr->cmd.c_str());
400 //*err += StringPrintf("%s => %s\n", expected.c_str(), result.c_str());
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900401 }
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900402 return true;
Dan Willemsenf6486ce2016-09-16 19:21:36 -0700403 } else if (g_flags.regen_debug) {
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900404 printf("shell %s: clean (rerun)\n", sr->cmd.c_str());
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900405 }
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900406 return false;
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900407 }
408
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900409 bool CheckStep2() {
410 unique_ptr<ThreadPool> tp(NewThreadPool(g_flags.num_jobs));
411
412 tp->Submit([this]() {
413 string err;
414 // TODO: Make glob cache thread safe and create a task for each glob.
415 for (GlobResult* gr : globs_) {
416 if (CheckGlobResult(gr, &err)) {
Shinichiro Hamaji8380fb82016-02-26 16:51:50 +0900417 unique_lock<mutex> lock(mu_);
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900418 if (!needs_regen_) {
419 needs_regen_ = true;
420 msg_ = err;
421 }
422 break;
423 }
424 }
425 });
426
427 for (ShellResult* sr : commands_) {
428 tp->Submit([this, sr]() {
429 string err;
430 if (CheckShellResult(sr, &err)) {
Shinichiro Hamaji8380fb82016-02-26 16:51:50 +0900431 unique_lock<mutex> lock(mu_);
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900432 if (!needs_regen_) {
433 needs_regen_ = true;
434 msg_ = err;
435 }
436 }
437 });
438 }
439
440 tp->Wait();
441 if (needs_regen_) {
442 fprintf(stderr, "%s", msg_.c_str());
443 }
444 return needs_regen_;
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900445 }
446
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900447 private:
448 double gen_time_;
449 vector<GlobResult*> globs_;
450 vector<ShellResult*> commands_;
Shinichiro Hamaji8380fb82016-02-26 16:51:50 +0900451 mutex mu_;
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900452 bool needs_regen_;
453 string msg_;
454};
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900455
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900456} // namespace
457
458bool NeedsRegen(double start_time, const string& orig_args) {
459 return StampChecker().NeedsRegen(start_time, orig_args);
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900460}