Ninja
clean.cc
Go to the documentation of this file.
00001 // Copyright 2011 Google Inc. All Rights Reserved.
00002 //
00003 // Licensed under the Apache License, Version 2.0 (the "License");
00004 // you may not use this file except in compliance with the License.
00005 // You may obtain a copy of the License at
00006 //
00007 //     http://www.apache.org/licenses/LICENSE-2.0
00008 //
00009 // Unless required by applicable law or agreed to in writing, software
00010 // distributed under the License is distributed on an "AS IS" BASIS,
00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 // See the License for the specific language governing permissions and
00013 // limitations under the License.
00014 
00015 #include "clean.h"
00016 
00017 #include <assert.h>
00018 #include <stdio.h>
00019 
00020 #include "disk_interface.h"
00021 #include "graph.h"
00022 #include "state.h"
00023 #include "util.h"
00024 
00025 Cleaner::Cleaner(State* state, const BuildConfig& config)
00026   : state_(state),
00027     config_(config),
00028     removed_(),
00029     cleaned_(),
00030     cleaned_files_count_(0),
00031     disk_interface_(new RealDiskInterface),
00032     status_(0) {
00033 }
00034 
00035 Cleaner::Cleaner(State* state,
00036                  const BuildConfig& config,
00037                  DiskInterface* disk_interface)
00038   : state_(state),
00039     config_(config),
00040     removed_(),
00041     cleaned_(),
00042     cleaned_files_count_(0),
00043     disk_interface_(disk_interface),
00044     status_(0) {
00045 }
00046 
00047 int Cleaner::RemoveFile(const string& path) {
00048   return disk_interface_->RemoveFile(path);
00049 }
00050 
00051 bool Cleaner::FileExists(const string& path) {
00052   return disk_interface_->Stat(path) > 0;
00053 }
00054 
00055 void Cleaner::Report(const string& path) {
00056   ++cleaned_files_count_;
00057   if (IsVerbose())
00058     printf("Remove %s\n", path.c_str());
00059 }
00060 
00061 void Cleaner::Remove(const string& path) {
00062   if (!IsAlreadyRemoved(path)) {
00063     removed_.insert(path);
00064     if (config_.dry_run) {
00065       if (FileExists(path))
00066         Report(path);
00067     } else {
00068       int ret = RemoveFile(path);
00069       if (ret == 0)
00070         Report(path);
00071       else if (ret == -1)
00072         status_ = 1;
00073     }
00074   }
00075 }
00076 
00077 bool Cleaner::IsAlreadyRemoved(const string& path) {
00078   set<string>::iterator i = removed_.find(path);
00079   return (i != removed_.end());
00080 }
00081 
00082 void Cleaner::RemoveEdgeFiles(Edge* edge) {
00083   string depfile = edge->GetBinding("depfile");
00084   if (!depfile.empty())
00085     Remove(depfile);
00086 
00087   string rspfile = edge->GetBinding("rspfile");
00088   if (!rspfile.empty())
00089     Remove(rspfile);
00090 }
00091 
00092 void Cleaner::PrintHeader() {
00093   if (config_.verbosity == BuildConfig::QUIET)
00094     return;
00095   printf("Cleaning...");
00096   if (IsVerbose())
00097     printf("\n");
00098   else
00099     printf(" ");
00100 }
00101 
00102 void Cleaner::PrintFooter() {
00103   if (config_.verbosity == BuildConfig::QUIET)
00104     return;
00105   printf("%d files.\n", cleaned_files_count_);
00106 }
00107 
00108 int Cleaner::CleanAll(bool generator) {
00109   Reset();
00110   PrintHeader();
00111   for (vector<Edge*>::iterator e = state_->edges_.begin();
00112        e != state_->edges_.end(); ++e) {
00113     // Do not try to remove phony targets
00114     if ((*e)->is_phony())
00115       continue;
00116     // Do not remove generator's files unless generator specified.
00117     if (!generator && (*e)->GetBindingBool("generator"))
00118       continue;
00119     for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
00120          out_node != (*e)->outputs_.end(); ++out_node) {
00121       Remove((*out_node)->path());
00122     }
00123 
00124     RemoveEdgeFiles(*e);
00125   }
00126   PrintFooter();
00127   return status_;
00128 }
00129 
00130 void Cleaner::DoCleanTarget(Node* target) {
00131   if (Edge* e = target->in_edge()) {
00132     // Do not try to remove phony targets
00133     if (!e->is_phony()) {
00134       Remove(target->path());
00135       RemoveEdgeFiles(e);
00136     }
00137     for (vector<Node*>::iterator n = e->inputs_.begin(); n != e->inputs_.end();
00138          ++n) {
00139       Node* next = *n;
00140       // call DoCleanTarget recursively if this node has not been visited
00141       if (cleaned_.count(next) == 0) {
00142         DoCleanTarget(next);
00143       }
00144     }
00145   }
00146 
00147   // mark this target to be cleaned already
00148   cleaned_.insert(target);
00149 }
00150 
00151 int Cleaner::CleanTarget(Node* target) {
00152   assert(target);
00153 
00154   Reset();
00155   PrintHeader();
00156   DoCleanTarget(target);
00157   PrintFooter();
00158   return status_;
00159 }
00160 
00161 int Cleaner::CleanTarget(const char* target) {
00162   assert(target);
00163 
00164   Reset();
00165   Node* node = state_->LookupNode(target);
00166   if (node) {
00167     CleanTarget(node);
00168   } else {
00169     Error("unknown target '%s'", target);
00170     status_ = 1;
00171   }
00172   return status_;
00173 }
00174 
00175 int Cleaner::CleanTargets(int target_count, char* targets[]) {
00176   Reset();
00177   PrintHeader();
00178   for (int i = 0; i < target_count; ++i) {
00179     const char* target_name = targets[i];
00180     Node* target = state_->LookupNode(target_name);
00181     if (target) {
00182       if (IsVerbose())
00183         printf("Target %s\n", target_name);
00184       DoCleanTarget(target);
00185     } else {
00186       Error("unknown target '%s'", target_name);
00187       status_ = 1;
00188     }
00189   }
00190   PrintFooter();
00191   return status_;
00192 }
00193 
00194 void Cleaner::DoCleanRule(const Rule* rule) {
00195   assert(rule);
00196 
00197   for (vector<Edge*>::iterator e = state_->edges_.begin();
00198        e != state_->edges_.end(); ++e) {
00199     if ((*e)->rule().name() == rule->name()) {
00200       for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
00201            out_node != (*e)->outputs_.end(); ++out_node) {
00202         Remove((*out_node)->path());
00203         RemoveEdgeFiles(*e);
00204       }
00205     }
00206   }
00207 }
00208 
00209 int Cleaner::CleanRule(const Rule* rule) {
00210   assert(rule);
00211 
00212   Reset();
00213   PrintHeader();
00214   DoCleanRule(rule);
00215   PrintFooter();
00216   return status_;
00217 }
00218 
00219 int Cleaner::CleanRule(const char* rule) {
00220   assert(rule);
00221 
00222   Reset();
00223   const Rule* r = state_->LookupRule(rule);
00224   if (r) {
00225     CleanRule(r);
00226   } else {
00227     Error("unknown rule '%s'", rule);
00228     status_ = 1;
00229   }
00230   return status_;
00231 }
00232 
00233 int Cleaner::CleanRules(int rule_count, char* rules[]) {
00234   assert(rules);
00235 
00236   Reset();
00237   PrintHeader();
00238   for (int i = 0; i < rule_count; ++i) {
00239     const char* rule_name = rules[i];
00240     const Rule* rule = state_->LookupRule(rule_name);
00241     if (rule) {
00242       if (IsVerbose())
00243         printf("Rule %s\n", rule_name);
00244       DoCleanRule(rule);
00245     } else {
00246       Error("unknown rule '%s'", rule_name);
00247       status_ = 1;
00248     }
00249   }
00250   PrintFooter();
00251   return status_;
00252 }
00253 
00254 void Cleaner::Reset() {
00255   status_ = 0;
00256   cleaned_files_count_ = 0;
00257   removed_.clear();
00258   cleaned_.clear();
00259 }