Imported Upstream version 1.9.0
[platform/upstream/ninja.git] / src / clean.cc
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
17 #include <assert.h>
18 #include <stdio.h>
19
20 #include "disk_interface.h"
21 #include "graph.h"
22 #include "state.h"
23 #include "util.h"
24
25 Cleaner::Cleaner(State* state, const BuildConfig& config)
26   : state_(state),
27     config_(config),
28     removed_(),
29     cleaned_(),
30     cleaned_files_count_(0),
31     disk_interface_(new RealDiskInterface),
32     status_(0) {
33 }
34
35 Cleaner::Cleaner(State* state,
36                  const BuildConfig& config,
37                  DiskInterface* disk_interface)
38   : state_(state),
39     config_(config),
40     removed_(),
41     cleaned_(),
42     cleaned_files_count_(0),
43     disk_interface_(disk_interface),
44     status_(0) {
45 }
46
47 int Cleaner::RemoveFile(const string& path) {
48   return disk_interface_->RemoveFile(path);
49 }
50
51 bool Cleaner::FileExists(const string& path) {
52   string err;
53   TimeStamp mtime = disk_interface_->Stat(path, &err);
54   if (mtime == -1)
55     Error("%s", err.c_str());
56   return mtime > 0;  // Treat Stat() errors as "file does not exist".
57 }
58
59 void Cleaner::Report(const string& path) {
60   ++cleaned_files_count_;
61   if (IsVerbose())
62     printf("Remove %s\n", path.c_str());
63 }
64
65 void Cleaner::Remove(const string& path) {
66   if (!IsAlreadyRemoved(path)) {
67     removed_.insert(path);
68     if (config_.dry_run) {
69       if (FileExists(path))
70         Report(path);
71     } else {
72       int ret = RemoveFile(path);
73       if (ret == 0)
74         Report(path);
75       else if (ret == -1)
76         status_ = 1;
77     }
78   }
79 }
80
81 bool Cleaner::IsAlreadyRemoved(const string& path) {
82   set<string>::iterator i = removed_.find(path);
83   return (i != removed_.end());
84 }
85
86 void Cleaner::RemoveEdgeFiles(Edge* edge) {
87   string depfile = edge->GetUnescapedDepfile();
88   if (!depfile.empty())
89     Remove(depfile);
90
91   string rspfile = edge->GetUnescapedRspfile();
92   if (!rspfile.empty())
93     Remove(rspfile);
94 }
95
96 void Cleaner::PrintHeader() {
97   if (config_.verbosity == BuildConfig::QUIET)
98     return;
99   printf("Cleaning...");
100   if (IsVerbose())
101     printf("\n");
102   else
103     printf(" ");
104   fflush(stdout);
105 }
106
107 void Cleaner::PrintFooter() {
108   if (config_.verbosity == BuildConfig::QUIET)
109     return;
110   printf("%d files.\n", cleaned_files_count_);
111 }
112
113 int Cleaner::CleanAll(bool generator) {
114   Reset();
115   PrintHeader();
116   for (vector<Edge*>::iterator e = state_->edges_.begin();
117        e != state_->edges_.end(); ++e) {
118     // Do not try to remove phony targets
119     if ((*e)->is_phony())
120       continue;
121     // Do not remove generator's files unless generator specified.
122     if (!generator && (*e)->GetBindingBool("generator"))
123       continue;
124     for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
125          out_node != (*e)->outputs_.end(); ++out_node) {
126       Remove((*out_node)->path());
127     }
128
129     RemoveEdgeFiles(*e);
130   }
131   PrintFooter();
132   return status_;
133 }
134
135 void Cleaner::DoCleanTarget(Node* target) {
136   if (Edge* e = target->in_edge()) {
137     // Do not try to remove phony targets
138     if (!e->is_phony()) {
139       Remove(target->path());
140       RemoveEdgeFiles(e);
141     }
142     for (vector<Node*>::iterator n = e->inputs_.begin(); n != e->inputs_.end();
143          ++n) {
144       Node* next = *n;
145       // call DoCleanTarget recursively if this node has not been visited
146       if (cleaned_.count(next) == 0) {
147         DoCleanTarget(next);
148       }
149     }
150   }
151
152   // mark this target to be cleaned already
153   cleaned_.insert(target);
154 }
155
156 int Cleaner::CleanTarget(Node* target) {
157   assert(target);
158
159   Reset();
160   PrintHeader();
161   DoCleanTarget(target);
162   PrintFooter();
163   return status_;
164 }
165
166 int Cleaner::CleanTarget(const char* target) {
167   assert(target);
168
169   Reset();
170   Node* node = state_->LookupNode(target);
171   if (node) {
172     CleanTarget(node);
173   } else {
174     Error("unknown target '%s'", target);
175     status_ = 1;
176   }
177   return status_;
178 }
179
180 int Cleaner::CleanTargets(int target_count, char* targets[]) {
181   Reset();
182   PrintHeader();
183   for (int i = 0; i < target_count; ++i) {
184     string target_name = targets[i];
185     uint64_t slash_bits;
186     string err;
187     if (!CanonicalizePath(&target_name, &slash_bits, &err)) {
188       Error("failed to canonicalize '%s': %s", target_name.c_str(), err.c_str());
189       status_ = 1;
190     } else {
191       Node* target = state_->LookupNode(target_name);
192       if (target) {
193         if (IsVerbose())
194           printf("Target %s\n", target_name.c_str());
195         DoCleanTarget(target);
196       } else {
197         Error("unknown target '%s'", target_name.c_str());
198         status_ = 1;
199       }
200     }
201   }
202   PrintFooter();
203   return status_;
204 }
205
206 void Cleaner::DoCleanRule(const Rule* rule) {
207   assert(rule);
208
209   for (vector<Edge*>::iterator e = state_->edges_.begin();
210        e != state_->edges_.end(); ++e) {
211     if ((*e)->rule().name() == rule->name()) {
212       for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
213            out_node != (*e)->outputs_.end(); ++out_node) {
214         Remove((*out_node)->path());
215         RemoveEdgeFiles(*e);
216       }
217     }
218   }
219 }
220
221 int Cleaner::CleanRule(const Rule* rule) {
222   assert(rule);
223
224   Reset();
225   PrintHeader();
226   DoCleanRule(rule);
227   PrintFooter();
228   return status_;
229 }
230
231 int Cleaner::CleanRule(const char* rule) {
232   assert(rule);
233
234   Reset();
235   const Rule* r = state_->bindings_.LookupRule(rule);
236   if (r) {
237     CleanRule(r);
238   } else {
239     Error("unknown rule '%s'", rule);
240     status_ = 1;
241   }
242   return status_;
243 }
244
245 int Cleaner::CleanRules(int rule_count, char* rules[]) {
246   assert(rules);
247
248   Reset();
249   PrintHeader();
250   for (int i = 0; i < rule_count; ++i) {
251     const char* rule_name = rules[i];
252     const Rule* rule = state_->bindings_.LookupRule(rule_name);
253     if (rule) {
254       if (IsVerbose())
255         printf("Rule %s\n", rule_name);
256       DoCleanRule(rule);
257     } else {
258       Error("unknown rule '%s'", rule_name);
259       status_ = 1;
260     }
261   }
262   PrintFooter();
263   return status_;
264 }
265
266 void Cleaner::Reset() {
267   status_ = 0;
268   cleaned_files_count_ = 0;
269   removed_.clear();
270   cleaned_.clear();
271 }