Thrift: MinGW port.

Summary:
Todd Berman from imeem has contributed a patch that allows
the Thrift compiler to be built with MinGW and run on Windows.

Reviewed By: mcslee

Test Plan:
Built the compiler from scratch on Linux.
If my changes messed up the MinGW build, I'm sure Todd will yell at me.

Revert Plan: ok


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665420 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/compiler/cpp/src/generate/t_cocoa_generator.cc b/compiler/cpp/src/generate/t_cocoa_generator.cc
index fc85365..1356c35 100644
--- a/compiler/cpp/src/generate/t_cocoa_generator.cc
+++ b/compiler/cpp/src/generate/t_cocoa_generator.cc
@@ -8,6 +8,7 @@
 #include <sys/stat.h>
 #include <sstream>
 #include "t_cocoa_generator.h"
+#include "platform.h"
 using namespace std;
 
 /**
@@ -16,7 +17,7 @@
  */
 void t_cocoa_generator::init_generator() {
   // Make output directory
-  mkdir(get_out_dir().c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
+  MKDIR(get_out_dir().c_str());
   cocoa_prefix_ = program_->get_cocoa_prefix();
 
   // we have a .h header file...
diff --git a/compiler/cpp/src/generate/t_cpp_generator.cc b/compiler/cpp/src/generate/t_cpp_generator.cc
index c465426..620d09e 100644
--- a/compiler/cpp/src/generate/t_cpp_generator.cc
+++ b/compiler/cpp/src/generate/t_cpp_generator.cc
@@ -10,6 +10,7 @@
 #include <sstream>
 #include <boost/lexical_cast.hpp>
 #include "t_cpp_generator.h"
+#include "platform.h"
 using namespace std;
 
 /**
@@ -20,7 +21,7 @@
  */
 void t_cpp_generator::init_generator() {
   // Make output directory
-  mkdir(get_out_dir().c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
+  MKDIR(get_out_dir().c_str());
 
   // Make output file
   string f_types_name = get_out_dir()+program_name_+"_types.h";
@@ -462,7 +463,7 @@
   // Isset struct has boolean fields, but only for non-required fields.
   bool has_nonrequired_fields = false;
   for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
-    if ((*m_iter)->get_req() != t_field::REQUIRED)
+    if ((*m_iter)->get_req() != t_field::T_REQUIRED)
       has_nonrequired_fields = true;
   }
 
@@ -476,7 +477,7 @@
         "__isset() : ";
       bool first = true;
       for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
-        if ((*m_iter)->get_req() == t_field::REQUIRED) {
+        if ((*m_iter)->get_req() == t_field::T_REQUIRED) {
           continue;
         }
         if (first) {
@@ -491,7 +492,7 @@
       out << " {}" << endl;
 
       for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
-        if ((*m_iter)->get_req() != t_field::REQUIRED) {
+        if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
           indent(out) <<
             "bool " << (*m_iter)->get_name() << ";" << endl;
         }
@@ -513,7 +514,7 @@
     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
       // Most existing Thrift code does not use isset or optional/required,
       // so we treat "default" fields as required.
-      if ((*m_iter)->get_req() != t_field::OPTIONAL) {
+      if ((*m_iter)->get_req() != t_field::T_OPTIONAL) {
         out <<
           indent() << "if (!(" << (*m_iter)->get_name()
                    << " == rhs." << (*m_iter)->get_name() << "))" << endl <<
@@ -638,7 +639,7 @@
       indent_up();
       for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
         indent(out) << "{ " << (*m_iter)->get_key() << ", " <<
-          (((*m_iter)->get_req() == t_field::OPTIONAL) ? "true" : "false") <<
+          (((*m_iter)->get_req() == t_field::T_OPTIONAL) ? "true" : "false") <<
           " }," << endl;
       }
       // Zero for the T_STOP marker.
@@ -751,7 +752,7 @@
 
   // Required variables aren't in __isset, so we need tmp vars to check them.
   for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
-    if ((*f_iter)->get_req() == t_field::REQUIRED)
+    if ((*f_iter)->get_req() == t_field::T_REQUIRED)
       indent(out) << "bool isset_" << (*f_iter)->get_name() << " = false;" << endl;
   }
   out << endl;
@@ -788,7 +789,7 @@
         indent_up();
 
         const char *isset_prefix =
-          ((*f_iter)->get_req() != t_field::REQUIRED) ? "this->__isset." : "isset_";
+          ((*f_iter)->get_req() != t_field::T_REQUIRED) ? "this->__isset." : "isset_";
 
 #if 0
         // This code throws an exception if the same field is encountered twice.
@@ -842,7 +843,7 @@
   // there might possibly be a chance of continuing.
   out << endl;
   for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
-    if ((*f_iter)->get_req() == t_field::REQUIRED)
+    if ((*f_iter)->get_req() == t_field::T_REQUIRED)
       out <<
         indent() << "if (!isset_" << (*f_iter)->get_name() << ')' << endl <<
         indent() << "  throw TProtocolException(TProtocolException::INVALID_DATA);" << endl;
@@ -878,7 +879,7 @@
   indent(out) <<
     "xfer += oprot->writeStructBegin(\"" << name << "\");" << endl;
   for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
-    if ((*f_iter)->get_req() == t_field::OPTIONAL) {
+    if ((*f_iter)->get_req() == t_field::T_OPTIONAL) {
       indent(out) << "if (this->__isset." << (*f_iter)->get_name() << ") {" << endl;
       indent_up();
     }
@@ -897,7 +898,7 @@
     // Write field closer
     indent(out) <<
       "xfer += oprot->writeFieldEnd();" << endl;
-    if ((*f_iter)->get_req() == t_field::OPTIONAL) {
+    if ((*f_iter)->get_req() == t_field::T_OPTIONAL) {
       indent_down();
       indent(out) << '}' << endl;
     }
diff --git a/compiler/cpp/src/generate/t_erl_generator.cc b/compiler/cpp/src/generate/t_erl_generator.cc
index 44683cd..02d7361 100644
--- a/compiler/cpp/src/generate/t_erl_generator.cc
+++ b/compiler/cpp/src/generate/t_erl_generator.cc
@@ -9,6 +9,7 @@
 #include <sys/types.h>
 #include <sstream>
 #include "t_erl_generator.h"
+#include "platform.h"
 
 using namespace std;
 
@@ -20,7 +21,7 @@
  */
 void t_erl_generator::init_generator() {
   // Make output directory
-  mkdir(get_out_dir().c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
+  MKDIR(get_out_dir().c_str());
 
   // setup export lines
   export_lines_first_ = true;
diff --git a/compiler/cpp/src/generate/t_hs_generator.cc b/compiler/cpp/src/generate/t_hs_generator.cc
index 8746ba4..6ef7a95 100644
--- a/compiler/cpp/src/generate/t_hs_generator.cc
+++ b/compiler/cpp/src/generate/t_hs_generator.cc
@@ -9,6 +9,7 @@
 #include <sys/types.h>
 #include <sstream>
 #include "t_hs_generator.h"
+#include "platform.h"
 using namespace std;
 
 /*
@@ -72,7 +73,7 @@
  */
 void t_hs_generator::init_generator() {
   // Make output directory
-  mkdir(get_out_dir().c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
+  MKDIR(get_out_dir().c_str());
 
   // Make output file
 
diff --git a/compiler/cpp/src/generate/t_java_generator.cc b/compiler/cpp/src/generate/t_java_generator.cc
index e0a1be2..cd4b873 100644
--- a/compiler/cpp/src/generate/t_java_generator.cc
+++ b/compiler/cpp/src/generate/t_java_generator.cc
@@ -8,6 +8,7 @@
 #include <sys/stat.h>
 #include <sstream>
 #include "t_java_generator.h"
+#include "platform.h"
 using namespace std;
 
 /**
@@ -18,7 +19,7 @@
  */
 void t_java_generator::init_generator() {
   // Make output directory
-  mkdir(get_out_dir().c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
+  MKDIR(get_out_dir().c_str());
   package_name_ = program_->get_java_package();
 
   string dir = package_name_;
@@ -26,12 +27,12 @@
   string::size_type loc;
   while ((loc = dir.find(".")) != string::npos) {
     subdir = subdir + "/" + dir.substr(0, loc);
-    mkdir(subdir.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
+    MKDIR(subdir.c_str());
     dir = dir.substr(loc+1);
   }
   if (dir.size() > 0) {
     subdir = subdir + "/" + dir;
-    mkdir(subdir.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
+    MKDIR(subdir.c_str());
   }
 
   package_dir_ = subdir;
@@ -565,7 +566,7 @@
         indent() << "if (this." << (*f_iter)->get_name() << " != null) {" << endl;
       indent_up();
     }
-    bool optional = bean_style_ && (*f_iter)->get_req() == t_field::OPTIONAL;
+    bool optional = bean_style_ && (*f_iter)->get_req() == t_field::T_OPTIONAL;
     if (optional) {
       out <<
         indent() << "if (this.__isset." << (*f_iter)->get_name() << ") {" << endl;
diff --git a/compiler/cpp/src/generate/t_ocaml_generator.cc b/compiler/cpp/src/generate/t_ocaml_generator.cc
index 8bf6457..f4f5c4a 100644
--- a/compiler/cpp/src/generate/t_ocaml_generator.cc
+++ b/compiler/cpp/src/generate/t_ocaml_generator.cc
@@ -9,6 +9,7 @@
 #include <sys/types.h>
 #include <sstream>
 #include "t_ocaml_generator.h"
+#include "platform.h"
 using namespace std;
 
 /*
@@ -72,7 +73,7 @@
  */
 void t_ocaml_generator::init_generator() {
   // Make output directory
-  mkdir(get_out_dir().c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
+  MKDIR(get_out_dir().c_str());
 
   // Make output file
   string f_types_name = get_out_dir()+program_name_+"_types.ml";
diff --git a/compiler/cpp/src/generate/t_perl_generator.cc b/compiler/cpp/src/generate/t_perl_generator.cc
index ee6b5b4..fe55418 100644
--- a/compiler/cpp/src/generate/t_perl_generator.cc
+++ b/compiler/cpp/src/generate/t_perl_generator.cc
@@ -8,6 +8,7 @@
 #include <sys/stat.h>
 #include <sstream>
 #include "t_perl_generator.h"
+#include "platform.h"
 using namespace std;
 
 /**
@@ -18,13 +19,13 @@
  */
 void t_perl_generator::init_generator() {
   // Make output directory
-  mkdir(get_out_dir().c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
+  MKDIR(get_out_dir().c_str());
 
   string outdir = get_out_dir();
   std::string ns = program_->get_perl_package();
   if (ns.length() > 0) {
     outdir += ns + "/";
-    mkdir(outdir.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
+    MKDIR(outdir.c_str());
   }
 
   // Make output file
diff --git a/compiler/cpp/src/generate/t_php_generator.cc b/compiler/cpp/src/generate/t_php_generator.cc
index 5b3c979..1f4ed1f 100644
--- a/compiler/cpp/src/generate/t_php_generator.cc
+++ b/compiler/cpp/src/generate/t_php_generator.cc
@@ -8,6 +8,7 @@
 #include <sys/stat.h>
 #include <sstream>
 #include "t_php_generator.h"
+#include "platform.h"
 using namespace std;
 
 /**
@@ -18,7 +19,7 @@
  */
 void t_php_generator::init_generator() {
   // Make output directory
-  mkdir(get_out_dir().c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
+  MKDIR(get_out_dir().c_str());
 
   // Make output file
   string f_types_name = get_out_dir()+program_name_+"_types.php";
diff --git a/compiler/cpp/src/generate/t_py_generator.cc b/compiler/cpp/src/generate/t_py_generator.cc
index 7f3507f..ca192ce 100644
--- a/compiler/cpp/src/generate/t_py_generator.cc
+++ b/compiler/cpp/src/generate/t_py_generator.cc
@@ -10,6 +10,7 @@
 #include <sstream>
 #include <algorithm>
 #include "t_py_generator.h"
+#include "platform.h"
 using namespace std;
 
 /**
@@ -24,7 +25,7 @@
   package_dir_ = get_out_dir();
   while (true) {
     // TODO: Do better error checking here.
-    mkdir(package_dir_.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
+    MKDIR(package_dir_.c_str());
     std::ofstream init_py((package_dir_+"/__init__.py").c_str());
     init_py.close();
     if (module.empty()) {
@@ -1030,13 +1031,16 @@
 
   // Make file executable, love that bitwise OR action
   chmod(f_remote_name.c_str(),
-        S_IRUSR |
-        S_IWUSR |
-        S_IXUSR |
-        S_IRGRP |
-        S_IXGRP |
-        S_IROTH |
-        S_IXOTH);
+          S_IRUSR
+        | S_IWUSR
+        | S_IXUSR
+#ifndef MINGW
+        | S_IRGRP
+        | S_IXGRP
+        | S_IROTH
+        | S_IXOTH
+#endif
+		);
 }
 
 /**
diff --git a/compiler/cpp/src/generate/t_rb_generator.cc b/compiler/cpp/src/generate/t_rb_generator.cc
index 59f25de..56690e8 100644
--- a/compiler/cpp/src/generate/t_rb_generator.cc
+++ b/compiler/cpp/src/generate/t_rb_generator.cc
@@ -9,6 +9,7 @@
 #include <sys/types.h>
 #include <sstream>
 #include "t_rb_generator.h"
+#include "platform.h"
 using namespace std;
 
 /**
@@ -19,7 +20,7 @@
  */
 void t_rb_generator::init_generator() {
   // Make output directory
-  mkdir(get_out_dir().c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
+  MKDIR(get_out_dir().c_str());
 
   // Make output file
   string f_types_name = get_out_dir()+program_name_+"_types.rb";
diff --git a/compiler/cpp/src/generate/t_st_generator.cc b/compiler/cpp/src/generate/t_st_generator.cc
index c8456bc..fa7d851 100644
--- a/compiler/cpp/src/generate/t_st_generator.cc
+++ b/compiler/cpp/src/generate/t_st_generator.cc
@@ -11,6 +11,7 @@
 #include <sys/types.h>
 #include <sstream>
 #include "t_st_generator.h"
+#include "platform.h"
 using namespace std;
 
 /**
@@ -21,7 +22,7 @@
  */
 void t_st_generator::init_generator() {
   // Make output directory
-  mkdir(T_ST_DIR, S_IREAD | S_IWRITE | S_IEXEC);
+  MKDIR(T_ST_DIR);
 
   temporary_var = 0;
 
@@ -560,7 +561,7 @@
   indent_up();
 
   for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
-    bool optional = (*fld_iter)->get_req() == t_field::OPTIONAL;
+    bool optional = (*fld_iter)->get_req() == t_field::T_OPTIONAL;
     string fname = (*fld_iter)->get_name();
     string accessor = sname + " " + sanitize(fname);
 
diff --git a/compiler/cpp/src/generate/t_xsd_generator.cc b/compiler/cpp/src/generate/t_xsd_generator.cc
index 04645d2..7bab56d 100644
--- a/compiler/cpp/src/generate/t_xsd_generator.cc
+++ b/compiler/cpp/src/generate/t_xsd_generator.cc
@@ -8,11 +8,12 @@
 #include <sys/stat.h>
 #include <sstream>
 #include "t_xsd_generator.h"
+#include "platform.h"
 using namespace std;
 
 void t_xsd_generator::init_generator() {
   // Make output directory
-  mkdir(get_out_dir().c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
+  MKDIR(get_out_dir().c_str());
 
   // Make output file
   string f_php_name = get_out_dir()+program_->get_name()+"_xsd.php";
diff --git a/compiler/cpp/src/main.cc b/compiler/cpp/src/main.cc
index ac5fa04..4877431 100644
--- a/compiler/cpp/src/main.cc
+++ b/compiler/cpp/src/main.cc
@@ -23,6 +23,11 @@
 #include <sys/stat.h>
 #include <errno.h>
 
+#ifdef MINGW
+# include <windows.h> /* for GetFullPathName */
+# include <limits.h>
+#endif
+
 // Careful: must include globals first for extern definitions
 #include "globals.h"
 
@@ -155,6 +160,28 @@
 bool gen_recurse = false;
 
 /**
+ * MinGW doesn't have realpath, so use fallback implementation in that case,
+ * otherwise this just calls through to realpath
+ */
+char *saferealpath(const char *path, char *resolved_path) {
+#ifdef MINGW
+  char buf[MAX_PATH];
+  char* basename;
+  DWORD len = GetFullPathName(path, MAX_PATH, buf, &basename);
+  if (len == 0 || len > MAX_PATH - 1){
+    strcpy(resolved_path, path);
+  } else {
+    CharLowerBuff(buf, len);
+    strcpy(resolved_path, buf);
+  }
+  return resolved_path;
+#else
+  return realpath(path, resolved_path);
+#endif
+}
+
+
+/**
  * Report an error to the user. This is called yyerror for historical
  * reasons (lex and yacc expect the error reporting routine to be called
  * this). Call this function to report any errors to the user.
@@ -276,7 +303,7 @@
   if (filename[0] == '/') {
     // Realpath!
     char rp[PATH_MAX];
-    if (realpath(filename.c_str(), rp) == NULL) {
+    if (saferealpath(filename.c_str(), rp) == NULL) {
       pwarning(0, "Cannot open include file %s\n", filename.c_str());
       return std::string();
     }
@@ -298,7 +325,7 @@
 
       // Realpath!
       char rp[PATH_MAX];
-      if (realpath(sfilename.c_str(), rp) == NULL) {
+      if (saferealpath(sfilename.c_str(), rp) == NULL) {
         continue;
       }
 
@@ -754,7 +781,7 @@
     parse(*iter, program);
   }
 
-  // Parse the program the file
+  // Parse the program file
   g_parse_mode = PROGRAM;
   g_program = program;
   g_scope = program->scope();
@@ -1011,6 +1038,16 @@
           usage();
         }
         out_path = arg;
+
+#ifdef MINGW
+        //strip out trailing \ on Windows
+        int last = out_path.length()-1;
+        if (out_path[last] == '\\')
+        {
+          out_path.erase(last);
+        }
+#endif
+
         struct stat sb;
         if (stat(out_path.c_str(), &sb) < 0) {
           fprintf(stderr, "Output directory %s is unusable: %s\n", out_path.c_str(), strerror(errno));
@@ -1038,8 +1075,8 @@
 
   // Real-pathify it
   char rp[PATH_MAX];
-  if (realpath(argv[i], rp) == NULL) {
-    failure("Could not open input file: %s", argv[i]);
+  if (saferealpath(argv[i], rp) == NULL) {
+    failure("Could not open input file with realpath: %s", argv[i]);
   }
   string input_file(rp);
 
diff --git a/compiler/cpp/src/parse/t_field.h b/compiler/cpp/src/parse/t_field.h
index 35ee1a2..fb882a9 100644
--- a/compiler/cpp/src/parse/t_field.h
+++ b/compiler/cpp/src/parse/t_field.h
@@ -34,7 +34,7 @@
     type_(type),
     name_(name),
     key_(key),
-    req_(OPT_IN_REQ_OUT),
+    req_(T_OPT_IN_REQ_OUT),
     value_(NULL),
     xsd_optional_(false),
     xsd_nillable_(false),
@@ -55,9 +55,9 @@
   }
 
   enum e_req {
-    REQUIRED,
-    OPTIONAL,
-    OPT_IN_REQ_OUT
+    T_REQUIRED,
+    T_OPTIONAL,
+    T_OPT_IN_REQ_OUT,
   };
 
   void set_req(e_req req) {
@@ -117,7 +117,7 @@
   // but it does the same thing.
   std::string get_fingerprint_material() const {
     return boost::lexical_cast<std::string>(key_) + ":" +
-      (req_ == OPTIONAL ? "opt-" : "") +
+      ((req_ == T_OPTIONAL) ? "opt-" : "") +
       type_->get_fingerprint_material();
   }
 
diff --git a/compiler/cpp/src/platform.h b/compiler/cpp/src/platform.h
new file mode 100644
index 0000000..df64e9a
--- /dev/null
+++ b/compiler/cpp/src/platform.h
@@ -0,0 +1,22 @@
+// Distributed under the Thrift Software License

+//

+// See accompanying file LICENSE or visit the Thrift site at:

+// http://developers.facebook.com/thrift/

+

+/**

+ * define for mkdir,since the method signature 

+ * is different for the non-POSIX MinGW

+ */

+

+#ifdef MINGW

+#include <io.h>

+#else

+#include <sys/types.h>

+#include <sys/stat.h>

+#endif

+

+#if defined MINGW

+#define MKDIR(x) mkdir(x)

+#else

+#define MKDIR(x) mkdir(x, S_IRWXU | S_IRWXG | S_IRWXO)

+#endif

diff --git a/compiler/cpp/src/thrifty.yy b/compiler/cpp/src/thrifty.yy
index e46d0ef..9475adf 100644
--- a/compiler/cpp/src/thrifty.yy
+++ b/compiler/cpp/src/thrifty.yy
@@ -825,9 +825,9 @@
         if (g_parse_mode == PROGRAM) {
           pwarning(1, "required keyword is ignored in argument lists.\n");
         }
-        $$ = t_field::OPT_IN_REQ_OUT;
+        $$ = t_field::T_OPT_IN_REQ_OUT;
       } else {
-        $$ = t_field::REQUIRED;
+        $$ = t_field::T_REQUIRED;
       }
     }
 | tok_optional
@@ -836,14 +836,14 @@
         if (g_parse_mode == PROGRAM) {
           pwarning(1, "optional keyword is ignored in argument lists.\n");
         }
-        $$ = t_field::OPT_IN_REQ_OUT;
+        $$ = t_field::T_OPT_IN_REQ_OUT;
       } else {
-        $$ = t_field::OPTIONAL;
+        $$ = t_field::T_OPTIONAL;
       }
     }
 |
     {
-      $$ = t_field::OPT_IN_REQ_OUT;
+      $$ = t_field::T_OPT_IN_REQ_OUT;
     }
 
 FieldValue: