Selaa lähdekoodia

Fix build on MinGW/Win32 (including implementing Subprocess using CreateProcess()).

kenton@google.com 16 vuotta sitten
vanhempi
commit
684d45b2fe

+ 9 - 0
src/google/protobuf/compiler/command_line_interface.cc

@@ -529,10 +529,19 @@ CommandLineInterface::InsertionOutputStream::~InsertionOutputStream() {
   // If everything was successful, overwrite the original file with the temp
   // If everything was successful, overwrite the original file with the temp
   // file.
   // file.
   if (!had_error) {
   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) {
     if (rename(temp_filename_.c_str(), filename_.c_str()) < 0) {
       cerr << filename_ << ": rename: " << strerror(errno) << endl;
       cerr << filename_ << ": rename: " << strerror(errno) << endl;
       had_error = true;
       had_error = true;
     }
     }
+#endif  // !_WIN32
   }
   }
 
 
   if (had_error) {
   if (had_error) {

+ 19 - 0
src/google/protobuf/compiler/command_line_interface_unittest.cc

@@ -248,7 +248,11 @@ void CommandLineInterfaceTest::Run(const string& command) {
 
 
   if (!disallow_plugins_) {
   if (!disallow_plugins_) {
     cli_.AllowPlugins("prefix-");
     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");
     args.push_back("--plugin=prefix-gen-plug=test_plugin");
+#endif
   }
   }
 
 
   scoped_array<const char*> argv(new const char*[args.size()]);
   scoped_array<const char*> argv(new const char*[args.size()]);
@@ -1026,9 +1030,16 @@ TEST_F(CommandLineInterfaceTest, GeneratorPluginCrash) {
 
 
   ExpectErrorSubstring("Saw message type MockCodeGenerator_Abort.");
   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.
   // Don't depend on the exact signal number.
   ExpectErrorSubstring(
   ExpectErrorSubstring(
       "--plug_out: prefix-gen-plug: Plugin killed by signal");
       "--plug_out: prefix-gen-plug: Plugin killed by signal");
+#endif
 }
 }
 
 
 TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
 TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
@@ -1042,11 +1053,19 @@ TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
       "--plugin=prefix-gen-badplug=no_such_file "
       "--plugin=prefix-gen-badplug=no_such_file "
       "--proto_path=$tmpdir error.proto");
       "--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(
   ExpectErrorSubstring(
       "no_such_file: program not found or is not executable");
       "no_such_file: program not found or is not executable");
 
 
+  // Error written by parent process when child fails.
   ExpectErrorSubstring(
   ExpectErrorSubstring(
       "--badplug_out: prefix-gen-badplug: Plugin failed with status code 1.");
       "--badplug_out: prefix-gen-badplug: Plugin failed with status code 1.");
+#endif
 }
 }
 
 
 TEST_F(CommandLineInterfaceTest, GeneratorPluginNotAllowed) {
 TEST_F(CommandLineInterfaceTest, GeneratorPluginNotAllowed) {

+ 10 - 0
src/google/protobuf/compiler/plugin.cc

@@ -36,6 +36,11 @@
 #include <set>
 #include <set>
 #include <unistd.h>
 #include <unistd.h>
 
 
+#ifdef _WIN32
+#include <io.h>
+#include <fcntl.h>
+#endif
+
 #include <google/protobuf/stubs/common.h>
 #include <google/protobuf/stubs/common.h>
 #include <google/protobuf/compiler/plugin.pb.h>
 #include <google/protobuf/compiler/plugin.pb.h>
 #include <google/protobuf/compiler/code_generator.h>
 #include <google/protobuf/compiler/code_generator.h>
@@ -80,6 +85,11 @@ int PluginMain(int argc, char* argv[], const CodeGenerator* generator) {
     return 1;
     return 1;
   }
   }
 
 
+#ifdef _WIN32
+  _setmode(STDIN_FILENO, _O_BINARY);
+  _setmode(STDOUT_FILENO, _O_BINARY);
+#endif
+
   CodeGeneratorRequest request;
   CodeGeneratorRequest request;
   if (!request.ParseFromFileDescriptor(STDIN_FILENO)) {
   if (!request.ParseFromFileDescriptor(STDIN_FILENO)) {
     cerr << argv[0] << ": protoc sent unparseable request to plugin." << endl;
     cerr << argv[0] << ": protoc sent unparseable request to plugin." << endl;

+ 231 - 1
src/google/protobuf/compiler/subprocess.cc

@@ -32,9 +32,12 @@
 
 
 #include <google/protobuf/compiler/subprocess.h>
 #include <google/protobuf/compiler/subprocess.h>
 
 
-#include <algorithm>
+#ifndef _WIN32
 #include <errno.h>
 #include <errno.h>
 #include <sys/wait.h>
 #include <sys/wait.h>
+#endif
+
+#include <algorithm>
 #include <google/protobuf/stubs/common.h>
 #include <google/protobuf/stubs/common.h>
 #include <google/protobuf/message.h>
 #include <google/protobuf/message.h>
 #include <google/protobuf/stubs/substitute.h>
 #include <google/protobuf/stubs/substitute.h>
@@ -43,6 +46,231 @@ namespace google {
 namespace protobuf {
 namespace protobuf {
 namespace compiler {
 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()
 Subprocess::Subprocess()
     : child_pid_(-1), child_stdin_(-1), child_stdout_(-1) {}
     : child_pid_(-1), child_stdin_(-1), child_stdout_(-1) {}
 
 
@@ -222,6 +450,8 @@ bool Subprocess::Communicate(const Message& input, Message* output,
   return true;
   return true;
 }
 }
 
 
+#endif  // !_WIN32
+
 }  // namespace compiler
 }  // namespace compiler
 }  // namespace protobuf
 }  // namespace protobuf
 }  // namespace google
 }  // namespace google

+ 25 - 3
src/google/protobuf/compiler/subprocess.h

@@ -33,12 +33,16 @@
 #ifndef GOOGLE_PROTOBUF_COMPILER_SUBPROCESS_H__
 #ifndef GOOGLE_PROTOBUF_COMPILER_SUBPROCESS_H__
 #define 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 <sys/types.h>
 #include <unistd.h>
 #include <unistd.h>
-#include <google/protobuf/stubs/common.h>
+#endif  // !_WIN32
 
 
+#include <string>
+#include <google/protobuf/stubs/common.h>
 
 
 namespace google {
 namespace google {
 namespace protobuf {
 namespace protobuf {
@@ -69,13 +73,31 @@ class Subprocess {
   // *error to a description of the problem.
   // *error to a description of the problem.
   bool Communicate(const Message& input, Message* output, string* error);
   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:
  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_;
   pid_t child_pid_;
 
 
   // The file descriptors for our end of the child's pipes.  We close each and
   // The file descriptors for our end of the child's pipes.  We close each and
   // set it to -1 when no longer needed.
   // set it to -1 when no longer needed.
   int child_stdin_;
   int child_stdin_;
   int child_stdout_;
   int child_stdout_;
+
+#endif  // !_WIN32
 };
 };
 
 
 }  // namespace compiler
 }  // namespace compiler

+ 21 - 4
src/google/protobuf/descriptor.cc

@@ -37,6 +37,7 @@
 #include <set>
 #include <set>
 #include <vector>
 #include <vector>
 #include <algorithm>
 #include <algorithm>
+#include <limits>
 
 
 #include <google/protobuf/descriptor.h>
 #include <google/protobuf/descriptor.h>
 #include <google/protobuf/descriptor_database.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);
             strtou64(proto.default_value().c_str(), &end_pos, 0);
           break;
           break;
         case FieldDescriptor::CPPTYPE_FLOAT:
         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;
           break;
         case FieldDescriptor::CPPTYPE_DOUBLE:
         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;
           break;
         case FieldDescriptor::CPPTYPE_BOOL:
         case FieldDescriptor::CPPTYPE_BOOL:
           if (proto.default_value() == "true") {
           if (proto.default_value() == "true") {

+ 1 - 1
src/google/protobuf/io/gzip_stream.cc

@@ -315,6 +315,6 @@ bool GzipOutputStream::Close() {
 
 
 }  // namespace io
 }  // namespace io
 }  // namespace protobuf
 }  // namespace protobuf
+}  // namespace google
 
 
 #endif  // HAVE_ZLIB
 #endif  // HAVE_ZLIB
-}  // namespace google

+ 2 - 0
src/google/protobuf/repeated_field_unittest.cc

@@ -261,7 +261,9 @@ TEST(RepeatedField, Truncate) {
   // Truncations that don't change the size are allowed, but growing is not
   // Truncations that don't change the size are allowed, but growing is not
   // allowed.
   // allowed.
   field.Truncate(field.size());
   field.Truncate(field.size());
+#ifdef GTEST_HAS_DEATH_TEST
   EXPECT_DEBUG_DEATH(field.Truncate(field.size() + 1), "new_size");
   EXPECT_DEBUG_DEATH(field.Truncate(field.size() + 1), "new_size");
+#endif
 }
 }