mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 19:48:20 +02:00
960 lines
24 KiB
C++
960 lines
24 KiB
C++
#pragma warning(disable:4996)
|
|
|
|
//#define BFBUILD_MAIN_THREAD_COMPILE
|
|
|
|
#include "BootApp.h"
|
|
#include <iostream>
|
|
#include "BeefySysLib/util/String.h"
|
|
#include "BeefySysLib/util/FileEnumerator.h"
|
|
#include "BeefySysLib/util/WorkThread.h"
|
|
#include "BeefySysLib/platform/PlatformHelper.h"
|
|
#include "Compiler/BfSystem.h"
|
|
|
|
#ifdef BF_PLATFORM_WINDOWS
|
|
#include <direct.h>
|
|
#endif
|
|
|
|
BF_IMPORT void BF_CALLTYPE Targets_Create();
|
|
BF_IMPORT void BF_CALLTYPE Targets_Delete();
|
|
|
|
BF_IMPORT void BF_CALLTYPE BfSystem_ReportMemory(void* bfSystem);
|
|
BF_EXPORT void BF_CALLTYPE BfCompiler_ProgramDone();
|
|
|
|
BF_IMPORT void BF_CALLTYPE Debugger_FullReportMemory();
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
BF_IMPORT void BF_CALLTYPE BfCompiler_Delete(void* bfCompiler);
|
|
BF_EXPORT void BF_CALLTYPE BfCompiler_SetOptions(void* bfCompiler, void* hotProject, int hotIdx,
|
|
const char* targetTriple, int toolsetType, int simdSetting, int allocStackCount, int maxWorkerThreads,
|
|
Beefy::BfCompilerOptionFlags optionFlags, const char* mallocLinkName, const char* freeLinkName);
|
|
BF_IMPORT void BF_CALLTYPE BfCompiler_ClearBuildCache(void* bfCompiler);
|
|
BF_IMPORT bool BF_CALLTYPE BfCompiler_Compile(void* bfCompiler, void* bfPassInstance, const char* outputPath);
|
|
BF_IMPORT float BF_CALLTYPE BfCompiler_GetCompletionPercentage(void* bfCompiler);
|
|
BF_IMPORT const char* BF_CALLTYPE BfCompiler_GetOutputFileNames(void* bfCompiler, void* bfProject, bool* hadOutputChanges);
|
|
BF_IMPORT const char* BF_CALLTYPE BfCompiler_GetUsedOutputFileNames(void* bfCompiler, void* bfProject, bool flushQueuedHotFiles, bool* hadOutputChanges);
|
|
|
|
BF_IMPORT void* BF_CALLTYPE BfSystem_CreateParser(void* bfSystem, void* bfProject);
|
|
BF_IMPORT void BF_CALLTYPE BfParser_SetSource(void* bfParser, const char* data, int length, const char* fileName);
|
|
BF_IMPORT void BF_CALLTYPE BfParser_SetCharIdData(void* bfParser, uint8* data, int length);
|
|
BF_IMPORT bool BF_CALLTYPE BfParser_Parse(void* bfParser, void* bfPassInstance, bool compatMode);
|
|
BF_IMPORT bool BF_CALLTYPE BfParser_Reduce(void* bfParser, void* bfPassInstance);
|
|
BF_IMPORT bool BF_CALLTYPE BfParser_BuildDefs(void* bfParser, void* bfPassInstance, void* resolvePassData, bool fullRefresh);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
BF_IMPORT void* BF_CALLTYPE BfSystem_Create();
|
|
BF_IMPORT void BF_CALLTYPE BfSystem_ReportMemory(void* bfSystem);
|
|
BF_IMPORT void BF_CALLTYPE BfSystem_Delete(void* bfSystem);
|
|
BF_IMPORT void* BF_CALLTYPE BfSystem_CreatePassInstance(void* bfSystem);
|
|
BF_IMPORT void* BF_CALLTYPE BfSystem_CreateCompiler(void* bfSystem, bool isResolveOnly);
|
|
BF_IMPORT void* BF_CALLTYPE BfSystem_CreateProject(void* bfSystem, const char* projectName);
|
|
BF_IMPORT void BF_CALLTYPE BfParser_Delete(void* bfParser);
|
|
BF_IMPORT void BF_CALLTYPE BfSystem_AddTypeOptions(void* bfSystem, const char* filter, int32 simdSetting, int32 optimizationLevel, int32 emitDebugInfo, int32 arrayBoundsCheck,
|
|
int32 initLocalVariables, int32 emitDynamicCastCheck, int32 emitObjectAccessCheck, int32 allocStackTraceDepth);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
BF_IMPORT void BF_CALLTYPE BfProject_SetDisabled(void* bfProject, bool disabled);
|
|
BF_IMPORT void BF_CALLTYPE BfProject_SetOptions(void* bfProject, int targetType, const char* startupObject, const char* preprocessorMacros,
|
|
int optLevel, int ltoType, int relocType, int picLevel, int32 flags);
|
|
BF_IMPORT void BF_CALLTYPE BfProject_ClearDependencies(void* bfProject);
|
|
BF_IMPORT void BF_CALLTYPE BfProject_AddDependency(void* bfProject, void* depProject);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
BF_IMPORT const char* BF_CALLTYPE BfPassInstance_PopOutString(void* bfPassInstance);
|
|
BF_IMPORT void BF_CALLTYPE BfPassInstance_Delete(void* bfPassInstance);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
BF_IMPORT const char* BF_CALLTYPE VSSupport_Find();
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
USING_NS_BF;
|
|
|
|
BootApp* Beefy::gApp = NULL;
|
|
uint32 gConsoleFGColor = 0;
|
|
uint32 gConsoleBGColor = 0;
|
|
|
|
static bool GetConsoleColor(uint32& fgColor, uint32& bgColor)
|
|
{
|
|
#ifdef _WIN32
|
|
static uint32 consoleColors[16] = { 0xff000000, 0xff000080, 0xff008000, 0xff008080, 0xff800000, 0xff800080, 0xff808000, 0xffc0c0c0,
|
|
0xff808080, 0xff0000ff, 0xff00ff00, 0xff00ffff, 0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff };
|
|
|
|
CONSOLE_SCREEN_BUFFER_INFO screenBuffInfo = { 0 };
|
|
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &screenBuffInfo);
|
|
fgColor = consoleColors[screenBuffInfo.wAttributes & 0xF];
|
|
bgColor = consoleColors[(screenBuffInfo.wAttributes >> 4) & 0xF];
|
|
return true;
|
|
#else
|
|
fgColor = 0xFF808080;
|
|
bgColor = 0xFF000000;
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
static WORD GetColorCode(uint32 color)
|
|
{
|
|
WORD code = 0;
|
|
#ifdef _WIN32
|
|
if (((color >> 0) & 0xFF) > 0x40)
|
|
code |= FOREGROUND_BLUE;
|
|
if (((color >> 8) & 0xFF) > 0x40)
|
|
code |= FOREGROUND_GREEN;
|
|
if (((color >> 16) & 0xFF) > 0x40)
|
|
code |= FOREGROUND_RED;
|
|
if ((((color >> 0) & 0xFF) > 0xC0) ||
|
|
(((color >> 8) & 0xFF) > 0xC0) ||
|
|
(((color >> 16) & 0xFF) > 0xC0))
|
|
code |= FOREGROUND_INTENSITY;
|
|
#endif
|
|
return code;
|
|
}
|
|
|
|
static bool SetConsoleColor(uint32 fgColor, uint32 bgColor)
|
|
{
|
|
#ifdef _WIN32
|
|
WORD attr = GetColorCode(fgColor) | (GetColorCode(bgColor) << 4);
|
|
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), attr);
|
|
SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), attr);
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
|
|
BootApp::BootApp()
|
|
{
|
|
Targets_Create();
|
|
|
|
mVerbosity = Verbosity_Normal;
|
|
mTargetType = BfTargetType_BeefConsoleApplication;
|
|
|
|
//char str[MAX_PATH];
|
|
//GetModuleFileNameA(NULL, str, MAX_PATH);
|
|
//mInstallDir = GetFileDir(str) + "/";
|
|
|
|
//getcwd(str, MAX_PATH);
|
|
//mStartupDir = str;
|
|
//mStartupDir += "/";
|
|
|
|
//mDoClean = false;
|
|
mHadCmdLine = false;
|
|
mShowedHelp = false;
|
|
mHadErrors = false;
|
|
|
|
mSystem = NULL;
|
|
mCompiler = NULL;
|
|
mProject = NULL;
|
|
mCELibProject = NULL;
|
|
mIsCERun = false;
|
|
mAsmKind = BfAsmKind_None;
|
|
mStartupObject = "Program";
|
|
|
|
#ifdef BF_PLATFORM_WINDOWS
|
|
mOptLevel = BfOptLevel_OgPlus;
|
|
mToolset = BfToolsetType_Microsoft;
|
|
#else
|
|
mOptLevel = BfOptLevel_O0;
|
|
mToolset = BfToolsetType_GNU;
|
|
#endif
|
|
mEmitIR = false;
|
|
|
|
#ifdef BF_PLATFORM_WINDOWS
|
|
mTargetTriple = "x86_64-pc-windows-msvc";
|
|
#elif defined BF_PLATFORM_MACOS
|
|
mTargetTriple = "x86_64-apple-macosx10.8.0";
|
|
#else
|
|
mTargetTriple = "x86_64-unknown-linux-gnu";
|
|
#endif
|
|
|
|
GetConsoleColor(gConsoleFGColor, gConsoleBGColor);
|
|
}
|
|
|
|
BootApp::~BootApp()
|
|
{
|
|
Targets_Delete();
|
|
}
|
|
|
|
void BootApp::OutputLine(const String& text, OutputPri outputPri)
|
|
{
|
|
if (mLogFile.IsOpen())
|
|
{
|
|
mLogFile.WriteSNZ(text);
|
|
mLogFile.WriteSNZ("\n");
|
|
}
|
|
|
|
if (outputPri == OutputPri_Error)
|
|
mHadErrors = true;
|
|
|
|
switch (outputPri)
|
|
{
|
|
case OutputPri_Low:
|
|
if (mVerbosity < Verbosity_Detailed)
|
|
return;
|
|
break;
|
|
case OutputPri_Normal:
|
|
if (mVerbosity < Verbosity_Normal)
|
|
return;
|
|
break;
|
|
case OutputPri_High:
|
|
case OutputPri_Warning:
|
|
case OutputPri_Error:
|
|
if (mVerbosity < Verbosity_Minimal)
|
|
return;
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
if (outputPri == OutputPri_Warning)
|
|
{
|
|
SetConsoleColor(0xFFFFFF00, gConsoleBGColor);
|
|
std::cerr << text.c_str() << std::endl;
|
|
SetConsoleColor(gConsoleFGColor, gConsoleBGColor);
|
|
}
|
|
else if (outputPri == OutputPri_Error)
|
|
{
|
|
SetConsoleColor(0xFFFF0000, gConsoleBGColor);
|
|
std::cerr << text.c_str() << std::endl;
|
|
SetConsoleColor(gConsoleFGColor, gConsoleBGColor);
|
|
}
|
|
else
|
|
std::cout << text.c_str() << std::endl;
|
|
}
|
|
|
|
void BootApp::Fail(const String& error)
|
|
{
|
|
if (mLogFile.IsOpen())
|
|
mLogFile.WriteSNZ("FAIL: " + error + "\n");
|
|
std::cerr << "FAIL: " << error.c_str() << std::endl;
|
|
mHadErrors = true;
|
|
}
|
|
|
|
bool BootApp::HandleCmdLine(const String &cmd, const String& param)
|
|
{
|
|
mHadCmdLine = true;
|
|
|
|
bool wantedParam = false;
|
|
|
|
if ((!cmd.StartsWith("-")) && (mIsCERun) && (mCESrc.IsEmpty()))
|
|
{
|
|
mCESrc = cmd;
|
|
return true;
|
|
}
|
|
|
|
if ((cmd == "-help") || (cmd == "-h") || (cmd == "/?"))
|
|
{
|
|
mShowedHelp = true;
|
|
std::cout << "BeefBoot - Beef bootstrapping tool" << std::endl;
|
|
|
|
return false;
|
|
}
|
|
else if (cmd == "-src")
|
|
{
|
|
mRequestedSrc.Add(param);
|
|
wantedParam = true;
|
|
}
|
|
else if (cmd == "-verbosity")
|
|
{
|
|
if (param == "quiet")
|
|
mVerbosity = Verbosity_Quiet;
|
|
else if (param == "minimal")
|
|
mVerbosity = Verbosity_Minimal;
|
|
else if (param == "normal")
|
|
mVerbosity = Verbosity_Normal;
|
|
else if (param == "detailed")
|
|
mVerbosity = Verbosity_Detailed;
|
|
else if (param == "diagnostic")
|
|
mVerbosity = Verbosity_Diagnostic;
|
|
else
|
|
{
|
|
Fail(StrFormat("Invalid verbosity level: '%s'", param.c_str()));
|
|
return false;
|
|
}
|
|
wantedParam = true;
|
|
}
|
|
else if (cmd == "-version")
|
|
{
|
|
BfpSystemResult sysResult;
|
|
String exePath;
|
|
BFP_GETSTR_HELPER(exePath, sysResult, BfpSystem_GetExecutablePath(__STR, __STRLEN, &sysResult));
|
|
|
|
std::cout << "0.0.0" << std::endl;
|
|
mShowedHelp = true;
|
|
return true;
|
|
}
|
|
else if (cmd == "-define")
|
|
{
|
|
if (!mDefines.IsEmpty())
|
|
mDefines += "\n";
|
|
mDefines += param;
|
|
wantedParam = true;
|
|
}
|
|
else if (cmd == "-startup")
|
|
{
|
|
mStartupObject = param;
|
|
wantedParam = true;
|
|
}
|
|
else if (cmd == "-out")
|
|
{
|
|
mTargetPath = param;
|
|
wantedParam = true;
|
|
}
|
|
else if (cmd == "-linkparams")
|
|
{
|
|
mLinkParams = param;
|
|
wantedParam = true;
|
|
}
|
|
else if (cmd == "-Og+")
|
|
{
|
|
mOptLevel = BfOptLevel_OgPlus;
|
|
}
|
|
else if (cmd == "-O0")
|
|
{
|
|
mOptLevel = BfOptLevel_O0;
|
|
}
|
|
else if (cmd == "-O1")
|
|
{
|
|
mOptLevel = BfOptLevel_O1;
|
|
}
|
|
else if (cmd == "-O2")
|
|
{
|
|
mOptLevel = BfOptLevel_O2;
|
|
}
|
|
else if (cmd == "-O3")
|
|
{
|
|
mOptLevel = BfOptLevel_O3;
|
|
}
|
|
else if (cmd == "-gnu")
|
|
{
|
|
mToolset = BfToolsetType_GNU;
|
|
}
|
|
else if (cmd == "-emitir")
|
|
{
|
|
mEmitIR = true;
|
|
}
|
|
else if (cmd == "-cedest")
|
|
{
|
|
mIsCERun = true;
|
|
mCEDest = param;
|
|
wantedParam = true;
|
|
}
|
|
else if (cmd == "-cesrc")
|
|
{
|
|
mIsCERun = true;
|
|
}
|
|
else if (cmd == "-emitasm")
|
|
{
|
|
if (param.IsEmpty())
|
|
{
|
|
mAsmKind = BfAsmKind_Intel;
|
|
}
|
|
else
|
|
{
|
|
if (param == "att")
|
|
mAsmKind = BfAsmKind_ATT;
|
|
else
|
|
mAsmKind = BfAsmKind_Intel;
|
|
wantedParam = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Fail("Unknown option: " + cmd);
|
|
return false;
|
|
}
|
|
|
|
if ((wantedParam) && (param.empty()))
|
|
{
|
|
Fail(StrFormat("Parameter expected for '%s'", cmd.c_str()));
|
|
return false;
|
|
}
|
|
else if ((!wantedParam) && (!param.empty()))
|
|
{
|
|
Fail(StrFormat("No parameter expected for '%s'", cmd.c_str()));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool BootApp::Init()
|
|
{
|
|
char* cwdPtr = getcwd(NULL, 0);
|
|
mWorkingDir = cwdPtr;
|
|
free(cwdPtr);
|
|
|
|
if ((mTargetPath.IsEmpty()) && (mCESrc.IsEmpty()))
|
|
{
|
|
Fail("'Out' path not specified");
|
|
}
|
|
|
|
if (mRequestedSrc.IsEmpty())
|
|
{
|
|
Fail("No source specified");
|
|
}
|
|
|
|
return !mHadErrors;
|
|
}
|
|
|
|
void BootApp::QueueFile(const StringImpl& path, void* project)
|
|
{
|
|
String ext;
|
|
ext = GetFileExtension(path);
|
|
if ((ext.Equals(".bf", StringImpl::CompareKind_OrdinalIgnoreCase)) ||
|
|
(ext.Equals(".cs", StringImpl::CompareKind_OrdinalIgnoreCase)))
|
|
{
|
|
int len;
|
|
const char* data = LoadTextData(path, &len);
|
|
if (data == NULL)
|
|
{
|
|
Fail(StrFormat("Unable to load file '%s'", path.c_str()));
|
|
return;
|
|
}
|
|
|
|
bool worked = true;
|
|
void* bfParser = BfSystem_CreateParser(mSystem, project);
|
|
BfParser_SetSource(bfParser, data, len, path.c_str());
|
|
//bfParser.SetCharIdData(charIdData);
|
|
worked &= BfParser_Parse(bfParser, mPassInstance, false);
|
|
worked &= BfParser_Reduce(bfParser, mPassInstance);
|
|
worked &= BfParser_BuildDefs(bfParser, mPassInstance, NULL, false);
|
|
|
|
delete data;
|
|
}
|
|
}
|
|
|
|
void BootApp::QueuePath(const StringImpl& path)
|
|
{
|
|
if (DirectoryExists(path))
|
|
{
|
|
for (auto& fileEntry : FileEnumerator(path, FileEnumerator::Flags_Files))
|
|
{
|
|
String filePath = fileEntry.GetFilePath();
|
|
|
|
String fileName;
|
|
fileName = GetFileName(filePath);
|
|
|
|
QueueFile(filePath, (mCELibProject != NULL) ? mCELibProject : mProject);
|
|
}
|
|
|
|
for (auto& fileEntry : FileEnumerator(path, FileEnumerator::Flags_Directories))
|
|
{
|
|
String childPath = fileEntry.GetFilePath();
|
|
String dirName;
|
|
dirName = GetFileName(childPath);
|
|
|
|
if (dirName == "build")
|
|
continue;
|
|
|
|
QueuePath(childPath);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QueueFile(path, mProject);
|
|
}
|
|
}
|
|
|
|
static void CompileThread(void* param)
|
|
{
|
|
BfpThread_SetName(NULL, "CompileThread", NULL);
|
|
|
|
BootApp* app = (BootApp*)param;
|
|
BfCompiler_ClearBuildCache(app->mCompiler);
|
|
|
|
if (!BfCompiler_Compile(app->mCompiler, app->mPassInstance, app->mBuildDir.c_str()))
|
|
app->mHadErrors = true;
|
|
}
|
|
|
|
void BootApp::DoCompile()
|
|
{
|
|
#ifdef BFBUILD_MAIN_THREAD_COMPILE
|
|
mOutputDirectory = outputDirectory;
|
|
CompileThread(this);
|
|
#else
|
|
|
|
WorkThreadFunc workThread;
|
|
workThread.Start(CompileThread, this);
|
|
|
|
int lastProgressTicks = 0;
|
|
|
|
bool showProgress = mVerbosity >= Verbosity_Normal;
|
|
|
|
int progressSize = 30;
|
|
if (showProgress)
|
|
{
|
|
std::cout << "[";
|
|
for (int i = 0; i < progressSize; i++)
|
|
std::cout << " ";
|
|
std::cout << "]";
|
|
for (int i = 0; i < progressSize + 1; i++)
|
|
std::cout << "\b";
|
|
std::cout.flush();
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
bool isDone = workThread.WaitForFinish(100);
|
|
|
|
float pct = BfCompiler_GetCompletionPercentage(mCompiler);
|
|
if (isDone)
|
|
pct = 1.0;
|
|
int progressTicks = (int)(pct * progressSize + 0.5f);
|
|
|
|
while (progressTicks > lastProgressTicks)
|
|
{
|
|
if (showProgress)
|
|
{
|
|
std::cout << "*";
|
|
std::cout.flush();
|
|
}
|
|
lastProgressTicks++;
|
|
}
|
|
|
|
if (isDone)
|
|
break;
|
|
}
|
|
|
|
if (showProgress)
|
|
std::cout << std::endl;
|
|
#endif
|
|
}
|
|
|
|
struct OutputContext
|
|
{
|
|
bool mIsError;
|
|
BfpFile* mFile;
|
|
};
|
|
|
|
static void OutputThread(void* param)
|
|
{
|
|
OutputContext* context = (OutputContext*)param;
|
|
|
|
String queuedStr;
|
|
|
|
while (true)
|
|
{
|
|
char data[1024];
|
|
|
|
BfpFileResult result;
|
|
int bytesRead = (int)BfpFile_Read(context->mFile, data, 1023, -1, &result);
|
|
if ((result != BfpFileResult_Ok) && (result != BfpFileResult_PartialData))
|
|
return;
|
|
|
|
data[bytesRead] = 0;
|
|
if (context->mIsError)
|
|
{
|
|
std::cerr << data;
|
|
std::cerr.flush();
|
|
}
|
|
else
|
|
{
|
|
std::cout << data;
|
|
std::cout.flush();
|
|
}
|
|
|
|
if (gApp->mLogFile.IsOpen())
|
|
{
|
|
// This is to ensure that error and output lines are not merged together, though they may interleave
|
|
queuedStr.Append(data, bytesRead);
|
|
while (true)
|
|
{
|
|
int crPos = (int)queuedStr.IndexOf('\n');
|
|
if (crPos == -1)
|
|
break;
|
|
|
|
AutoCrit autoCrit(gApp->mLogCritSect);
|
|
if (context->mIsError)
|
|
gApp->mLogFile.WriteSNZ("err> ");
|
|
else
|
|
gApp->mLogFile.WriteSNZ("out> ");
|
|
|
|
int endPos = crPos;
|
|
if ((endPos > 0) && (queuedStr[endPos - 1] == '\r'))
|
|
endPos--;
|
|
gApp->mLogFile.Write((void*)queuedStr.c_str(), endPos);
|
|
gApp->mLogFile.WriteSNZ("\n");
|
|
queuedStr.Remove(0, crPos + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool BootApp::QueueRun(const String& fileName, const String& args, const String& workingDir, BfpSpawnFlags extraFlags)
|
|
{
|
|
OutputLine(StrFormat("EXECUTING: %s %s", fileName.c_str(), args.c_str()), OutputPri_Low);
|
|
|
|
BfpSpawnFlags spawnFlags = (BfpSpawnFlags)(BfpSpawnFlag_NoWindow | BfpSpawnFlag_RedirectStdOutput | BfpSpawnFlag_RedirectStdError | extraFlags);
|
|
BfpSpawn* spawn = BfpSpawn_Create(fileName.c_str(), args.c_str(), workingDir.c_str(), NULL, spawnFlags, NULL);
|
|
if (spawn == NULL)
|
|
{
|
|
Fail(StrFormat("Failed to execute '%s'", fileName.c_str()));
|
|
return false;
|
|
}
|
|
int exitCode = 0;
|
|
|
|
OutputContext outputContext;;
|
|
outputContext.mIsError = false;
|
|
OutputContext errorContext;
|
|
errorContext.mIsError = false;
|
|
BfpSpawn_GetStdHandles(spawn, NULL, &outputContext.mFile, &errorContext.mFile);
|
|
|
|
BfpThread* outputThread = BfpThread_Create(OutputThread, (void*)&outputContext);
|
|
BfpThread* errorThread = BfpThread_Create(OutputThread, (void*)&errorContext);
|
|
|
|
BfpSpawn_WaitFor(spawn, -1, &exitCode, NULL);
|
|
|
|
if (outputContext.mFile != NULL)
|
|
BfpFile_Close(outputContext.mFile, NULL);
|
|
if (errorContext.mFile != NULL)
|
|
BfpFile_Close(errorContext.mFile, NULL);
|
|
|
|
BfpThread_WaitFor(outputThread, -1);
|
|
BfpThread_WaitFor(errorThread, -1);
|
|
|
|
BfpThread_Release(outputThread);
|
|
BfpThread_Release(errorThread);
|
|
BfpSpawn_Release(spawn);
|
|
|
|
if (exitCode != 0)
|
|
{
|
|
Fail(StrFormat("Exit code returned: %d", exitCode));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool BootApp::CopyFile(const StringImpl& srcPath, const StringImpl& destPath)
|
|
{
|
|
BfpFileResult result = BfpFileResult_Ok;
|
|
for (int i = 0; i < 20; i++)
|
|
{
|
|
BfpFile_Copy(srcPath.c_str(), destPath.c_str(), BfpFileCopyKind_Always, &result);
|
|
if (result == BfpFileResult_Ok)
|
|
return true;
|
|
BfpThread_Sleep(100);
|
|
}
|
|
Fail(StrFormat("Failed to copy '%s' to '%s'", srcPath.c_str(), destPath.c_str()));
|
|
return false;
|
|
}
|
|
|
|
#ifdef BF_PLATFORM_WINDOWS
|
|
void BootApp::DoLinkMS()
|
|
{
|
|
String vsStr = VSSupport_Find();
|
|
|
|
int toolIdx = (int)vsStr.IndexOf("TOOL64\t");
|
|
int toolCrIdx = (int)vsStr.IndexOf('\n', toolIdx + 1);
|
|
if ((toolIdx == -1) || (toolCrIdx == -1))
|
|
{
|
|
Fail("Failed to detect Visual Studio configuration. Is Visual Studio 2015 or later installed?");
|
|
return;
|
|
}
|
|
|
|
String linkerPath = vsStr.Substring(toolIdx + 7, toolCrIdx - toolIdx - 7);
|
|
linkerPath.Append("\\link.exe");
|
|
|
|
String linkLine;
|
|
|
|
String targetPath = mTargetPath;
|
|
|
|
bool hadOutputChanges;
|
|
const char* result = BfCompiler_GetUsedOutputFileNames(mCompiler, mProject, true, &hadOutputChanges);
|
|
if (result == NULL)
|
|
return;
|
|
std::string fileNamesStr;
|
|
fileNamesStr += result;
|
|
if (fileNamesStr.length() == 0)
|
|
return;
|
|
int curIdx = -1;
|
|
while (curIdx < (int)fileNamesStr.length())
|
|
{
|
|
int nextBr = (int)fileNamesStr.find('\n', curIdx + 1);
|
|
if (nextBr == -1)
|
|
nextBr = (int)fileNamesStr.length();
|
|
linkLine.Append(fileNamesStr.substr(curIdx + 1, nextBr - curIdx - 1));
|
|
linkLine.Append(" ");
|
|
curIdx = nextBr;
|
|
}
|
|
|
|
linkLine.Append("-out:");
|
|
IDEUtils::AppendWithOptionalQuotes(linkLine, targetPath);
|
|
linkLine.Append(" ");
|
|
|
|
if (mTargetType == BfTargetType_BeefConsoleApplication)
|
|
linkLine.Append("-subsystem:console ");
|
|
else
|
|
linkLine.Append("-subsystem:windows ");
|
|
|
|
linkLine.Append("-defaultlib:libcmtd ");
|
|
linkLine.Append("-nologo ");
|
|
|
|
linkLine.Append("-pdb:");
|
|
int lastDotPos = (int)targetPath.LastIndexOf('.');
|
|
if (lastDotPos == -1)
|
|
lastDotPos = (int)targetPath.length();
|
|
auto pdbName = String(targetPath, 0, lastDotPos);
|
|
pdbName.Append(".pdb");
|
|
IDEUtils::AppendWithOptionalQuotes(linkLine, pdbName);
|
|
linkLine.Append(" ");
|
|
|
|
linkLine.Append("-debug ");
|
|
|
|
int checkIdx = 0;
|
|
while (true)
|
|
{
|
|
int libIdx = (int)vsStr.IndexOf("LIB64\t", checkIdx);
|
|
if (libIdx == -1)
|
|
break;
|
|
int libCrIdx = (int)vsStr.IndexOf('\n', libIdx + 1);
|
|
if (libCrIdx == -1)
|
|
break;
|
|
|
|
String libPath = vsStr.Substring(libIdx + 6, libCrIdx - libIdx - 6);
|
|
linkLine.Append("-libpath:\"");
|
|
linkLine.Append(libPath);
|
|
linkLine.Append("\" ");
|
|
checkIdx = libCrIdx + 1;
|
|
}
|
|
|
|
linkLine.Append(mLinkParams);
|
|
|
|
BfpSpawnFlags flags = BfpSpawnFlag_None;
|
|
flags = (BfpSpawnFlags)(BfpSpawnFlag_UseArgsFile | BfpSpawnFlag_UseArgsFile_Native | BfpSpawnFlag_UseArgsFile_BOM);
|
|
|
|
auto runCmd = QueueRun(linkerPath, linkLine, mWorkingDir, flags);
|
|
}
|
|
#endif
|
|
|
|
void BootApp::DoLinkGNU()
|
|
{
|
|
String linkerPath;
|
|
if (FileExists("/usr/bin/clang++"))
|
|
linkerPath = "/usr/bin/clang++";
|
|
else
|
|
linkerPath = "/usr/bin/c++";
|
|
|
|
String linkLine;
|
|
|
|
String targetPath = mTargetPath;
|
|
|
|
bool hadOutputChanges;
|
|
const char* result = BfCompiler_GetUsedOutputFileNames(mCompiler, mProject, true, &hadOutputChanges);
|
|
if (result == NULL)
|
|
return;
|
|
std::string fileNamesStr;
|
|
fileNamesStr += result;
|
|
if (fileNamesStr.length() == 0)
|
|
return;
|
|
int curIdx = -1;
|
|
while (curIdx < (int)fileNamesStr.length())
|
|
{
|
|
int nextBr = (int)fileNamesStr.find('\n', curIdx + 1);
|
|
if (nextBr == -1)
|
|
nextBr = (int)fileNamesStr.length();
|
|
linkLine.Append(fileNamesStr.substr(curIdx + 1, nextBr - curIdx - 1));
|
|
linkLine.Append(" ");
|
|
curIdx = nextBr;
|
|
}
|
|
|
|
linkLine.Append("-o ");
|
|
IDEUtils::AppendWithOptionalQuotes(linkLine, targetPath);
|
|
linkLine.Append(" ");
|
|
linkLine.Append("-g ");
|
|
|
|
linkLine.Append("-debug ");
|
|
#ifdef BF_PLATFORM_LINUX
|
|
linkLine.Append("-no-pie ");
|
|
#endif
|
|
|
|
linkLine.Append(mLinkParams);
|
|
|
|
auto runCmd = QueueRun(linkerPath, linkLine, mWorkingDir, BfpSpawnFlag_UseArgsFile);
|
|
}
|
|
|
|
bool BootApp::Compile()
|
|
{
|
|
DWORD startTick = BFTickCount();
|
|
|
|
mSystem = BfSystem_Create();
|
|
|
|
mCompiler = BfSystem_CreateCompiler(mSystem, false);
|
|
|
|
String projectName = GetFileName(mTargetPath);
|
|
int dotPos = (int)projectName.IndexOf('.');
|
|
if (dotPos != -1)
|
|
projectName.RemoveToEnd(dotPos);
|
|
if (projectName.IsEmpty())
|
|
projectName.Append("BeefProject");
|
|
|
|
mProject = BfSystem_CreateProject(mSystem, projectName.c_str());
|
|
|
|
if (mIsCERun)
|
|
{
|
|
mCELibProject = BfSystem_CreateProject(mSystem, "BeefLib");
|
|
|
|
BfProjectFlags flags = BfProjectFlags_None;
|
|
BfProject_SetOptions(mCELibProject, BfTargetType_BeefLib, "", mDefines.c_str(), mOptLevel, 0, 0, 0, flags);
|
|
}
|
|
|
|
if (!mDefines.IsEmpty())
|
|
mDefines.Append("\n");
|
|
mDefines.Append("BF_64_BIT");
|
|
mDefines.Append("\nBF_LITTLE_ENDIAN");
|
|
mDefines.Append("\n");
|
|
mDefines.Append(BF_PLATFORM_NAME);
|
|
|
|
int ltoType = 0;
|
|
BfProjectFlags flags = BfProjectFlags_None;
|
|
if (mIsCERun)
|
|
{
|
|
flags = (BfProjectFlags)(flags | BfProjectFlags_SingleModule | BfProjectFlags_AlwaysIncludeAll);
|
|
if (mAsmKind == BfAsmKind_ATT)
|
|
flags = (BfProjectFlags)(flags | BfProjectFlags_AsmOutput | BfProjectFlags_AsmOutput_ATT);
|
|
else if (mAsmKind == BfAsmKind_Intel)
|
|
flags = (BfProjectFlags)(flags | BfProjectFlags_AsmOutput);
|
|
}
|
|
BfProject_SetOptions(mProject, mTargetType, mStartupObject.c_str(), mDefines.c_str(), mOptLevel, ltoType, 0, 0, flags);
|
|
|
|
if (mCELibProject != NULL)
|
|
BfProject_AddDependency(mProject, mCELibProject);
|
|
|
|
mPassInstance = BfSystem_CreatePassInstance(mSystem);
|
|
|
|
Beefy::String exePath;
|
|
BfpGetStrHelper(exePath, [](char* outStr, int* inOutStrSize, BfpResult* result)
|
|
{
|
|
BfpSystem_GetExecutablePath(outStr, inOutStrSize, (BfpSystemResult*)result);
|
|
});
|
|
mBuildDir = GetFileDir(exePath) + "/build";
|
|
|
|
RecursiveCreateDirectory(mBuildDir + "/" + projectName);
|
|
if (mIsCERun)
|
|
RecursiveCreateDirectory(mBuildDir + "/BeefLib");
|
|
|
|
BfCompilerOptionFlags optionFlags = (BfCompilerOptionFlags)(BfCompilerOptionFlag_EmitDebugInfo | BfCompilerOptionFlag_EmitLineInfo | BfCompilerOptionFlag_GenerateOBJ | BfCompilerOptionFlag_OmitDebugHelpers);
|
|
if (mEmitIR)
|
|
optionFlags = (BfCompilerOptionFlags)(optionFlags | BfCompilerOptionFlag_WriteIR);
|
|
|
|
int maxWorkerThreads = BfpSystem_GetNumLogicalCPUs(NULL);
|
|
if (maxWorkerThreads <= 1)
|
|
maxWorkerThreads = 6;
|
|
|
|
BfCompiler_SetOptions(mCompiler, NULL, 0, mTargetTriple.c_str(), mToolset, BfSIMDSetting_SSE2, 1, maxWorkerThreads, optionFlags, "malloc", "free");
|
|
|
|
if (mIsCERun)
|
|
{
|
|
QueueFile(mCESrc, mProject);
|
|
}
|
|
for (auto& srcName : mRequestedSrc)
|
|
{
|
|
String absPath = GetAbsPath(srcName, mWorkingDir);
|
|
QueuePath(absPath);
|
|
}
|
|
|
|
if (!mHadErrors)
|
|
{
|
|
DoCompile();
|
|
OutputLine(StrFormat("TIMING: Beef compiling: %0.1fs", (BFTickCount() - startTick) / 1000.0), OutputPri_Normal);
|
|
|
|
if (!mCEDest.IsEmpty())
|
|
{
|
|
String ext;
|
|
String srcResult = mBuildDir + "/BeefProject/BeefProject";
|
|
if (mAsmKind == BfAsmKind_None)
|
|
srcResult += BF_OBJ_EXT;
|
|
else
|
|
srcResult += ".s";
|
|
|
|
CopyFile(srcResult, mCEDest);
|
|
}
|
|
|
|
if ((mIsCERun) && (mEmitIR))
|
|
{
|
|
String ext;
|
|
String srcResult = mBuildDir + "/BeefProject/BeefProject";
|
|
String irDestPath = mCEDest;
|
|
int dotPos = (int)irDestPath.LastIndexOf('.');
|
|
if (dotPos != -1)
|
|
irDestPath.RemoveToEnd(dotPos);
|
|
|
|
if (mOptLevel == BfOptLevel_OgPlus)
|
|
{
|
|
srcResult += ".beir";
|
|
irDestPath += ".ll";
|
|
}
|
|
else
|
|
{
|
|
srcResult += ".ll";
|
|
irDestPath += ".ll";
|
|
}
|
|
|
|
CopyFile(srcResult, irDestPath);
|
|
}
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
const char* msg = BfPassInstance_PopOutString(mPassInstance);
|
|
if (msg == NULL)
|
|
break;
|
|
|
|
if ((strncmp(msg, ":warn ", 6) == 0))
|
|
{
|
|
OutputLine(msg + 6, OutputPri_Warning);
|
|
}
|
|
else if ((strncmp(msg, ":error ", 7) == 0))
|
|
{
|
|
OutputLine(msg + 7, OutputPri_Error);
|
|
}
|
|
else if ((strncmp(msg, ":med ", 5) == 0))
|
|
{
|
|
OutputLine(msg + 5, OutputPri_Normal);
|
|
}
|
|
else if ((strncmp(msg, ":low ", 5) == 0))
|
|
{
|
|
OutputLine(msg + 5, OutputPri_Low);
|
|
}
|
|
else if ((strncmp(msg, "ERROR(", 6) == 0) || (strncmp(msg, "ERROR:", 6) == 0))
|
|
{
|
|
OutputLine(msg, OutputPri_Error);
|
|
}
|
|
else if ((strncmp(msg, "WARNING(", 8) == 0) || (strncmp(msg, "WARNING:", 8) == 0))
|
|
{
|
|
OutputLine(msg, OutputPri_Warning);
|
|
}
|
|
else
|
|
OutputLine(msg);
|
|
}
|
|
|
|
if ((!mHadErrors) && (!mTargetPath.IsEmpty()))
|
|
{
|
|
if (mVerbosity == Verbosity_Normal)
|
|
{
|
|
std::cout << "Linking " << mTargetPath.c_str() << "...";
|
|
std::cout.flush();
|
|
}
|
|
|
|
#ifdef BF_PLATFORM_WINDOWS
|
|
DoLinkMS();
|
|
#else
|
|
DoLinkGNU();
|
|
#endif
|
|
|
|
if (mVerbosity == Verbosity_Normal)
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
BfPassInstance_Delete(mPassInstance);
|
|
BfCompiler_Delete(mCompiler);
|
|
|
|
BfSystem_Delete(mSystem);
|
|
|
|
return !mHadErrors;
|
|
}
|
|
|