Ninja
test.cc
Go to the documentation of this file.
1// Copyright 2011 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#ifdef _WIN32
16#include <direct.h> // Has to be before util.h is included.
17#endif
18
19#include "test.h"
20
21#include <algorithm>
22
23#include <errno.h>
24#include <stdlib.h>
25#ifdef _WIN32
26#include <windows.h>
27#include <io.h>
28#else
29#include <unistd.h>
30#endif
31
32#include "build_log.h"
33#include "graph.h"
34#include "manifest_parser.h"
35#include "util.h"
36
37#ifdef _AIX
38extern "C" {
39 // GCC "helpfully" strips the definition of mkdtemp out on AIX.
40 // The function is still present, so if we define it ourselves
41 // it will work perfectly fine.
42 extern char* mkdtemp(char* name_template);
43}
44#endif
45
46using namespace std;
47
48namespace {
49
50#ifdef _WIN32
51/// Windows has no mkdtemp. Implement it in terms of _mktemp_s.
52char* mkdtemp(char* name_template) {
53 int err = _mktemp_s(name_template, strlen(name_template) + 1);
54 if (err < 0) {
55 perror("_mktemp_s");
56 return NULL;
57 }
58
59 err = _mkdir(name_template);
60 if (err < 0) {
61 perror("mkdir");
62 return NULL;
63 }
64
65 return name_template;
66}
67#endif // _WIN32
68
69string GetSystemTempDir() {
70#ifdef _WIN32
71 char buf[1024];
72 if (!GetTempPath(sizeof(buf), buf))
73 return "";
74 return buf;
75#else
76 const char* tempdir = getenv("TMPDIR");
77 if (tempdir)
78 return tempdir;
79 return "/tmp";
80#endif
81}
82
83} // anonymous namespace
84
88
90 AssertParse(state,
91"rule cat\n"
92" command = cat $in > $out\n");
93}
94
96 EXPECT_FALSE(strpbrk(path.c_str(), "/\\"));
97 return state_.GetNode(path, 0);
98}
99
100void AssertParse(State* state, const char* input,
102 ManifestParser parser(state, NULL, opts);
103 string err;
104 EXPECT_TRUE(parser.ParseTest(input, &err));
105 ASSERT_EQ("", err);
106 VerifyGraph(*state);
107}
108
109void AssertHash(const char* expected, uint64_t actual) {
110 ASSERT_EQ(BuildLog::LogEntry::HashCommand(expected), actual);
111}
112
113void VerifyGraph(const State& state) {
114 for (vector<Edge*>::const_iterator e = state.edges_.begin();
115 e != state.edges_.end(); ++e) {
116 // All edges need at least one output.
117 EXPECT_FALSE((*e)->outputs_.empty());
118 // Check that the edge's inputs have the edge as out-edge.
119 for (vector<Node*>::const_iterator in_node = (*e)->inputs_.begin();
120 in_node != (*e)->inputs_.end(); ++in_node) {
121 const vector<Edge*>& out_edges = (*in_node)->out_edges();
122 EXPECT_NE(find(out_edges.begin(), out_edges.end(), *e),
123 out_edges.end());
124 }
125 // Check that the edge's outputs have the edge as in-edge.
126 for (vector<Node*>::const_iterator out_node = (*e)->outputs_.begin();
127 out_node != (*e)->outputs_.end(); ++out_node) {
128 EXPECT_EQ((*out_node)->in_edge(), *e);
129 }
130 }
131
132 // The union of all in- and out-edges of each nodes should be exactly edges_.
133 set<const Edge*> node_edge_set;
134 for (State::Paths::const_iterator p = state.paths_.begin();
135 p != state.paths_.end(); ++p) {
136 const Node* n = p->second;
137 if (n->in_edge())
138 node_edge_set.insert(n->in_edge());
139 node_edge_set.insert(n->out_edges().begin(), n->out_edges().end());
140 }
141 set<const Edge*> edge_set(state.edges_.begin(), state.edges_.end());
142 EXPECT_EQ(node_edge_set, edge_set);
143}
144
145void VirtualFileSystem::Create(const string& path,
146 const string& contents) {
147 files_[path].mtime = now_;
148 files_[path].contents = contents;
149 files_created_.insert(path);
150}
151
152TimeStamp VirtualFileSystem::Stat(const string& path, string* err) const {
153 FileMap::const_iterator i = files_.find(path);
154 if (i != files_.end()) {
155 *err = i->second.stat_error;
156 return i->second.mtime;
157 }
158 return 0;
159}
160
161bool VirtualFileSystem::WriteFile(const string& path, const string& contents,
162 bool /*crlf_on_windows*/) {
163 Create(path, contents);
164 return true;
165}
166
167bool VirtualFileSystem::MakeDir(const string& path) {
168 directories_made_.push_back(path);
169 return true; // success
170}
171
173 string* contents,
174 string* err) {
175 files_read_.push_back(path);
176 FileMap::iterator i = files_.find(path);
177 if (i != files_.end()) {
178 *contents = i->second.contents;
179 return Okay;
180 }
181 *err = strerror(ENOENT);
182 return NotFound;
183}
184
185int VirtualFileSystem::RemoveFile(const string& path) {
186 if (find(directories_made_.begin(), directories_made_.end(), path)
187 != directories_made_.end())
188 return -1;
189 FileMap::iterator i = files_.find(path);
190 if (i != files_.end()) {
191 files_.erase(i);
192 files_removed_.insert(path);
193 return 0;
194 } else {
195 return 1;
196 }
197}
198
199void ScopedTempDir::CreateAndEnter(const string& name) {
200 // First change into the system temp dir and save it for cleanup.
201 start_dir_ = GetSystemTempDir();
202 if (start_dir_.empty())
203 Fatal("couldn't get system temp dir");
204 if (chdir(start_dir_.c_str()) < 0)
205 Fatal("chdir: %s", strerror(errno));
206
207 // Create a temporary subdirectory of that.
208 char name_template[1024];
209 strcpy(name_template, name.c_str());
210 strcat(name_template, "-XXXXXX");
211 char* tempname = mkdtemp(name_template);
212 if (!tempname)
213 Fatal("mkdtemp: %s", strerror(errno));
214 temp_dir_name_ = tempname;
215
216 // chdir into the new temporary directory.
217 if (chdir(temp_dir_name_.c_str()) < 0)
218 Fatal("chdir: %s", strerror(errno));
219}
220
222 if (temp_dir_name_.empty())
223 return; // Something went wrong earlier.
224
225 // Move out of the directory we're about to clobber.
226 if (chdir(start_dir_.c_str()) < 0)
227 Fatal("chdir: %s", strerror(errno));
228
229#ifdef _WIN32
230 string command = "rmdir /s /q " + temp_dir_name_;
231#else
232 string command = "rm -rf " + temp_dir_name_;
233#endif
234 if (system(command.c_str()) < 0)
235 Fatal("system: %s", strerror(errno));
236
237 temp_dir_name_.clear();
238}
239
241 : path_(std::move(other.path_)), released_(other.released_) {
242 other.released_ = true;
243}
244
245/// It would be nice to use '= default' here instead but some old compilers
246/// such as GCC from Ubuntu 16.06 will not compile it with "noexcept", so just
247/// write it manually.
249 if (this != &other) {
250 this->~ScopedFilePath();
251 new (this) ScopedFilePath(std::move(other));
252 }
253 return *this;
254}
255
261
263 released_ = true;
264}
Definition hash_map.h:26
static uint64_t HashCommand(StringPiece command)
Definition build_log.cc:60
Status
Result of ReadFile.
Parses .ninja files.
bool ParseTest(const std::string &input, std::string *err)
Parse a text string of input. Used by tests.
Information about a node in the dependency graph: the file, whether it's dirty, mtime,...
Definition graph.h:42
Edge * in_edge() const
Definition graph.h:100
const std::vector< Edge * > & out_edges() const
Definition graph.h:114
bool released_
Definition test.h:127
std::string path_
Definition test.h:126
void Release()
Release the file, the destructor will not remove the file.
Definition test.cc:262
~ScopedFilePath()
Destructor destroys the file, unless Release() was called.
Definition test.cc:256
ScopedFilePath & operator=(ScopedFilePath &&) noexcept
It would be nice to use '= default' here instead but some old compilers such as GCC from Ubuntu 16....
Definition test.cc:248
ScopedFilePath(const std::string &path)
Constructor just records the file path.
Definition test.h:108
void CreateAndEnter(const std::string &name)
Create a temporary directory and chdir into it.
Definition test.cc:199
std::string start_dir_
The temp directory containing our dir.
Definition test.h:98
std::string temp_dir_name_
The subdirectory name for our dir, or empty if it hasn't been set up.
Definition test.h:100
void Cleanup()
Clean up the temporary directory.
Definition test.cc:221
void AddCatRule(State *state)
Add a "cat" rule to state.
Definition test.cc:89
Node * GetNode(const std::string &path)
Short way to get a Node by its path from state_.
Definition test.cc:95
Global state (file status) for a single run.
Definition state.h:95
std::vector< Edge * > edges_
All the edges of the graph.
Definition state.h:138
Paths paths_
Definition state.h:132
bool WriteFile(const std::string &path, const std::string &contents, bool) override
Create a file, with the specified name and contents If crlf_on_windows is true, will be converted t...
Definition test.cc:161
bool MakeDir(const std::string &path) override
Create a directory, returning false on failure.
Definition test.cc:167
int RemoveFile(const std::string &path) override
Remove the file named path.
Definition test.cc:185
Status ReadFile(const std::string &path, std::string *contents, std::string *err) override
Read and store in given string.
Definition test.cc:172
TimeStamp Stat(const std::string &path, std::string *err) const override
stat() a file, returning the mtime, or 0 if missing and -1 on other errors.
Definition test.cc:152
std::set< std::string > files_removed_
Definition test.h:83
FileMap files_
Definition test.h:82
int now_
A simple fake timestamp for file operations.
Definition test.h:87
std::set< std::string > files_created_
Definition test.h:84
std::vector< std::string > files_read_
Definition test.h:80
void Create(const std::string &path, const std::string &contents)
"Create" a file with contents.
Definition test.cc:145
std::vector< std::string > directories_made_
Definition test.h:79
void AssertParse(State *state, const char *input, ManifestParserOptions opts)
Definition test.cc:100
void AssertHash(const char *expected, uint64_t actual)
Definition test.cc:109
void VerifyGraph(const State &state)
Definition test.cc:113
void AssertParse(State *state, const char *input, ManifestParserOptions=ManifestParserOptions())
Definition test.cc:100
int64_t TimeStamp
Definition timestamp.h:31
int platformAwareUnlink(const char *filename)
Definition util.cc:1025
void Fatal(const char *msg,...)
Log a fatal message and exit.
Definition util.cc:67
unsigned long long uint64_t
Definition win32port.h:29