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 Cleaner::Cleaner(State* state, const BuildConfig& config)
26  : state_(state),
27  config_(config),
28  removed_(),
29  cleaned_(),
30  cleaned_files_count_(0),
31  disk_interface_(new RealDiskInterface),
32  status_(0) {
33 }
34 
36  const BuildConfig& config,
37  DiskInterface* disk_interface)
38  : state_(state),
39  config_(config),
40  removed_(),
41  cleaned_(),
42  cleaned_files_count_(0),
43  disk_interface_(disk_interface),
44  status_(0) {
45 }
46 
47 int Cleaner::RemoveFile(const string& path) {
48  return disk_interface_->RemoveFile(path);
49 }
50 
51 bool Cleaner::FileExists(const string& path) {
52  string err;
53  TimeStamp mtime = disk_interface_->Stat(path, &err);
54  if (mtime == -1)
55  Error("%s", err.c_str());
56  return mtime > 0; // Treat Stat() errors as "file does not exist".
57 }
58 
59 void Cleaner::Report(const string& path) {
61  if (IsVerbose())
62  printf("Remove %s\n", path.c_str());
63 }
64 
65 void Cleaner::Remove(const string& path) {
66  if (!IsAlreadyRemoved(path)) {
67  removed_.insert(path);
68  if (config_.dry_run) {
69  if (FileExists(path))
70  Report(path);
71  } else {
72  int ret = RemoveFile(path);
73  if (ret == 0)
74  Report(path);
75  else if (ret == -1)
76  status_ = 1;
77  }
78  }
79 }
80 
81 bool Cleaner::IsAlreadyRemoved(const string& path) {
82  set<string>::iterator i = removed_.find(path);
83  return (i != removed_.end());
84 }
85 
87  string depfile = edge->GetUnescapedDepfile();
88  if (!depfile.empty())
89  Remove(depfile);
90 
91  string rspfile = edge->GetUnescapedRspfile();
92  if (!rspfile.empty())
93  Remove(rspfile);
94 }
95 
98  return;
99  printf("Cleaning...");
100  if (IsVerbose())
101  printf("\n");
102  else
103  printf(" ");
104  fflush(stdout);
105 }
106 
109  return;
110  printf("%d files.\n", cleaned_files_count_);
111 }
112 
113 int Cleaner::CleanAll(bool generator) {
114  Reset();
115  PrintHeader();
116  for (vector<Edge*>::iterator e = state_->edges_.begin();
117  e != state_->edges_.end(); ++e) {
118  // Do not try to remove phony targets
119  if ((*e)->is_phony())
120  continue;
121  // Do not remove generator's files unless generator specified.
122  if (!generator && (*e)->GetBindingBool("generator"))
123  continue;
124  for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
125  out_node != (*e)->outputs_.end(); ++out_node) {
126  Remove((*out_node)->path());
127  }
128 
129  RemoveEdgeFiles(*e);
130  }
131  PrintFooter();
132  return status_;
133 }
134 
136  if (Edge* e = target->in_edge()) {
137  // Do not try to remove phony targets
138  if (!e->is_phony()) {
139  Remove(target->path());
140  RemoveEdgeFiles(e);
141  }
142  for (vector<Node*>::iterator n = e->inputs_.begin(); n != e->inputs_.end();
143  ++n) {
144  Node* next = *n;
145  // call DoCleanTarget recursively if this node has not been visited
146  if (cleaned_.count(next) == 0) {
147  DoCleanTarget(next);
148  }
149  }
150  }
151 
152  // mark this target to be cleaned already
153  cleaned_.insert(target);
154 }
155 
157  assert(target);
158 
159  Reset();
160  PrintHeader();
161  DoCleanTarget(target);
162  PrintFooter();
163  return status_;
164 }
165 
166 int Cleaner::CleanTarget(const char* target) {
167  assert(target);
168 
169  Reset();
170  Node* node = state_->LookupNode(target);
171  if (node) {
172  CleanTarget(node);
173  } else {
174  Error("unknown target '%s'", target);
175  status_ = 1;
176  }
177  return status_;
178 }
179 
180 int Cleaner::CleanTargets(int target_count, char* targets[]) {
181  Reset();
182  PrintHeader();
183  for (int i = 0; i < target_count; ++i) {
184  string target_name = targets[i];
185  uint64_t slash_bits;
186  string err;
187  if (!CanonicalizePath(&target_name, &slash_bits, &err)) {
188  Error("failed to canonicalize '%s': %s", target_name.c_str(), err.c_str());
189  status_ = 1;
190  } else {
191  Node* target = state_->LookupNode(target_name);
192  if (target) {
193  if (IsVerbose())
194  printf("Target %s\n", target_name.c_str());
195  DoCleanTarget(target);
196  } else {
197  Error("unknown target '%s'", target_name.c_str());
198  status_ = 1;
199  }
200  }
201  }
202  PrintFooter();
203  return status_;
204 }
205 
206 void Cleaner::DoCleanRule(const Rule* rule) {
207  assert(rule);
208 
209  for (vector<Edge*>::iterator e = state_->edges_.begin();
210  e != state_->edges_.end(); ++e) {
211  if ((*e)->rule().name() == rule->name()) {
212  for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
213  out_node != (*e)->outputs_.end(); ++out_node) {
214  Remove((*out_node)->path());
215  RemoveEdgeFiles(*e);
216  }
217  }
218  }
219 }
220 
221 int Cleaner::CleanRule(const Rule* rule) {
222  assert(rule);
223 
224  Reset();
225  PrintHeader();
226  DoCleanRule(rule);
227  PrintFooter();
228  return status_;
229 }
230 
231 int Cleaner::CleanRule(const char* rule) {
232  assert(rule);
233 
234  Reset();
235  const Rule* r = state_->bindings_.LookupRule(rule);
236  if (r) {
237  CleanRule(r);
238  } else {
239  Error("unknown rule '%s'", rule);
240  status_ = 1;
241  }
242  return status_;
243 }
244 
245 int Cleaner::CleanRules(int rule_count, char* rules[]) {
246  assert(rules);
247 
248  Reset();
249  PrintHeader();
250  for (int i = 0; i < rule_count; ++i) {
251  const char* rule_name = rules[i];
252  const Rule* rule = state_->bindings_.LookupRule(rule_name);
253  if (rule) {
254  if (IsVerbose())
255  printf("Rule %s\n", rule_name);
256  DoCleanRule(rule);
257  } else {
258  Error("unknown rule '%s'", rule_name);
259  status_ = 1;
260  }
261  }
262  PrintFooter();
263  return status_;
264 }
265 
267  status_ = 0;
269  removed_.clear();
270  cleaned_.clear();
271 }
int status_
Definition: clean.h:104
const BuildConfig & config_
Definition: clean.h:99
void Report(const string &path)
Definition: clean.cc:59
Verbosity verbosity
Definition: build.h:148
vector< Edge * > edges_
All the edges of the graph.
Definition: state.h:125
int CleanTargets(int target_count, char *targets[])
Clean the given target targets.
Definition: clean.cc:180
const Rule * LookupRule(const string &rule_name)
Definition: eval_env.cc:44
const string & path() const
Definition: graph.h:75
Edge * in_edge() const
Definition: graph.h:90
bool CanonicalizePath(string *path, uint64_t *slash_bits, string *err)
Canonicalize a path like "foo/../bar.h" into just "bar.h".
Definition: util.cc:93
Cleaner(State *state, const BuildConfig &config)
Build a cleaner object with a real disk interface.
Definition: clean.cc:25
string GetUnescapedRspfile()
Like GetBinding("rspfile"), but without shell escaping.
Definition: graph.cc:386
Information about a node in the dependency graph: the file, whether it&#39;s dirty, mtime, etc.
Definition: graph.h:37
void Remove(const string &path)
Remove the given path file only if it has not been already removed.
Definition: clean.cc:65
void Reset()
Definition: clean.cc:266
Interface for accessing the disk.
An edge in the dependency graph; links between Nodes using Rules.
Definition: graph.h:131
Node * LookupNode(StringPiece path) const
Definition: state.cc:112
DiskInterface * disk_interface_
Definition: clean.h:103
set< Node * > cleaned_
Definition: clean.h:101
int RemoveFile(const string &path)
Remove the file path.
Definition: clean.cc:47
bool IsVerbose() const
Definition: clean.h:71
int CleanAll(bool generator=false)
Clean all built files, except for files created by generator rules.
Definition: clean.cc:113
Implementation of DiskInterface that actually hits the disk.
void DoCleanTarget(Node *target)
Helper recursive method for CleanTarget().
Definition: clean.cc:135
int CleanRule(const Rule *rule)
Clean all the file built with the given rule rule.
Definition: clean.cc:221
An invokable build command and associated metadata (description, etc.).
Definition: eval_env.h:55
int CleanRules(int rule_count, char *rules[])
Clean the file produced by the given rules.
Definition: clean.cc:245
bool FileExists(const string &path)
Definition: clean.cc:51
virtual TimeStamp Stat(const string &path, string *err) const =0
stat() a file, returning the mtime, or 0 if missing and -1 on other errors.
int64_t TimeStamp
Definition: timestamp.h:31
BindingEnv bindings_
Definition: state.h:127
void PrintHeader()
Definition: clean.cc:96
State * state_
Definition: clean.h:98
void DoCleanRule(const Rule *rule)
Definition: clean.cc:206
int cleaned_files_count_
Definition: clean.h:102
const string & name() const
Definition: eval_env.h:58
void RemoveEdgeFiles(Edge *edge)
Remove the depfile and rspfile for an Edge.
Definition: clean.cc:86
virtual int RemoveFile(const string &path)=0
Remove the file named path.
Options (e.g. verbosity, parallelism) passed to a build.
Definition: build.h:139
Global state (file status) for a single run.
Definition: state.h:85
unsigned long long uint64_t
Definition: win32port.h:29
void PrintFooter()
Definition: clean.cc:107
string GetUnescapedDepfile()
Like GetBinding("depfile"), but without shell escaping.
Definition: graph.cc:381
int CleanTarget(Node *target)
Clean the given target and all the file built for it.
Definition: clean.cc:156
set< string > removed_
Definition: clean.h:100
bool IsAlreadyRemoved(const string &path)
Definition: clean.cc:81
bool dry_run
Definition: build.h:149
void Error(const char *msg,...)
Log an error message.
Definition: util.cc:84