Fix build on MinGW/Win32 (including implementing Subprocess using CreateProcess()).
This commit is contained in:
parent
bdbb863099
commit
684d45b2fe
@ -529,10 +529,19 @@ CommandLineInterface::InsertionOutputStream::~InsertionOutputStream() {
|
||||
// If everything was successful, overwrite the original file with the temp
|
||||
// file.
|
||||
if (!had_error) {
|
||||
#ifdef _WIN32
|
||||
// rename() on Windows fails if the file exists.
|
||||
if (!MoveFileEx(temp_filename_.c_str(), filename_.c_str(),
|
||||
MOVEFILE_REPLACE_EXISTING)) {
|
||||
cerr << filename_ << ": MoveFileEx: "
|
||||
<< Subprocess::Win32ErrorMessage(GetLastError()) << endl;
|
||||
}
|
||||
#else // _WIN32
|
||||
if (rename(temp_filename_.c_str(), filename_.c_str()) < 0) {
|
||||
cerr << filename_ << ": rename: " << strerror(errno) << endl;
|
||||
had_error = true;
|
||||
}
|
||||
#endif // !_WIN32
|
||||
}
|
||||
|
||||
if (had_error) {
|
||||
|
@ -248,7 +248,11 @@ void CommandLineInterfaceTest::Run(const string& command) {
|
||||
|
||||
if (!disallow_plugins_) {
|
||||
cli_.AllowPlugins("prefix-");
|
||||
#ifdef _WIN32
|
||||
args.push_back("--plugin=prefix-gen-plug=test_plugin.exe");
|
||||
#else
|
||||
args.push_back("--plugin=prefix-gen-plug=test_plugin");
|
||||
#endif
|
||||
}
|
||||
|
||||
scoped_array<const char*> argv(new const char*[args.size()]);
|
||||
@ -1026,9 +1030,16 @@ TEST_F(CommandLineInterfaceTest, GeneratorPluginCrash) {
|
||||
|
||||
ExpectErrorSubstring("Saw message type MockCodeGenerator_Abort.");
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows doesn't have signals. It looks like abort()ing causes the process
|
||||
// to exit with status code 3, but let's not depend on the exact number here.
|
||||
ExpectErrorSubstring(
|
||||
"--plug_out: prefix-gen-plug: Plugin failed with status code");
|
||||
#else
|
||||
// Don't depend on the exact signal number.
|
||||
ExpectErrorSubstring(
|
||||
"--plug_out: prefix-gen-plug: Plugin killed by signal");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
|
||||
@ -1042,11 +1053,19 @@ TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
|
||||
"--plugin=prefix-gen-badplug=no_such_file "
|
||||
"--proto_path=$tmpdir error.proto");
|
||||
|
||||
#ifdef _WIN32
|
||||
ExpectErrorSubstring(
|
||||
"--badplug_out: prefix-gen-badplug: The system cannot find the file "
|
||||
"specified.");
|
||||
#else
|
||||
// Error written to stdout by child process after exec() fails.
|
||||
ExpectErrorSubstring(
|
||||
"no_such_file: program not found or is not executable");
|
||||
|
||||
// Error written by parent process when child fails.
|
||||
ExpectErrorSubstring(
|
||||
"--badplug_out: prefix-gen-badplug: Plugin failed with status code 1.");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(CommandLineInterfaceTest, GeneratorPluginNotAllowed) {
|
||||
|
@ -36,6 +36,11 @@
|
||||
#include <set>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include <google/protobuf/stubs/common.h>
|
||||
#include <google/protobuf/compiler/plugin.pb.h>
|
||||
#include <google/protobuf/compiler/code_generator.h>
|
||||
@ -80,6 +85,11 @@ int PluginMain(int argc, char* argv[], const CodeGenerator* generator) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
_setmode(STDIN_FILENO, _O_BINARY);
|
||||
_setmode(STDOUT_FILENO, _O_BINARY);
|
||||
#endif
|
||||
|
||||
CodeGeneratorRequest request;
|
||||
if (!request.ParseFromFileDescriptor(STDIN_FILENO)) {
|
||||
cerr << argv[0] << ": protoc sent unparseable request to plugin." << endl;
|
||||
|
@ -32,9 +32,12 @@
|
||||
|
||||
#include <google/protobuf/compiler/subprocess.h>
|
||||
|
||||
#include <algorithm>
|
||||
#ifndef _WIN32
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <google/protobuf/stubs/common.h>
|
||||
#include <google/protobuf/message.h>
|
||||
#include <google/protobuf/stubs/substitute.h>
|
||||
@ -43,6 +46,231 @@ namespace google {
|
||||
namespace protobuf {
|
||||
namespace compiler {
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static void CloseHandleOrDie(HANDLE handle) {
|
||||
if (!CloseHandle(handle)) {
|
||||
GOOGLE_LOG(FATAL) << "CloseHandle: "
|
||||
<< Subprocess::Win32ErrorMessage(GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
Subprocess::Subprocess()
|
||||
: process_start_error_(ERROR_SUCCESS),
|
||||
child_handle_(NULL), child_stdin_(NULL), child_stdout_(NULL) {}
|
||||
|
||||
Subprocess::~Subprocess() {
|
||||
if (child_stdin_ != NULL) {
|
||||
CloseHandleOrDie(child_stdin_);
|
||||
}
|
||||
if (child_stdout_ != NULL) {
|
||||
CloseHandleOrDie(child_stdout_);
|
||||
}
|
||||
}
|
||||
|
||||
void Subprocess::Start(const string& program, SearchMode search_mode) {
|
||||
// Create the pipes.
|
||||
HANDLE stdin_pipe_read;
|
||||
HANDLE stdin_pipe_write;
|
||||
HANDLE stdout_pipe_read;
|
||||
HANDLE stdout_pipe_write;
|
||||
|
||||
if (!CreatePipe(&stdin_pipe_read, &stdin_pipe_write, NULL, 0)) {
|
||||
GOOGLE_LOG(FATAL) << "CreatePipe: " << Win32ErrorMessage(GetLastError());
|
||||
}
|
||||
if (!CreatePipe(&stdout_pipe_read, &stdout_pipe_write, NULL, 0)) {
|
||||
GOOGLE_LOG(FATAL) << "CreatePipe: " << Win32ErrorMessage(GetLastError());
|
||||
}
|
||||
|
||||
// Make child side of the pipes inheritable.
|
||||
if (!SetHandleInformation(stdin_pipe_read,
|
||||
HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
|
||||
GOOGLE_LOG(FATAL) << "SetHandleInformation: "
|
||||
<< Win32ErrorMessage(GetLastError());
|
||||
}
|
||||
if (!SetHandleInformation(stdout_pipe_write,
|
||||
HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
|
||||
GOOGLE_LOG(FATAL) << "SetHandleInformation: "
|
||||
<< Win32ErrorMessage(GetLastError());
|
||||
}
|
||||
|
||||
// Setup STARTUPINFO to redirect handles.
|
||||
STARTUPINFO startup_info;
|
||||
ZeroMemory(&startup_info, sizeof(startup_info));
|
||||
startup_info.cb = sizeof(startup_info);
|
||||
startup_info.dwFlags = STARTF_USESTDHANDLES;
|
||||
startup_info.hStdInput = stdin_pipe_read;
|
||||
startup_info.hStdOutput = stdout_pipe_write;
|
||||
startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
if (startup_info.hStdError == INVALID_HANDLE_VALUE) {
|
||||
GOOGLE_LOG(FATAL) << "GetStdHandle: "
|
||||
<< Win32ErrorMessage(GetLastError());
|
||||
}
|
||||
|
||||
// CreateProcess() mutates its second parameter. WTF?
|
||||
char* name_copy = strdup(program.c_str());
|
||||
|
||||
// Create the process.
|
||||
PROCESS_INFORMATION process_info;
|
||||
|
||||
if (CreateProcess((search_mode == SEARCH_PATH) ? NULL : program.c_str(),
|
||||
(search_mode == SEARCH_PATH) ? name_copy : NULL,
|
||||
NULL, // process security attributes
|
||||
NULL, // thread security attributes
|
||||
TRUE, // inherit handles?
|
||||
0, // obscure creation flags
|
||||
NULL, // environment (inherit from parent)
|
||||
NULL, // current directory (inherit from parent)
|
||||
&startup_info,
|
||||
&process_info)) {
|
||||
child_handle_ = process_info.hProcess;
|
||||
CloseHandleOrDie(process_info.hThread);
|
||||
child_stdin_ = stdin_pipe_write;
|
||||
child_stdout_ = stdout_pipe_read;
|
||||
} else {
|
||||
process_start_error_ = GetLastError();
|
||||
CloseHandleOrDie(stdin_pipe_write);
|
||||
CloseHandleOrDie(stdout_pipe_read);
|
||||
}
|
||||
|
||||
CloseHandleOrDie(stdin_pipe_read);
|
||||
CloseHandleOrDie(stdout_pipe_write);
|
||||
free(name_copy);
|
||||
}
|
||||
|
||||
bool Subprocess::Communicate(const Message& input, Message* output,
|
||||
string* error) {
|
||||
if (process_start_error_ != ERROR_SUCCESS) {
|
||||
*error = Win32ErrorMessage(process_start_error_);
|
||||
return false;
|
||||
}
|
||||
|
||||
GOOGLE_CHECK(child_handle_ != NULL) << "Must call Start() first.";
|
||||
|
||||
string input_data = input.SerializeAsString();
|
||||
string output_data;
|
||||
|
||||
int input_pos = 0;
|
||||
|
||||
while (child_stdout_ != NULL) {
|
||||
HANDLE handles[2];
|
||||
int handle_count = 0;
|
||||
|
||||
if (child_stdin_ != NULL) {
|
||||
handles[handle_count++] = child_stdin_;
|
||||
}
|
||||
if (child_stdout_ != NULL) {
|
||||
handles[handle_count++] = child_stdout_;
|
||||
}
|
||||
|
||||
DWORD wait_result =
|
||||
WaitForMultipleObjects(handle_count, handles, FALSE, INFINITE);
|
||||
|
||||
HANDLE signaled_handle;
|
||||
if (wait_result >= WAIT_OBJECT_0 &&
|
||||
wait_result < WAIT_OBJECT_0 + handle_count) {
|
||||
signaled_handle = handles[wait_result - WAIT_OBJECT_0];
|
||||
} else if (wait_result == WAIT_FAILED) {
|
||||
GOOGLE_LOG(FATAL) << "WaitForMultipleObjects: "
|
||||
<< Win32ErrorMessage(GetLastError());
|
||||
} else {
|
||||
GOOGLE_LOG(FATAL) << "WaitForMultipleObjects: Unexpected return code: "
|
||||
<< wait_result;
|
||||
}
|
||||
|
||||
if (signaled_handle == child_stdin_) {
|
||||
DWORD n;
|
||||
if (!WriteFile(child_stdin_,
|
||||
input_data.data() + input_pos,
|
||||
input_data.size() - input_pos,
|
||||
&n, NULL)) {
|
||||
// Child closed pipe. Presumably it will report an error later.
|
||||
// Pretend we're done for now.
|
||||
input_pos = input_data.size();
|
||||
} else {
|
||||
input_pos += n;
|
||||
}
|
||||
|
||||
if (input_pos == input_data.size()) {
|
||||
// We're done writing. Close.
|
||||
CloseHandleOrDie(child_stdin_);
|
||||
child_stdin_ = NULL;
|
||||
}
|
||||
} else if (signaled_handle == child_stdout_) {
|
||||
char buffer[4096];
|
||||
DWORD n;
|
||||
|
||||
if (!ReadFile(child_stdout_, buffer, sizeof(buffer), &n, NULL)) {
|
||||
// We're done reading. Close.
|
||||
CloseHandleOrDie(child_stdout_);
|
||||
child_stdout_ = NULL;
|
||||
} else {
|
||||
output_data.append(buffer, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (child_stdin_ != NULL) {
|
||||
// Child did not finish reading input before it closed the output.
|
||||
// Presumably it exited with an error.
|
||||
CloseHandleOrDie(child_stdin_);
|
||||
child_stdin_ = NULL;
|
||||
}
|
||||
|
||||
DWORD wait_result = WaitForSingleObject(child_handle_, INFINITE);
|
||||
|
||||
if (wait_result == WAIT_FAILED) {
|
||||
GOOGLE_LOG(FATAL) << "WaitForSingleObject: "
|
||||
<< Win32ErrorMessage(GetLastError());
|
||||
} else if (wait_result != WAIT_OBJECT_0) {
|
||||
GOOGLE_LOG(FATAL) << "WaitForSingleObject: Unexpected return code: "
|
||||
<< wait_result;
|
||||
}
|
||||
|
||||
DWORD exit_code;
|
||||
if (!GetExitCodeProcess(child_handle_, &exit_code)) {
|
||||
GOOGLE_LOG(FATAL) << "GetExitCodeProcess: "
|
||||
<< Win32ErrorMessage(GetLastError());
|
||||
}
|
||||
|
||||
CloseHandleOrDie(child_handle_);
|
||||
child_handle_ = NULL;
|
||||
|
||||
if (exit_code != 0) {
|
||||
*error = strings::Substitute(
|
||||
"Plugin failed with status code $0.", exit_code);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!output->ParseFromString(output_data)) {
|
||||
*error = "Plugin output is unparseable: " + CEscape(output_data);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string Subprocess::Win32ErrorMessage(DWORD error_code) {
|
||||
char* message;
|
||||
|
||||
// WTF?
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, error_code, 0,
|
||||
(LPTSTR)&message, // NOT A BUG!
|
||||
0, NULL);
|
||||
|
||||
string result = message;
|
||||
LocalFree(message);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
#else // _WIN32
|
||||
|
||||
Subprocess::Subprocess()
|
||||
: child_pid_(-1), child_stdin_(-1), child_stdout_(-1) {}
|
||||
|
||||
@ -222,6 +450,8 @@ bool Subprocess::Communicate(const Message& input, Message* output,
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // !_WIN32
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
|
@ -33,12 +33,16 @@
|
||||
#ifndef GOOGLE_PROTOBUF_COMPILER_SUBPROCESS_H__
|
||||
#define GOOGLE_PROTOBUF_COMPILER_SUBPROCESS_H__
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN // right...
|
||||
#include <windows.h>
|
||||
#else // _WIN32
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <google/protobuf/stubs/common.h>
|
||||
#endif // !_WIN32
|
||||
|
||||
#include <string>
|
||||
#include <google/protobuf/stubs/common.h>
|
||||
|
||||
namespace google {
|
||||
namespace protobuf {
|
||||
@ -69,13 +73,31 @@ class Subprocess {
|
||||
// *error to a description of the problem.
|
||||
bool Communicate(const Message& input, Message* output, string* error);
|
||||
|
||||
#ifdef _WIN32
|
||||
// Given an error code, returns a human-readable error message. This is
|
||||
// defined here so that CommandLineInterface can share it.
|
||||
static string Subprocess::Win32ErrorMessage(DWORD error_code);
|
||||
#endif
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
DWORD process_start_error_;
|
||||
HANDLE child_handle_;
|
||||
|
||||
// The file handles for our end of the child's pipes. We close each and
|
||||
// set it to NULL when no longer needed.
|
||||
HANDLE child_stdin_;
|
||||
HANDLE child_stdout_;
|
||||
|
||||
#else // _WIN32
|
||||
pid_t child_pid_;
|
||||
|
||||
// The file descriptors for our end of the child's pipes. We close each and
|
||||
// set it to -1 when no longer needed.
|
||||
int child_stdin_;
|
||||
int child_stdout_;
|
||||
|
||||
#endif // !_WIN32
|
||||
};
|
||||
|
||||
} // namespace compiler
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include <google/protobuf/descriptor.h>
|
||||
#include <google/protobuf/descriptor_database.h>
|
||||
@ -3000,12 +3001,28 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto,
|
||||
strtou64(proto.default_value().c_str(), &end_pos, 0);
|
||||
break;
|
||||
case FieldDescriptor::CPPTYPE_FLOAT:
|
||||
result->default_value_float_ =
|
||||
NoLocaleStrtod(proto.default_value().c_str(), &end_pos);
|
||||
if (proto.default_value() == "inf") {
|
||||
result->default_value_float_ = numeric_limits<float>::infinity();
|
||||
} else if (proto.default_value() == "-inf") {
|
||||
result->default_value_float_ = -numeric_limits<float>::infinity();
|
||||
} else if (proto.default_value() == "nan") {
|
||||
result->default_value_float_ = numeric_limits<float>::quiet_NaN();
|
||||
} else {
|
||||
result->default_value_float_ =
|
||||
NoLocaleStrtod(proto.default_value().c_str(), &end_pos);
|
||||
}
|
||||
break;
|
||||
case FieldDescriptor::CPPTYPE_DOUBLE:
|
||||
result->default_value_double_ =
|
||||
NoLocaleStrtod(proto.default_value().c_str(), &end_pos);
|
||||
if (proto.default_value() == "inf") {
|
||||
result->default_value_double_ = numeric_limits<double>::infinity();
|
||||
} else if (proto.default_value() == "-inf") {
|
||||
result->default_value_double_ = -numeric_limits<double>::infinity();
|
||||
} else if (proto.default_value() == "nan") {
|
||||
result->default_value_double_ = numeric_limits<double>::quiet_NaN();
|
||||
} else {
|
||||
result->default_value_double_ =
|
||||
NoLocaleStrtod(proto.default_value().c_str(), &end_pos);
|
||||
}
|
||||
break;
|
||||
case FieldDescriptor::CPPTYPE_BOOL:
|
||||
if (proto.default_value() == "true") {
|
||||
|
@ -315,6 +315,6 @@ bool GzipOutputStream::Close() {
|
||||
|
||||
} // namespace io
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
|
||||
#endif // HAVE_ZLIB
|
||||
} // namespace google
|
||||
|
@ -261,7 +261,9 @@ TEST(RepeatedField, Truncate) {
|
||||
// Truncations that don't change the size are allowed, but growing is not
|
||||
// allowed.
|
||||
field.Truncate(field.size());
|
||||
#ifdef GTEST_HAS_DEATH_TEST
|
||||
EXPECT_DEBUG_DEATH(field.Truncate(field.size() + 1), "new_size");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user