From a3cbb4d4ddbd3661720603cc26b25cad6177b4c8 Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 12 Feb 2019 09:16:50 -0500 Subject: clean: remove outputs specified by dyndep files Some outputs may not be known in the main build manifest and are instead discovered through a dyndep binding. Load dyndep files that are available during cleaning so that we can clean these outputs too. --- src/clean.cc | 19 +++++++++++++++++++ src/clean.h | 5 +++++ src/clean_test.cc | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/src/clean.cc b/src/clean.cc index caee8d3..d1f221d 100644 --- a/src/clean.cc +++ b/src/clean.cc @@ -27,6 +27,7 @@ Cleaner::Cleaner(State* state, DiskInterface* disk_interface) : state_(state), config_(config), + dyndep_loader_(state, disk_interface), removed_(), cleaned_(), cleaned_files_count_(0), @@ -103,6 +104,7 @@ void Cleaner::PrintFooter() { int Cleaner::CleanAll(bool generator) { Reset(); PrintHeader(); + LoadDyndeps(); for (vector::iterator e = state_->edges_.begin(); e != state_->edges_.end(); ++e) { // Do not try to remove phony targets @@ -148,6 +150,7 @@ int Cleaner::CleanTarget(Node* target) { Reset(); PrintHeader(); + LoadDyndeps(); DoCleanTarget(target); PrintFooter(); return status_; @@ -170,6 +173,7 @@ int Cleaner::CleanTarget(const char* target) { int Cleaner::CleanTargets(int target_count, char* targets[]) { Reset(); PrintHeader(); + LoadDyndeps(); for (int i = 0; i < target_count; ++i) { string target_name = targets[i]; uint64_t slash_bits; @@ -213,6 +217,7 @@ int Cleaner::CleanRule(const Rule* rule) { Reset(); PrintHeader(); + LoadDyndeps(); DoCleanRule(rule); PrintFooter(); return status_; @@ -237,6 +242,7 @@ int Cleaner::CleanRules(int rule_count, char* rules[]) { Reset(); PrintHeader(); + LoadDyndeps(); for (int i = 0; i < rule_count; ++i) { const char* rule_name = rules[i]; const Rule* rule = state_->bindings_.LookupRule(rule_name); @@ -259,3 +265,16 @@ void Cleaner::Reset() { removed_.clear(); cleaned_.clear(); } + +void Cleaner::LoadDyndeps() { + // Load dyndep files that exist, before they are cleaned. + for (vector::iterator e = state_->edges_.begin(); + e != state_->edges_.end(); ++e) { + if (Node* dyndep = (*e)->dyndep_) { + // Capture and ignore errors loading the dyndep file. + // We clean as much of the graph as we know. + std::string err; + dyndep_loader_.LoadDyndeps(dyndep, &err); + } + } +} diff --git a/src/clean.h b/src/clean.h index a007486..d044fb1 100644 --- a/src/clean.h +++ b/src/clean.h @@ -19,6 +19,7 @@ #include #include "build.h" +#include "dyndep.h" using namespace std; @@ -91,8 +92,12 @@ struct Cleaner { void DoCleanRule(const Rule* rule); void Reset(); + /// Load dependencies from dyndep bindings. + void LoadDyndeps(); + State* state_; const BuildConfig& config_; + DyndepLoader dyndep_loader_; set removed_; set cleaned_; int cleaned_files_count_; diff --git a/src/clean_test.cc b/src/clean_test.cc index 63734ac..45187f4 100644 --- a/src/clean_test.cc +++ b/src/clean_test.cc @@ -285,6 +285,55 @@ TEST_F(CleanTest, CleanDepFileOnCleanRule) { EXPECT_EQ(2u, fs_.files_removed_.size()); } +TEST_F(CleanTest, CleanDyndep) { + // Verify that a dyndep file can be loaded to discover a new output + // to be cleaned. + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out: cat in || dd\n" +" dyndep = dd\n" + )); + fs_.Create("in", ""); + fs_.Create("dd", +"ninja_dyndep_version = 1\n" +"build out | out.imp: dyndep\n" +); + fs_.Create("out", ""); + fs_.Create("out.imp", ""); + + Cleaner cleaner(&state_, config_, &fs_); + + ASSERT_EQ(0, cleaner.cleaned_files_count()); + EXPECT_EQ(0, cleaner.CleanAll()); + EXPECT_EQ(2, cleaner.cleaned_files_count()); + EXPECT_EQ(2u, fs_.files_removed_.size()); + + string err; + EXPECT_EQ(0, fs_.Stat("out", &err)); + EXPECT_EQ(0, fs_.Stat("out.imp", &err)); +} + +TEST_F(CleanTest, CleanDyndepMissing) { + // Verify that a missing dyndep file is tolerated. + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out: cat in || dd\n" +" dyndep = dd\n" + )); + fs_.Create("in", ""); + fs_.Create("out", ""); + fs_.Create("out.imp", ""); + + Cleaner cleaner(&state_, config_, &fs_); + + ASSERT_EQ(0, cleaner.cleaned_files_count()); + EXPECT_EQ(0, cleaner.CleanAll()); + EXPECT_EQ(1, cleaner.cleaned_files_count()); + EXPECT_EQ(1u, fs_.files_removed_.size()); + + string err; + EXPECT_EQ(0, fs_.Stat("out", &err)); + EXPECT_EQ(1, fs_.Stat("out.imp", &err)); +} + TEST_F(CleanTest, CleanRspFile) { ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, "rule cc\n" -- cgit v0.12