// Copyright 2011 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "clean.h" #include #include #include "disk_interface.h" #include "graph.h" #include "state.h" #include "util.h" Cleaner::Cleaner(State* state, const BuildConfig& config) : state_(state), config_(config), removed_(), cleaned_(), cleaned_files_count_(0), disk_interface_(new RealDiskInterface), status_(0) { } Cleaner::Cleaner(State* state, const BuildConfig& config, DiskInterface* disk_interface) : state_(state), config_(config), removed_(), cleaned_(), cleaned_files_count_(0), disk_interface_(disk_interface), status_(0) { } int Cleaner::RemoveFile(const string& path) { return disk_interface_->RemoveFile(path); } bool Cleaner::FileExists(const string& path) { return disk_interface_->Stat(path) > 0; } void Cleaner::Report(const string& path) { ++cleaned_files_count_; if (IsVerbose()) printf("Remove %s\n", path.c_str()); } void Cleaner::Remove(const string& path) { if (!IsAlreadyRemoved(path)) { removed_.insert(path); if (config_.dry_run) { if (FileExists(path)) Report(path); } else { int ret = RemoveFile(path); if (ret == 0) Report(path); else if (ret == -1) status_ = 1; } } } bool Cleaner::IsAlreadyRemoved(const string& path) { set::iterator i = removed_.find(path); return (i != removed_.end()); } void Cleaner::RemoveEdgeFiles(Edge* edge) { string depfile = edge->GetBinding("depfile"); if (!depfile.empty()) Remove(depfile); string rspfile = edge->GetBinding("rspfile"); if (!rspfile.empty()) Remove(rspfile); } void Cleaner::PrintHeader() { if (config_.verbosity == BuildConfig::QUIET) return; printf("Cleaning..."); if (IsVerbose()) printf("\n"); else printf(" "); } void Cleaner::PrintFooter() { if (config_.verbosity == BuildConfig::QUIET) return; printf("%d files.\n", cleaned_files_count_); } int Cleaner::CleanAll(bool generator) { Reset(); PrintHeader(); for (vector::iterator e = state_->edges_.begin(); e != state_->edges_.end(); ++e) { // Do not try to remove phony targets if ((*e)->is_phony()) continue; // Do not remove generator's files unless generator specified. if (!generator && (*e)->GetBindingBool("generator")) continue; for (vector::iterator out_node = (*e)->outputs_.begin(); out_node != (*e)->outputs_.end(); ++out_node) { Remove((*out_node)->path()); } RemoveEdgeFiles(*e); } PrintFooter(); return status_; } void Cleaner::DoCleanTarget(Node* target) { if (Edge* e = target->in_edge()) { // Do not try to remove phony targets if (!e->is_phony()) { Remove(target->path()); RemoveEdgeFiles(e); } for (vector::iterator n = e->inputs_.begin(); n != e->inputs_.end(); ++n) { Node* next = *n; // call DoCleanTarget recursively if this node has not been visited if (cleaned_.count(next) == 0) { DoCleanTarget(next); } } } // mark this target to be cleaned already cleaned_.insert(target); } int Cleaner::CleanTarget(Node* target) { assert(target); Reset(); PrintHeader(); DoCleanTarget(target); PrintFooter(); return status_; } int Cleaner::CleanTarget(const char* target) { assert(target); Reset(); Node* node = state_->LookupNode(target); if (node) { CleanTarget(node); } else { Error("unknown target '%s'", target); status_ = 1; } return status_; } int Cleaner::CleanTargets(int target_count, char* targets[]) { Reset(); PrintHeader(); for (int i = 0; i < target_count; ++i) { const char* target_name = targets[i]; Node* target = state_->LookupNode(target_name); if (target) { if (IsVerbose()) printf("Target %s\n", target_name); DoCleanTarget(target); } else { Error("unknown target '%s'", target_name); status_ = 1; } } PrintFooter(); return status_; } void Cleaner::DoCleanRule(const Rule* rule) { assert(rule); for (vector::iterator e = state_->edges_.begin(); e != state_->edges_.end(); ++e) { if ((*e)->rule().name() == rule->name()) { for (vector::iterator out_node = (*e)->outputs_.begin(); out_node != (*e)->outputs_.end(); ++out_node) { Remove((*out_node)->path()); RemoveEdgeFiles(*e); } } } } int Cleaner::CleanRule(const Rule* rule) { assert(rule); Reset(); PrintHeader(); DoCleanRule(rule); PrintFooter(); return status_; } int Cleaner::CleanRule(const char* rule) { assert(rule); Reset(); const Rule* r = state_->LookupRule(rule); if (r) { CleanRule(r); } else { Error("unknown rule '%s'", rule); status_ = 1; } return status_; } int Cleaner::CleanRules(int rule_count, char* rules[]) { assert(rules); Reset(); PrintHeader(); for (int i = 0; i < rule_count; ++i) { const char* rule_name = rules[i]; const Rule* rule = state_->LookupRule(rule_name); if (rule) { if (IsVerbose()) printf("Rule %s\n", rule_name); DoCleanRule(rule); } else { Error("unknown rule '%s'", rule_name); status_ = 1; } } PrintFooter(); return status_; } void Cleaner::Reset() { status_ = 0; cleaned_files_count_ = 0; removed_.clear(); cleaned_.clear(); }