BootApp.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952
  1. #pragma warning(disable:4996)
  2. //#define BFBUILD_MAIN_THREAD_COMPILE
  3. #include "BootApp.h"
  4. #include <iostream>
  5. #include "BeefySysLib/util/String.h"
  6. #include "BeefySysLib/util/FileEnumerator.h"
  7. #include "BeefySysLib/util/WorkThread.h"
  8. #include "BeefySysLib/platform/PlatformHelper.h"
  9. #include "Compiler/BfSystem.h"
  10. #ifdef BF_PLATFORM_WINDOWS
  11. #include <direct.h>
  12. #endif
  13. BF_IMPORT void BF_CALLTYPE Targets_Create();
  14. BF_IMPORT void BF_CALLTYPE Targets_Delete();
  15. BF_IMPORT void BF_CALLTYPE BfSystem_ReportMemory(void* bfSystem);
  16. BF_EXPORT void BF_CALLTYPE BfCompiler_ProgramDone();
  17. BF_IMPORT void BF_CALLTYPE Debugger_FullReportMemory();
  18. //////////////////////////////////////////////////////////////////////////
  19. BF_IMPORT void BF_CALLTYPE BfCompiler_Delete(void* bfCompiler);
  20. BF_EXPORT void BF_CALLTYPE BfCompiler_SetOptions(void* bfCompiler, void* hotProject, int hotIdx,
  21. const char* targetTriple, int toolsetType, int simdSetting, int allocStackCount, int maxWorkerThreads,
  22. Beefy::BfCompilerOptionFlags optionFlags, const char* mallocLinkName, const char* freeLinkName);
  23. BF_IMPORT void BF_CALLTYPE BfCompiler_ClearBuildCache(void* bfCompiler);
  24. BF_IMPORT bool BF_CALLTYPE BfCompiler_Compile(void* bfCompiler, void* bfPassInstance, const char* outputPath);
  25. BF_IMPORT float BF_CALLTYPE BfCompiler_GetCompletionPercentage(void* bfCompiler);
  26. BF_IMPORT const char* BF_CALLTYPE BfCompiler_GetOutputFileNames(void* bfCompiler, void* bfProject, bool* hadOutputChanges);
  27. BF_IMPORT const char* BF_CALLTYPE BfCompiler_GetUsedOutputFileNames(void* bfCompiler, void* bfProject, bool flushQueuedHotFiles, bool* hadOutputChanges);
  28. BF_IMPORT void* BF_CALLTYPE BfSystem_CreateParser(void* bfSystem, void* bfProject);
  29. BF_IMPORT void BF_CALLTYPE BfParser_SetSource(void* bfParser, const char* data, int length, const char* fileName);
  30. BF_IMPORT void BF_CALLTYPE BfParser_SetCharIdData(void* bfParser, uint8* data, int length);
  31. BF_IMPORT bool BF_CALLTYPE BfParser_Parse(void* bfParser, void* bfPassInstance, bool compatMode);
  32. BF_IMPORT bool BF_CALLTYPE BfParser_Reduce(void* bfParser, void* bfPassInstance);
  33. BF_IMPORT bool BF_CALLTYPE BfParser_BuildDefs(void* bfParser, void* bfPassInstance, void* resolvePassData, bool fullRefresh);
  34. //////////////////////////////////////////////////////////////////////////
  35. BF_IMPORT void* BF_CALLTYPE BfSystem_Create();
  36. BF_IMPORT void BF_CALLTYPE BfSystem_ReportMemory(void* bfSystem);
  37. BF_IMPORT void BF_CALLTYPE BfSystem_Delete(void* bfSystem);
  38. BF_IMPORT void* BF_CALLTYPE BfSystem_CreatePassInstance(void* bfSystem);
  39. BF_IMPORT void* BF_CALLTYPE BfSystem_CreateCompiler(void* bfSystem, bool isResolveOnly);
  40. BF_IMPORT void* BF_CALLTYPE BfSystem_CreateProject(void* bfSystem, const char* projectName);
  41. BF_IMPORT void BF_CALLTYPE BfParser_Delete(void* bfParser);
  42. BF_IMPORT void BF_CALLTYPE BfSystem_AddTypeOptions(void* bfSystem, const char* filter, int32 simdSetting, int32 optimizationLevel, int32 emitDebugInfo, int32 arrayBoundsCheck,
  43. int32 initLocalVariables, int32 emitDynamicCastCheck, int32 emitObjectAccessCheck, int32 allocStackTraceDepth);
  44. //////////////////////////////////////////////////////////////////////////
  45. BF_IMPORT void BF_CALLTYPE BfProject_SetDisabled(void* bfProject, bool disabled);
  46. BF_IMPORT void BF_CALLTYPE BfProject_SetOptions(void* bfProject, int targetType, const char* startupObject, const char* preprocessorMacros,
  47. int optLevel, int ltoType, int32 flags);
  48. BF_IMPORT void BF_CALLTYPE BfProject_ClearDependencies(void* bfProject);
  49. BF_IMPORT void BF_CALLTYPE BfProject_AddDependency(void* bfProject, void* depProject);
  50. //////////////////////////////////////////////////////////////////////////
  51. BF_IMPORT const char* BF_CALLTYPE BfPassInstance_PopOutString(void* bfPassInstance);
  52. BF_IMPORT void BF_CALLTYPE BfPassInstance_Delete(void* bfPassInstance);
  53. //////////////////////////////////////////////////////////////////////////
  54. BF_IMPORT const char* BF_CALLTYPE VSSupport_Find();
  55. //////////////////////////////////////////////////////////////////////////
  56. USING_NS_BF;
  57. BootApp* Beefy::gApp = NULL;
  58. uint32 gConsoleFGColor = 0;
  59. uint32 gConsoleBGColor = 0;
  60. static bool GetConsoleColor(uint32& fgColor, uint32& bgColor)
  61. {
  62. #ifdef _WIN32
  63. static uint32 consoleColors[16] = { 0xff000000, 0xff000080, 0xff008000, 0xff008080, 0xff800000, 0xff800080, 0xff808000, 0xffc0c0c0,
  64. 0xff808080, 0xff0000ff, 0xff00ff00, 0xff00ffff, 0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff };
  65. CONSOLE_SCREEN_BUFFER_INFO screenBuffInfo = { 0 };
  66. GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &screenBuffInfo);
  67. fgColor = consoleColors[screenBuffInfo.wAttributes & 0xF];
  68. bgColor = consoleColors[(screenBuffInfo.wAttributes >> 4) & 0xF];
  69. return true;
  70. #else
  71. fgColor = 0xFF808080;
  72. bgColor = 0xFF000000;
  73. return false;
  74. #endif
  75. }
  76. static WORD GetColorCode(uint32 color)
  77. {
  78. WORD code = 0;
  79. #ifdef _WIN32
  80. if (((color >> 0) & 0xFF) > 0x40)
  81. code |= FOREGROUND_BLUE;
  82. if (((color >> 8) & 0xFF) > 0x40)
  83. code |= FOREGROUND_GREEN;
  84. if (((color >> 16) & 0xFF) > 0x40)
  85. code |= FOREGROUND_RED;
  86. if ((((color >> 0) & 0xFF) > 0xC0) ||
  87. (((color >> 8) & 0xFF) > 0xC0) ||
  88. (((color >> 16) & 0xFF) > 0xC0))
  89. code |= FOREGROUND_INTENSITY;
  90. #endif
  91. return code;
  92. }
  93. static bool SetConsoleColor(uint32 fgColor, uint32 bgColor)
  94. {
  95. #ifdef _WIN32
  96. WORD attr = GetColorCode(fgColor) | (GetColorCode(bgColor) << 4);
  97. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), attr);
  98. SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), attr);
  99. return true;
  100. #else
  101. return false;
  102. #endif
  103. }
  104. BootApp::BootApp()
  105. {
  106. Targets_Create();
  107. mVerbosity = Verbosity_Normal;
  108. mTargetType = BfTargetType_BeefConsoleApplication;
  109. //char str[MAX_PATH];
  110. //GetModuleFileNameA(NULL, str, MAX_PATH);
  111. //mInstallDir = GetFileDir(str) + "/";
  112. //getcwd(str, MAX_PATH);
  113. //mStartupDir = str;
  114. //mStartupDir += "/";
  115. //mDoClean = false;
  116. mHadCmdLine = false;
  117. mShowedHelp = false;
  118. mHadErrors = false;
  119. mSystem = NULL;
  120. mCompiler = NULL;
  121. mProject = NULL;
  122. mCELibProject = NULL;
  123. mIsCERun = false;
  124. mAsmKind = BfAsmKind_None;
  125. mStartupObject = "Program";
  126. #ifdef BF_PLATFORM_WINDOWS
  127. mOptLevel = BfOptLevel_OgPlus;
  128. mToolset = BfToolsetType_Microsoft;
  129. #else
  130. mOptLevel = BfOptLevel_O0;
  131. mToolset = BfToolsetType_GNU;
  132. #endif
  133. mEmitIR = false;
  134. #ifdef BF_PLATFORM_WINDOWS
  135. mTargetTriple = "x86_64-pc-windows-msvc";
  136. #elif defined BF_PLATFORM_OSX
  137. mTargetTriple = "x86_64-apple-macosx10.14.0";
  138. #else
  139. mTargetTriple = "x86_64-unknown-linux-gnu";
  140. #endif
  141. GetConsoleColor(gConsoleFGColor, gConsoleBGColor);
  142. }
  143. BootApp::~BootApp()
  144. {
  145. Targets_Delete();
  146. }
  147. void BootApp::OutputLine(const String& text, OutputPri outputPri)
  148. {
  149. if (mLogFile.IsOpen())
  150. {
  151. mLogFile.WriteSNZ(text);
  152. mLogFile.WriteSNZ("\n");
  153. }
  154. if (outputPri == OutputPri_Error)
  155. mHadErrors = true;
  156. switch (outputPri)
  157. {
  158. case OutputPri_Low:
  159. if (mVerbosity < Verbosity_Detailed)
  160. return;
  161. break;
  162. case OutputPri_Normal:
  163. if (mVerbosity < Verbosity_Normal)
  164. return;
  165. break;
  166. case OutputPri_High:
  167. case OutputPri_Warning:
  168. case OutputPri_Error:
  169. if (mVerbosity < Verbosity_Minimal)
  170. return;
  171. break;
  172. default: break;
  173. }
  174. if (outputPri == OutputPri_Warning)
  175. {
  176. SetConsoleColor(0xFFFFFF00, gConsoleBGColor);
  177. std::cerr << text.c_str() << std::endl;
  178. SetConsoleColor(gConsoleFGColor, gConsoleBGColor);
  179. }
  180. else if (outputPri == OutputPri_Error)
  181. {
  182. SetConsoleColor(0xFFFF0000, gConsoleBGColor);
  183. std::cerr << text.c_str() << std::endl;
  184. SetConsoleColor(gConsoleFGColor, gConsoleBGColor);
  185. }
  186. else
  187. std::cout << text.c_str() << std::endl;
  188. }
  189. void BootApp::Fail(const String& error)
  190. {
  191. if (mLogFile.IsOpen())
  192. mLogFile.WriteSNZ("FAIL: " + error + "\n");
  193. std::cerr << "FAIL: " << error.c_str() << std::endl;
  194. mHadErrors = true;
  195. }
  196. bool BootApp::HandleCmdLine(const String &cmd, const String& param)
  197. {
  198. mHadCmdLine = true;
  199. bool wantedParam = false;
  200. if ((!cmd.StartsWith("-")) && (mIsCERun) && (mCESrc.IsEmpty()))
  201. {
  202. mCESrc = cmd;
  203. return true;
  204. }
  205. if ((cmd == "-help") || (cmd == "-h") || (cmd == "/?"))
  206. {
  207. mShowedHelp = true;
  208. std::cout << "BeefBoot - Beef bootstrapping tool" << std::endl;
  209. return false;
  210. }
  211. else if (cmd == "-src")
  212. {
  213. mRequestedSrc.Add(param);
  214. wantedParam = true;
  215. }
  216. else if (cmd == "-verbosity")
  217. {
  218. if (param == "quiet")
  219. mVerbosity = Verbosity_Quiet;
  220. else if (param == "minimal")
  221. mVerbosity = Verbosity_Minimal;
  222. else if (param == "normal")
  223. mVerbosity = Verbosity_Normal;
  224. else if (param == "detailed")
  225. mVerbosity = Verbosity_Detailed;
  226. else if (param == "diagnostic")
  227. mVerbosity = Verbosity_Diagnostic;
  228. else
  229. {
  230. Fail(StrFormat("Invalid verbosity level: '%s'", param.c_str()));
  231. return false;
  232. }
  233. wantedParam = true;
  234. }
  235. else if (cmd == "-version")
  236. {
  237. BfpSystemResult sysResult;
  238. String exePath;
  239. BFP_GETSTR_HELPER(exePath, sysResult, BfpSystem_GetExecutablePath(__STR, __STRLEN, &sysResult));
  240. std::cout << "0.0.0" << std::endl;
  241. mShowedHelp = true;
  242. return true;
  243. }
  244. else if (cmd == "-define")
  245. {
  246. if (!mDefines.IsEmpty())
  247. mDefines += "\n";
  248. mDefines += param;
  249. wantedParam = true;
  250. }
  251. else if (cmd == "-startup")
  252. {
  253. mStartupObject = param;
  254. wantedParam = true;
  255. }
  256. else if (cmd == "-out")
  257. {
  258. mTargetPath = param;
  259. wantedParam = true;
  260. }
  261. else if (cmd == "-linkparams")
  262. {
  263. mLinkParams = param;
  264. wantedParam = true;
  265. }
  266. else if (cmd == "-Og+")
  267. {
  268. mOptLevel = BfOptLevel_OgPlus;
  269. }
  270. else if (cmd == "-O0")
  271. {
  272. mOptLevel = BfOptLevel_O0;
  273. }
  274. else if (cmd == "-O1")
  275. {
  276. mOptLevel = BfOptLevel_O1;
  277. }
  278. else if (cmd == "-O2")
  279. {
  280. mOptLevel = BfOptLevel_O2;
  281. }
  282. else if (cmd == "-O3")
  283. {
  284. mOptLevel = BfOptLevel_O3;
  285. }
  286. else if (cmd == "-gnu")
  287. {
  288. mToolset = BfToolsetType_GNU;
  289. }
  290. else if (cmd == "-emitir")
  291. {
  292. mEmitIR = true;
  293. }
  294. else if (cmd == "-cedest")
  295. {
  296. mIsCERun = true;
  297. mCEDest = param;
  298. wantedParam = true;
  299. }
  300. else if (cmd == "-cesrc")
  301. {
  302. mIsCERun = true;
  303. }
  304. else if (cmd == "-emitasm")
  305. {
  306. if (param.IsEmpty())
  307. {
  308. mAsmKind = BfAsmKind_Intel;
  309. }
  310. else
  311. {
  312. if (param == "att")
  313. mAsmKind = BfAsmKind_ATT;
  314. else
  315. mAsmKind = BfAsmKind_Intel;
  316. wantedParam = true;
  317. }
  318. }
  319. else
  320. {
  321. Fail("Unknown option: " + cmd);
  322. return false;
  323. }
  324. if ((wantedParam) && (param.empty()))
  325. {
  326. Fail(StrFormat("Parameter expected for '%s'", cmd.c_str()));
  327. return false;
  328. }
  329. else if ((!wantedParam) && (!param.empty()))
  330. {
  331. Fail(StrFormat("No parameter expected for '%s'", cmd.c_str()));
  332. return false;
  333. }
  334. return true;
  335. }
  336. bool BootApp::Init()
  337. {
  338. char* cwdPtr = getcwd(NULL, 0);
  339. mWorkingDir = cwdPtr;
  340. free(cwdPtr);
  341. if ((mTargetPath.IsEmpty()) && (mCESrc.IsEmpty()))
  342. {
  343. Fail("'Out' path not specified");
  344. }
  345. if (mRequestedSrc.IsEmpty())
  346. {
  347. Fail("No source specified");
  348. }
  349. return !mHadErrors;
  350. }
  351. void BootApp::QueueFile(const StringImpl& path, void* project)
  352. {
  353. String ext;
  354. ext = GetFileExtension(path);
  355. if ((ext.Equals(".bf", StringImpl::CompareKind_OrdinalIgnoreCase)) ||
  356. (ext.Equals(".cs", StringImpl::CompareKind_OrdinalIgnoreCase)))
  357. {
  358. int len;
  359. const char* data = LoadTextData(path, &len);
  360. if (data == NULL)
  361. {
  362. Fail(StrFormat("Unable to load file '%s'", path.c_str()));
  363. return;
  364. }
  365. bool worked = true;
  366. void* bfParser = BfSystem_CreateParser(mSystem, project);
  367. BfParser_SetSource(bfParser, data, len, path.c_str());
  368. //bfParser.SetCharIdData(charIdData);
  369. worked &= BfParser_Parse(bfParser, mPassInstance, false);
  370. worked &= BfParser_Reduce(bfParser, mPassInstance);
  371. worked &= BfParser_BuildDefs(bfParser, mPassInstance, NULL, false);
  372. delete data;
  373. }
  374. }
  375. void BootApp::QueuePath(const StringImpl& path)
  376. {
  377. if (DirectoryExists(path))
  378. {
  379. for (auto& fileEntry : FileEnumerator(path, FileEnumerator::Flags_Files))
  380. {
  381. String filePath = fileEntry.GetFilePath();
  382. String fileName;
  383. fileName = GetFileName(filePath);
  384. QueueFile(filePath, (mCELibProject != NULL) ? mCELibProject : mProject);
  385. }
  386. for (auto& fileEntry : FileEnumerator(path, FileEnumerator::Flags_Directories))
  387. {
  388. String childPath = fileEntry.GetFilePath();
  389. String dirName;
  390. dirName = GetFileName(childPath);
  391. if (dirName == "build")
  392. continue;
  393. QueuePath(childPath);
  394. }
  395. }
  396. else
  397. {
  398. QueueFile(path, mProject);
  399. }
  400. }
  401. static void CompileThread(void* param)
  402. {
  403. BfpThread_SetName(NULL, "CompileThread", NULL);
  404. BootApp* app = (BootApp*)param;
  405. BfCompiler_ClearBuildCache(app->mCompiler);
  406. if (!BfCompiler_Compile(app->mCompiler, app->mPassInstance, app->mBuildDir.c_str()))
  407. app->mHadErrors = true;
  408. }
  409. void BootApp::DoCompile()
  410. {
  411. #ifdef BFBUILD_MAIN_THREAD_COMPILE
  412. mOutputDirectory = outputDirectory;
  413. CompileThread(this);
  414. #else
  415. WorkThreadFunc workThread;
  416. workThread.Start(CompileThread, this);
  417. int lastProgressTicks = 0;
  418. bool showProgress = mVerbosity >= Verbosity_Normal;
  419. int progressSize = 30;
  420. if (showProgress)
  421. {
  422. std::cout << "[";
  423. for (int i = 0; i < progressSize; i++)
  424. std::cout << " ";
  425. std::cout << "]";
  426. for (int i = 0; i < progressSize + 1; i++)
  427. std::cout << "\b";
  428. std::cout.flush();
  429. }
  430. while (true)
  431. {
  432. bool isDone = workThread.WaitForFinish(100);
  433. float pct = BfCompiler_GetCompletionPercentage(mCompiler);
  434. if (isDone)
  435. pct = 1.0;
  436. int progressTicks = (int)(pct * progressSize + 0.5f);
  437. while (progressTicks > lastProgressTicks)
  438. {
  439. if (showProgress)
  440. {
  441. std::cout << "*";
  442. std::cout.flush();
  443. }
  444. lastProgressTicks++;
  445. }
  446. if (isDone)
  447. break;
  448. }
  449. if (showProgress)
  450. std::cout << std::endl;
  451. #endif
  452. }
  453. struct OutputContext
  454. {
  455. bool mIsError;
  456. BfpFile* mFile;
  457. };
  458. static void OutputThread(void* param)
  459. {
  460. OutputContext* context = (OutputContext*)param;
  461. String queuedStr;
  462. while (true)
  463. {
  464. char data[1024];
  465. BfpFileResult result;
  466. int bytesRead = (int)BfpFile_Read(context->mFile, data, 1023, -1, &result);
  467. if ((result != BfpFileResult_Ok) && (result != BfpFileResult_PartialData))
  468. return;
  469. data[bytesRead] = 0;
  470. if (context->mIsError)
  471. {
  472. std::cerr << data;
  473. std::cerr.flush();
  474. }
  475. else
  476. {
  477. std::cout << data;
  478. std::cout.flush();
  479. }
  480. if (gApp->mLogFile.IsOpen())
  481. {
  482. // This is to ensure that error and output lines are not merged together, though they may interleave
  483. queuedStr.Append(data, bytesRead);
  484. while (true)
  485. {
  486. int crPos = (int)queuedStr.IndexOf('\n');
  487. if (crPos == -1)
  488. break;
  489. AutoCrit autoCrit(gApp->mLogCritSect);
  490. if (context->mIsError)
  491. gApp->mLogFile.WriteSNZ("err> ");
  492. else
  493. gApp->mLogFile.WriteSNZ("out> ");
  494. int endPos = crPos;
  495. if ((endPos > 0) && (queuedStr[endPos - 1] == '\r'))
  496. endPos--;
  497. gApp->mLogFile.Write((void*)queuedStr.c_str(), endPos);
  498. gApp->mLogFile.WriteSNZ("\n");
  499. queuedStr.Remove(0, crPos + 1);
  500. }
  501. }
  502. }
  503. }
  504. bool BootApp::QueueRun(const String& fileName, const String& args, const String& workingDir, BfpSpawnFlags extraFlags)
  505. {
  506. OutputLine(StrFormat("EXECUTING: %s %s", fileName.c_str(), args.c_str()), OutputPri_Low);
  507. BfpSpawnFlags spawnFlags = (BfpSpawnFlags)(BfpSpawnFlag_NoWindow | BfpSpawnFlag_RedirectStdOutput | BfpSpawnFlag_RedirectStdError | extraFlags);
  508. BfpSpawn* spawn = BfpSpawn_Create(fileName.c_str(), args.c_str(), workingDir.c_str(), NULL, spawnFlags, NULL);
  509. if (spawn == NULL)
  510. {
  511. Fail(StrFormat("Failed to execute '%s'", fileName.c_str()));
  512. return false;
  513. }
  514. int exitCode = 0;
  515. OutputContext outputContext;;
  516. outputContext.mIsError = false;
  517. OutputContext errorContext;
  518. errorContext.mIsError = false;
  519. BfpSpawn_GetStdHandles(spawn, NULL, &outputContext.mFile, &errorContext.mFile);
  520. BfpThread* outputThread = BfpThread_Create(OutputThread, (void*)&outputContext);
  521. BfpThread* errorThread = BfpThread_Create(OutputThread, (void*)&errorContext);
  522. BfpSpawn_WaitFor(spawn, -1, &exitCode, NULL);
  523. if (outputContext.mFile != NULL)
  524. BfpFile_Close(outputContext.mFile, NULL);
  525. if (errorContext.mFile != NULL)
  526. BfpFile_Close(errorContext.mFile, NULL);
  527. BfpThread_WaitFor(outputThread, -1);
  528. BfpThread_WaitFor(errorThread, -1);
  529. BfpThread_Release(outputThread);
  530. BfpThread_Release(errorThread);
  531. BfpSpawn_Release(spawn);
  532. if (exitCode != 0)
  533. {
  534. Fail(StrFormat("Exit code returned: %d", exitCode));
  535. return false;
  536. }
  537. return true;
  538. }
  539. bool BootApp::CopyFile(const StringImpl& srcPath, const StringImpl& destPath)
  540. {
  541. BfpFileResult result = BfpFileResult_Ok;
  542. for (int i = 0; i < 20; i++)
  543. {
  544. BfpFile_Copy(srcPath.c_str(), destPath.c_str(), BfpFileCopyKind_Always, &result);
  545. if (result == BfpFileResult_Ok)
  546. return true;
  547. BfpThread_Sleep(100);
  548. }
  549. Fail(StrFormat("Failed to copy '%s' to '%s'", srcPath.c_str(), destPath.c_str()));
  550. return false;
  551. }
  552. #ifdef BF_PLATFORM_WINDOWS
  553. void BootApp::DoLinkMS()
  554. {
  555. String vsStr = VSSupport_Find();
  556. int toolIdx = (int)vsStr.IndexOf("TOOL64\t");
  557. int toolCrIdx = (int)vsStr.IndexOf('\n', toolIdx + 1);
  558. if ((toolIdx == -1) || (toolCrIdx == -1))
  559. {
  560. Fail("Failed to detect Visual Studio configuration. Is Visual Studio 2015 or later installed?");
  561. return;
  562. }
  563. String linkerPath = vsStr.Substring(toolIdx + 7, toolCrIdx - toolIdx - 7);
  564. linkerPath.Append("\\link.exe");
  565. String linkLine;
  566. String targetPath = mTargetPath;
  567. bool hadOutputChanges;
  568. const char* result = BfCompiler_GetUsedOutputFileNames(mCompiler, mProject, true, &hadOutputChanges);
  569. if (result == NULL)
  570. return;
  571. std::string fileNamesStr;
  572. fileNamesStr += result;
  573. if (fileNamesStr.length() == 0)
  574. return;
  575. int curIdx = -1;
  576. while (curIdx < (int)fileNamesStr.length())
  577. {
  578. int nextBr = (int)fileNamesStr.find('\n', curIdx + 1);
  579. if (nextBr == -1)
  580. nextBr = (int)fileNamesStr.length();
  581. linkLine.Append(fileNamesStr.substr(curIdx + 1, nextBr - curIdx - 1));
  582. linkLine.Append(" ");
  583. curIdx = nextBr;
  584. }
  585. linkLine.Append("-out:");
  586. IDEUtils::AppendWithOptionalQuotes(linkLine, targetPath);
  587. linkLine.Append(" ");
  588. if (mTargetType == BfTargetType_BeefConsoleApplication)
  589. linkLine.Append("-subsystem:console ");
  590. else
  591. linkLine.Append("-subsystem:windows ");
  592. linkLine.Append("-defaultlib:libcmtd ");
  593. linkLine.Append("-nologo ");
  594. linkLine.Append("-pdb:");
  595. int lastDotPos = (int)targetPath.LastIndexOf('.');
  596. if (lastDotPos == -1)
  597. lastDotPos = (int)targetPath.length();
  598. auto pdbName = String(targetPath, 0, lastDotPos);
  599. pdbName.Append(".pdb");
  600. IDEUtils::AppendWithOptionalQuotes(linkLine, pdbName);
  601. linkLine.Append(" ");
  602. linkLine.Append("-debug ");
  603. int checkIdx = 0;
  604. while (true)
  605. {
  606. int libIdx = (int)vsStr.IndexOf("LIB64\t", checkIdx);
  607. if (libIdx == -1)
  608. break;
  609. int libCrIdx = (int)vsStr.IndexOf('\n', libIdx + 1);
  610. if (libCrIdx == -1)
  611. break;
  612. String libPath = vsStr.Substring(libIdx + 6, libCrIdx - libIdx - 6);
  613. linkLine.Append("-libpath:\"");
  614. linkLine.Append(libPath);
  615. linkLine.Append("\" ");
  616. checkIdx = libCrIdx + 1;
  617. }
  618. linkLine.Append(mLinkParams);
  619. BfpSpawnFlags flags = BfpSpawnFlag_None;
  620. flags = (BfpSpawnFlags)(BfpSpawnFlag_UseArgsFile | BfpSpawnFlag_UseArgsFile_Native | BfpSpawnFlag_UseArgsFile_BOM);
  621. auto runCmd = QueueRun(linkerPath, linkLine, mWorkingDir, flags);
  622. }
  623. #endif
  624. void BootApp::DoLinkGNU()
  625. {
  626. String linkerPath = "/usr/bin/c++";
  627. String linkLine;
  628. String targetPath = mTargetPath;
  629. bool hadOutputChanges;
  630. const char* result = BfCompiler_GetUsedOutputFileNames(mCompiler, mProject, true, &hadOutputChanges);
  631. if (result == NULL)
  632. return;
  633. std::string fileNamesStr;
  634. fileNamesStr += result;
  635. if (fileNamesStr.length() == 0)
  636. return;
  637. int curIdx = -1;
  638. while (curIdx < (int)fileNamesStr.length())
  639. {
  640. int nextBr = (int)fileNamesStr.find('\n', curIdx + 1);
  641. if (nextBr == -1)
  642. nextBr = (int)fileNamesStr.length();
  643. linkLine.Append(fileNamesStr.substr(curIdx + 1, nextBr - curIdx - 1));
  644. linkLine.Append(" ");
  645. curIdx = nextBr;
  646. }
  647. linkLine.Append("-o ");
  648. IDEUtils::AppendWithOptionalQuotes(linkLine, targetPath);
  649. linkLine.Append(" ");
  650. linkLine.Append("-g ");
  651. linkLine.Append("-debug -no-pie ");
  652. linkLine.Append(mLinkParams);
  653. auto runCmd = QueueRun(linkerPath, linkLine, mWorkingDir, BfpSpawnFlag_UseArgsFile);
  654. }
  655. bool BootApp::Compile()
  656. {
  657. DWORD startTick = BFTickCount();
  658. mSystem = BfSystem_Create();
  659. mCompiler = BfSystem_CreateCompiler(mSystem, false);
  660. String projectName = GetFileName(mTargetPath);
  661. int dotPos = (int)projectName.IndexOf('.');
  662. if (dotPos != -1)
  663. projectName.RemoveToEnd(dotPos);
  664. if (projectName.IsEmpty())
  665. projectName.Append("BeefProject");
  666. mProject = BfSystem_CreateProject(mSystem, projectName.c_str());
  667. if (mIsCERun)
  668. {
  669. mCELibProject = BfSystem_CreateProject(mSystem, "BeefLib");
  670. BfProjectFlags flags = BfProjectFlags_None;
  671. BfProject_SetOptions(mCELibProject, BfTargetType_BeefLib, "", mDefines.c_str(), mOptLevel, 0, flags);
  672. }
  673. if (!mDefines.IsEmpty())
  674. mDefines.Append("\n");
  675. mDefines.Append("BF_64_BIT");
  676. mDefines.Append("\nBF_LITTLE_ENDIAN");
  677. mDefines.Append("\n");
  678. mDefines.Append(BF_PLATFORM_NAME);
  679. int ltoType = 0;
  680. BfProjectFlags flags = BfProjectFlags_None;
  681. if (mIsCERun)
  682. {
  683. flags = (BfProjectFlags)(flags | BfProjectFlags_SingleModule | BfProjectFlags_AlwaysIncludeAll);
  684. if (mAsmKind == BfAsmKind_ATT)
  685. flags = (BfProjectFlags)(flags | BfProjectFlags_AsmOutput | BfProjectFlags_AsmOutput_ATT);
  686. else if (mAsmKind == BfAsmKind_Intel)
  687. flags = (BfProjectFlags)(flags | BfProjectFlags_AsmOutput);
  688. }
  689. BfProject_SetOptions(mProject, mTargetType, mStartupObject.c_str(), mDefines.c_str(), mOptLevel, ltoType, flags);
  690. if (mCELibProject != NULL)
  691. BfProject_AddDependency(mProject, mCELibProject);
  692. mPassInstance = BfSystem_CreatePassInstance(mSystem);
  693. Beefy::String exePath;
  694. BfpGetStrHelper(exePath, [](char* outStr, int* inOutStrSize, BfpResult* result)
  695. {
  696. BfpSystem_GetExecutablePath(outStr, inOutStrSize, (BfpSystemResult*)result);
  697. });
  698. mBuildDir = GetFileDir(exePath) + "/build";
  699. RecursiveCreateDirectory(mBuildDir + "/" + projectName);
  700. if (mIsCERun)
  701. RecursiveCreateDirectory(mBuildDir + "/BeefLib");
  702. BfCompilerOptionFlags optionFlags = (BfCompilerOptionFlags)(BfCompilerOptionFlag_EmitDebugInfo | BfCompilerOptionFlag_EmitLineInfo | BfCompilerOptionFlag_GenerateOBJ | BfCompilerOptionFlag_OmitDebugHelpers);
  703. if (mEmitIR)
  704. optionFlags = (BfCompilerOptionFlags)(optionFlags | BfCompilerOptionFlag_WriteIR);
  705. int maxWorkerThreads = BfpSystem_GetNumLogicalCPUs(NULL);
  706. if (maxWorkerThreads <= 1)
  707. maxWorkerThreads = 6;
  708. BfCompiler_SetOptions(mCompiler, NULL, 0, mTargetTriple.c_str(), mToolset, BfSIMDSetting_SSE2, 1, maxWorkerThreads, optionFlags, "malloc", "free");
  709. if (mIsCERun)
  710. {
  711. QueueFile(mCESrc, mProject);
  712. }
  713. for (auto& srcName : mRequestedSrc)
  714. {
  715. String absPath = GetAbsPath(srcName, mWorkingDir);
  716. QueuePath(absPath);
  717. }
  718. if (!mHadErrors)
  719. {
  720. DoCompile();
  721. OutputLine(StrFormat("TIMING: Beef compiling: %0.1fs", (BFTickCount() - startTick) / 1000.0), OutputPri_Normal);
  722. if (!mCEDest.IsEmpty())
  723. {
  724. String ext;
  725. String srcResult = mBuildDir + "/BeefProject/BeefProject";
  726. if (mAsmKind == BfAsmKind_None)
  727. srcResult += BF_OBJ_EXT;
  728. else
  729. srcResult += ".s";
  730. CopyFile(srcResult, mCEDest);
  731. }
  732. if ((mIsCERun) && (mEmitIR))
  733. {
  734. String ext;
  735. String srcResult = mBuildDir + "/BeefProject/BeefProject";
  736. String irDestPath = mCEDest;
  737. int dotPos = (int)irDestPath.LastIndexOf('.');
  738. if (dotPos != -1)
  739. irDestPath.RemoveToEnd(dotPos);
  740. if (mOptLevel == BfOptLevel_OgPlus)
  741. {
  742. srcResult += ".beir";
  743. irDestPath += ".ll";
  744. }
  745. else
  746. {
  747. srcResult += ".ll";
  748. irDestPath += ".ll";
  749. }
  750. CopyFile(srcResult, irDestPath);
  751. }
  752. }
  753. while (true)
  754. {
  755. const char* msg = BfPassInstance_PopOutString(mPassInstance);
  756. if (msg == NULL)
  757. break;
  758. if ((strncmp(msg, ":warn ", 6) == 0))
  759. {
  760. OutputLine(msg + 6, OutputPri_Warning);
  761. }
  762. else if ((strncmp(msg, ":error ", 7) == 0))
  763. {
  764. OutputLine(msg + 7, OutputPri_Error);
  765. }
  766. else if ((strncmp(msg, ":med ", 5) == 0))
  767. {
  768. OutputLine(msg + 5, OutputPri_Normal);
  769. }
  770. else if ((strncmp(msg, ":low ", 5) == 0))
  771. {
  772. OutputLine(msg + 5, OutputPri_Low);
  773. }
  774. else if ((strncmp(msg, "ERROR(", 6) == 0) || (strncmp(msg, "ERROR:", 6) == 0))
  775. {
  776. OutputLine(msg, OutputPri_Error);
  777. }
  778. else if ((strncmp(msg, "WARNING(", 8) == 0) || (strncmp(msg, "WARNING:", 8) == 0))
  779. {
  780. OutputLine(msg, OutputPri_Warning);
  781. }
  782. else
  783. OutputLine(msg);
  784. }
  785. if ((!mHadErrors) && (!mTargetPath.IsEmpty()))
  786. {
  787. if (mVerbosity == Verbosity_Normal)
  788. {
  789. std::cout << "Linking " << mTargetPath.c_str() << "...";
  790. std::cout.flush();
  791. }
  792. #ifdef BF_PLATFORM_WINDOWS
  793. DoLinkMS();
  794. #else
  795. DoLinkGNU();
  796. #endif
  797. if (mVerbosity == Verbosity_Normal)
  798. std::cout << std::endl;
  799. }
  800. BfPassInstance_Delete(mPassInstance);
  801. BfCompiler_Delete(mCompiler);
  802. BfSystem_Delete(mSystem);
  803. return !mHadErrors;
  804. }