BootApp.cpp 22 KB

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