diff --git a/RedPandaIDE/addon/api.cpp b/RedPandaIDE/addon/api.cpp index 0d3ff398..8c253010 100644 --- a/RedPandaIDE/addon/api.cpp +++ b/RedPandaIDE/addon/api.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #ifdef Q_OS_WINDOWS # include @@ -51,11 +52,126 @@ using pIsWow64Process2_t = BOOL (WINAPI *)( ); #endif -// C_Debug.debug(string) -> () +static QString luaDump(const QJsonValue &value) { + if (value.isNull()) + return "nil"; + if (value.isBool()) + return value.toBool() ? "true" : "false"; + if (value.isDouble()) + return QString::number(value.toDouble()); + if (value.isString()) { + QString s = value.toString(); + s.replace('\\', "\\\\"); + s.replace('"', "\\\""); + s.replace('\n', "\\n"); + s.replace('\r', "\\r"); + s.replace('\t', "\\t"); + return '"' + s + '"'; + } + if (value.isArray()) { + QString s = "{"; + for (const QJsonValue &v : value.toArray()) + s += luaDump(v) + ','; + s += '}'; + return s; + } + if (value.isObject()) { + QJsonObject o = value.toObject(); + QString s = "{"; + for (const QString &k : o.keys()) + s += '[' + luaDump(k) + "]=" + luaDump(o[k]) + ','; + s += '}'; + return s; + } + return "nil"; +} + +static QString stringify(const QJsonValue &value) { + if (value.isString()) + return value.toString(); + if (value.isNull()) + return "[nil]"; + if (value.isBool()) + return value.toBool() ? "[true]" : "[false]"; + if (value.isDouble()) + return QString("[number %1]").arg(value.toDouble()); + if (value.isArray() || value.isObject()) + return QString("[table %1]").arg(luaDump(value)); + return "[unknown]"; +} + +/* https://stackoverflow.com/questions/15687223/explicit-call-to-destructor-before-longjmp-croak + * + * C++11 18.10/4: A setjmp/longjmp call pair has undefined behavior if + * replacing the setjmp and longjmp by catch and throw would invoke any + * non-trivial destructors for any automatic objects. + * + * Use impl function so the API boundary contains only POD types. + */ +void luaApiImpl_Debug_debug(lua_State *L) { + QString s = AddOn::RaiiLuaState::fetchString(L, 1); + int nArgs = lua_gettop(L); + for (int i = 2; i <= nArgs; ++i) { + QJsonValue arg; + try { + arg = AddOn::RaiiLuaState::fetch(L, i); + } catch (const AddOn::LuaError &e) { + QString reason = e.reason() + QString(" ('C_Debug.debug' argument #%1)").arg(i); + throw AddOn::LuaError(reason); + } + s = s.arg(stringify(arg)); + } + qDebug().noquote() << s; +} + +// C_Debug.debug(string, ...) -> () extern "C" int luaApi_Debug_debug(lua_State *L) noexcept { - QString info = AddOn::RaiiLuaState::fetchString(L, 1); - qDebug() << info; - return 0; + bool error = false; + try { + luaApiImpl_Debug_debug(L); + } catch (const AddOn::LuaError &e) { + error = true; + AddOn::RaiiLuaState::push(L, e.reason()); + } + if (error) { + lua_error(L); // longjmp, never returns + __builtin_unreachable(); + } else { + return 0; + } +} + +void luaApiImpl_Debug_messageBox(lua_State *L) { + QString s = AddOn::RaiiLuaState::fetchString(L, 1); + int nArgs = lua_gettop(L); + for (int i = 2; i <= nArgs; ++i) { + QJsonValue arg; + try { + arg = AddOn::RaiiLuaState::fetch(L, i); + } catch (const AddOn::LuaError &e) { + QString reason = e.reason() + QString(" ('C_Debug.messageBox' argument #%1)").arg(i); + throw AddOn::LuaError(reason); + } + s = s.arg(stringify(arg)); + } + QMessageBox::information(nullptr, "Debug", s); +} + +// C_Debug.messageBox(string, ...) -> () +extern "C" int luaApi_Debug_messageBox(lua_State *L) noexcept { + bool error = false; + try { + luaApiImpl_Debug_messageBox(L); + } catch (const AddOn::LuaError &e) { + error = true; + AddOn::RaiiLuaState::push(L, e.reason()); + } + if (error) { + lua_error(L); // longjmp, never returns + __builtin_unreachable(); + } else { + return 0; + } } // C_Desktop.desktopEnvironment() -> string @@ -262,9 +378,7 @@ extern "C" int luaApi_System_supportedAppArchList(lua_State *L) noexcept { } } - // workaround for Debian 10 Qt 5.11, better to be - // QStringList result{arches.begin(), arches.end()}; - QStringList result = arches.toList(); + QStringList result = arches.values(); AddOn::RaiiLuaState::push(L, result); return 1; #endif @@ -291,15 +405,37 @@ extern "C" int luaApi_System_readRegistry(lua_State *L) noexcept } #endif -// C_Util.format(string, ...) -> string -extern "C" int luaApi_Util_format(lua_State *L) noexcept +void luaApiImpl_Util_format(lua_State *L) { QString s = AddOn::RaiiLuaState::fetchString(L, 1); int nArgs = lua_gettop(L); for (int i = 2; i <= nArgs; ++i) { - QJsonValue arg = AddOn::RaiiLuaState::fetch(L, i); - s = s.arg(arg.toString()); + QJsonValue arg; + try { + arg = AddOn::RaiiLuaState::fetch(L, i); + } catch (const AddOn::LuaError &e) { + QString reason = e.reason() + QString(" ('C_Util.format' argument #%1)").arg(i); + throw AddOn::LuaError(reason); + } + s = s.arg(stringify(arg)); } AddOn::RaiiLuaState::push(L, s); - return 1; +} + +// C_Util.format(string, ...) -> string +extern "C" int luaApi_Util_format(lua_State *L) noexcept +{ + bool error = false; + try { + luaApiImpl_Util_format(L); + } catch (const AddOn::LuaError &e) { + error = true; + AddOn::RaiiLuaState::push(L, e.reason()); + } + if (error) { + lua_error(L); // longjmp, never returns + __builtin_unreachable(); + } else { + return 1; + } } diff --git a/RedPandaIDE/addon/api.h b/RedPandaIDE/addon/api.h index c147703c..6afd92e5 100644 --- a/RedPandaIDE/addon/api.h +++ b/RedPandaIDE/addon/api.h @@ -20,6 +20,7 @@ struct lua_State; extern "C" int luaApi_Debug_debug(lua_State *L) noexcept; +extern "C" int luaApi_Debug_messageBox(lua_State *L) noexcept; extern "C" int luaApi_Desktop_desktopEnvironment(lua_State *L) noexcept; extern "C" int luaApi_Desktop_language(lua_State *L) noexcept; diff --git a/RedPandaIDE/addon/executor.cpp b/RedPandaIDE/addon/executor.cpp index 0c941a79..08efb4eb 100644 --- a/RedPandaIDE/addon/executor.cpp +++ b/RedPandaIDE/addon/executor.cpp @@ -26,7 +26,8 @@ namespace AddOn { static QMap> apiGroups{ {"C_Debug", { - {"debug", &luaApi_Debug_debug}, // (string) -> () + {"debug", &luaApi_Debug_debug}, // (string, ...) -> () + {"messageBox", &luaApi_Debug_messageBox}, // (string, ...) -> () }}, {"C_Desktop", { @@ -80,7 +81,11 @@ extern "C" void luaHook_timeoutKiller(lua_State *L, lua_Debug *ar [[maybe_unused } }; -ThemeExecutor::ThemeExecutor() : SimpleExecutor({"C_Debug", "C_Desktop"}) {} +ThemeExecutor::ThemeExecutor() : SimpleExecutor( + "theme", 0, 1, + {"C_Debug", "C_Desktop", "C_Util"}) +{ +} QJsonObject ThemeExecutor::operator()(const QByteArray &script, const QString &name) { @@ -92,28 +97,74 @@ QJsonObject ThemeExecutor::operator()(const QByteArray &script, throw LuaError("Theme script must return an object."); } +SimpleExecutor::SimpleExecutor(const QString &kind, int major, int minor, const QList &apis) + : mKind(kind), mMajor(major), mMinor(minor), mApis(apis) +{ +} + +bool SimpleExecutor::apiVersionCheck(const QJsonObject &addonApi) { + const QString &addonKind = addonApi["kind"].toString(); + if (addonKind != mKind) + return false; + if (!addonApi.contains("major") || !addonApi.contains("minor")) + return false; + int addonMajor = addonApi["major"].toInt(); + int addonMinor = addonApi["minor"].toInt(); + if (mMajor == 0) + return addonMajor == mMajor && addonMinor == mMinor; + else + return addonMajor == mMajor && addonMinor <= mMinor; +} + QJsonValue SimpleExecutor::runScript(const QByteArray &script, const QString &name, std::chrono::microseconds timeLimit) { RaiiLuaState L(name, timeLimit); - L.openLibs(); - for (auto &api : mApis) - registerApiGroup(L, api); - int retLoad = L.loadBuffer(script, name); if (retLoad != 0) - throw LuaError(QString("Lua script load error: %1.").arg(L.popString())); + throw LuaError(QString("Lua load error: %1.").arg(L.popString())); L.setHook(&luaHook_timeoutKiller, LUA_MASKCOUNT, 1'000'000); // ~5ms on early 2020s desktop CPUs L.setTimeStart(); - int callResult = L.pCall(0, 1, 0); + int callResult = L.pCall(0, 0, 0); if (callResult != 0) { throw LuaError(QString("Lua error: %1.").arg(L.popString())); } + // call `apiVersion()` to check compatibility + int type = L.getGlobal("apiVersion"); + if (type != LUA_TFUNCTION) { + throw LuaError("Add-on interface error: `apiVersion` is not a function."); + } + callResult = L.pCall(0, 1, 0); + if (callResult != 0) { + throw LuaError(QString("Lua error: %1.").arg(L.popString())); + } + QJsonObject addonApi = L.popObject(); + if (!apiVersionCheck(addonApi)) { + throw LuaError(QString("Add-on interface error: API version incompatible with %1:%2.%3.") + .arg(mKind).arg(mMajor).arg(mMinor)); + } + + // inject APIs and call `main()` + L.openLibs(); + for (auto &api : mApis) + registerApiGroup(L, api); + type = L.getGlobal("main"); + if (type != LUA_TFUNCTION) { + throw LuaError("Add-on interface error: `main` is not a function."); + } + callResult = L.pCall(0, 1, 0); + if (callResult != 0) { + throw LuaError(QString("Lua error: %1.").arg(L.popString())); + } return L.fetch(1); } -CompilerHintExecutor::CompilerHintExecutor() : SimpleExecutor({"C_Debug", "C_Desktop", "C_FileSystem", "C_System", "C_Util"}) {} +CompilerHintExecutor::CompilerHintExecutor() : SimpleExecutor( + "compiler_hint", 0, 1, + {"C_Debug", "C_Desktop", "C_FileSystem", "C_System", "C_Util"}) +{ +} QJsonObject CompilerHintExecutor::operator()(const QByteArray &script) { using namespace std::chrono_literals; diff --git a/RedPandaIDE/addon/executor.h b/RedPandaIDE/addon/executor.h index 671305b6..4aca718b 100644 --- a/RedPandaIDE/addon/executor.h +++ b/RedPandaIDE/addon/executor.h @@ -27,13 +27,17 @@ namespace AddOn { // simple, stateless Lua executor class SimpleExecutor { protected: - SimpleExecutor(const QList &apis): mApis(apis) {} + SimpleExecutor(const QString &kind, int major, int minor, const QList &apis); + + bool apiVersionCheck(const QJsonObject &addonApi); - // run a Lua script and fetch its return value as type R QJsonValue runScript(const QByteArray &script, const QString &name, std::chrono::microseconds timeLimit); private: + QString mKind; + int mMajor; + int mMinor; QStringList mApis; }; diff --git a/RedPandaIDE/addon/runtime.cpp b/RedPandaIDE/addon/runtime.cpp index 752dbb59..2b374326 100644 --- a/RedPandaIDE/addon/runtime.cpp +++ b/RedPandaIDE/addon/runtime.cpp @@ -68,6 +68,11 @@ QString RaiiLuaState::fetchString(int index) return lua_tostring(mLua, index); } +QJsonObject RaiiLuaState::fetchObject(int index) +{ + return fetchTableImpl(mLua, index, 0).toObject(); +} + QJsonValue RaiiLuaState::fetch(int index) { return fetchValueImpl(mLua, index, 0); @@ -93,6 +98,11 @@ QString RaiiLuaState::fetchString(lua_State *L, int index) return lua_tostring(L, index); } +QJsonObject RaiiLuaState::fetchObject(lua_State *L, int index) +{ + return fetchTableImpl(L, index, 0).toObject(); +} + QJsonValue RaiiLuaState::fetch(lua_State *L, int index) { return fetchValueImpl(L, index, 0); @@ -112,6 +122,13 @@ QString RaiiLuaState::popString() return value; } +QJsonObject RaiiLuaState::popObject() +{ + QJsonObject value = fetchObject(-1); + lua_pop(mLua, 1); + return value; +} + QJsonValue RaiiLuaState::pop() { QJsonValue value = fetch(-1); @@ -191,6 +208,11 @@ int RaiiLuaState::pCall(int nargs, int nresults, int msgh) return lua_pcall(mLua, nargs, nresults, msgh); } +int RaiiLuaState::getGlobal(const QString &name) +{ + return lua_getglobal(mLua, name.toUtf8().constData()); +} + void RaiiLuaState::setGlobal(const QString &name) { return lua_setglobal(mLua, name.toUtf8().constData()); @@ -230,7 +252,16 @@ QJsonValue RaiiLuaState::fetchTableImpl(lua_State *L, int index, int depth) // here we take the fact that Lua iterates array part first bool processingArrayPart = true; while (lua_next(L, newIndex)) { - QJsonValue v = pop(L); + QJsonValue v; + try { + v = fetchValueImpl(L, -1, depth); + } catch (const LuaError &e) { + QString key = fetchString(L, -2); + QString reason = e.reason() + QString(" (at table key '%1')").arg(key); + lua_pop(L, 2); + throw LuaError(reason); + } + lua_pop(L, 1); if (processingArrayPart && lua_isinteger(L, -1) && fetchInteger(L, -1) == arrayPart.size() + 1) // we are still in array part arrayPart.push_back(v); @@ -275,8 +306,11 @@ QJsonValue RaiiLuaState::fetchValueImpl(lua_State *L, int index, int depth) return fetchString(L, index); else if (lua_istable(L, index)) return fetchTableImpl(L, index, depth + 1); - else - throw LuaError(QString("Lua type error: unknown type %1.").arg(lua_typename(L, index))); + else { + int type = lua_type(L, index); + const char *name = lua_typename(L, type); + throw LuaError(QString("Lua type error: unknown type %1.").arg(name)); + } } QHash RaiiLuaState::mExtraState; diff --git a/RedPandaIDE/addon/runtime.h b/RedPandaIDE/addon/runtime.h index 3eae0137..c5b2c8b3 100644 --- a/RedPandaIDE/addon/runtime.h +++ b/RedPandaIDE/addon/runtime.h @@ -68,6 +68,7 @@ public: static long long fetchInteger(lua_State *L, int index); static double fetchNumber(lua_State *L, int index); static QString fetchString(lua_State *L, int index); + static QJsonObject fetchObject(lua_State *L, int index); static QJsonValue fetch(lua_State *L, int index); bool popBoolean(); @@ -94,6 +95,7 @@ public: int loadBuffer(const QByteArray &buff, const QString &name); void openLibs(); int pCall(int nargs, int nresults, int msgh); + int getGlobal(const QString &name); void setGlobal(const QString &name); void setHook(lua_Hook f, int mask, int count); diff --git a/RedPandaIDE/settings.cpp b/RedPandaIDE/settings.cpp index da6a4f8b..92a6fffa 100644 --- a/RedPandaIDE/settings.cpp +++ b/RedPandaIDE/settings.cpp @@ -3279,7 +3279,9 @@ void Settings::CompilerSets::findSets() try { compilerHint = AddOn::CompilerHintExecutor{}(script); } catch (const AddOn::LuaError &e) { - qDebug() << "Error in compiler_hint.lua:" << e.reason(); + QMessageBox::critical(nullptr, + QObject::tr("Error executing platform compiler hint add-on"), + e.reason()); } if (!compilerHint.empty()) { QJsonArray compilerList = compilerHint["compilerList"].toArray(); diff --git a/RedPandaIDE/themes/contrast.lua b/RedPandaIDE/themes/contrast.lua index 3852ef13..dc02c16e 100644 --- a/RedPandaIDE/themes/contrast.lua +++ b/RedPandaIDE/themes/contrast.lua @@ -1,43 +1,53 @@ +function apiVersion() + return { + kind = "theme", + major = 0, + minor = 1, + } +end + local nameMap = { - en_US = "Contrast", - pt_BR = "Contraste", - zh_CN = "高对比度", - zh_TW = "高對比度" + en_US = "Contrast", + pt_BR = "Contraste", + zh_CN = "高对比度", + zh_TW = "高對比度", } -local lang = C_Desktop.language() +function main() + local lang = C_Desktop.language() -return { - ["name"] = nameMap[lang] or nameMap.en_US, - ["style"] = "RedPandaDarkFusion", - ["default scheme"] = "Twilight", - ["default iconset"] = "contrast", - ["palette"] = { - PaletteWindow = "#000000", - PaletteWindowText = "#FFFFFF", - PaletteBase = "#141414", - PaletteAlternateBase = "#191919", - PaletteButton = "#141414", - PaletteButtonDisabled = "#141414", - PaletteBrightText = "#ff0000", - PaletteText = "#FFFFFF", - PaletteButtonText = "#FFFFFF", - PaletteButtonTextDisabled = "#9DA9B5", - PaletteHighlight = "#aa1f75cc", - PaletteDark = "#232323", - PaletteHighlightedText = "#e7e7e7", - PaletteToolTipBase = "#66000000", - PaletteToolTipText = "#e7e7e7", - PaletteLink = "#007af4", - PaletteLinkVisited = "#a57aff", - PaletteWindowDisabled = "#0A0A0A", - PaletteWindowTextDisabled = "#9DA9B5", - PaletteHighlightDisabled = "#9DA9B5", - PaletteHighlightedTextDisabled = "#9DA9B5", - PaletteBaseDisabled = "#000000", - PaletteTextDisabled = "#9DA9B5", - PaletteMid = "#FFFFFF", - PaletteLight = "#505050", - PaletteMidLight = "#00ff00" - } -} + return { + ["name"] = nameMap[lang] or nameMap.en_US, + ["style"] = "RedPandaDarkFusion", + ["default scheme"] = "Twilight", + ["default iconset"] = "contrast", + ["palette"] = { + PaletteWindow = "#000000", + PaletteWindowText = "#FFFFFF", + PaletteBase = "#141414", + PaletteAlternateBase = "#191919", + PaletteButton = "#141414", + PaletteButtonDisabled = "#141414", + PaletteBrightText = "#ff0000", + PaletteText = "#FFFFFF", + PaletteButtonText = "#FFFFFF", + PaletteButtonTextDisabled = "#9DA9B5", + PaletteHighlight = "#aa1f75cc", + PaletteDark = "#232323", + PaletteHighlightedText = "#e7e7e7", + PaletteToolTipBase = "#66000000", + PaletteToolTipText = "#e7e7e7", + PaletteLink = "#007af4", + PaletteLinkVisited = "#a57aff", + PaletteWindowDisabled = "#0A0A0A", + PaletteWindowTextDisabled = "#9DA9B5", + PaletteHighlightDisabled = "#9DA9B5", + PaletteHighlightedTextDisabled = "#9DA9B5", + PaletteBaseDisabled = "#000000", + PaletteTextDisabled = "#9DA9B5", + PaletteMid = "#FFFFFF", + PaletteLight = "#505050", + PaletteMidlight = "#00ff00", + }, + } +end diff --git a/RedPandaIDE/themes/dark.lua b/RedPandaIDE/themes/dark.lua index 3619a73c..73307fbc 100644 --- a/RedPandaIDE/themes/dark.lua +++ b/RedPandaIDE/themes/dark.lua @@ -1,43 +1,53 @@ +function apiVersion() + return { + kind = "theme", + major = 0, + minor = 1, + } +end + local nameMap = { - en_US = "Dark", - pt_BR = "Escura", - zh_CN = "深色", - zh_TW = "深色" + en_US = "Dark", + pt_BR = "Escura", + zh_CN = "深色", + zh_TW = "深色", } -local lang = C_Desktop.language() +function main() + local lang = C_Desktop.language() -return { - ["name"] = nameMap[lang] or nameMap.en_US, - ["style"] = "RedPandaDarkFusion", - ["default scheme"] = "VS Code", - ["default iconset"] = "contrast", - ["palette"] = { - PaletteWindow = "#19232D", - PaletteWindowText = "#E0E1E3", - PaletteBase = "#1E1E1E", - PaletteAlternateBase = "#303030", - PaletteButton = "#19232D", - PaletteButtonDisabled = "#19232D", - PaletteBrightText = "#ff0000", - PaletteText = "#e7e7e7", - PaletteButtonText = "#d3d3d3", - PaletteButtonTextDisabled = "#9DA9B5", - PaletteHighlight = "#aa1f75cc", - PaletteDark = "#232323", - PaletteHighlightedText = "#e7e7e7", - PaletteToolTipBase = "#66000000", - PaletteToolTipText = "#e7e7e7", - PaletteLink = "#007af4", - PaletteLinkVisited = "#a57aff", - PaletteWindowDisabled = "#333333", - PaletteWindowTextDisabled = "#9DA9B5", - PaletteHighlightDisabled = "#26486B", - PaletteHighlightedTextDisabled = "#9DA9B5", - PaletteBaseDisabled = "#19232D", - PaletteTextDisabled = "#9DA9B5", - PaletteMid = "#707070", - PaletteLight = "#505050", - PaletteMidLight = "#00ff00" - } -} + return { + ["name"] = nameMap[lang] or nameMap.en_US, + ["style"] = "RedPandaDarkFusion", + ["default scheme"] = "VS Code", + ["default iconset"] = "contrast", + ["palette"] = { + PaletteWindow = "#19232D", + PaletteWindowText = "#E0E1E3", + PaletteBase = "#1E1E1E", + PaletteAlternateBase = "#303030", + PaletteButton = "#19232D", + PaletteButtonDisabled = "#19232D", + PaletteBrightText = "#ff0000", + PaletteText = "#e7e7e7", + PaletteButtonText = "#d3d3d3", + PaletteButtonTextDisabled = "#9DA9B5", + PaletteHighlight = "#aa1f75cc", + PaletteDark = "#232323", + PaletteHighlightedText = "#e7e7e7", + PaletteToolTipBase = "#66000000", + PaletteToolTipText = "#e7e7e7", + PaletteLink = "#007af4", + PaletteLinkVisited = "#a57aff", + PaletteWindowDisabled = "#333333", + PaletteWindowTextDisabled = "#9DA9B5", + PaletteHighlightDisabled = "#26486B", + PaletteHighlightedTextDisabled = "#9DA9B5", + PaletteBaseDisabled = "#19232D", + PaletteTextDisabled = "#9DA9B5", + PaletteMid = "#707070", + PaletteLight = "#505050", + PaletteMidlight = "#00ff00", + }, + } +end diff --git a/RedPandaIDE/themes/default.lua b/RedPandaIDE/themes/default.lua index 58452229..155735b9 100644 --- a/RedPandaIDE/themes/default.lua +++ b/RedPandaIDE/themes/default.lua @@ -1,41 +1,51 @@ +function apiVersion() + return { + kind = "theme", + major = 0, + minor = 1, + } +end + local nameMap = { - en_US = "Light", - pt_BR = "Clara", - zh_CN = "浅色", - zh_TW = "淺色" + en_US = "Light", + pt_BR = "Clara", + zh_CN = "浅色", + zh_TW = "淺色", } -local lang = C_Desktop.language() +function main() + local lang = C_Desktop.language() -return { - ["name"] = nameMap[lang] or nameMap.en_US, - ["style"] = "RedPandaLightFusion", - ["default scheme"] = "Intellij Classic", - ["default iconset"] = "newlook", - ["palette"] = { - PaletteWindow = "#efefef", - PaletteWindowText = "#000000", - PaletteBase = "#ffffff", - PaletteAlternateBase = "#f7f7f7", - PaletteToolTipBase = "#ffffdc", - PaletteToolTipText = "#000000", - PaletteText = "#000000", - PaletteButton = "#efefef", - PaletteButtonText = "#000000", - PaletteBrightText = "#ffffff", - PaletteLink = "#0000ff", - PaletteLinkVisited = "#ff00ff", - PaletteLight = "#ffffff", - PaletteMidLight = "#cacaca", - PaletteDark = "#9f9f9f", - PaletteMid = "#b8b8b8", - PaletteWindowDisabled = "#efefef", - PaletteWindowTextDisabled = "#bebebe", - PaletteBaseDisabled = "#efefef", - PaletteTextDisabled = "#bebebe", - PaletteButtonDisabled = "#efefef", - PaletteButtonTextDisabled = "#bebebe", - PaletteHighlight = "#dddddd", - PaletteHighlightedText = "#000000" - } -} + return { + ["name"] = nameMap[lang] or nameMap.en_US, + ["style"] = "RedPandaLightFusion", + ["default scheme"] = "Intellij Classic", + ["default iconset"] = "newlook", + ["palette"] = { + PaletteWindow = "#efefef", + PaletteWindowText = "#000000", + PaletteBase = "#ffffff", + PaletteAlternateBase = "#f7f7f7", + PaletteToolTipBase = "#ffffdc", + PaletteToolTipText = "#000000", + PaletteText = "#000000", + PaletteButton = "#efefef", + PaletteButtonText = "#000000", + PaletteBrightText = "#ffffff", + PaletteLink = "#0000ff", + PaletteLinkVisited = "#ff00ff", + PaletteLight = "#ffffff", + PaletteMidlight = "#cacaca", + PaletteDark = "#9f9f9f", + PaletteMid = "#b8b8b8", + PaletteWindowDisabled = "#efefef", + PaletteWindowTextDisabled = "#bebebe", + PaletteBaseDisabled = "#efefef", + PaletteTextDisabled = "#bebebe", + PaletteButtonDisabled = "#efefef", + PaletteButtonTextDisabled = "#bebebe", + PaletteHighlight = "#dddddd", + PaletteHighlightedText = "#000000", + }, + } +end diff --git a/RedPandaIDE/themes/molo.lua b/RedPandaIDE/themes/molo.lua index 9e5e0fef..3c31f23c 100644 --- a/RedPandaIDE/themes/molo.lua +++ b/RedPandaIDE/themes/molo.lua @@ -1,43 +1,53 @@ +function apiVersion() + return { + kind = "theme", + major = 0, + minor = 1, + } +end + local nameMap = { - en_US = "MoLo", - pt_BR = "Molo", - zh_CN = "墨落", - zh_TW = "墨落" + en_US = "MoLo", + pt_BR = "Molo", + zh_CN = "墨落", + zh_TW = "墨落", } -local lang = C_Desktop.language() +function main() + local lang = C_Desktop.language() -return { - ["name"] = nameMap[lang] or nameMap.en_US, - ["style"] = "RedPandaDarkFusion", - ["default scheme"] = "MoLo CodeBlack", - ["default iconset"] = "newlook", - ["palette"] = { - PaletteWindow = "#000000", - PaletteWindowText = "#FFFFFF", - PaletteBase = "#141414", - PaletteAlternateBase = "#191919", - PaletteButton = "#141414", - PaletteButtonDisabled = "#141414", - PaletteBrightText = "#ff0000", - PaletteText = "#FFFFFF", - PaletteButtonText = "#FFFFFF", - PaletteButtonTextDisabled = "#9DA9B5", - PaletteHighlight = "#aa1f75cc", - PaletteDark = "#232323", - PaletteHighlightedText = "#e7e7e7", - PaletteToolTipBase = "#66000000", - PaletteToolTipText = "#e7e7e7", - PaletteLink = "#007af4", - PaletteLinkVisited = "#a57aff", - PaletteWindowDisabled = "#0A0A0A", - PaletteWindowTextDisabled = "#9DA9B5", - PaletteHighlightDisabled = "#9DA9B5", - PaletteHighlightedTextDisabled = "#9DA9B5", - PaletteBaseDisabled = "#000000", - PaletteTextDisabled = "#9DA9B5", - PaletteMid = "#FFFFFF", - PaletteLight = "#505050", - PaletteMidLight = "#00ff00" - } -} + return { + ["name"] = nameMap[lang] or nameMap.en_US, + ["style"] = "RedPandaDarkFusion", + ["default scheme"] = "MoLo CodeBlack", + ["default iconset"] = "newlook", + ["palette"] = { + PaletteWindow = "#000000", + PaletteWindowText = "#FFFFFF", + PaletteBase = "#141414", + PaletteAlternateBase = "#191919", + PaletteButton = "#141414", + PaletteButtonDisabled = "#141414", + PaletteBrightText = "#ff0000", + PaletteText = "#FFFFFF", + PaletteButtonText = "#FFFFFF", + PaletteButtonTextDisabled = "#9DA9B5", + PaletteHighlight = "#aa1f75cc", + PaletteDark = "#232323", + PaletteHighlightedText = "#e7e7e7", + PaletteToolTipBase = "#66000000", + PaletteToolTipText = "#e7e7e7", + PaletteLink = "#007af4", + PaletteLinkVisited = "#a57aff", + PaletteWindowDisabled = "#0A0A0A", + PaletteWindowTextDisabled = "#9DA9B5", + PaletteHighlightDisabled = "#9DA9B5", + PaletteHighlightedTextDisabled = "#9DA9B5", + PaletteBaseDisabled = "#000000", + PaletteTextDisabled = "#9DA9B5", + PaletteMid = "#FFFFFF", + PaletteLight = "#505050", + PaletteMidlight = "#00ff00", + }, + } +end diff --git a/RedPandaIDE/themes/random_light.lua b/RedPandaIDE/themes/random_light.lua index 34229dc7..1796d4f6 100644 --- a/RedPandaIDE/themes/random_light.lua +++ b/RedPandaIDE/themes/random_light.lua @@ -1,102 +1,132 @@ -function rgbFromString(color) - local r, g, b = color:match("#(%x%x)(%x%x)(%x%x)") - return {tonumber(r, 16) / 255, tonumber(g, 16) / 255, tonumber(b, 16) / 255} +function apiVersion() + return { + kind = "theme", + major = 0, + minor = 1, + } end -function rgbToString(rgb) - return string.format("#%02x%02x%02x", - math.floor(rgb[1] * 255), math.floor(rgb[2] * 255), math.floor(rgb[3] * 255) - ) + + + + + + + + + + + + +local function rgbFromString(color) + local r, g, b = color:match("#(%x%x)(%x%x)(%x%x)") + return { + r = tonumber(r, 16) / 255, + g = tonumber(g, 16) / 255, + b = tonumber(b, 16) / 255, + } end -function hsvToRgb(h, s, v) - local r, g, b - local i = math.floor(h * 6) - local f = h * 6 - i - local p = v * (1 - s) - local q = v * (1 - f * s) - local t = v * (1 - (1 - f) * s) - i = i % 6 - if i == 0 then - r, g, b = v, t, p - elseif i == 1 then - r, g, b = q, v, p - elseif i == 2 then - r, g, b = p, v, t - elseif i == 3 then - r, g, b = p, q, v - elseif i == 4 then - r, g, b = t, p, v - elseif i == 5 then - r, g, b = v, p, q - end - return {r, g, b} +local function rgbToString(rgb) + return string.format( + "#%02x%02x%02x", + math.floor(rgb.r * 255), + math.floor(rgb.g * 255), + math.floor(rgb.b * 255)) + end -function blend(lower, upper, alpha) - local r = (1 - alpha) * lower[1] + alpha * upper[1] - local g = (1 - alpha) * lower[2] + alpha * upper[2] - local b = (1 - alpha) * lower[3] + alpha * upper[3] - return {r, g, b} +local function hsvToRgb(hsv) + local r, g, b + local h, s, v = hsv.h, hsv.s, hsv.v + local i = math.floor(h * 6) + local f = h * 6 - i + local p = v * (1 - s) + local q = v * (1 - f * s) + local t = v * (1 - (1 - f) * s) + i = i % 6 + if i == 0 then + r, g, b = v, t, p + elseif i == 1 then + r, g, b = q, v, p + elseif i == 2 then + r, g, b = p, v, t + elseif i == 3 then + r, g, b = p, q, v + elseif i == 4 then + r, g, b = t, p, v + elseif i == 5 then + r, g, b = v, p, q + end + return { r = r, g = g, b = b } end -local hue = math.random() -local upperColor = hsvToRgb(hue, 0.6, 1) - -function transform(color) - local lowerColor = rgbFromString(color) - local blended = blend(lowerColor, upperColor, 0.1) - return rgbToString(blended) +local function blend(lower, upper, alpha) + local r = (1 - alpha) * lower.r + alpha * upper.r + local g = (1 - alpha) * lower.g + alpha * upper.g + local b = (1 - alpha) * lower.b + alpha * upper.b + return { r = r, g = g, b = b } end -function transformPalette(palette) - local transformed = {} - for key, value in pairs(palette) do - transformed[key] = transform(value) - end - return transformed +local function transform(color, upperColor) + local lowerColor = rgbFromString(color) + local blended = blend(lowerColor, upperColor, 0.1) + return rgbToString(blended) +end + +local function transformPalette(palette, upperColor) + local transformed = {} + for key, value in pairs(palette) do + transformed[key] = transform(value, upperColor) + end + return transformed end local originalPalette = { - PaletteWindow = "#efefef", - PaletteWindowText = "#000000", - PaletteBase = "#ffffff", - PaletteAlternateBase = "#f7f7f7", - PaletteToolTipBase = "#ffffdc", - PaletteToolTipText = "#000000", - PaletteText = "#000000", - PaletteButton = "#efefef", - PaletteButtonText = "#000000", - PaletteBrightText = "#ffffff", - PaletteLink = "#0000ff", - PaletteLinkVisited = "#ff00ff", - PaletteLight = "#ffffff", - PaletteMidLight = "#cacaca", - PaletteDark = "#9f9f9f", - PaletteMid = "#b8b8b8", - PaletteWindowDisabled = "#efefef", - PaletteWindowTextDisabled = "#bebebe", - PaletteBaseDisabled = "#efefef", - PaletteTextDisabled = "#bebebe", - PaletteButtonDisabled = "#efefef", - PaletteButtonTextDisabled = "#bebebe", - PaletteHighlight = "#dddddd", - PaletteHighlightedText = "#000000" + PaletteWindow = "#efefef", + PaletteWindowText = "#000000", + PaletteBase = "#ffffff", + PaletteAlternateBase = "#f7f7f7", + PaletteToolTipBase = "#ffffdc", + PaletteToolTipText = "#000000", + PaletteText = "#000000", + PaletteButton = "#efefef", + PaletteButtonText = "#000000", + PaletteBrightText = "#ffffff", + PaletteLink = "#0000ff", + PaletteLinkVisited = "#ff00ff", + PaletteLight = "#ffffff", + PaletteMidlight = "#cacaca", + PaletteDark = "#9f9f9f", + PaletteMid = "#b8b8b8", + PaletteWindowDisabled = "#efefef", + PaletteWindowTextDisabled = "#bebebe", + PaletteBaseDisabled = "#efefef", + PaletteTextDisabled = "#bebebe", + PaletteButtonDisabled = "#efefef", + PaletteButtonTextDisabled = "#bebebe", + PaletteHighlight = "#dddddd", + PaletteHighlightedText = "#000000", } local nameMap = { - en_US = "Random Light", - pt_BR = "Clara aleatória", - zh_CN = "随机浅色", - zh_TW = "隨機淺色" + en_US = "Random Light", + pt_BR = "Clara aleatória", + zh_CN = "随机浅色", + zh_TW = "隨機淺色", } -local lang = C_Desktop.language() +function main() + local hue = math.random() + local upperColor = hsvToRgb({ h = hue, s = 0.6, v = 1 }) -return { - ["name"] = nameMap[lang] or nameMap.en_US, - ["style"] = "RedPandaLightFusion", - ["default scheme"] = "Adaptive", - ["default iconset"] = "newlook", - ["palette"] = transformPalette(originalPalette) -} + local lang = C_Desktop.language() + + return { + ["name"] = nameMap[lang] or nameMap.en_US, + ["style"] = "RedPandaLightFusion", + ["default scheme"] = "Adaptive", + ["default iconset"] = "newlook", + ["palette"] = transformPalette(originalPalette, upperColor), + } +end diff --git a/RedPandaIDE/themes/system.lua b/RedPandaIDE/themes/system.lua index 2fd0173c..ea661869 100644 --- a/RedPandaIDE/themes/system.lua +++ b/RedPandaIDE/themes/system.lua @@ -1,26 +1,49 @@ -local desktopEnvironment = C_Desktop.desktopEnvironment() -local useSystemStyle = desktopEnvironment == "xdg" or desktopEnvironment == "macos" - -local systemAppMode = C_Desktop.systemAppMode() -local isDarkMode = systemAppMode == "dark" - -function getStyle() - if useSystemStyle then - return C_Desktop.systemStyle() - else - if isDarkMode then - return "RedPandaDarkFusion" - else - return "RedPandaLightFusion" - end - end +function apiVersion() + return { + kind = "theme", + major = 0, + minor = 1, + } end -function getPalette() - if useSystemStyle then - return {} - elseif isDarkMode then - return { -- palette from `dark.lua` +local nameMap = { + en_US = "System Style and Color", + pt_BR = "Estilo e Cor do Sistema", + zh_CN = "跟随系统样式和颜色", + zh_TW = "跟隨系統樣式和顏色", +} + +local nameMapNoStyle = { + en_US = "System Color", + pt_BR = "Cor do Sistema", + zh_CN = "跟随系统颜色", + zh_TW = "跟隨系統顏色", +} + +function main() + local desktopEnvironment = C_Desktop.desktopEnvironment() + local useSystemStyle = desktopEnvironment == "xdg" or desktopEnvironment == "macos" + + local systemAppMode = C_Desktop.systemAppMode() + local isDarkMode = systemAppMode == "dark" + + local function getStyle() + if useSystemStyle then + return C_Desktop.systemStyle() + else + if isDarkMode then + return "RedPandaDarkFusion" + else + return "RedPandaLightFusion" + end + end + end + + local function getPalette() + if useSystemStyle then + return {} + elseif isDarkMode then + return { PaletteWindow = "#19232D", PaletteWindowText = "#E0E1E3", PaletteBase = "#1E1E1E", @@ -46,10 +69,10 @@ function getPalette() PaletteTextDisabled = "#9DA9B5", PaletteMid = "#707070", PaletteLight = "#505050", - PaletteMidLight = "#00ff00" - } - else - return { -- palette from `default.lua` + PaletteMidlight = "#00ff00", + } + else + return { PaletteWindow = "#efefef", PaletteWindowText = "#000000", PaletteBase = "#ffffff", @@ -63,7 +86,7 @@ function getPalette() PaletteLink = "#0000ff", PaletteLinkVisited = "#ff00ff", PaletteLight = "#ffffff", - PaletteMidLight = "#cacaca", + PaletteMidlight = "#cacaca", PaletteDark = "#9f9f9f", PaletteMid = "#b8b8b8", PaletteWindowDisabled = "#efefef", @@ -73,31 +96,18 @@ function getPalette() PaletteButtonDisabled = "#efefef", PaletteButtonTextDisabled = "#bebebe", PaletteHighlight = "#dddddd", - PaletteHighlightedText = "#000000" - } - end + PaletteHighlightedText = "#000000", + } + end + end + + local lang = C_Desktop.language() + + return { + ["name"] = useSystemStyle and (nameMap[lang] or nameMap.en_US) or (nameMapNoStyle[lang] or nameMapNoStyle.en_US), + ["style"] = getStyle(), + ["default scheme"] = "Adaptive", + ["default iconset"] = "newlook", + ["palette"] = getPalette(), + } end - -local nameMap = { - en_US = "System Style and Color", - pt_BR = "Estilo e Cor do Sistema", - zh_CN = "跟随系统样式和颜色", - zh_TW = "跟隨系統樣式和顏色" -} - -local nameMapNoStyle = { - en_US = "System Color", - pt_BR = "Cor do Sistema", - zh_CN = "跟随系统颜色", - zh_TW = "跟隨系統顏色" -} - -local lang = C_Desktop.language() - -return { - ["name"] = useSystemStyle and (nameMap[lang] or nameMap.en_US) or (nameMapNoStyle[lang] or nameMapNoStyle.en_US), - ["style"] = getStyle(), - ["default scheme"] = "Adaptive", - ["default iconset"] = "newlook", - ["palette"] = getPalette() -} diff --git a/RedPandaIDE/translations/RedPandaIDE_pt_BR.ts b/RedPandaIDE/translations/RedPandaIDE_pt_BR.ts index c4a20cce..b59607d6 100644 --- a/RedPandaIDE/translations/RedPandaIDE_pt_BR.ts +++ b/RedPandaIDE/translations/RedPandaIDE_pt_BR.ts @@ -7110,6 +7110,10 @@ Failed to detect terminal arguments pattern for “%1”. + + Error executing platform compiler hint add-on + + RegisterModel diff --git a/RedPandaIDE/translations/RedPandaIDE_zh_CN.ts b/RedPandaIDE/translations/RedPandaIDE_zh_CN.ts index 300038ea..783ab04d 100644 --- a/RedPandaIDE/translations/RedPandaIDE_zh_CN.ts +++ b/RedPandaIDE/translations/RedPandaIDE_zh_CN.ts @@ -9679,6 +9679,10 @@ p, li { white-space: pre-wrap; } Failed to detect terminal arguments pattern for “%1”. 无法检测适用于 “%1” 的终端参数模式。 + + Error executing platform compiler hint add-on + 执行平台编译器提示附加组件错误 + RegisterModel diff --git a/RedPandaIDE/translations/RedPandaIDE_zh_TW.ts b/RedPandaIDE/translations/RedPandaIDE_zh_TW.ts index c1d05c7b..ab815827 100644 --- a/RedPandaIDE/translations/RedPandaIDE_zh_TW.ts +++ b/RedPandaIDE/translations/RedPandaIDE_zh_TW.ts @@ -6622,6 +6622,10 @@ Failed to detect terminal arguments pattern for “%1”. + + Error executing platform compiler hint add-on + + RegisterModel diff --git a/RedPandaIDE/utils.h b/RedPandaIDE/utils.h index 80f51d06..bd13564c 100644 --- a/RedPandaIDE/utils.h +++ b/RedPandaIDE/utils.h @@ -176,4 +176,8 @@ QString defaultShell(); QString appArch(); QString osArch(); +#ifdef _MSC_VER +#define __builtin_unreachable() (__assume(0)) +#endif + #endif // UTILS_H diff --git a/addon/compiler_hint/archlinux.tl b/addon/compiler_hint/archlinux.tl new file mode 100644 index 00000000..94518103 --- /dev/null +++ b/addon/compiler_hint/archlinux.tl @@ -0,0 +1,313 @@ +global function apiVersion(): ApiVersion + return { + kind = "compiler_hint", + major = 0, + minor = 1, + } +end + +local nameMap: {string:{string:string}} = { + systemGcc = { + en_US = "System GCC", + pt_BR = "GCC do sistema", + zh_CN = "系统 GCC", + zh_TW = "系統 GCC", + }, + systemClang = { + en_US = "System Clang", + pt_BR = "Clang do sistema", + zh_CN = "系统 Clang", + zh_TW = "系統 Clang", + }, + multilibGcc = { + en_US = "Multilib GCC", + pt_BR = "GCC multilib", + zh_CN = "Multilib GCC", + zh_TW = "Multilib GCC", + }, + multilibClang = { + en_US = "Multilib Clang", + pt_BR = "Clang multilib", + zh_CN = "Multilib Clang", + zh_TW = "Multilib Clang", + }, + crossGcc = { + en_US = "Cross GCC", + pt_BR = "GCC cruzado", + zh_CN = "交叉编译 GCC", + zh_TW = "交叉編譯 GCC", + }, + mingwGcc = { + en_US = "MinGW GCC", + pt_BR = "GCC MinGW", + zh_CN = "MinGW GCC", + zh_TW = "MinGW GCC", + }, + mingwClang = { + en_US = "MinGW Clang", + pt_BR = "Clang MinGW", + zh_CN = "MinGW Clang", + zh_TW = "MinGW Clang", + }, + release = { + en_US = ", release", + pt_BR = ", lançamento", + zh_CN = ",发布", + zh_TW = ",發佈", + }, + debug = { + en_US = ", debug", + pt_BR = ", depuração", + zh_CN = ",调试", + zh_TW = ",偵錯", + }, + debugWithAsan = { + en_US = ", debug with ASan", + pt_BR = ", depuração com ASan", + zh_CN = ",ASan 调试", + zh_TW = ",ASan 偵錯", + }, +} + +local function mergeCompilerSet(compilerSet: CompilerHint.CompilerSet, other: CompilerHint.CompilerSet) + local c = compilerSet as {string:any} + local o = other as {string:any} + for k, v in pairs(o) do + c[k] = v + end +end + +local record Config + isMultilib: boolean | nil + isMingw: boolean | nil + isClang: boolean | nil + customCompileParams: {string} | nil + customLinkParams: {string} | nil + triplet: string | nil +end + +local function generateConfig( + name: string, lang: string, + cCompiler: string, cxxCompiler: string, + config: Config +): CompilerHint.CompilerSet, CompilerHint.CompilerSet, CompilerHint.CompilerSet + local commonOptions: CompilerHint.CompilerSet = { + cCompiler = cCompiler, + cxxCompiler = cxxCompiler, + debugger = "/usr/bin/gdb", + debugServer = "/usr/bin/gdbserver", + make = "/usr/bin/make", + compilerType = config.isClang and "Clang" or "GCC_UTF8", + preprocessingSuffix = ".i", + compilationProperSuffix = ".s", + assemblingSuffix = ".o", + executableSuffix = config.isMingw and ".exe" or "", + compilationStage = 3, + ccCmdOptUsePipe = "on", + ccCmdOptWarningAll = "on", + ccCmdOptWarningExtra = "on", + ccCmdOptCheckIsoConformance = "on", + binDirs = {"/usr/bin"}, + } + if config.isMultilib then + commonOptions.ccCmdOptPointerSize = "32" + end + if config.isMingw then + commonOptions.resourceCompiler = "/usr/bin/" .. config.triplet .. "-windres" + end + if config.customCompileParams then + commonOptions.customCompileParams = config.customCompileParams + end + if config.customLinkParams then + commonOptions.customLinkParams = config.customLinkParams + end + local release = { + name = name .. (nameMap.release[lang] or nameMap.release.en_US), + staticLink = true, + linkCmdOptStripExe = "on", + ccCmdOptOptimize = "2", + } + local debug_ = { + name = name .. (nameMap.debug[lang] or nameMap.debug.en_US), + ccCmdOptDebugInfo = "on", + } + local debugWithAsan = { + name = name .. (nameMap.debugWithAsan[lang] or nameMap.debugWithAsan.en_US), + ccCmdOptDebugInfo = "on", + ccCmdOptAddressSanitizer = "address", + } + mergeCompilerSet(release, commonOptions) + mergeCompilerSet(debug_, commonOptions) + mergeCompilerSet(debugWithAsan, commonOptions) + return release, debug_, debugWithAsan +end + +global function main(): CompilerHint + local arch = C_System.osArch() + local libexecDir = C_System.appLibexecDir() + local lang = C_Desktop.language() + + local compilerList = {} + + do + local release, debug_, debugWithAsan = generateConfig( + nameMap.systemGcc[lang] or nameMap.systemGcc.en_US, lang, + "/usr/bin/gcc", "/usr/bin/g++", + {} + ) + table.insert(compilerList, release) + table.insert(compilerList, debug_) + table.insert(compilerList, debugWithAsan) + end + + if C_FileSystem.isExecutable("/usr/bin/clang") then + local release, debug_, debugWithAsan = generateConfig( + nameMap.systemClang[lang] or nameMap.systemClang.en_US, lang, + "/usr/bin/clang", "/usr/bin/clang++", + {isClang = true} + ) + table.insert(compilerList, release) + table.insert(compilerList, debug_) + table.insert(compilerList, debugWithAsan) + end + + -- with lib32-gcc-libs installed, system GCC and Clang can target 32-bit + if arch == "x86_64" and C_FileSystem.isExecutable("/usr/lib32/libstdc++.so") then + do + local release, debug_, debugWithAsan = generateConfig( + nameMap.multilibGcc[lang] or nameMap.multilibGcc.en_US, lang, + "/usr/bin/gcc", "/usr/bin/g++", + {isMultilib = true} + ) + table.insert(compilerList, release) + table.insert(compilerList, debug_) + table.insert(compilerList, debugWithAsan) + end + + if C_FileSystem.isExecutable("/usr/bin/clang") then + local release, debug_, debugWithAsan = generateConfig( + nameMap.multilibClang[lang] or nameMap.multilibClang.en_US, lang, + "/usr/bin/clang", "/usr/bin/clang++", + {isClang = true, isMultilib = true} + ) + table.insert(compilerList, release) + table.insert(compilerList, debug_) + table.insert(compilerList, debugWithAsan) + end + end + + -- cross GCC + if ( + arch == "x86_64" and + C_FileSystem.exists("/proc/sys/fs/binfmt_misc/qemu-aarch64") and + C_FileSystem.isExecutable("/usr/bin/aarch64-linux-gnu-gcc") + ) then + local release, _, _ = generateConfig( + (nameMap.crossGcc[lang] or nameMap.crossGcc.en_US) .. " aarch64", lang, + "/usr/bin/aarch64-linux-gnu-gcc", "/usr/bin/aarch64-linux-gnu-g++", + {} + ) + table.insert(compilerList, release) + end + + -- with wine or WSL init registered in binfmt_misc, Windows binaries can run seamlessly + if ( + arch == "x86_64" and ( + C_FileSystem.exists("/proc/sys/fs/binfmt_misc/DOSWin") or + C_FileSystem.exists("/proc/sys/fs/binfmt_misc/WSLInterop") + ) + ) then + if C_FileSystem.isExecutable("/usr/bin/x86_64-w64-mingw32-gcc") then + local extraObjects = { + utf8init = libexecDir .. "/x86_64-w64-mingw32/utf8init.o", + utf8manifest = libexecDir .. "/x86_64-w64-mingw32/utf8manifest.o", + } + + do + local release, _, _ = generateConfig( + (nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " x86_64", lang, + "/usr/bin/x86_64-w64-mingw32-gcc", "/usr/bin/x86_64-w64-mingw32-g++", + { + isMingw = true, + triplet = "x86_64-w64-mingw32", + customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest}, + } + ) + table.insert(compilerList, release) + end + + -- system Clang can target Windows + if C_FileSystem.isExecutable("/usr/bin/clang") then + local release, _, _ = generateConfig( + (nameMap.mingwClang[lang] or nameMap.mingwClang.en_US) .. " x86_64", lang, + "/usr/bin/clang", "/usr/bin/clang++", + { + isClang = true, + isMingw = true, + triplet = "x86_64-w64-mingw32", + customCompileParams = {"-target", "x86_64-w64-mingw32"}, + customLinkParams = { + "-target", "x86_64-w64-mingw32", + extraObjects.utf8init, extraObjects.utf8manifest, + "-lstdc++", "-lwinpthread", + }, + } + ) + table.insert(compilerList, release) + end + end + + if C_FileSystem.isExecutable("/usr/bin/i686-w64-mingw32-gcc") then + local extraObjects = { + utf8init = libexecDir .. "/i686-w64-mingw32/utf8init.o", + utf8manifest = libexecDir .. "/i686-w64-mingw32/utf8manifest.o", + } + + do + local release, _, _ = generateConfig( + (nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " i686", lang, + "/usr/bin/i686-w64-mingw32-gcc", "/usr/bin/i686-w64-mingw32-g++", + { + isMingw = true, + triplet = "i686-w64-mingw32", + customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest}, + } + ) + table.insert(compilerList, release) + end + + -- system Clang can target Windows + if C_FileSystem.isExecutable("/usr/bin/clang") then + local release, _, _ = generateConfig( + (nameMap.mingwClang[lang] or nameMap.mingwClang.en_US) .. " i686", lang, + "/usr/bin/clang", "/usr/bin/clang++", + { + isClang = true, + isMingw = true, + triplet = "i686-w64-mingw32", + customCompileParams = {"-target", "i686-w64-mingw32"}, + customLinkParams = { + "-target", "i686-w64-mingw32", + extraObjects.utf8init, extraObjects.utf8manifest, + "-lstdc++", "-lwinpthread", + }, + } + ) + table.insert(compilerList, release) + end + end + end + + local result = { + compilerList = compilerList, + noSearch = { + "/usr/bin", + "/opt/cuda/bin", + "/usr/lib/ccache/bin", + }, + preferCompiler = 3, -- System GCC Debug with ASan + } + + return result + +end diff --git a/addon/compiler_hint/windows_domain.tl b/addon/compiler_hint/windows_domain.tl new file mode 100644 index 00000000..9c818414 --- /dev/null +++ b/addon/compiler_hint/windows_domain.tl @@ -0,0 +1,430 @@ +global function apiVersion(): ApiVersion + return { + kind = "compiler_hint", + major = 0, + minor = 1, + } +end + +local gnuArchMap: {string:string} = { + i386 = "i686", + x86_64 = "x86_64", + arm = "armv7", + arm64 = "aarch64", +} + +local profileNameMap: {string:{string:string}} = { + release = { + en_US = "release", + pt_BR = "lançamento", + zh_CN = "发布", + zh_TW = "發佈", + }, + debug = { + en_US = "debug", + pt_BR = "depuração", + zh_CN = "调试", + zh_TW = "偵錯", + }, + debugWithAsan = { + en_US = "debug with ASan", + pt_BR = "depuração com ASan", + zh_CN = "ASan 调试", + zh_TW = "ASan 偵錯", + }, +} + +local function nameGeneratorMingwGcc(lang: string, arch: string, profile: string, isUtf8: boolean): string + local template: {string:string} = { + en_US = "MinGW GCC %1 in %2, %3", + pt_BR = "GCC MinGW %1 em %2, %3", + zh_CN = "%2 MinGW GCC %1,%3", + zh_TW = "%2 MinGW GCC %1,%3", + } + local systemCodePage: {string:string} = { + en_US = "system code page", + pt_Br = "página de código do sistema", + zh_CN = "系统代码页", + zh_TW = "系統代碼頁", + } + return C_Util.format( + template[lang] or template.en_US, + gnuArchMap[arch], + isUtf8 and "UTF-8" or systemCodePage[lang] or systemCodePage.en_US, + profileNameMap[profile][lang] or profileNameMap[profile].en_US + ) +end + +local function nameGeneratorClang(lang: string, arch: string, profile: string, isMingw: boolean): string + local template: {string:string} = { + en_US = "%1 Clang %2, %3", + pt_BR = "Clang %2 %1, %3", + zh_CN = "%1 Clang %2,%3", + zh_TW = "%1 Clang %2,%3", + } + local msvcCompatible: {string:string} = { + en_US = "MSVC-compatible", + pt_BR = "compatível com MSVC", + zh_CN = "兼容 MSVC 的", + zh_TW = "相容 MSVC 的", + } + return C_Util.format( + template[lang] or template.en_US, + isMingw and "LLVM-MinGW" or msvcCompatible[lang] or msvcCompatible.en_US, + gnuArchMap[arch], + profileNameMap[profile][lang] or profileNameMap[profile].en_US + ) +end + +local function mergeCompilerSet(compilerSet: CompilerHint.CompilerSet, other: CompilerHint.CompilerSet) + local c = compilerSet as {string:any} + local o = other as {string:any} + for k, v in pairs(o) do + c[k] = v + end +end + +local record Programs + cCompiler: string + cxxCompiler: string + make: string + debugger: string + debugServer: string + resourceCompiler: string + binDirs: {string} + libDirs: {string} | nil +end + +local record Config + arch: string + isAnsi: boolean | nil + isClang: boolean | nil + customCompileParams: {string} | nil + customLinkParams: {string} | nil +end + +local function generateConfig( + nameGen: (function (arch: string, profile: string): string), + programs: Programs, + config: Config +): CompilerHint.CompilerSet, CompilerHint.CompilerSet, CompilerHint.CompilerSet + local commonOptions : CompilerHint.CompilerSet = { + cCompiler = programs.cCompiler, + cxxCompiler = programs.cxxCompiler, + debugger = programs.debugger, + debugServer = programs.debugServer, + make = programs.make, + resourceCompiler = programs.resourceCompiler, + binDirs = programs.binDirs, + compilerType = config.isClang and "Clang" or "GCC_UTF8", + preprocessingSuffix = ".i", + compilationProperSuffix = ".s", + assemblingSuffix = ".o", + executableSuffix = ".exe", + compilationStage = 3, + ccCmdOptUsePipe = "on", + ccCmdOptWarningAll = "on", + ccCmdOptWarningExtra = "on", + ccCmdOptCheckIsoConformance = "on", + } + if programs.libDirs then + commonOptions.libDirs = programs.libDirs + end + if config.isAnsi then + commonOptions.execCharset = "SYSTEM" + end + if config.customCompileParams then + commonOptions.customCompileParams = config.customCompileParams + end + if config.customLinkParams then + commonOptions.customLinkParams = config.customLinkParams + end + local release: CompilerHint.CompilerSet = { + name = nameGen(config.arch, "release"), + staticLink = true, + linkCmdOptStripExe = "on", + ccCmdOptOptimize = "2", + } + local debug_: CompilerHint.CompilerSet = { + name = nameGen(config.arch, "debug"), + ccCmdOptDebugInfo = "on", + } + local debugWithAsan: CompilerHint.CompilerSet = { + name = nameGen(config.arch, "debugWithAsan"), + ccCmdOptDebugInfo = "on", + ccCmdOptAddressSanitizer = "address", + } + mergeCompilerSet(release, commonOptions) + mergeCompilerSet(debug_, commonOptions) + mergeCompilerSet(debugWithAsan, commonOptions) + return release, debug_, debugWithAsan +end + +global function main(): CompilerHint + local appArch = C_System.appArch() + local libexecDir = C_System.appLibexecDir() + local lang = C_Desktop.language() + local supportedAppArches = C_System.supportedAppArchList() + + local compilerList = {} + local noSearch = {} + local preferCompiler = 0 + + local function checkAndAddMingw(arch: string) + local binDir: string + local libDir: string + local excludeBinDir: string + if arch == "i386" then + binDir = libexecDir .. "/mingw32/bin" -- must match case because Windows filesystem can be case sensitive + libDir = libexecDir .. "/mingw32/i686-w64-mingw32/lib" + excludeBinDir = libexecDir .. "/MinGW32/bin" -- workaround for path check + elseif arch == "x86_64" then + binDir = libexecDir .. "/mingw64/bin" + libDir = libexecDir .. "/mingw64/x86_64-w64-mingw32/lib" + excludeBinDir = libexecDir .. "/MinGW64/bin" + else + return + end + + if not C_FileSystem.isExecutable(binDir .. "/gcc.exe") then + return + end + + local programs: Programs = { + cCompiler = binDir .. "/gcc.exe", + cxxCompiler = binDir .. "/g++.exe", + make = binDir .. "/mingw32-make.exe", + debugger = binDir .. "/gdb.exe", + debugServer = binDir .. "/gdbserver.exe", + resourceCompiler = binDir .. "/windres.exe", + binDirs = {binDir}, + } + local extraObjects = { + utf8init = libDir .. "/utf8init.o", + utf8manifest = libDir .. "/utf8manifest.o", + } + + local release, debug_, debugWithAsan = generateConfig( + function (arch_: string, profile: string): string + return nameGeneratorMingwGcc(lang, arch_, profile, true) + end, + programs, + { + arch = arch, + customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest}, + } + ) + table.insert(compilerList, release) + table.insert(compilerList, debug_) + if preferCompiler == 0 then + preferCompiler = 2 + end + + release, debug_, debugWithAsan = generateConfig( + function (arch_: string, profile: string): string + return nameGeneratorMingwGcc(lang, arch_, profile, false) + end, + programs, + { + arch = arch, + isAnsi = true, + } + ) + table.insert(compilerList, release) + table.insert(compilerList, debug_) + + table.insert(noSearch, excludeBinDir) + end + + local function checkAndAddClang() + if not C_FileSystem.isExecutable(libexecDir .. "/llvm-mingw/bin/clang.exe") then + return + end + + local binDir = libexecDir .. "/llvm-mingw/bin" + local appTriplet = gnuArchMap[appArch] .. "-w64-mingw32" + do + -- appArch is always debuggable + local libDir = libexecDir .. "/llvm-mingw/" .. appTriplet .. "/lib" + local programs: Programs = { + cCompiler = binDir .. "/" .. appTriplet .. "-clang.exe", + cxxCompiler = binDir .. "/" .. appTriplet .. "-clang++.exe", + make = binDir .. "/mingw32-make.exe", + debugger = binDir .. "/lldb-mi.exe", + debugServer = binDir .. "/lldb-server.exe", + resourceCompiler = binDir .. "/" .. appTriplet .. "-windres.exe", + binDirs = {binDir}, + } + local extraObjects = { + utf8init = libDir .. "/utf8init.o", + utf8manifest = libDir .. "/utf8manifest.o", + } + local release, debug_, debugWithAsan = generateConfig( + function (arch_: string, profile: string): string + return nameGeneratorClang(lang, arch_, profile, true) + end, + programs, + { + arch = appArch, + customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest}, + isClang = true, + } + ) + table.insert(compilerList, release) + table.insert(compilerList, debug_) + if appArch ~= "arm64" then + table.insert(compilerList, debugWithAsan) + if preferCompiler == 0 then + preferCompiler = 3 + end + else + if preferCompiler == 0 then + preferCompiler = 2 + end + end + end + + for _, foreignArch in ipairs(supportedAppArches) do + if foreignArch ~= appArch then + local foreignTriplet = gnuArchMap[foreignArch] .. "-w64-mingw32" + local libDir = libexecDir .. "/llvm-mingw/" .. foreignTriplet .. "/lib" + local programs: Programs = { + cCompiler = binDir .. "/" .. foreignTriplet .. "-clang.exe", + cxxCompiler = binDir .. "/" .. foreignTriplet .. "-clang++.exe", + make = binDir .. "/mingw32-make.exe", + debugger = binDir .. "/lldb-mi.exe", + debugServer = binDir .. "/lldb-server.exe", + resourceCompiler = binDir .. "/" .. foreignTriplet .. "-windres.exe", + binDirs = {binDir}, + } + local extraObjects = { + utf8init = libDir .. "/utf8init.o", + utf8manifest = libDir .. "/utf8manifest.o", + } + local release, _, _ = generateConfig( + function (arch_: string, profile: string): string + return nameGeneratorClang(lang, arch_, profile, true) + end, + programs, + { + arch = foreignArch, + customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest}, + isClang = true, + } + ) + table.insert(compilerList, release) + end + end + + table.insert(noSearch, binDir) + + local llvmOrgPath = C_System.readRegistry([[Software\LLVM\LLVM]], "") or C_System.readRegistry([[Software\Wow6432Node\LLVM\LLVM]], "") + if not llvmOrgPath then + return + end + local llvmOrgBinDir = llvmOrgPath .. "/bin" + + do + local msvcTriplet = gnuArchMap[appArch] .. "-pc-windows-msvc" + local libDir = libexecDir .. "/llvm-mingw/" .. msvcTriplet .. "/lib" + local programs: Programs = { + cCompiler = llvmOrgBinDir .. "/clang.exe", + cxxCompiler = llvmOrgBinDir .. "/clang++.exe", + make = binDir .. "/mingw32-make.exe", + debugger = binDir .. "/lldb-mi.exe", + debugServer = binDir .. "/lldb-server.exe", + resourceCompiler = binDir .. "/" .. appTriplet .. "-windres.exe", + binDirs = {llvmOrgBinDir}, + libDirs = {libDir}, + } + local extraObjects = { + utf8init = libDir .. "/utf8init.o", + utf8manifest = libDir .. "/utf8manifest.o", + } + local release, debug_, _ = generateConfig( + function (arch: string, profile: string): string + return nameGeneratorClang(lang, arch, profile, false) + end, + programs, + { + arch = appArch, + customCompileParams = { + "-target", msvcTriplet, + "-fms-extensions", + "-fms-compatibility", + "-fdelayed-template-parsing", + }, + customLinkParams = { + "-target", msvcTriplet, + extraObjects.utf8init, extraObjects.utf8manifest, + }, + isClang = true, + } + ) + table.insert(compilerList, release) + table.insert(compilerList, debug_) + end + + for _, foreignArch in ipairs(supportedAppArches) do + if foreignArch ~= appArch then + local foreignTriplet = gnuArchMap[foreignArch] .. "-w64-mingw32" + local msvcTriplet = gnuArchMap[foreignArch] .. "-pc-windows-msvc" + local libDir = libexecDir .. "/llvm-mingw/" .. msvcTriplet .. "/lib" + local programs: Programs = { + cCompiler = llvmOrgBinDir .. "/clang.exe", + cxxCompiler = llvmOrgBinDir .. "/clang++.exe", + make = binDir .. "/mingw32-make.exe", + debugger = binDir .. "/lldb-mi.exe", + debugServer = binDir .. "/lldb-server.exe", + resourceCompiler = binDir .. "/" .. foreignTriplet .. "-windres.exe", + binDirs = {llvmOrgBinDir}, + libDirs = {libDir}, + } + local extraObjects = { + utf8init = libDir .. "/utf8init.o", + utf8manifest = libDir .. "/utf8manifest.o", + } + local release, _, _ = generateConfig( + function (arch: string, profile: string): string + return nameGeneratorClang(lang, arch, profile, false) + end, + programs, + { + arch = foreignArch, + customCompileParams = { + "-target", msvcTriplet, + "-fms-extensions", + "-fms-compatibility", + "-fdelayed-template-parsing", + }, + customLinkParams = { + "-target", msvcTriplet, + extraObjects.utf8init, extraObjects.utf8manifest, + }, + isClang = true, + } + ) + table.insert(compilerList, release) + end + end + table.insert(noSearch, llvmOrgBinDir) + end + + if appArch == "x86_64" then + checkAndAddMingw("x86_64") + checkAndAddClang() + elseif appArch == "arm64" then + checkAndAddClang() + else + checkAndAddMingw("i386") + checkAndAddClang() + end + + local result = { + compilerList = compilerList, + noSearch = noSearch, + preferCompiler = preferCompiler, + } + + return result +end diff --git a/addon/defs/compiler_hint.d.tl b/addon/defs/compiler_hint.d.tl new file mode 100644 index 00000000..09102e0b --- /dev/null +++ b/addon/defs/compiler_hint.d.tl @@ -0,0 +1,107 @@ +local env = require("defs.global_env") + +global C_Debug = env.C_Debug +global C_Desktop = env.C_Desktop +global C_FileSystem = env.C_FileSystem +global C_System = env.C_System +global C_Util = env.C_Util + +global record CompilerHint + -- found compiler sets + compilerList: {CompilerSet} + + -- do not search in these directories anymore + noSearch: {string} + + -- prefer compiler set index (in Lua, 1-based) in compilerList + -- 0 for no preference + preferCompiler: integer + + record CompilerSet + name: string + + -- internal + + -- e.g. "x86_64-linux-gnu", "x86_64-w64-mingw32" + dumpMachine: string + -- e.g. "13.2.1", "17.0.6" + version: string + -- e.g. "TDM-GCC", "MinGW" + type: string + -- e.g. "x86_64", "aarch64" + target: string + compilerType: CompilerType + + -- general + + staticLink: boolean + -- automatically sets useCustomCompileParams + customCompileParams: {string} + -- automatically sets useCustomLinkParams + customLinkParams: {string} + -- automatically sets autoAddCharsetParams + execCharset: string + + -- setting: code generation + + ccCmdOptOptimize: string + ccCmdOptStd: string + cCmdOptStd: string + ccCmdOptInstruction: string + ccCmdOptPointerSize: string + ccCmdOptDebugInfo: string + ccCmdOptProfileInfo: string + ccCmdOptSyntaxOnly: string + + -- setting: warnings + + ccCmdOptInhibitAllWarning: string + ccCmdOptWarningAll: string + ccCmdOptWarningExtra: string + ccCmdOptCheckIsoConformance: string + ccCmdOptWarningAsError: string + ccCmdOptAbortOnError: string + ccCmdOptStackProtector: string + ccCmdOptAddressSanitizer: string + + -- setting: linker + + ccCmdOptUsePipe: string + linkCmdOptNoLinkStdlib: string + linkCmdOptNoConsole: string + linkCmdOptStripExe: string + + -- directory + + binDirs: {string} + cIncludeDirs: {string} + cxxIncludeDirs: {string} + libDirs: {string} + defaultLibDirs: {string} + defaultCIncludeDirs: {string} + defaultCxxIncludeDirs: {string} + + -- program + + cCompiler: string + cxxCompiler: string + make: string + debugger: string + debugServer: string + resourceCompiler: string + + -- output + + preprocessingSuffix: string + compilationProperSuffix: string + assemblingSuffix: string + executableSuffix: string + compilationStage: integer + + enum CompilerType + "GCC" + "GCC_UTF8" + "Clang" + end + end +end diff --git a/addon/defs/global_env.d.tl b/addon/defs/global_env.d.tl new file mode 100644 index 00000000..aa47edff --- /dev/null +++ b/addon/defs/global_env.d.tl @@ -0,0 +1,100 @@ +global record ApiVersion + kind: Kind + major: integer + minor: integer + + enum Kind + "theme" + "compiler_hint" + end +end + +local record env + record C_Debug + -- print message to console, with Qt-style string format + debug: function (format: string, ...: any): nil + + -- show message box, with Qt-style string format + messageBox: function (format: string, ...: any): nil + end + + record C_Desktop + -- return desktoop environment name + desktopEnvironment: function (): DesktopEnvironment + + -- return language code, e.g. "en_US", "zh_CN" + language: function (): string + + -- return available Qt styles, e.g. {"breeze", "fusion", "windows"} + qtStyleList: function (): {string} + + -- return system app mode, light or dark + systemAppMode: function (): AppMode + + -- return default Qt style, e.g. "fusion" + systemStyle: function (): string + + enum DesktopEnvironment + "windows" -- Windows Win32 + "macos" -- macOS + "xdg" -- XDG-compliant desktop environment (e.g. GNOME, KDE Plasma) + "unknown" -- other desktops or non-desktop environments (e.g. Windows UWP, Android) + end + + enum AppMode + "light" + "dark" + end + end + + record C_FileSystem + -- return whether the path exists + exists: function (path: string): boolean + + -- return whether the path is executable + isExecutable: function (path: string): boolean + end + + record C_System + -- returns the architecture of Red Panda C++, name following `QSysInfo` + -- e.g. "i386", "x86_64", "arm", "arm64", "riscv64", "loongarch64" + -- though unsupported, MSVC arm64ec is handled correctly (returns "arm64ec") + appArch: function (): string + + -- returns the directory of Red Panda C++ + -- e.g. "/usr/bin", "C:/Program Files/RedPanda-Cpp", "C:/Users/中文/AppData/Local/RedPanda-CPP" + appDir: function (): string + + -- returns the libexec directory of Red Panda C++ + -- e.g. "/usr/libexec/RedPandaCPP", "C:/Program Files/RedPanda-Cpp" + appLibexecDir: function (): string + + -- returns the resource directory of Red Panda C++ + -- e.g. "/usr/share/RedPandaCPP", "C:/Program Files/RedPanda-Cpp" + appResourceDir: function (): string + + -- returns the architecture of the OS, name following `QSysInfo` + -- e.g. "i386", "x86_64", "arm", "arm64" + -- Windows arm64 is handled correctly even if Red Panda C++ runs under emulation + osArch: function (): string + + -- returns supported application architectures by OS, name following `QSysInfo` + -- e.g. {"i386", "x86_64", "arm64"} + -- Windows 10 1709 or later: accurate result + -- Legacy Windows: hardcoded result, i.e. "i386" is always included even though WoW64 is not available + -- macOS: accurate result supposed, but not tested + -- Linux: osArch + appArch + QEMU user mode emulation, no multilib detection + -- other (BSD): osArch + appArch, no multilib detection + supportedAppArchList: function (): {string} + + -- read `subKey\name` from HKEY_CURRENT_USER and HKEY_LOCAL_MACHINE in order (Windows only) + readRegistry: function (subKey: string, name: string): string | nil + end + + record C_Util + -- Qt-style string format, replace %1, %2, etc. with arguments + format: function (format: string, ...: any): string + end +end + +return env diff --git a/addon/defs/theme.d.tl b/addon/defs/theme.d.tl new file mode 100644 index 00000000..0dca7fe6 --- /dev/null +++ b/addon/defs/theme.d.tl @@ -0,0 +1,63 @@ +local env = require("defs.global_env") + +global C_Debug = env.C_Debug +global C_Desktop = env.C_Desktop +global C_Util = env.C_Util + +global record Theme + name: string + style: string + ["default scheme"]: string + ["default iconset"]: BuiltInIconSet + palette: Palette + + enum BuiltInIconSet + "newlook" + "contrast" + "bluesky" + end + + record Palette + PaletteWindow: string | nil + PaletteWindowText: string | nil + PaletteBase: string | nil + PaletteAlternateBase: string | nil + PaletteToolTipBase: string | nil + PaletteToolTipText: string | nil + PaletteText: string | nil + PaletteButton: string | nil + PaletteButtonText: string | nil + PaletteBrightText: string | nil + PaletteHighlight: string | nil + PaletteHighlightedText: string | nil + PaletteLink: string | nil + PaletteLinkVisited: string | nil + + PaletteLight: string | nil + PaletteMidlight: string | nil + PaletteDark: string | nil + PaletteMid: string | nil + PaletteShadow: string | nil + + PaletteWindowDisabled: string | nil + PaletteWindowTextDisabled: string | nil + PaletteBaseDisabled: string | nil + PaletteAlternateBaseDisabled: string | nil + PaletteToolTipBaseDisabled: string | nil + PaletteToolTipTextDisabled: string | nil + PaletteTextDisabled: string | nil + PaletteButtonDisabled: string | nil + PaletteButtonTextDisabled: string | nil + PaletteBrightTextDisabled: string | nil + PaletteHighlightDisabled: string | nil + PaletteHighlightedTextDisabled: string | nil + PaletteLinkDisabled: string | nil + PaletteLinkVisitedDisabled: string | nil + + PaletteLightDisabled: string | nil + PaletteMidlightDisabled: string | nil + PaletteDarkDisabled: string | nil + PaletteMidDisabled: string | nil + PaletteShadowDisabled: string | nil + end +end diff --git a/addon/gen.sh b/addon/gen.sh new file mode 100755 index 00000000..2e19e91b --- /dev/null +++ b/addon/gen.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -euo pipefail + +function gen-theme() { + local file="$1" + local bn="$(basename $file)" + local out="RedPandaIDE/themes/${bn%.tl}.lua" + echo -e "\033[1;33mChecking $file\033[0m" + tl check --include-dir addon --global-env-def defs/theme --quiet "$file" + echo -e "\033[1;32mCompiling $file\033[0m" + tl gen --include-dir addon --global-env-def defs/theme --gen-compat off --gen-target 5.4 -o "$out" "$file" +} + +for file in addon/theme/*.tl; do + gen-theme "$file" +done + +function gen-compiler-hint() { + local file="$1" + local out="$2" + echo -e "\033[1;33mChecking $file\033[0m" + tl check --include-dir addon --global-env-def defs/compiler_hint --quiet "$file" + echo -e "\033[1;32mCompiling $file\033[0m" + tl gen --include-dir addon --global-env-def defs/compiler_hint --gen-compat off --gen-target 5.4 -o "$out" "$file" +} + +gen-compiler-hint addon/compiler_hint/windows_domain.tl packages/msys/domain/compiler_hint.lua +gen-compiler-hint addon/compiler_hint/archlinux.tl packages/archlinux/compiler_hint.lua diff --git a/addon/theme/contrast.tl b/addon/theme/contrast.tl new file mode 100644 index 00000000..4b8a0efe --- /dev/null +++ b/addon/theme/contrast.tl @@ -0,0 +1,53 @@ +global function apiVersion(): ApiVersion + return { + kind = "theme", + major = 0, + minor = 1, + } +end + +local nameMap: {string:string} = { + en_US = "Contrast", + pt_BR = "Contraste", + zh_CN = "高对比度", + zh_TW = "高對比度", +} + +global function main(): Theme + local lang = C_Desktop.language() + + return { + ["name"] = nameMap[lang] or nameMap.en_US, + ["style"] = "RedPandaDarkFusion", + ["default scheme"] = "Twilight", + ["default iconset"] = "contrast", + ["palette"] = { + PaletteWindow = "#000000", + PaletteWindowText = "#FFFFFF", + PaletteBase = "#141414", + PaletteAlternateBase = "#191919", + PaletteButton = "#141414", + PaletteButtonDisabled = "#141414", + PaletteBrightText = "#ff0000", + PaletteText = "#FFFFFF", + PaletteButtonText = "#FFFFFF", + PaletteButtonTextDisabled = "#9DA9B5", + PaletteHighlight = "#aa1f75cc", + PaletteDark = "#232323", + PaletteHighlightedText = "#e7e7e7", + PaletteToolTipBase = "#66000000", + PaletteToolTipText = "#e7e7e7", + PaletteLink = "#007af4", + PaletteLinkVisited = "#a57aff", + PaletteWindowDisabled = "#0A0A0A", + PaletteWindowTextDisabled = "#9DA9B5", + PaletteHighlightDisabled = "#9DA9B5", + PaletteHighlightedTextDisabled = "#9DA9B5", + PaletteBaseDisabled = "#000000", + PaletteTextDisabled = "#9DA9B5", + PaletteMid = "#FFFFFF", + PaletteLight = "#505050", + PaletteMidlight = "#00ff00", + }, + } +end diff --git a/addon/theme/dark.tl b/addon/theme/dark.tl new file mode 100644 index 00000000..ae097861 --- /dev/null +++ b/addon/theme/dark.tl @@ -0,0 +1,53 @@ +global function apiVersion(): ApiVersion + return { + kind = "theme", + major = 0, + minor = 1, + } +end + +local nameMap: {string:string} = { + en_US = "Dark", + pt_BR = "Escura", + zh_CN = "深色", + zh_TW = "深色", +} + +global function main(): Theme + local lang = C_Desktop.language() + + return { + ["name"] = nameMap[lang] or nameMap.en_US, + ["style"] = "RedPandaDarkFusion", + ["default scheme"] = "VS Code", + ["default iconset"] = "contrast", + ["palette"] = { + PaletteWindow = "#19232D", + PaletteWindowText = "#E0E1E3", + PaletteBase = "#1E1E1E", + PaletteAlternateBase = "#303030", + PaletteButton = "#19232D", + PaletteButtonDisabled = "#19232D", + PaletteBrightText = "#ff0000", + PaletteText = "#e7e7e7", + PaletteButtonText = "#d3d3d3", + PaletteButtonTextDisabled = "#9DA9B5", + PaletteHighlight = "#aa1f75cc", + PaletteDark = "#232323", + PaletteHighlightedText = "#e7e7e7", + PaletteToolTipBase = "#66000000", + PaletteToolTipText = "#e7e7e7", + PaletteLink = "#007af4", + PaletteLinkVisited = "#a57aff", + PaletteWindowDisabled = "#333333", + PaletteWindowTextDisabled = "#9DA9B5", + PaletteHighlightDisabled = "#26486B", + PaletteHighlightedTextDisabled = "#9DA9B5", + PaletteBaseDisabled = "#19232D", + PaletteTextDisabled = "#9DA9B5", + PaletteMid = "#707070", + PaletteLight = "#505050", + PaletteMidlight = "#00ff00", + }, + } +end diff --git a/addon/theme/default.tl b/addon/theme/default.tl new file mode 100644 index 00000000..3605bd57 --- /dev/null +++ b/addon/theme/default.tl @@ -0,0 +1,51 @@ +global function apiVersion(): ApiVersion + return { + kind = "theme", + major = 0, + minor = 1, + } +end + +local nameMap: {string:string} = { + en_US = "Light", + pt_BR = "Clara", + zh_CN = "浅色", + zh_TW = "淺色", +} + +global function main(): Theme + local lang = C_Desktop.language() + + return { + ["name"] = nameMap[lang] or nameMap.en_US, + ["style"] = "RedPandaLightFusion", + ["default scheme"] = "Intellij Classic", + ["default iconset"] = "newlook", + ["palette"] = { + PaletteWindow = "#efefef", + PaletteWindowText = "#000000", + PaletteBase = "#ffffff", + PaletteAlternateBase = "#f7f7f7", + PaletteToolTipBase = "#ffffdc", + PaletteToolTipText = "#000000", + PaletteText = "#000000", + PaletteButton = "#efefef", + PaletteButtonText = "#000000", + PaletteBrightText = "#ffffff", + PaletteLink = "#0000ff", + PaletteLinkVisited = "#ff00ff", + PaletteLight = "#ffffff", + PaletteMidlight = "#cacaca", + PaletteDark = "#9f9f9f", + PaletteMid = "#b8b8b8", + PaletteWindowDisabled = "#efefef", + PaletteWindowTextDisabled = "#bebebe", + PaletteBaseDisabled = "#efefef", + PaletteTextDisabled = "#bebebe", + PaletteButtonDisabled = "#efefef", + PaletteButtonTextDisabled = "#bebebe", + PaletteHighlight = "#dddddd", + PaletteHighlightedText = "#000000", + }, + } +end diff --git a/addon/theme/molo.tl b/addon/theme/molo.tl new file mode 100644 index 00000000..f09f68cc --- /dev/null +++ b/addon/theme/molo.tl @@ -0,0 +1,53 @@ +global function apiVersion(): ApiVersion + return { + kind = "theme", + major = 0, + minor = 1, + } +end + +local nameMap: {string:string} = { + en_US = "MoLo", + pt_BR = "Molo", + zh_CN = "墨落", + zh_TW = "墨落", +} + +global function main(): Theme + local lang = C_Desktop.language() + + return { + ["name"] = nameMap[lang] or nameMap.en_US, + ["style"] = "RedPandaDarkFusion", + ["default scheme"] = "MoLo CodeBlack", + ["default iconset"] = "newlook", + ["palette"] = { + PaletteWindow = "#000000", + PaletteWindowText = "#FFFFFF", + PaletteBase = "#141414", + PaletteAlternateBase = "#191919", + PaletteButton = "#141414", + PaletteButtonDisabled = "#141414", + PaletteBrightText = "#ff0000", + PaletteText = "#FFFFFF", + PaletteButtonText = "#FFFFFF", + PaletteButtonTextDisabled = "#9DA9B5", + PaletteHighlight = "#aa1f75cc", + PaletteDark = "#232323", + PaletteHighlightedText = "#e7e7e7", + PaletteToolTipBase = "#66000000", + PaletteToolTipText = "#e7e7e7", + PaletteLink = "#007af4", + PaletteLinkVisited = "#a57aff", + PaletteWindowDisabled = "#0A0A0A", + PaletteWindowTextDisabled = "#9DA9B5", + PaletteHighlightDisabled = "#9DA9B5", + PaletteHighlightedTextDisabled = "#9DA9B5", + PaletteBaseDisabled = "#000000", + PaletteTextDisabled = "#9DA9B5", + PaletteMid = "#FFFFFF", + PaletteLight = "#505050", + PaletteMidlight = "#00ff00", + }, + } +end diff --git a/addon/theme/random_light.tl b/addon/theme/random_light.tl new file mode 100644 index 00000000..517a365f --- /dev/null +++ b/addon/theme/random_light.tl @@ -0,0 +1,132 @@ +global function apiVersion(): ApiVersion + return { + kind = "theme", + major = 0, + minor = 1, + } +end + +local record Rgb + r: number + g: number + b: number +end + +local record Hsv + h: number + s: number + v: number +end + +local function rgbFromString(color: string): Rgb + local r, g, b = color:match("#(%x%x)(%x%x)(%x%x)") + return { + r = tonumber(r, 16) / 255, + g = tonumber(g, 16) / 255, + b = tonumber(b, 16) / 255, + } +end + +local function rgbToString(rgb: Rgb): string + return string.format( + "#%02x%02x%02x", + math.floor(rgb.r * 255), + math.floor(rgb.g * 255), + math.floor(rgb.b * 255) + ) +end + +local function hsvToRgb(hsv: Hsv): Rgb + local r, g, b: number, number, number + local h, s, v = hsv.h, hsv.s, hsv.v + local i = math.floor(h * 6) + local f = h * 6 - i + local p = v * (1 - s) + local q = v * (1 - f * s) + local t = v * (1 - (1 - f) * s) + i = i % 6 + if i == 0 then + r, g, b = v, t, p + elseif i == 1 then + r, g, b = q, v, p + elseif i == 2 then + r, g, b = p, v, t + elseif i == 3 then + r, g, b = p, q, v + elseif i == 4 then + r, g, b = t, p, v + elseif i == 5 then + r, g, b = v, p, q + end + return {r = r, g = g, b = b} +end + +local function blend(lower: Rgb, upper: Rgb, alpha: number): Rgb + local r = (1 - alpha) * lower.r + alpha * upper.r + local g = (1 - alpha) * lower.g + alpha * upper.g + local b = (1 - alpha) * lower.b + alpha * upper.b + return {r = r, g = g, b = b} +end + +local function transform(color: string, upperColor: Rgb): string + local lowerColor = rgbFromString(color) + local blended = blend(lowerColor, upperColor, 0.1) + return rgbToString(blended) +end + +local function transformPalette(palette: Theme.Palette, upperColor: Rgb): Theme.Palette + local transformed = {} + for key, value in pairs(palette as {string:string}) do + transformed[key] = transform(value, upperColor) + end + return transformed as Theme.Palette +end + +local originalPalette: Theme.Palette = { + PaletteWindow = "#efefef", + PaletteWindowText = "#000000", + PaletteBase = "#ffffff", + PaletteAlternateBase = "#f7f7f7", + PaletteToolTipBase = "#ffffdc", + PaletteToolTipText = "#000000", + PaletteText = "#000000", + PaletteButton = "#efefef", + PaletteButtonText = "#000000", + PaletteBrightText = "#ffffff", + PaletteLink = "#0000ff", + PaletteLinkVisited = "#ff00ff", + PaletteLight = "#ffffff", + PaletteMidlight = "#cacaca", + PaletteDark = "#9f9f9f", + PaletteMid = "#b8b8b8", + PaletteWindowDisabled = "#efefef", + PaletteWindowTextDisabled = "#bebebe", + PaletteBaseDisabled = "#efefef", + PaletteTextDisabled = "#bebebe", + PaletteButtonDisabled = "#efefef", + PaletteButtonTextDisabled = "#bebebe", + PaletteHighlight = "#dddddd", + PaletteHighlightedText = "#000000", +} + +local nameMap: {string:string} = { + en_US = "Random Light", + pt_BR = "Clara aleatória", + zh_CN = "随机浅色", + zh_TW = "隨機淺色", +} + +global function main(): Theme + local hue = math.random() + local upperColor = hsvToRgb({h = hue, s = 0.6, v = 1}) + + local lang = C_Desktop.language() + + return { + ["name"] = nameMap[lang] or nameMap.en_US, + ["style"] = "RedPandaLightFusion", + ["default scheme"] = "Adaptive", + ["default iconset"] = "newlook", + ["palette"] = transformPalette(originalPalette, upperColor), + } +end diff --git a/addon/theme/system.tl b/addon/theme/system.tl new file mode 100644 index 00000000..aecacdd3 --- /dev/null +++ b/addon/theme/system.tl @@ -0,0 +1,113 @@ +global function apiVersion(): ApiVersion + return { + kind = "theme", + major = 0, + minor = 1, + } +end + +local nameMap: {string:string} = { + en_US = "System Style and Color", + pt_BR = "Estilo e Cor do Sistema", + zh_CN = "跟随系统样式和颜色", + zh_TW = "跟隨系統樣式和顏色", +} + +local nameMapNoStyle: {string:string} = { + en_US = "System Color", + pt_BR = "Cor do Sistema", + zh_CN = "跟随系统颜色", + zh_TW = "跟隨系統顏色", +} + +global function main(): Theme + local desktopEnvironment = C_Desktop.desktopEnvironment() + local useSystemStyle = desktopEnvironment == "xdg" or desktopEnvironment == "macos" + + local systemAppMode = C_Desktop.systemAppMode() + local isDarkMode = systemAppMode == "dark" + + local function getStyle(): string + if useSystemStyle then + return C_Desktop.systemStyle() + else + if isDarkMode then + return "RedPandaDarkFusion" + else + return "RedPandaLightFusion" + end + end + end + + local function getPalette(): Theme.Palette + if useSystemStyle then + return {} + elseif isDarkMode then + return { -- palette from `dark.lua` + PaletteWindow = "#19232D", + PaletteWindowText = "#E0E1E3", + PaletteBase = "#1E1E1E", + PaletteAlternateBase = "#303030", + PaletteButton = "#19232D", + PaletteButtonDisabled = "#19232D", + PaletteBrightText = "#ff0000", + PaletteText = "#e7e7e7", + PaletteButtonText = "#d3d3d3", + PaletteButtonTextDisabled = "#9DA9B5", + PaletteHighlight = "#aa1f75cc", + PaletteDark = "#232323", + PaletteHighlightedText = "#e7e7e7", + PaletteToolTipBase = "#66000000", + PaletteToolTipText = "#e7e7e7", + PaletteLink = "#007af4", + PaletteLinkVisited = "#a57aff", + PaletteWindowDisabled = "#333333", + PaletteWindowTextDisabled = "#9DA9B5", + PaletteHighlightDisabled = "#26486B", + PaletteHighlightedTextDisabled = "#9DA9B5", + PaletteBaseDisabled = "#19232D", + PaletteTextDisabled = "#9DA9B5", + PaletteMid = "#707070", + PaletteLight = "#505050", + PaletteMidlight = "#00ff00", + } + else + return { -- palette from `default.lua` + PaletteWindow = "#efefef", + PaletteWindowText = "#000000", + PaletteBase = "#ffffff", + PaletteAlternateBase = "#f7f7f7", + PaletteToolTipBase = "#ffffdc", + PaletteToolTipText = "#000000", + PaletteText = "#000000", + PaletteButton = "#efefef", + PaletteButtonText = "#000000", + PaletteBrightText = "#ffffff", + PaletteLink = "#0000ff", + PaletteLinkVisited = "#ff00ff", + PaletteLight = "#ffffff", + PaletteMidlight = "#cacaca", + PaletteDark = "#9f9f9f", + PaletteMid = "#b8b8b8", + PaletteWindowDisabled = "#efefef", + PaletteWindowTextDisabled = "#bebebe", + PaletteBaseDisabled = "#efefef", + PaletteTextDisabled = "#bebebe", + PaletteButtonDisabled = "#efefef", + PaletteButtonTextDisabled = "#bebebe", + PaletteHighlight = "#dddddd", + PaletteHighlightedText = "#000000", + } + end + end + + local lang = C_Desktop.language() + + return { + ["name"] = useSystemStyle and (nameMap[lang] or nameMap.en_US) or (nameMapNoStyle[lang] or nameMapNoStyle.en_US), + ["style"] = getStyle(), + ["default scheme"] = "Adaptive", + ["default iconset"] = "newlook", + ["palette"] = getPalette() + } +end diff --git a/docs/addon.md b/docs/addon.md index 88c72552..7e69702d 100644 --- a/docs/addon.md +++ b/docs/addon.md @@ -14,28 +14,81 @@ - Red Panda C++ APIs exposed to add-on are organized by groups. - Each group is a Lua table. - Each API is a function in the table. - - Available API groups vary by add-on type. -- Localization is handled by add-on. e.g. - ```lua - local lang = C_Desktop.language() - local localizedName = { - en_US = "System", - pt_BR = "Sistema", - zh_CN = "系统", - zh_TW = "系統", - } - return { - name = localizedName[lang] or localizedName.en_US, - -- ... - } - ``` + - Available API groups vary by add-on kind. + - See [`env.d.tl`](../addon/defs/global_env.d.tl) for API definitions. +- Add-on can be executed only if the API version is compatible. +- Localization is handled by add-on. + +### API Version Check + +Add-on must implement `apiVersion` that takes no argument and returns `ApiVersion` ([`env.d.tl`](../addon/defs/global_env.d.tl)). + +Before execution, Red Panda C++ will call `apiVersion()` _without injecting any API group_[^1] to check whether the add-on is compatible with current Red Panda C++ (host). + +[^1]: Thus do not call any API (incl. Lua standard library) in `apiVersion` and file scope. + +Add-on is compatible with host if and only if: +``` +(add-on kind = host kind) ∧ ( + ((add-on major = host major = 0) ∧ (add-on minor = host minor)) ∨ + ((add-on major = host major ≥ 1) ∧ (add-on minor ≤ host minor)) +) +``` + +That is to say: +- API version is kind-specific. +- For a given kind, API major reported by add-on must be equal to host major. + - API major = 0 means unstable, minor updates may break backward compatibility. + - API major ≥ 1 means stable, minor updates keep backward compatibility. + +### Types + +Types in Red Panda C++ add-on interface are defined in [Teal](https://github.com/teal-language/tl) language, a typed dialect of Lua. + +To make use of the type definitions, add-on can be written in Teal. To check and compile Teal add-on: +```bash +tl check --include-dir /path/to/RedPanda-CPP/addon --global-env-def defs/theme addon.tl +tl gen --include-dir /path/to/RedPanda-CPP/addon --global-env-def defs/theme --gen-compat off --gen-target 5.4 addon.tl +``` + +### Localization + +Example: + +```lua +local lang = C_Desktop.language() +-- note: explicitly declare as `{string:string}` for Teal +local localizedName = { + en_US = "System", + pt_BR = "Sistema", + zh_CN = "系统", + zh_TW = "系統", +} +return { + name = localizedName[lang] or localizedName.en_US, + -- ... +} +``` ## Simple Add-on -A simple add-on is a Lua script that returns a single value. +A simple add-on is a Lua script with a `main` function returning single value. + +### Theme Add-on + +Current API version: `theme:0.1`. + +Available API groups: +- `C_Debug` +- `C_Desktop` +- `C_Util` + +`main` function takes no argument and returns `Theme` ([`theme.d.tl`](../addon/defs/theme.d.tl)). ### Compiler Hint Add-on +Current API version: `compiler_hint:0.1`. + If `$appLibexecDir/compiler_hint.lua` exists, it will be executed as compiler hint add-on when searching for compiler. Available API groups: @@ -45,149 +98,4 @@ Available API groups: - `C_System` - `C_Util` -Return value schema: -```typescript -{ - // found compiler sets - compilerList: [compilerSet], - - // do not search in these directories anymore - noSearch: [string], - - // prefer compiler set index (in Lua, 1-based) in compilerList - // 0 for no preference - preferCompiler: number, -} -``` - -`compilerSet` schema: -```typescript -{ - name: string, - - // internal - dumpMachine: string, // e.g. "x86_64-linux-gnu", "x86_64-w64-mingw32" - version: string, // e.g. "13.2.1", "17.0.6" - type: string, // e.g. "TDM-GCC", "MinGW" - target: string, // e.g. "x86_64", "aarch64" - compilerType: string, // "GCC" or "Clang" - - // general - staticLink: boolean, - customCompileParams: [string], // automatically sets useCustomCompileParams - customLinkParams: [string], // automatically sets useCustomLinkParams - execCharset: string, // automatically sets autoAddCharsetParams - - // setting - // - code generation - ccCmdOptOptimize: string, - ccCmdOptStd: string, - cCmdOptStd: string, - ccCmdOptInstruction: string, - ccCmdOptPointerSize: string, - ccCmdOptDebugInfo: string, - ccCmdOptProfileInfo: string, - ccCmdOptSyntaxOnly: string, - // - warnings - ccCmdOptInhibitAllWarning: string, - ccCmdOptWarningAll: string, - ccCmdOptWarningExtra: string, - ccCmdOptCheckIsoConformance: string, - ccCmdOptWarningAsError: string, - ccCmdOptAbortOnError: string, - ccCmdOptStackProtector: string, - ccCmdOptAddressSanitizer: string, - // - linker - ccCmdOptUsePipe: string, - linkCmdOptNoLinkStdlib: string, - linkCmdOptNoConsole: string, - linkCmdOptStripExe: string, - - // directory - binDirs: [string], - cIncludeDirs: [string], - cxxIncludeDirs: [string], - libDirs: [string], - defaultLibDirs: [string], - defaultCIncludeDirs: [string], - defaultCxxIncludeDirs: [string], - - // program - cCompiler: string, - cxxCompiler: string, - make: string, - debugger: string, - debugServer: string, - resourceCompiler: string, - - // output - preprocessingSuffix: string, - compilationProperSuffix: string, - assemblingSuffix: string, - executableSuffix: string, - compilationStage: number, -} -``` - -## API Groups - -### `C_Debug` - -`C_Debug` is available to all add-on types. - -- `C_Debug.debug`: `(message: string) -> ()`, print message to console (via `qDebug()`). - -### `C_Desktop` - -- `C_Desktop.desktopEnvironment`: `() -> string`, return desktop environment name. - - `windows`: Windows (Win32 only) - - `macos`: macOS - - `xdg`: XDG-compliant desktop environment (e.g. GNOME, KDE Plasma) - - `unknown`: other desktops or non-desktop environments (e.g. Windows UWP, Android) -- `C_Desktop.language`: `() -> string`, return language code. - - e.g. `en_US`, `zh_CN` -- `C_Desktop.qtStyleList`: `() -> [string]`, return available Qt styles. - - e.g. `{"breeze", "fusion", "windows"}` -- `C_Desktop.systemAppMode`: `() -> string`, return system app mode. - - `light`: light mode - - `dark`: dark mode -- `C_Desktop.systemStyle`: `() -> string`, return default Qt style. - - e.g. `fusion` - -### `C_FileSystem` - -- `C_FileSystem.exists`: `(path: string) -> boolean`, return whether the path exists. -- `C_FileSystem.isExecutable`: `(path: string) -> boolean`, return whether the path is executable. - -### `C_System` - -- `C_System.appArch`: `() -> string`, return the architecture of Red Panda C++, name following `QSysInfo`. - - e.g. `i386`, `x86_64`, `arm`, `arm64`, `riscv64`, `loongarch64` - - Though unsupprted, MSVC arm64ec is handled correctly (returns `arm64ec`) -- `C_System.appDir`: `() -> string`, return the directory of Red Panda C++. - - e.g. `/usr/bin`, `C:/Program Files/RedPanda-Cpp` -- `C_System.appLibexecDir`: `() -> string`, return the libexec directory of Red Panda C++. - - e.g. `/usr/libexec/RedPandaCPP`, `C:/Program Files/RedPanda-Cpp` -- `C_System.appResourceDir`: `() -> string`, return the resource directory of Red Panda C++. - - e.g. `/usr/share/RedPandaCPP`, `C:/Program Files/RedPanda-Cpp` -- `C_System.osArch`: `() -> string`, return the architecture of the OS, name following `QSysInfo`. - - e.g. `i386`, `x86_64`, `arm`, `arm64` - - Windows arm64 is handled correctly even if Red Panda C++ runs under emulation -- `C_System.supportedAppArchList`: `() -> [string]`, return supported application architectures by OS, name following `QSysInfo`. - - e.g. `{"i386", "x86_64", "arm64"}` - - Windows 10 1709 or later: accurate result - - Legacy Windows: hardcoded - - `{"i386", "x86_64"}` for x86_64 even though WoW64 is not available - - macOS: accurate result supposed, but not tested - - Linux: osArch + appArch + QEMU user mode emulation - - No multilib detection. It’s packager’s responsibility to detect multilib support in `compiler_hint.lua` - - other (BSD): osArch + appArch (no multilib) - -Windows specific: - -- `C_System.readRegistry`: `(subKey: string, name: string) -> string | nil`, read `subKey\name` from `HKEY_CURRENT_USER` and `HKEY_LOCAL_MACHINE` in order. - - `name` can be empty string for default value - -### `C_Util` - -- `C_Util.format`: `(format: string, ...) -> string`, Qt-style string format, replace `%1`, `%2`, etc. with arguments. +`main` function takes no argument and returns `CompilerHint` ([`compiler_hint.d.tl`](../addon/defs/compiler_hint.d.tl)). diff --git a/packages/archlinux/compiler_hint.lua b/packages/archlinux/compiler_hint.lua index 3e7a0071..5d509fe3 100644 --- a/packages/archlinux/compiler_hint.lua +++ b/packages/archlinux/compiler_hint.lua @@ -1,271 +1,313 @@ -local arch = C_System.osArch() -local libexecDir = C_System.appLibexecDir() -local lang = C_Desktop.language() +function apiVersion() + return { + kind = "compiler_hint", + major = 0, + minor = 1, + } +end local nameMap = { - systemGcc = { - en_US = "System GCC", - pt_BR = "GCC do sistema", - zh_CN = "系统 GCC", - zh_TW = "系統 GCC", - }, - systemClang = { - en_US = "System Clang", - pt_BR = "Clang do sistema", - zh_CN = "系统 Clang", - zh_TW = "系統 Clang", - }, - multilibGcc = { - en_US = "Multilib GCC", - pt_BR = "GCC multilib", - zh_CN = "Multilib GCC", - zh_TW = "Multilib GCC", - }, - multilibClang = { - en_US = "Multilib Clang", - pt_BR = "Clang multilib", - zh_CN = "Multilib Clang", - zh_TW = "Multilib Clang", - }, - crossGcc = { - en_US = "Cross GCC", - pt_BR = "GCC cruzado", - zh_CN = "交叉编译 GCC", - zh_TW = "交叉編譯 GCC", - }, - mingwGcc = { - en_US = "MinGW GCC", - pt_BR = "GCC MinGW", - zh_CN = "MinGW GCC", - zh_TW = "MinGW GCC", - }, - mingwClang = { - en_US = "MinGW Clang", - pt_BR = "Clang MinGW", - zh_CN = "MinGW Clang", - zh_TW = "MinGW Clang", - }, - release = { - en_US = ", release", - pt_BR = ", lançamento", - zh_CN = ",发布", - zh_TW = ",發佈", - }, - debug = { - en_US = ", debug", - pt_BR = ", depuração", - zh_CN = ",调试", - zh_TW = ",偵錯", - }, - debugWithAsan = { - en_US = ", debug with ASan", - pt_BR = ", depuração com ASan", - zh_CN = ",ASan 调试", - zh_TW = ",ASan 偵錯", - }, + systemGcc = { + en_US = "System GCC", + pt_BR = "GCC do sistema", + zh_CN = "系统 GCC", + zh_TW = "系統 GCC", + }, + systemClang = { + en_US = "System Clang", + pt_BR = "Clang do sistema", + zh_CN = "系统 Clang", + zh_TW = "系統 Clang", + }, + multilibGcc = { + en_US = "Multilib GCC", + pt_BR = "GCC multilib", + zh_CN = "Multilib GCC", + zh_TW = "Multilib GCC", + }, + multilibClang = { + en_US = "Multilib Clang", + pt_BR = "Clang multilib", + zh_CN = "Multilib Clang", + zh_TW = "Multilib Clang", + }, + crossGcc = { + en_US = "Cross GCC", + pt_BR = "GCC cruzado", + zh_CN = "交叉编译 GCC", + zh_TW = "交叉編譯 GCC", + }, + mingwGcc = { + en_US = "MinGW GCC", + pt_BR = "GCC MinGW", + zh_CN = "MinGW GCC", + zh_TW = "MinGW GCC", + }, + mingwClang = { + en_US = "MinGW Clang", + pt_BR = "Clang MinGW", + zh_CN = "MinGW Clang", + zh_TW = "MinGW Clang", + }, + release = { + en_US = ", release", + pt_BR = ", lançamento", + zh_CN = ",发布", + zh_TW = ",發佈", + }, + debug = { + en_US = ", debug", + pt_BR = ", depuração", + zh_CN = ",调试", + zh_TW = ",偵錯", + }, + debugWithAsan = { + en_US = ", debug with ASan", + pt_BR = ", depuração com ASan", + zh_CN = ",ASan 调试", + zh_TW = ",ASan 偵錯", + }, } -function generateConfig(name, cCompiler, cxxCompiler, config) - local commonOptions = { - cCompiler = cCompiler, - cxxCompiler = cxxCompiler, - debugger = "/usr/bin/gdb", - debugServer = "/usr/bin/gdbserver", - make = "/usr/bin/make", - compilerType = config.isClang and "Clang" or "GCC_UTF8", - preprocessingSuffix = ".i", - compilationProperSuffix = ".s", - assemblingSuffix = ".o", - executableSuffix = config.isMingw and ".exe" or "", - compilationStage = 3, - ccCmdOptUsePipe = "on", - ccCmdOptWarningAll = "on", - ccCmdOptWarningExtra = "on", - ccCmdOptCheckIsoConformance = "on", - binDirs = {"/usr/bin"}, - } - if config.isMultilib then - commonOptions.ccCmdOptPointerSize = "32" - end - if config.isMingw then - commonOptions.resourceCompiler = "/usr/bin/" .. config.triplet .. "-windres" - end - if config.customCompileParams then - commonOptions.customCompileParams = config.customCompileParams - end - if config.customLinkParams then - commonOptions.customLinkParams = config.customLinkParams - end - local release = { - name = name .. (nameMap.release[lang] or nameMap.release.en_US), - staticLink = true, - linkCmdOptStripExe = "on", - ccCmdOptOptimize = "2", - } - local debug = { - name = name .. (nameMap.debug[lang] or nameMap.debug.en_US), - ccCmdOptDebugInfo = "on", - } - local debugWithAsan = { - name = name .. (nameMap.debugWithAsan[lang] or nameMap.debugWithAsan.en_US), - ccCmdOptDebugInfo = "on", - ccCmdOptAddressSanitizer = "address", - } - for k, v in pairs(commonOptions) do - release[k] = v - debug[k] = v - debugWithAsan[k] = v - end - return release, debug, debugWithAsan +local function mergeCompilerSet(compilerSet, other) + local c = compilerSet + local o = other + for k, v in pairs(o) do + c[k] = v + end end -local compilerList = {} -do - local release, debug, debugWithAsan = generateConfig( - nameMap.systemGcc[lang] or nameMap.systemGcc.en_US, - "/usr/bin/gcc", "/usr/bin/g++", - {} - ) - table.insert(compilerList, release) - table.insert(compilerList, debug) - table.insert(compilerList, debugWithAsan) + + + + + + + + +local function generateConfig( + name, lang, + cCompiler, cxxCompiler, + config) + + local commonOptions = { + cCompiler = cCompiler, + cxxCompiler = cxxCompiler, + debugger = "/usr/bin/gdb", + debugServer = "/usr/bin/gdbserver", + make = "/usr/bin/make", + compilerType = config.isClang and "Clang" or "GCC_UTF8", + preprocessingSuffix = ".i", + compilationProperSuffix = ".s", + assemblingSuffix = ".o", + executableSuffix = config.isMingw and ".exe" or "", + compilationStage = 3, + ccCmdOptUsePipe = "on", + ccCmdOptWarningAll = "on", + ccCmdOptWarningExtra = "on", + ccCmdOptCheckIsoConformance = "on", + binDirs = { "/usr/bin" }, + } + if config.isMultilib then + commonOptions.ccCmdOptPointerSize = "32" + end + if config.isMingw then + commonOptions.resourceCompiler = "/usr/bin/" .. config.triplet .. "-windres" + end + if config.customCompileParams then + commonOptions.customCompileParams = config.customCompileParams + end + if config.customLinkParams then + commonOptions.customLinkParams = config.customLinkParams + end + local release = { + name = name .. (nameMap.release[lang] or nameMap.release.en_US), + staticLink = true, + linkCmdOptStripExe = "on", + ccCmdOptOptimize = "2", + } + local debug_ = { + name = name .. (nameMap.debug[lang] or nameMap.debug.en_US), + ccCmdOptDebugInfo = "on", + } + local debugWithAsan = { + name = name .. (nameMap.debugWithAsan[lang] or nameMap.debugWithAsan.en_US), + ccCmdOptDebugInfo = "on", + ccCmdOptAddressSanitizer = "address", + } + mergeCompilerSet(release, commonOptions) + mergeCompilerSet(debug_, commonOptions) + mergeCompilerSet(debugWithAsan, commonOptions) + return release, debug_, debugWithAsan end -if C_FileSystem.isExecutable("/usr/bin/clang") then - local release, debug, debugWithAsan = generateConfig( - nameMap.systemClang[lang] or nameMap.systemClang.en_US, - "/usr/bin/clang", "/usr/bin/clang++", - {isClang = true} - ) - table.insert(compilerList, release) - table.insert(compilerList, debug) - table.insert(compilerList, debugWithAsan) -end +function main() + local arch = C_System.osArch() + local libexecDir = C_System.appLibexecDir() + local lang = C_Desktop.language() --- with lib32-gcc-libs installed, system GCC and Clang can target 32-bit -if arch == "x86_64" and C_FileSystem.isExecutable("/usr/lib32/libstdc++.so") then - local release, debug, debugWithAsan = generateConfig( - nameMap.multilibGcc[lang] or nameMap.multilibGcc.en_US, - "/usr/bin/gcc", "/usr/bin/g++", - {isMultilib = true} - ) - table.insert(compilerList, release) - table.insert(compilerList, debug) - table.insert(compilerList, debugWithAsan) - if C_FileSystem.isExecutable("/usr/bin/clang") then - local release, debug, debugWithAsan = generateConfig( - nameMap.multilibClang[lang] or nameMap.multilibClang.en_US, - "/usr/bin/clang", "/usr/bin/clang++", - {isClang = true, isMultilib = true} - ) - table.insert(compilerList, release) - table.insert(compilerList, debug) - table.insert(compilerList, debugWithAsan) - end -end + local compilerList = {} --- cross GCC -if ( - arch == "x86_64" and - C_FileSystem.exists("/proc/sys/fs/binfmt_misc/qemu-aarch64") and - C_FileSystem.isExecutable("/usr/bin/aarch64-linux-gnu-gcc") -) then - local release, debug, debugWithAsan = generateConfig( - (nameMap.crossGcc[lang] or nameMap.crossGcc.en_US) .. " aarch64", - "/usr/bin/aarch64-linux-gnu-gcc", "/usr/bin/aarch64-linux-gnu-g++", - {} - ) - table.insert(compilerList, release) -end + do + local release, debug_, debugWithAsan = generateConfig( + nameMap.systemGcc[lang] or nameMap.systemGcc.en_US, lang, + "/usr/bin/gcc", "/usr/bin/g++", + {}) --- with wine or WSL init registered in binfmt_misc, Windows binaries can run seamlessly -if ( - arch == "x86_64" and ( - C_FileSystem.exists("/proc/sys/fs/binfmt_misc/DOSWin") or - C_FileSystem.exists("/proc/sys/fs/binfmt_misc/WSLInterop") - ) -) then - if C_FileSystem.isExecutable("/usr/bin/x86_64-w64-mingw32-gcc") then - local extraObjects = { + table.insert(compilerList, release) + table.insert(compilerList, debug_) + table.insert(compilerList, debugWithAsan) + end + + if C_FileSystem.isExecutable("/usr/bin/clang") then + local release, debug_, debugWithAsan = generateConfig( + nameMap.systemClang[lang] or nameMap.systemClang.en_US, lang, + "/usr/bin/clang", "/usr/bin/clang++", + { isClang = true }) + + table.insert(compilerList, release) + table.insert(compilerList, debug_) + table.insert(compilerList, debugWithAsan) + end + + + if arch == "x86_64" and C_FileSystem.isExecutable("/usr/lib32/libstdc++.so") then + do + local release, debug_, debugWithAsan = generateConfig( + nameMap.multilibGcc[lang] or nameMap.multilibGcc.en_US, lang, + "/usr/bin/gcc", "/usr/bin/g++", + { isMultilib = true }) + + table.insert(compilerList, release) + table.insert(compilerList, debug_) + table.insert(compilerList, debugWithAsan) + end + + if C_FileSystem.isExecutable("/usr/bin/clang") then + local release, debug_, debugWithAsan = generateConfig( + nameMap.multilibClang[lang] or nameMap.multilibClang.en_US, lang, + "/usr/bin/clang", "/usr/bin/clang++", + { isClang = true, isMultilib = true }) + + table.insert(compilerList, release) + table.insert(compilerList, debug_) + table.insert(compilerList, debugWithAsan) + end + end + + + if ( + arch == "x86_64" and + C_FileSystem.exists("/proc/sys/fs/binfmt_misc/qemu-aarch64") and + C_FileSystem.isExecutable("/usr/bin/aarch64-linux-gnu-gcc")) then + + local release, _, _ = generateConfig( + (nameMap.crossGcc[lang] or nameMap.crossGcc.en_US) .. " aarch64", lang, + "/usr/bin/aarch64-linux-gnu-gcc", "/usr/bin/aarch64-linux-gnu-g++", + {}) + + table.insert(compilerList, release) + end + + + if ( + arch == "x86_64" and ( + C_FileSystem.exists("/proc/sys/fs/binfmt_misc/DOSWin") or + C_FileSystem.exists("/proc/sys/fs/binfmt_misc/WSLInterop"))) then + + + if C_FileSystem.isExecutable("/usr/bin/x86_64-w64-mingw32-gcc") then + local extraObjects = { utf8init = libexecDir .. "/x86_64-w64-mingw32/utf8init.o", utf8manifest = libexecDir .. "/x86_64-w64-mingw32/utf8manifest.o", - } - local release, debug, debugWithAsan = generateConfig( - (nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " x86_64", + } + + do + local release, _, _ = generateConfig( + (nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " x86_64", lang, "/usr/bin/x86_64-w64-mingw32-gcc", "/usr/bin/x86_64-w64-mingw32-g++", { - isMingw = true, - triplet = "x86_64-w64-mingw32", - customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest}, - } - ) - table.insert(compilerList, release) - -- system Clang can target Windows - if C_FileSystem.isExecutable("/usr/bin/clang") then - local release, debug, debugWithAsan = generateConfig( - (nameMap.mingwClang[lang] or nameMap.mingwClang.en_US) .. " x86_64", - "/usr/bin/clang", "/usr/bin/clang++", - { - isClang = true, - isMingw = true, - triplet = "x86_64-w64-mingw32", - customCompileParams = {"-target", "x86_64-w64-mingw32"}, - customLinkParams = { - "-target", "x86_64-w64-mingw32", - extraObjects.utf8init, extraObjects.utf8manifest, - "-lstdc++", "-lwinpthread", - }, - } - ) + isMingw = true, + triplet = "x86_64-w64-mingw32", + customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest }, + }) + table.insert(compilerList, release) - end - end - if C_FileSystem.isExecutable("/usr/bin/i686-w64-mingw32-gcc") then - local extraObjects = { + end + + + if C_FileSystem.isExecutable("/usr/bin/clang") then + local release, _, _ = generateConfig( + (nameMap.mingwClang[lang] or nameMap.mingwClang.en_US) .. " x86_64", lang, + "/usr/bin/clang", "/usr/bin/clang++", + { + isClang = true, + isMingw = true, + triplet = "x86_64-w64-mingw32", + customCompileParams = { "-target", "x86_64-w64-mingw32" }, + customLinkParams = { + "-target", "x86_64-w64-mingw32", + extraObjects.utf8init, extraObjects.utf8manifest, + "-lstdc++", "-lwinpthread", + }, + }) + + table.insert(compilerList, release) + end + end + + if C_FileSystem.isExecutable("/usr/bin/i686-w64-mingw32-gcc") then + local extraObjects = { utf8init = libexecDir .. "/i686-w64-mingw32/utf8init.o", utf8manifest = libexecDir .. "/i686-w64-mingw32/utf8manifest.o", - } - local release, debug, debugWithAsan = generateConfig( - (nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " i686", + } + + do + local release, _, _ = generateConfig( + (nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " i686", lang, "/usr/bin/i686-w64-mingw32-gcc", "/usr/bin/i686-w64-mingw32-g++", { - isMingw = true, - triplet = "i686-w64-mingw32", - customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest}, - } - ) - table.insert(compilerList, release) - -- system Clang can target Windows - if C_FileSystem.isExecutable("/usr/bin/clang") then - local release, debug, debugWithAsan = generateConfig( - (nameMap.mingwClang[lang] or nameMap.mingwClang.en_US) .. " i686", - "/usr/bin/clang", "/usr/bin/clang++", - { - isClang = true, - isMingw = true, - triplet = "i686-w64-mingw32", - customCompileParams = {"-target", "i686-w64-mingw32"}, - customLinkParams = { - "-target", "i686-w64-mingw32", - extraObjects.utf8init, extraObjects.utf8manifest, - "-lstdc++", "-lwinpthread", - }, - } - ) + isMingw = true, + triplet = "i686-w64-mingw32", + customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest }, + }) + table.insert(compilerList, release) - end - end + end + + + if C_FileSystem.isExecutable("/usr/bin/clang") then + local release, _, _ = generateConfig( + (nameMap.mingwClang[lang] or nameMap.mingwClang.en_US) .. " i686", lang, + "/usr/bin/clang", "/usr/bin/clang++", + { + isClang = true, + isMingw = true, + triplet = "i686-w64-mingw32", + customCompileParams = { "-target", "i686-w64-mingw32" }, + customLinkParams = { + "-target", "i686-w64-mingw32", + extraObjects.utf8init, extraObjects.utf8manifest, + "-lstdc++", "-lwinpthread", + }, + }) + + table.insert(compilerList, release) + end + end + end + + local result = { + compilerList = compilerList, + noSearch = { + "/usr/bin", + "/opt/cuda/bin", + "/usr/lib/ccache/bin", + }, + preferCompiler = 3, + } + + return result + end - -local result = { - compilerList = compilerList, - noSearch = { - "/usr/bin", - "/opt/cuda/bin", - "/usr/lib/ccache/bin", - }, - preferCompiler = 3, -- System GCC Debug with ASan -} - -return result diff --git a/packages/msys/domain/compiler_hint.lua b/packages/msys/domain/compiler_hint.lua index c5532b79..2fc454a3 100644 --- a/packages/msys/domain/compiler_hint.lua +++ b/packages/msys/domain/compiler_hint.lua @@ -1,386 +1,430 @@ -local arch = C_System.osArch() -local appArch = C_System.appArch() -local libexecDir = C_System.appLibexecDir() -local lang = C_Desktop.language() -local supportedAppArches = C_System.supportedAppArchList() +function apiVersion() + return { + kind = "compiler_hint", + major = 0, + minor = 1, + } +end local gnuArchMap = { - i386 = "i686", - x86_64 = "x86_64", - arm = "armv7", - arm64 = "aarch64", + i386 = "i686", + x86_64 = "x86_64", + arm = "armv7", + arm64 = "aarch64", } local profileNameMap = { - release = { - en_US = "release", - pt_BR = "lançamento", - zh_CN = "发布", - zh_TW = "發佈", - }, - debug = { - en_US = "debug", - pt_BR = "depuração", - zh_CN = "调试", - zh_TW = "偵錯", - }, - debugWithAsan = { - en_US = "debug with ASan", - pt_BR = "depuração com ASan", - zh_CN = "ASan 调试", - zh_TW = "ASan 偵錯", - }, + release = { + en_US = "release", + pt_BR = "lançamento", + zh_CN = "发布", + zh_TW = "發佈", + }, + debug = { + en_US = "debug", + pt_BR = "depuração", + zh_CN = "调试", + zh_TW = "偵錯", + }, + debugWithAsan = { + en_US = "debug with ASan", + pt_BR = "depuração com ASan", + zh_CN = "ASan 调试", + zh_TW = "ASan 偵錯", + }, } -local nameGenerator = { - mingwGcc = function (lang, arch, profile, isUtf8) - local template = { - en_US = "MinGW GCC %1 in %2, %3", - pt_BR = "GCC MinGW %1 em %2, %3", - zh_CN = "%2 MinGW GCC %1,%3", - zh_TW = "%2 MinGW GCC %1,%3", - } - local systemCodePage = { - en_US = "system code page", - pt_Br = "página de código do sistema", - zh_CN = "系统代码页", - zh_TW = "系統代碼頁", - } - return C_Util.format( - template[lang] or template.en_US, - gnuArchMap[arch], - isUtf8 and "UTF-8" or systemCodePage[lang] or systemCodePage.en_US, - profileNameMap[profile][lang] or profileNameMap[profile].en_US - ) - end, - clang = function (lang, arch, profile, isMingw) - local template = { - en_US = "%1 Clang %2, %3", - pt_BR = "Clang %2 %1, %3", - zh_CN = "%1 Clang %2,%3", - zh_CN = "%1 Clang %2,%3", - } - local msvcCompatible = { - en_US = "MSVC-compatible", - pt_BR = "compatível com MSVC", - zh_CN = "兼容 MSVC 的", - zh_TW = "相容 MSVC 的", - } - return C_Util.format( - template[lang] or template.en_US, - isMingw and "LLVM-MinGW" or msvcCompatible[lang] or msvcCompatible.en_US, - gnuArchMap[arch], - profileNameMap[profile][lang] or profileNameMap[profile].en_US - ) - end, -} +local function nameGeneratorMingwGcc(lang, arch, profile, isUtf8) + local template = { + en_US = "MinGW GCC %1 in %2, %3", + pt_BR = "GCC MinGW %1 em %2, %3", + zh_CN = "%2 MinGW GCC %1,%3", + zh_TW = "%2 MinGW GCC %1,%3", + } + local systemCodePage = { + en_US = "system code page", + pt_Br = "página de código do sistema", + zh_CN = "系统代码页", + zh_TW = "系統代碼頁", + } + return C_Util.format( + template[lang] or template.en_US, + gnuArchMap[arch], + isUtf8 and "UTF-8" or systemCodePage[lang] or systemCodePage.en_US, + profileNameMap[profile][lang] or profileNameMap[profile].en_US) -function generateConfig(nameGen, programs, config) - local commonOptions = { - cCompiler = programs.cCompiler, - cxxCompiler = programs.cxxCompiler, - debugger = programs.debugger, - debugServer = programs.debugServer, - make = programs.make, - resourceCompiler = programs.resourceCompiler, - binDirs = programs.binDirs, - compilerType = config.isClang and "Clang" or "GCC_UTF8", - preprocessingSuffix = ".i", - compilationProperSuffix = ".s", - assemblingSuffix = ".o", - executableSuffix = ".exe", - compilationStage = 3, - ccCmdOptUsePipe = "on", - ccCmdOptWarningAll = "on", - ccCmdOptWarningExtra = "on", - ccCmdOptCheckIsoConformance = "on", - } - if programs.libDirs then - commonOptions.libDirs = programs.libDirs - end - if config.isAnsi then - commonOptions.execCharset = "SYSTEM" - end - if config.customCompileParams then - commonOptions.customCompileParams = config.customCompileParams - end - if config.customLinkParams then - commonOptions.customLinkParams = config.customLinkParams - end - local release = { - name = nameGen(config.arch, "release"), - staticLink = true, - linkCmdOptStripExe = "on", - ccCmdOptOptimize = "2", - } - local debug = { - name = nameGen(config.arch, "debug"), - ccCmdOptDebugInfo = "on", - } - local debugWithAsan = { - name = nameGen(config.arch, "debugWithAsan"), - ccCmdOptDebugInfo = "on", - ccCmdOptAddressSanitizer = "address", - } - for k, v in pairs(commonOptions) do - release[k] = v - debug[k] = v - debugWithAsan[k] = v - end - return release, debug, debugWithAsan end -function contains(t, v) - for _, vv in ipairs(t) do - if vv == v then - return true - end - end - return false +local function nameGeneratorClang(lang, arch, profile, isMingw) + local template = { + en_US = "%1 Clang %2, %3", + pt_BR = "Clang %2 %1, %3", + zh_CN = "%1 Clang %2,%3", + zh_TW = "%1 Clang %2,%3", + } + local msvcCompatible = { + en_US = "MSVC-compatible", + pt_BR = "compatível com MSVC", + zh_CN = "兼容 MSVC 的", + zh_TW = "相容 MSVC 的", + } + return C_Util.format( + template[lang] or template.en_US, + isMingw and "LLVM-MinGW" or msvcCompatible[lang] or msvcCompatible.en_US, + gnuArchMap[arch], + profileNameMap[profile][lang] or profileNameMap[profile].en_US) + end -local compilerList = {} -local noSearch = {} -local preferCompiler = 0 - -function checkAndAddMingw(arch) - local binDir - local libDir - local excludeBinDir - if arch == "i386" then - binDir = libexecDir .. "/mingw32/bin" -- must match case because Windows filesystem can be case sensitive - libDir = libexecDir .. "/mingw32/i686-w64-mingw32/lib" - excludeBinDir = libexecDir .. "/MinGW32/bin" -- workaround for path check - elseif arch == "x86_64" then - binDir = libexecDir .. "/mingw64/bin" - libDir = libexecDir .. "/mingw64/x86_64-w64-mingw32/lib" - excludeBinDir = libexecDir .. "/MinGW64/bin" - else - return - end - - if not C_FileSystem.isExecutable(binDir .. "/gcc.exe") then - return - end - - local programs = { - cCompiler = binDir .. "/gcc.exe", - cxxCompiler = binDir .. "/g++.exe", - make = binDir .. "/mingw32-make.exe", - debugger = binDir .. "/gdb.exe", - debugServer = binDir .. "/gdbserver.exe", - resourceCompiler = binDir .. "/windres.exe", - binDirs = {binDir}, - } - local extraObjects = { - utf8init = libDir .. "/utf8init.o", - utf8manifest = libDir .. "/utf8manifest.o", - } - - local release, debug, debugWithAsan = generateConfig( - function (arch, profile) return nameGenerator.mingwGcc(lang, arch, profile, true) end, - programs, - { - arch = arch, - customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest}, - } - ) - table.insert(compilerList, release) - table.insert(compilerList, debug) - if preferCompiler == 0 then - preferCompiler = 2 - end - - release, debug, debugWithAsan = generateConfig( - function (arch, profile) return nameGenerator.mingwGcc(lang, arch, profile, false) end, - programs, - { - arch = arch, - isAnsi = true, - } - ) - table.insert(compilerList, release) - table.insert(compilerList, debug) - - table.insert(noSearch, excludeBinDir) +local function mergeCompilerSet(compilerSet, other) + local c = compilerSet + local o = other + for k, v in pairs(o) do + c[k] = v + end end -function checkAndAddClang() - if not C_FileSystem.isExecutable(libexecDir .. "/llvm-mingw/bin/clang.exe") then - return - end - -- appArch is always debuggable - local appTriplet = gnuArchMap[appArch] .. "-w64-mingw32" - local binDir = libexecDir .. "/llvm-mingw/bin" - local libDir = libexecDir .. "/llvm-mingw/" .. appTriplet .. "/lib" - local programs = { - cCompiler = binDir .. "/" .. appTriplet .. "-clang.exe", - cxxCompiler = binDir .. "/" .. appTriplet .. "-clang++.exe", - make = binDir .. "/mingw32-make.exe", - debugger = binDir .. "/lldb-mi.exe", - debugServer = binDir .. "/lldb-server.exe", - resourceCompiler = binDir .. "/" .. appTriplet .. "-windres.exe", - binDirs = {binDir}, - } - local extraObjects = { - utf8init = libDir .. "/utf8init.o", - utf8manifest = libDir .. "/utf8manifest.o", - } - local release, debug, debugWithAsan = generateConfig( - function (arch, profile) return nameGenerator.clang(lang, arch, profile, true) end, - programs, - { + + + + + + + + + + + + + + + + + + +local function generateConfig( + nameGen, + programs, + config) + + local commonOptions = { + cCompiler = programs.cCompiler, + cxxCompiler = programs.cxxCompiler, + debugger = programs.debugger, + debugServer = programs.debugServer, + make = programs.make, + resourceCompiler = programs.resourceCompiler, + binDirs = programs.binDirs, + compilerType = config.isClang and "Clang" or "GCC_UTF8", + preprocessingSuffix = ".i", + compilationProperSuffix = ".s", + assemblingSuffix = ".o", + executableSuffix = ".exe", + compilationStage = 3, + ccCmdOptUsePipe = "on", + ccCmdOptWarningAll = "on", + ccCmdOptWarningExtra = "on", + ccCmdOptCheckIsoConformance = "on", + } + if programs.libDirs then + commonOptions.libDirs = programs.libDirs + end + if config.isAnsi then + commonOptions.execCharset = "SYSTEM" + end + if config.customCompileParams then + commonOptions.customCompileParams = config.customCompileParams + end + if config.customLinkParams then + commonOptions.customLinkParams = config.customLinkParams + end + local release = { + name = nameGen(config.arch, "release"), + staticLink = true, + linkCmdOptStripExe = "on", + ccCmdOptOptimize = "2", + } + local debug_ = { + name = nameGen(config.arch, "debug"), + ccCmdOptDebugInfo = "on", + } + local debugWithAsan = { + name = nameGen(config.arch, "debugWithAsan"), + ccCmdOptDebugInfo = "on", + ccCmdOptAddressSanitizer = "address", + } + mergeCompilerSet(release, commonOptions) + mergeCompilerSet(debug_, commonOptions) + mergeCompilerSet(debugWithAsan, commonOptions) + return release, debug_, debugWithAsan +end + +function main() + local appArch = C_System.appArch() + local libexecDir = C_System.appLibexecDir() + local lang = C_Desktop.language() + local supportedAppArches = C_System.supportedAppArchList() + + local compilerList = {} + local noSearch = {} + local preferCompiler = 0 + + local function checkAndAddMingw(arch) + local binDir + local libDir + local excludeBinDir + if arch == "i386" then + binDir = libexecDir .. "/mingw32/bin" + libDir = libexecDir .. "/mingw32/i686-w64-mingw32/lib" + excludeBinDir = libexecDir .. "/MinGW32/bin" + elseif arch == "x86_64" then + binDir = libexecDir .. "/mingw64/bin" + libDir = libexecDir .. "/mingw64/x86_64-w64-mingw32/lib" + excludeBinDir = libexecDir .. "/MinGW64/bin" + else + return + end + + if not C_FileSystem.isExecutable(binDir .. "/gcc.exe") then + return + end + + local programs = { + cCompiler = binDir .. "/gcc.exe", + cxxCompiler = binDir .. "/g++.exe", + make = binDir .. "/mingw32-make.exe", + debugger = binDir .. "/gdb.exe", + debugServer = binDir .. "/gdbserver.exe", + resourceCompiler = binDir .. "/windres.exe", + binDirs = { binDir }, + } + local extraObjects = { + utf8init = libDir .. "/utf8init.o", + utf8manifest = libDir .. "/utf8manifest.o", + } + + local release, debug_, debugWithAsan = generateConfig( + function(arch_, profile) + return nameGeneratorMingwGcc(lang, arch_, profile, true) + end, + programs, + { + arch = arch, + customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest }, + }) + + table.insert(compilerList, release) + table.insert(compilerList, debug_) + if preferCompiler == 0 then + preferCompiler = 2 + end + + release, debug_, debugWithAsan = generateConfig( + function(arch_, profile) + return nameGeneratorMingwGcc(lang, arch_, profile, false) + end, + programs, + { + arch = arch, + isAnsi = true, + }) + + table.insert(compilerList, release) + table.insert(compilerList, debug_) + + table.insert(noSearch, excludeBinDir) + end + + local function checkAndAddClang() + if not C_FileSystem.isExecutable(libexecDir .. "/llvm-mingw/bin/clang.exe") then + return + end + + local binDir = libexecDir .. "/llvm-mingw/bin" + local appTriplet = gnuArchMap[appArch] .. "-w64-mingw32" + do + + local libDir = libexecDir .. "/llvm-mingw/" .. appTriplet .. "/lib" + local programs = { + cCompiler = binDir .. "/" .. appTriplet .. "-clang.exe", + cxxCompiler = binDir .. "/" .. appTriplet .. "-clang++.exe", + make = binDir .. "/mingw32-make.exe", + debugger = binDir .. "/lldb-mi.exe", + debugServer = binDir .. "/lldb-server.exe", + resourceCompiler = binDir .. "/" .. appTriplet .. "-windres.exe", + binDirs = { binDir }, + } + local extraObjects = { + utf8init = libDir .. "/utf8init.o", + utf8manifest = libDir .. "/utf8manifest.o", + } + local release, debug_, debugWithAsan = generateConfig( + function(arch_, profile) + return nameGeneratorClang(lang, arch_, profile, true) + end, + programs, + { arch = appArch, - customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest}, + customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest }, isClang = true, - } - ) - table.insert(compilerList, release) - table.insert(compilerList, debug) - if appArch ~= "arm64" then - table.insert(compilerList, debugWithAsan) - if preferCompiler == 0 then - preferCompiler = 3 - end - else - if preferCompiler == 0 then - preferCompiler = 2 - end - end + }) - for _, foreignArch in ipairs(supportedAppArches) do - if foreignArch ~= appArch then + table.insert(compilerList, release) + table.insert(compilerList, debug_) + if appArch ~= "arm64" then + table.insert(compilerList, debugWithAsan) + if preferCompiler == 0 then + preferCompiler = 3 + end + else + if preferCompiler == 0 then + preferCompiler = 2 + end + end + end + + for _, foreignArch in ipairs(supportedAppArches) do + if foreignArch ~= appArch then local foreignTriplet = gnuArchMap[foreignArch] .. "-w64-mingw32" local libDir = libexecDir .. "/llvm-mingw/" .. foreignTriplet .. "/lib" local programs = { - cCompiler = binDir .. "/" .. foreignTriplet .. "-clang.exe", - cxxCompiler = binDir .. "/" .. foreignTriplet .. "-clang++.exe", - make = binDir .. "/mingw32-make.exe", - debugger = binDir .. "/lldb-mi.exe", - debugServer = binDir .. "/lldb-server.exe", - resourceCompiler = binDir .. "/" .. foreignTriplet .. "-windres.exe", - binDirs = {binDir}, + cCompiler = binDir .. "/" .. foreignTriplet .. "-clang.exe", + cxxCompiler = binDir .. "/" .. foreignTriplet .. "-clang++.exe", + make = binDir .. "/mingw32-make.exe", + debugger = binDir .. "/lldb-mi.exe", + debugServer = binDir .. "/lldb-server.exe", + resourceCompiler = binDir .. "/" .. foreignTriplet .. "-windres.exe", + binDirs = { binDir }, } local extraObjects = { - utf8init = libDir .. "/utf8init.o", - utf8manifest = libDir .. "/utf8manifest.o", + utf8init = libDir .. "/utf8init.o", + utf8manifest = libDir .. "/utf8manifest.o", } - local release, debug, debugWithAsan = generateConfig( - function (arch, profile) return nameGenerator.clang(lang, arch, profile, true) end, - programs, - { - arch = foreignArch, - customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest}, - isClang = true, - } - ) + local release, _, _ = generateConfig( + function(arch_, profile) + return nameGeneratorClang(lang, arch_, profile, true) + end, + programs, + { + arch = foreignArch, + customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest }, + isClang = true, + }) + table.insert(compilerList, release) - end - end + end + end - table.insert(noSearch, binDir) + table.insert(noSearch, binDir) - local llvmOrgPath = C_System.readRegistry([[Software\LLVM\LLVM]], "") or C_System.readRegistry([[Software\Wow6432Node\LLVM\LLVM]], "") - if not llvmOrgPath then - return - end + local llvmOrgPath = C_System.readRegistry([[Software\LLVM\LLVM]], "") or C_System.readRegistry([[Software\Wow6432Node\LLVM\LLVM]], "") + if not llvmOrgPath then + return + end + local llvmOrgBinDir = llvmOrgPath .. "/bin" - local llvmOrgBinDir = llvmOrgPath .. "/bin" - local msvcTriplet = gnuArchMap[appArch] .. "-pc-windows-msvc" - local libDir = libexecDir .. "/llvm-mingw/" .. msvcTriplet .. "/lib" - local programs = { - cCompiler = llvmOrgBinDir .. "/clang.exe", - cxxCompiler = llvmOrgBinDir .. "/clang++.exe", - make = binDir .. "/mingw32-make.exe", - debugger = binDir .. "/lldb-mi.exe", - debugServer = binDir .. "/lldb-server.exe", - resourceCompiler = binDir .. "/" .. appTriplet .. "-windres.exe", - binDirs = {llvmOrgBinDir}, - libDirs = {libDir}, - } - local extraObjects = { - utf8init = libDir .. "/utf8init.o", - utf8manifest = libDir .. "/utf8manifest.o", - } - local release, debug, debugWithAsan = generateConfig( - function (arch, profile) return nameGenerator.clang(lang, arch, profile, false) end, - programs, - { + do + local msvcTriplet = gnuArchMap[appArch] .. "-pc-windows-msvc" + local libDir = libexecDir .. "/llvm-mingw/" .. msvcTriplet .. "/lib" + local programs = { + cCompiler = llvmOrgBinDir .. "/clang.exe", + cxxCompiler = llvmOrgBinDir .. "/clang++.exe", + make = binDir .. "/mingw32-make.exe", + debugger = binDir .. "/lldb-mi.exe", + debugServer = binDir .. "/lldb-server.exe", + resourceCompiler = binDir .. "/" .. appTriplet .. "-windres.exe", + binDirs = { llvmOrgBinDir }, + libDirs = { libDir }, + } + local extraObjects = { + utf8init = libDir .. "/utf8init.o", + utf8manifest = libDir .. "/utf8manifest.o", + } + local release, debug_, _ = generateConfig( + function(arch, profile) + return nameGeneratorClang(lang, arch, profile, false) + end, + programs, + { arch = appArch, customCompileParams = { - "-target", msvcTriplet, - "-fms-extensions", - "-fms-compatibility", - "-fdelayed-template-parsing", + "-target", msvcTriplet, + "-fms-extensions", + "-fms-compatibility", + "-fdelayed-template-parsing", }, customLinkParams = { - "-target", msvcTriplet, - extraObjects.utf8init, extraObjects.utf8manifest, + "-target", msvcTriplet, + extraObjects.utf8init, extraObjects.utf8manifest, }, isClang = true, - } - ) - table.insert(compilerList, release) - table.insert(compilerList, debug) + }) - for _, foreignArch in ipairs(supportedAppArches) do - if foreignArch ~= appArch then + table.insert(compilerList, release) + table.insert(compilerList, debug_) + end + + for _, foreignArch in ipairs(supportedAppArches) do + if foreignArch ~= appArch then local foreignTriplet = gnuArchMap[foreignArch] .. "-w64-mingw32" local msvcTriplet = gnuArchMap[foreignArch] .. "-pc-windows-msvc" local libDir = libexecDir .. "/llvm-mingw/" .. msvcTriplet .. "/lib" local programs = { - cCompiler = llvmOrgBinDir .. "/clang.exe", - cxxCompiler = llvmOrgBinDir .. "/clang++.exe", - make = binDir .. "/mingw32-make.exe", - debugger = binDir .. "/lldb-mi.exe", - debugServer = binDir .. "/lldb-server.exe", - resourceCompiler = binDir .. "/" .. foreignTriplet .. "-windres.exe", - binDirs = {llvmOrgBinDir}, - libDirs = {libDir}, + cCompiler = llvmOrgBinDir .. "/clang.exe", + cxxCompiler = llvmOrgBinDir .. "/clang++.exe", + make = binDir .. "/mingw32-make.exe", + debugger = binDir .. "/lldb-mi.exe", + debugServer = binDir .. "/lldb-server.exe", + resourceCompiler = binDir .. "/" .. foreignTriplet .. "-windres.exe", + binDirs = { llvmOrgBinDir }, + libDirs = { libDir }, } local extraObjects = { - utf8init = libDir .. "/utf8init.o", - utf8manifest = libDir .. "/utf8manifest.o", + utf8init = libDir .. "/utf8init.o", + utf8manifest = libDir .. "/utf8manifest.o", } - local release, debug, debugWithAsan = generateConfig( - function (arch, profile) return nameGenerator.clang(lang, arch, profile, false) end, - programs, - { - arch = foreignArch, - customCompileParams = { - "-target", msvcTriplet, - "-fms-extensions", - "-fms-compatibility", - "-fdelayed-template-parsing", - }, - customLinkParams = { - "-target", msvcTriplet, - extraObjects.utf8init, extraObjects.utf8manifest, - }, - isClang = true, - } - ) + local release, _, _ = generateConfig( + function(arch, profile) + return nameGeneratorClang(lang, arch, profile, false) + end, + programs, + { + arch = foreignArch, + customCompileParams = { + "-target", msvcTriplet, + "-fms-extensions", + "-fms-compatibility", + "-fdelayed-template-parsing", + }, + customLinkParams = { + "-target", msvcTriplet, + extraObjects.utf8init, extraObjects.utf8manifest, + }, + isClang = true, + }) + table.insert(compilerList, release) - end - end - table.insert(noSearch, llvmOrgBinDir) + end + end + table.insert(noSearch, llvmOrgBinDir) + end + + if appArch == "x86_64" then + checkAndAddMingw("x86_64") + checkAndAddClang() + elseif appArch == "arm64" then + checkAndAddClang() + else + checkAndAddMingw("i386") + checkAndAddClang() + end + + local result = { + compilerList = compilerList, + noSearch = noSearch, + preferCompiler = preferCompiler, + } + + return result end - -if appArch == "x86_64" then - checkAndAddMingw("x86_64") - checkAndAddClang() -elseif appArch == "arm64" then - checkAndAddClang() -else - checkAndAddMingw("i386") - checkAndAddClang() -end - -local result = { - compilerList = compilerList, - noSearch = noSearch, - preferCompiler = preferCompiler, -} - -return result