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.
mkdtemp(char * name_template)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
GetSystemTempDir()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
StateTestWithBuiltinRules()85 StateTestWithBuiltinRules::StateTestWithBuiltinRules() {
86 AddCatRule(&state_);
87 }
88
AddCatRule(State * state)89 void StateTestWithBuiltinRules::AddCatRule(State* state) {
90 AssertParse(state,
91 "rule cat\n"
92 " command = cat $in > $out\n");
93 }
94
GetNode(const string & path)95 Node* StateTestWithBuiltinRules::GetNode(const string& path) {
96 EXPECT_FALSE(strpbrk(path.c_str(), "/\\"));
97 return state_.GetNode(path, 0);
98 }
99
AssertParse(State * state,const char * input,ManifestParserOptions opts)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
AssertHash(const char * expected,uint64_t actual)109 void AssertHash(const char* expected, uint64_t actual) {
110 ASSERT_EQ(BuildLog::LogEntry::HashCommand(expected), actual);
111 }
112
VerifyGraph(const State & state)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
Create(const string & path,const string & contents)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
Stat(const string & path,string * err) const152 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
WriteFile(const string & path,const string & contents)161 bool VirtualFileSystem::WriteFile(const string& path, const string& contents) {
162 Create(path, contents);
163 return true;
164 }
165
MakeDir(const string & path)166 bool VirtualFileSystem::MakeDir(const string& path) {
167 directories_made_.push_back(path);
168 return true; // success
169 }
170
ReadFile(const string & path,string * contents,string * err)171 FileReader::Status VirtualFileSystem::ReadFile(const string& path,
172 string* contents,
173 string* err) {
174 files_read_.push_back(path);
175 FileMap::iterator i = files_.find(path);
176 if (i != files_.end()) {
177 *contents = i->second.contents;
178 return Okay;
179 }
180 *err = strerror(ENOENT);
181 return NotFound;
182 }
183
RemoveFile(const string & path)184 int VirtualFileSystem::RemoveFile(const string& path) {
185 if (find(directories_made_.begin(), directories_made_.end(), path)
186 != directories_made_.end())
187 return -1;
188 FileMap::iterator i = files_.find(path);
189 if (i != files_.end()) {
190 files_.erase(i);
191 files_removed_.insert(path);
192 return 0;
193 } else {
194 return 1;
195 }
196 }
197
CreateAndEnter(const string & name)198 void ScopedTempDir::CreateAndEnter(const string& name) {
199 // First change into the system temp dir and save it for cleanup.
200 start_dir_ = GetSystemTempDir();
201 if (start_dir_.empty())
202 Fatal("couldn't get system temp dir");
203 if (chdir(start_dir_.c_str()) < 0)
204 Fatal("chdir: %s", strerror(errno));
205
206 // Create a temporary subdirectory of that.
207 char name_template[1024];
208 strcpy(name_template, name.c_str());
209 strcat(name_template, "-XXXXXX");
210 char* tempname = mkdtemp(name_template);
211 if (!tempname)
212 Fatal("mkdtemp: %s", strerror(errno));
213 temp_dir_name_ = tempname;
214
215 // chdir into the new temporary directory.
216 if (chdir(temp_dir_name_.c_str()) < 0)
217 Fatal("chdir: %s", strerror(errno));
218 }
219
Cleanup()220 void ScopedTempDir::Cleanup() {
221 if (temp_dir_name_.empty())
222 return; // Something went wrong earlier.
223
224 // Move out of the directory we're about to clobber.
225 if (chdir(start_dir_.c_str()) < 0)
226 Fatal("chdir: %s", strerror(errno));
227
228 #ifdef _WIN32
229 string command = "rmdir /s /q " + temp_dir_name_;
230 #else
231 string command = "rm -rf " + temp_dir_name_;
232 #endif
233 if (system(command.c_str()) < 0)
234 Fatal("system: %s", strerror(errno));
235
236 temp_dir_name_.clear();
237 }
238