• 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 
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   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 
DoCleanTarget(Node * target)149 void 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 
CleanTarget(Node * target)170 int Cleaner::CleanTarget(Node* target) {
171   assert(target);
172 
173   Reset();
174   PrintHeader();
175   LoadDyndeps();
176   DoCleanTarget(target);
177   PrintFooter();
178   return status_;
179 }
180 
CleanTarget(const char * target)181 int 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 
CleanTargets(int target_count,char * targets[])195 int 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 
DoCleanRule(const Rule * rule)222 void 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 
CleanRule(const Rule * rule)237 int 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 
CleanRule(const char * rule)248 int 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 
CleanRules(int rule_count,char * rules[])262 int 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 
Reset()284 void Cleaner::Reset() {
285   status_ = 0;
286   cleaned_files_count_ = 0;
287   removed_.clear();
288   cleaned_.clear();
289 }
290 
LoadDyndeps()291 void 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