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 | |
85 | StateTestWithBuiltinRules::StateTestWithBuiltinRules() { |
86 | AddCatRule(&state_); |
87 | } |
88 | |
89 | void StateTestWithBuiltinRules::AddCatRule(State* state) { |
90 | AssertParse(state, |
91 | "rule cat\n" |
92 | " command = cat $in > $out\n" ); |
93 | } |
94 | |
95 | Node* StateTestWithBuiltinRules::GetNode(const string& path) { |
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 | Create(path, contents); |
163 | return true; |
164 | } |
165 | |
166 | bool VirtualFileSystem::MakeDir(const string& path) { |
167 | directories_made_.push_back(path); |
168 | return true; // success |
169 | } |
170 | |
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 | |
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 | |
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 | |
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 | |