Ninja
clean.cc
Go to the documentation of this file.
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 
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 
38 int Cleaner::RemoveFile(const string& path) {
39  return disk_interface_->RemoveFile(path);
40 }
41 
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 
50 void Cleaner::Report(const string& path) {
52  if (IsVerbose())
53  printf("Remove %s\n", path.c_str());
54 }
55 
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 
72 bool Cleaner::IsAlreadyRemoved(const string& path) {
73  set<string>::iterator i = removed_.find(path);
74  return (i != removed_.end());
75 }
76 
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 
89  return;
90  printf("Cleaning...");
91  if (IsVerbose())
92  printf("\n");
93  else
94  printf(" ");
95  fflush(stdout);
96 }
97 
100  return;
101  printf("%d files.\n", cleaned_files_count_);
102 }
103 
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 
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 
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 
172  assert(target);
173 
174  Reset();
175  PrintHeader();
176  LoadDyndeps();
177  DoCleanTarget(target);
178  PrintFooter();
179  return status_;
180 }
181 
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 
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 
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 
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 
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 
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 
286  status_ = 0;
288  removed_.clear();
289  cleaned_.clear();
290 }
291 
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 }
Definition: hash_map.h:26
const Rule * LookupRule(const std::string &rule_name)
Definition: eval_env.cc:46
Options (e.g. verbosity, parallelism) passed to a build.
Definition: build.h:176
bool dry_run
Definition: build.h:186
Verbosity verbosity
Definition: build.h:185
ExternalStringHashMap< std::unique_ptr< LogEntry > >::Type Entries
Definition: build_log.h:95
const BuildConfig & config_
Definition: clean.h:102
int CleanRule(const Rule *rule)
Clean all the file built with the given rule rule.
Definition: clean.cc:238
int CleanTarget(Node *target)
Clean the given target and all the file built for it.
Definition: clean.cc:171
void PrintFooter()
Definition: clean.cc:98
void RemoveEdgeFiles(Edge *edge)
Remove the depfile and rspfile for an Edge.
Definition: clean.cc:77
int cleaned_files_count_
Definition: clean.h:106
void DoCleanTarget(Node *target)
Helper recursive method for CleanTarget().
Definition: clean.cc:150
bool IsVerbose() const
Definition: clean.h:71
bool IsAlreadyRemoved(const std::string &path)
Definition: clean.cc:72
void Remove(const std::string &path)
Remove the given path file only if it has not been already removed.
Definition: clean.cc:56
int CleanRules(int rule_count, char *rules[])
Clean the file produced by the given rules.
Definition: clean.cc:263
void Report(const std::string &path)
Definition: clean.cc:50
void Reset()
Definition: clean.cc:285
DyndepLoader dyndep_loader_
Definition: clean.h:103
void LoadDyndeps()
Load dependencies from dyndep bindings.
Definition: clean.cc:292
bool FileExists(const std::string &path)
Definition: clean.cc:42
Cleaner(State *state, const BuildConfig &config, DiskInterface *disk_interface)
Build a cleaner object with the given disk_interface.
Definition: clean.cc:27
void DoCleanRule(const Rule *rule)
Definition: clean.cc:223
std::set< std::string > removed_
Definition: clean.h:104
std::set< Node * > cleaned_
Definition: clean.h:105
int CleanTargets(int target_count, char *targets[])
Clean the given target targets.
Definition: clean.cc:196
int CleanDead(const BuildLog::Entries &entries)
Clean the files produced by previous builds that are no longer in the manifest.
Definition: clean.cc:127
int status_
Definition: clean.h:108
int CleanAll(bool generator=false)
Clean all built files, except for files created by generator rules.
Definition: clean.cc:104
int RemoveFile(const std::string &path)
Remove the file path.
Definition: clean.cc:38
State * state_
Definition: clean.h:101
DiskInterface * disk_interface_
Definition: clean.h:107
void PrintHeader()
Definition: clean.cc:87
Interface for accessing the disk.
virtual int RemoveFile(const std::string &path)=0
Remove the file named path.
virtual TimeStamp Stat(const std::string &path, std::string *err) const =0
stat() a file, returning the mtime, or 0 if missing and -1 on other errors.
bool LoadDyndeps(Node *node, std::string *err) const
Load a dyndep file from the given node's path and update the build graph with the new information.
Definition: dyndep.cc:30
An edge in the dependency graph; links between Nodes using Rules.
Definition: graph.h:175
std::string GetUnescapedRspfile() const
Like GetBinding("rspfile"), but without shell escaping.
Definition: graph.cc:530
std::string GetUnescapedDepfile() const
Like GetBinding("depfile"), but without shell escaping.
Definition: graph.cc:520
Information about a node in the dependency graph: the file, whether it's dirty, mtime,...
Definition: graph.h:42
const std::string & path() const
Definition: graph.h:82
Edge * in_edge() const
Definition: graph.h:100
bool dyndep_pending() const
Definition: graph.h:97
const std::vector< Edge * > & out_edges() const
Definition: graph.h:114
An invocable build command and associated metadata (description, etc.).
Definition: eval_env.h:66
const std::string & name() const
Definition: eval_env.h:73
Global state (file status) for a single run.
Definition: state.h:95
std::vector< Edge * > edges_
All the edges of the graph.
Definition: state.h:138
Node * LookupNode(StringPiece path) const
Definition: state.cc:104
BindingEnv bindings_
Definition: state.h:140
int64_t TimeStamp
Definition: timestamp.h:31
void Error(const char *msg, va_list ap)
Definition: util.cc:98
void CanonicalizePath(string *path, uint64_t *slash_bits)
Definition: util.cc:124
unsigned long long uint64_t
Definition: win32port.h:29