1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 #include <IceUtil/Options.h>
6 #include <IceUtil/CtrlCHandler.h>
7 #include <IceUtil/Mutex.h>
8 #include <IceUtil/MutexPtrLock.h>
9 #include <IceUtil/ConsoleUtil.h>
10 
11 #include <Slice/Preprocessor.h>
12 #include <Slice/FileTracker.h>
13 #include <Slice/Util.h>
14 #include "Gen.h"
15 
16 using namespace std;
17 using namespace Slice;
18 using namespace IceUtilInternal;
19 
20 namespace
21 {
22 
23 IceUtil::Mutex* globalMutex = 0;
24 bool interrupted = false;
25 
26 class Init
27 {
28 public:
29 
Init()30     Init()
31     {
32         globalMutex = new IceUtil::Mutex;
33     }
34 
~Init()35     ~Init()
36     {
37         delete globalMutex;
38         globalMutex = 0;
39     }
40 };
41 
42 Init init;
43 
44 }
45 
46 void
interruptedCallback(int)47 interruptedCallback(int /*signal*/)
48 {
49     IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(globalMutex);
50 
51     interrupted = true;
52 }
53 
54 void
usage(const string & n)55 usage(const string& n)
56 {
57     consoleErr << "Usage: " << n << " [options] slice-files...\n";
58     consoleErr <<
59         "Options:\n"
60         "-h, --help               Show this message.\n"
61         "-v, --version            Display the Ice version.\n"
62         "-DNAME                   Define NAME as 1.\n"
63         "-DNAME=DEF               Define NAME as DEF.\n"
64         "-UNAME                   Remove any definition for NAME.\n"
65         "-IDIR                    Put DIR in the include file search path.\n"
66         "-E                       Print preprocessor output on stdout.\n"
67         "--output-dir DIR         Create files in the directory DIR.\n"
68         "-d, --debug              Print debug messages.\n"
69         "--depend                 Generate Makefile dependencies.\n"
70         "--depend-xml             Generate dependencies in XML format.\n"
71         "--depend-file FILE       Write dependencies to FILE instead of standard output.\n"
72         "--validate               Validate command line options.\n"
73         "--header-ext EXT         Use EXT instead of the default `h' extension.\n"
74         "--source-ext EXT         Use EXT instead of the default `cpp' extension.\n"
75         "--add-header HDR[,GUARD] Add #include for HDR (with guard GUARD) to generated source file.\n"
76         "--include-dir DIR        Use DIR as the header include directory in source files.\n"
77         "--impl-c++11             Generate sample implementations for C++11 mapping.\n"
78         "--impl-c++98             Generate sample implementations for C++98 mapping.\n"
79         "--checksum               Generate checksums for Slice definitions.\n"
80         "--dll-export SYMBOL      Use SYMBOL for DLL exports\n"
81         "                         deprecated: use instead [[\"cpp:dll-export:SYMBOL\"]] metadata.\n"
82         "--ice                    Allow reserved Ice prefix in Slice identifiers\n"
83         "                         deprecated: use instead [[\"ice-prefix\"]] metadata.\n"
84         "--underscore             Allow underscores in Slice identifiers\n"
85         "                         deprecated: use instead [[\"underscore\"]] metadata.\n"
86         ;
87 }
88 
89 int
compile(const vector<string> & argv)90 compile(const vector<string>& argv)
91 {
92     IceUtilInternal::Options opts;
93     opts.addOpt("h", "help");
94     opts.addOpt("v", "version");
95     opts.addOpt("", "validate");
96     opts.addOpt("", "header-ext", IceUtilInternal::Options::NeedArg, "h");
97     opts.addOpt("", "source-ext", IceUtilInternal::Options::NeedArg, "cpp");
98     opts.addOpt("", "add-header", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat);
99     opts.addOpt("D", "", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat);
100     opts.addOpt("U", "", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat);
101     opts.addOpt("I", "", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat);
102     opts.addOpt("E");
103     opts.addOpt("", "include-dir", IceUtilInternal::Options::NeedArg);
104     opts.addOpt("", "output-dir", IceUtilInternal::Options::NeedArg);
105     opts.addOpt("", "dll-export", IceUtilInternal::Options::NeedArg);
106     opts.addOpt("", "impl-c++98");
107     opts.addOpt("", "impl-c++11");
108     opts.addOpt("", "depend");
109     opts.addOpt("", "depend-xml");
110     opts.addOpt("", "depend-file", IceUtilInternal::Options::NeedArg, "");
111     opts.addOpt("d", "debug");
112     opts.addOpt("", "ice");
113     opts.addOpt("", "underscore");
114     opts.addOpt("", "checksum");
115 
116     bool validate = find(argv.begin(), argv.end(), "--validate") != argv.end();
117     vector<string> args;
118     try
119     {
120         args = opts.parse(argv);
121     }
122     catch(const IceUtilInternal::BadOptException& e)
123     {
124         consoleErr << argv[0] << ": " << e.reason << endl;
125         if(!validate)
126         {
127             usage(argv[0]);
128         }
129         return EXIT_FAILURE;
130     }
131 
132     if(opts.isSet("help"))
133     {
134         usage(argv[0]);
135         return EXIT_SUCCESS;
136     }
137 
138     if(opts.isSet("version"))
139     {
140         consoleErr << ICE_STRING_VERSION << endl;
141         return EXIT_SUCCESS;
142     }
143 
144     string headerExtension = opts.optArg("header-ext");
145     string sourceExtension = opts.optArg("source-ext");
146 
147     vector<string> extraHeaders = opts.argVec("add-header");
148 
149     vector<string> cppArgs;
150     vector<string> optargs = opts.argVec("D");
151     for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i)
152     {
153         cppArgs.push_back("-D" + *i);
154     }
155 
156     optargs = opts.argVec("U");
157     for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i)
158     {
159         cppArgs.push_back("-U" + *i);
160     }
161 
162     vector<string> includePaths;
163     includePaths = opts.argVec("I");
164     for(vector<string>::const_iterator i = includePaths.begin(); i != includePaths.end(); ++i)
165     {
166         cppArgs.push_back("-I" + Preprocessor::normalizeIncludePath(*i));
167     }
168 
169     bool preprocess = opts.isSet("E");
170 
171     string include = opts.optArg("include-dir");
172 
173     string output = opts.optArg("output-dir");
174 
175     string dllExport = opts.optArg("dll-export");
176 
177     bool implCpp98 = opts.isSet("impl-c++98");
178 
179     bool implCpp11 = opts.isSet("impl-c++11");
180 
181     bool depend = opts.isSet("depend");
182 
183     bool dependxml = opts.isSet("depend-xml");
184 
185     string dependFile = opts.optArg("depend-file");
186 
187     bool debug = opts.isSet("debug");
188 
189     bool ice = opts.isSet("ice");
190 
191     bool underscore = opts.isSet("underscore");
192 
193     bool checksum = opts.isSet("checksum");
194 
195     if(args.empty())
196     {
197         consoleErr << argv[0] << ": error: no input file" << endl;
198         if(!validate)
199         {
200             usage(argv[0]);
201         }
202         return EXIT_FAILURE;
203     }
204 
205     if(depend && dependxml)
206     {
207         consoleErr << argv[0] << ": error: cannot specify both --depend and --depend-xml" << endl;
208         if(!validate)
209         {
210             usage(argv[0]);
211         }
212         return EXIT_FAILURE;
213     }
214 
215     if(implCpp98 && implCpp11)
216     {
217         consoleErr << argv[0] << ": error: cannot specify both --impl-c++98 and --impl-c++11" << endl;
218         if(!validate)
219         {
220             usage(argv[0]);
221         }
222         return EXIT_FAILURE;
223     }
224 
225     if(validate)
226     {
227         return EXIT_SUCCESS;
228     }
229 
230     int status = EXIT_SUCCESS;
231 
232     IceUtil::CtrlCHandler ctrlCHandler;
233     ctrlCHandler.setCallback(interruptedCallback);
234 
235     ostringstream os;
236     if(dependxml)
237     {
238         os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<dependencies>" << endl;
239     }
240 
241     for(vector<string>::const_iterator i = args.begin(); i != args.end(); ++i)
242     {
243         //
244         // Ignore duplicates.
245         //
246         vector<string>::iterator p = find(args.begin(), args.end(), *i);
247         if(p != i)
248         {
249             continue;
250         }
251 
252         if(depend || dependxml)
253         {
254             PreprocessorPtr icecpp = Preprocessor::create(argv[0], *i, cppArgs);
255             FILE* cppHandle = icecpp->preprocess(false, "-D__SLICE2CPP__");
256 
257             if(cppHandle == 0)
258             {
259                 return EXIT_FAILURE;
260             }
261 
262             UnitPtr u = Unit::createUnit(false, false, ice, underscore);
263             int parseStatus = u->parse(*i, cppHandle, debug);
264 
265             string ext = headerExtension;
266             static const string headerExtPrefix = "cpp:header-ext:";
267             DefinitionContextPtr dc = u->findDefinitionContext(u->topLevelFile());
268             assert(dc);
269             string meta = dc->findMetaData(headerExtPrefix);
270             if(meta.size() > headerExtPrefix.size())
271             {
272                 ext = meta.substr(headerExtPrefix.size());
273             }
274 
275             u->destroy();
276 
277             if(parseStatus == EXIT_FAILURE)
278             {
279                 return EXIT_FAILURE;
280             }
281 
282             if(!icecpp->printMakefileDependencies(os, depend ? Preprocessor::CPlusPlus : Preprocessor::SliceXML,
283                                                   includePaths, "-D__SLICE2CPP__", sourceExtension, ext))
284             {
285                 return EXIT_FAILURE;
286             }
287 
288             if(!icecpp->close())
289             {
290                 return EXIT_FAILURE;
291             }
292         }
293         else
294         {
295             PreprocessorPtr icecpp = Preprocessor::create(argv[0], *i, cppArgs);
296             FILE* cppHandle = icecpp->preprocess(true, "-D__SLICE2CPP__");
297 
298             if(cppHandle == 0)
299             {
300                 return EXIT_FAILURE;
301             }
302 
303             if(preprocess)
304             {
305                 char buf[4096];
306                 while(fgets(buf, static_cast<int>(sizeof(buf)), cppHandle) != ICE_NULLPTR)
307                 {
308                     if(fputs(buf, stdout) == EOF)
309                     {
310                         return EXIT_FAILURE;
311                     }
312                 }
313                 if(!icecpp->close())
314                 {
315                     return EXIT_FAILURE;
316                 }
317             }
318             else
319             {
320                 UnitPtr u = Unit::createUnit(false, false, ice, underscore);
321                 int parseStatus = u->parse(*i, cppHandle, debug);
322 
323                 if(!icecpp->close())
324                 {
325                     u->destroy();
326                     return EXIT_FAILURE;
327                 }
328 
329                 if(parseStatus == EXIT_FAILURE)
330                 {
331                     status = EXIT_FAILURE;
332                 }
333                 else
334                 {
335                     try
336                     {
337                         Gen gen(icecpp->getBaseName(), headerExtension, sourceExtension, extraHeaders, include,
338                                 includePaths, dllExport, output, implCpp98, implCpp11, checksum, ice);
339                         gen.generate(u);
340                     }
341                     catch(const Slice::FileException& ex)
342                     {
343                         // If a file could not be created, then
344                         // cleanup any created files.
345                         FileTracker::instance()->cleanup();
346                         u->destroy();
347                         consoleErr << argv[0] << ": error: " << ex.reason() << endl;
348                         return EXIT_FAILURE;
349                     }
350                 }
351 
352                 u->destroy();
353             }
354         }
355 
356         {
357             IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(globalMutex);
358 
359             if(interrupted)
360             {
361                 FileTracker::instance()->cleanup();
362                 return EXIT_FAILURE;
363             }
364         }
365     }
366 
367     if(dependxml)
368     {
369         os << "</dependencies>\n";
370     }
371 
372     if(depend || dependxml)
373     {
374         writeDependencies(os.str(), dependFile);
375     }
376 
377     return status;
378 }
379 
380 #ifdef _WIN32
wmain(int argc,wchar_t * argv[])381 int wmain(int argc, wchar_t* argv[])
382 #else
383 int main(int argc, char* argv[])
384 #endif
385 {
386     vector<string> args = Slice::argvToArgs(argc, argv);
387     try
388     {
389         return compile(args);
390     }
391     catch(const std::exception& ex)
392     {
393         consoleErr << args[0] << ": error:" << ex.what() << endl;
394         return EXIT_FAILURE;
395     }
396     catch(...)
397     {
398         consoleErr << args[0] << ": error:" << "unknown exception" << endl;
399         return EXIT_FAILURE;
400     }
401 }
402