diff --git a/RedPandaIDE/addon/api.cpp b/RedPandaIDE/addon/api.cpp index 8c253010..fd47246d 100644 --- a/RedPandaIDE/addon/api.cpp +++ b/RedPandaIDE/addon/api.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #ifdef Q_OS_WINDOWS # include @@ -236,6 +238,23 @@ extern "C" int luaApi_FileSystem_isExecutable(lua_State *L) noexcept { return 1; } +// C_FileSystem.matchFiles(string, string) -> {string} +extern "C" int luaApi_FileSystem_matchFiles(lua_State *L) noexcept { + QString dir = AddOn::RaiiLuaState::fetchString(L, 1); + QString pattern = AddOn::RaiiLuaState::fetchString(L, 2); + QRegularExpression re(pattern); + QStringList result; + QDirIterator it(dir, QDir::Files); + while (it.hasNext()) { + it.next(); + if (re.match(it.fileName()).hasMatch()) + result.append(it.fileName()); + } + std::sort(result.begin(), result.end()); + AddOn::RaiiLuaState::push(L, result); + return 1; +} + // C_System.appArch() -> string extern "C" int luaApi_System_appArch(lua_State *L) noexcept { AddOn::RaiiLuaState::push(L, appArch()); @@ -269,6 +288,104 @@ extern "C" int luaApi_System_osArch(lua_State *L) noexcept { return 1; } +void luaApiImpl_System_popen(lua_State *L) { + using namespace std::chrono; + QMetaEnum exitStatusEnum = QMetaEnum::fromType(); + QMetaEnum processErrorEnum = QMetaEnum::fromType(); + + QString prog = AddOn::RaiiLuaState::fetchString(L, 1); + QStringList args; + try { + QJsonArray argsArray = AddOn::RaiiLuaState::fetchArray(L, 2); + for (const QJsonValue &arg : argsArray) + args.append(arg.toString()); + } catch (const AddOn::LuaError &e) { + QString reason = e.reason() + " ('C_System.popen' argument #2)"; + throw AddOn::LuaError(reason); + } + + // fetch and normalize timeout + microseconds timeout; + AddOn::LuaExtraState &extraState = AddOn::RaiiLuaState::extraState(L); + try { + QJsonObject option; + if (lua_type(L, 3) == LUA_TTABLE) + option = AddOn::RaiiLuaState::fetchObject(L, 3); + if (option.contains("timeout")) + timeout = milliseconds(option["timeout"].toInt()); + else + timeout = AddOn::RaiiLuaState::extraState(L).timeLimit; + } catch (const AddOn::LuaError &e) { + QString reason = e.reason() + " ('C_System.popen' argument #3)"; + throw AddOn::LuaError(reason); + } + time_point deadline = system_clock::now() + timeout; + if (deadline > extraState.timeStart + timeout) { + deadline = extraState.timeStart + timeout; + timeout = duration_cast(deadline - system_clock::now()); + } + + QProcess process; + process.setProgram(prog); + process.setArguments(args); + process.start(QIODevice::ReadOnly); + if (!process.waitForStarted(duration_cast(timeout).count())) { + QJsonObject result{ + {"exitStatus", exitStatusEnum.valueToKey(QProcess::CrashExit)}, + {"error", processErrorEnum.valueToKey(process.error())}, + }; + AddOn::RaiiLuaState::push(L, ""); + AddOn::RaiiLuaState::push(L, ""); + AddOn::RaiiLuaState::push(L, result); + return; + } + + process.closeWriteChannel(); + timeout = duration_cast(deadline - system_clock::now()); + process.waitForFinished(duration_cast(timeout).count()); + + QByteArray stdoutData = process.readAllStandardOutput(); + QByteArray stderrData = process.readAllStandardError(); + + if ( + QProcess::ExitStatus exitStatus = process.exitStatus(); + exitStatus == QProcess::NormalExit + ) { + QJsonObject result{ + {"exitStatus", exitStatusEnum.valueToKey(exitStatus)}, + {"exitCode", process.exitCode()}, + }; + AddOn::RaiiLuaState::push(L, QString::fromUtf8(stdoutData)); + AddOn::RaiiLuaState::push(L, QString::fromUtf8(stderrData)); + AddOn::RaiiLuaState::push(L, result); + } else { + QJsonObject result{ + {"exitStatus", exitStatusEnum.valueToKey(exitStatus)}, + {"error", processErrorEnum.valueToKey(process.error())}, + }; + AddOn::RaiiLuaState::push(L, ""); + AddOn::RaiiLuaState::push(L, QString::fromUtf8(stderrData)); + AddOn::RaiiLuaState::push(L, result); + } +} + +// C_System.popen(string, {string}, PopenOption) -> string, string, PopenResult +extern "C" int luaApi_System_popen(lua_State *L) noexcept { + bool error = false; + try { + luaApiImpl_System_popen(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 3; + } +} + static QString qtArchitectureNormalization(const QString &arch) { // adjust QEMU user emulation arches to match QSysInfo::currentCpuArchitecture if (arch == "aarch64") diff --git a/RedPandaIDE/addon/api.h b/RedPandaIDE/addon/api.h index 6afd92e5..234adc5d 100644 --- a/RedPandaIDE/addon/api.h +++ b/RedPandaIDE/addon/api.h @@ -30,12 +30,14 @@ extern "C" int luaApi_Desktop_systemStyle(lua_State *L) noexcept; extern "C" int luaApi_FileSystem_exists(lua_State *L) noexcept; extern "C" int luaApi_FileSystem_isExecutable(lua_State *L) noexcept; +extern "C" int luaApi_FileSystem_matchFiles(lua_State *L) noexcept; extern "C" int luaApi_System_appArch(lua_State *L) noexcept; extern "C" int luaApi_System_appDir(lua_State *L) noexcept; extern "C" int luaApi_System_appLibexecDir(lua_State *L) noexcept; extern "C" int luaApi_System_appResourceDir(lua_State *L) noexcept; extern "C" int luaApi_System_osArch(lua_State *L) noexcept; +extern "C" int luaApi_System_popen(lua_State *L) noexcept; extern "C" int luaApi_System_supportedAppArchList(lua_State *L) noexcept; #ifdef Q_OS_WINDOWS extern "C" int luaApi_System_readRegistry(lua_State *L) noexcept; diff --git a/RedPandaIDE/addon/executor.cpp b/RedPandaIDE/addon/executor.cpp index 08efb4eb..2b25fba4 100644 --- a/RedPandaIDE/addon/executor.cpp +++ b/RedPandaIDE/addon/executor.cpp @@ -42,6 +42,7 @@ static QMap> apiGroups{ { {"exists", &luaApi_FileSystem_exists}, // (string) -> bool {"isExecutable", &luaApi_FileSystem_isExecutable}, // (string) -> bool + {"matchFiles", &luaApi_FileSystem_matchFiles}, // (string, string) -> [string] }}, {"C_System", { @@ -50,6 +51,7 @@ static QMap> apiGroups{ {"appLibexecDir", &luaApi_System_appLibexecDir}, // () -> string {"appResourceDir", &luaApi_System_appResourceDir}, // () -> string {"osArch", &luaApi_System_osArch}, // () -> string + {"popen", &luaApi_System_popen}, {"supportedAppArchList", &luaApi_System_supportedAppArchList}, // () -> string #ifdef Q_OS_WINDOWS @@ -161,7 +163,7 @@ QJsonValue SimpleExecutor::runScript(const QByteArray &script, } CompilerHintExecutor::CompilerHintExecutor() : SimpleExecutor( - "compiler_hint", 0, 1, + "compiler_hint", 0, 2, {"C_Debug", "C_Desktop", "C_FileSystem", "C_System", "C_Util"}) { } diff --git a/RedPandaIDE/addon/runtime.cpp b/RedPandaIDE/addon/runtime.cpp index 2b374326..e039e138 100644 --- a/RedPandaIDE/addon/runtime.cpp +++ b/RedPandaIDE/addon/runtime.cpp @@ -98,6 +98,11 @@ QString RaiiLuaState::fetchString(lua_State *L, int index) return lua_tostring(L, index); } +QJsonArray RaiiLuaState::fetchArray(lua_State *L, int index) +{ + return fetchTableImpl(L, index, 0).toArray(); +} + QJsonObject RaiiLuaState::fetchObject(lua_State *L, int index) { return fetchTableImpl(L, index, 0).toObject(); @@ -183,6 +188,16 @@ void RaiiLuaState::push(lua_State *L, const QStringList &value) } } +void RaiiLuaState::push(lua_State *L, const QJsonArray &value) +{ + pushArrayImpl(L, value, 0); +} + +void RaiiLuaState::push(lua_State *L, const QJsonObject &value) +{ + pushObjectImpl(L, value, 0); +} + int RaiiLuaState::getTop() { return lua_gettop(mLua); @@ -313,6 +328,56 @@ QJsonValue RaiiLuaState::fetchValueImpl(lua_State *L, int index, int depth) } } +void RaiiLuaState::pushArrayImpl(lua_State *L, const QJsonArray &value, int depth) +{ + if (depth == 1) + // check stack size at first recursion to avoid multiple reallocations + lua_checkstack(L, LUA_STACK_SIZE); + if (depth > TABLE_MAX_DEPTH) + throw LuaError("Lua runtime error: table nested too deeply"); + + lua_newtable(L); + for (int i = 0; i < value.size(); i++) { + push(L, i + 1); + pushValueImpl(L, value[i], depth); + lua_settable(L, -3); + } +} + +void RaiiLuaState::pushObjectImpl(lua_State *L, const QJsonObject &value, int depth) +{ + if (depth == 1) + // check stack size at first recursion to avoid multiple reallocations + lua_checkstack(L, LUA_STACK_SIZE); + if (depth > TABLE_MAX_DEPTH) + throw LuaError("Lua runtime error: table nested too deeply"); + + lua_newtable(L); + for (auto it = value.begin(); it != value.end(); ++it) { + push(L, it.key()); + pushValueImpl(L, it.value(), depth); + lua_settable(L, -3); + } +} + +void RaiiLuaState::pushValueImpl(lua_State *L, const QJsonValue &value, int depth) +{ + if (value.isNull()) + lua_pushnil(L); + else if (value.isBool()) + lua_pushboolean(L, value.toBool()); + else if (value.isDouble()) + lua_pushnumber(L, value.toDouble()); + else if (value.isString()) + lua_pushstring(L, value.toString().toUtf8().constData()); + else if (value.isObject()) + pushObjectImpl(L, value.toObject(), depth + 1); + else if (value.isArray()) + pushArrayImpl(L, value.toArray(), depth + 1); + else + throw LuaError("Lua type error: unknown type."); +} + QHash RaiiLuaState::mExtraState; } // namespace AddOn diff --git a/RedPandaIDE/addon/runtime.h b/RedPandaIDE/addon/runtime.h index c5b2c8b3..32cf5f7b 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 QJsonArray fetchArray(lua_State *L, int index); static QJsonObject fetchObject(lua_State *L, int index); static QJsonValue fetch(lua_State *L, int index); @@ -88,6 +89,8 @@ public: static void push(lua_State *L, bool value); static void push(lua_State *L, const QString &value); static void push(lua_State *L, const QStringList &value); + static void push(lua_State *L, const QJsonArray &value); + static void push(lua_State *L, const QJsonObject &value); int getTop(); static int getTop(lua_State *L); @@ -107,6 +110,10 @@ private: static QJsonValue fetchTableImpl(lua_State *L, int index, int depth); static QJsonValue fetchValueImpl(lua_State *L, int index, int depth); + static void pushArrayImpl(lua_State *L, const QJsonArray &value, int depth); + static void pushObjectImpl(lua_State *L, const QJsonObject &value, int depth); + static void pushValueImpl(lua_State *L, const QJsonValue &value, int depth); + private: lua_State *mLua; static QHash mExtraState; diff --git a/addon/compiler_hint/archlinux.tl b/addon/compiler_hint/archlinux.tl index 94518103..3241b02d 100644 --- a/addon/compiler_hint/archlinux.tl +++ b/addon/compiler_hint/archlinux.tl @@ -2,73 +2,77 @@ global function apiVersion(): ApiVersion return { kind = "compiler_hint", major = 0, - minor = 1, + minor = 2, } end -local nameMap: {string:{string:string}} = { - systemGcc = { - en_US = "System GCC", - pt_BR = "GCC do sistema", - zh_CN = "系统 GCC", - zh_TW = "系統 GCC", +local enum CompilerKind + "system" + "multilib" + "cross" + "mingw" +end + +local compilerNameTemplate: {CompilerKind:{string:string}} = { + system = { + en_US = "System %1, %2", + pt_BR = "%1 sistema, %2", + zh_CN = "系统 %1,%2", + zh_TW = "系統 %1,%2", }, - systemClang = { - en_US = "System Clang", - pt_BR = "Clang do sistema", - zh_CN = "系统 Clang", - zh_TW = "系統 Clang", + multilib = { + en_US = "Multilib %1, %2", + pt_BR = "%1 multilib, %2", + zh_CN = "Multilib %1,%2", + zh_TW = "Multilib %1,%2", }, - multilibGcc = { - en_US = "Multilib GCC", - pt_BR = "GCC multilib", - zh_CN = "Multilib GCC", - zh_TW = "Multilib GCC", + cross = { + en_US = "Cross %1 %3, %2", + pt_BR = "%1 cruzado %3, %2", + zh_CN = "交叉编译 %1 %3,%2", + zh_TW = "交叉編譯 %1 %3,%2", }, - 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 偵錯", + mingw = { + en_US = "MinGW %1 %3, %2", + pt_BR = "MinGW %1 %3, %2", + zh_CN = "MinGW %1 %3,%2", + zh_TW = "MinGW %1 %3,%2", }, } +local enum Profile + "release" + "debug" + "debugWithAsan" +end + +local profileNameMap: {Profile:{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 nameGenerator(lang: string, name: string, kind: CompilerKind, profile: Profile, arch: string | nil): string + local template = compilerNameTemplate[kind][lang] or compilerNameTemplate[kind].en_US + local profileName = profileNameMap[profile][lang] or profileNameMap[profile].en_US + return C_Util.format(template, name, profileName, arch) +end + local function mergeCompilerSet(compilerSet: CompilerHint.CompilerSet, other: CompilerHint.CompilerSet) local c = compilerSet as {string:any} local o = other as {string:any} @@ -78,16 +82,14 @@ local function mergeCompilerSet(compilerSet: CompilerHint.CompilerSet, other: Co end local record Config - isMultilib: boolean | nil - isMingw: boolean | nil - isClang: boolean | nil customCompileParams: {string} | nil customLinkParams: {string} | nil + arch: string | nil triplet: string | nil end local function generateConfig( - name: string, lang: string, + lang: string, name: string, kind: CompilerKind, cCompiler: string, cxxCompiler: string, config: Config ): CompilerHint.CompilerSet, CompilerHint.CompilerSet, CompilerHint.CompilerSet @@ -97,11 +99,11 @@ local function generateConfig( debugger = "/usr/bin/gdb", debugServer = "/usr/bin/gdbserver", make = "/usr/bin/make", - compilerType = config.isClang and "Clang" or "GCC_UTF8", + compilerType = name:sub(1, 5) == "Clang" and "Clang" or "GCC_UTF8", preprocessingSuffix = ".i", compilationProperSuffix = ".s", assemblingSuffix = ".o", - executableSuffix = config.isMingw and ".exe" or "", + executableSuffix = kind == "mingw" and ".exe" or "", compilationStage = 3, ccCmdOptUsePipe = "on", ccCmdOptWarningAll = "on", @@ -109,10 +111,10 @@ local function generateConfig( ccCmdOptCheckIsoConformance = "on", binDirs = {"/usr/bin"}, } - if config.isMultilib then + if kind == "multilib" then commonOptions.ccCmdOptPointerSize = "32" end - if config.isMingw then + if kind == "mingw" then commonOptions.resourceCompiler = "/usr/bin/" .. config.triplet .. "-windres" end if config.customCompileParams then @@ -122,17 +124,17 @@ local function generateConfig( commonOptions.customLinkParams = config.customLinkParams end local release = { - name = name .. (nameMap.release[lang] or nameMap.release.en_US), + name = nameGenerator(lang, name, kind, "release", config.arch), staticLink = true, linkCmdOptStripExe = "on", ccCmdOptOptimize = "2", } local debug_ = { - name = name .. (nameMap.debug[lang] or nameMap.debug.en_US), + name = nameGenerator(lang, name, kind, "debug", config.arch), ccCmdOptDebugInfo = "on", } local debugWithAsan = { - name = name .. (nameMap.debugWithAsan[lang] or nameMap.debugWithAsan.en_US), + name = nameGenerator(lang, name, kind, "debugWithAsan", config.arch), ccCmdOptDebugInfo = "on", ccCmdOptAddressSanitizer = "address", } @@ -151,8 +153,20 @@ global function main(): CompilerHint do local release, debug_, debugWithAsan = generateConfig( - nameMap.systemGcc[lang] or nameMap.systemGcc.en_US, lang, - "/usr/bin/gcc", "/usr/bin/g++", + lang, "GCC", "system", "/usr/bin/gcc", "/usr/bin/g++", + {} + ) + table.insert(compilerList, release) + table.insert(compilerList, debug_) + table.insert(compilerList, debugWithAsan) + end + + local versionedGccs = C_FileSystem.matchFiles("/usr/bin", "^gcc-[0-9]+$") + for _, gcc in ipairs(versionedGccs) do + local version = gcc:sub(5) + local name = "GCC " .. version + local release, debug_, debugWithAsan = generateConfig( + lang, name, "system", "/usr/bin/" .. gcc, "/usr/bin/g++-" .. version, {} ) table.insert(compilerList, release) @@ -162,9 +176,8 @@ global function main(): CompilerHint 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} + lang, "Clang", "system", "/usr/bin/clang", "/usr/bin/clang++", + {} ) table.insert(compilerList, release) table.insert(compilerList, debug_) @@ -175,9 +188,8 @@ global function main(): CompilerHint 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} + lang, "GCC", "multilib", "/usr/bin/gcc", "/usr/bin/g++", + {} ) table.insert(compilerList, release) table.insert(compilerList, debug_) @@ -186,9 +198,8 @@ global function main(): CompilerHint 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} + lang, "Clang", "multilib", "/usr/bin/clang", "/usr/bin/clang++", + {} ) table.insert(compilerList, release) table.insert(compilerList, debug_) @@ -203,9 +214,9 @@ global function main(): CompilerHint C_FileSystem.isExecutable("/usr/bin/aarch64-linux-gnu-gcc") ) then local release, _, _ = generateConfig( - (nameMap.crossGcc[lang] or nameMap.crossGcc.en_US) .. " aarch64", lang, + lang, "GCC", "cross", "/usr/bin/aarch64-linux-gnu-gcc", "/usr/bin/aarch64-linux-gnu-g++", - {} + {arch = "aarch64"} ) table.insert(compilerList, release) end @@ -225,10 +236,10 @@ global function main(): CompilerHint do local release, _, _ = generateConfig( - (nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " x86_64", lang, + lang, "GCC", "mingw", "/usr/bin/x86_64-w64-mingw32-gcc", "/usr/bin/x86_64-w64-mingw32-g++", { - isMingw = true, + arch = "x86_64", triplet = "x86_64-w64-mingw32", customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest}, } @@ -239,11 +250,9 @@ global function main(): CompilerHint -- 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++", + lang, "Clang", "mingw", "/usr/bin/clang", "/usr/bin/clang++", { - isClang = true, - isMingw = true, + arch = "x86_64", triplet = "x86_64-w64-mingw32", customCompileParams = {"-target", "x86_64-w64-mingw32"}, customLinkParams = { @@ -265,10 +274,10 @@ global function main(): CompilerHint do local release, _, _ = generateConfig( - (nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " i686", lang, + lang, "GCC", "mingw", "/usr/bin/i686-w64-mingw32-gcc", "/usr/bin/i686-w64-mingw32-g++", { - isMingw = true, + arch = "i686", triplet = "i686-w64-mingw32", customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest}, } @@ -279,11 +288,10 @@ global function main(): CompilerHint -- 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, + lang, "Clang", "mingw", "/usr/bin/clang", "/usr/bin/clang++", { - isClang = true, - isMingw = true, + arch = "i686", triplet = "i686-w64-mingw32", customCompileParams = {"-target", "i686-w64-mingw32"}, customLinkParams = { diff --git a/addon/compiler_hint/debian.tl b/addon/compiler_hint/debian.tl index 75bf7bd1..f2187394 100644 --- a/addon/compiler_hint/debian.tl +++ b/addon/compiler_hint/debian.tl @@ -2,61 +2,84 @@ global function apiVersion(): ApiVersion return { kind = "compiler_hint", major = 0, - minor = 1, + minor = 2, } end -local nameMap: {string:{string:string}} = { - systemGcc = { - en_US = "System GCC", - pt_BR = "GCC do sistema", - zh_CN = "系统 GCC", - zh_TW = "系統 GCC", +local enum CompilerKind + "system" + "multilib" + "libx32" + "cross" + "mingw" +end + +local compilerNameTemplate: {CompilerKind:{string:string}} = { + system = { + en_US = "System %1, %2", + pt_BR = "%1 sistema, %2", + zh_CN = "系统 %1,%2", + zh_TW = "系統 %1,%2", }, - systemClang = { - en_US = "System Clang", - pt_BR = "Clang do sistema", - zh_CN = "系统 Clang", - zh_TW = "系統 Clang", + multilib = { + en_US = "Multilib %1, %2", + pt_BR = "%1 multilib, %2", + zh_CN = "Multilib %1,%2", + zh_TW = "Multilib %1,%2", }, - multilibGcc = { - en_US = "Multilib GCC", - pt_BR = "GCC multilib", - zh_CN = "Multilib GCC", - zh_TW = "Multilib GCC", + libx32 = { + en_US = "Libx32 %1, %2", + pt_BR = "%1 libx32, %2", + zh_CN = "Libx32 %1,%2", + zh_TW = "Libx32 %1,%2", }, - multilibClang = { - en_US = "Multilib Clang", - pt_BR = "Clang multilib", - zh_CN = "Multilib Clang", - zh_TW = "Multilib Clang", + cross = { + en_US = "Cross %1 %3, %2", + pt_BR = "%1 cruzado %3, %2", + zh_CN = "交叉编译 %1 %3,%2", + zh_TW = "交叉編譯 %1 %3,%2", }, - mingwGcc = { - en_US = "MinGW GCC", - pt_BR = "GCC MinGW", - zh_CN = "MinGW GCC", - zh_TW = "MinGW GCC", - }, - 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 偵錯", + mingw = { + en_US = "MinGW %1 %3, %2", + pt_BR = "MinGW %1 %3, %2", + zh_CN = "MinGW %1 %3,%2", + zh_TW = "MinGW %1 %3,%2", }, } +local enum Profile + "release" + "debug" + "debugWithAsan" +end + +local profileNameMap: {Profile:{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 nameGenerator(lang: string, name: string, kind: CompilerKind, profile: Profile, arch: string | nil): string + local template = compilerNameTemplate[kind][lang] or compilerNameTemplate[kind].en_US + local profileName = profileNameMap[profile][lang] or profileNameMap[profile].en_US + return C_Util.format(template, name, profileName, arch) +end + local function mergeCompilerSet(compilerSet: CompilerHint.CompilerSet, other: CompilerHint.CompilerSet) local c = compilerSet as {string:any} local o = other as {string:any} @@ -66,16 +89,14 @@ local function mergeCompilerSet(compilerSet: CompilerHint.CompilerSet, other: Co end local record Config - isMultilib: boolean | nil - isMingw: boolean | nil - isClang: boolean | nil customCompileParams: {string} | nil customLinkParams: {string} | nil + arch: string | nil triplet: string | nil end local function generateConfig( - name: string, lang: string, + lang: string, name: string, kind: CompilerKind, cCompiler: string, cxxCompiler: string, config: Config ): CompilerHint.CompilerSet, CompilerHint.CompilerSet, CompilerHint.CompilerSet @@ -85,11 +106,11 @@ local function generateConfig( debugger = "/usr/bin/gdb", debugServer = "/usr/bin/gdbserver", make = "/usr/bin/make", - compilerType = config.isClang and "Clang" or "GCC_UTF8", + compilerType = name:sub(1, 5) == "Clang" and "Clang" or "GCC_UTF8", preprocessingSuffix = ".i", compilationProperSuffix = ".s", assemblingSuffix = ".o", - executableSuffix = config.isMingw and ".exe" or "", + executableSuffix = kind == "mingw" and ".exe" or "", compilationStage = 3, ccCmdOptUsePipe = "on", ccCmdOptWarningAll = "on", @@ -97,10 +118,10 @@ local function generateConfig( ccCmdOptCheckIsoConformance = "on", binDirs = {"/usr/bin"}, } - if config.isMultilib then + if kind == "multilib" then commonOptions.ccCmdOptPointerSize = "32" end - if config.isMingw then + if kind == "mingw" then commonOptions.resourceCompiler = "/usr/bin/" .. config.triplet .. "-windres" end if config.customCompileParams then @@ -110,17 +131,17 @@ local function generateConfig( commonOptions.customLinkParams = config.customLinkParams end local release = { - name = name .. (nameMap.release[lang] or nameMap.release.en_US), + name = nameGenerator(lang, name, kind, "release", config.arch), staticLink = true, linkCmdOptStripExe = "on", ccCmdOptOptimize = "2", } local debug_ = { - name = name .. (nameMap.debug[lang] or nameMap.debug.en_US), + name = nameGenerator(lang, name, kind, "debug", config.arch), ccCmdOptDebugInfo = "on", } local debugWithAsan = { - name = name .. (nameMap.debugWithAsan[lang] or nameMap.debugWithAsan.en_US), + name = nameGenerator(lang, name, kind, "debugWithAsan", config.arch), ccCmdOptDebugInfo = "on", ccCmdOptAddressSanitizer = "address", } @@ -137,10 +158,28 @@ global function main(): CompilerHint local compilerList = {} + local gccDumpVersion = C_System.popen("/usr/bin/gcc", {"-dumpfullversion"}) + gccDumpVersion = gccDumpVersion:match("^[0-9.]+") + local gccFsVersion = C_System.popen("/usr/bin/gcc", {"-dumpversion"}) + gccFsVersion = gccFsVersion:match("^[0-9.]+") + local clangDumpVersion: string + do local release, debug_, debugWithAsan = generateConfig( - nameMap.systemGcc[lang] or nameMap.systemGcc.en_US, lang, - "/usr/bin/gcc", "/usr/bin/g++", + lang, "GCC (" .. gccDumpVersion .. ")", "system", "/usr/bin/gcc", "/usr/bin/g++", + {} + ) + table.insert(compilerList, release) + table.insert(compilerList, debug_) + table.insert(compilerList, debugWithAsan) + end + + local versionedGccs = C_FileSystem.matchFiles("/usr/bin", "^gcc-[0-9]+$") + for _, gcc in ipairs(versionedGccs) do + local version = gcc:sub(5) + local name = "GCC " .. version + local release, debug_, debugWithAsan = generateConfig( + lang, name, "system", "/usr/bin/" .. gcc, "/usr/bin/g++-" .. version, {} ) table.insert(compilerList, release) @@ -149,23 +188,36 @@ global function main(): CompilerHint end if C_FileSystem.isExecutable("/usr/bin/clang") then + clangDumpVersion = C_System.popen("/usr/bin/clang", {"-dumpversion"}) + clangDumpVersion = clangDumpVersion:match("^[0-9.]+") local release, debug_, debugWithAsan = generateConfig( - nameMap.systemClang[lang] or nameMap.systemClang.en_US, lang, - "/usr/bin/clang", "/usr/bin/clang++", - {isClang = true} + lang, "Clang (" .. clangDumpVersion .. ")", "system", "/usr/bin/clang", "/usr/bin/clang++", + {} ) table.insert(compilerList, release) table.insert(compilerList, debug_) table.insert(compilerList, debugWithAsan) end - -- TODO: better to check 32-bit libstdc++.a (lib32stdc++-dev), but its path is not fixed - if arch == "x86_64" and C_FileSystem.exists("/usr/lib32/libc.a") then + local versionedClangs = C_FileSystem.matchFiles("/usr/bin", "^clang-[0-9]+$") + for _, clang in ipairs(versionedClangs) do + local version = clang:sub(7) + local name = "Clang " .. version + local release, debug_, debugWithAsan = generateConfig( + lang, name, "system", "/usr/bin/" .. clang, "/usr/bin/clang++-" .. version, + {} + ) + table.insert(compilerList, release) + table.insert(compilerList, debug_) + table.insert(compilerList, debugWithAsan) + end + + -- with lib32stdc++-dev installed, system GCC and Clang can target 32-bit + if arch == "x86_64" and C_FileSystem.exists("/usr/lib/gcc/x86_64-linux-gnu/" .. gccFsVersion .. "/32/libstdc++.a") then do local release, debug_, debugWithAsan = generateConfig( - nameMap.multilibGcc[lang] or nameMap.multilibGcc.en_US, lang, - "/usr/bin/gcc", "/usr/bin/g++", - {isMultilib = true} + lang, "GCC (" .. gccDumpVersion .. ")", "multilib", "/usr/bin/gcc", "/usr/bin/g++", + {} ) table.insert(compilerList, release) table.insert(compilerList, debug_) @@ -174,9 +226,37 @@ global function main(): CompilerHint 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} + lang, "Clang (" .. clangDumpVersion .. ")", "multilib", "/usr/bin/clang", "/usr/bin/clang++", + {} + ) + table.insert(compilerList, release) + table.insert(compilerList, debug_) + table.insert(compilerList, debugWithAsan) + end + end + + -- with libx32stdc++-dev installed, system GCC and Clang can target x32 + if arch == "x86_64" and C_FileSystem.exists("/usr/lib/gcc/x86_64-linux-gnu/" .. gccFsVersion .. "/x32/libstdc++.a") then + do + local release, debug_, debugWithAsan = generateConfig( + lang, "GCC (" .. gccDumpVersion .. ")", "libx32", "/usr/bin/gcc", "/usr/bin/g++", + { + customCompileParams = {"-mx32"}, + customLinkParams = {"-mx32"}, + } + ) + 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( + lang, "Clang (" .. clangDumpVersion .. ")", "libx32", "/usr/bin/clang", "/usr/bin/clang++", + { + customCompileParams = {"-mx32"}, + customLinkParams = {"-mx32"}, + } ) table.insert(compilerList, release) table.insert(compilerList, debug_) @@ -199,10 +279,10 @@ global function main(): CompilerHint do local release, _, _ = generateConfig( - (nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " x86_64", lang, + lang, "GCC", "mingw", "/usr/bin/x86_64-w64-mingw32-gcc", "/usr/bin/x86_64-w64-mingw32-g++", { - isMingw = true, + arch = "x86_64", triplet = "x86_64-w64-mingw32", customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest}, } @@ -219,10 +299,10 @@ global function main(): CompilerHint do local release, _, _ = generateConfig( - (nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " i686", lang, + lang, "GCC", "mingw", "/usr/bin/i686-w64-mingw32-gcc", "/usr/bin/i686-w64-mingw32-g++", { - isMingw = true, + arch = "i686", triplet = "i686-w64-mingw32", customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest}, } diff --git a/addon/compiler_hint/windows_domain.tl b/addon/compiler_hint/windows_domain.tl index 7ebdcfbc..2830d468 100644 --- a/addon/compiler_hint/windows_domain.tl +++ b/addon/compiler_hint/windows_domain.tl @@ -2,7 +2,7 @@ global function apiVersion(): ApiVersion return { kind = "compiler_hint", major = 0, - minor = 1, + minor = 2, } end diff --git a/addon/defs/global_env.d.tl b/addon/defs/global_env.d.tl index aa47edff..9ffe2020 100644 --- a/addon/defs/global_env.d.tl +++ b/addon/defs/global_env.d.tl @@ -53,6 +53,10 @@ local record env -- return whether the path is executable isExecutable: function (path: string): boolean + + -- return matched files in the directory (non-recursive), with Qt-style regex + -- e.g. matchFiles("/usr/bin", "^gcc-[0-9]+$") may return {"gcc-12", "gcc-13"} + matchFiles: function (dir: string, qtRegex: string): {string} end record C_System @@ -78,6 +82,9 @@ local record env -- Windows arm64 is handled correctly even if Red Panda C++ runs under emulation osArch: function (): string + -- returns (1) stdout, (2) stderr and (3) exit status of the program + popen: function(prog: string, args: {string}, option: PopenOption | nil): string, string, PopenResult + -- returns supported application architectures by OS, name following `QSysInfo` -- e.g. {"i386", "x86_64", "arm64"} -- Windows 10 1709 or later: accurate result @@ -89,6 +96,30 @@ local record env -- read `subKey\name` from HKEY_CURRENT_USER and HKEY_LOCAL_MACHINE in order (Windows only) readRegistry: function (subKey: string, name: string): string | nil + + record PopenOption + timeout: integer | nil -- in milliseconds + end + + record PopenResult + exitStatus: QProcessExitStatus + error: QProcessProcessError | nil + exitCode: integer | nil -- only valid if exitStatus is NormalExit + end + + enum QProcessExitStatus + "NormalExit" + "CrashExit" + end + + enum QProcessProcessError + "FailedToStart" + "Crashed" + "Timedout" + "WriteError" + "ReadError" + "UnknownError" + end end record C_Util diff --git a/packages/archlinux/compiler_hint.lua b/packages/archlinux/compiler_hint.lua index 5d509fe3..9ebb7b81 100644 --- a/packages/archlinux/compiler_hint.lua +++ b/packages/archlinux/compiler_hint.lua @@ -2,73 +2,77 @@ function apiVersion() return { kind = "compiler_hint", major = 0, - minor = 1, + minor = 2, } end -local nameMap = { - systemGcc = { - en_US = "System GCC", - pt_BR = "GCC do sistema", - zh_CN = "系统 GCC", - zh_TW = "系統 GCC", + + + + + + + +local compilerNameTemplate = { + system = { + en_US = "System %1, %2", + pt_BR = "%1 sistema, %2", + zh_CN = "系统 %1,%2", + zh_TW = "系統 %1,%2", }, - systemClang = { - en_US = "System Clang", - pt_BR = "Clang do sistema", - zh_CN = "系统 Clang", - zh_TW = "系統 Clang", + multilib = { + en_US = "Multilib %1, %2", + pt_BR = "%1 multilib, %2", + zh_CN = "Multilib %1,%2", + zh_TW = "Multilib %1,%2", }, - multilibGcc = { - en_US = "Multilib GCC", - pt_BR = "GCC multilib", - zh_CN = "Multilib GCC", - zh_TW = "Multilib GCC", + cross = { + en_US = "Cross %1 %3, %2", + pt_BR = "%1 cruzado %3, %2", + zh_CN = "交叉编译 %1 %3,%2", + zh_TW = "交叉編譯 %1 %3,%2", }, - 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 偵錯", + mingw = { + en_US = "MinGW %1 %3, %2", + pt_BR = "MinGW %1 %3, %2", + zh_CN = "MinGW %1 %3,%2", + zh_TW = "MinGW %1 %3,%2", }, } + + + + + + +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 偵錯", + }, +} + +local function nameGenerator(lang, name, kind, profile, arch) + local template = compilerNameTemplate[kind][lang] or compilerNameTemplate[kind].en_US + local profileName = profileNameMap[profile][lang] or profileNameMap[profile].en_US + return C_Util.format(template, name, profileName, arch) +end + local function mergeCompilerSet(compilerSet, other) local c = compilerSet local o = other @@ -84,10 +88,8 @@ end - - local function generateConfig( - name, lang, + lang, name, kind, cCompiler, cxxCompiler, config) @@ -97,11 +99,11 @@ local function generateConfig( debugger = "/usr/bin/gdb", debugServer = "/usr/bin/gdbserver", make = "/usr/bin/make", - compilerType = config.isClang and "Clang" or "GCC_UTF8", + compilerType = name:sub(1, 5) == "Clang" and "Clang" or "GCC_UTF8", preprocessingSuffix = ".i", compilationProperSuffix = ".s", assemblingSuffix = ".o", - executableSuffix = config.isMingw and ".exe" or "", + executableSuffix = kind == "mingw" and ".exe" or "", compilationStage = 3, ccCmdOptUsePipe = "on", ccCmdOptWarningAll = "on", @@ -109,10 +111,10 @@ local function generateConfig( ccCmdOptCheckIsoConformance = "on", binDirs = { "/usr/bin" }, } - if config.isMultilib then + if kind == "multilib" then commonOptions.ccCmdOptPointerSize = "32" end - if config.isMingw then + if kind == "mingw" then commonOptions.resourceCompiler = "/usr/bin/" .. config.triplet .. "-windres" end if config.customCompileParams then @@ -122,17 +124,17 @@ local function generateConfig( commonOptions.customLinkParams = config.customLinkParams end local release = { - name = name .. (nameMap.release[lang] or nameMap.release.en_US), + name = nameGenerator(lang, name, kind, "release", config.arch), staticLink = true, linkCmdOptStripExe = "on", ccCmdOptOptimize = "2", } local debug_ = { - name = name .. (nameMap.debug[lang] or nameMap.debug.en_US), + name = nameGenerator(lang, name, kind, "debug", config.arch), ccCmdOptDebugInfo = "on", } local debugWithAsan = { - name = name .. (nameMap.debugWithAsan[lang] or nameMap.debugWithAsan.en_US), + name = nameGenerator(lang, name, kind, "debugWithAsan", config.arch), ccCmdOptDebugInfo = "on", ccCmdOptAddressSanitizer = "address", } @@ -151,8 +153,20 @@ function main() do local release, debug_, debugWithAsan = generateConfig( - nameMap.systemGcc[lang] or nameMap.systemGcc.en_US, lang, - "/usr/bin/gcc", "/usr/bin/g++", + lang, "GCC", "system", "/usr/bin/gcc", "/usr/bin/g++", + {}) + + table.insert(compilerList, release) + table.insert(compilerList, debug_) + table.insert(compilerList, debugWithAsan) + end + + local versionedGccs = C_FileSystem.matchFiles("/usr/bin", "^gcc-[0-9]+$") + for _, gcc in ipairs(versionedGccs) do + local version = gcc:sub(5) + local name = "GCC " .. version + local release, debug_, debugWithAsan = generateConfig( + lang, name, "system", "/usr/bin/" .. gcc, "/usr/bin/g++-" .. version, {}) table.insert(compilerList, release) @@ -162,9 +176,8 @@ function main() 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 }) + lang, "Clang", "system", "/usr/bin/clang", "/usr/bin/clang++", + {}) table.insert(compilerList, release) table.insert(compilerList, debug_) @@ -175,9 +188,8 @@ function main() 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 }) + lang, "GCC", "multilib", "/usr/bin/gcc", "/usr/bin/g++", + {}) table.insert(compilerList, release) table.insert(compilerList, debug_) @@ -186,9 +198,8 @@ function main() 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 }) + lang, "Clang", "multilib", "/usr/bin/clang", "/usr/bin/clang++", + {}) table.insert(compilerList, release) table.insert(compilerList, debug_) @@ -203,9 +214,9 @@ function main() C_FileSystem.isExecutable("/usr/bin/aarch64-linux-gnu-gcc")) then local release, _, _ = generateConfig( - (nameMap.crossGcc[lang] or nameMap.crossGcc.en_US) .. " aarch64", lang, + lang, "GCC", "cross", "/usr/bin/aarch64-linux-gnu-gcc", "/usr/bin/aarch64-linux-gnu-g++", - {}) + { arch = "aarch64" }) table.insert(compilerList, release) end @@ -225,10 +236,10 @@ function main() do local release, _, _ = generateConfig( - (nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " x86_64", lang, + lang, "GCC", "mingw", "/usr/bin/x86_64-w64-mingw32-gcc", "/usr/bin/x86_64-w64-mingw32-g++", { - isMingw = true, + arch = "x86_64", triplet = "x86_64-w64-mingw32", customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest }, }) @@ -239,11 +250,9 @@ function main() 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++", + lang, "Clang", "mingw", "/usr/bin/clang", "/usr/bin/clang++", { - isClang = true, - isMingw = true, + arch = "x86_64", triplet = "x86_64-w64-mingw32", customCompileParams = { "-target", "x86_64-w64-mingw32" }, customLinkParams = { @@ -265,10 +274,10 @@ function main() do local release, _, _ = generateConfig( - (nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " i686", lang, + lang, "GCC", "mingw", "/usr/bin/i686-w64-mingw32-gcc", "/usr/bin/i686-w64-mingw32-g++", { - isMingw = true, + arch = "i686", triplet = "i686-w64-mingw32", customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest }, }) @@ -279,11 +288,10 @@ function main() if C_FileSystem.isExecutable("/usr/bin/clang") then local release, _, _ = generateConfig( - (nameMap.mingwClang[lang] or nameMap.mingwClang.en_US) .. " i686", lang, + lang, "Clang", "mingw", "/usr/bin/clang", "/usr/bin/clang++", { - isClang = true, - isMingw = true, + arch = "i686", triplet = "i686-w64-mingw32", customCompileParams = { "-target", "i686-w64-mingw32" }, customLinkParams = { diff --git a/packages/debian/compiler_hint.lua b/packages/debian/compiler_hint.lua index e8c7f13a..454435e5 100644 --- a/packages/debian/compiler_hint.lua +++ b/packages/debian/compiler_hint.lua @@ -2,61 +2,84 @@ function apiVersion() return { kind = "compiler_hint", major = 0, - minor = 1, + minor = 2, } end -local nameMap = { - systemGcc = { - en_US = "System GCC", - pt_BR = "GCC do sistema", - zh_CN = "系统 GCC", - zh_TW = "系統 GCC", + + + + + + + + +local compilerNameTemplate = { + system = { + en_US = "System %1, %2", + pt_BR = "%1 sistema, %2", + zh_CN = "系统 %1,%2", + zh_TW = "系統 %1,%2", }, - systemClang = { - en_US = "System Clang", - pt_BR = "Clang do sistema", - zh_CN = "系统 Clang", - zh_TW = "系統 Clang", + multilib = { + en_US = "Multilib %1, %2", + pt_BR = "%1 multilib, %2", + zh_CN = "Multilib %1,%2", + zh_TW = "Multilib %1,%2", }, - multilibGcc = { - en_US = "Multilib GCC", - pt_BR = "GCC multilib", - zh_CN = "Multilib GCC", - zh_TW = "Multilib GCC", + libx32 = { + en_US = "Libx32 %1, %2", + pt_BR = "%1 libx32, %2", + zh_CN = "Libx32 %1,%2", + zh_TW = "Libx32 %1,%2", }, - multilibClang = { - en_US = "Multilib Clang", - pt_BR = "Clang multilib", - zh_CN = "Multilib Clang", - zh_TW = "Multilib Clang", + cross = { + en_US = "Cross %1 %3, %2", + pt_BR = "%1 cruzado %3, %2", + zh_CN = "交叉编译 %1 %3,%2", + zh_TW = "交叉編譯 %1 %3,%2", }, - mingwGcc = { - en_US = "MinGW GCC", - pt_BR = "GCC MinGW", - zh_CN = "MinGW GCC", - zh_TW = "MinGW GCC", - }, - 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 偵錯", + mingw = { + en_US = "MinGW %1 %3, %2", + pt_BR = "MinGW %1 %3, %2", + zh_CN = "MinGW %1 %3,%2", + zh_TW = "MinGW %1 %3,%2", }, } + + + + + + +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 偵錯", + }, +} + +local function nameGenerator(lang, name, kind, profile, arch) + local template = compilerNameTemplate[kind][lang] or compilerNameTemplate[kind].en_US + local profileName = profileNameMap[profile][lang] or profileNameMap[profile].en_US + return C_Util.format(template, name, profileName, arch) +end + local function mergeCompilerSet(compilerSet, other) local c = compilerSet local o = other @@ -72,10 +95,8 @@ end - - local function generateConfig( - name, lang, + lang, name, kind, cCompiler, cxxCompiler, config) @@ -85,11 +106,11 @@ local function generateConfig( debugger = "/usr/bin/gdb", debugServer = "/usr/bin/gdbserver", make = "/usr/bin/make", - compilerType = config.isClang and "Clang" or "GCC_UTF8", + compilerType = name:sub(1, 5) == "Clang" and "Clang" or "GCC_UTF8", preprocessingSuffix = ".i", compilationProperSuffix = ".s", assemblingSuffix = ".o", - executableSuffix = config.isMingw and ".exe" or "", + executableSuffix = kind == "mingw" and ".exe" or "", compilationStage = 3, ccCmdOptUsePipe = "on", ccCmdOptWarningAll = "on", @@ -97,10 +118,10 @@ local function generateConfig( ccCmdOptCheckIsoConformance = "on", binDirs = { "/usr/bin" }, } - if config.isMultilib then + if kind == "multilib" then commonOptions.ccCmdOptPointerSize = "32" end - if config.isMingw then + if kind == "mingw" then commonOptions.resourceCompiler = "/usr/bin/" .. config.triplet .. "-windres" end if config.customCompileParams then @@ -110,17 +131,17 @@ local function generateConfig( commonOptions.customLinkParams = config.customLinkParams end local release = { - name = name .. (nameMap.release[lang] or nameMap.release.en_US), + name = nameGenerator(lang, name, kind, "release", config.arch), staticLink = true, linkCmdOptStripExe = "on", ccCmdOptOptimize = "2", } local debug_ = { - name = name .. (nameMap.debug[lang] or nameMap.debug.en_US), + name = nameGenerator(lang, name, kind, "debug", config.arch), ccCmdOptDebugInfo = "on", } local debugWithAsan = { - name = name .. (nameMap.debugWithAsan[lang] or nameMap.debugWithAsan.en_US), + name = nameGenerator(lang, name, kind, "debugWithAsan", config.arch), ccCmdOptDebugInfo = "on", ccCmdOptAddressSanitizer = "address", } @@ -137,10 +158,28 @@ function main() local compilerList = {} + local gccDumpVersion = C_System.popen("/usr/bin/gcc", { "-dumpfullversion" }) + gccDumpVersion = gccDumpVersion:match("^[0-9.]+") + local gccFsVersion = C_System.popen("/usr/bin/gcc", { "-dumpversion" }) + gccFsVersion = gccFsVersion:match("^[0-9.]+") + local clangDumpVersion + do local release, debug_, debugWithAsan = generateConfig( - nameMap.systemGcc[lang] or nameMap.systemGcc.en_US, lang, - "/usr/bin/gcc", "/usr/bin/g++", + lang, "GCC (" .. gccDumpVersion .. ")", "system", "/usr/bin/gcc", "/usr/bin/g++", + {}) + + table.insert(compilerList, release) + table.insert(compilerList, debug_) + table.insert(compilerList, debugWithAsan) + end + + local versionedGccs = C_FileSystem.matchFiles("/usr/bin", "^gcc-[0-9]+$") + for _, gcc in ipairs(versionedGccs) do + local version = gcc:sub(5) + local name = "GCC " .. version + local release, debug_, debugWithAsan = generateConfig( + lang, name, "system", "/usr/bin/" .. gcc, "/usr/bin/g++-" .. version, {}) table.insert(compilerList, release) @@ -149,10 +188,24 @@ function main() end if C_FileSystem.isExecutable("/usr/bin/clang") then + clangDumpVersion = C_System.popen("/usr/bin/clang", { "-dumpversion" }) + clangDumpVersion = clangDumpVersion:match("^[0-9.]+") local release, debug_, debugWithAsan = generateConfig( - nameMap.systemClang[lang] or nameMap.systemClang.en_US, lang, - "/usr/bin/clang", "/usr/bin/clang++", - { isClang = true }) + lang, "Clang (" .. clangDumpVersion .. ")", "system", "/usr/bin/clang", "/usr/bin/clang++", + {}) + + table.insert(compilerList, release) + table.insert(compilerList, debug_) + table.insert(compilerList, debugWithAsan) + end + + local versionedClangs = C_FileSystem.matchFiles("/usr/bin", "^clang-[0-9]+$") + for _, clang in ipairs(versionedClangs) do + local version = clang:sub(7) + local name = "Clang " .. version + local release, debug_, debugWithAsan = generateConfig( + lang, name, "system", "/usr/bin/" .. clang, "/usr/bin/clang++-" .. version, + {}) table.insert(compilerList, release) table.insert(compilerList, debug_) @@ -160,12 +213,11 @@ function main() end - if arch == "x86_64" and C_FileSystem.exists("/usr/lib32/libc.a") then + if arch == "x86_64" and C_FileSystem.exists("/usr/lib/gcc/x86_64-linux-gnu/" .. gccFsVersion .. "/32/libstdc++.a") then do local release, debug_, debugWithAsan = generateConfig( - nameMap.multilibGcc[lang] or nameMap.multilibGcc.en_US, lang, - "/usr/bin/gcc", "/usr/bin/g++", - { isMultilib = true }) + lang, "GCC (" .. gccDumpVersion .. ")", "multilib", "/usr/bin/gcc", "/usr/bin/g++", + {}) table.insert(compilerList, release) table.insert(compilerList, debug_) @@ -174,9 +226,37 @@ function main() 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 }) + lang, "Clang (" .. clangDumpVersion .. ")", "multilib", "/usr/bin/clang", "/usr/bin/clang++", + {}) + + table.insert(compilerList, release) + table.insert(compilerList, debug_) + table.insert(compilerList, debugWithAsan) + end + end + + + if arch == "x86_64" and C_FileSystem.exists("/usr/lib/gcc/x86_64-linux-gnu/" .. gccFsVersion .. "/x32/libstdc++.a") then + do + local release, debug_, debugWithAsan = generateConfig( + lang, "GCC (" .. gccDumpVersion .. ")", "libx32", "/usr/bin/gcc", "/usr/bin/g++", + { + customCompileParams = { "-mx32" }, + customLinkParams = { "-mx32" }, + }) + + 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( + lang, "Clang (" .. clangDumpVersion .. ")", "libx32", "/usr/bin/clang", "/usr/bin/clang++", + { + customCompileParams = { "-mx32" }, + customLinkParams = { "-mx32" }, + }) table.insert(compilerList, release) table.insert(compilerList, debug_) @@ -199,10 +279,10 @@ function main() do local release, _, _ = generateConfig( - (nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " x86_64", lang, + lang, "GCC", "mingw", "/usr/bin/x86_64-w64-mingw32-gcc", "/usr/bin/x86_64-w64-mingw32-g++", { - isMingw = true, + arch = "x86_64", triplet = "x86_64-w64-mingw32", customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest }, }) @@ -219,10 +299,10 @@ function main() do local release, _, _ = generateConfig( - (nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " i686", lang, + lang, "GCC", "mingw", "/usr/bin/i686-w64-mingw32-gcc", "/usr/bin/i686-w64-mingw32-g++", { - isMingw = true, + arch = "i686", triplet = "i686-w64-mingw32", customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest }, }) diff --git a/packages/msys/domain/compiler_hint.lua b/packages/msys/domain/compiler_hint.lua index 85bdca84..9fc35193 100644 --- a/packages/msys/domain/compiler_hint.lua +++ b/packages/msys/domain/compiler_hint.lua @@ -2,7 +2,7 @@ function apiVersion() return { kind = "compiler_hint", major = 0, - minor = 1, + minor = 2, } end