summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Niklas Hasse <jhasse@bixense.com>2019-11-16 16:09:06 +0100
committerJan Niklas Hasse <jhasse@bixense.com>2019-12-23 14:11:09 +0100
commit8f87ee0469820d247fa20971c07dbad5f2aa744e (patch)
tree6e7fba2da028d4ebfbae6b475cdef636c9e50fd4
parenta67718de4e5e4f78b6f66c6a3ecd0432941c7df3 (diff)
downloadninja-8f87ee0469820d247fa20971c07dbad5f2aa744e.tar.gz
Add restat tool which recalculates all mtimes in the build log
-rw-r--r--src/build_log.cc48
-rw-r--r--src/build_log.h5
-rw-r--r--src/build_log_test.cc42
-rw-r--r--src/ninja.cc38
4 files changed, 133 insertions, 0 deletions
diff --git a/src/build_log.cc b/src/build_log.cc
index c4a08a0..0b06cc5 100644
--- a/src/build_log.cc
+++ b/src/build_log.cc
@@ -21,6 +21,7 @@
#endif
#include "build_log.h"
+#include "disk_interface.h"
#include <errno.h>
#include <stdlib.h>
@@ -418,3 +419,50 @@ bool BuildLog::Recompact(const string& path, const BuildLogUser& user,
return true;
}
+
+bool BuildLog::Restat(const StringPiece path,
+ const DiskInterface& disk_interface,
+ 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");
+ if (!f) {
+ *err = strerror(errno);
+ return false;
+ }
+
+ if (fprintf(f, kFileSignature, kCurrentVersion) < 0) {
+ *err = strerror(errno);
+ fclose(f);
+ return false;
+ }
+ for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
+ const TimeStamp mtime = disk_interface.Stat(i->second->output, err);
+ if (mtime == -1) {
+ fclose(f);
+ return false;
+ }
+ i->second->mtime = mtime;
+
+ if (!WriteEntry(f, *i->second)) {
+ *err = strerror(errno);
+ fclose(f);
+ return false;
+ }
+ }
+
+ fclose(f);
+ if (unlink(path.str_) < 0) {
+ *err = strerror(errno);
+ return false;
+ }
+
+ if (rename(temp_path.c_str(), path.str_) < 0) {
+ *err = strerror(errno);
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/build_log.h b/src/build_log.h
index 5268fab..d52dd3b 100644
--- a/src/build_log.h
+++ b/src/build_log.h
@@ -23,6 +23,7 @@ using namespace std;
#include "timestamp.h"
#include "util.h" // uint64_t
+struct DiskInterface;
struct Edge;
/// Can answer questions about the manifest for the BuildLog.
@@ -81,6 +82,10 @@ struct BuildLog {
/// Rewrite the known log entries, throwing away old data.
bool Recompact(const string& path, const BuildLogUser& user, string* err);
+ /// Restat all outputs in the log
+ bool Restat(StringPiece path, const DiskInterface& disk_interface,
+ std::string* err);
+
typedef ExternalStringHashMap<LogEntry*>::Type Entries;
const Entries& entries() const { return entries_; }
diff --git a/src/build_log_test.cc b/src/build_log_test.cc
index ad30380..eee8290 100644
--- a/src/build_log_test.cc
+++ b/src/build_log_test.cc
@@ -25,6 +25,7 @@
#include <sys/types.h>
#include <unistd.h>
#endif
+#include <cassert>
namespace {
@@ -216,6 +217,47 @@ TEST_F(BuildLogTest, DuplicateVersionHeader) {
ASSERT_NO_FATAL_FAILURE(AssertHash("command2", e->command_hash));
}
+struct TestDiskInterface : public DiskInterface {
+ virtual TimeStamp Stat(const string& path, string* err) const {
+ return 4;
+ }
+ virtual bool WriteFile(const string& path, const string& contents) {
+ assert(false);
+ return true;
+ }
+ virtual bool MakeDir(const string& path) {
+ assert(false);
+ return false;
+ }
+ virtual Status ReadFile(const string& path, string* contents, string* err) {
+ assert(false);
+ return NotFound;
+ }
+ virtual int RemoveFile(const string& path) {
+ assert(false);
+ return 0;
+ }
+};
+
+TEST_F(BuildLogTest, Restat) {
+ FILE* f = fopen(kTestFilename, "wb");
+ fprintf(f, "# ninja log v4\n"
+ "1\t2\t3\tout\tcommand\n");
+ fclose(f);
+ std::string err;
+ BuildLog log;
+ EXPECT_TRUE(log.Load(kTestFilename, &err));
+ ASSERT_EQ("", err);
+ BuildLog::LogEntry* e = log.LookupByOutput("out");
+ ASSERT_EQ(3, e->mtime);
+
+ TestDiskInterface testDiskInterface;
+ EXPECT_TRUE(log.Restat(kTestFilename, testDiskInterface, &err));
+ ASSERT_EQ("", err);
+ e = log.LookupByOutput("out");
+ ASSERT_EQ(4, e->mtime);
+}
+
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.
diff --git a/src/ninja.cc b/src/ninja.cc
index c24f09d..8b76ded 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -125,6 +125,7 @@ struct NinjaMain : public BuildLogUser {
int ToolClean(const Options* options, int argc, char* argv[]);
int ToolCompilationDatabase(const Options* options, int argc, char* argv[]);
int ToolRecompact(const Options* options, int argc, char* argv[]);
+ int ToolRestat(const Options* options, int argc, char* argv[]);
int ToolUrtle(const Options* options, int argc, char** argv);
int ToolRules(const Options* options, int argc, char* argv[]);
@@ -852,6 +853,41 @@ int NinjaMain::ToolRecompact(const Options* options, int argc, char* argv[]) {
return 0;
}
+int NinjaMain::ToolRestat(const Options* options, int argc, char* argv[]) {
+ if (!EnsureBuildDirExists())
+ return 1;
+
+ string log_path = ".ninja_log";
+ if (!build_dir_.empty())
+ log_path = build_dir_ + "/" + log_path;
+
+ string err;
+ if (!build_log_.Load(log_path, &err)) {
+ Error("loading build log %s: %s", log_path.c_str(), err.c_str());
+ return EXIT_FAILURE;
+ }
+ if (!err.empty()) {
+ // Hack: Load() can return a warning via err by returning true.
+ Warning("%s", err.c_str());
+ err.clear();
+ }
+
+ bool success = build_log_.Restat(log_path, disk_interface_, &err);
+ if (!success) {
+ Error("failed recompaction: %s", err.c_str());
+ return EXIT_FAILURE;
+ }
+
+ if (!config_.dry_run) {
+ if (!build_log_.OpenForWrite(log_path, *this, &err)) {
+ Error("opening build log: %s", err.c_str());
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
+
int NinjaMain::ToolUrtle(const Options* options, int argc, char** argv) {
// RLE encoded.
const char* urtle =
@@ -904,6 +940,8 @@ const Tool* ChooseTool(const string& tool_name) {
Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCompilationDatabase },
{ "recompact", "recompacts ninja-internal data structures",
Tool::RUN_AFTER_LOAD, &NinjaMain::ToolRecompact },
+ { "restat", "restats all outputs in the build log",
+ Tool::RUN_AFTER_LOAD, &NinjaMain::ToolRestat },
{ "rules", "list all rules",
Tool::RUN_AFTER_LOAD, &NinjaMain::ToolRules },
{ "urtle", NULL,