1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 #include <cmajor/cmcm/Compiler.hpp>
  7 #include <cmajor/Build/Build.hpp>
  8 #include <cmajor/cmres/InitDone.hpp>
  9 #include <sngcm/ast/InitDone.hpp>
 10 #include <soulng/lexer/ParsingException.hpp>
 11 #include <soulng/util/InitDone.hpp>
 12 #include <soulng/util/Path.hpp>
 13 #include <soulng/util/Unicode.hpp>
 14 #include <soulng/util/Log.hpp>
 15 #include <soulng/util/System.hpp>
 16 #include <cmajor/symbols/GlobalFlags.hpp>
 17 #include <sngxml/dom/Parser.hpp>
 18 #include <sngxml/dom/CharacterData.hpp>
 19 #include <sngxml/xpath/InitDone.hpp>
 20 #include <cmajor/symbols/InitDone.hpp>
 21 #include <cmajor/symbols/Exception.hpp>
 22 #include <cmajor/symbols/ModuleCache.hpp>
 23 #include <boost/lexical_cast.hpp>
 24 #include <string>
 25 #include <iostream>
 26 #include <sstream>
 27 #include <memory>
 28 #include <vector>
 29 #include <mutex>
 30 #include <stdexcept>
 31 
 32 bool initialized = false;
 33 
 34 using namespace soulng::unicode;
 35 
 36 class CompileData 
 37 {
 38 public:
 39     static void Init();
 40     static void Done();
 41     static CompileData& Instance() { return *instance; }
 42     int SetCompileResult(const std::u16string& compileResult);
 43     int GetCompileResultLength(int compileResultHandle);
 44     int GetCompileResult(int compileResultHandlechar16_t* bufint length);
 45 private:
 46     static std::unique_ptr<CompileData> instance;
 47     std::mutex mtx;
 48     std::vector<std::std::unique_ptr<std::u16string>>compileResults;
 49 };
 50 
 51 std::unique_ptr<CompileData> CompileData::instance;
 52 
 53 void CompileData::Init()
 54 {
 55     instance.reset(new CompileData());
 56 }
 57 
 58 void CompileData::Done()
 59 {
 60     instance.reset();
 61 }
 62 
 63 int CompileData::SetCompileResult(const std::u16string& compileResult)
 64 {
 65     std::lock_guard<std::mutex> lock(mtx);
 66     int compileResultHandle = compileResults.size();
 67     compileResults.push_back(std::unique_ptr<std::u16string>(new std::u16string(compileResult)));
 68     return compileResultHandle;
 69 }
 70 
 71 int CompileData::GetCompileResultLength(int compileResultHandle)
 72 {
 73     std::lock_guard<std::mutex> lock(mtx);
 74     if (compileResultHandle < 0 || compileResultHandle >= compileResults.size()) return -1;
 75     if (!compileResults[compileResultHandle]) return -1;
 76     const std::u16string& compileResult = *compileResults[compileResultHandle];
 77     return compileResult.length();
 78 }
 79 
 80 int CompileData::GetCompileResult(int compileResultHandlechar16_t* bufint size)
 81 {
 82     std::lock_guard<std::mutex> lock(mtx);
 83     if (compileResultHandle < 0 || compileResultHandle >= compileResults.size()) return -1;
 84     if (!compileResults[compileResultHandle]) return -1;
 85     const std::u16string& compileResult = *compileResults[compileResultHandle];
 86     int n = compileResult.length();
 87     if (size <= n) return -1;
 88     for (int i = 0; i < n; ++i)
 89     {
 90         buf[i] = compileResult[i];
 91     }
 92     for (int i = n; i < size; ++i)
 93     {
 94         buf[i] = u'\0';
 95     }
 96     compileResults[compileResultHandle].reset();
 97     return 0;
 98 }
 99 
100 extern "C" void Init()
101 {
102     soulng::util::Init();
103     sngcm::ast::Init();
104     cmajor::symbols::Init();
105     CompileData::Init();
106     soulng::util::SetLogMode(soulng::util::LogMode::queue); 
107     soulng::util::DisableConsoleWindow();
108     sngxml::xpath::Init();
109     cmajor::resources::Init();
110     initialized = true;
111 }
112 
113 extern "C" void Done()
114 {
115     initialized = false;
116     cmajor::resources::Done();
117     sngxml::xpath::Done();
118     CompileData::Done();
119     cmajor::symbols::Done();
120     sngcm::ast::Done();
121     soulng::util::Done();
122 }
123 
124 std::std::unique_ptr<sngxml::dom::Element>SpanElement(cmajor::symbols::Module*moduleconstsoulng::lexer::Span&span)
125 {
126     std::unique_ptr<sngxml::dom::Element> spanElement(new sngxml::dom::Element(U"span"));
127     if (span.Valid() && module)
128     {
129         std::string fileName = module->GetFilePath(span.fileIndex);
130         if (fileName.empty()) return std::unique_ptr<sngxml::dom::Element>();
131         spanElement->SetAttribute(U"file"ToUtf32(fileName));
132         spanElement->SetAttribute(U"line"ToUtf32(std::to_string(span.line)));
133         std::u32string text = module->GetErrorLines(span);
134         int32_t startCol = 0;
135         int32_t endCol = 0;
136         module->GetColumns(spanstartColendCol);
137         spanElement->SetAttribute(U"startCol"ToUtf32(std::to_string(startCol)));
138         spanElement->SetAttribute(U"endCol"ToUtf32(std::to_string(endCol)));
139         spanElement->SetAttribute(U"text"text);
140     }
141     else
142     {
143         spanElement.reset();
144     }
145     return spanElement;
146 }
147 
148 void AddWarningsTo(sngxml::dom::Element* diagnosticsElementcmajor::symbols::Module* module)
149 {
150     if (!module) return;
151     if (!module->WarningCollection().Warnings().empty())
152     {
153         for (const cmajor::symbols::Warning& warning : module->WarningCollection().Warnings())
154         {
155             std::unique_ptr<sngxml::dom::Element> diagnosticElement(new sngxml::dom::Element(U"diagnostic"));
156             diagnosticElement->SetAttribute(U"category"U"warning");
157             diagnosticElement->SetAttribute(U"message"ToUtf32(warning.Message()));
158             diagnosticElement->SetAttribute(U"project"warning.Project());
159             std::unique_ptr<sngxml::dom::Element> spanElement = SpanElement(modulewarning.Defined());
160             if (spanElement)
161             {
162                 diagnosticElement->AppendChild(std::unique_ptr<sngxml::dom::Node>(spanElement.release()));
163             }
164             diagnosticsElement->AppendChild(std::unique_ptr<sngxml::dom::Node>(diagnosticElement.release()));
165             for (const std::std::pair<soulng::lexer::Spanboost::uuids::uuid>&spanModuleId : warning.References())
166             {
167                 if (!spanModuleId.first.Valid()) continue;
168                 std::unique_ptr<sngxml::dom::Element> diagnosticElement(new sngxml::dom::Element(U"diagnostic"));
169                 diagnosticElement->SetAttribute(U"category"U"info");
170                 diagnosticElement->SetAttribute(U"message"ToUtf32("see reference to"));
171                 cmajor::symbols::Module* mod = cmajor::symbols::GetModuleById(spanModuleId.second);
172                 std::unique_ptr<sngxml::dom::Element> spanElement = SpanElement(modspanModuleId.first);
173                 if (spanElement)
174                 {
175                     diagnosticElement->AppendChild(std::unique_ptr<sngxml::dom::Node>(spanElement.release()));
176                     diagnosticsElement->AppendChild(std::unique_ptr<sngxml::dom::Node>(diagnosticElement.release()));
177                 }
178             }
179         }
180     }
181 }
182 
183 const char* version = "3.10.0";
184 
185 extern "C" int Compile(const char16_t* compileXmlRequest)
186 {
187     if (!initialized) return -1;
188     std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
189     cmajor::symbols::ResetGlobalFlags();
190     cmajor::symbols::SetCompilerVersion(version);
191     cmajor::symbols::Module* module = nullptr;
192     bool noDebugInfo = false;
193     std::unique_ptr<cmajor::symbols::Module> rootModule;
194     std::vector<std::unique_ptr<cmajor::symbols::Module>> rootModules;
195     std::set<std::string> builtProjects;
196     sngxml::dom::Document compileResultDoc;
197     std::unique_ptr<sngxml::dom::Element> compileResultElement(new sngxml::dom::Element(U"compileResult"));
198     std::unique_ptr<sngxml::dom::Element> diagnosticsElement(new sngxml::dom::Element(U"diagnostics"));
199     try
200     {
201         std::u32string compileRequest = ToUtf32(compileXmlRequest);
202         std::unique_ptr<sngxml::dom::Document> compileRequestDoc = sngxml::dom::ParseDocument(compileRequest, "compileRequest");
203         sngxml::dom::Element* compileRequestElement = compileRequestDoc->DocumentElement();
204         std::string filePath = ToUtf8(compileRequestElement->GetAttribute(U"filePath"));
205         std::string config = ToUtf8(compileRequestElement->GetAttribute(U"config"));
206         if (config == "release")
207         {
208             cmajor::symbols::SetGlobalFlag(cmajor::symbols::GlobalFlags::release);
209         }
210         bool verbose = compileRequestElement->GetAttribute(U"verbose") == U"true";
211         if (verbose)
212         {
213             cmajor::symbols::SetGlobalFlag(cmajor::symbols::GlobalFlags::verbose);
214         }
215         bool clean = compileRequestElement->GetAttribute(U"clean") == U"true";
216         if (clean)
217         {
218             cmajor::symbols::SetGlobalFlag(cmajor::symbols::GlobalFlags::clean);
219         }
220         bool rebuild = compileRequestElement->GetAttribute(U"rebuild") == U"true";
221         if (rebuild)
222         {
223             cmajor::symbols::SetGlobalFlag(cmajor::symbols::GlobalFlags::rebuild);
224         }
225         bool time = compileRequestElement->GetAttribute(U"time") == U"true";
226         bool strictNothrow = compileRequestElement->GetAttribute(U"strict-nothrow") == U"true";
227         if (strictNothrow)
228         {
229             cmajor::symbols::SetGlobalFlag(cmajor::symbols::GlobalFlags::strictNothrow);
230         }
231         bool emitLlvm = compileRequestElement->GetAttribute(U"emit-llvm") == U"true";
232         if (emitLlvm)
233         {
234             cmajor::symbols::SetGlobalFlag(cmajor::symbols::GlobalFlags::emitLlvm);
235         }
236         bool emitOptLlvm = compileRequestElement->GetAttribute(U"emit-opt-llvm") == U"true";
237         if (emitOptLlvm)
238         {
239             cmajor::symbols::SetGlobalFlag(cmajor::symbols::GlobalFlags::emitOptLlvm);
240         }
241         bool linkWithDebugRuntime = compileRequestElement->GetAttribute(U"link-with-debug-runtime") == U"true";
242         if (linkWithDebugRuntime)
243         {
244             cmajor::symbols::SetGlobalFlag(cmajor::symbols::GlobalFlags::linkWithDebugRuntime);
245         }
246         bool linkUsingMsLink = compileRequestElement->GetAttribute(U"link-using-ms-link") == U"true";
247         if (linkUsingMsLink)
248         {
249             cmajor::symbols::SetGlobalFlag(cmajor::symbols::GlobalFlags::linkUsingMsLink);
250         }
251         bool singleThreadedCompile = compileRequestElement->GetAttribute(U"single-threaded-compile") == U"true";
252         if (singleThreadedCompile)
253         {
254             cmajor::symbols::SetGlobalFlag(cmajor::symbols::GlobalFlags::singleThreadedCompile); 
255         }
256         std::string optimizationLevelStr = ToUtf8(compileRequestElement->GetAttribute(U"optimization-level"));
257         if (!optimizationLevelStr.empty())
258         {
259             int optimizationLevel = boost::lexical_cast<int>(optimizationLevelStr);
260             cmajor::symbols::SetOptimizationLevel(optimizationLevel);
261         }
262         std::string buildThreads = ToUtf8(compileRequestElement->GetAttribute(U"build-threads"));
263         if (!buildThreads.empty())
264         {
265             int numBuildThreads = boost::lexical_cast<int>(buildThreads);
266             cmajor::symbols::SetNumBuildThreads(numBuildThreads);
267         }
268         if (cmajor::symbols::GetGlobalFlag(cmajor::symbols::GlobalFlags::verbose))
269         {
270             LogMessage(-1, "Cmajor compiler with LLVM backend version " + std::string(version) + " for Windows x64");
271         }
272         if (!cmajor::symbols::GetGlobalFlag(cmajor::symbols::GlobalFlags::release) && !noDebugInfo)
273         {
274             cmajor::symbols::SetGlobalFlag(cmajor::symbols::GlobalFlags::generateDebugInfo);
275         }
276         if (Path::GetExtension(filePath) == ".cms")
277         {
278             cmajor::build::BuildSolution(GetFullPath(filePath), rootModules);
279         }
280         else if (Path::GetExtension(filePath) == ".cmp")
281         {
282             module = rootModule.get();
283             cmajor::build::BuildProject(GetFullPath(filePath), rootModule, builtProjects);
284         }
285         else
286         {
287             throw std::runtime_error("invalid 'filePath' parameter: not .cms or .cmp");
288         }
289         compileResultElement->SetAttribute(U"success", U"true");
290         if (time)
291         {
292             std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
293             auto dur = end - start;
294             long long totalSecs = std::chrono::duration_cast<std::chrono::seconds>(dur).count() + 1;
295             int hours = static_cast<int>(totalSecs / 3600);
296             int mins = static_cast<int>((totalSecs / 60) % 60);
297             int secs = static_cast<int>(totalSecs % 60);
298             std::stringstream timeStream;
299             timeStream <<
300                 (hours > 0 ? std::to_string(hours) + " hour" + ((hours != 1) ? "s " : " ") : "") <<
301                 (mins > 0 ? std::to_string(mins) + " minute" + ((mins != 1) ? "s " : " ") : "") <<
302                 secs << " second" << ((secs != 1) ? "s" : "");
303             LogMessage(-1, timeStream.str());
304         }
305     }
306     catch (const soulng::lexer::ParsingException& ex)
307     {
308         LogMessage(-1, ex.what());
309         module = static_cast<cmajor::symbols::Module*>(ex.Module());
310         compileResultElement->SetAttribute(U"success", U"false");
311         std::unique_ptr<sngxml::dom::Element> diagnosticElement(new sngxml::dom::Element(U"diagnostic"));
312         diagnosticElement->SetAttribute(U"category", U"error");
313         diagnosticElement->SetAttribute(U"message", ToUtf32(ex.Message()));
314         diagnosticElement->SetAttribute(U"tool", U"cmc");
315         diagnosticElement->SetAttribute(U"project", ToUtf32(ex.Project()));
316         std::unique_ptr<sngxml::dom::Element> spanElement = SpanElement(module, ex.GetSpan()); 
317         if (spanElement)
318         {
319             diagnosticElement->AppendChild(std::unique_ptr<sngxml::dom::Node>(spanElement.release()));
320         }
321         diagnosticsElement->AppendChild(std::unique_ptr<sngxml::dom::Node>(diagnosticElement.release()));
322     }
323     catch (const cmajor::symbols::Exception& ex)
324     {
325         LogMessage(-1, ex.What());
326         cmajor::symbols::Module* module = cmajor::symbols::GetModuleById(ex.DefinedModuleId());
327         compileResultElement->SetAttribute(U"success", U"false");
328         std::unique_ptr<sngxml::dom::Element> diagnosticElement(new sngxml::dom::Element(U"diagnostic"));
329         diagnosticElement->SetAttribute(U"category", U"error");
330         diagnosticElement->SetAttribute(U"message", ToUtf32(ex.Message()));
331         diagnosticElement->SetAttribute(U"tool", module->GetCurrentToolName());
332         diagnosticElement->SetAttribute(U"project", module->GetCurrentProjectName());
333         std::unique_ptr<sngxml::dom::Element> spanElement = SpanElement(module, ex.Defined());
334         if (spanElement)
335         {
336             diagnosticElement->AppendChild(std::unique_ptr<sngxml::dom::Node>(spanElement.release()));
337         }
338         diagnosticsElement->AppendChild(std::unique_ptr<sngxml::dom::Node>(diagnosticElement.release()));
339         for (const std::pair<soulng::lexer::Span, boost::uuids::uuid>& spanModuleId : ex.References())
340         {
341             if (!spanModuleId.first.Valid()) continue;
342             std::unique_ptr<sngxml::dom::Element> diagnosticElement(new sngxml::dom::Element(U"diagnostic"));
343             diagnosticElement->SetAttribute(U"category", U"info");
344             diagnosticElement->SetAttribute(U"message", ToUtf32("see reference to"));
345             cmajor::symbols::Module* mod = cmajor::symbols::GetModuleById(spanModuleId.second);
346             std::unique_ptr<sngxml::dom::Element> spanElement = SpanElement(mod, spanModuleId.first);
347             if (spanElement)
348             {
349                 diagnosticElement->AppendChild(std::unique_ptr<sngxml::dom::Node>(spanElement.release()));
350                 diagnosticsElement->AppendChild(std::unique_ptr<sngxml::dom::Node>(diagnosticElement.release()));
351             }
352         }
353     }
354     catch (const std::exception& ex)
355     {
356         LogMessage(-1, ex.what());
357         compileResultElement->SetAttribute(U"success", U"false");
358         std::unique_ptr<sngxml::dom::Element> diagnosticElement(new sngxml::dom::Element(U"diagnostic"));
359         diagnosticElement->SetAttribute(U"category", U"error");
360         diagnosticElement->SetAttribute(U"message", ToUtf32(ex.what()));
361         diagnosticsElement->AppendChild(std::unique_ptr<sngxml::dom::Node>(diagnosticElement.release()));
362     }
363     if (!module)
364     {
365         if (rootModule)
366         {
367             module = rootModule.get();
368         }
369         else
370         {
371             for (const auto& m : rootModules)
372             {
373                 if (m)
374                 {
375                     module = m.get();
376                 }
377             }
378         }
379     }
380     AddWarningsTo(diagnosticsElement.get(), module);
381     compileResultElement->AppendChild(std::unique_ptr<sngxml::dom::Node>(diagnosticsElement.release()));
382     compileResultDoc.AppendChild(std::unique_ptr<sngxml::dom::Node>(compileResultElement.release()));
383     std::ostringstream strStream;
384     CodeFormatter formatter(strStream);
385     formatter.SetIndentSize(1);
386     compileResultDoc.Write(formatter);
387     return CompileData::Instance().SetCompileResult(ToUtf16(strStream.str()));
388 }
389 
390 extern "C" int GetCompileResultLength(int compileResultHandle)
391 {
392     if (!initialized) return -1;
393     return CompileData::Instance().GetCompileResultLength(compileResultHandle);
394 }
395 
396 extern "C" int GetCompileResult(int compileResultHandle, char16_t* buf, int size)
397 {
398     if (!initialized) return -1;
399     return CompileData::Instance().GetCompileResult(compileResultHandle, buf, size);
400 }
401 
402 extern "C" void StopBuild()
403 {
404     cmajor::build::StopBuild();
405 }
406 
407 extern "C" int WaitForLogMessage()
408 {
409     if (!initialized) return -1;
410     return soulng::util::WaitForLogMessage();
411 }
412 
413 extern "C" int FetchLogMessage(char16_t* buf, int size)
414 {
415     if (!initialized) return -1;
416     return soulng::util::FetchLogMessage(buf, size);
417 }
418 
419 extern "C" void StartLog()
420 {
421     soulng::util::StartLog();
422 }
423 
424 extern "C" void EndLog()
425 {
426     soulng::util::EndLog();
427 }
428 
429 extern "C" void ResetModuleCache()
430 {
431     cmajor::symbols::ResetModuleCache();
432 }
433 
434 extern "C" void SetUseModuleCache(bool useModuleCache_)
435 {
436     cmajor::symbols::SetUseModuleCache(useModuleCache_);
437 }