[thrift] Output dir selection + updated TSCons

Summary: Allows setting the output directory via the new '-o dir' cmdline option.

  TSCons is updated to use this to put the output in the right place no matter
  the cwd, so doing dependent builds from different directories won't break.

Reviewed By: martin
Test Plan: mkdir /tmp/honk; thrift -cpp -java -javabean -php -phpi -py -rb -xsd -perl -erl -ocaml -hs -cocoa -o /tmp/honk Tablet.thrift
Revert: svn


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665311 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 fe28a7a..728cd49 100644
--- a/compiler/cpp/src/generate/t_cocoa_generator.cc
+++ b/compiler/cpp/src/generate/t_cocoa_generator.cc
@@ -16,12 +16,12 @@
  */
 void t_cocoa_generator::init_generator() {
   // Make output directory
-  mkdir(T_COCOA_DIR, S_IREAD | S_IWRITE | S_IEXEC);
+  mkdir(get_out_dir().c_str(), S_IREAD | S_IWRITE | S_IEXEC);
   cocoa_prefix_ = program_->get_cocoa_prefix();
 
   // we have a .h header file...
   string f_header_name = program_name_+".h";
-  string f_header_fullname = string(T_COCOA_DIR)+"/"+f_header_name;
+  string f_header_fullname = get_out_dir()+f_header_name;
   f_header_.open(f_header_fullname.c_str());
 
   f_header_ << 
@@ -33,7 +33,7 @@
     cocoa_thrift_imports();
 
   // ...and a .m implementation file
-  string f_impl_name = string(T_COCOA_DIR)+"/"+program_name_+".m";
+  string f_impl_name = get_out_dir()+program_name_+".m";
   f_impl_.open(f_impl_name.c_str());
 
   f_impl_ << 
diff --git a/compiler/cpp/src/generate/t_cocoa_generator.h b/compiler/cpp/src/generate/t_cocoa_generator.h
index b34ba8b..9ceac31 100644
--- a/compiler/cpp/src/generate/t_cocoa_generator.h
+++ b/compiler/cpp/src/generate/t_cocoa_generator.h
@@ -14,9 +14,6 @@
 
 #include "t_oop_generator.h"
 
-// TODO(mcslee: Paramaterize the output dir
-#define T_COCOA_DIR "gen-cocoa"
-
 /**
  * Objective-C code generator.
  *
@@ -26,7 +23,9 @@
 class t_cocoa_generator : public t_oop_generator {
  public:
   t_cocoa_generator(t_program* program) :
-    t_oop_generator(program) {}
+    t_oop_generator(program) {
+    out_dir_base_ = "gen-cocoa";
+  }
 
   /**
    * Init and close methods
diff --git a/compiler/cpp/src/generate/t_cpp_generator.cc b/compiler/cpp/src/generate/t_cpp_generator.cc
index d5f2baf..3482320 100644
--- a/compiler/cpp/src/generate/t_cpp_generator.cc
+++ b/compiler/cpp/src/generate/t_cpp_generator.cc
@@ -20,13 +20,13 @@
  */
 void t_cpp_generator::init_generator() {
   // Make output directory
-  mkdir(T_CPP_DIR, S_IREAD | S_IWRITE | S_IEXEC);
+  mkdir(get_out_dir().c_str(), S_IREAD | S_IWRITE | S_IEXEC);
 
   // Make output file
-  string f_types_name = string(T_CPP_DIR)+"/"+program_name_+"_types.h";
+  string f_types_name = get_out_dir()+program_name_+"_types.h";
   f_types_.open(f_types_name.c_str());
 
-  string f_types_impl_name = string(T_CPP_DIR)+"/"+program_name_+"_types.cpp";
+  string f_types_impl_name = get_out_dir()+program_name_+"_types.cpp";
   f_types_impl_.open(f_types_impl_name.c_str());
 
   // Print header
@@ -163,11 +163,11 @@
  * Generates a class that holds all the constants.
  */
 void t_cpp_generator::generate_consts(std::vector<t_const*> consts) {
-  string f_consts_name = string(T_CPP_DIR)+"/"+program_name_+"_constants.h";
+  string f_consts_name = get_out_dir()+program_name_+"_constants.h";
   ofstream f_consts;
   f_consts.open(f_consts_name.c_str());
 
-  string f_consts_impl_name = string(T_CPP_DIR)+"/"+program_name_+"_constants.cpp";
+  string f_consts_impl_name = get_out_dir()+program_name_+"_constants.cpp";
   ofstream f_consts_impl;
   f_consts_impl.open(f_consts_impl_name.c_str());
 
@@ -1002,7 +1002,7 @@
   string svcname = tservice->get_name();
 
   // Make output files
-  string f_header_name = string(T_CPP_DIR)+"/"+svcname+".h";
+  string f_header_name = get_out_dir()+svcname+".h";
   f_header_.open(f_header_name.c_str());
 
   // Print header file includes
@@ -1026,7 +1026,7 @@
     endl;
 
   // Service implementation file includes
-  string f_service_name = string(T_CPP_DIR)+"/"+svcname+".cpp";
+  string f_service_name = get_out_dir()+svcname+".cpp";
   f_service_.open(f_service_name.c_str());
   f_service_ <<
     autogen_comment();
@@ -2065,7 +2065,7 @@
   string svcname = tservice->get_name();
 
   // Service implementation file includes
-  string f_skeleton_name = string(T_CPP_DIR)+"/"+svcname+"_server.skeleton.cpp";
+  string f_skeleton_name = get_out_dir()+svcname+"_server.skeleton.cpp";
 
   string ns = namespace_prefix(tservice->get_program()->get_cpp_namespace());
 
diff --git a/compiler/cpp/src/generate/t_cpp_generator.h b/compiler/cpp/src/generate/t_cpp_generator.h
index 008140a..574a7d2 100644
--- a/compiler/cpp/src/generate/t_cpp_generator.h
+++ b/compiler/cpp/src/generate/t_cpp_generator.h
@@ -14,9 +14,6 @@
 
 #include "t_oop_generator.h"
 
-// TODO(mcslee): Paramaterize the output dir
-#define T_CPP_DIR "gen-cpp"
-
 /**
  * C++ code generator. This is legitimacy incarnate.
  *
@@ -26,7 +23,10 @@
  public:
   t_cpp_generator(t_program* program, bool gen_dense) :
     t_oop_generator(program),
-    gen_dense_(gen_dense) {}
+    gen_dense_(gen_dense) {
+
+    out_dir_base_ = "gen-cpp";
+  }
 
   /**
    * Init and close methods
diff --git a/compiler/cpp/src/generate/t_erl_generator.cc b/compiler/cpp/src/generate/t_erl_generator.cc
index 6e5f998..c070b76 100644
--- a/compiler/cpp/src/generate/t_erl_generator.cc
+++ b/compiler/cpp/src/generate/t_erl_generator.cc
@@ -20,15 +20,15 @@
  */
 void t_erl_generator::init_generator() {
   // Make output directory
-  mkdir(T_ERL_DIR, S_IREAD | S_IWRITE | S_IEXEC);
+  mkdir(get_out_dir().c_str(), S_IREAD | S_IWRITE | S_IEXEC);
 
   // setup export lines
   export_lines_first_ = true;
   export_types_lines_first_ = true;
 
   // types files
-  string f_types_name = string(T_ERL_DIR)+"/"+program_name_+"_types.erl";
-  string f_types_hrl_name = string(T_ERL_DIR)+"/"+program_name_+"_types.hrl";
+  string f_types_name = get_out_dir()+program_name_+"_types.erl";
+  string f_types_hrl_name = get_out_dir()+program_name_+"_types.hrl";
 
   f_types_file_.open(f_types_name.c_str());
   f_types_hrl_file_.open(f_types_hrl_name.c_str());
@@ -47,7 +47,7 @@
   f_types_hrl_file_ << render_includes() << endl;
 
   // consts file
-  string f_consts_name = string(T_ERL_DIR)+"/"+program_name_+"_constants.hrl";
+  string f_consts_name = get_out_dir()+program_name_+"_constants.hrl";
   f_consts_.open(f_consts_name.c_str());
 
   f_consts_ <<
@@ -511,8 +511,8 @@
   // ...awesome
   service_name_[0] = tolower(service_name_[0]);
 
-  string f_service_hrl_name = string(T_ERL_DIR)+"/"+service_name_+"_thrift.hrl";
-  string f_service_name = string(T_ERL_DIR)+"/"+service_name_+"_thrift.erl";
+  string f_service_hrl_name = get_out_dir()+service_name_+"_thrift.hrl";
+  string f_service_name = get_out_dir()+service_name_+"_thrift.erl";
   f_service_file_.open(f_service_name.c_str());
   f_service_hrl_.open(f_service_hrl_name.c_str());
 
diff --git a/compiler/cpp/src/generate/t_erl_generator.h b/compiler/cpp/src/generate/t_erl_generator.h
index cf2c87b..831bb21 100644
--- a/compiler/cpp/src/generate/t_erl_generator.h
+++ b/compiler/cpp/src/generate/t_erl_generator.h
@@ -8,8 +8,6 @@
 
 #include "t_oop_generator.h"
 
-#define T_ERL_DIR "gen-erl"
-
 /**
  * Erlang code generator.
  *
@@ -22,6 +20,7 @@
   {
     program_name_[0] = tolower(program_name_[0]);
     service_name_[0] = tolower(service_name_[0]);
+    out_dir_base_ = "gen-erl";
   }
 
   /**
diff --git a/compiler/cpp/src/generate/t_generator.h b/compiler/cpp/src/generate/t_generator.h
index beea968..8452f1f 100644
--- a/compiler/cpp/src/generate/t_generator.h
+++ b/compiler/cpp/src/generate/t_generator.h
@@ -79,6 +79,13 @@
   }
 
   /**
+   * Get the current output directory
+   */
+  virtual std::string get_out_dir() const {
+    return program_->get_out_path() + out_dir_base_ + "/";
+  }
+
+  /**
    * Creates a unique temporary variable name, which is just "name" with a
    * number appended to it (i.e. name35)
    */
@@ -159,6 +166,11 @@
    */
   std::string service_name_;
 
+  /**
+   * Output type-specifc directory name ("gen-*")
+   */
+  std::string out_dir_base_;
+
  private:
   /**
    * Current code indentation level
diff --git a/compiler/cpp/src/generate/t_hs_generator.cc b/compiler/cpp/src/generate/t_hs_generator.cc
index 920b453..03d62a4 100644
--- a/compiler/cpp/src/generate/t_hs_generator.cc
+++ b/compiler/cpp/src/generate/t_hs_generator.cc
@@ -72,15 +72,15 @@
  */
 void t_hs_generator::init_generator() {
   // Make output directory
-  mkdir(T_HS_DIR, S_IREAD | S_IWRITE | S_IEXEC);
+  mkdir(get_out_dir().c_str(), S_IREAD | S_IWRITE | S_IEXEC);
 
   // Make output file
 
   string pname = capitalize(program_name_);
-  string f_types_name = string(T_HS_DIR)+"/"+pname+"_Types.hs";
+  string f_types_name = get_out_dir()+pname+"_Types.hs";
   f_types_.open(f_types_name.c_str());
 
-  string f_consts_name = string(T_HS_DIR)+"/"+pname+"_Consts.hs";
+  string f_consts_name = get_out_dir()+pname+"_Consts.hs";
   f_consts_.open(f_consts_name.c_str());
 
   // Print header
@@ -509,7 +509,7 @@
  * @param tservice The service definition
  */
 void t_hs_generator::generate_service(t_service* tservice) {
-  string f_service_name = string(T_HS_DIR)+"/"+capitalize(service_name_)+".hs";
+  string f_service_name = get_out_dir()+capitalize(service_name_)+".hs";
   f_service_.open(f_service_name.c_str());
 
   f_service_ <<
@@ -586,7 +586,7 @@
  * @param tservice The service to generate a header definition for
  */
 void t_hs_generator::generate_service_interface(t_service* tservice) {
-  string f_iface_name = string(T_HS_DIR)+"/"+capitalize(service_name_)+"_Iface.hs";
+  string f_iface_name = get_out_dir()+capitalize(service_name_)+"_Iface.hs";
   f_iface_.open(f_iface_name.c_str());
   indent(f_iface_) << "module " << capitalize(service_name_) << "_Iface where" << endl;
 
@@ -623,7 +623,7 @@
  * @param tservice The service to generate a server for.
  */
 void t_hs_generator::generate_service_client(t_service* tservice) {
-  string f_client_name = string(T_HS_DIR)+"/"+capitalize(service_name_)+"_Client.hs";
+  string f_client_name = get_out_dir()+capitalize(service_name_)+"_Client.hs";
   f_client_.open(f_client_name.c_str());
 
   vector<t_function*> functions = tservice->get_functions();
diff --git a/compiler/cpp/src/generate/t_hs_generator.h b/compiler/cpp/src/generate/t_hs_generator.h
index 41a59b1..bae507b 100644
--- a/compiler/cpp/src/generate/t_hs_generator.h
+++ b/compiler/cpp/src/generate/t_hs_generator.h
@@ -14,8 +14,6 @@
 
 #include "t_oop_generator.h"
 
-#define T_HS_DIR "gen-hs"
-
 /**
  * Haskell code generator.
  *
@@ -24,7 +22,10 @@
 class t_hs_generator : public t_oop_generator {
  public:
   t_hs_generator(t_program* program) :
-    t_oop_generator(program) {}
+    t_oop_generator(program) {
+
+    out_dir_base_ = "gen-hs";
+  }
 
   /**
    * Init and close methods
diff --git a/compiler/cpp/src/generate/t_java_generator.cc b/compiler/cpp/src/generate/t_java_generator.cc
index 0bfab75..a6d2c4a 100644
--- a/compiler/cpp/src/generate/t_java_generator.cc
+++ b/compiler/cpp/src/generate/t_java_generator.cc
@@ -18,12 +18,11 @@
  */
 void t_java_generator::init_generator() {
   // Make output directory
-  const char* java_dir = bean_style_ ? T_JAVABEAN_DIR : T_JAVA_DIR;
-  mkdir(java_dir, S_IREAD | S_IWRITE | S_IEXEC);
+  mkdir(get_out_dir().c_str(), S_IREAD | S_IWRITE | S_IEXEC);
   package_name_ = program_->get_java_package();
 
   string dir = package_name_;
-  string subdir = java_dir;
+  string subdir = get_out_dir();
   string::size_type loc;
   while ((loc = dir.find(".")) != string::npos) {
     subdir = subdir + "/" + dir.substr(0, loc);
diff --git a/compiler/cpp/src/generate/t_java_generator.h b/compiler/cpp/src/generate/t_java_generator.h
index 913b0f9..d3b85d5 100644
--- a/compiler/cpp/src/generate/t_java_generator.h
+++ b/compiler/cpp/src/generate/t_java_generator.h
@@ -14,10 +14,6 @@
 
 #include "t_oop_generator.h"
 
-// TODO(mcslee: Paramaterize the output dir
-#define T_JAVA_DIR "gen-java"
-#define T_JAVABEAN_DIR "gen-javabean"
-
 /**
  * Java code generator.
  *
@@ -27,7 +23,11 @@
  public:
   t_java_generator(t_program* program, bool bean_style=false) :
     t_oop_generator(program),
-    bean_style_(bean_style) {}
+    bean_style_(bean_style) {
+
+    out_dir_base_ = (bean_style_ ? "gen-javabean" : "gen-java");
+  }
+
 
   /**
    * Init and close methods
diff --git a/compiler/cpp/src/generate/t_ocaml_generator.cc b/compiler/cpp/src/generate/t_ocaml_generator.cc
index 0a5f97b..7bfe4fd 100644
--- a/compiler/cpp/src/generate/t_ocaml_generator.cc
+++ b/compiler/cpp/src/generate/t_ocaml_generator.cc
@@ -72,15 +72,15 @@
  */
 void t_ocaml_generator::init_generator() {
   // Make output directory
-  mkdir(T_OCAML_DIR, S_IREAD | S_IWRITE | S_IEXEC);
+  mkdir(get_out_dir().c_str(), S_IREAD | S_IWRITE | S_IEXEC);
 
   // Make output file
-  string f_types_name = string(T_OCAML_DIR)+"/"+program_name_+"_types.ml";
+  string f_types_name = get_out_dir()+program_name_+"_types.ml";
   f_types_.open(f_types_name.c_str());
-  string f_types_i_name = string(T_OCAML_DIR)+"/"+program_name_+"_types.mli";
+  string f_types_i_name = get_out_dir()+program_name_+"_types.mli";
   f_types_i_.open(f_types_i_name.c_str());
 
-  string f_consts_name = string(T_OCAML_DIR)+"/"+program_name_+"_consts.ml";
+  string f_consts_name = get_out_dir()+program_name_+"_consts.ml";
   f_consts_.open(f_consts_name.c_str());
 
   // Print header
@@ -557,9 +557,9 @@
  * @param tservice The service definition
  */
 void t_ocaml_generator::generate_service(t_service* tservice) {
-  string f_service_name = string(T_OCAML_DIR)+"/"+capitalize(service_name_)+".ml";
+  string f_service_name = get_out_dir()+capitalize(service_name_)+".ml";
   f_service_.open(f_service_name.c_str());
-  string f_service_i_name = string(T_OCAML_DIR)+"/"+capitalize(service_name_)+".mli";
+  string f_service_i_name = get_out_dir()+capitalize(service_name_)+".mli";
   f_service_i_.open(f_service_i_name.c_str());
 
   f_service_ <<
diff --git a/compiler/cpp/src/generate/t_ocaml_generator.h b/compiler/cpp/src/generate/t_ocaml_generator.h
index 1ea9b2f..331cf22 100644
--- a/compiler/cpp/src/generate/t_ocaml_generator.h
+++ b/compiler/cpp/src/generate/t_ocaml_generator.h
@@ -14,8 +14,6 @@
 
 #include "t_oop_generator.h"
 
-#define T_OCAML_DIR "gen-ocaml"
-
 /**
  * OCaml code generator.
  *
@@ -24,7 +22,9 @@
 class t_ocaml_generator : public t_oop_generator {
  public:
   t_ocaml_generator(t_program* program) :
-    t_oop_generator(program) {}
+    t_oop_generator(program) {
+    out_dir_base_ = "gen-ocaml";
+  }
 
   /**
    * Init and close methods
diff --git a/compiler/cpp/src/generate/t_perl_generator.cc b/compiler/cpp/src/generate/t_perl_generator.cc
index 76a4443..edec010 100644
--- a/compiler/cpp/src/generate/t_perl_generator.cc
+++ b/compiler/cpp/src/generate/t_perl_generator.cc
@@ -18,19 +18,19 @@
  */
 void t_perl_generator::init_generator() {
   // Make output directory
-  mkdir(T_PERL_DIR, S_IREAD | S_IWRITE | S_IEXEC);
+  mkdir(get_out_dir().c_str(), S_IREAD | S_IWRITE | S_IEXEC);
 
-  string outdir(T_PERL_DIR);
+  string outdir = get_out_dir();
   std::string ns = program_->get_perl_package();
   if (ns.length() > 0) {
-    outdir += "/" + ns;
+    outdir += ns + "/";
     mkdir(outdir.c_str(), S_IREAD | S_IWRITE | S_IEXEC);
   }
 
   // Make output file
-  string f_types_name = outdir+"/Types.pm";
+  string f_types_name = outdir+"Types.pm";
   f_types_.open(f_types_name.c_str());
-  string f_consts_name = outdir+"/Constants.pm";
+  string f_consts_name = outdir+"Constants.pm";
   f_consts_.open(f_consts_name.c_str());
 
   // Print header
@@ -465,7 +465,7 @@
  * @param tservice The service definition
  */
 void t_perl_generator::generate_service(t_service* tservice) {
-  string f_service_name = string(T_PERL_DIR)+"/"+service_name_+".pm";
+  string f_service_name = get_out_dir()+service_name_+".pm";
   f_service_.open(f_service_name.c_str());
 
   f_service_ <<
diff --git a/compiler/cpp/src/generate/t_perl_generator.h b/compiler/cpp/src/generate/t_perl_generator.h
index a023543..dcb2e5e 100644
--- a/compiler/cpp/src/generate/t_perl_generator.h
+++ b/compiler/cpp/src/generate/t_perl_generator.h
@@ -23,7 +23,8 @@
  public:
   t_perl_generator(t_program* program) :
     t_oop_generator(program) {
-    T_PERL_DIR = "gen-perl";
+    
+    out_dir_base_ = "gen-perl";
   }
 
   /**
@@ -148,11 +149,6 @@
  private:
 
   /**
-   * Output directory
-   */
-  char* T_PERL_DIR;
-
-  /**
    * File streams
    */
   std::ofstream f_types_;
diff --git a/compiler/cpp/src/generate/t_php_generator.cc b/compiler/cpp/src/generate/t_php_generator.cc
index 2f478fa..0a680a5 100644
--- a/compiler/cpp/src/generate/t_php_generator.cc
+++ b/compiler/cpp/src/generate/t_php_generator.cc
@@ -18,12 +18,12 @@
  */
 void t_php_generator::init_generator() {
   // Make output directory
-  mkdir(T_PHP_DIR, S_IREAD | S_IWRITE | S_IEXEC);
+  mkdir(get_out_dir().c_str(), S_IREAD | S_IWRITE | S_IEXEC);
 
   // Make output file
-  string f_types_name = string(T_PHP_DIR)+"/"+program_name_+"_types.php";
+  string f_types_name = get_out_dir()+program_name_+"_types.php";
   f_types_.open(f_types_name.c_str());
-  string f_consts_name = string(T_PHP_DIR)+"/"+program_name_+"_constants.php";
+  string f_consts_name = get_out_dir()+program_name_+"_constants.php";
   f_consts_.open(f_consts_name.c_str());
 
   // Print header
@@ -552,7 +552,7 @@
  * @param tservice The service definition
  */
 void t_php_generator::generate_service(t_service* tservice) {
-  string f_service_name = string(T_PHP_DIR)+"/"+service_name_+".php";
+  string f_service_name = get_out_dir()+service_name_+".php";
   f_service_.open(f_service_name.c_str());
 
   f_service_ <<
diff --git a/compiler/cpp/src/generate/t_php_generator.h b/compiler/cpp/src/generate/t_php_generator.h
index 16aac87..1f2e258 100644
--- a/compiler/cpp/src/generate/t_php_generator.h
+++ b/compiler/cpp/src/generate/t_php_generator.h
@@ -25,11 +25,7 @@
     t_oop_generator(program),
     binary_inline_(binary_inline),
     rest_(rest) {
-    if (binary_inline_) {
-      T_PHP_DIR = "gen-phpi";
-    } else {
-      T_PHP_DIR = "gen-php";
-    }
+    out_dir_base_ = (binary_inline_ ? "gen-phpi" : "gen-php");
   }
   
   /**
@@ -146,11 +142,6 @@
  private:
 
   /**
-   * Output directory
-   */
-  char* T_PHP_DIR;
-
-  /**
    * File streams
    */
   std::ofstream f_types_;
diff --git a/compiler/cpp/src/generate/t_py_generator.cc b/compiler/cpp/src/generate/t_py_generator.cc
index 8433058..3dd3892 100644
--- a/compiler/cpp/src/generate/t_py_generator.cc
+++ b/compiler/cpp/src/generate/t_py_generator.cc
@@ -21,7 +21,7 @@
 void t_py_generator::init_generator() {
   // Make output directory
   string module = get_real_py_module(program_);
-  package_dir_ = T_PY_DIR;
+  package_dir_ = get_out_dir();
   while (true) {
     // TODO: Do better error checking here.
     mkdir(package_dir_.c_str(), S_IREAD | S_IWRITE | S_IEXEC);
diff --git a/compiler/cpp/src/generate/t_py_generator.h b/compiler/cpp/src/generate/t_py_generator.h
index 723745f..c2eed8d 100644
--- a/compiler/cpp/src/generate/t_py_generator.h
+++ b/compiler/cpp/src/generate/t_py_generator.h
@@ -14,8 +14,6 @@
 
 #include "t_generator.h"
 
-#define T_PY_DIR "gen-py"
-
 /**
  * Python code generator.
  *
@@ -24,7 +22,9 @@
 class t_py_generator : public t_generator {
  public:
   t_py_generator(t_program* program) :
-    t_generator(program) {}
+    t_generator(program) {
+    out_dir_base_ = "gen-py";
+  }
 
   /**
    * Init and close methods
diff --git a/compiler/cpp/src/generate/t_rb_generator.cc b/compiler/cpp/src/generate/t_rb_generator.cc
index 9e3cd3c..b68ef30 100644
--- a/compiler/cpp/src/generate/t_rb_generator.cc
+++ b/compiler/cpp/src/generate/t_rb_generator.cc
@@ -19,13 +19,13 @@
  */
 void t_rb_generator::init_generator() {
   // Make output directory
-  mkdir(T_RB_DIR, S_IREAD | S_IWRITE | S_IEXEC);
+  mkdir(get_out_dir().c_str(), S_IREAD | S_IWRITE | S_IEXEC);
 
   // Make output file
-  string f_types_name = string(T_RB_DIR)+"/"+program_name_+"_types.rb";
+  string f_types_name = get_out_dir()+program_name_+"_types.rb";
   f_types_.open(f_types_name.c_str());
 
-  string f_consts_name = string(T_RB_DIR)+"/"+program_name_+"_constants.rb";
+  string f_consts_name = get_out_dir()+program_name_+"_constants.rb";
   f_consts_.open(f_consts_name.c_str());
 
   // Print header
@@ -380,7 +380,7 @@
  * @param tservice The service definition
  */
 void t_rb_generator::generate_service(t_service* tservice) {
-  string f_service_name = string(T_RB_DIR)+"/"+service_name_+".rb";
+  string f_service_name = get_out_dir()+service_name_+".rb";
   f_service_.open(f_service_name.c_str());
 
   f_service_ <<
diff --git a/compiler/cpp/src/generate/t_rb_generator.h b/compiler/cpp/src/generate/t_rb_generator.h
index 0c7524f..d5b8abc 100644
--- a/compiler/cpp/src/generate/t_rb_generator.h
+++ b/compiler/cpp/src/generate/t_rb_generator.h
@@ -15,8 +15,6 @@
 
 #include "t_oop_generator.h"
 
-#define T_RB_DIR "gen-rb"
-
 /**
  * Ruby code generator.
  *
@@ -25,7 +23,10 @@
 class t_rb_generator : public t_oop_generator {
  public:
   t_rb_generator(t_program* program) :
-    t_oop_generator(program) {}
+    t_oop_generator(program) {
+
+    out_dir_base_ = "gen-rb";
+  }
 
   /**
    * Init and close methods
diff --git a/compiler/cpp/src/generate/t_xsd_generator.cc b/compiler/cpp/src/generate/t_xsd_generator.cc
index 3f0b8f9..0910048 100644
--- a/compiler/cpp/src/generate/t_xsd_generator.cc
+++ b/compiler/cpp/src/generate/t_xsd_generator.cc
@@ -12,10 +12,10 @@
 
 void t_xsd_generator::init_generator() {
   // Make output directory
-  mkdir(T_XSD_DIR, S_IREAD | S_IWRITE | S_IEXEC);
+  mkdir(get_out_dir().c_str(), S_IREAD | S_IWRITE | S_IEXEC);
 
   // Make output file
-  string f_php_name = string(T_XSD_DIR)+"/"+program_->get_name()+"_xsd.php";
+  string f_php_name = get_out_dir()+program_->get_name()+"_xsd.php";
   f_php_.open(f_php_name.c_str());
 
   f_php_ <<
@@ -170,7 +170,7 @@
 
 void t_xsd_generator::generate_service(t_service* tservice) {
   // Make output file
-  string f_xsd_name = string(T_XSD_DIR)+"/"+tservice->get_name()+".xsd";
+  string f_xsd_name = get_out_dir()+tservice->get_name()+".xsd";
   f_xsd_.open(f_xsd_name.c_str());
 
   string ns = program_->get_xsd_namespace();
diff --git a/compiler/cpp/src/generate/t_xsd_generator.h b/compiler/cpp/src/generate/t_xsd_generator.h
index aa5623f..31d9f60 100644
--- a/compiler/cpp/src/generate/t_xsd_generator.h
+++ b/compiler/cpp/src/generate/t_xsd_generator.h
@@ -12,9 +12,6 @@
 #include <sstream>
 #include "t_generator.h"
 
-// TODO(mcslee): Paramaterize the output dir
-#define T_XSD_DIR "gen-xsd"
-
 /**
  * XSD generator, creates an XSD for the base types etc.
  *
@@ -23,7 +20,9 @@
 class t_xsd_generator : public t_generator {
  public:
   t_xsd_generator(t_program* program) :
-    t_generator(program) {}
+    t_generator(program) {
+    out_dir_base_ = "gen-xsd";
+  }
 
   virtual ~t_xsd_generator() {}
 
diff --git a/compiler/cpp/src/main.cc b/compiler/cpp/src/main.cc
index 0078055..468a7b0 100644
--- a/compiler/cpp/src/main.cc
+++ b/compiler/cpp/src/main.cc
@@ -21,6 +21,7 @@
 #include <string>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <errno.h>
 
 // Careful: must include globals first for extern definitions
 #include "globals.h"
@@ -566,6 +567,8 @@
   fprintf(stderr, "  -ocaml      Generate OCaml output files\n");
   fprintf(stderr, "  -hs         Generate Haskell output files\n");
   fprintf(stderr, "  -cocoa      Generate Cocoa/Objective-C output files\n");
+  fprintf(stderr, "  -o dir      Set the output directory for gen-* packages\n");
+  fprintf(stderr, "               (default: current directory)\n");
   fprintf(stderr, "  -I dir      Add a directory to the list of directories\n");
   fprintf(stderr, "                searched for include directives\n");
   fprintf(stderr, "  -dense      Generate metadata for TDenseProtocol (C++)\n");
@@ -770,6 +773,9 @@
   if (gen_recurse) {
     const vector<t_program*>& includes = program->get_includes();
     for (size_t i = 0; i < includes.size(); ++i) {
+      // Propogate output path from parent to child programs
+      includes[i]->set_out_path(program->get_out_path());
+    
       generate(includes[i]);
     }
   }
@@ -889,6 +895,7 @@
  */
 int main(int argc, char** argv) {
   int i;
+  std::string out_path;
 
   // Setup time string
   time_t now = time(NULL);
@@ -960,6 +967,22 @@
           usage();
         }
         g_incl_searchpath.push_back(arg);
+      } else if (strcmp(arg, "-o") == 0) {
+        arg = argv[++i];
+        if (arg == NULL) {
+          fprintf(stderr, "-o: missing output directory");
+          usage();
+        } 
+        out_path = arg;
+        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));
+          return -1;
+        }
+        if (! S_ISDIR(sb.st_mode)) {
+          fprintf(stderr, "Output directory %s exists but is not a directory\n", out_path.c_str());
+          return -1;
+        }
       } else {
         fprintf(stderr, "!!! Unrecognized option: %s\n", arg);
         usage();
@@ -985,6 +1008,9 @@
 
   // Instance of the global parse tree
   t_program* program = new t_program(input_file);
+  if (out_path.size()) {
+    program->set_out_path(out_path);
+  }
 
   // Initialize global types
   g_type_void   = new t_base_type("void",   t_base_type::TYPE_VOID);
diff --git a/compiler/cpp/src/parse/t_program.h b/compiler/cpp/src/parse/t_program.h
index f7516a3..224c9d5 100644
--- a/compiler/cpp/src/parse/t_program.h
+++ b/compiler/cpp/src/parse/t_program.h
@@ -46,12 +46,14 @@
  public:
   t_program(std::string path, std::string name) :
     path_(path),
-    name_(name) {
+    name_(name),
+    out_path_("./") {
     scope_ = new t_scope();
   }
 
   t_program(std::string path) :
-    path_(path) {
+    path_(path),
+    out_path_("./") {
     name_ = program_name(path);
     scope_ = new t_scope();
   }
@@ -59,6 +61,9 @@
   // Path accessor
   const std::string& get_path() const { return path_; }
 
+  // Output path accessor
+  const std::string& get_out_path() const { return out_path_; }
+
   // Name accessor
   const std::string& get_name() const { return name_; }
 
@@ -84,6 +89,15 @@
   // Programs to include
   const std::vector<t_program*>& get_includes() const { return includes_; }
 
+  void set_out_path(std::string out_path) {
+    out_path_ = out_path;
+    // Ensure that it ends with a trailing '/' (or '\' for windows machines)
+    char c = out_path_.at(out_path_.size() - 1);
+    if (!(c == '/' || c == '\\')) {
+      out_path_.push_back('/');
+    }
+  }
+
   // Scoping and namespacing
   void set_namespace(std::string name) {
     namespace_ = name;
@@ -186,6 +200,9 @@
   // Name
   std::string name_;
 
+  // Output directory
+  std::string out_path_;
+
   // Namespace
   std::string namespace_;