• 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 
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