It's the only piece that cares about the current time.
public CommandRunner {
BuildTest() : config_(MakeConfig()),
builder_(&state_, config_, NULL, &fs_),
- now_(1), last_command_(NULL), status_(config_) {
+ last_command_(NULL), status_(config_) {
builder_.command_runner_.reset(this);
AssertParse(&state_,
"build cat1: cat in1\n"
"build cat2: cat in1 in2\n"
"build cat12: cat cat1 cat2\n");
- fs_.Create("in1", now_, "");
- fs_.Create("in2", now_, "");
+ fs_.Create("in1", "");
+ fs_.Create("in2", "");
}
~BuildTest() {
BuildConfig config_;
VirtualFileSystem fs_;
Builder builder_;
- int now_;
vector<string> commands_ran_;
Edge* last_command_;
edge->rule().name() == "touch-interrupt") {
for (vector<Node*>::iterator out = edge->outputs_.begin();
out != edge->outputs_.end(); ++out) {
- fs_.Create((*out)->path(), now_, "");
+ fs_.Create((*out)->path(), "");
}
} else if (edge->rule().name() == "true" ||
edge->rule().name() == "fail" ||
EXPECT_EQ("cat cat1 cat2 > cat12", commands_ran_[2]);
- now_++;
+ fs_.Tick();
// Modifying in2 requires rebuilding one intermediate file
// and the final file.
- fs_.Create("in2", now_, "");
+ fs_.Create("in2", "");
state_.Reset();
EXPECT_TRUE(builder_.AddTarget("cat12", &err));
ASSERT_EQ("", err);
" command = touch $out\n"
"build out1 out2: touch in.txt\n"));
- fs_.Create("in.txt", now_, "");
+ fs_.Create("in.txt", "");
string err;
EXPECT_TRUE(builder_.AddTarget("out1", &err));
"build in1 otherfile: touch in\n"
"build out: touch in | in1\n"));
- fs_.Create("in", now_, "");
- fs_.Create("in1", ++now_, "");
+ fs_.Create("in", "");
+ fs_.Tick();
+ fs_.Create("in1", "");
string err;
EXPECT_TRUE(builder_.AddTarget("out", &err));
"build c4: cat c3\n"
"build c5: cat c4\n"));
- fs_.Create("c1", now_, "");
+ fs_.Create("c1", "");
string err;
EXPECT_TRUE(builder_.AddTarget("c5", &err));
ASSERT_EQ("", err);
EXPECT_TRUE(builder_.AlreadyUpToDate());
- now_++;
+ fs_.Tick();
- fs_.Create("c3", now_, "");
+ fs_.Create("c3", "");
err.clear();
commands_ran_.clear();
state_.Reset();
#endif
EXPECT_EQ("", err);
- now_ = 0; // Make all stat()s return file not found.
EXPECT_TRUE(builder_.Build(&err));
ASSERT_EQ("", err);
ASSERT_EQ(2u, fs_.directories_made_.size());
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
"rule cc\n command = cc $in\n depfile = $out.d\n"
"build foo.o: cc foo.c\n"));
- fs_.Create("foo.c", now_, "");
+ fs_.Create("foo.c", "");
EXPECT_TRUE(builder_.AddTarget("foo.o", &err));
ASSERT_EQ("", err);
"build foo.o: cc foo.c\n"));
Edge* edge = state_.edges_.back();
- fs_.Create("foo.c", now_, "");
+ fs_.Create("foo.c", "");
GetNode("bar.h")->MarkDirty(); // Mark bar.h as missing.
- fs_.Create("foo.o.d", now_, "foo.o: blah.h bar.h\n");
+ fs_.Create("foo.o.d", "foo.o: blah.h bar.h\n");
EXPECT_TRUE(builder_.AddTarget("foo.o", &err));
ASSERT_EQ("", err);
ASSERT_EQ(1u, fs_.files_read_.size());
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
"rule cc\n command = cc $in\n depfile = $out.d\n"
"build foo.o: cc foo.c\n"));
- fs_.Create("foo.c", now_, "");
- fs_.Create("foo.o.d", now_, "randomtext\n");
+ fs_.Create("foo.c", "");
+ fs_.Create("foo.o.d", "randomtext\n");
EXPECT_FALSE(builder_.AddTarget("foo.o", &err));
EXPECT_EQ("expected depfile 'foo.o.d' to mention 'foo.o', got 'randomtext'",
err);
"build foo.o: cc foo.c || otherfile\n"));
Edge* edge = state_.edges_.back();
- fs_.Create("foo.c", now_, "");
- fs_.Create("otherfile", now_, "");
- fs_.Create("foo.o.d", now_, "foo.o: blah.h bar.h\n");
+ fs_.Create("foo.c", "");
+ fs_.Create("otherfile", "");
+ fs_.Create("foo.o.d", "foo.o: blah.h bar.h\n");
EXPECT_TRUE(builder_.AddTarget("foo.o", &err));
ASSERT_EQ("", err);
ASSERT_EQ("", err);
ASSERT_EQ(1u, commands_ran_.size());
- now_++;
+ fs_.Tick();
// implicit dep dirty, expect a rebuild.
- fs_.Create("blah.h", now_, "");
- fs_.Create("bar.h", now_, "");
+ fs_.Create("blah.h", "");
+ fs_.Create("bar.h", "");
commands_ran_.clear();
state_.Reset();
EXPECT_TRUE(builder_.AddTarget("foo.o", &err));
ASSERT_EQ("", err);
ASSERT_EQ(1u, commands_ran_.size());
- now_++;
+ fs_.Tick();
// order only dep dirty, no rebuild.
- fs_.Create("otherfile", now_, "");
+ fs_.Create("otherfile", "");
commands_ran_.clear();
state_.Reset();
EXPECT_TRUE(builder_.AddTarget("foo.o", &err));
"build oo.h: cc oo.h.in\n"
"build foo.o: cc foo.c || oo.h\n"));
- fs_.Create("foo.c", now_, "");
- fs_.Create("oo.h.in", now_, "");
+ fs_.Create("foo.c", "");
+ fs_.Create("oo.h.in", "");
// foo.o and order-only dep dirty, build both.
EXPECT_TRUE(builder_.AddTarget("foo.o", &err));
ASSERT_EQ(1u, commands_ran_.size());
ASSERT_EQ("cc oo.h.in", commands_ran_[0]);
- now_++;
+ fs_.Tick();
// order-only dep dirty, build it only.
- fs_.Create("oo.h.in", now_, "");
+ fs_.Create("oo.h.in", "");
commands_ran_.clear();
state_.Reset();
EXPECT_TRUE(builder_.AddTarget("foo.o", &err));
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
"build out: cat bar.cc\n"
"build all: phony out\n"));
- fs_.Create("bar.cc", now_, "");
+ fs_.Create("bar.cc", "");
EXPECT_TRUE(builder_.AddTarget("all", &err));
ASSERT_EQ("", err);
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
"build out: cat bar.cc\n"
"build all: phony out\n"));
- fs_.Create("bar.cc", now_, "");
- fs_.Create("out", now_, "");
+ fs_.Create("bar.cc", "");
+ fs_.Create("out", "");
EXPECT_TRUE(builder_.AddTarget("all", &err));
ASSERT_EQ("", err);
// Create input/output that would be considered up to date when
// not considering the command line hash.
- fs_.Create("in", now_, "");
- fs_.Create("out1", now_, "");
+ fs_.Create("in", "");
+ fs_.Create("out1", "");
string err;
// Because it's not in the log, it should not be up-to-date until
"build out2: true out1\n"
"build out3: cat out2\n"));
- fs_.Create("out1", now_, "");
- fs_.Create("out2", now_, "");
- fs_.Create("out3", now_, "");
+ fs_.Create("out1", "");
+ fs_.Create("out2", "");
+ fs_.Create("out3", "");
- now_++;
+ fs_.Tick();
- fs_.Create("in", now_, "");
+ fs_.Create("in", "");
// Do a pre-build so that there's commands in the log for the outputs,
// otherwise, the lack of an entry in the build log will cause out3 to rebuild
commands_ran_.clear();
state_.Reset();
- now_++;
+ fs_.Tick();
- fs_.Create("in", now_, "");
+ fs_.Create("in", "");
// "cc" touches out1, so we should build out2. But because "true" does not
// touch out2, we should cancel the build of out3.
EXPECT_TRUE(builder_.AddTarget("out3", &err));
ASSERT_EQ("", err);
EXPECT_TRUE(builder_.AlreadyUpToDate());
- now_++;
+ fs_.Tick();
- fs_.Create("in", now_, "");
+ fs_.Create("in", "");
// The build log entry should not, however, prevent us from rebuilding out2
// if out1 changes.
"build out1: true in\n"
"build out2: cc out1\n"));
- fs_.Create("in", now_, "");
- fs_.Create("out2", now_, "");
+ fs_.Create("in", "");
+ fs_.Create("out2", "");
// Do a pre-build so that there's commands in the log for the outputs,
// otherwise, the lack of an entry in the build log will cause out2 to rebuild
commands_ran_.clear();
state_.Reset();
- now_++;
- fs_.Create("in", now_, "");
- fs_.Create("out2", now_, "");
+ fs_.Tick();
+ fs_.Create("in", "");
+ fs_.Create("out2", "");
// Run a build, expect only the first command to run.
// It doesn't touch its output (due to being the "true" command), so
"build out2: cc out1\n"));
// Create all necessary files
- fs_.Create("in", now_, "");
+ fs_.Create("in", "");
// The implicit dependencies and the depfile itself
// are newer than the output
- TimeStamp restat_mtime = ++now_;
- fs_.Create("out1.d", now_, "out1: will.be.deleted restat.file\n");
- fs_.Create("will.be.deleted", now_, "");
- fs_.Create("restat.file", now_, "");
+ TimeStamp restat_mtime = fs_.Tick();
+ fs_.Create("out1.d", "out1: will.be.deleted restat.file\n");
+ fs_.Create("will.be.deleted", "");
+ fs_.Create("restat.file", "");
// Run the build, out1 and out2 get built
string err;
"build out2: true out1\n"
"build out3: cat out2\n"));
- fs_.Create("out1", now_, "");
- fs_.Create("out2", now_, "");
- fs_.Create("out3", now_, "");
+ fs_.Create("out1", "");
+ fs_.Create("out2", "");
+ fs_.Create("out3", "");
- now_++;
+ fs_.Tick();
- fs_.Create("in", now_, "");
+ fs_.Create("in", "");
// "cc" touches out1, so we should build out2. But because "true" does not
// touch out2, we should cancel the build of out3.
" rspfile = out2.rsp\n"
" long_command = Some very long command\n"));
- fs_.Create("out1", now_, "");
- fs_.Create("out2", now_, "");
- fs_.Create("out3", now_, "");
+ fs_.Create("out1", "");
+ fs_.Create("out2", "");
+ fs_.Create("out3", "");
- now_++;
+ fs_.Tick();
- fs_.Create("in", now_, "");
+ fs_.Create("in", "");
string err;
EXPECT_TRUE(builder_.AddTarget("out1", &err));
" rspfile = out.rsp\n"
" long_command = Another very long command\n"));
- fs_.Create("out", now_, "");
- now_++;
- fs_.Create("in", now_, "");
+ fs_.Create("out", "");
+ fs_.Tick();
+ fs_.Create("in", "");
string err;
EXPECT_TRUE(builder_.AddTarget("out", &err));
" rspfile = out.rsp\n"
" long_command = Original very long command\n"));
- fs_.Create("out", now_, "");
- now_++;
- fs_.Create("in", now_, "");
+ fs_.Create("out", "");
+ fs_.Tick();
+ fs_.Create("in", "");
string err;
EXPECT_TRUE(builder_.AddTarget("out", &err));
"build out1: interrupt in1\n"
"build out2: touch-interrupt in2\n"));
- fs_.Create("out1", now_, "");
- fs_.Create("out2", now_, "");
- now_++;
- fs_.Create("in1", now_, "");
- fs_.Create("in2", now_, "");
+ fs_.Create("out1", "");
+ fs_.Create("out2", "");
+ fs_.Tick();
+ fs_.Create("in1", "");
+ fs_.Create("in2", "");
// An untouched output of an interrupted command should be retained.
string err;
EXPECT_FALSE(builder_.Build(&err));
EXPECT_EQ("interrupted by user", err);
builder_.Cleanup();
- EXPECT_EQ(now_-1, fs_.Stat("out1"));
+ EXPECT_GT(fs_.Stat("out1"), 0);
err = "";
// A touched output of an interrupted command should be deleted.
"build nonexistent: phony\n"
"build out1: cat || nonexistent\n"
"build out2: cat nonexistent\n"));
- fs_.Create("out1", now_, "");
- fs_.Create("out2", now_, "");
+ fs_.Create("out1", "");
+ fs_.Create("out2", "");
// out1 should be up to date even though its input is dirty, because its
// order-only dependency has nothing to do.
"build out1: cat in1\n"
"build in2: cat src2\n"
"build out2: cat in2\n"));
- fs_.Create("in1", 1, "");
- fs_.Create("out1", 1, "");
- fs_.Create("in2", 1, "");
- fs_.Create("out2", 1, "");
+ fs_.Create("in1", "");
+ fs_.Create("out1", "");
+ fs_.Create("in2", "");
+ fs_.Create("out2", "");
Cleaner cleaner(&state_, config_, &fs_);
"build out1: cat in1\n"
"build in2: cat src2\n"
"build out2: cat in2\n"));
- fs_.Create("in1", 1, "");
- fs_.Create("out1", 1, "");
- fs_.Create("in2", 1, "");
- fs_.Create("out2", 1, "");
+ fs_.Create("in1", "");
+ fs_.Create("out1", "");
+ fs_.Create("in2", "");
+ fs_.Create("out2", "");
config_.dry_run = true;
Cleaner cleaner(&state_, config_, &fs_);
"build out1: cat in1\n"
"build in2: cat src2\n"
"build out2: cat in2\n"));
- fs_.Create("in1", 1, "");
- fs_.Create("out1", 1, "");
- fs_.Create("in2", 1, "");
- fs_.Create("out2", 1, "");
+ fs_.Create("in1", "");
+ fs_.Create("out1", "");
+ fs_.Create("in2", "");
+ fs_.Create("out2", "");
Cleaner cleaner(&state_, config_, &fs_);
"build out1: cat in1\n"
"build in2: cat src2\n"
"build out2: cat in2\n"));
- fs_.Create("in1", 1, "");
- fs_.Create("out1", 1, "");
- fs_.Create("in2", 1, "");
- fs_.Create("out2", 1, "");
+ fs_.Create("in1", "");
+ fs_.Create("out1", "");
+ fs_.Create("in2", "");
+ fs_.Create("out2", "");
config_.dry_run = true;
Cleaner cleaner(&state_, config_, &fs_);
"build out1: cat in1\n"
"build in2: cat_e src2\n"
"build out2: cat in2\n"));
- fs_.Create("in1", 1, "");
- fs_.Create("out1", 1, "");
- fs_.Create("in2", 1, "");
- fs_.Create("out2", 1, "");
+ fs_.Create("in1", "");
+ fs_.Create("out1", "");
+ fs_.Create("in2", "");
+ fs_.Create("out2", "");
Cleaner cleaner(&state_, config_, &fs_);
"build out1: cat in1\n"
"build in2: cat_e src2\n"
"build out2: cat in2\n"));
- fs_.Create("in1", 1, "");
- fs_.Create("out1", 1, "");
- fs_.Create("in2", 1, "");
- fs_.Create("out2", 1, "");
+ fs_.Create("in1", "");
+ fs_.Create("out1", "");
+ fs_.Create("in2", "");
+ fs_.Create("out2", "");
config_.dry_run = true;
Cleaner cleaner(&state_, config_, &fs_);
" generator = 1\n"
"build out1: cat in1\n"
"build out2: regen in2\n"));
- fs_.Create("out1", 1, "");
- fs_.Create("out2", 1, "");
+ fs_.Create("out1", "");
+ fs_.Create("out2", "");
Cleaner cleaner(&state_, config_, &fs_);
EXPECT_EQ(0, cleaner.CleanAll());
EXPECT_EQ(1, cleaner.cleaned_files_count());
EXPECT_EQ(1u, fs_.files_removed_.size());
- fs_.Create("out1", 1, "");
+ fs_.Create("out1", "");
EXPECT_EQ(0, cleaner.CleanAll(/*generator=*/true));
EXPECT_EQ(2, cleaner.cleaned_files_count());
" command = cc $in > $out\n"
" depfile = $out.d\n"
"build out1: cc in1\n"));
- fs_.Create("out1", 1, "");
- fs_.Create("out1.d", 1, "");
+ fs_.Create("out1", "");
+ fs_.Create("out1.d", "");
Cleaner cleaner(&state_, config_, &fs_);
EXPECT_EQ(0, cleaner.CleanAll());
" command = cc $in > $out\n"
" depfile = $out.d\n"
"build out1: cc in1\n"));
- fs_.Create("out1", 1, "");
- fs_.Create("out1.d", 1, "");
+ fs_.Create("out1", "");
+ fs_.Create("out1.d", "");
Cleaner cleaner(&state_, config_, &fs_);
EXPECT_EQ(0, cleaner.CleanTarget("out1"));
" command = cc $in > $out\n"
" depfile = $out.d\n"
"build out1: cc in1\n"));
- fs_.Create("out1", 1, "");
- fs_.Create("out1.d", 1, "");
+ fs_.Create("out1", "");
+ fs_.Create("out1.d", "");
Cleaner cleaner(&state_, config_, &fs_);
EXPECT_EQ(0, cleaner.CleanRule("cc"));
"build out1: cc in1\n"
" rspfile = cc1.rsp\n"
" rspfile_content=$in\n"));
- fs_.Create("out1", 1, "");
- fs_.Create("cc1.rsp", 1, "");
+ fs_.Create("out1", "");
+ fs_.Create("cc1.rsp", "");
Cleaner cleaner(&state_, config_, &fs_);
EXPECT_EQ(0, cleaner.CleanAll());
"build out2: cat_rsp in2\n"
" rspfile=out2.rsp\n"
" rspfile_content=$in\n"));
- fs_.Create("in1", 1, "");
- fs_.Create("out1", 1, "");
- fs_.Create("in2.rsp", 1, "");
- fs_.Create("out2.rsp", 1, "");
- fs_.Create("in2", 1, "");
- fs_.Create("out2", 1, "");
+ fs_.Create("in1", "");
+ fs_.Create("out1", "");
+ fs_.Create("in2.rsp", "");
+ fs_.Create("out2.rsp", "");
+ fs_.Create("in2", "");
+ fs_.Create("out2", "");
Cleaner cleaner(&state_, config_, &fs_);
ASSERT_EQ(0, cleaner.cleaned_files_count());
"build t1: cat\n"
"build t2: cat\n"));
- fs_.Create("phony", 1, "");
- fs_.Create("t1", 1, "");
- fs_.Create("t2", 1, "");
+ fs_.Create("phony", "");
+ fs_.Create("t1", "");
+ fs_.Create("t2", "");
// Check that CleanAll does not remove "phony".
Cleaner cleaner(&state_, config_, &fs_);
EXPECT_EQ(2, cleaner.cleaned_files_count());
EXPECT_NE(0, fs_.Stat("phony"));
- fs_.Create("t1", 1, "");
- fs_.Create("t2", 1, "");
+ fs_.Create("t1", "");
+ fs_.Create("t2", "");
// Check that CleanTarget does not remove "phony".
EXPECT_EQ(0, cleaner.CleanTarget("phony"));
TEST_F(GraphTest, MissingImplicit) {
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
"build out: cat in | implicit\n"));
- fs_.Create("in", 1, "");
- fs_.Create("out", 1, "");
+ fs_.Create("in", "");
+ fs_.Create("out", "");
Edge* edge = GetNode("out")->in_edge();
string err;
TEST_F(GraphTest, ModifiedImplicit) {
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
"build out: cat in | implicit\n"));
- fs_.Create("in", 1, "");
- fs_.Create("out", 1, "");
- fs_.Create("implicit", 2, "");
+ fs_.Create("in", "");
+ fs_.Create("out", "");
+ fs_.Tick();
+ fs_.Create("implicit", "");
Edge* edge = GetNode("out")->in_edge();
string err;
" depfile = $out.d\n"
" command = cat $in > $out\n"
"build out.o: catdep foo.cc\n"));
- fs_.Create("implicit.h", 2, "");
- fs_.Create("foo.cc", 1, "");
- fs_.Create("out.o.d", 1, "out.o: ./foo/../implicit.h\n");
- fs_.Create("out.o", 1, "");
+ fs_.Create("foo.cc", "");
+ fs_.Create("out.o.d", "out.o: ./foo/../implicit.h\n");
+ fs_.Create("out.o", "");
+ fs_.Tick();
+ fs_.Create("implicit.h", "");
Edge* edge = GetNode("out.o")->in_edge();
string err;
" command = cat $in > $out\n"
"build implicit.h: cat data\n"
"build out.o: catdep foo.cc || implicit.h\n"));
- fs_.Create("data", 2, "");
- fs_.Create("implicit.h", 1, "");
- fs_.Create("foo.cc", 1, "");
- fs_.Create("out.o.d", 1, "out.o: implicit.h\n");
- fs_.Create("out.o", 1, "");
+ fs_.Create("implicit.h", "");
+ fs_.Create("foo.cc", "");
+ fs_.Create("out.o.d", "out.o: implicit.h\n");
+ fs_.Create("out.o", "");
+ fs_.Tick();
+ fs_.Create("data", "");
Edge* edge = GetNode("out.o")->in_edge();
string err;
" depfile = $out.d\n"
" command = cat $in > $out\n"
"build ./out.o: catdep ./foo.cc\n"));
- fs_.Create("foo.cc", 1, "");
- fs_.Create("out.o.d", 1, "out.o: foo.cc\n");
- fs_.Create("out.o", 1, "");
+ fs_.Create("foo.cc", "");
+ fs_.Create("out.o.d", "out.o: foo.cc\n");
+ fs_.Create("out.o", "");
Edge* edge = GetNode("out.o")->in_edge();
string err;
" depfile = $out.d\n"
" command = cat $in > $out\n"
"build ./out.o: catdep ./foo.cc\n"));
- fs_.Create("foo.cc", 1, "");
- fs_.Create("out.o.d", 1, "out.o: bar/../foo.cc\n");
- fs_.Create("out.o", 1, "");
+ fs_.Create("foo.cc", "");
+ fs_.Create("out.o.d", "out.o: bar/../foo.cc\n");
+ fs_.Create("out.o", "");
Edge* edge = GetNode("out.o")->in_edge();
string err;
" depfile = $out.d\n"
" command = cat $in > $out\n"
"build ./out.o: catdep ./foo.cc\n"));
- fs_.Create("foo.h", 1, "");
- fs_.Create("foo.cc", 1, "");
- fs_.Create("out.o.d", 2, "out.o: foo.h\n");
- fs_.Create("out.o", 2, "");
+ fs_.Create("foo.h", "");
+ fs_.Create("foo.cc", "");
+ fs_.Tick();
+ fs_.Create("out.o.d", "out.o: foo.h\n");
+ fs_.Create("out.o", "");
Edge* edge = GetNode("out.o")->in_edge();
string err;
ASSERT_EQ(BuildLog::LogEntry::HashCommand(expected), actual);
}
-void VirtualFileSystem::Create(const string& path, int time,
+void VirtualFileSystem::Create(const string& path,
const string& contents) {
- files_[path].mtime = time;
+ files_[path].mtime = now_;
files_[path].contents = contents;
files_created_.insert(path);
}
}
bool VirtualFileSystem::WriteFile(const string& path, const string& contents) {
- Create(path, 0, contents);
+ Create(path, contents);
return true;
}
/// of disk state. It also logs file accesses and directory creations
/// so it can be used by tests to verify disk access patterns.
struct VirtualFileSystem : public DiskInterface {
- /// "Create" a file with a given mtime and contents.
- void Create(const string& path, int time, const string& contents);
+ VirtualFileSystem() : now_(1) {}
+
+ /// "Create" a file with contents.
+ void Create(const string& path, const string& contents);
+
+ /// Tick "time" forwards; subsequent file operations will be newer than
+ /// previous ones.
+ int Tick() {
+ return ++now_;
+ }
// DiskInterface
virtual TimeStamp Stat(const string& path);
FileMap files_;
set<string> files_removed_;
set<string> files_created_;
+
+ /// A simple fake timestamp for file operations.
+ int now_;
};
struct ScopedTempDir {