summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorEvan Martin <martine@danga.com>2012-08-13 23:38:41 (GMT)
committerEvan Martin <martine@danga.com>2012-08-13 23:38:41 (GMT)
commit6c04f14fd27485a5948a93c17aacfd6cdb5fcdb7 (patch)
treea8c707334b7066f65cb8916be3efa5a2314239e2 /src
parent77b078b01f0a0262f5f0abe61ca73cd981fcd506 (diff)
parent356f31abb92c87b3fa32700d3b44e49f1321cdb2 (diff)
downloadNinja-6c04f14fd27485a5948a93c17aacfd6cdb5fcdb7.zip
Ninja-6c04f14fd27485a5948a93c17aacfd6cdb5fcdb7.tar.gz
Ninja-6c04f14fd27485a5948a93c17aacfd6cdb5fcdb7.tar.bz2
Merge branch 'master' of github.com:martine/ninja
Diffstat (limited to 'src')
-rw-r--r--src/includes_normalize-win32.cc115
-rw-r--r--src/includes_normalize.h35
-rw-r--r--src/includes_normalize_test.cc98
-rw-r--r--src/msvc_helper-win32.cc164
-rw-r--r--src/msvc_helper.h47
-rw-r--r--src/msvc_helper_test.cc74
-rw-r--r--src/subprocess-win32.cc8
-rw-r--r--src/util.cc4
-rw-r--r--src/util.h6
9 files changed, 542 insertions, 9 deletions
diff --git a/src/includes_normalize-win32.cc b/src/includes_normalize-win32.cc
new file mode 100644
index 0000000..134cfb8
--- /dev/null
+++ b/src/includes_normalize-win32.cc
@@ -0,0 +1,115 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "includes_normalize.h"
+
+#include "string_piece.h"
+#include "util.h"
+
+#include <algorithm>
+#include <iterator>
+#include <sstream>
+
+#include <windows.h>
+
+namespace {
+
+/// Return true if paths a and b are on the same Windows drive.
+bool SameDrive(StringPiece a, StringPiece b) {
+ char a_absolute[_MAX_PATH];
+ char b_absolute[_MAX_PATH];
+ GetFullPathName(a.AsString().c_str(), sizeof(a_absolute), a_absolute, NULL);
+ GetFullPathName(b.AsString().c_str(), sizeof(b_absolute), b_absolute, NULL);
+ char a_drive[_MAX_DIR];
+ char b_drive[_MAX_DIR];
+ _splitpath(a_absolute, a_drive, NULL, NULL, NULL);
+ _splitpath(b_absolute, b_drive, NULL, NULL, NULL);
+ return _stricmp(a_drive, b_drive) == 0;
+}
+
+} // anonymous namespace
+
+string IncludesNormalize::Join(const vector<string>& list, char sep) {
+ string ret;
+ for (size_t i = 0; i < list.size(); ++i) {
+ ret += list[i];
+ if (i != list.size() - 1)
+ ret += sep;
+ }
+ return ret;
+}
+
+vector<string> IncludesNormalize::Split(const string& input, char sep) {
+ vector<string> elems;
+ stringstream ss(input);
+ string item;
+ while (getline(ss, item, sep))
+ elems.push_back(item);
+ return elems;
+}
+
+string IncludesNormalize::ToLower(const string& s) {
+ string ret;
+ transform(s.begin(), s.end(), back_inserter(ret), tolower);
+ return ret;
+}
+
+string IncludesNormalize::AbsPath(StringPiece s) {
+ char result[_MAX_PATH];
+ GetFullPathName(s.AsString().c_str(), sizeof(result), result, NULL);
+ return result;
+}
+
+string IncludesNormalize::Relativize(StringPiece path, const string& start) {
+ vector<string> start_list = Split(AbsPath(start), '\\');
+ vector<string> path_list = Split(AbsPath(path), '\\');
+ int i;
+ for (i = 0; i < static_cast<int>(min(start_list.size(), path_list.size()));
+ ++i) {
+ if (ToLower(start_list[i]) != ToLower(path_list[i]))
+ break;
+ }
+
+ vector<string> rel_list;
+ for (int j = 0; j < static_cast<int>(start_list.size() - i); ++j)
+ rel_list.push_back("..");
+ for (int j = i; j < static_cast<int>(path_list.size()); ++j)
+ rel_list.push_back(path_list[j]);
+ if (rel_list.size() == 0)
+ return ".";
+ return Join(rel_list, '\\');
+}
+
+string IncludesNormalize::Normalize(const string& input,
+ const char* relative_to) {
+ char copy[_MAX_PATH];
+ size_t len = input.size();
+ strncpy(copy, input.c_str(), input.size() + 1);
+ for (size_t j = 0; j < len; ++j)
+ if (copy[j] == '/')
+ copy[j] = '\\';
+ string err;
+ if (!CanonicalizePath(copy, &len, &err)) {
+ Warning("couldn't canonicalize '%s: %s\n", input.c_str(), err.c_str());
+ }
+ string curdir;
+ if (!relative_to) {
+ curdir = AbsPath(".");
+ relative_to = curdir.c_str();
+ }
+ StringPiece partially_fixed(copy, len);
+ if (!SameDrive(partially_fixed, relative_to))
+ return ToLower(partially_fixed.AsString());
+ return ToLower(Relativize(partially_fixed, relative_to));
+}
diff --git a/src/includes_normalize.h b/src/includes_normalize.h
new file mode 100644
index 0000000..43527af
--- /dev/null
+++ b/src/includes_normalize.h
@@ -0,0 +1,35 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+#include <vector>
+using namespace std;
+
+struct StringPiece;
+
+/// Utility functions for normalizing include paths on Windows.
+/// TODO: this likely duplicates functionality of CanonicalizePath; refactor.
+struct IncludesNormalize {
+ // Internal utilities made available for testing, maybe useful otherwise.
+ static string Join(const vector<string>& list, char sep);
+ static vector<string> Split(const string& input, char sep);
+ static string ToLower(const string& s);
+ static string AbsPath(StringPiece s);
+ static string Relativize(StringPiece path, const string& start);
+
+ /// Normalize by fixing slashes style, fixing redundant .. and . and makes the
+ /// path relative to |relative_to|. Case is normalized to lowercase on
+ /// Windows too.
+ static string Normalize(const string& input, const char* relative_to);
+};
diff --git a/src/includes_normalize_test.cc b/src/includes_normalize_test.cc
new file mode 100644
index 0000000..77b5b3b
--- /dev/null
+++ b/src/includes_normalize_test.cc
@@ -0,0 +1,98 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "includes_normalize.h"
+
+#include <gtest/gtest.h>
+
+#include "test.h"
+#include "util.h"
+
+TEST(IncludesNormalize, Simple) {
+ EXPECT_EQ("b", IncludesNormalize::Normalize("a\\..\\b", NULL));
+ EXPECT_EQ("b", IncludesNormalize::Normalize("a\\../b", NULL));
+ EXPECT_EQ("a\\b", IncludesNormalize::Normalize("a\\.\\b", NULL));
+ EXPECT_EQ("a\\b", IncludesNormalize::Normalize("a\\./b", NULL));
+}
+
+namespace {
+
+string GetCurDir() {
+ char buf[_MAX_PATH];
+ _getcwd(buf, sizeof(buf));
+ vector<string> parts = IncludesNormalize::Split(string(buf), '\\');
+ return parts[parts.size() - 1];
+}
+
+} // namespace
+
+TEST(IncludesNormalize, WithRelative) {
+ string currentdir = IncludesNormalize::ToLower(GetCurDir());
+ EXPECT_EQ("c", IncludesNormalize::Normalize("a/b/c", "a/b"));
+ EXPECT_EQ("a", IncludesNormalize::Normalize(IncludesNormalize::AbsPath("a"), NULL));
+ EXPECT_EQ(string("..\\") + currentdir + string("\\a"),
+ IncludesNormalize::Normalize("a", "../b"));
+ EXPECT_EQ(string("..\\") + currentdir + string("\\a\\b"),
+ IncludesNormalize::Normalize("a/b", "../c"));
+ EXPECT_EQ("..\\..\\a", IncludesNormalize::Normalize("a", "b/c"));
+ EXPECT_EQ(".", IncludesNormalize::Normalize("a", "a"));
+}
+
+TEST(IncludesNormalize, Case) {
+ EXPECT_EQ("b", IncludesNormalize::Normalize("Abc\\..\\b", NULL));
+ EXPECT_EQ("bdef", IncludesNormalize::Normalize("Abc\\..\\BdEf", NULL));
+ EXPECT_EQ("a\\b", IncludesNormalize::Normalize("A\\.\\b", NULL));
+ EXPECT_EQ("a\\b", IncludesNormalize::Normalize("A\\./b", NULL));
+ EXPECT_EQ("a\\b", IncludesNormalize::Normalize("A\\.\\B", NULL));
+ EXPECT_EQ("a\\b", IncludesNormalize::Normalize("A\\./B", NULL));
+}
+
+TEST(IncludesNormalize, Join) {
+ vector<string> x;
+ EXPECT_EQ("", IncludesNormalize::Join(x, ':'));
+ x.push_back("alpha");
+ EXPECT_EQ("alpha", IncludesNormalize::Join(x, ':'));
+ x.push_back("beta");
+ x.push_back("gamma");
+ EXPECT_EQ("alpha:beta:gamma", IncludesNormalize::Join(x, ':'));
+}
+
+TEST(IncludesNormalize, Split) {
+ EXPECT_EQ("", IncludesNormalize::Join(IncludesNormalize::Split("", '/'), ':'));
+ EXPECT_EQ("a", IncludesNormalize::Join(IncludesNormalize::Split("a", '/'), ':'));
+ EXPECT_EQ("a:b:c", IncludesNormalize::Join(IncludesNormalize::Split("a/b/c", '/'), ':'));
+}
+
+TEST(IncludesNormalize, ToLower) {
+ EXPECT_EQ("", IncludesNormalize::ToLower(""));
+ EXPECT_EQ("stuff", IncludesNormalize::ToLower("Stuff"));
+ EXPECT_EQ("stuff and things", IncludesNormalize::ToLower("Stuff AND thINGS"));
+ EXPECT_EQ("stuff 3and thin43gs", IncludesNormalize::ToLower("Stuff 3AND thIN43GS"));
+}
+
+TEST(IncludesNormalize, DifferentDrive) {
+ EXPECT_EQ("stuff.h",
+ IncludesNormalize::Normalize("p:\\vs08\\stuff.h", "p:\\vs08"));
+ EXPECT_EQ("stuff.h",
+ IncludesNormalize::Normalize("P:\\vs08\\stuff.h", "p:\\vs08"));
+ EXPECT_EQ("p:\\vs08\\stuff.h",
+ IncludesNormalize::Normalize("P:\\vs08\\stuff.h", "c:\\vs08"));
+ EXPECT_EQ("p:\\vs08\\stuff.h",
+ IncludesNormalize::Normalize("P:\\vs08\\stuff.h", "D:\\stuff/things"));
+ EXPECT_EQ("p:\\vs08\\stuff.h",
+ IncludesNormalize::Normalize("P:/vs08\\stuff.h", "D:\\stuff/things"));
+ // TODO: this fails; fix it.
+ //EXPECT_EQ("P:\\wee\\stuff.h",
+ // IncludesNormalize::Normalize("P:/vs08\\../wee\\stuff.h", "D:\\stuff/things"));
+}
diff --git a/src/msvc_helper-win32.cc b/src/msvc_helper-win32.cc
new file mode 100644
index 0000000..624bc2d
--- /dev/null
+++ b/src/msvc_helper-win32.cc
@@ -0,0 +1,164 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "msvc_helper.h"
+
+#include <string.h>
+#include <windows.h>
+
+#include "includes_normalize.h"
+#include "util.h"
+
+namespace {
+
+/// Return true if \a input ends with \a needle.
+bool EndsWith(const string& input, const string& needle) {
+ return (input.size() >= needle.size() &&
+ input.substr(input.size() - needle.size()) == needle);
+}
+
+} // anonymous namespace
+
+// static
+string CLWrapper::FilterShowIncludes(const string& line) {
+ static const char kMagicPrefix[] = "Note: including file: ";
+ const char* in = line.c_str();
+ const char* end = in + line.size();
+
+ if (end - in > (int)sizeof(kMagicPrefix) - 1 &&
+ memcmp(in, kMagicPrefix, sizeof(kMagicPrefix) - 1) == 0) {
+ in += sizeof(kMagicPrefix) - 1;
+ while (*in == ' ')
+ ++in;
+ return line.substr(in - line.c_str());
+ }
+ return "";
+}
+
+// static
+bool CLWrapper::IsSystemInclude(const string& path) {
+ // TODO: this is a heuristic, perhaps there's a better way?
+ return (path.find("program files") != string::npos ||
+ path.find("microsoft visual studio") != string::npos);
+}
+
+// static
+bool CLWrapper::FilterInputFilename(const string& line) {
+ // TODO: other extensions, like .asm?
+ return EndsWith(line, ".c") ||
+ EndsWith(line, ".cc") ||
+ EndsWith(line, ".cxx") ||
+ EndsWith(line, ".cpp");
+}
+
+int CLWrapper::Run(const string& command, string* extra_output) {
+ SECURITY_ATTRIBUTES security_attributes = {};
+ security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+ security_attributes.bInheritHandle = TRUE;
+
+ // Must be inheritable so subprocesses can dup to children.
+ HANDLE nul = CreateFile("NUL", GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE |
+ FILE_SHARE_DELETE,
+ &security_attributes, OPEN_EXISTING, 0, NULL);
+ if (nul == INVALID_HANDLE_VALUE)
+ Fatal("couldn't open nul");
+
+ HANDLE stdout_read, stdout_write;
+ if (!CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0))
+ Win32Fatal("CreatePipe");
+
+ if (!SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0))
+ Win32Fatal("SetHandleInformation");
+
+ PROCESS_INFORMATION process_info = {};
+ STARTUPINFO startup_info = {};
+ startup_info.cb = sizeof(STARTUPINFO);
+ startup_info.hStdInput = nul;
+ startup_info.hStdError = stdout_write;
+ startup_info.hStdOutput = stdout_write;
+ startup_info.dwFlags |= STARTF_USESTDHANDLES;
+
+ if (!CreateProcessA(NULL, (char*)command.c_str(), NULL, NULL,
+ /* inherit handles */ TRUE, 0,
+ NULL, NULL,
+ &startup_info, &process_info)) {
+ Win32Fatal("CreateProcess");
+ }
+
+ if (!CloseHandle(nul) ||
+ !CloseHandle(stdout_write)) {
+ Win32Fatal("CloseHandle");
+ }
+
+ // Read output of the subprocess and parse it.
+ string output;
+ DWORD read_len = 1;
+ while (read_len) {
+ char buf[64 << 10];
+ read_len = 0;
+ if (!::ReadFile(stdout_read, buf, sizeof(buf), &read_len, NULL) &&
+ GetLastError() != ERROR_BROKEN_PIPE) {
+ Win32Fatal("ReadFile");
+ }
+ output.append(buf, read_len);
+
+ // Loop over all lines in the output and process them.
+ for (;;) {
+ size_t ofs = output.find_first_of("\r\n");
+ if (ofs == string::npos)
+ break;
+ string line = output.substr(0, ofs);
+
+ string include = FilterShowIncludes(line);
+ if (!include.empty()) {
+ include = IncludesNormalize::Normalize(include, NULL);
+ if (!IsSystemInclude(include))
+ includes_.push_back(include);
+ } else if (FilterInputFilename(line)) {
+ // Drop it.
+ // TODO: if we support compiling multiple output files in a single
+ // cl.exe invocation, we should stash the filename.
+ } else {
+ if (extra_output) {
+ extra_output->append(line);
+ extra_output->append("\n");
+ } else {
+ printf("%s\n", line.c_str());
+ }
+ }
+
+ if (ofs < output.size() && output[ofs] == '\r')
+ ++ofs;
+ if (ofs < output.size() && output[ofs] == '\n')
+ ++ofs;
+ output = output.substr(ofs);
+ }
+ }
+
+ if (WaitForSingleObject(process_info.hProcess, INFINITE) == WAIT_FAILED)
+ Win32Fatal("WaitForSingleObject");
+
+ DWORD exit_code = 0;
+ if (!GetExitCodeProcess(process_info.hProcess, &exit_code))
+ Win32Fatal("GetExitCodeProcess");
+
+ if (!CloseHandle(stdout_read) ||
+ !CloseHandle(process_info.hProcess) ||
+ !CloseHandle(process_info.hThread)) {
+ Win32Fatal("CloseHandle");
+ }
+
+ return exit_code;
+}
diff --git a/src/msvc_helper.h b/src/msvc_helper.h
new file mode 100644
index 0000000..41b9f9b
--- /dev/null
+++ b/src/msvc_helper.h
@@ -0,0 +1,47 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+#include <vector>
+using namespace std;
+
+/// Visual Studio's cl.exe requires some massaging to work with Ninja;
+/// for example, it emits include information on stderr in a funny
+/// format when building with /showIncludes. This class wraps a CL
+/// process and parses that output to extract the file list.
+struct CLWrapper {
+ /// Start a process and parse its output. Returns its exit code.
+ /// Any non-parsed output is buffered into \a extra_output if provided,
+ /// otherwise it is printed to stdout while the process runs.
+ /// Crashes (calls Fatal()) on error.
+ int Run(const string& command, string* extra_output=NULL);
+
+ /// Parse a line of cl.exe output and extract /showIncludes info.
+ /// If a dependency is extracted, returns a nonempty string.
+ /// Exposed for testing.
+ static string FilterShowIncludes(const string& line);
+
+ /// Return true if a mentioned include file is a system path.
+ /// Expects the path to already by normalized (including lower case).
+ /// Filtering these out reduces dependency information considerably.
+ static bool IsSystemInclude(const string& path);
+
+ /// Parse a line of cl.exe output and return true if it looks like
+ /// it's printing an input filename. This is a heuristic but it appears
+ /// to be the best we can do.
+ /// Exposed for testing.
+ static bool FilterInputFilename(const string& line);
+
+ vector<string> includes_;
+};
diff --git a/src/msvc_helper_test.cc b/src/msvc_helper_test.cc
new file mode 100644
index 0000000..b65d66f
--- /dev/null
+++ b/src/msvc_helper_test.cc
@@ -0,0 +1,74 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "msvc_helper.h"
+
+#include <gtest/gtest.h>
+
+#include "test.h"
+#include "util.h"
+
+TEST(MSVCHelperTest, ShowIncludes) {
+ ASSERT_EQ("", CLWrapper::FilterShowIncludes(""));
+
+ ASSERT_EQ("", CLWrapper::FilterShowIncludes("Sample compiler output"));
+ ASSERT_EQ("c:\\Some Files\\foobar.h",
+ CLWrapper::FilterShowIncludes("Note: including file: "
+ "c:\\Some Files\\foobar.h"));
+ ASSERT_EQ("c:\\initspaces.h",
+ CLWrapper::FilterShowIncludes("Note: including file: "
+ "c:\\initspaces.h"));
+}
+
+TEST(MSVCHelperTest, FilterInputFilename) {
+ ASSERT_TRUE(CLWrapper::FilterInputFilename("foobar.cc"));
+ ASSERT_TRUE(CLWrapper::FilterInputFilename("foo bar.cc"));
+ ASSERT_TRUE(CLWrapper::FilterInputFilename("baz.c"));
+
+ ASSERT_FALSE(CLWrapper::FilterInputFilename(
+ "src\\cl_helper.cc(166) : fatal error C1075: end "
+ "of file found ..."));
+}
+
+TEST(MSVCHelperTest, Run) {
+ CLWrapper cl;
+ string output;
+ cl.Run("cmd /c \"echo foo&& echo Note: including file: foo.h&&echo bar\"",
+ &output);
+ ASSERT_EQ("foo\nbar\n", output);
+ ASSERT_EQ(1u, cl.includes_.size());
+ ASSERT_EQ("foo.h", cl.includes_[0]);
+}
+
+TEST(MSVCHelperTest, RunFilenameFilter) {
+ CLWrapper cl;
+ string output;
+ cl.Run("cmd /c \"echo foo.cc&& echo cl: warning\"",
+ &output);
+ ASSERT_EQ("cl: warning\n", output);
+}
+
+TEST(MSVCHelperTest, RunSystemInclude) {
+ CLWrapper cl;
+ string output;
+ cl.Run("cmd /c \"echo Note: including file: c:\\Program Files\\foo.h&&"
+ "echo Note: including file: d:\\Microsoft Visual Studio\\bar.h&&"
+ "echo Note: including file: path.h\"",
+ &output);
+ // We should have dropped the first two includes because they look like
+ // system headers.
+ ASSERT_EQ("", output);
+ ASSERT_EQ(1u, cl.includes_.size());
+ ASSERT_EQ("path.h", cl.includes_[0]);
+}
diff --git a/src/subprocess-win32.cc b/src/subprocess-win32.cc
index 38b6957..4b103a5 100644
--- a/src/subprocess-win32.cc
+++ b/src/subprocess-win32.cc
@@ -20,14 +20,6 @@
#include "util.h"
-namespace {
-
-void Win32Fatal(const char* function) {
- Fatal("%s: %s", function, GetLastErrorString().c_str());
-}
-
-} // anonymous namespace
-
Subprocess::Subprocess() : child_(NULL) , overlapped_(), is_reading_(false) {
}
diff --git a/src/util.cc b/src/util.cc
index 90a1d45..14f6265 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -247,6 +247,10 @@ string GetLastErrorString() {
LocalFree(msg_buf);
return msg;
}
+
+void Win32Fatal(const char* function) {
+ Fatal("%s: %s", function, GetLastErrorString().c_str());
+}
#endif
static bool islatinalpha(int c) {
diff --git a/src/util.h b/src/util.h
index ab1882b..3a658fb 100644
--- a/src/util.h
+++ b/src/util.h
@@ -41,7 +41,8 @@ bool CanonicalizePath(string* path, string* err);
bool CanonicalizePath(char* path, size_t* len, string* err);
-/// Read a file to a string.
+/// Read a file to a string (in text mode: with CRLF conversion
+/// on Windows).
/// Returns -errno and fills in \a err on error.
int ReadFile(const string& path, string* contents, string* err);
@@ -81,6 +82,9 @@ string ElideMiddle(const string& str, size_t width);
#ifdef _WIN32
/// Convert the value returned by GetLastError() into a string.
string GetLastErrorString();
+
+/// Calls Fatal() with a function name and GetLastErrorString.
+void Win32Fatal(const char* function);
#endif
#endif // NINJA_UTIL_H_