From a3f5c703eaf72e563b7fcff1d613dceac1a8b677 Mon Sep 17 00:00:00 2001 From: David 'Digit' Turner Date: Tue, 2 Apr 2024 20:19:44 +0200 Subject: [PATCH] BuildLog: Use DiskInterface instance. Make the BuildLog class take a DiskInterface reference in its constructor, to ensure that all i/o operations use the same interface. + Adjust call sites accordingly. For tests, always use a SystemDiskInterface instead of the VirtualFileSystem instance when the latter is available, as this is exactly what the previous code was doing. --- src/build_log.cc | 28 +++++++-------- src/build_log.h | 18 +++++++--- src/build_log_perftest.cc | 8 +++-- src/build_log_test.cc | 75 ++++++++++++++++++++++++--------------- src/build_test.cc | 28 +++++++++------ src/clean_test.cc | 21 +++++------ src/disk_interface.cc | 1 + src/ninja.cc | 4 +-- 8 files changed, 109 insertions(+), 74 deletions(-) diff --git a/src/build_log.cc b/src/build_log.cc index 52c7c84f85..d586932504 100644 --- a/src/build_log.cc +++ b/src/build_log.cc @@ -21,7 +21,6 @@ #endif #include "build_log.h" -#include "disk_interface.h" #include #include @@ -121,8 +120,8 @@ BuildLog::LogEntry::LogEntry(const string& output, uint64_t command_hash, start_time(start_time), end_time(end_time), mtime(mtime) {} -BuildLog::BuildLog() - : log_file_(NULL), needs_recompaction_(false) {} +BuildLog::BuildLog(DiskInterface& disk_interface) + : disk_interface_(&disk_interface) {} BuildLog::~BuildLog() { Close(); @@ -186,7 +185,7 @@ bool BuildLog::OpenForWriteIfNeeded() { if (log_file_ || log_file_path_.empty()) { return true; } - log_file_ = fopen(log_file_path_.c_str(), "ab"); + log_file_ = disk_interface_->OpenFile(log_file_path_, "ab"); if (!log_file_) { return false; } @@ -260,7 +259,7 @@ struct LineReader { LoadStatus BuildLog::Load(const string& path, string* err) { METRIC_RECORD(".ninja_log load"); - FILE* file = fopen(path.c_str(), "r"); + FILE* file = disk_interface_->OpenFile(path, "r"); if (!file) { if (errno == ENOENT) return LOAD_NOT_FOUND; @@ -290,7 +289,7 @@ LoadStatus BuildLog::Load(const string& path, string* err) { } if (invalid_log_version) { fclose(file); - unlink(path.c_str()); + disk_interface_->RemoveFile(path); // Don't report this as a failure. A missing build log will cause // us to rebuild the outputs anyway. return LOAD_NOT_FOUND; @@ -394,8 +393,8 @@ bool BuildLog::Recompact(const string& path, const BuildLogUser& user, METRIC_RECORD(".ninja_log recompact"); Close(); - string temp_path = path + ".recompact"; - FILE* f = fopen(temp_path.c_str(), "wb"); + std::string temp_path = path + ".recompact"; + FILE* f = disk_interface_->OpenFile(temp_path, "wb"); if (!f) { *err = strerror(errno); return false; @@ -425,12 +424,12 @@ bool BuildLog::Recompact(const string& path, const BuildLogUser& user, entries_.erase(dead_outputs[i]); fclose(f); - if (unlink(path.c_str()) < 0) { + if (disk_interface_->RemoveFile(path) < 0) { *err = strerror(errno); return false; } - if (rename(temp_path.c_str(), path.c_str()) < 0) { + if (!disk_interface_->RenameFile(temp_path, path)) { *err = strerror(errno); return false; } @@ -439,14 +438,13 @@ bool BuildLog::Recompact(const string& path, const BuildLogUser& user, } bool BuildLog::Restat(const StringPiece path, - const DiskInterface& disk_interface, const int output_count, char** outputs, std::string* const err) { METRIC_RECORD(".ninja_log restat"); Close(); std::string temp_path = path.AsString() + ".restat"; - FILE* f = fopen(temp_path.c_str(), "wb"); + FILE* f = disk_interface_->OpenFile(temp_path, "wb"); if (!f) { *err = strerror(errno); return false; @@ -466,7 +464,7 @@ bool BuildLog::Restat(const StringPiece path, } } if (!skip) { - const TimeStamp mtime = disk_interface.Stat(i->second->output, err); + const TimeStamp mtime = disk_interface_->Stat(i->second->output, err); if (mtime == -1) { fclose(f); return false; @@ -482,12 +480,12 @@ bool BuildLog::Restat(const StringPiece path, } fclose(f); - if (unlink(path.str_) < 0) { + if (disk_interface_->RemoveFile(path.str_) < 0) { *err = strerror(errno); return false; } - if (rename(temp_path.c_str(), path.str_) < 0) { + if (!disk_interface_->RenameFile(temp_path, path.AsString())) { *err = strerror(errno); return false; } diff --git a/src/build_log.h b/src/build_log.h index 1223c2a16a..7fb9466f26 100644 --- a/src/build_log.h +++ b/src/build_log.h @@ -15,9 +15,11 @@ #ifndef NINJA_BUILD_LOG_H_ #define NINJA_BUILD_LOG_H_ +#include #include #include +#include "disk_interface.h" #include "hash_map.h" #include "load_status.h" #include "timestamp.h" @@ -41,7 +43,10 @@ struct BuildLogUser { /// 2) timing information, perhaps for generating reports /// 3) restat information struct BuildLog { - BuildLog(); + /// Constructor takes a reference to an existing DiskInterface instance. + BuildLog(DiskInterface& disk_interface); + + /// Destructor. ~BuildLog(); /// Prepares writing to the log file without actually opening it - that will @@ -87,21 +92,24 @@ struct BuildLog { std::string* err); /// Restat all outputs in the log - bool Restat(StringPiece path, const DiskInterface& disk_interface, - int output_count, char** outputs, std::string* err); + bool Restat(StringPiece path, int output_count, char** outputs, std::string* err); typedef ExternalStringHashMap::Type Entries; const Entries& entries() const { return entries_; } private: + /// Default destructor is private and never implemented. + BuildLog() = delete; + /// Should be called before using log_file_. When false is returned, errno /// will be set. bool OpenForWriteIfNeeded(); + DiskInterface* disk_interface_ = nullptr; Entries entries_; - FILE* log_file_; + FILE* log_file_ = nullptr; std::string log_file_path_; - bool needs_recompaction_; + bool needs_recompaction_ = false; }; #endif // NINJA_BUILD_LOG_H_ diff --git a/src/build_log_perftest.cc b/src/build_log_perftest.cc index 5a936198fb..66ba096480 100644 --- a/src/build_log_perftest.cc +++ b/src/build_log_perftest.cc @@ -35,7 +35,8 @@ struct NoDeadPaths : public BuildLogUser { }; bool WriteTestData(string* err) { - BuildLog log; + SystemDiskInterface disk_interface; + BuildLog log(disk_interface); NoDeadPaths no_dead_paths; if (!log.OpenForWrite(kTestFilename, no_dead_paths, err)) @@ -109,9 +110,10 @@ int main() { return 1; } + SystemDiskInterface disk_interface; { // Read once to warm up disk cache. - BuildLog log; + BuildLog log(disk_interface); if (log.Load(kTestFilename, &err) == LOAD_ERROR) { fprintf(stderr, "Failed to read test data: %s\n", err.c_str()); return 1; @@ -120,7 +122,7 @@ int main() { const int kNumRepetitions = 5; for (int i = 0; i < kNumRepetitions; ++i) { int64_t start = GetTimeMillis(); - BuildLog log; + BuildLog log(disk_interface); if (log.Load(kTestFilename, &err) == LOAD_ERROR) { fprintf(stderr, "Failed to read test data: %s\n", err.c_str()); return 1; diff --git a/src/build_log_test.cc b/src/build_log_test.cc index 9d9ac21a7f..7107ebe761 100644 --- a/src/build_log_test.cc +++ b/src/build_log_test.cc @@ -36,12 +36,18 @@ const char kTestFilename[] = "BuildLogTest-tempfile"; struct BuildLogTest : public StateTestWithBuiltinRules, public BuildLogUser { virtual void SetUp() { // In case a crashing test left a stale file behind. - unlink(kTestFilename); + disk_interface_.RemoveFile(kTestFilename); } virtual void TearDown() { - unlink(kTestFilename); + disk_interface_.RemoveFile(kTestFilename); } virtual bool IsPathDead(StringPiece s) const { return false; } + + BuildLog CreateBuildLog() { + return BuildLog(disk_interface_); + } + + SystemDiskInterface disk_interface_; }; TEST_F(BuildLogTest, WriteRead) { @@ -49,7 +55,7 @@ TEST_F(BuildLogTest, WriteRead) { "build out: cat mid\n" "build mid: cat in\n"); - BuildLog log1; + BuildLog log1(disk_interface_); string err; EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err)); ASSERT_EQ("", err); @@ -57,7 +63,7 @@ TEST_F(BuildLogTest, WriteRead) { log1.RecordCommand(state_.edges_[1], 20, 25); log1.Close(); - BuildLog log2; + BuildLog log2(disk_interface_); EXPECT_TRUE(log2.Load(kTestFilename, &err)); ASSERT_EQ("", err); @@ -76,7 +82,7 @@ TEST_F(BuildLogTest, FirstWriteAddsSignature) { const char kExpectedVersion[] = "# ninja log vX\n"; const size_t kVersionPos = strlen(kExpectedVersion) - 2; // Points at 'X'. - BuildLog log; + BuildLog log(disk_interface_); string contents, err; EXPECT_TRUE(log.OpenForWrite(kTestFilename, *this, &err)); @@ -103,7 +109,7 @@ TEST_F(BuildLogTest, FirstWriteAddsSignature) { } TEST_F(BuildLogTest, DoubleEntry) { - FILE* f = fopen(kTestFilename, "wb"); + FILE* f = disk_interface_.OpenFile(kTestFilename, "wb"); fprintf(f, "# ninja log v6\n"); fprintf(f, "0\t1\t2\tout\t%" PRIx64 "\n", BuildLog::LogEntry::HashCommand("command abc")); @@ -112,7 +118,7 @@ TEST_F(BuildLogTest, DoubleEntry) { fclose(f); string err; - BuildLog log; + BuildLog log(disk_interface_); EXPECT_TRUE(log.Load(kTestFilename, &err)); ASSERT_EQ("", err); @@ -127,7 +133,7 @@ TEST_F(BuildLogTest, Truncate) { "build mid: cat in\n"); { - BuildLog log1; + BuildLog log1(disk_interface_); string err; EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err)); ASSERT_EQ("", err); @@ -147,7 +153,7 @@ TEST_F(BuildLogTest, Truncate) { // For all possible truncations of the input file, assert that we don't // crash when parsing. for (off_t size = statbuf.st_size; size > 0; --size) { - BuildLog log2; + BuildLog log2(disk_interface_); string err; EXPECT_TRUE(log2.OpenForWrite(kTestFilename, *this, &err)); ASSERT_EQ("", err); @@ -157,33 +163,33 @@ TEST_F(BuildLogTest, Truncate) { ASSERT_TRUE(Truncate(kTestFilename, size, &err)); - BuildLog log3; + BuildLog log3(disk_interface_); err.clear(); ASSERT_TRUE(log3.Load(kTestFilename, &err) == LOAD_SUCCESS || !err.empty()); } } TEST_F(BuildLogTest, ObsoleteOldVersion) { - FILE* f = fopen(kTestFilename, "wb"); + FILE* f = disk_interface_.OpenFile(kTestFilename, "wb"); fprintf(f, "# ninja log v3\n"); fprintf(f, "123 456 0 out command\n"); fclose(f); string err; - BuildLog log; + BuildLog log(disk_interface_); EXPECT_TRUE(log.Load(kTestFilename, &err)); ASSERT_NE(err.find("version"), string::npos); } TEST_F(BuildLogTest, SpacesInOutput) { - FILE* f = fopen(kTestFilename, "wb"); + FILE* f = disk_interface_.OpenFile(kTestFilename, "wb"); fprintf(f, "# ninja log v6\n"); fprintf(f, "123\t456\t456\tout with space\t%" PRIx64 "\n", BuildLog::LogEntry::HashCommand("command")); fclose(f); string err; - BuildLog log; + BuildLog log(disk_interface_); EXPECT_TRUE(log.Load(kTestFilename, &err)); ASSERT_EQ("", err); @@ -199,7 +205,7 @@ TEST_F(BuildLogTest, DuplicateVersionHeader) { // Old versions of ninja accidentally wrote multiple version headers to the // build log on Windows. This shouldn't crash, and the second version header // should be ignored. - FILE* f = fopen(kTestFilename, "wb"); + FILE* f = disk_interface_.OpenFile(kTestFilename, "wb"); fprintf(f, "# ninja log v6\n"); fprintf(f, "123\t456\t456\tout\t%" PRIx64 "\n", BuildLog::LogEntry::HashCommand("command")); @@ -209,7 +215,7 @@ TEST_F(BuildLogTest, DuplicateVersionHeader) { fclose(f); string err; - BuildLog log; + BuildLog log(disk_interface_); EXPECT_TRUE(log.Load(kTestFilename, &err)); ASSERT_EQ("", err); @@ -228,31 +234,42 @@ TEST_F(BuildLogTest, DuplicateVersionHeader) { ASSERT_NO_FATAL_FAILURE(AssertHash("command2", e->command_hash)); } -struct TestDiskInterface : public NullDiskInterface { - TimeStamp Stat(const string& path, string* err) const override { return 4; } +struct TestDiskInterface : public RealDiskInterface { + void EnableMockTimestamps() { enable_mock_ = true; } + + TimeStamp Stat(const string& path, string* err) const override { + if (enable_mock_) + return 4; + else + return this->RealDiskInterface::Stat(path, err); + } + + private: + bool enable_mock_ = false; }; TEST_F(BuildLogTest, Restat) { - FILE* f = fopen(kTestFilename, "wb"); + FILE* f = disk_interface_.OpenFile(kTestFilename, "wb"); fprintf(f, "# ninja log v6\n" "1\t2\t3\tout\tcommand\n"); fclose(f); std::string err; - BuildLog log; + TestDiskInterface testDiskInterface; + BuildLog log(testDiskInterface); EXPECT_TRUE(log.Load(kTestFilename, &err)); ASSERT_EQ("", err); BuildLog::LogEntry* e = log.LookupByOutput("out"); ASSERT_EQ(3, e->mtime); - TestDiskInterface testDiskInterface; char out2[] = { 'o', 'u', 't', '2', 0 }; char* filter2[] = { out2 }; - EXPECT_TRUE(log.Restat(kTestFilename, testDiskInterface, 1, filter2, &err)); + testDiskInterface.EnableMockTimestamps(); + EXPECT_TRUE(log.Restat(kTestFilename, 1, filter2, &err)); ASSERT_EQ("", err); e = log.LookupByOutput("out"); ASSERT_EQ(3, e->mtime); // unchanged, since the filter doesn't match - EXPECT_TRUE(log.Restat(kTestFilename, testDiskInterface, 0, NULL, &err)); + EXPECT_TRUE(log.Restat(kTestFilename, 0, NULL, &err)); ASSERT_EQ("", err); e = log.LookupByOutput("out"); ASSERT_EQ(4, e->mtime); @@ -261,7 +278,7 @@ TEST_F(BuildLogTest, Restat) { TEST_F(BuildLogTest, VeryLongInputLine) { // Ninja's build log buffer is currently 256kB. Lines longer than that are // silently ignored, but don't affect parsing of other lines. - FILE* f = fopen(kTestFilename, "wb"); + FILE* f = disk_interface_.OpenFile(kTestFilename, "wb"); fprintf(f, "# ninja log v6\n"); fprintf(f, "123\t456\t456\tout\tcommand start"); for (size_t i = 0; i < (512 << 10) / strlen(" more_command"); ++i) @@ -272,7 +289,7 @@ TEST_F(BuildLogTest, VeryLongInputLine) { fclose(f); string err; - BuildLog log; + BuildLog log(disk_interface_); EXPECT_TRUE(log.Load(kTestFilename, &err)); ASSERT_EQ("", err); @@ -291,7 +308,7 @@ TEST_F(BuildLogTest, MultiTargetEdge) { AssertParse(&state_, "build out out.d: cat\n"); - BuildLog log; + BuildLog log(disk_interface_); log.RecordCommand(state_.edges_[0], 21, 22); ASSERT_EQ(2u, log.entries().size()); @@ -316,7 +333,7 @@ TEST_F(BuildLogRecompactTest, Recompact) { "build out: cat in\n" "build out2: cat in\n"); - BuildLog log1; + BuildLog log1(disk_interface_); string err; EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err)); ASSERT_EQ("", err); @@ -328,7 +345,7 @@ TEST_F(BuildLogRecompactTest, Recompact) { log1.Close(); // Load... - BuildLog log2; + BuildLog log2(disk_interface_); EXPECT_TRUE(log2.Load(kTestFilename, &err)); ASSERT_EQ("", err); ASSERT_EQ(2u, log2.entries().size()); @@ -339,7 +356,7 @@ TEST_F(BuildLogRecompactTest, Recompact) { log2.Close(); // "out2" is dead, it should've been removed. - BuildLog log3; + BuildLog log3(disk_interface_); EXPECT_TRUE(log2.Load(kTestFilename, &err)); ASSERT_EQ("", err); ASSERT_EQ(1u, log2.entries().size()); diff --git a/src/build_test.cc b/src/build_test.cc index 7675aceecf..8870529c31 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -205,9 +205,9 @@ void PlanTest::TestPoolWithDepthOne(const char* test_case) { GetNode("out1")->MarkDirty(); GetNode("out2")->MarkDirty(); string err; - EXPECT_TRUE(plan_.AddTarget(GetNode("out1"), &err)); + EXPECT_TRUE(plan_.AddTarget(GetNode("out1"), &err)) << err; ASSERT_EQ("", err); - EXPECT_TRUE(plan_.AddTarget(GetNode("out2"), &err)); + EXPECT_TRUE(plan_.AddTarget(GetNode("out2"), &err)) << err; ASSERT_EQ("", err); plan_.PrepareQueue(); ASSERT_TRUE(plan_.more_to_do()); @@ -490,7 +490,9 @@ TEST_F(PlanTest, PriorityWithoutBuildLog) { GetNode("b0")->MarkDirty(); GetNode("c0")->MarkDirty(); GetNode("out")->MarkDirty(); - BuildLog log; + + SystemDiskInterface disk_interface; + BuildLog log(disk_interface); PrepareForTarget("out", &log); EXPECT_EQ(GetNode("out")->in_edge()->critical_path_weight(), 1); @@ -594,7 +596,8 @@ void BuildTest::RebuildTarget(const string& target, const char* manifest, AssertParse(pstate, manifest); string err; - BuildLog build_log, *pbuild_log = NULL; + SystemDiskInterface disk_interface; + BuildLog build_log(disk_interface), *pbuild_log = NULL; if (log_path) { ASSERT_TRUE(build_log.Load(log_path, &err)); ASSERT_TRUE(build_log.OpenForWrite(log_path, *this, &err)); @@ -1540,7 +1543,8 @@ struct BuildWithLogTest : public BuildTest { builder_.SetBuildLog(&build_log_); } - BuildLog build_log_; + SystemDiskInterface disk_interface_; + BuildLog build_log_{ disk_interface_ }; }; TEST_F(BuildWithLogTest, ImplicitGeneratedOutOfDate) { @@ -2720,9 +2724,11 @@ TEST_F(BuildWithDepsLogTest, TestInputMtimeRaceCondition) { ASSERT_NO_FATAL_FAILURE(AddCatRule(&state)); ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest)); - BuildLog build_log; - ASSERT_TRUE(build_log.Load(build_log_file_.path(), &err)); - ASSERT_TRUE(build_log.OpenForWrite(build_log_file_.path(), *this, &err)); + SystemDiskInterface disk_interface; + BuildLog build_log(disk_interface); + ASSERT_TRUE(build_log.Load(build_log_file_.path(), &err)) << err; + ASSERT_TRUE(build_log.OpenForWrite(build_log_file_.path(), *this, &err)) + << err; DepsLog deps_log; ASSERT_TRUE(deps_log.Load(deps_log_file_.path(), &state, &err)); @@ -2802,7 +2808,8 @@ TEST_F(BuildWithDepsLogTest, TestInputMtimeRaceConditionWithDepFile) { State state; ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest)); - BuildLog build_log; + SystemDiskInterface disk_interface; + BuildLog build_log(disk_interface); ASSERT_TRUE(build_log.Load(build_log_file_.path(), &err)); ASSERT_TRUE(build_log.OpenForWrite(build_log_file_.path(), *this, &err)); @@ -3077,7 +3084,8 @@ TEST_F(BuildWithDepsLogTest, DiscoveredDepDuringBuildChanged) { fs_.Create("in1", ""); fs_.Tick(); - BuildLog build_log; + SystemDiskInterface disk_interface; + BuildLog build_log(disk_interface); { State state; diff --git a/src/clean_test.cc b/src/clean_test.cc index e99909c0d0..3a33cdcd64 100644 --- a/src/clean_test.cc +++ b/src/clean_test.cc @@ -13,10 +13,11 @@ // limitations under the License. #include "clean.h" -#include "build.h" -#include "util.h" +#include "build.h" +#include "disk_interface.h" #include "test.h" +#include "util.h" #ifndef _WIN32 #include @@ -469,13 +470,13 @@ TEST_F(CleanTest, CleanDepFileAndRspFileWithSpaces) { struct CleanDeadTest : public CleanTest, public BuildLogUser{ virtual void SetUp() { // In case a crashing test left a stale file behind. - unlink(kTestFilename); + disk_interface_.RemoveFile(kTestFilename); CleanTest::SetUp(); } - virtual void TearDown() { - unlink(kTestFilename); - } + virtual void TearDown() { disk_interface_.RemoveFile(kTestFilename); } virtual bool IsPathDead(StringPiece) const { return false; } + + SystemDiskInterface disk_interface_; }; TEST_F(CleanDeadTest, CleanDead) { @@ -493,7 +494,7 @@ TEST_F(CleanDeadTest, CleanDead) { fs_.Create("out1", ""); fs_.Create("out2", ""); - BuildLog log1; + BuildLog log1(disk_interface_); string err; EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err)); ASSERT_EQ("", err); @@ -501,7 +502,7 @@ TEST_F(CleanDeadTest, CleanDead) { log1.RecordCommand(state.edges_[1], 20, 25); log1.Close(); - BuildLog log2; + BuildLog log2(disk_interface_); EXPECT_TRUE(log2.Load(kTestFilename, &err)); ASSERT_EQ("", err); ASSERT_EQ(2u, log2.entries().size()); @@ -556,7 +557,7 @@ TEST_F(CleanDeadTest, CleanDeadPreservesInputs) { fs_.Create("out1", ""); fs_.Create("out2", ""); - BuildLog log1; + BuildLog log1(disk_interface_); string err; EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err)); ASSERT_EQ("", err); @@ -564,7 +565,7 @@ TEST_F(CleanDeadTest, CleanDeadPreservesInputs) { log1.RecordCommand(state.edges_[1], 20, 25); log1.Close(); - BuildLog log2; + BuildLog log2(disk_interface_); EXPECT_TRUE(log2.Load(kTestFilename, &err)); ASSERT_EQ("", err); ASSERT_EQ(2u, log2.entries().size()); diff --git a/src/disk_interface.cc b/src/disk_interface.cc index d6e6ff41b1..86d4fb559a 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -27,6 +27,7 @@ #include // _mkdir #include +#include #include #else #include diff --git a/src/ninja.cc b/src/ninja.cc index 7885bb3682..aa49a208c5 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -86,7 +86,7 @@ struct Options { struct NinjaMain : public BuildLogUser { NinjaMain(const char* ninja_command, const BuildConfig& config) : ninja_command_(ninja_command), config_(config), - start_time_millis_(GetTimeMillis()) {} + build_log_(disk_interface_), start_time_millis_(GetTimeMillis()) {} /// Command line used to run Ninja. const char* ninja_command_; @@ -1071,7 +1071,7 @@ int NinjaMain::ToolRestat(const Options* options, int argc, char* argv[]) { err.clear(); } - bool success = build_log_.Restat(log_path, disk_interface_, argc, argv, &err); + bool success = build_log_.Restat(log_path, argc, argv, &err); if (!success) { Error("failed recompaction: %s", err.c_str()); return EXIT_FAILURE;