root/trunk/whisperlib/net/rpc/parser/main.cc

Revision 7, 15.4 kB (checked in by whispercastorg, 2 years ago)

version 0.2.0

Line 
1 // Copyright (c) 2009, Whispersoft s.r.l.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Whispersoft s.r.l. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Cosmin Tudorache
31
32 #include <string.h>
33
34 #include <fstream>
35 #include <map>
36
37 #include "net/rpc/parser/rpc-ptypes.h"
38 #include "net/rpc/parser/rpc-ptypes-check.h"
39 #include "net/rpc/parser/rpc-parser.h"
40 #include "net/rpc/parser/export/rpc-exporter.h"
41 #include "net/rpc/parser/export/C++/rpc-exporter-cc.h"
42 #include "net/rpc/parser/export/Javascript/rpc-exporter-js.h"
43 #include "net/rpc/parser/export/Python/rpc-exporter-python.h"
44 #include "net/rpc/parser/export/Php/rpc-exporter-php.h"
45
46 #include "common/base/types.h"
47 #include "common/base/scoped_ptr.h"
48 #include "common/base/log.h"
49 #include "common/base/gflags.h"
50 #include "common/base/common.h"
51 #include "common/base/system.h"
52
53 //////////////////////////////////////////////////////////////////////
54
55 DEFINE_string(cc,
56               "",
57               "Parameters for the C++ exporter "
58               "(use --helpparams for details)");
59 DEFINE_string(js,
60               "",
61               "Parameters for the Javascript exported "
62               "(use --helpparams for details)");
63 DEFINE_string(php,
64               "",
65               "Parameters for the PHP exporter "
66               "(use --helpparams for details)");
67 DEFINE_string(py,
68               "",
69               "Parameters for the Python exporter  "
70               "(use --helpparams for details)");
71 DEFINE_bool(verbose,
72             false,
73             "We are verbose in our error messages");
74 DEFINE_bool(help_params,
75             false,
76             "Details the possible params for exporters");
77
78 //////////////////////////////////////////////////////////////////////
79
80 typedef map<string, string> MapStringString;
81 typedef vector<string> ArrayString;
82
83 bool CliInsertNonDuplicate(
84   const char* name,
85   const char* value,
86   MapStringString& out) {
87   CHECK_NOT_NULL(name);
88
89   // insert empty string instead of NULL
90   //
91   if ( value == NULL ) {
92     value = "";
93   }
94
95   // try to insert into map
96   //
97   pair<MapStringString::iterator, bool> result =
98     out.insert(MapStringString::value_type(name, value));
99
100   // check if we already have this parameter in map
101   //
102   if ( result.second == false ) {
103     // key already exists and was not replaced
104     //
105     const string& prev_value = result.first->second;
106     LOG_ERROR << "ERROR: duplicate parameter " << name << endl
107               << " first value: \"" << prev_value << "\"" << endl
108               << " second value: \"" << value << "\"";
109     return false;
110   }
111
112   return true;
113 }
114
115 //
116 //  Parse command line arguments
117 // input:
118 //   argc: number of command line arguments. As received by main(..).
119 //   argv: arguments vector. As received by main(..).
120 //   inputFiles: fill with input file names
121 //   flags: fill with simple parameters. e.g. "-v"
122 //   params: fill with complex parameters. e.g. "--c out=abc.h"
123 // returns:
124 //  success value.
125 //
126 bool CliParseCommandLine(ArrayString& flags,
127                          MapStringString& params) {
128   if ( !FLAGS_cc.empty() ) {
129     CliInsertNonDuplicate("cc", FLAGS_cc.c_str(), params);
130   }
131   if ( !FLAGS_js.empty() ) {
132     CliInsertNonDuplicate("js", FLAGS_js.c_str(), params);
133   }
134   if ( !FLAGS_php.empty() ) {
135     CliInsertNonDuplicate("php", FLAGS_php.c_str(), params);
136   }
137   if ( !FLAGS_py.empty() ) {
138     CliInsertNonDuplicate("py", FLAGS_py.c_str(), params);
139   }
140   return true;
141 }
142
143 bool CliParseExportParams(const char* szExporterParams,
144                           MapStringString& out) {
145   string strExporterParams(szExporterParams);
146   strutil::StrTrimChars(strExporterParams, " '\"");
147
148   char* const szTmp = strdup(strExporterParams.c_str());
149
150   const char* kDelim = ",;";
151   char* pch = strtok(szTmp, kDelim);
152   while ( pch != NULL ) {
153     char* pname = pch;
154     char* pvalue = strchr(pch, '=');
155     if ( pvalue ) {
156       *(pvalue++) = 0;
157     }
158     const string strName(strutil::StrTrimChars(string(pname), " '\""));
159     const string strValue(pvalue
160                           ? strutil::StrTrimChars(string(pvalue), " '\"")
161                           : string());
162     if ( strValue.empty() ) {
163       const string strName(strutil::StrTrimChars(string(pname), " '\""));
164       LOG_ERROR << "Missing value for export parameter \"" << strName << "\"";
165       free(szTmp);
166       return false;
167     }
168     LOG_INFO << "Export param extracted: ["
169              << strName << ":" << strValue << "]";
170     if ( !CliInsertNonDuplicate(strName.c_str(), strValue.c_str(), out) ) {
171       LOG_ERROR << " Error inserting param !";
172       free(szTmp);
173       return false;
174     }
175     pch = strtok(NULL, kDelim);
176   }
177   free(szTmp);
178   return true;
179 }
180
181 int main(int argc, char** argv) {
182   ArrayString inputFiles;
183   for ( int i = 1; i < argc; ++i ) {
184     if ( *argv[i]  != '-' ) {
185       inputFiles.push_back(argv[i]);
186     }
187   }
188
189   common::Init(argc, argv);
190
191   LOG_INFO << " FLAG: cc[" << FLAGS_cc << "]";
192   if ( FLAGS_help_params ) {
193     // print usage and return
194     //
195     cout << "Usage: " << argv[0]
196          << " <input rpc file1> <input rpc file2>... "
197          << " --cc=<C++ params>"
198             " --js=<Javascript params>"
199             " --php<PHP params>"
200             " --py<Python params>" << endl;
201     cout << "Where: all language params are contained in 1 single "
202         "string with the following format: 'param=value, param=value, ...'. "
203         "All params are optional, unless specified otherwise. "
204         "Not specifying a certain output file will not generate "
205         "that file." << endl;
206     cout << "C++ params:" << endl;
207     {
208       const map<string, string>& paramsDesc =
209           rpc::ExporterCC::ParamsDescription();
210       for ( map<string, string>::const_iterator it = paramsDesc.begin();
211             it != paramsDesc.end(); ++it ) {
212         const string& pname = it->first;
213         const string& pdesc = it->second;
214         cout << "  " << pname << " = <filename> " << pdesc << endl;
215       }
216     }
217     cout << "Javascript params:" << endl;
218     {
219       const map<string, string>& paramsDesc =
220           rpc::ExporterJS::ParamsDescription();
221       for ( map<string, string>::const_iterator it = paramsDesc.begin();
222             it != paramsDesc.end(); ++it ) {
223         const string& pname = it->first;
224         const string& pdesc = it->second;
225         cout << "  " << pname << " = <filename> " << pdesc << endl;
226       }
227     }
228     cout << "PHP params:" << endl;
229     {
230       const map<string, string>& paramsDesc =
231           rpc::ExporterPHP::ParamsDescription();
232       for ( map<string, string>::const_iterator it = paramsDesc.begin();
233             it != paramsDesc.end(); ++it ) {
234         const string& pname = it->first;
235         const string& pdesc = it->second;
236         cout << "  " << pname << " = <filename> " << pdesc << endl;
237       }
238     }
239     cout << "Python params:" << endl;
240     {
241       const map<string, string>& paramsDesc =
242           rpc::ExporterPython::ParamsDescription();
243       for ( map<string, string>::const_iterator it = paramsDesc.begin();
244             it != paramsDesc.end(); ++it ) {
245         const string& pname = it->first;
246         const string& pdesc = it->second;
247         cout << "  " << pname << " = <filename> " << pdesc << endl;
248       }
249     }
250     return 0;
251   }
252
253   ArrayString flags;
254   MapStringString cmdParams;
255
256   CHECK(CliParseCommandLine(flags, cmdParams));
257
258   // make absolute paths for all inputFiles
259   //
260   // [COSMIN] But for log sake, leave them relative
261   // for ( ArrayString::iterator it = inputFiles.begin();
262   //      it != inputFiles.end(); ++it) {
263   //   string& filename = *it;
264   //   filename = StringUtils::MakeAbsolutePath(filename.c_str());
265   // }
266
267   // analyze command line parameters. Ignore exporters for now.
268   //
269   for ( MapStringString::iterator it = cmdParams.begin();
270         it != cmdParams.end(); ++it ) {
271     const string& name  = it->first;
272     const string& value = it->second;
273     CHECK(!value.empty());
274
275     LOG_INFO << "CLI Parameter: " << name << " = " << value;
276
277     // ignore exporters in this stage
278     //
279     if ( name == "cc" ) { continue; }
280     if ( name == "js" ) { continue; }
281     if ( name == "php" ) { continue; }
282     if ( name == "py" ) { continue; }
283
284     LOG_ERROR << "Unrecognized parameter: " << name;
285     return -1;
286   }
287
288   if ( inputFiles.empty() ) {
289     LOG_ERROR << "No input files specified.";
290     return -1;
291   }
292
293   // create the lists of customTypes and services.
294   // These will be populated by the parser.
295   //
296   PCustomTypeArray customTypes;
297   PServiceArray services;
298   PVerbatimArray verbatim;
299   for ( ArrayString::iterator it = inputFiles.begin();
300         it != inputFiles.end(); ++it ) {
301     const char* inputFile = it->c_str();
302     LOG_INFO << "Parsing file: " << inputFile;
303
304     // open the input file as istream
305     //
306     ifstream file(inputFile);
307     if ( !file.is_open() ) {
308       LOG_ERROR << "Failed to open file: [" << inputFile << "]";
309       return -1;
310     }
311
312     // parse input file and fill in those lists.
313     // TODO(cosmin): The input file may import additional files,
314     //               the parser should handle those too.
315     //
316     rpc::Parser lexer(file, cout, customTypes, services, verbatim, inputFile,
317                       FLAGS_verbose);
318     CHECK(lexer.Run()) << " Parser error in file " << inputFile;
319   }
320
321   // LOG the customTypes& services found
322   //
323   LOG_INFO << "Number of CustomTypes found: " << customTypes.size();
324   for ( PCustomTypeArray::const_iterator it = customTypes.begin();
325         it != customTypes.end(); ++it ) {
326     const PCustomType& customType = *it;
327     LOG_INFO << "  - " << customType.name_.c_str() << ":";
328
329     const PCustomType::AttributeArray& attributes = customType.attributes_;
330     for ( PCustomType::AttributeArray::const_iterator it = attributes.begin();
331           it != attributes.end(); ++it ) {
332       const PCustomType::Attribute& attribute = *it;
333       LOG_INFO << "    - " << (attribute.isOptional_ ? "OPTIONAL" : "REQUIRED")
334                << " " << attribute.type_ << " " << attribute.name_;
335     }
336   }
337   LOG_INFO << "Number of services found: " << services.size();
338   for ( PServiceArray::const_iterator it = services.begin();
339         it != services.end(); ++it ) {
340     const PService& service = *it;
341     LOG_INFO << "  - " << service.name_ << ": ";
342
343     const PFunctionArray& functions = service.functions_;
344     for ( PFunctionArray::const_iterator it = functions.begin();
345           it != functions.end(); ++it ) {
346       const PFunction& func = *it;
347       string s = "    - " + func.name_ + " (";
348
349       const PParamArray& args = func.input_;
350       for ( PParamArray::const_iterator it = args.begin();
351             it != args.end(); ) {
352         const PParam& arg = *it;
353         s += arg.type_.ToString() + " " + arg.name_;
354
355         ++it;
356         if ( it != args.end() ) {
357           s += ", ";
358         }
359       }
360       s += ") => " + func.output_.ToString();
361       LOG_INFO << s;
362     }
363   }
364
365   // create the list of exporters
366   //
367   typedef list<rpc::Exporter*> ExporterList;
368   ExporterList exporters;
369
370   for ( MapStringString::iterator it = cmdParams.begin();
371         it != cmdParams.end(); ++it ) {
372     const string& name  = it->first;
373     const string& value = it->second;
374
375 #define CONSIDER_EXPORTER(opName, exporterName)                         \
376     if ( name == opName ) {                                             \
377       /* parse export parameters and pass these to the exporter */      \
378       MapStringString exportParams;                                     \
379       if ( !CliParseExportParams(value.c_str(), exportParams) ) {       \
380         LOG_ERROR << " Error in CliParseExportParams";                  \
381         return -1;                                                      \
382       }                                                                 \
383       LOG_INFO << opName << " export params: "                          \
384                << strutil::ToString(exportParams);                      \
385       rpc::Exporter* exporter = new exporterName(customTypes,           \
386                                                  services,              \
387                                                  verbatim,              \
388                                                  inputFiles);           \
389       if ( !exporter->SetParams(exportParams) ) {                       \
390         LOG_ERROR << " Error in exporter->SetParams";                   \
391         return -1;                                                      \
392       }                                                                 \
393       exporters.push_back(exporter);                                    \
394       continue;                                                         \
395     }
396
397     CONSIDER_EXPORTER("cc", rpc::ExporterCC);
398     CONSIDER_EXPORTER("js", rpc::ExporterJS);
399     CONSIDER_EXPORTER("php", rpc::ExporterPHP);
400     CONSIDER_EXPORTER("py", rpc::ExporterPython);
401 #undef CONSIDER_EXPORTER
402
403     // ignore other parameters
404   }
405
406   // verify types definitions correctness
407   //
408   if ( !VerifyPTypesCorrectenss(customTypes, services,
409                                 rpc::ExporterCC::RestrictedKeywords()) ||
410        !VerifyPTypesCorrectenss(customTypes, services,
411                                 rpc::ExporterJS::RestrictedKeywords()) ||
412        !VerifyPTypesCorrectenss(customTypes, services,
413                                 rpc::ExporterPHP::RestrictedKeywords()) ||
414        !VerifyPTypesCorrectenss(customTypes, services,
415                                 rpc::ExporterPython::RestrictedKeywords()) ) {
416
417     LOG_ERROR << "Error on VerifyPTypesCorrectenss !!";
418     return -1;
419   }
420
421   if(exporters.empty()) {
422     LOG_ERROR << "No translator specified.";
423     return -1;
424   }
425
426   // call the exporters on the generated types & services
427   //
428   for ( ExporterList::iterator it = exporters.begin();
429         it != exporters.end(); ++it ) {
430     rpc::Exporter& exporter = **it;
431
432     // execute the translation of the customTypes and services into the
433     // specified language
434     //
435     bool success = exporter.Export();
436     if ( !success ) {
437       LOG_ERROR << "Failed to translate rpc customTypes and services for"
438                    " language: " << exporter.GetLanguageName();
439       return -1;
440     }
441   }
442
443   // delete exporters
444   //
445   while ( !exporters.empty() ) {
446     rpc::Exporter* exporter = exporters.front();
447     exporters.pop_front();
448     delete exporter;
449   }
450
451   return 0;
452 }
Note: See TracBrowser for help on using the browser.