1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 #include <sngcm/ast/InitDone.hpp>
  7 #include <soulng/util/InitDone.hpp>
  8 #include <cmajor/build/Build.hpp>
  9 #include <cmajor/cmmid/InitDone.hpp>
 10 #include <cmajor/symbols/Exception.hpp>
 11 #include <cmajor/symbols/InitDone.hpp>
 12 #include <cmajor/symbols/GlobalFlags.hpp>
 13 #include <cmajor/symbols/ModuleCache.hpp>
 14 #include <cmajor/symbols/Warning.hpp>
 15 #include <sngxml/dom/Document.hpp>
 16 #include <sngxml/dom/Element.hpp>
 17 #include <sngxml/dom/CharacterData.hpp>
 18 #include <soulng/lexer/ParsingException.hpp>
 19 #include <soulng/util/Util.hpp>
 20 #include <soulng/util/Path.hpp>
 21 #include <soulng/util/Json.hpp>
 22 #include <soulng/util/Unicode.hpp>
 23 #include <boost/filesystem.hpp>
 24 #include <boost/lexical_cast.hpp>
 25 #include <iostream>
 26 #include <string>
 27 #include <stdexcept>
 28 #include <chrono>
 29 
 30 struct InitDone 
 31 {
 32     InitDone()
 33     {
 34         soulng::util::Init();
 35         sngcm::ast::Init();
 36         cmajor::symbols::Init();
 37     }
 38     ~InitDone()
 39     {
 40         cmajor::symbols::Done();
 41         sngcm::ast::Done();
 42         soulng::util::Done();
 43     }
 44 };
 45 
 46 struct BackendInit 
 47 {
 48     BackendInit()
 49     {
 50         CmmInit(cmajor::mid::BackEndKind::cmsxBackEnd);
 51     }
 52     ~BackendInit()
 53     {
 54         CmmDone();
 55     }
 56 };
 57 
 58 const char* version = "0.2.0";
 59 
 60 void PrintHelp()
 61 {
 62 #ifdef _WIN32
 63 
 64 #else
 65     std::cout << "Cmajor System X compiler version " << version << std::endl;
 66 #endif
 67     std::cout << "Usage: sxcmc [options] { project.cmp | solution.cms }" << std::endl;
 68     std::cout << "Compiles given Cmajor solutions and projects to System X libraries or executables." << std::endl;
 69     std::cout << "Options:\n" << 
 70         "--help (-h)\n" << 
 71         "   print this help message\n" << 
 72         "--config=CONFIG (-c=CONFIG)\n" << 
 73         "   set configuration to CONFIG (debug | release)\n" << 
 74         "   default is debug\n" << 
 75         "--optimization-level=LEVEL (-O=LEVEL)\n" << 
 76         "   set optimization level to LEVEL=0-3\n" << 
 77         "   defaults: debug=0, release=3\n" << 
 78         "--verbose (-v)\n" << 
 79         "   print verbose messages\n" << 
 80         "--quiet (-q)\n" << 
 81         "   print no messages\n" << 
 82         "--strict-nothrow (-s)\n" << 
 83         "   treat nothrow violation as an error\n" << 
 84         "--time (-t)\n" << 
 85         "   print duration of compilation\n" << 
 86         "--outdir=OUTDIR (-o=OUTDIR)\n" << 
 87         "   set output directory root to OUTDIR\n" << 
 88         "--rebuild (-u)\n" << 
 89         "   build although sources not changed\n" << 
 90         "--clean (-e)\n" << 
 91         "   clean given solutions and projects\n" << 
 92         "--debug-parse (-p)\n" << 
 93         "   debug parsing to stdout\n" << 
 94         "--define SYMBOL (-D SYMBOL)\n" << 
 95         "   define a conditional compilation symbol SYMBOL.\n" << 
 96         "--gen-debug-info (-g)\n" << 
 97         "   generate debug info (on by default in debug configuration)\n" << 
 98         "--no-debug-info (-n)\n" << 
 99         "   don't generate debug info even for debug build\n" << 
100         "--build-threads=N (-bt=N)\n" << 
101         "   set number of build threads to N\n" << 
102         "--disable-module-cache (-dm)\n" << 
103         "   do not cache recently built modules\n" << 
104         "--single-threaded-compile (-st)\n" << 
105         "   compile source files in a project using a single thread\n" << 
106         "--debug-compile (-dc)\n" << 
107         "   show debug messages from multithreaded compilation\n" << 
108         std::endl;
109 }
110 
111 using namespace soulng::util;
112 using namespace soulng::unicode;
113 using namespace cmajor::symbols;
114 using namespace cmajor::build;
115 
116 int main(int argcconst char** argv)
117 {
118     SetBackEnd(cmajor::symbols::BackEnd::cmsx);
119     //SetNumBuildThreads(1);
120     //SetGlobalFlag(GlobalFlags::singleThreadedCompile);
121     std::set<std::string> builtProjects;
122     std::unique_ptr<Module> rootModule;
123     std::vector<std::std::unique_ptr<Module>>rootModules;
124     try
125     {
126         std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
127         InitDone initDone;
128         std::string projectName;
129         std::string projectDirectory;
130         std::string target = "program";
131         std::vector<std::string> files;
132         std::vector<std::string> sourceFiles;
133         std::vector<std::string> referenceFiles;
134         if (argc < 2)
135         {
136             PrintHelp();
137         }
138         else
139         {
140             SetCompilerVersion(version);
141             bool prevWasDefine = false;
142             bool noDebugInfo = false;
143             bool useModuleCache = true;
144             for (int i = 1; i < argc; ++i)
145             {
146                 std::string arg = argv[i];
147                 if (!arg.empty() && arg[0] == '-')
148                 {
149                     if (arg == "--help" || arg == "-h")
150                     {
151                         PrintHelp();
152                         return 0;
153                     }
154                     else if (arg == "--verbose" || arg == "-v")
155                     {
156                         SetGlobalFlag(GlobalFlags::verbose);
157                     }
158                     else if (arg == "--quiet" || arg == "-q")
159                     {
160                         SetGlobalFlag(GlobalFlags::quiet);
161                     }
162                     else if (arg == "--clean" || arg == "-e")
163                     {
164                         SetGlobalFlag(GlobalFlags::clean);
165                     }
166                     else if (arg == "--debug-parse" || arg == "-p")
167                     {
168                         SetGlobalFlag(GlobalFlags::debugParsing);
169                     }
170                     else if (arg == "--strict-nothrow" || arg == "-s")
171                     {
172                         SetGlobalFlag(GlobalFlags::strictNothrow);
173                     }
174                     else if (arg == "--time" || arg == "-t")
175                     {
176                         SetGlobalFlag(GlobalFlags::time);
177                     }
178                     else if (arg == "--rebuild" || arg == "-u")
179                     {
180                         SetGlobalFlag(GlobalFlags::rebuild);
181                     }
182                     else if (arg == "--define" || arg == "-D")
183                     {
184                         prevWasDefine = true;
185                     }
186                     else if (arg == "--gen-debug-info" || arg == "-g")
187                     {
188                         SetGlobalFlag(GlobalFlags::generateDebugInfo);
189                     }
190                     else if (arg == "--no-debug-info" || arg == "-n")
191                     {
192                         noDebugInfo = true;
193                     }
194                     else if (arg == "--disable-module-cache" || arg == "-dm")
195                     {
196                         useModuleCache = false;
197                     }
198                     else if (arg == "--single-threaded-compile" || arg == "-st")
199                     {
200                         SetGlobalFlag(GlobalFlags::singleThreadedCompile);
201                     }
202                     else if (arg == "--debug-compile" || arg == "-dc")
203                     {
204                         SetGlobalFlag(GlobalFlags::debugCompile);
205                     }
206                     else if (arg.find('=') != std::string::npos)
207                     {
208                         std::vector<std::string> components = Split(arg'=');
209                         if (components.size() == 2)
210                         {
211                             if (components[0] == "--config" || components[0] == "-c")
212                             {
213                                 if (components[1] == "release")
214                                 {
215                                     SetGlobalFlag(GlobalFlags::release);
216                                 }
217                                 else if (components[1] != "debug")
218                                 {
219                                     throw std::runtime_error("unknown configuration '" + components[1] + "'");
220                                 }
221                             }
222                             else if (components[0] == "--optimization-level" || components[0] == "-O")
223                             {
224                                 int optimizationLevel = boost::lexical_cast<int>(components[1]);
225                                 if (optimizationLevel >= 0 && optimizationLevel <= 3)
226                                 {
227                                     SetOptimizationLevel(optimizationLevel);
228                                 }
229                                 else
230                                 {
231                                     throw std::runtime_error("unknown optimization level '" + components[1] + "'");
232                                 }
233                             }
234                             else if (components[0] == "--reference" || components[0] == "-r")
235                             {
236                                 std::string file = components[1];
237                                 boost::filesystem::path fp(file);
238                                 if (!boost::filesystem::exists(fp))
239                                 {
240                                     throw std::runtime_error("referenced project file '" + fp.generic_string() + "' not found");
241                                 }
242                                 referenceFiles.push_back(file);
243                             }
244                             else if (components[0] == "--target" || components[0] == "-a")
245                             {
246                                 target = components[1];
247                                 if (target != "program" && target != "library" && target != "unitTest")
248                                 {
249                                     throw std::runtime_error("unknown target '" + target + "': not 'program', 'library', or 'unitTest'");
250                                 }
251                             }
252                             else if (components[0] == "--name" || components[0] == "-N")
253                             {
254                                 projectName = components[1];
255                             }
256                             else if (components[0] == "--dir" || components[0] == "-pd")
257                             {
258                                 projectDirectory = components[1];
259                             }
260                             else if (components[0] == "--build-threads" || components[0] == "-bt")
261                             {
262                                 int numBuildThreads = boost::lexical_cast<int>(components[1]);
263                                 SetNumBuildThreads(numBuildThreads);
264                             }
265                             else if (components[0] == "--outdir" || components[0] == "-o")
266                             {
267                                 std::string outdir = components[1];
268                                 SetOutDir(outdir);
269                             }
270                             else
271                             {
272                                 throw std::runtime_error("unknown option '" + arg + "'");
273                             }
274                         }
275                         else
276                         {
277                             throw std::runtime_error("invalid argument '" + arg + "'");
278                         }
279                     }
280                     else
281                     {
282                         throw std::runtime_error("unknown option '" + arg + "'");
283                     }
284                 }
285                 else if (prevWasDefine)
286                 {
287                     prevWasDefine = false;
288                     DefineCommandLineConditionalSymbol(ToUtf32(arg));
289                 }
290                 else
291                 {
292                     files.push_back(arg);
293                 }
294             }
295             if (files.empty())
296             {
297                 PrintHelp();
298                 return 0;
299             }
300             if (GetGlobalFlag(GlobalFlags::verbose))
301             {
302 #ifdef _WIN32
303 
304 #else
305                 std::cout << "Cmajor System X compiler version " << version << std::endl;
306 #endif
307             }
308 #ifndef _WIN32
309             noDebugInfo = true;
310 #endif
311             SetUseModuleCache(useModuleCache);
312             BackendInit backend;
313             if (!GetGlobalFlag(GlobalFlags::release) && !noDebugInfo)
314             {
315                 SetGlobalFlag(GlobalFlags::generateDebugInfo);
316             }
317 #ifndef _WIN32
318             SetNumBuildThreads(1);
319             SetGlobalFlag(GlobalFlags::singleThreadedCompile);
320 #endif
321             for (const std::string& file : files)
322             {
323                 boost::filesystem::path fp(file);
324                 if (fp.extension() == ".cms")
325                 {
326                     if (GetGlobalFlag(GlobalFlags::msbuild))
327                     {
328                         throw std::runtime_error("solution file '" + fp.generic_string() + "'  cannot be given in --msbuild mode");
329                     }
330                     else if (!boost::filesystem::exists(fp))
331                     {
332                         throw std::runtime_error("solution file '" + fp.generic_string() + "' not found");
333                     }
334                     else
335                     {
336                         BuildSolution(GetFullPath(fp.generic_string())rootModules);
337                     }
338                 }
339                 else if (fp.extension() == ".cmp")
340                 {
341                     if (GetGlobalFlag(GlobalFlags::msbuild))
342                     {
343                         throw std::runtime_error("project file '" + fp.generic_string() + "'  cannot be given in --msbuild mode");
344                     }
345                     else if (!boost::filesystem::exists(fp))
346                     {
347                         throw std::runtime_error("project file '" + fp.generic_string() + "' not found");
348                     }
349                     else
350                     {
351                         BuildProject(GetFullPath(fp.generic_string())rootModulebuiltProjects);
352                     }
353                 }
354                 else if (fp.extension() == ".cm")
355                 {
356                     if (GetGlobalFlag(GlobalFlags::msbuild))
357                     {
358                         boost::filesystem::path f(projectDirectory);
359                         f /= fp;
360                         if (!boost::filesystem::exists(f))
361                         {
362                             throw std::runtime_error("source file '" + f.generic_string() + "' not found");
363                         }
364                         else
365                         {
366                             sourceFiles.push_back(GetFullPath(f.generic_string()));
367                         }
368                     }
369                     else
370                     {
371                         throw std::runtime_error("single .cm source file '" + fp.generic_string() + "' cannot be given if not in --msbuild mode");
372                     }
373                 }
374                 else
375                 {
376                     if (GetGlobalFlag(GlobalFlags::msbuild))
377                     {
378                         throw std::runtime_error("Argument '" + fp.generic_string() + "' has invalid extension. Not Cmajor source (.cm) file.");
379                     }
380                     else
381                     {
382                         throw std::runtime_error("Argument '" + fp.generic_string() + "' has invalid extension. Not Cmajor solution (.cms) or project (.cmp) file.");
383                     }
384                 }
385             }
386             if (rootModule && !rootModule->WarningCollection().Warnings().empty())
387             {
388                 if (!GetGlobalFlag(GlobalFlags::quiet) && !GetGlobalFlag(GlobalFlags::ide) && !GetGlobalFlag(GlobalFlags::msbuild))
389                 {
390                 }
391                 if (GetGlobalFlag(GlobalFlags::ide))
392                 {
393                     std::unique_ptr<JsonObject> compileResult(new JsonObject());
394                     compileResult->AddField(U"success"std::unique_ptr<JsonValue>(new JsonBool(true)));
395                     std::cerr << compileResult->ToString() << std::endl;
396                 }
397             }
398             else if (GetGlobalFlag(GlobalFlags::ide))
399             {
400                 std::unique_ptr<JsonObject> compileResult(new JsonObject());
401                 compileResult->AddField(U"success"std::unique_ptr<JsonValue>(new JsonBool(true)));
402                 std::cerr << compileResult->ToString() << std::endl;
403             }
404             if (GetGlobalFlag(GlobalFlags::time))
405             {
406                 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
407                 auto dur = end - start;
408                 long long totalSecs = std::chrono::duration_cast<std::chrono::seconds>(dur).count() + 1;
409                 int hours = static_cast<int>(totalSecs / 3600);
410                 int mins = static_cast<int>((totalSecs / 60) % 60);
411                 int secs = static_cast<int>(totalSecs % 60);
412                 std::cout << 
413                     (hours > 0 ? std::to_string(hours) + " hour" + ((hours != 1) ? "s " : " ") : "") << 
414                     (mins > 0 ? std::to_string(mins) + " minute" + ((mins != 1) ? "s " : " ") : "") << 
415                     secs << " second" << ((secs != 1) ? "s" : "") << std::endl;
416             }
417         }
418     }
419     catch (const soulng::lexer::ParsingException& ex;)
420     {
421         if (!GetGlobalFlag(GlobalFlags::quiet))
422         {
423             std::cerr << ex.what() << std::endl;
424         }
425         return 1;
426     }
427     catch (const Exception& ex;)
428     {
429         if (!GetGlobalFlag(GlobalFlags::quiet))
430         {
431             std::cerr << ex.What() << std::endl;
432         }
433         return 1;
434     }
435     catch (const std::exception& ex;)
436     {
437         if (!GetGlobalFlag(GlobalFlags::quiet))
438         {
439             std::cerr << ex.what() << std::endl;
440         }
441         return 1;
442     }
443     return 0;
444 }