blob: 2c7cc73229a129dacec78d6430531c31c11b697c [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>
20
21#include "fileutil.h"
22#include "find.h"
23#include "io.h"
24#include "log.h"
25#include "ninja.h"
26#include "stats.h"
27#include "strutil.h"
28
29static bool ShouldIgnoreDirty(StringPiece s) {
30 Pattern pat(g_flags.ignore_dirty_pattern);
31 Pattern nopat(g_flags.no_ignore_dirty_pattern);
32 return pat.Match(s) && !nopat.Match(s);
33}
34
35bool NeedsRegen(double start_time, const string& orig_args) {
36 bool retval = false;
37#define RETURN_TRUE do { \
38 if (g_flags.dump_kati_stamp) \
39 retval = true; \
40 else \
41 return true; \
42 } while (0)
43
44#define LOAD_INT(fp) ({ \
45 int v = LoadInt(fp); \
46 if (v < 0) { \
47 fprintf(stderr, "incomplete kati_stamp, regenerating...\n"); \
48 RETURN_TRUE; \
49 } \
50 v; \
51 })
52
53#define LOAD_STRING(fp, s) ({ \
54 if (!LoadString(fp, s)) { \
55 fprintf(stderr, "incomplete kati_stamp, regenerating...\n"); \
56 RETURN_TRUE; \
57 } \
58 })
59
60 if (!Exists(GetNinjaFilename())) {
61 fprintf(stderr, "%s is missing, regenerating...\n",
62 GetNinjaFilename().c_str());
63 return true;
64 }
65 if (!Exists(GetNinjaShellScriptFilename())) {
66 fprintf(stderr, "%s is missing, regenerating...\n",
67 GetNinjaShellScriptFilename().c_str());
68 return true;
69 }
70
71 const string& stamp_filename = GetNinjaStampFilename();
72 FILE* fp = fopen(stamp_filename.c_str(), "rb+");
73 if (!fp) {
74 if (g_flags.dump_kati_stamp)
75 printf("%s: %s\n", stamp_filename.c_str(), strerror(errno));
76 return true;
77 }
78 ScopedFile sfp(fp);
79
80 double gen_time;
81 size_t r = fread(&gen_time, sizeof(gen_time), 1, fp);
82 if (r != 1) {
83 fprintf(stderr, "incomplete kati_stamp, regenerating...\n");
84 RETURN_TRUE;
85 }
86 if (g_flags.dump_kati_stamp)
87 printf("Generated time: %f\n", gen_time);
88
89 string s, s2;
90 int num_files = LOAD_INT(fp);
91 for (int i = 0; i < num_files; i++) {
92 LOAD_STRING(fp, &s);
93 double ts = GetTimestamp(s);
94 if (gen_time < ts) {
95 if (g_flags.regen_ignoring_kati_binary) {
96 string kati_binary;
97 GetExecutablePath(&kati_binary);
98 if (s == kati_binary) {
99 fprintf(stderr, "%s was modified, ignored.\n", s.c_str());
100 continue;
101 }
102 }
103 if (ShouldIgnoreDirty(s)) {
104 if (g_flags.dump_kati_stamp)
105 printf("file %s: ignored (%f)\n", s.c_str(), ts);
106 continue;
107 }
108 if (g_flags.dump_kati_stamp)
109 printf("file %s: dirty (%f)\n", s.c_str(), ts);
110 else
111 fprintf(stderr, "%s was modified, regenerating...\n", s.c_str());
112 RETURN_TRUE;
113 } else if (g_flags.dump_kati_stamp) {
114 printf("file %s: clean (%f)\n", s.c_str(), ts);
115 }
116 }
117
118 int num_undefineds = LOAD_INT(fp);
119 for (int i = 0; i < num_undefineds; i++) {
120 LOAD_STRING(fp, &s);
121 if (getenv(s.c_str())) {
122 if (g_flags.dump_kati_stamp) {
123 printf("env %s: dirty (unset => %s)\n", s.c_str(), getenv(s.c_str()));
124 } else {
125 fprintf(stderr, "Environment variable %s was set, regenerating...\n",
126 s.c_str());
127 }
128 RETURN_TRUE;
129 } else if (g_flags.dump_kati_stamp) {
130 printf("env %s: clean (unset)\n", s.c_str());
131 }
132 }
133
134 int num_envs = LOAD_INT(fp);
135 for (int i = 0; i < num_envs; i++) {
136 LOAD_STRING(fp, &s);
137 StringPiece val(getenv(s.c_str()));
138 LOAD_STRING(fp, &s2);
139 if (val != s2) {
140 if (g_flags.dump_kati_stamp) {
141 printf("env %s: dirty (%s => %.*s)\n",
142 s.c_str(), s2.c_str(), SPF(val));
143 } else {
144 fprintf(stderr, "Environment variable %s was modified (%s => %.*s), "
145 "regenerating...\n",
146 s.c_str(), s2.c_str(), SPF(val));
147 }
148 RETURN_TRUE;
149 } else if (g_flags.dump_kati_stamp) {
150 printf("env %s: clean (%.*s)\n", s.c_str(), SPF(val));
151 }
152 }
153
154 {
155 int num_globs = LOAD_INT(fp);
156 string pat;
157 for (int i = 0; i < num_globs; i++) {
158 COLLECT_STATS("glob time (regen)");
159 LOAD_STRING(fp, &pat);
160#if 0
161 bool needs_reglob = false;
162 int num_dirs = LOAD_INT(fp);
163 for (int j = 0; j < num_dirs; j++) {
164 LOAD_STRING(fp, &s);
165 // TODO: Handle removed files properly.
166 needs_reglob |= gen_time < GetTimestamp(s);
167 }
168#endif
169 int num_files = LOAD_INT(fp);
170 vector<string>* files;
171 Glob(pat.c_str(), &files);
172 sort(files->begin(), files->end());
173 bool needs_regen = files->size() != static_cast<size_t>(num_files);
174 for (int j = 0; j < num_files; j++) {
175 LOAD_STRING(fp, &s);
176 if (!needs_regen) {
177 if ((*files)[j] != s) {
178 needs_regen = true;
179 break;
180 }
181 }
182 }
183 if (needs_regen) {
184 if (ShouldIgnoreDirty(pat)) {
185 if (g_flags.dump_kati_stamp) {
186 printf("wildcard %s: ignored\n", pat.c_str());
187 }
188 continue;
189 }
190 if (g_flags.dump_kati_stamp) {
191 printf("wildcard %s: dirty\n", pat.c_str());
192 } else {
193 fprintf(stderr, "wildcard(%s) was changed, regenerating...\n",
194 pat.c_str());
195 }
196 RETURN_TRUE;
197 } else if (g_flags.dump_kati_stamp) {
198 printf("wildcard %s: clean\n", pat.c_str());
199 }
200 }
201 }
202
203 int num_crs = LOAD_INT(fp);
204 for (int i = 0; i < num_crs; i++) {
205 string cmd, expected;
206 LOAD_STRING(fp, &cmd);
207 LOAD_STRING(fp, &expected);
208
209 {
210 COLLECT_STATS("stat time (regen)");
211 bool has_condition = LOAD_INT(fp);
212 if (has_condition) {
213 bool should_run_command = false;
214
215 int num_missing_dirs = LOAD_INT(fp);
216 for (int j = 0; j < num_missing_dirs; j++) {
217 LOAD_STRING(fp, &s);
218 should_run_command |= Exists(s);
219 }
220
221 int num_read_dirs = LOAD_INT(fp);
222 for (int j = 0; j < num_read_dirs; j++) {
223 LOAD_STRING(fp, &s);
224 // We assume we rarely do a significant change for the top
225 // directory which affects the results of find command.
226 if (s == "" || s == "." || ShouldIgnoreDirty(s))
227 continue;
228
229 struct stat st;
230 if (lstat(s.c_str(), &st) != 0) {
231 should_run_command = true;
232 continue;
233 }
234 double ts = GetTimestampFromStat(st);
235 if (gen_time < ts) {
236 should_run_command = true;
237 continue;
238 }
239 if (S_ISLNK(st.st_mode)) {
240 ts = GetTimestamp(s);
241 should_run_command |= (ts < 0 || gen_time < ts);
242 }
243 }
244
245 if (!should_run_command) {
246 if (g_flags.dump_kati_stamp)
247 printf("shell %s: clean (no rerun)\n", cmd.c_str());
248 continue;
249 }
250 }
251 }
252
253 FindCommand fc;
254 if (fc.Parse(cmd) && !fc.chdir.empty() && ShouldIgnoreDirty(fc.chdir)) {
255 if (g_flags.dump_kati_stamp)
256 printf("shell %s: ignored\n", cmd.c_str());
257 continue;
258 }
259
260 {
261 COLLECT_STATS_WITH_SLOW_REPORT("shell time (regen)", cmd.c_str());
262 string result;
263 RunCommand("/bin/sh", cmd, RedirectStderr::DEV_NULL, &result);
264 FormatForCommandSubstitution(&result);
265 if (expected != result) {
266 if (g_flags.dump_kati_stamp) {
267 printf("shell %s: dirty\n", cmd.c_str());
268 } else {
269 fprintf(stderr, "$(shell %s) was changed, regenerating...\n",
270 cmd.c_str());
271#if 0
272 fprintf(stderr, "%s => %s\n",
273 expected.c_str(), result.c_str());
274#endif
275 }
276 RETURN_TRUE;
277 } else if (g_flags.dump_kati_stamp) {
278 printf("shell %s: clean (rerun)\n", cmd.c_str());
279 }
280 }
281 }
282
283 LoadString(fp, &s);
284 if (orig_args != s) {
285 fprintf(stderr, "arguments changed, regenerating...\n");
286 RETURN_TRUE;
287 }
288
289 if (!retval) {
290 if (fseek(fp, 0, SEEK_SET) < 0)
291 PERROR("fseek");
292 size_t r = fwrite(&start_time, sizeof(start_time), 1, fp);
293 CHECK(r == 1);
294 }
295
296 return retval;
297}