OpenSDN source code
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
src/contrail-common/sandesh/compiler/main.cc
Go to the documentation of this file.
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  *
19  * Copyright 2006-2017 The Apache Software Foundation.
20  * https://github.com/apache/thrift
21  */
22 
35 #include <cassert>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <stdarg.h>
39 #include <time.h>
40 #include <string>
41 #include <algorithm>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <errno.h>
45 #include <limits.h>
46 
47 // Careful: must include globals first for extern definitions
48 #include "globals.h"
49 
50 #include "main.h"
51 #include "parse/t_program.h"
52 #include "parse/t_scope.h"
53 #include "generate/t_generator.h"
54 
55 #include "version.h"
56 
57 using namespace std;
58 
63 
78 #ifdef SANDESH
79 t_type* g_type_u16;
80 t_type* g_type_u32;
81 t_type* g_type_u64;
82 t_type* g_type_ipv4;
83 t_type* g_type_ipaddr;
84 t_type* g_type_static_const_string;
85 t_type* g_type_sandesh_system;
86 t_type* g_type_sandesh_request;
87 t_type* g_type_sandesh_response;
88 t_type* g_type_sandesh_trace;
89 t_type* g_type_sandesh_trace_object;
90 t_type* g_type_sandesh_buffer;
91 t_type* g_type_sandesh_uve;
92 t_type* g_type_sandesh_dynamic_uve;
93 t_type* g_type_sandesh_alarm;
94 t_type* g_type_sandesh_object;
95 t_type* g_type_sandesh_flow;
96 t_type* g_type_sandesh_session;
97 t_type* g_type_xml;
98 t_type* g_type_uuid_t;
99 
100 #endif
101 
102 
107 
112 
117 
122 
126 string g_curdir;
127 
131 string g_curpath;
132 
136 vector<string> g_incl_searchpath;
137 
143 
147 int g_debug = 0;
148 
152 int g_strict = 127;
153 
157 int g_warn = 1;
158 
162 int g_verbose = 0;
163 
168 
172 char* g_doctext;
173 
178 
185 
190 
195 
199 bool gen_cpp = false;
200 bool gen_dense = false;
201 bool gen_java = false;
202 bool gen_javabean = false;
203 bool gen_rb = false;
204 bool gen_py = false;
205 bool gen_py_newstyle = false;
206 bool gen_xsd = false;
207 bool gen_php = false;
208 bool gen_phpi = false;
209 bool gen_phps = true;
210 bool gen_phpa = false;
211 bool gen_phpo = false;
212 bool gen_rest = false;
213 bool gen_perl = false;
214 bool gen_erl = false;
215 bool gen_ocaml = false;
216 bool gen_hs = false;
217 bool gen_cocoa = false;
218 bool gen_csharp = false;
219 bool gen_delphi = false;
220 bool gen_st = false;
221 bool gen_recurse = false;
222 
227 char *saferealpath(const char *path, char *resolved_path) {
228  return realpath(path, resolved_path);
229 }
230 
231 
240 void yyerror(const char* fmt, ...) {
241  va_list args;
242  fprintf(stderr,
243  "[ERROR:%s:%d] (last token was '%s')\n",
244  g_curpath.c_str(),
245  yylineno,
246  yytext);
247 
248  va_start(args, fmt);
249  vfprintf(stderr, fmt, args);
250  va_end(args);
251 
252  fprintf(stderr, "\n");
253 }
254 
260 void pdebug(const char* fmt, ...) {
261  if (g_debug == 0) {
262  return;
263  }
264  va_list args;
265  printf("[PARSE:%d] ", yylineno);
266  va_start(args, fmt);
267  vprintf(fmt, args);
268  va_end(args);
269  printf("\n");
270 }
271 
277 void pverbose(const char* fmt, ...) {
278  if (g_verbose == 0) {
279  return;
280  }
281  va_list args;
282  va_start(args, fmt);
283  vprintf(fmt, args);
284  va_end(args);
285 }
286 
292 void pwarning(int level, const char* fmt, ...) {
293  if (g_warn < level) {
294  return;
295  }
296  va_list args;
297  printf("[WARNING:%s:%d] ", g_curpath.c_str(), yylineno);
298  va_start(args, fmt);
299  vprintf(fmt, args);
300  va_end(args);
301  printf("\n");
302 }
303 
309 void failure(const char* fmt, ...) {
310  va_list args;
311  fprintf(stderr, "[FAILURE:%s:%d] ", g_curpath.c_str(), yylineno);
312  va_start(args, fmt);
313  vfprintf(stderr, fmt, args);
314  va_end(args);
315  printf("\n");
316  exit(1);
317 }
318 
322 string program_name(string filename) {
323  string::size_type slash = filename.rfind("/");
324  if (slash != string::npos) {
325  filename = filename.substr(slash+1);
326  }
327  string::size_type dot = filename.rfind(".");
328  if (dot != string::npos) {
329  filename = filename.substr(0, dot);
330  }
331  return filename;
332 }
333 
337 string directory_name(string filename) {
338  string::size_type slash = filename.rfind("/");
339  // No slash, just use the current directory
340  if (slash == string::npos) {
341  return ".";
342  }
343  return filename.substr(0, slash);
344 }
345 
349 string include_file(string filename) {
350  // Absolute path? Just try that
351  if (filename[0] == '/') {
352  // Realpath!
353  char rp[PATH_MAX];
354  if (saferealpath(filename.c_str(), rp) == NULL) {
355  pwarning(0, "Cannot open include file %s\n", filename.c_str());
356  return std::string();
357  }
358 
359  // Stat this file
360  struct stat finfo;
361  if (stat(rp, &finfo) == 0) {
362  return rp;
363  }
364  } else { // relative path, start searching
365  // new search path with current dir global
366  vector<string> sp = g_incl_searchpath;
367  sp.insert(sp.begin(), g_curdir);
368 
369  // iterate through paths
370  vector<string>::iterator it;
371  for (it = sp.begin(); it != sp.end(); it++) {
372  string sfilename = *(it) + "/" + filename;
373 
374  // Realpath!
375  char rp[PATH_MAX];
376  if (saferealpath(sfilename.c_str(), rp) == NULL) {
377  continue;
378  }
379 
380  // Stat this files
381  struct stat finfo;
382  if (stat(rp, &finfo) == 0) {
383  return rp;
384  }
385  }
386  }
387 
388  // Uh oh
389  pwarning(0, "Could not find include file %s\n", filename.c_str());
390  return std::string();
391 }
392 
398  if (g_doctext != NULL) {
399  pwarning(2, "Uncaptured doctext at on line %d.", g_doctext_lineno);
400  }
401  free(g_doctext);
402  g_doctext = NULL;
403 }
404 
409  if(g_program_doctext_candidate != NULL) {
412  }
415  pdebug("%s","program doctext set to INVALID");
416 }
417 
424  pdebug("%s","program doctext set to ABSOLUTELY_SURE");
425  } else {
427  pdebug("%s","program doctext set to NO_PROGRAM_DOCTEXT");
428  }
429 }
430 
437 char* clean_up_doctext(char* doctext) {
438  // Convert to C++ string, and remove carriage returns if added.
439  string docstring = doctext;
440  docstring.erase(
441  remove(docstring.begin(), docstring.end(), '\r'),
442  docstring.end());
443 
444  // Separate into lines.
445  vector<string> lines;
446  string::size_type pos = string::npos;
447  string::size_type last;
448  while (true) {
449  last = (pos == string::npos) ? 0 : pos+1;
450  pos = docstring.find('\n', last);
451  if (pos == string::npos) {
452  // First bit of cleaning. If the last line is only whitespace, drop it.
453  string::size_type nonwhite = docstring.find_first_not_of(" \t", last);
454  if (nonwhite != string::npos) {
455  lines.push_back(docstring.substr(last));
456  }
457  break;
458  }
459  lines.push_back(docstring.substr(last, pos-last));
460  }
461 
462  // A very profound docstring.
463  if (lines.empty()) {
464  return NULL;
465  }
466 
467  // Clear leading whitespace from the first line.
468  pos = lines.front().find_first_not_of(" \t");
469  lines.front().erase(0, pos);
470 
471  // If every nonblank line after the first has the same number of spaces/tabs,
472  // then a star, remove them.
473  bool have_prefix = true;
474  bool found_prefix = false;
475  string::size_type prefix_len = 0;
476  vector<string>::iterator l_iter;
477  for (l_iter = lines.begin()+1; l_iter != lines.end(); ++l_iter) {
478  if (l_iter->empty()) {
479  continue;
480  }
481 
482  pos = l_iter->find_first_not_of(" \t");
483  if (!found_prefix) {
484  if (pos != string::npos) {
485  if (l_iter->at(pos) == '*') {
486  found_prefix = true;
487  prefix_len = pos;
488  } else {
489  have_prefix = false;
490  break;
491  }
492  } else {
493  // Whitespace-only line. Truncate it.
494  l_iter->clear();
495  }
496  } else if (l_iter->size() > pos
497  && l_iter->at(pos) == '*'
498  && pos == prefix_len) {
499  // Business as usual.
500  } else if (pos == string::npos) {
501  // Whitespace-only line. Let's truncate it for them.
502  l_iter->clear();
503  } else {
504  // The pattern has been broken.
505  have_prefix = false;
506  break;
507  }
508  }
509 
510  // If our prefix survived, delete it from every line.
511  if (have_prefix) {
512  // Get the star too.
513  prefix_len++;
514  for (l_iter = lines.begin()+1; l_iter != lines.end(); ++l_iter) {
515  l_iter->erase(0, prefix_len);
516  }
517  }
518 
519  // Now delete the minimum amount of leading whitespace from each line.
520  prefix_len = string::npos;
521  for (l_iter = lines.begin()+1; l_iter != lines.end(); ++l_iter) {
522  if (l_iter->empty()) {
523  continue;
524  }
525  pos = l_iter->find_first_not_of(" \t");
526  if (pos != string::npos
527  && (prefix_len == string::npos || pos < prefix_len)) {
528  prefix_len = pos;
529  }
530  }
531 
532  // If our prefix survived, delete it from every line.
533  if (prefix_len != string::npos) {
534  for (l_iter = lines.begin()+1; l_iter != lines.end(); ++l_iter) {
535  l_iter->erase(0, prefix_len);
536  }
537  }
538 
539  // Remove trailing whitespace from every line.
540  for (l_iter = lines.begin(); l_iter != lines.end(); ++l_iter) {
541  pos = l_iter->find_last_not_of(" \t");
542  if (pos != string::npos && pos != l_iter->length()-1) {
543  l_iter->erase(pos+1);
544  }
545  }
546 
547  // If the first line is empty, remove it.
548  // Don't do this earlier because a lot of steps skip the first line.
549  if (lines.front().empty()) {
550  lines.erase(lines.begin());
551  }
552 
553  // Now rejoin the lines and copy them back into doctext.
554  docstring.clear();
555  for (l_iter = lines.begin(); l_iter != lines.end(); ++l_iter) {
556  docstring += *l_iter;
557  docstring += '\n';
558  }
559 
560  assert(docstring.length() <= strlen(doctext));
561  strcpy(doctext, docstring.c_str());
562  return doctext;
563 }
564 
566 static bool dump_docs = false;
567 
573 void dump_docstrings(t_program* program) {
574 
575  string progdoc = program->get_doc();
576  if (!progdoc.empty()) {
577  printf("Whole program doc:\n%s\n", progdoc.c_str());
578  }
579  const vector<t_typedef*>& typedefs = program->get_typedefs();
580  vector<t_typedef*>::const_iterator t_iter;
581  for (t_iter = typedefs.begin(); t_iter != typedefs.end(); ++t_iter) {
582  t_typedef* td = *t_iter;
583  if (td->has_doc()) {
584  printf("typedef %s:\n%s\n", td->get_name().c_str(), td->get_doc().c_str());
585  }
586  }
587  const vector<t_enum*>& enums = program->get_enums();
588  vector<t_enum*>::const_iterator e_iter;
589  for (e_iter = enums.begin(); e_iter != enums.end(); ++e_iter) {
590  t_enum* en = *e_iter;
591  if (en->has_doc()) {
592  printf("enum %s:\n%s\n", en->get_name().c_str(), en->get_doc().c_str());
593  }
594  }
595  const vector<t_const*>& consts = program->get_consts();
596  vector<t_const*>::const_iterator c_iter;
597  for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
598  t_const* co = *c_iter;
599  if (co->has_doc()) {
600  printf("const %s:\n%s\n", co->get_name().c_str(), co->get_doc().c_str());
601  }
602  }
603  const vector<t_struct*>& structs = program->get_structs();
604  vector<t_struct*>::const_iterator s_iter;
605  for (s_iter = structs.begin(); s_iter != structs.end(); ++s_iter) {
606  t_struct* st = *s_iter;
607  if (st->has_doc()) {
608  printf("struct %s:\n%s\n", st->get_name().c_str(), st->get_doc().c_str());
609  }
610  const vector<t_field*>& fields = st->get_members();
611  vector<t_field*>::const_iterator f_iter;
612  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
613  t_field* fp = *f_iter;
614  if (fp->has_doc()) {
615  printf("fields %s:\n%s\n",
616  fp->get_name().c_str(), fp->get_doc().c_str());
617  }
618  }
619  }
620  const vector<t_struct*>& xceptions = program->get_xceptions();
621  vector<t_struct*>::const_iterator x_iter;
622  for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
623  t_struct* xn = *x_iter;
624  if (xn->has_doc()) {
625  printf("xception %s:\n%s\n", xn->get_name().c_str(), xn->get_doc().c_str());
626  }
627  }
628 #ifdef SANDESH
629  const vector<t_sandesh*>& sandeshs = program->get_sandeshs();
630  vector<t_sandesh*>::const_iterator sn_iter;
631  for (sn_iter = sandeshs.begin(); sn_iter != sandeshs.end(); ++sn_iter) {
632  t_sandesh* sn = *sn_iter;
633  if (sn->has_doc()) {
634  printf("sandesh %s:\n%s\n", sn->get_name().c_str(), sn->get_doc().c_str());
635  }
636  const vector<t_field*>& sn_fields = sn->get_members();
637  vector<t_field*>::const_iterator snf_iter;
638  for (snf_iter = sn_fields.begin(); snf_iter != sn_fields.end(); ++snf_iter) {
639  t_field* snfp = *snf_iter;
640  if (snfp->has_doc()) {
641  printf("fields %s:\n%s\n",
642  snfp->get_name().c_str(), snfp->get_doc().c_str());
643  }
644  }
645  }
646 #endif
647  const vector<t_service*>& services = program->get_services();
648  vector<t_service*>::const_iterator v_iter;
649  for (v_iter = services.begin(); v_iter != services.end(); ++v_iter) {
650  t_service* sv = *v_iter;
651  if (sv->has_doc()) {
652  printf("service %s:\n%s\n", sv->get_name().c_str(), sv->get_doc().c_str());
653  }
654  }
655 }
656 
661  const vector<t_struct*>& structs = program->get_structs();
662  vector<t_struct*>::const_iterator s_iter;
663  for (s_iter = structs.begin(); s_iter != structs.end(); ++s_iter) {
664  t_struct* st = *s_iter;
665  st->generate_fingerprint();
666  }
667 
668  const vector<t_struct*>& xceptions = program->get_xceptions();
669  vector<t_struct*>::const_iterator x_iter;
670  for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
671  t_struct* st = *x_iter;
672  st->generate_fingerprint();
673  }
674 
675  const vector<t_enum*>& enums = program->get_enums();
676  vector<t_enum*>::const_iterator e_iter;
677  for (e_iter = enums.begin(); e_iter != enums.end(); ++e_iter) {
678  t_enum* e = *e_iter;
680  }
681 
683 
684 #ifdef SANDESH
685  {
686  const vector<t_sandesh*>& sandeshs = program->get_sandeshs();
687  vector<t_sandesh*>::const_iterator s_iter;
688  for (s_iter = sandeshs.begin(); s_iter != sandeshs.end(); ++s_iter) {
689  t_sandesh* st = *s_iter;
690  st->generate_fingerprint();
691  }
692  }
693 #endif
694 
695  // If you want to generate fingerprints for implicit structures, start here.
696  /*
697  const vector<t_service*>& services = program->get_services();
698  vector<t_service*>::const_iterator v_iter;
699  for (v_iter = services.begin(); v_iter != services.end(); ++v_iter) {
700  t_service* sv = *v_iter;
701  }
702  */
703 }
704 
708 void version() {
709 #ifdef SANDESH
710  printf("Sandesh version %s\n", SANDESH_VERSION);
711 #else
712  printf("Thrift version %s\n", THRIFT_VERSION);
713 #endif
714 }
715 
719 void usage() {
720 #ifdef SANDESH
721  fprintf(stderr, "Usage: sandesh [options] file\n");
722 #else
723  fprintf(stderr, "Usage: thrift [options] file\n");
724 #endif
725  fprintf(stderr, "Options:\n");
726  fprintf(stderr, " -version Print the compiler version\n");
727  fprintf(stderr, " -o dir Set the output directory for gen-* packages\n");
728  fprintf(stderr, " (default: current directory)\n");
729  fprintf(stderr, " -out dir Set the ouput location for generated files.\n");
730  fprintf(stderr," (no gen-* folder will be created)\n");
731  fprintf(stderr, " -I dir Add a directory to the list of directories\n");
732  fprintf(stderr, " searched for include directives\n");
733  fprintf(stderr, " -nowarn Suppress all compiler warnings (BAD!)\n");
734  fprintf(stderr, " -strict Strict compiler warnings on\n");
735  fprintf(stderr, " -v[erbose] Verbose mode\n");
736  fprintf(stderr, " -r[ecurse] Also generate included files\n");
737  fprintf(stderr, " -debug Parse debug trace to stdout\n");
738  fprintf(stderr, " --allow-neg-keys Allow negative field keys (Used to "
739  "preserve protocol\n");
740  fprintf(stderr, " compatibility with older .thrift files)\n");
741  fprintf(stderr, " --allow-64bit-consts Do not print warnings about using 64-bit constants\n");
742  fprintf(stderr, " --gen STR Generate code with a dynamically-registered generator.\n");
743  fprintf(stderr, " STR has the form language[:key1=val1[,key2,[key3=val3]]].\n");
744  fprintf(stderr, " Keys and values are options passed to the generator.\n");
745  fprintf(stderr, " Many options will not require values.\n");
746  fprintf(stderr, "\n");
747  fprintf(stderr, "Available generators (and options):\n");
749  t_generator_registry::gen_map_t::iterator iter;
750  for (iter = gen_map.begin(); iter != gen_map.end(); ++iter) {
751  fprintf(stderr, " %s (%s):\n",
752  iter->second->get_short_name().c_str(),
753  iter->second->get_long_name().c_str());
754  fprintf(stderr, "%s", iter->second->get_documentation().c_str());
755  }
756  exit(1);
757 }
758 
767 void validate_const_rec(std::string name, t_type* type, t_const_value* value) {
768  if (type->is_void()) {
769  throw "type error: cannot declare a void const: " + name;
770  }
771 
772  if (type->is_base_type()) {
773  t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
774  switch (tbase) {
776  if (value->get_type() != t_const_value::CV_STRING) {
777  throw "type error: const \"" + name + "\" was declared as string";
778  }
779  break;
780 #ifdef SANDESH
781  case t_base_type::TYPE_STATIC_CONST_STRING:
782  if (value->get_type() != t_const_value::CV_STRING) {
783  throw "type error: const \"" + name + "\" was declared as static const string";
784  }
785  break;
786  case t_base_type::TYPE_U16:
787  if (value->get_type() != t_const_value::CV_INTEGER) {
788  throw "type error: const \"" + name + "\" was declared as u16";
789  }
790  break;
791  case t_base_type::TYPE_U32:
792  if (value->get_type() != t_const_value::CV_INTEGER) {
793  throw "type error: const \"" + name + "\" was declared as u32";
794  }
795  break;
796  case t_base_type::TYPE_U64:
797  if (value->get_type() != t_const_value::CV_INTEGER) {
798  throw "type error: const \"" + name + "\" was declared as u64";
799  }
800  break;
801  case t_base_type::TYPE_UUID:
802  if (value->get_type() != t_const_value::CV_STRING) {
803  throw "type error: const \"" + name + "\" was declared as ct_uuid_t";
804  }
805  break;
806  case t_base_type::TYPE_IPADDR:
807  if (value->get_type() != t_const_value::CV_STRING) {
808  throw "type error: const \"" + name + "\" was declared as ipaddr";
809  }
810  break;
811 #endif
813  if (value->get_type() != t_const_value::CV_INTEGER) {
814  throw "type error: const \"" + name + "\" was declared as bool";
815  }
816  break;
818  if (value->get_type() != t_const_value::CV_INTEGER) {
819  throw "type error: const \"" + name + "\" was declared as byte";
820  }
821  break;
823  if (value->get_type() != t_const_value::CV_INTEGER) {
824  throw "type error: const \"" + name + "\" was declared as i16";
825  }
826  break;
828  if (value->get_type() != t_const_value::CV_INTEGER) {
829  throw "type error: const \"" + name + "\" was declared as i32";
830  }
831  break;
833  if (value->get_type() != t_const_value::CV_INTEGER) {
834  throw "type error: const \"" + name + "\" was declared as i64";
835  }
836  break;
838  if (value->get_type() != t_const_value::CV_INTEGER &&
839  value->get_type() != t_const_value::CV_DOUBLE) {
840  throw "type error: const \"" + name + "\" was declared as double";
841  }
842  break;
843  default:
844  throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase) + name;
845  }
846  } else if (type->is_enum()) {
847  if (value->get_type() != t_const_value::CV_IDENTIFIER) {
848  throw "type error: const \"" + name + "\" was declared as enum";
849  }
850 
851  // see if there's a dot in the identifier
852  std::string name_portion = value->get_identifier_name();
853 
854  const vector<t_enum_value*>& enum_values = ((t_enum*)type)->get_constants();
855  vector<t_enum_value*>::const_iterator c_iter;
856  bool found = false;
857 
858  for (c_iter = enum_values.begin(); c_iter != enum_values.end(); ++c_iter) {
859  if ((*c_iter)->get_name() == name_portion) {
860  found = true;
861  break;
862  }
863  }
864  if (!found) {
865  throw "type error: const " + name + " was declared as type "
866  + type->get_name() + " which is an enum, but "
867  + value->get_identifier() + " is not a valid value for that enum";
868  }
869  } else if (type->is_struct() || type->is_xception()) {
870  if (value->get_type() != t_const_value::CV_MAP) {
871  throw "type error: const \"" + name + "\" was declared as struct/xception";
872  }
873  const vector<t_field*>& fields = ((t_struct*)type)->get_members();
874  vector<t_field*>::const_iterator f_iter;
875 
876  const map<t_const_value*, t_const_value*>& val = value->get_map();
877  map<t_const_value*, t_const_value*>::const_iterator v_iter;
878  for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
879  if (v_iter->first->get_type() != t_const_value::CV_STRING) {
880  throw "type error: " + name + " struct key must be string";
881  }
882  t_type* field_type = NULL;
883  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
884  if ((*f_iter)->get_name() == v_iter->first->get_string()) {
885  field_type = (*f_iter)->get_type();
886  }
887  }
888  if (field_type == NULL) {
889  throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
890  }
891 
892  validate_const_rec(name + "." + v_iter->first->get_string(), field_type, v_iter->second);
893  }
894  } else if (type->is_map()) {
895  t_type* k_type = ((t_map*)type)->get_key_type();
896  t_type* v_type = ((t_map*)type)->get_val_type();
897  const map<t_const_value*, t_const_value*>& val = value->get_map();
898  map<t_const_value*, t_const_value*>::const_iterator v_iter;
899  for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
900  validate_const_rec(name + "<key>", k_type, v_iter->first);
901  validate_const_rec(name + "<val>", v_type, v_iter->second);
902  }
903  } else if (type->is_list() || type->is_set()) {
904  t_type* e_type;
905  if (type->is_list()) {
906  e_type = ((t_list*)type)->get_elem_type();
907  } else {
908  e_type = ((t_set*)type)->get_elem_type();
909  }
910  const vector<t_const_value*>& val = value->get_list();
911  vector<t_const_value*>::const_iterator v_iter;
912  for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
913  validate_const_rec(name + "<elem>", e_type, *v_iter);
914  }
915  }
916 }
917 
922  validate_const_rec(c->get_name(), c->get_type(), c->get_value());
923 }
924 
929  validate_const_rec(field->get_name(), field->get_type(), cv);
930 }
931 
935 bool validate_throws(t_struct* throws) {
936  const vector<t_field*>& members = throws->get_members();
937  vector<t_field*>::const_iterator m_iter;
938  for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
939  if (!t_generator::get_true_type((*m_iter)->get_type())->is_xception()) {
940  return false;
941  }
942  }
943  return true;
944 }
945 
949 void parse(t_program* program, t_program* parent_program) {
950  // Get scope file path
951  string path = program->get_path();
952 
953  // Set current dir global, which is used in the include_file function
954  g_curdir = directory_name(path);
955  g_curpath = path;
956 
957  // Open the file
958  yyin = fopen(path.c_str(), "r");
959  if (yyin == 0) {
960  failure("Could not open input file: \"%s\"", path.c_str());
961  }
962 
963  // Create new scope and scan for includes
964  pverbose("Scanning %s for includes\n", path.c_str());
966  g_program = program;
967  g_scope = program->scope();
968  try {
969  yylineno = 1;
970  if (yyparse() != 0) {
971  failure("Parser error during include pass.");
972  }
973  } catch (string x) {
974  failure(x.c_str());
975  }
976  fclose(yyin);
977 
978  // Recursively parse all the include programs
979  vector<t_program*>& includes = program->get_includes();
980  vector<t_program*>::iterator iter;
981  for (iter = includes.begin(); iter != includes.end(); ++iter) {
982  parse(*iter, program);
983  }
984 
985  // reset program doctext status before parsing a new file
987 
988  // Parse the program file
989  g_parse_mode = PROGRAM;
990  g_program = program;
991  g_scope = program->scope();
992  g_parent_scope = (parent_program != NULL) ? parent_program->scope() : NULL;
993  g_parent_prefix = program->get_name() + ".";
994  g_curpath = path;
995  yyin = fopen(path.c_str(), "r");
996  if (yyin == 0) {
997  failure("Could not open input file: \"%s\"", path.c_str());
998  }
999  pverbose("Parsing %s for types\n", path.c_str());
1000  yylineno = 1;
1001  try {
1002  if (yyparse() != 0) {
1003  failure("Parser error during types pass.");
1004  }
1005  } catch (string x) {
1006  failure(x.c_str());
1007  }
1008  fclose(yyin);
1009 }
1010 
1014 void generate(t_program* program, const vector<string>& generator_strings) {
1015  // Oooohh, recursive code generation, hot!!
1016  if (gen_recurse) {
1017  const vector<t_program*>& includes = program->get_includes();
1018  for (size_t i = 0; i < includes.size(); ++i) {
1019  // Propogate output path from parent to child programs
1020  includes[i]->set_out_path(program->get_out_path(), program->is_out_path_absolute());
1021 
1022  generate(includes[i], generator_strings);
1023  }
1024  }
1025 
1026  // Generate code!
1027  try {
1028  pverbose("Program: %s\n", program->get_path().c_str());
1029 
1030  // Compute fingerprints.
1031  generate_all_fingerprints(program);
1032 
1033  if (dump_docs) {
1034  dump_docstrings(program);
1035  }
1036 
1037  vector<string>::const_iterator iter;
1038  for (iter = generator_strings.begin(); iter != generator_strings.end(); ++iter) {
1039  t_generator* generator = t_generator_registry::get_generator(program, *iter);
1040 
1041  if (generator == NULL) {
1042  pwarning(1, "Unable to get a generator for \"%s\".\n", iter->c_str());
1043  } else {
1044  pverbose("Generating \"%s\"\n", iter->c_str());
1045  generator->generate_program();
1046  delete generator;
1047  }
1048  }
1049 
1050  } catch (string s) {
1051  printf("Error: %s\n", s.c_str());
1052 #ifdef SANDESH
1053  failure("Error: %s\n", s.c_str());
1054 #endif
1055  } catch (const char* exc) {
1056  printf("Error: %s\n", exc);
1057 #ifdef SANDESH
1058  failure("Error: %s\n", exc);
1059 #endif
1060  }
1061 }
1062 
1067 int main(int argc, char** argv) {
1068  int i;
1069  std::string out_path;
1070  bool out_path_is_absolute = false;
1071 
1072  // Setup time string
1073  time_t now = time(NULL);
1074  g_time_str = ctime(&now);
1075 
1076  // Check for necessary arguments, you gotta have at least a filename and
1077  // an output language flag
1078  if (argc < 2) {
1079  usage();
1080  }
1081 
1082  vector<string> generator_strings;
1083 
1084  // Set the current path to a dummy value to make warning messages clearer.
1085  g_curpath = "arguments";
1086 
1087  // Hacky parameter handling... I didn't feel like using a library sorry!
1088  for (i = 1; i < argc-1; i++) {
1089  char* arg;
1090 
1091  char *saveptr;
1092  arg = strtok_r(argv[i], " ", &saveptr);
1093  while (arg != NULL) {
1094  // Treat double dashes as single dashes
1095  if (arg[0] == '-' && arg[1] == '-') {
1096  ++arg;
1097  }
1098 
1099  if (strcmp(arg, "-version") == 0) {
1100  version();
1101  exit(1);
1102  } else if (strcmp(arg, "-debug") == 0) {
1103  g_debug = 1;
1104  } else if (strcmp(arg, "-nowarn") == 0) {
1105  g_warn = 0;
1106  } else if (strcmp(arg, "-strict") == 0) {
1107  g_strict = 255;
1108  g_warn = 2;
1109  } else if (strcmp(arg, "-v") == 0 || strcmp(arg, "-verbose") == 0 ) {
1110  g_verbose = 1;
1111  } else if (strcmp(arg, "-r") == 0 || strcmp(arg, "-recurse") == 0 ) {
1112  gen_recurse = true;
1113  } else if (strcmp(arg, "-allow-neg-keys") == 0) {
1114  g_allow_neg_field_keys = true;
1115  } else if (strcmp(arg, "-allow-64bit-consts") == 0) {
1116  g_allow_64bit_consts = true;
1117  } else if (strcmp(arg, "-gen") == 0) {
1118  arg = argv[++i];
1119  if (arg == NULL) {
1120  fprintf(stderr, "!!! Missing generator specification\n");
1121  usage();
1122  }
1123  generator_strings.push_back(arg);
1124  } else if (strcmp(arg, "-dense") == 0) {
1125  gen_dense = true;
1126  } else if (strcmp(arg, "-cpp") == 0) {
1127  gen_cpp = true;
1128  } else if (strcmp(arg, "-javabean") == 0) {
1129  gen_javabean = true;
1130  } else if (strcmp(arg, "-java") == 0) {
1131  gen_java = true;
1132  } else if (strcmp(arg, "-php") == 0) {
1133  gen_php = true;
1134  } else if (strcmp(arg, "-phpi") == 0) {
1135  gen_phpi = true;
1136  } else if (strcmp(arg, "-phps") == 0) {
1137  gen_php = true;
1138  gen_phps = true;
1139  } else if (strcmp(arg, "-phpl") == 0) {
1140  gen_php = true;
1141  gen_phps = false;
1142  } else if (strcmp(arg, "-phpa") == 0) {
1143  gen_php = true;
1144  gen_phps = false;
1145  gen_phpa = true;
1146  } else if (strcmp(arg, "-phpo") == 0) {
1147  gen_php = true;
1148  gen_phpo = true;
1149  } else if (strcmp(arg, "-rest") == 0) {
1150  gen_rest = true;
1151  } else if (strcmp(arg, "-py") == 0) {
1152  gen_py = true;
1153  } else if (strcmp(arg, "-pyns") == 0) {
1154  gen_py = true;
1155  gen_py_newstyle = true;
1156  } else if (strcmp(arg, "-rb") == 0) {
1157  gen_rb = true;
1158  } else if (strcmp(arg, "-xsd") == 0) {
1159  gen_xsd = true;
1160  } else if (strcmp(arg, "-perl") == 0) {
1161  gen_perl = true;
1162  } else if (strcmp(arg, "-erl") == 0) {
1163  gen_erl = true;
1164  } else if (strcmp(arg, "-ocaml") == 0) {
1165  gen_ocaml = true;
1166  } else if (strcmp(arg, "-hs") == 0) {
1167  gen_hs = true;
1168  } else if (strcmp(arg, "-cocoa") == 0) {
1169  gen_cocoa = true;
1170  } else if (strcmp(arg, "-st") == 0) {
1171  gen_st = true;
1172  } else if (strcmp(arg, "-csharp") == 0) {
1173  gen_csharp = true;
1174  } else if (strcmp(arg, "-delphi") == 0) {
1175  gen_delphi = true;
1176  } else if (strcmp(arg, "-cpp_use_include_prefix") == 0) {
1177  g_cpp_use_include_prefix = true;
1178  } else if (strcmp(arg, "-I") == 0) {
1179  // An argument of "-I\ asdf" is invalid and has unknown results
1180  arg = argv[++i];
1181 
1182  if (arg == NULL) {
1183  fprintf(stderr, "!!! Missing Include directory\n");
1184  usage();
1185  }
1186  g_incl_searchpath.push_back(arg);
1187  } else if ((strcmp(arg, "-o") == 0) || (strcmp(arg, "-out") == 0)) {
1188  out_path_is_absolute = (strcmp(arg, "-out") == 0) ? true : false;
1189 
1190  arg = argv[++i];
1191  if (arg == NULL) {
1192  fprintf(stderr, "-o: missing output directory\n");
1193  usage();
1194  }
1195  out_path = arg;
1196 
1197  struct stat sb;
1198  if (stat(out_path.c_str(), &sb) < 0) {
1199  fprintf(stderr, "Output directory %s is unusable: %s\n", out_path.c_str(), strerror(errno));
1200  return -1;
1201  }
1202  if (! S_ISDIR(sb.st_mode)) {
1203  fprintf(stderr, "Output directory %s exists but is not a directory\n", out_path.c_str());
1204  return -1;
1205  }
1206  } else {
1207  fprintf(stderr, "!!! Unrecognized option: %s\n", arg);
1208  usage();
1209  }
1210 
1211  // Tokenize more
1212  arg = strtok_r(NULL, " ", &saveptr);
1213  }
1214  }
1215 
1216  // if you're asking for version, you have a right not to pass a file
1217  if (strcmp(argv[argc-1], "-version") == 0) {
1218  version();
1219  exit(1);
1220  }
1221 
1222  // TODO(dreiss): Delete these when everyone is using the new hotness.
1223  if (gen_cpp) {
1224  pwarning(1, "-cpp is deprecated. Use --gen cpp");
1225  string gen_string = "cpp:";
1226  if (gen_dense) {
1227  gen_string.append("dense,");
1228  }
1230  gen_string.append("include_prefix,");
1231  }
1232  generator_strings.push_back(gen_string);
1233  }
1234  if (gen_java) {
1235  pwarning(1, "-java is deprecated. Use --gen java");
1236  generator_strings.push_back("java");
1237  }
1238  if (gen_javabean) {
1239  pwarning(1, "-javabean is deprecated. Use --gen java:beans");
1240  generator_strings.push_back("java:beans");
1241  }
1242  if (gen_csharp) {
1243  pwarning(1, "-csharp is deprecated. Use --gen csharp");
1244  generator_strings.push_back("csharp");
1245  }
1246  if (gen_delphi) {
1247  pwarning(1, "-delphi is deprecated. Use --gen delphi");
1248  generator_strings.push_back("delphi");
1249  }
1250  if (gen_py) {
1251  pwarning(1, "-py is deprecated. Use --gen py");
1252  generator_strings.push_back("py");
1253  }
1254  if (gen_rb) {
1255  pwarning(1, "-rb is deprecated. Use --gen rb");
1256  generator_strings.push_back("rb");
1257  }
1258  if (gen_perl) {
1259  pwarning(1, "-perl is deprecated. Use --gen perl");
1260  generator_strings.push_back("perl");
1261  }
1262  if (gen_php || gen_phpi) {
1263  pwarning(1, "-php is deprecated. Use --gen php");
1264  string gen_string = "php:";
1265  if (gen_phpi) {
1266  gen_string.append("inlined,");
1267  } else if(gen_phps) {
1268  gen_string.append("server,");
1269  } else if(gen_phpa) {
1270  gen_string.append("autoload,");
1271  } else if(gen_phpo) {
1272  gen_string.append("oop,");
1273  } else if(gen_rest) {
1274  gen_string.append("rest,");
1275  }
1276  generator_strings.push_back(gen_string);
1277  }
1278  if (gen_cocoa) {
1279  pwarning(1, "-cocoa is deprecated. Use --gen cocoa");
1280  generator_strings.push_back("cocoa");
1281  }
1282  if (gen_erl) {
1283  pwarning(1, "-erl is deprecated. Use --gen erl");
1284  generator_strings.push_back("erl");
1285  }
1286  if (gen_st) {
1287  pwarning(1, "-st is deprecated. Use --gen st");
1288  generator_strings.push_back("st");
1289  }
1290  if (gen_ocaml) {
1291  pwarning(1, "-ocaml is deprecated. Use --gen ocaml");
1292  generator_strings.push_back("ocaml");
1293  }
1294  if (gen_hs) {
1295  pwarning(1, "-hs is deprecated. Use --gen hs");
1296  generator_strings.push_back("hs");
1297  }
1298  if (gen_xsd) {
1299  pwarning(1, "-xsd is deprecated. Use --gen xsd");
1300  generator_strings.push_back("xsd");
1301  }
1302 
1303  // You gotta generate something!
1304  if (generator_strings.empty()) {
1305  fprintf(stderr, "!!! No output language(s) specified\n\n");
1306  usage();
1307  }
1308 
1309  // Real-pathify it
1310  char rp[PATH_MAX];
1311  if (argv[i] == NULL) {
1312  fprintf(stderr, "!!! Missing file name\n");
1313  usage();
1314  }
1315  if (saferealpath(argv[i], rp) == NULL) {
1316  failure("Could not open input file with realpath: %s", argv[i]);
1317  }
1318  string input_file(rp);
1319 
1320  // Instance of the global parse tree
1321  t_program* program = new t_program(input_file);
1322  if (out_path.size()) {
1323  program->set_out_path(out_path, out_path_is_absolute);
1324  }
1325 
1326  // Compute the cpp include prefix.
1327  // infer this from the filename passed in
1328  string input_filename = argv[i];
1329  string include_prefix;
1330 
1331  string::size_type last_slash = string::npos;
1332  if ((last_slash = input_filename.rfind("/")) != string::npos) {
1333  include_prefix = input_filename.substr(0, last_slash);
1334  }
1335 
1336  program->set_include_prefix(include_prefix);
1337 
1338  // Initialize global types
1342  ((t_base_type*)g_type_binary)->set_binary(true);
1344  ((t_base_type*)g_type_slist)->set_string_list(true);
1351 #ifdef SANDESH
1352  g_type_u16 = new t_base_type("u16", t_base_type::TYPE_U16);
1353  g_type_u32 = new t_base_type("u32", t_base_type::TYPE_U32);
1354  g_type_u64 = new t_base_type("u64", t_base_type::TYPE_U64);
1355  g_type_ipv4 = new t_base_type("ipv4", t_base_type::TYPE_IPV4);
1356  g_type_ipaddr = new t_base_type("ipaddr", t_base_type::TYPE_IPADDR);
1357  g_type_xml = new t_base_type("xml", t_base_type::TYPE_XML);
1358  g_type_uuid_t = new t_base_type("ct_uuid_t", t_base_type::TYPE_UUID);
1359  g_type_static_const_string = new t_base_type("static const string", t_base_type::TYPE_STATIC_CONST_STRING);
1360  g_type_sandesh_system = new t_base_type("system", t_base_type::TYPE_SANDESH_SYSTEM);
1361  g_type_sandesh_request = new t_base_type("request", t_base_type::TYPE_SANDESH_REQUEST);
1362  g_type_sandesh_response = new t_base_type("response", t_base_type::TYPE_SANDESH_RESPONSE);
1363  g_type_sandesh_trace = new t_base_type("trace", t_base_type::TYPE_SANDESH_TRACE);
1364  g_type_sandesh_trace_object = new t_base_type("traceobject", t_base_type::TYPE_SANDESH_TRACE_OBJECT);
1365  g_type_sandesh_buffer = new t_base_type("buffer", t_base_type::TYPE_SANDESH_BUFFER);
1366  g_type_sandesh_uve = new t_base_type("uve", t_base_type::TYPE_SANDESH_UVE);
1367  g_type_sandesh_dynamic_uve = new t_base_type("dynamicuve", t_base_type::TYPE_SANDESH_DYNAMIC_UVE);
1368  g_type_sandesh_alarm = new t_base_type("alarm", t_base_type::TYPE_SANDESH_ALARM);
1369  g_type_sandesh_object = new t_base_type("object", t_base_type::TYPE_SANDESH_OBJECT);
1370  g_type_sandesh_flow = new t_base_type("flow", t_base_type::TYPE_SANDESH_FLOW);
1371  g_type_sandesh_session = new t_base_type("session", t_base_type::TYPE_SANDESH_SESSION);
1372 #endif
1373 
1374  // Parse it!
1375  parse(program, NULL);
1376 
1377  // The current path is not really relevant when we are doing generation.
1378  // Reset the variable to make warning messages clearer.
1379  g_curpath = "generation";
1380  // Reset yylineno for the heck of it. Use 1 instead of 0 because
1381  // That is what shows up during argument parsing.
1382  yylineno = 1;
1383  // Generate it!
1384  generate(program, generator_strings);
1385 
1386  // Clean up. Who am I kidding... this program probably orphans heap memory
1387  // all over the place, but who cares because it is about to exit and it is
1388  // all referenced and used by this wacky parse tree up until now anyways.
1389 
1390  delete program;
1391  delete g_type_void;
1392  delete g_type_string;
1393  delete g_type_bool;
1394  delete g_type_byte;
1395  delete g_type_i16;
1396  delete g_type_i32;
1397  delete g_type_i64;
1398  delete g_type_double;
1399 #ifdef SANDESH
1400  delete g_type_u16;
1401  delete g_type_u32;
1402  delete g_type_u64;
1403  delete g_type_ipv4;
1404  delete g_type_ipaddr;
1405  delete g_type_static_const_string;
1406  delete g_type_sandesh_system;
1407  delete g_type_sandesh_request;
1408  delete g_type_sandesh_response;
1409  delete g_type_sandesh_trace;
1410  delete g_type_sandesh_trace_object;
1411  delete g_type_sandesh_buffer;
1412  delete g_type_sandesh_uve;
1413  delete g_type_sandesh_dynamic_uve;
1414  delete g_type_sandesh_alarm;
1415  delete g_type_sandesh_object;
1416  delete g_type_sandesh_flow;
1417  delete g_type_sandesh_session;
1418  delete g_type_xml;
1419  delete g_type_uuid_t;
1420 
1421 #endif
1422 
1423  // Finished
1424  return 0;
1425 }
void validate_field_value(t_field *field, t_const_value *cv)
char * saferealpath(const char *path, char *resolved_path)
virtual bool is_xception() const
Definition: t_type.h:74
const std::string & get_name() const
Definition: t_field.h:95
char yytext[]
void dump_docstrings(t_program *program)
void pverbose(const char *fmt,...)
virtual void generate_program()
Definition: t_generator.cc:30
std::string get_identifier_name() const
Definition: t_enum.h:30
bool has_doc()
Definition: t_doc.h:41
void generate_all_fingerprints(t_program *program)
void failure(const char *fmt,...)
const std::string & get_path() const
Definition: t_program.h:83
Definition: t_type.h:48
void pdebug(const char *fmt,...)
const std::vector< t_program * > & get_includes() const
Definition: t_program.h:126
string program_name(string filename)
PARSE_MODE
Definition: globals.h:54
vector< string > g_incl_searchpath
static std::string t_base_name(t_base tbase)
Definition: t_base_type.h:218
const std::vector< t_typedef * > & get_typedefs() const
Definition: t_program.h:101
virtual bool is_map() const
Definition: t_type.h:78
const std::string & get_name() const
Definition: t_program.h:92
int yylineno
void yyerror(const char *fmt,...)
PROGDOCTEXT_STATUS
Definition: globals.h:146
virtual bool is_enum() const
Definition: t_type.h:72
t_type * get_type() const
Definition: t_field.h:91
virtual bool is_base_type() const
Definition: t_type.h:61
void parse(t_program *program, t_program *parent_program)
const std::map< t_const_value *, t_const_value * > & get_map() const
const members_type & get_members()
void pwarning(int level, const char *fmt,...)
virtual void generate_fingerprint()
t_const_value_type get_type() const
void set_include_prefix(std::string include_prefix)
Definition: t_program.h:169
t_type * get_type() const
Definition: t_const.h:40
string include_file(string filename)
const std::vector< t_const * > & get_consts() const
Definition: t_program.h:103
uint8_t type
Definition: load_balance.h:109
bool is_out_path_absolute()
Definition: t_program.h:89
t_scope * scope()
Definition: t_program.h:144
char * clean_up_doctext(char *doctext)
PROGDOCTEXT_STATUS g_program_doctext_status
virtual bool is_set() const
Definition: t_type.h:77
const std::vector< t_enum * > & get_enums() const
Definition: t_program.h:102
const std::vector< t_struct * > & get_xceptions() const
Definition: t_program.h:105
std::string get_name() const
Definition: t_const.h:44
void set_out_path(std::string out_path, bool out_path_is_absolute)
Definition: t_program.h:128
virtual bool is_void() const
Definition: t_type.h:60
virtual void generate_fingerprint()
Definition: parse.cc:13
t_const_value * get_value() const
Definition: t_const.h:48
std::map< std::string, t_generator_factory * > gen_map_t
static gen_map_t & get_generator_map()
Definition: t_generator.cc:174
void validate_const_rec(std::string name, t_type *type, t_const_value *value)
FILE * yyin
std::string get_identifier() const
virtual const std::string & get_name() const
Definition: t_type.h:56
Definition: t_map.h:30
int yyparse(void)
virtual bool is_list() const
Definition: t_type.h:76
bool validate_throws(t_struct *throws)
string directory_name(string filename)
const std::string & get_out_path() const
Definition: t_program.h:86
static t_type * get_true_type(t_type *type)
Definition: t_generator.h:268
virtual bool is_struct() const
Definition: t_type.h:73
Definition: t_set.h:29
const std::string & get_doc() const
Definition: t_doc.h:37
const std::vector< t_struct * > & get_structs() const
Definition: t_program.h:104
Definition: t_list.h:29
static t_generator * get_generator(t_program *program, const std::string &options)
Definition: t_generator.cc:137
void validate_const_type(t_const *c)
const std::vector< t_service * > & get_services() const
Definition: t_program.h:107
#define SANDESH_VERSION
Definition: version.h:1
void generate(t_program *program, const vector< string > &generator_strings)
const std::vector< t_const_value * > & get_list() const