Infrastructure for loading code generators a bit more dynamically.

Add a generic and easy-to-use mechanism for Thrift code generators to
register themselves centrally.  The central registry is used to
obtain documentation for the options accepted by individual generators
and get instances of individual generators.  It also does a little bit of
option parsing that will be useful for all generators.

Obviously, this change cannot be tested on its own.  I can only say
that Thrift still builds and runs correctly.  Subsequent changes
will apply this infrastructure to specific code generators.
Steve Grimm has assured me that this is standard Git practice.

In fact, I ran this test after converting the C++ and Java generators:

dreiss@dreiss-vmware:dynamic_generators:thrift/test$ mkdir old new
dreiss@dreiss-vmware:dynamic_generators:thrift/test$ cd old
dreiss@dreiss-vmware:dynamic_generators:thrift/test/old$ ../../compiler/cpp/thrift -cpp -dense -java -javabean ../DebugProtoTest.thrift
[WARNING::1] -cpp is deprecated.  Use --gen cpp
[WARNING::1] -java is deprecated.  Use --gen java
[WARNING::1] -javabean is deprecated.  Use --gen java:beans
dreiss@dreiss-vmware:dynamic_generators:thrift/test/old$ cd ../new/
dreiss@dreiss-vmware:dynamic_generators:thrift/test/new$ ../../compiler/cpp/thrift --gen cpp:dense --gen java --gen java:beans ../DebugProtoTest.thrift
dreiss@dreiss-vmware:dynamic_generators:thrift/test/new$ cd ..
dreiss@dreiss-vmware:dynamic_generators:thrift/test$ diff -ur old/ new/
dreiss@dreiss-vmware:dynamic_generators:thrift/test$


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665507 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/compiler/cpp/src/main.cc b/compiler/cpp/src/main.cc
index 8b1848c..b24a903 100644
--- a/compiler/cpp/src/main.cc
+++ b/compiler/cpp/src/main.cc
@@ -635,6 +635,21 @@
   fprintf(stderr, "  -v[erbose]  Verbose mode\n");
   fprintf(stderr, "  -r[ecurse]  Also generate included files\n");
   fprintf(stderr, "  -debug      Parse debug trace to stdout\n");
+  fprintf(stderr, "  --gen STR   Generate code with a dynamically-registered generator.\n");
+  fprintf(stderr, "                STR has the form language[:key1=val1[,key2,[key3=val3]]].\n");
+  fprintf(stderr, "                Keys and values are options passed to the generator.\n");
+  fprintf(stderr, "                Many options will not require values.\n");
+  fprintf(stderr, "\n");
+  fprintf(stderr, "Available generators (and options):\n");
+
+  t_generator_registry::gen_map_t gen_map = t_generator_registry::get_generator_map();
+  t_generator_registry::gen_map_t::iterator iter;
+  for (iter = gen_map.begin(); iter != gen_map.end(); ++iter) {
+    fprintf(stderr, "  %s (%s):\n",
+        iter->second->get_short_name().c_str(),
+        iter->second->get_long_name().c_str());
+    fprintf(stderr, "%s", iter->second->get_documentation().c_str());
+  }
   exit(1);
 }
 
@@ -839,7 +854,7 @@
 /**
  * Generate code
  */
-void generate(t_program* program) {
+void generate(t_program* program, const vector<string>& generator_strings) {
   // Oooohh, recursive code generation, hot!!
   if (gen_recurse) {
     const vector<t_program*>& includes = program->get_includes();
@@ -847,7 +862,7 @@
       // Propogate output path from parent to child programs
       includes[i]->set_out_path(program->get_out_path());
 
-      generate(includes[i]);
+      generate(includes[i], generator_strings);
     }
   }
 
@@ -967,6 +982,19 @@
     if (dump_docs) {
       dump_docstrings(program);
     }
+
+    vector<string>::const_iterator iter;
+    for (iter = generator_strings.begin(); iter != generator_strings.end(); ++iter) {
+      t_generator* generator = t_generator_registry::get_generator(program, *iter);
+
+      if (generator == NULL) {
+        pwarning(1, "Unable to get a generator for \"%s\".\n", iter->c_str());
+      } else {
+        pverbose("Generating \"%s\"\n", iter->c_str());
+        generator->generate_program();
+      }
+    }
+
   } catch (string s) {
     printf("Error: %s\n", s.c_str());
   } catch (const char* exc) {
@@ -993,6 +1021,8 @@
     usage();
   }
 
+  vector<string> generator_strings;
+
   // Set the current path to a dummy value to make warning messages clearer.
   g_curpath = "arguments";
 
@@ -1017,6 +1047,13 @@
         g_verbose = 1;
       } else if (strcmp(arg, "-r") == 0 || strcmp(arg, "-recurse") == 0 ) {
         gen_recurse = true;
+      } else if (strcmp(arg, "-gen") == 0) {
+        arg = argv[++i];
+        if (arg == NULL) {
+          fprintf(stderr, "!!! Missing generator specification");
+          usage();
+        }
+        generator_strings.push_back(arg);
       } else if (strcmp(arg, "-dense") == 0) {
         gen_dense = true;
       } else if (strcmp(arg, "-cpp") == 0) {
@@ -1115,7 +1152,7 @@
   }
 
   // You gotta generate something!
-  if (!gen_cpp && !gen_java && !gen_javabean && !gen_php && !gen_phpi && !gen_py && !gen_rb && !gen_xsd && !gen_perl && !gen_erl && !gen_ocaml && !gen_hs && !gen_cocoa && !gen_st && !gen_csharp) {
+  if (!gen_cpp && !gen_java && !gen_javabean && !gen_php && !gen_phpi && !gen_py && !gen_rb && !gen_xsd && !gen_perl && !gen_erl && !gen_ocaml && !gen_hs && !gen_cocoa && !gen_st && !gen_csharp && generator_strings.empty()) {
     fprintf(stderr, "!!! No output language(s) specified\n\n");
     usage();
   }
@@ -1170,7 +1207,7 @@
   yylineno = 1;
 
   // Generate it!
-  generate(program);
+  generate(program, generator_strings);
 
   // Clean up. Who am I kidding... this program probably orphans heap memory
   // all over the place, but who cares because it is about to exit and it is