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
25using namespace std;
26
27Cleaner::Cleaner(State* state,
28 const BuildConfig& config,
29 DiskInterface* disk_interface)
30 : state_(state),
31 config_(config),
32 dyndep_loader_(state, disk_interface),
33 cleaned_files_count_(0),
34 disk_interface_(disk_interface),
35 status_(0) {
36}
37
38int Cleaner::RemoveFile(const string& path) {
39 return disk_interface_->RemoveFile(path);
40}
41
42bool 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
50void Cleaner::Report(const string& path) {
51 ++cleaned_files_count_;
52 if (IsVerbose())
53 printf("Remove %s\n", path.c_str());
54}
55
56void 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
72bool Cleaner::IsAlreadyRemoved(const string& path) {
73 set<string>::iterator i = removed_.find(path);
74 return (i != removed_.end());
75}
76
77void 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
87void 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
98void Cleaner::PrintFooter() {
99 if (config_.verbosity == BuildConfig::QUIET)
100 return;
101 printf("%d files.\n", cleaned_files_count_);
102}
103
104int 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
127int 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 // Detecting stale outputs works as follows:
133 //
134 // - If it has no Node, it is not in the build graph, or the deps log
135 // anymore, hence is stale.
136 //
137 // - If it isn't an output or input for any edge, it comes from a stale
138 // entry in the deps log, but no longer referenced from the build
139 // graph.
140 //
141 if (!n || (!n->in_edge() && n->out_edges().empty())) {
142 Remove(i->first.AsString());
143 }
144 }
145 PrintFooter();
146 return status_;
147}
148
149void Cleaner::DoCleanTarget(Node* target) {
150 if (Edge* e = target->in_edge()) {
151 // Do not try to remove phony targets
152 if (!e->is_phony()) {
153 Remove(target->path());
154 RemoveEdgeFiles(e);
155 }
156 for (vector<Node*>::iterator n = e->inputs_.begin(); n != e->inputs_.end();
157 ++n) {
158 Node* next = *n;
159 // call DoCleanTarget recursively if this node has not been visited
160 if (cleaned_.count(next) == 0) {
161 DoCleanTarget(next);
162 }
163 }
164 }
165
166 // mark this target to be cleaned already
167 cleaned_.insert(target);
168}
169
170int Cleaner::CleanTarget(Node* target) {
171 assert(target);
172
173 Reset();
174 PrintHeader();
175 LoadDyndeps();
176 DoCleanTarget(target);
177 PrintFooter();
178 return status_;
179}
180
181int Cleaner::CleanTarget(const char* target) {
182 assert(target);
183
184 Reset();
185 Node* node = state_->LookupNode(target);
186 if (node) {
187 CleanTarget(node);
188 } else {
189 Error("unknown target '%s'", target);
190 status_ = 1;
191 }
192 return status_;
193}
194
195int Cleaner::CleanTargets(int target_count, char* targets[]) {
196 Reset();
197 PrintHeader();
198 LoadDyndeps();
199 for (int i = 0; i < target_count; ++i) {
200 string target_name = targets[i];
201 if (target_name.empty()) {
202 Error("failed to canonicalize '': empty path");
203 status_ = 1;
204 continue;
205 }
206 uint64_t slash_bits;
207 CanonicalizePath(&target_name, &slash_bits);
208 Node* target = state_->LookupNode(target_name);
209 if (target) {
210 if (IsVerbose())
211 printf("Target %s\n", target_name.c_str());
212 DoCleanTarget(target);
213 } else {
214 Error("unknown target '%s'", target_name.c_str());
215 status_ = 1;
216 }
217 }
218 PrintFooter();
219 return status_;
220}
221
222void Cleaner::DoCleanRule(const Rule* rule) {
223 assert(rule);
224
225 for (vector<Edge*>::iterator e = state_->edges_.begin();
226 e != state_->edges_.end(); ++e) {
227 if ((*e)->rule().name() == rule->name()) {
228 for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
229 out_node != (*e)->outputs_.end(); ++out_node) {
230 Remove((*out_node)->path());
231 RemoveEdgeFiles(*e);
232 }
233 }
234 }
235}
236
237int Cleaner::CleanRule(const Rule* rule) {
238 assert(rule);
239
240 Reset();
241 PrintHeader();
242 LoadDyndeps();
243 DoCleanRule(rule);
244 PrintFooter();
245 return status_;
246}
247
248int Cleaner::CleanRule(const char* rule) {
249 assert(rule);
250
251 Reset();
252 const Rule* r = state_->bindings_.LookupRule(rule);
253 if (r) {
254 CleanRule(r);
255 } else {
256 Error("unknown rule '%s'", rule);
257 status_ = 1;
258 }
259 return status_;
260}
261
262int Cleaner::CleanRules(int rule_count, char* rules[]) {
263 assert(rules);
264
265 Reset();
266 PrintHeader();
267 LoadDyndeps();
268 for (int i = 0; i < rule_count; ++i) {
269 const char* rule_name = rules[i];
270 const Rule* rule = state_->bindings_.LookupRule(rule_name);
271 if (rule) {
272 if (IsVerbose())
273 printf("Rule %s\n", rule_name);
274 DoCleanRule(rule);
275 } else {
276 Error("unknown rule '%s'", rule_name);
277 status_ = 1;
278 }
279 }
280 PrintFooter();
281 return status_;
282}
283
284void Cleaner::Reset() {
285 status_ = 0;
286 cleaned_files_count_ = 0;
287 removed_.clear();
288 cleaned_.clear();
289}
290
291void Cleaner::LoadDyndeps() {
292 // Load dyndep files that exist, before they are cleaned.
293 for (vector<Edge*>::iterator e = state_->edges_.begin();
294 e != state_->edges_.end(); ++e) {
295 if (Node* dyndep = (*e)->dyndep_) {
296 // Capture and ignore errors loading the dyndep file.
297 // We clean as much of the graph as we know.
298 std::string err;
299 dyndep_loader_.LoadDyndeps(dyndep, &err);
300 }
301 }
302}
303