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
38 extern "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 
46 using namespace std;
47 
48 namespace {
49 
50 #ifdef _WIN32
51 /// Windows has no mkdtemp. Implement it in terms of _mktemp_s.
52 char* 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 
69 string 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 
86  AddCatRule(&state_);
87 }
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 
100 void AssertParse(State* state, const char* input,
101  ManifestParserOptions opts) {
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 
109 void AssertHash(const char* expected, uint64_t actual) {
110  ASSERT_EQ(BuildLog::LogEntry::HashCommand(expected), actual);
111 }
112 
113 void 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 
145 void 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 
152 TimeStamp 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 
161 bool VirtualFileSystem::WriteFile(const string& path, const string& contents,
162  bool /*crlf_on_windows*/) {
163  Create(path, contents);
164  return true;
165 }
166 
167 bool 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 
185 int 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 
199 void 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 
257  if (!released_) {
258  platformAwareUnlink(path_.c_str());
259  }
260 }
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
A class that records a file path and ensures that it is removed on destruction.
Definition: test.h:106
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
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
void Create(const std::string &path, const std::string &contents)
"Create" a file with contents.
Definition: test.cc:145
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
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