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