1 // Copyright 2011 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 "clean.h"
16 #include "build.h"
17
18 #include "util.h"
19 #include "test.h"
20
21 #ifndef _WIN32
22 #include <unistd.h>
23 #endif
24
25 namespace {
26
27 const char kTestFilename[] = "CleanTest-tempfile";
28
29 struct CleanTest : public StateTestWithBuiltinRules {
30 VirtualFileSystem fs_;
31 BuildConfig config_;
SetUp__anon2dae0e730111::CleanTest32 virtual void SetUp() {
33 config_.verbosity = BuildConfig::QUIET;
34 }
35 };
36
TEST_F(CleanTest,CleanAll)37 TEST_F(CleanTest, CleanAll) {
38 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
39 "build in1: cat src1\n"
40 "build out1: cat in1\n"
41 "build in2: cat src2\n"
42 "build out2: cat in2\n"));
43 fs_.Create("in1", "");
44 fs_.Create("out1", "");
45 fs_.Create("in2", "");
46 fs_.Create("out2", "");
47
48 Cleaner cleaner(&state_, config_, &fs_);
49
50 ASSERT_EQ(0, cleaner.cleaned_files_count());
51 EXPECT_EQ(0, cleaner.CleanAll());
52 EXPECT_EQ(4, cleaner.cleaned_files_count());
53 EXPECT_EQ(4u, fs_.files_removed_.size());
54
55 // Check they are removed.
56 string err;
57 EXPECT_EQ(0, fs_.Stat("in1", &err));
58 EXPECT_EQ(0, fs_.Stat("out1", &err));
59 EXPECT_EQ(0, fs_.Stat("in2", &err));
60 EXPECT_EQ(0, fs_.Stat("out2", &err));
61 fs_.files_removed_.clear();
62
63 EXPECT_EQ(0, cleaner.CleanAll());
64 EXPECT_EQ(0, cleaner.cleaned_files_count());
65 EXPECT_EQ(0u, fs_.files_removed_.size());
66 }
67
TEST_F(CleanTest,CleanAllDryRun)68 TEST_F(CleanTest, CleanAllDryRun) {
69 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
70 "build in1: cat src1\n"
71 "build out1: cat in1\n"
72 "build in2: cat src2\n"
73 "build out2: cat in2\n"));
74 fs_.Create("in1", "");
75 fs_.Create("out1", "");
76 fs_.Create("in2", "");
77 fs_.Create("out2", "");
78
79 config_.dry_run = true;
80 Cleaner cleaner(&state_, config_, &fs_);
81
82 ASSERT_EQ(0, cleaner.cleaned_files_count());
83 EXPECT_EQ(0, cleaner.CleanAll());
84 EXPECT_EQ(4, cleaner.cleaned_files_count());
85 EXPECT_EQ(0u, fs_.files_removed_.size());
86
87 // Check they are not removed.
88 string err;
89 EXPECT_LT(0, fs_.Stat("in1", &err));
90 EXPECT_LT(0, fs_.Stat("out1", &err));
91 EXPECT_LT(0, fs_.Stat("in2", &err));
92 EXPECT_LT(0, fs_.Stat("out2", &err));
93 fs_.files_removed_.clear();
94
95 EXPECT_EQ(0, cleaner.CleanAll());
96 EXPECT_EQ(4, cleaner.cleaned_files_count());
97 EXPECT_EQ(0u, fs_.files_removed_.size());
98 }
99
TEST_F(CleanTest,CleanTarget)100 TEST_F(CleanTest, CleanTarget) {
101 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
102 "build in1: cat src1\n"
103 "build out1: cat in1\n"
104 "build in2: cat src2\n"
105 "build out2: cat in2\n"));
106 fs_.Create("in1", "");
107 fs_.Create("out1", "");
108 fs_.Create("in2", "");
109 fs_.Create("out2", "");
110
111 Cleaner cleaner(&state_, config_, &fs_);
112
113 ASSERT_EQ(0, cleaner.cleaned_files_count());
114 ASSERT_EQ(0, cleaner.CleanTarget("out1"));
115 EXPECT_EQ(2, cleaner.cleaned_files_count());
116 EXPECT_EQ(2u, fs_.files_removed_.size());
117
118 // Check they are removed.
119 string err;
120 EXPECT_EQ(0, fs_.Stat("in1", &err));
121 EXPECT_EQ(0, fs_.Stat("out1", &err));
122 EXPECT_LT(0, fs_.Stat("in2", &err));
123 EXPECT_LT(0, fs_.Stat("out2", &err));
124 fs_.files_removed_.clear();
125
126 ASSERT_EQ(0, cleaner.CleanTarget("out1"));
127 EXPECT_EQ(0, cleaner.cleaned_files_count());
128 EXPECT_EQ(0u, fs_.files_removed_.size());
129 }
130
TEST_F(CleanTest,CleanTargetDryRun)131 TEST_F(CleanTest, CleanTargetDryRun) {
132 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
133 "build in1: cat src1\n"
134 "build out1: cat in1\n"
135 "build in2: cat src2\n"
136 "build out2: cat in2\n"));
137 fs_.Create("in1", "");
138 fs_.Create("out1", "");
139 fs_.Create("in2", "");
140 fs_.Create("out2", "");
141
142 config_.dry_run = true;
143 Cleaner cleaner(&state_, config_, &fs_);
144
145 ASSERT_EQ(0, cleaner.cleaned_files_count());
146 ASSERT_EQ(0, cleaner.CleanTarget("out1"));
147 EXPECT_EQ(2, cleaner.cleaned_files_count());
148 EXPECT_EQ(0u, fs_.files_removed_.size());
149
150 // Check they are not removed.
151 string err;
152 EXPECT_LT(0, fs_.Stat("in1", &err));
153 EXPECT_LT(0, fs_.Stat("out1", &err));
154 EXPECT_LT(0, fs_.Stat("in2", &err));
155 EXPECT_LT(0, fs_.Stat("out2", &err));
156 fs_.files_removed_.clear();
157
158 ASSERT_EQ(0, cleaner.CleanTarget("out1"));
159 EXPECT_EQ(2, cleaner.cleaned_files_count());
160 EXPECT_EQ(0u, fs_.files_removed_.size());
161 }
162
TEST_F(CleanTest,CleanRule)163 TEST_F(CleanTest, CleanRule) {
164 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
165 "rule cat_e\n"
166 " command = cat -e $in > $out\n"
167 "build in1: cat_e src1\n"
168 "build out1: cat in1\n"
169 "build in2: cat_e src2\n"
170 "build out2: cat in2\n"));
171 fs_.Create("in1", "");
172 fs_.Create("out1", "");
173 fs_.Create("in2", "");
174 fs_.Create("out2", "");
175
176 Cleaner cleaner(&state_, config_, &fs_);
177
178 ASSERT_EQ(0, cleaner.cleaned_files_count());
179 ASSERT_EQ(0, cleaner.CleanRule("cat_e"));
180 EXPECT_EQ(2, cleaner.cleaned_files_count());
181 EXPECT_EQ(2u, fs_.files_removed_.size());
182
183 // Check they are removed.
184 string err;
185 EXPECT_EQ(0, fs_.Stat("in1", &err));
186 EXPECT_LT(0, fs_.Stat("out1", &err));
187 EXPECT_EQ(0, fs_.Stat("in2", &err));
188 EXPECT_LT(0, fs_.Stat("out2", &err));
189 fs_.files_removed_.clear();
190
191 ASSERT_EQ(0, cleaner.CleanRule("cat_e"));
192 EXPECT_EQ(0, cleaner.cleaned_files_count());
193 EXPECT_EQ(0u, fs_.files_removed_.size());
194 }
195
TEST_F(CleanTest,CleanRuleDryRun)196 TEST_F(CleanTest, CleanRuleDryRun) {
197 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
198 "rule cat_e\n"
199 " command = cat -e $in > $out\n"
200 "build in1: cat_e src1\n"
201 "build out1: cat in1\n"
202 "build in2: cat_e src2\n"
203 "build out2: cat in2\n"));
204 fs_.Create("in1", "");
205 fs_.Create("out1", "");
206 fs_.Create("in2", "");
207 fs_.Create("out2", "");
208
209 config_.dry_run = true;
210 Cleaner cleaner(&state_, config_, &fs_);
211
212 ASSERT_EQ(0, cleaner.cleaned_files_count());
213 ASSERT_EQ(0, cleaner.CleanRule("cat_e"));
214 EXPECT_EQ(2, cleaner.cleaned_files_count());
215 EXPECT_EQ(0u, fs_.files_removed_.size());
216
217 // Check they are not removed.
218 string err;
219 EXPECT_LT(0, fs_.Stat("in1", &err));
220 EXPECT_LT(0, fs_.Stat("out1", &err));
221 EXPECT_LT(0, fs_.Stat("in2", &err));
222 EXPECT_LT(0, fs_.Stat("out2", &err));
223 fs_.files_removed_.clear();
224
225 ASSERT_EQ(0, cleaner.CleanRule("cat_e"));
226 EXPECT_EQ(2, cleaner.cleaned_files_count());
227 EXPECT_EQ(0u, fs_.files_removed_.size());
228 }
229
TEST_F(CleanTest,CleanRuleGenerator)230 TEST_F(CleanTest, CleanRuleGenerator) {
231 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
232 "rule regen\n"
233 " command = cat $in > $out\n"
234 " generator = 1\n"
235 "build out1: cat in1\n"
236 "build out2: regen in2\n"));
237 fs_.Create("out1", "");
238 fs_.Create("out2", "");
239
240 Cleaner cleaner(&state_, config_, &fs_);
241 EXPECT_EQ(0, cleaner.CleanAll());
242 EXPECT_EQ(1, cleaner.cleaned_files_count());
243 EXPECT_EQ(1u, fs_.files_removed_.size());
244
245 fs_.Create("out1", "");
246
247 EXPECT_EQ(0, cleaner.CleanAll(/*generator=*/true));
248 EXPECT_EQ(2, cleaner.cleaned_files_count());
249 EXPECT_EQ(2u, fs_.files_removed_.size());
250 }
251
TEST_F(CleanTest,CleanDepFile)252 TEST_F(CleanTest, CleanDepFile) {
253 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
254 "rule cc\n"
255 " command = cc $in > $out\n"
256 " depfile = $out.d\n"
257 "build out1: cc in1\n"));
258 fs_.Create("out1", "");
259 fs_.Create("out1.d", "");
260
261 Cleaner cleaner(&state_, config_, &fs_);
262 EXPECT_EQ(0, cleaner.CleanAll());
263 EXPECT_EQ(2, cleaner.cleaned_files_count());
264 EXPECT_EQ(2u, fs_.files_removed_.size());
265 }
266
TEST_F(CleanTest,CleanDepFileOnCleanTarget)267 TEST_F(CleanTest, CleanDepFileOnCleanTarget) {
268 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
269 "rule cc\n"
270 " command = cc $in > $out\n"
271 " depfile = $out.d\n"
272 "build out1: cc in1\n"));
273 fs_.Create("out1", "");
274 fs_.Create("out1.d", "");
275
276 Cleaner cleaner(&state_, config_, &fs_);
277 EXPECT_EQ(0, cleaner.CleanTarget("out1"));
278 EXPECT_EQ(2, cleaner.cleaned_files_count());
279 EXPECT_EQ(2u, fs_.files_removed_.size());
280 }
281
TEST_F(CleanTest,CleanDepFileOnCleanRule)282 TEST_F(CleanTest, CleanDepFileOnCleanRule) {
283 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
284 "rule cc\n"
285 " command = cc $in > $out\n"
286 " depfile = $out.d\n"
287 "build out1: cc in1\n"));
288 fs_.Create("out1", "");
289 fs_.Create("out1.d", "");
290
291 Cleaner cleaner(&state_, config_, &fs_);
292 EXPECT_EQ(0, cleaner.CleanRule("cc"));
293 EXPECT_EQ(2, cleaner.cleaned_files_count());
294 EXPECT_EQ(2u, fs_.files_removed_.size());
295 }
296
TEST_F(CleanTest,CleanDyndep)297 TEST_F(CleanTest, CleanDyndep) {
298 // Verify that a dyndep file can be loaded to discover a new output
299 // to be cleaned.
300 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
301 "build out: cat in || dd\n"
302 " dyndep = dd\n"
303 ));
304 fs_.Create("in", "");
305 fs_.Create("dd",
306 "ninja_dyndep_version = 1\n"
307 "build out | out.imp: dyndep\n"
308 );
309 fs_.Create("out", "");
310 fs_.Create("out.imp", "");
311
312 Cleaner cleaner(&state_, config_, &fs_);
313
314 ASSERT_EQ(0, cleaner.cleaned_files_count());
315 EXPECT_EQ(0, cleaner.CleanAll());
316 EXPECT_EQ(2, cleaner.cleaned_files_count());
317 EXPECT_EQ(2u, fs_.files_removed_.size());
318
319 string err;
320 EXPECT_EQ(0, fs_.Stat("out", &err));
321 EXPECT_EQ(0, fs_.Stat("out.imp", &err));
322 }
323
TEST_F(CleanTest,CleanDyndepMissing)324 TEST_F(CleanTest, CleanDyndepMissing) {
325 // Verify that a missing dyndep file is tolerated.
326 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
327 "build out: cat in || dd\n"
328 " dyndep = dd\n"
329 ));
330 fs_.Create("in", "");
331 fs_.Create("out", "");
332 fs_.Create("out.imp", "");
333
334 Cleaner cleaner(&state_, config_, &fs_);
335
336 ASSERT_EQ(0, cleaner.cleaned_files_count());
337 EXPECT_EQ(0, cleaner.CleanAll());
338 EXPECT_EQ(1, cleaner.cleaned_files_count());
339 EXPECT_EQ(1u, fs_.files_removed_.size());
340
341 string err;
342 EXPECT_EQ(0, fs_.Stat("out", &err));
343 EXPECT_EQ(1, fs_.Stat("out.imp", &err));
344 }
345
TEST_F(CleanTest,CleanRspFile)346 TEST_F(CleanTest, CleanRspFile) {
347 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
348 "rule cc\n"
349 " command = cc $in > $out\n"
350 " rspfile = $rspfile\n"
351 " rspfile_content=$in\n"
352 "build out1: cc in1\n"
353 " rspfile = cc1.rsp\n"));
354 fs_.Create("out1", "");
355 fs_.Create("cc1.rsp", "");
356
357 Cleaner cleaner(&state_, config_, &fs_);
358 EXPECT_EQ(0, cleaner.CleanAll());
359 EXPECT_EQ(2, cleaner.cleaned_files_count());
360 EXPECT_EQ(2u, fs_.files_removed_.size());
361 }
362
TEST_F(CleanTest,CleanRsp)363 TEST_F(CleanTest, CleanRsp) {
364 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
365 "rule cat_rsp \n"
366 " command = cat $rspfile > $out\n"
367 " rspfile = $rspfile\n"
368 " rspfile_content = $in\n"
369 "build in1: cat src1\n"
370 "build out1: cat in1\n"
371 "build in2: cat_rsp src2\n"
372 " rspfile=in2.rsp\n"
373 "build out2: cat_rsp in2\n"
374 " rspfile=out2.rsp\n"
375 ));
376 fs_.Create("in1", "");
377 fs_.Create("out1", "");
378 fs_.Create("in2.rsp", "");
379 fs_.Create("out2.rsp", "");
380 fs_.Create("in2", "");
381 fs_.Create("out2", "");
382
383 Cleaner cleaner(&state_, config_, &fs_);
384 ASSERT_EQ(0, cleaner.cleaned_files_count());
385 ASSERT_EQ(0, cleaner.CleanTarget("out1"));
386 EXPECT_EQ(2, cleaner.cleaned_files_count());
387 ASSERT_EQ(0, cleaner.CleanTarget("in2"));
388 EXPECT_EQ(2, cleaner.cleaned_files_count());
389 ASSERT_EQ(0, cleaner.CleanRule("cat_rsp"));
390 EXPECT_EQ(2, cleaner.cleaned_files_count());
391
392 EXPECT_EQ(6u, fs_.files_removed_.size());
393
394 // Check they are removed.
395 string err;
396 EXPECT_EQ(0, fs_.Stat("in1", &err));
397 EXPECT_EQ(0, fs_.Stat("out1", &err));
398 EXPECT_EQ(0, fs_.Stat("in2", &err));
399 EXPECT_EQ(0, fs_.Stat("out2", &err));
400 EXPECT_EQ(0, fs_.Stat("in2.rsp", &err));
401 EXPECT_EQ(0, fs_.Stat("out2.rsp", &err));
402 }
403
TEST_F(CleanTest,CleanFailure)404 TEST_F(CleanTest, CleanFailure) {
405 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
406 "build dir: cat src1\n"));
407 fs_.MakeDir("dir");
408 Cleaner cleaner(&state_, config_, &fs_);
409 EXPECT_NE(0, cleaner.CleanAll());
410 }
411
TEST_F(CleanTest,CleanPhony)412 TEST_F(CleanTest, CleanPhony) {
413 string err;
414 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
415 "build phony: phony t1 t2\n"
416 "build t1: cat\n"
417 "build t2: cat\n"));
418
419 fs_.Create("phony", "");
420 fs_.Create("t1", "");
421 fs_.Create("t2", "");
422
423 // Check that CleanAll does not remove "phony".
424 Cleaner cleaner(&state_, config_, &fs_);
425 EXPECT_EQ(0, cleaner.CleanAll());
426 EXPECT_EQ(2, cleaner.cleaned_files_count());
427 EXPECT_LT(0, fs_.Stat("phony", &err));
428
429 fs_.Create("t1", "");
430 fs_.Create("t2", "");
431
432 // Check that CleanTarget does not remove "phony".
433 EXPECT_EQ(0, cleaner.CleanTarget("phony"));
434 EXPECT_EQ(2, cleaner.cleaned_files_count());
435 EXPECT_LT(0, fs_.Stat("phony", &err));
436 }
437
TEST_F(CleanTest,CleanDepFileAndRspFileWithSpaces)438 TEST_F(CleanTest, CleanDepFileAndRspFileWithSpaces) {
439 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
440 "rule cc_dep\n"
441 " command = cc $in > $out\n"
442 " depfile = $out.d\n"
443 "rule cc_rsp\n"
444 " command = cc $in > $out\n"
445 " rspfile = $out.rsp\n"
446 " rspfile_content = $in\n"
447 "build out$ 1: cc_dep in$ 1\n"
448 "build out$ 2: cc_rsp in$ 1\n"
449 ));
450 fs_.Create("out 1", "");
451 fs_.Create("out 2", "");
452 fs_.Create("out 1.d", "");
453 fs_.Create("out 2.rsp", "");
454
455 Cleaner cleaner(&state_, config_, &fs_);
456 EXPECT_EQ(0, cleaner.CleanAll());
457 EXPECT_EQ(4, cleaner.cleaned_files_count());
458 EXPECT_EQ(4u, fs_.files_removed_.size());
459
460 string err;
461 EXPECT_EQ(0, fs_.Stat("out 1", &err));
462 EXPECT_EQ(0, fs_.Stat("out 2", &err));
463 EXPECT_EQ(0, fs_.Stat("out 1.d", &err));
464 EXPECT_EQ(0, fs_.Stat("out 2.rsp", &err));
465 }
466
467 struct CleanDeadTest : public CleanTest, public BuildLogUser{
SetUp__anon2dae0e730111::CleanDeadTest468 virtual void SetUp() {
469 // In case a crashing test left a stale file behind.
470 unlink(kTestFilename);
471 CleanTest::SetUp();
472 }
TearDown__anon2dae0e730111::CleanDeadTest473 virtual void TearDown() {
474 unlink(kTestFilename);
475 }
IsPathDead__anon2dae0e730111::CleanDeadTest476 virtual bool IsPathDead(StringPiece) const { return false; }
477 };
478
TEST_F(CleanDeadTest,CleanDead)479 TEST_F(CleanDeadTest, CleanDead) {
480 State state;
481 ASSERT_NO_FATAL_FAILURE(AssertParse(&state,
482 "rule cat\n"
483 " command = cat $in > $out\n"
484 "build out1: cat in\n"
485 "build out2: cat in\n"
486 ));
487 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
488 "build out2: cat in\n"
489 ));
490 fs_.Create("in", "");
491 fs_.Create("out1", "");
492 fs_.Create("out2", "");
493
494 BuildLog log1;
495 string err;
496 EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err));
497 ASSERT_EQ("", err);
498 log1.RecordCommand(state.edges_[0], 15, 18);
499 log1.RecordCommand(state.edges_[1], 20, 25);
500 log1.Close();
501
502 BuildLog log2;
503 EXPECT_TRUE(log2.Load(kTestFilename, &err));
504 ASSERT_EQ("", err);
505 ASSERT_EQ(2u, log2.entries().size());
506 ASSERT_TRUE(log2.LookupByOutput("out1"));
507 ASSERT_TRUE(log2.LookupByOutput("out2"));
508
509 // First use the manifest that describe how to build out1.
510 Cleaner cleaner1(&state, config_, &fs_);
511 EXPECT_EQ(0, cleaner1.CleanDead(log2.entries()));
512 EXPECT_EQ(0, cleaner1.cleaned_files_count());
513 EXPECT_EQ(0u, fs_.files_removed_.size());
514 EXPECT_NE(0, fs_.Stat("in", &err));
515 EXPECT_NE(0, fs_.Stat("out1", &err));
516 EXPECT_NE(0, fs_.Stat("out2", &err));
517
518 // Then use the manifest that does not build out1 anymore.
519 Cleaner cleaner2(&state_, config_, &fs_);
520 EXPECT_EQ(0, cleaner2.CleanDead(log2.entries()));
521 EXPECT_EQ(1, cleaner2.cleaned_files_count());
522 EXPECT_EQ(1u, fs_.files_removed_.size());
523 EXPECT_EQ("out1", *(fs_.files_removed_.begin()));
524 EXPECT_NE(0, fs_.Stat("in", &err));
525 EXPECT_EQ(0, fs_.Stat("out1", &err));
526 EXPECT_NE(0, fs_.Stat("out2", &err));
527
528 // Nothing to do now.
529 EXPECT_EQ(0, cleaner2.CleanDead(log2.entries()));
530 EXPECT_EQ(0, cleaner2.cleaned_files_count());
531 EXPECT_EQ(1u, fs_.files_removed_.size());
532 EXPECT_EQ("out1", *(fs_.files_removed_.begin()));
533 EXPECT_NE(0, fs_.Stat("in", &err));
534 EXPECT_EQ(0, fs_.Stat("out1", &err));
535 EXPECT_NE(0, fs_.Stat("out2", &err));
536 log2.Close();
537 }
538 } // anonymous namespace
539