blob: f5ed50adba8c0ac790a8ffd187f8bb2dcfd879ea [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"
26#include "io.h"
27#include "log.h"
28#include "ninja.h"
29#include "stats.h"
30#include "strutil.h"
Shinichiro Hamaji702befc2016-01-27 17:21:39 +090031#include "thread_pool.h"
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +090032
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +090033namespace {
34
35#define RETURN_TRUE do { \
36 if (g_flags.dump_kati_stamp) \
37 needs_regen_ = true; \
38 else \
39 return true; \
40 } while (0)
41
42bool ShouldIgnoreDirty(StringPiece s) {
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +090043 Pattern pat(g_flags.ignore_dirty_pattern);
44 Pattern nopat(g_flags.no_ignore_dirty_pattern);
45 return pat.Match(s) && !nopat.Match(s);
46}
47
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +090048class StampChecker {
49 struct GlobResult {
50 string pat;
51 vector<string> result;
52 };
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +090053
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +090054 struct ShellResult {
Stefan Beckerd4f28712016-04-07 13:29:36 +030055 string shell;
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +090056 string cmd;
57 string result;
58 vector<string> missing_dirs;
59 vector<string> read_dirs;
60 bool has_condition;
61 };
62
63 public:
64 StampChecker()
65 : needs_regen_(false) {
66 }
67
68 ~StampChecker() {
69 for (GlobResult* gr : globs_) {
70 delete gr;
71 }
72 for (ShellResult* sr : commands_) {
73 delete sr;
74 }
75 }
76
77 bool NeedsRegen(double start_time, const string& orig_args) {
78 if (IsMissingOutputs())
79 RETURN_TRUE;
80
81 if (CheckStep1(orig_args))
82 RETURN_TRUE;
83
84 if (CheckStep2())
85 RETURN_TRUE;
86
87 if (!needs_regen_) {
88 FILE* fp = fopen(GetNinjaStampFilename().c_str(), "rb+");
89 if (!fp)
90 return true;
91 ScopedFile sfp(fp);
92 if (fseek(fp, 0, SEEK_SET) < 0)
93 PERROR("fseek");
94 size_t r = fwrite(&start_time, sizeof(start_time), 1, fp);
95 CHECK(r == 1);
96 }
97 return needs_regen_;
98 }
99
100 private:
101 bool IsMissingOutputs() {
102 if (!Exists(GetNinjaFilename())) {
103 fprintf(stderr, "%s is missing, regenerating...\n",
104 GetNinjaFilename().c_str());
105 return true;
106 }
107 if (!Exists(GetNinjaShellScriptFilename())) {
108 fprintf(stderr, "%s is missing, regenerating...\n",
109 GetNinjaShellScriptFilename().c_str());
110 return true;
111 }
112 return false;
113 }
114
115 bool CheckStep1(const string& orig_args) {
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900116#define LOAD_INT(fp) ({ \
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900117 int v = LoadInt(fp); \
118 if (v < 0) { \
119 fprintf(stderr, "incomplete kati_stamp, regenerating...\n"); \
120 RETURN_TRUE; \
121 } \
122 v; \
123 })
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900124
125#define LOAD_STRING(fp, s) ({ \
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900126 if (!LoadString(fp, s)) { \
127 fprintf(stderr, "incomplete kati_stamp, regenerating...\n"); \
128 RETURN_TRUE; \
129 } \
130 })
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900131
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900132 const string& stamp_filename = GetNinjaStampFilename();
133 FILE* fp = fopen(stamp_filename.c_str(), "rb");
134 if (!fp) {
Dan Willemsenf6486ce2016-09-16 19:21:36 -0700135 if (g_flags.regen_debug)
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900136 printf("%s: %s\n", stamp_filename.c_str(), strerror(errno));
137 return true;
138 }
139 ScopedFile sfp(fp);
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900140
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900141 double gen_time;
142 size_t r = fread(&gen_time, sizeof(gen_time), 1, fp);
143 gen_time_ = gen_time;
144 if (r != 1) {
145 fprintf(stderr, "incomplete kati_stamp, regenerating...\n");
146 RETURN_TRUE;
147 }
Dan Willemsenf6486ce2016-09-16 19:21:36 -0700148 if (g_flags.regen_debug)
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900149 printf("Generated time: %f\n", gen_time);
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900150
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900151 string s, s2;
152 int num_files = LOAD_INT(fp);
153 for (int i = 0; i < num_files; i++) {
154 LOAD_STRING(fp, &s);
155 double ts = GetTimestamp(s);
156 if (gen_time < ts) {
157 if (g_flags.regen_ignoring_kati_binary) {
158 string kati_binary;
159 GetExecutablePath(&kati_binary);
160 if (s == kati_binary) {
161 fprintf(stderr, "%s was modified, ignored.\n", s.c_str());
162 continue;
163 }
164 }
165 if (ShouldIgnoreDirty(s)) {
Dan Willemsenf6486ce2016-09-16 19:21:36 -0700166 if (g_flags.regen_debug)
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900167 printf("file %s: ignored (%f)\n", s.c_str(), ts);
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900168 continue;
169 }
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900170 if (g_flags.dump_kati_stamp)
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900171 printf("file %s: dirty (%f)\n", s.c_str(), ts);
172 else
173 fprintf(stderr, "%s was modified, regenerating...\n", s.c_str());
174 RETURN_TRUE;
175 } else if (g_flags.dump_kati_stamp) {
176 printf("file %s: clean (%f)\n", s.c_str(), ts);
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900177 }
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900178 }
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900179
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900180 int num_undefineds = LOAD_INT(fp);
181 for (int i = 0; i < num_undefineds; i++) {
182 LOAD_STRING(fp, &s);
183 if (getenv(s.c_str())) {
184 if (g_flags.dump_kati_stamp) {
185 printf("env %s: dirty (unset => %s)\n", s.c_str(), getenv(s.c_str()));
186 } else {
187 fprintf(stderr, "Environment variable %s was set, regenerating...\n",
188 s.c_str());
189 }
190 RETURN_TRUE;
191 } else if (g_flags.dump_kati_stamp) {
192 printf("env %s: clean (unset)\n", s.c_str());
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900193 }
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900194 }
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900195
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900196 int num_envs = LOAD_INT(fp);
197 for (int i = 0; i < num_envs; i++) {
198 LOAD_STRING(fp, &s);
199 StringPiece val(getenv(s.c_str()));
200 LOAD_STRING(fp, &s2);
201 if (val != s2) {
202 if (g_flags.dump_kati_stamp) {
203 printf("env %s: dirty (%s => %.*s)\n",
204 s.c_str(), s2.c_str(), SPF(val));
205 } else {
206 fprintf(stderr, "Environment variable %s was modified (%s => %.*s), "
207 "regenerating...\n",
208 s.c_str(), s2.c_str(), SPF(val));
209 }
210 RETURN_TRUE;
211 } else if (g_flags.dump_kati_stamp) {
212 printf("env %s: clean (%.*s)\n", s.c_str(), SPF(val));
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900213 }
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900214 }
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900215
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900216 int num_globs = LOAD_INT(fp);
217 string pat;
218 for (int i = 0; i < num_globs; i++) {
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900219 GlobResult* gr = new GlobResult;
220 globs_.push_back(gr);
221
222 LOAD_STRING(fp, &gr->pat);
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900223 int num_files = LOAD_INT(fp);
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900224 gr->result.resize(num_files);
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900225 for (int j = 0; j < num_files; j++) {
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900226 LOAD_STRING(fp, &gr->result[j]);
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900227 }
228 }
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900229
230 int num_crs = LOAD_INT(fp);
231 for (int i = 0; i < num_crs; i++) {
232 ShellResult* sr = new ShellResult;
233 commands_.push_back(sr);
Stefan Beckerd4f28712016-04-07 13:29:36 +0300234 LOAD_STRING(fp, &sr->shell);
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900235 LOAD_STRING(fp, &sr->cmd);
236 LOAD_STRING(fp, &sr->result);
237 sr->has_condition = LOAD_INT(fp);
238 if (!sr->has_condition)
239 continue;
240
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 }
251 }
252
253 LoadString(fp, &s);
254 if (orig_args != s) {
255 fprintf(stderr, "arguments changed, regenerating...\n");
256 RETURN_TRUE;
257 }
258
259 return needs_regen_;
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900260 }
261
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900262 bool CheckGlobResult(const GlobResult* gr, string* err) {
263 COLLECT_STATS("glob time (regen)");
264 vector<string>* files;
265 Glob(gr->pat.c_str(), &files);
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900266 bool needs_regen = files->size() != gr->result.size();
267 for (size_t i = 0; i < gr->result.size(); i++) {
268 if (!needs_regen) {
269 if ((*files)[i] != gr->result[i]) {
270 needs_regen = true;
271 break;
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900272 }
273 }
274 }
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900275 if (needs_regen) {
276 if (ShouldIgnoreDirty(gr->pat)) {
277 if (g_flags.dump_kati_stamp) {
278 printf("wildcard %s: ignored\n", gr->pat.c_str());
279 }
280 return false;
281 }
282 if (g_flags.dump_kati_stamp) {
283 printf("wildcard %s: dirty\n", gr->pat.c_str());
284 } else {
285 *err = StringPrintf("wildcard(%s) was changed, regenerating...\n",
286 gr->pat.c_str());
287 }
288 } else if (g_flags.dump_kati_stamp) {
289 printf("wildcard %s: clean\n", gr->pat.c_str());
290 }
291 return needs_regen;
292 }
293
294 bool ShouldRunCommand(const ShellResult* sr) {
295 if (!sr->has_condition)
296 return true;
297
298 COLLECT_STATS("stat time (regen)");
299 for (const string& dir : sr->missing_dirs) {
300 if (Exists(dir))
301 return true;
302 }
303 for (const string& dir : sr->read_dirs) {
304 // We assume we rarely do a significant change for the top
305 // directory which affects the results of find command.
306 if (dir == "" || dir == "." || ShouldIgnoreDirty(dir))
307 continue;
308
309 struct stat st;
310 if (lstat(dir.c_str(), &st) != 0) {
311 return true;
312 }
313 double ts = GetTimestampFromStat(st);
314 if (gen_time_ < ts) {
315 return true;
316 }
317 if (S_ISLNK(st.st_mode)) {
318 ts = GetTimestamp(dir);
319 if (ts < 0 || gen_time_ < ts)
320 return true;
321 }
322 }
323 return false;
324 }
325
326 bool CheckShellResult(const ShellResult* sr, string* err) {
327 if (!ShouldRunCommand(sr)) {
Dan Willemsenf6486ce2016-09-16 19:21:36 -0700328 if (g_flags.regen_debug)
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900329 printf("shell %s: clean (no rerun)\n", sr->cmd.c_str());
330 return false;
331 }
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900332
333 FindCommand fc;
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900334 if (fc.Parse(sr->cmd) &&
335 !fc.chdir.empty() && ShouldIgnoreDirty(fc.chdir)) {
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900336 if (g_flags.dump_kati_stamp)
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900337 printf("shell %s: ignored\n", sr->cmd.c_str());
338 return false;
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900339 }
340
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900341 COLLECT_STATS_WITH_SLOW_REPORT("shell time (regen)", sr->cmd.c_str());
342 string result;
Stefan Beckerd4f28712016-04-07 13:29:36 +0300343 RunCommand(sr->shell, sr->cmd, RedirectStderr::DEV_NULL, &result);
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900344 FormatForCommandSubstitution(&result);
345 if (sr->result != result) {
346 if (g_flags.dump_kati_stamp) {
347 printf("shell %s: dirty\n", sr->cmd.c_str());
348 } else {
349 *err = StringPrintf("$(shell %s) was changed, regenerating...\n",
350 sr->cmd.c_str());
351 //*err += StringPrintf("%s => %s\n", expected.c_str(), result.c_str());
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900352 }
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900353 return true;
Dan Willemsenf6486ce2016-09-16 19:21:36 -0700354 } else if (g_flags.regen_debug) {
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900355 printf("shell %s: clean (rerun)\n", sr->cmd.c_str());
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900356 }
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900357 return false;
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900358 }
359
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900360 bool CheckStep2() {
361 unique_ptr<ThreadPool> tp(NewThreadPool(g_flags.num_jobs));
362
363 tp->Submit([this]() {
364 string err;
365 // TODO: Make glob cache thread safe and create a task for each glob.
366 for (GlobResult* gr : globs_) {
367 if (CheckGlobResult(gr, &err)) {
Shinichiro Hamaji8380fb82016-02-26 16:51:50 +0900368 unique_lock<mutex> lock(mu_);
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900369 if (!needs_regen_) {
370 needs_regen_ = true;
371 msg_ = err;
372 }
373 break;
374 }
375 }
376 });
377
378 for (ShellResult* sr : commands_) {
379 tp->Submit([this, sr]() {
380 string err;
381 if (CheckShellResult(sr, &err)) {
Shinichiro Hamaji8380fb82016-02-26 16:51:50 +0900382 unique_lock<mutex> lock(mu_);
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900383 if (!needs_regen_) {
384 needs_regen_ = true;
385 msg_ = err;
386 }
387 }
388 });
389 }
390
391 tp->Wait();
392 if (needs_regen_) {
393 fprintf(stderr, "%s", msg_.c_str());
394 }
395 return needs_regen_;
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900396 }
397
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900398 private:
399 double gen_time_;
400 vector<GlobResult*> globs_;
401 vector<ShellResult*> commands_;
Shinichiro Hamaji8380fb82016-02-26 16:51:50 +0900402 mutex mu_;
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900403 bool needs_regen_;
404 string msg_;
405};
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900406
Shinichiro Hamaji6bbf9e22016-01-19 18:59:13 +0900407} // namespace
408
409bool NeedsRegen(double start_time, const string& orig_args) {
410 return StampChecker().NeedsRegen(start_time, orig_args);
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900411}