blob: 3fe23ec32b6668525aceb6249ebcdc63e4336d97 [file] [log] [blame]
// 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 <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#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_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_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<string>::iterator i = removed_.find(path);
return (i != removed_.end());
}
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<Edge*>::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)->rule().generator())
continue;
for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
out_node != (*e)->outputs_.end(); ++out_node) {
Remove((*out_node)->path());
}
// Remove the depfile
if (!(*e)->rule().depfile().empty())
Remove((*e)->EvaluateDepFile());
// Remove the response file
if ((*e)->HasRspFile())
Remove((*e)->GetRspFile());
}
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());
if (!target->in_edge()->rule().depfile().empty())
Remove(target->in_edge()->EvaluateDepFile());
if (e->HasRspFile())
Remove(e->GetRspFile());
}
for (vector<Node*>::iterator n = e->inputs_.begin(); n != e->inputs_.end();
++n) {
DoCleanTarget(*n);
}
}
}
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<Edge*>::iterator e = state_->edges_.begin();
e != state_->edges_.end(); ++e) {
if ((*e)->rule().name() == rule->name()) {
for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
out_node != (*e)->outputs_.end(); ++out_node) {
Remove((*out_node)->path());
if (!(*e)->rule().depfile().empty())
Remove((*e)->EvaluateDepFile());
if ((*e)->HasRspFile())
Remove((*e)->GetRspFile());
}
}
}
}
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();
}