summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorEvan Martin <martine@danga.com>2012-08-12 21:52:18 (GMT)
committerEvan Martin <martine@danga.com>2012-08-12 21:52:18 (GMT)
commit1843f550d9b8b6d271cefdfb5fffd150bb8ef069 (patch)
tree6d4e9aa59249d5397f954ba541333126c5d52f38 /src
parent031237133e33ee4eb5e9641396d6b0f566b575c8 (diff)
downloadNinja-1843f550d9b8b6d271cefdfb5fffd150bb8ef069.zip
Ninja-1843f550d9b8b6d271cefdfb5fffd150bb8ef069.tar.gz
Ninja-1843f550d9b8b6d271cefdfb5fffd150bb8ef069.tar.bz2
add subprocess-spawning to msvc_helper
Rather than using subprocess.h, reimplement the subprocess code. This allows: 1) using anonymous (instead of named) pipes 2) not using all the completion port craziness 3) printing the output as it happens 4) further variation, like adjusting the environment (in a forthcoming change) without affecting the main subprocess code
Diffstat (limited to 'src')
-rw-r--r--src/msvc_helper-win32.cc98
-rw-r--r--src/msvc_helper.h9
-rw-r--r--src/msvc_helper_test.cc10
3 files changed, 116 insertions, 1 deletions
diff --git a/src/msvc_helper-win32.cc b/src/msvc_helper-win32.cc
index ff18e80..ac957e4 100644
--- a/src/msvc_helper-win32.cc
+++ b/src/msvc_helper-win32.cc
@@ -15,8 +15,9 @@
#include "msvc_helper.h"
#include <string.h>
+#include <windows.h>
-#include "string_piece.h"
+#include "util.h"
// static
string CLWrapper::FilterShowIncludes(const string& line) {
@@ -33,3 +34,98 @@ string CLWrapper::FilterShowIncludes(const string& line) {
}
return "";
}
+
+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()) {
+ includes_.push_back(include);
+ } 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
index e7c2b1a..5a657be 100644
--- a/src/msvc_helper.h
+++ b/src/msvc_helper.h
@@ -23,7 +23,16 @@ struct StringPiece;
/// 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);
+
+ vector<string> includes_;
};
diff --git a/src/msvc_helper_test.cc b/src/msvc_helper_test.cc
index 9eb3c89..8db4c69 100644
--- a/src/msvc_helper_test.cc
+++ b/src/msvc_helper_test.cc
@@ -30,3 +30,13 @@ TEST(MSVCHelperTest, ShowIncludes) {
CLWrapper::FilterShowIncludes("Note: including file: "
"c:\\initspaces.h"));
}
+
+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]);
+}