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