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