• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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__anon78c7974f0111::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__anon78c7974f0111::CleanDeadTest468   virtual void SetUp() {
469     // In case a crashing test left a stale file behind.
470     unlink(kTestFilename);
471     CleanTest::SetUp();
472   }
TearDown__anon78c7974f0111::CleanDeadTest473   virtual void TearDown() {
474     unlink(kTestFilename);
475   }
IsPathDead__anon78c7974f0111::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