diff --git a/.gitignore b/.gitignore index 848b9448..853b52eb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /.vs /.vscode +/assets /dist /packages/appimage/*/*.AppImage *.bak diff --git a/BUILD.md b/BUILD.md index cbb3c11b..2b6f1069 100644 --- a/BUILD.md +++ b/BUILD.md @@ -78,6 +78,12 @@ For Windows 7 or later: | Qt.io + MSVC | ✔️ | ✔️ | ❌ | | vcpkg + MSVC | ✔️ | ✔️ | ❌ | +qmake variables: +- `PREFIX`: where `$MAKE install` installs files to. +- `WINDOWS_PREFER_OPENCONSOLE=ON` (make phase): prefer UTF-8 compatible `OpenConsole.exe`. + - `OpenConsole.exe` is a part of Windows Terminal. UTF-8 input support was added in version 1.18. + - `OpenConsole.exe` requires ConPTY, which was introduced in Windows 10 1809. + Notes for Windows on ARM: - Red Panda C++ can be built for ARM64 ABI only on Windows 11 ARM64, while it is supposed (but not tested) to run on Windows 10 ARM64. - ARM64EC (“emulation compatible”) host is not supported, i.e., Red Panda C++ cannot be built with ARM64EC toolchain. diff --git a/BUILD_cn.md b/BUILD_cn.md index 82707ba1..2c0050fe 100644 --- a/BUILD_cn.md +++ b/BUILD_cn.md @@ -78,6 +78,12 @@ | Qt.io + MSVC | ✔️ | ✔️ | ❌ | | vcpkg + MSVC | ✔️ | ✔️ | ❌ | +qmake 变量: +- `PREFIX`:`$MAKE install` 的安装路径。 +- `WINDOWS_PREFER_OPENCONSOLE=ON`(make 阶段):首选兼容 UTF-8 的 `OpenConsole.exe`。 + - `OpenConsole.exe` 是 Windows 终端的组件,在 1.18 版本加入了 UTF-8 输出支持。 + - `OpenConsole.exe` 需要 Windows 10 1809 加入的 ConPTY 接口。 + 关于 ARM 上的 Windows 的注记: - 小熊猫 C++ 只能在 Windows 11 ARM64 上构建 ARM64 版,成品应该能在 Windows 10 ARM64 上运行(但没有测试过)。 - 不支持 ARM64EC(“仿真兼容”)主机,即不能用 ARM64EC 工具链构建小熊猫 C++。 diff --git a/NEWS.md b/NEWS.md index 7d88a11b..9df7e5e2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -36,6 +36,12 @@ Red Panda C++ Version 2.26 - Enhancement: Better type induction for auto in foreach loop of maps. - Enhancement: Better contrast for scroller slider in dark theme. - Enhancement: Using lua script in themes. + - Enhancement: Add compiler hint add-on interface for packager. + - Enhancement: Loose some limit about platform/architecture (such as ASan). + - Enhancement: add support for Windows user-wide installation. + - Enhancement: add qmake variable to control preference of UTF-8 compatible OpenConsole.exe on Windows. + - Enhancement: add Windows arm64 package. + - Fix: Force to use debug server when debugging with lldb-mi to fix input/output on Windows. Red Panda C++ Version 2.25 diff --git a/RedPandaIDE/RedPandaIDE.pro b/RedPandaIDE/RedPandaIDE.pro index 38971ab2..0b734bc3 100644 --- a/RedPandaIDE/RedPandaIDE.pro +++ b/RedPandaIDE/RedPandaIDE.pro @@ -59,6 +59,13 @@ isEmpty(TEST_VERSION) { } else { DEFINES += REDPANDA_CPP_VERSION=\\\"$${APP_VERSION}-$${TEST_VERSION}\\\" } +win32 { + _WINDOWS_PREFER_OPENCONSOLE = $$(WINDOWS_PREFER_OPENCONSOLE) + equals(_WINDOWS_PREFER_OPENCONSOLE, "ON") { + DEFINES += WINDOWS_PREFER_OPENCONSOLE + } +} + gcc { QMAKE_CXXFLAGS_RELEASE += -Werror=return-type diff --git a/RedPandaIDE/addon/api.cpp b/RedPandaIDE/addon/api.cpp index 024a5ea1..0d3ff398 100644 --- a/RedPandaIDE/addon/api.cpp +++ b/RedPandaIDE/addon/api.cpp @@ -20,13 +20,37 @@ #include #include #include +#include + +#ifdef Q_OS_WINDOWS +# include +#endif #include +#include "utils.h" #include "settings.h" #include "thememanager.h" #include "runtime.h" +#ifdef Q_OS_WINDOWS +// added in Windows 11 21H2, declare our version to support old SDKs. +constexpr int win32_flag_UserEnabled = 0x00000001; + +// added in Windows 11 21H2, use GetProcAddress to detect availability. +using pGetMachineTypeAttributes_t = HRESULT (WINAPI *)( + USHORT Machine, int *MachineTypeAttributes +); + +// added in Windows 10 1709, use GetProcAddress to detect availability. +using pIsWow64GuestMachineSupported_t = HRESULT (WINAPI *)( + USHORT WowGuestMachine, BOOL *MachineIsSupported +); +using pIsWow64Process2_t = BOOL (WINAPI *)( + HANDLE hProcess, USHORT *pProcessMachine, USHORT *pNativeMachine +); +#endif + // C_Debug.debug(string) -> () extern "C" int luaApi_Debug_debug(lua_State *L) noexcept { QString info = AddOn::RaiiLuaState::fetchString(L, 1); @@ -78,3 +102,204 @@ extern "C" int luaApi_Desktop_systemStyle(lua_State *L) noexcept { AddOn::RaiiLuaState::push(L, AppTheme::initialStyle()); return 1; } + +// C_FileSystem.exists(string) -> bool +extern "C" int luaApi_FileSystem_exists(lua_State *L) noexcept { + QString path = AddOn::RaiiLuaState::fetchString(L, 1); + QFileInfo fileInfo(path); + AddOn::RaiiLuaState::push(L, fileInfo.exists()); + return 1; +} + +// C_FileSystem.isExecutable(string) -> bool +extern "C" int luaApi_FileSystem_isExecutable(lua_State *L) noexcept { + QString path = AddOn::RaiiLuaState::fetchString(L, 1); + QFileInfo fileInfo(path); + bool isExecutable = fileInfo.isFile() && fileInfo.isReadable() && fileInfo.isExecutable(); + AddOn::RaiiLuaState::push(L, isExecutable); + return 1; +} + +// C_System.appArch() -> string +extern "C" int luaApi_System_appArch(lua_State *L) noexcept { + AddOn::RaiiLuaState::push(L, appArch()); + return 1; +} + +// C_System.appDir() -> string +extern "C" int luaApi_System_appDir(lua_State *L) noexcept { + QString appDir = pSettings->dirs().appDir(); + AddOn::RaiiLuaState::push(L, appDir); + return 1; +} + +// C_System.appLibexecDir() -> string +extern "C" int luaApi_System_appLibexecDir(lua_State *L) noexcept { + QString appLibexecDir = pSettings->dirs().appLibexecDir(); + AddOn::RaiiLuaState::push(L, appLibexecDir); + return 1; +} + +// C_System.appResourceDir() -> string +extern "C" int luaApi_System_appResourceDir(lua_State *L) noexcept { + QString appResourceDir = pSettings->dirs().appResourceDir(); + AddOn::RaiiLuaState::push(L, appResourceDir); + return 1; +} + +// C_System.osArch() -> string +extern "C" int luaApi_System_osArch(lua_State *L) noexcept { + AddOn::RaiiLuaState::push(L, osArch()); + return 1; +} + +static QString qtArchitectureNormalization(const QString &arch) { + // adjust QEMU user emulation arches to match QSysInfo::currentCpuArchitecture + if (arch == "aarch64") + return "arm64"; + if (arch.startsWith("ppc")) + return "power" + arch.mid(3); + if (arch == "sparc64") + return "sparcv9"; + return arch; +} + +// C_System.supportedAppArchList() -> [string] +extern "C" int luaApi_System_supportedAppArchList(lua_State *L) noexcept { +#ifdef Q_OS_WINDOWS + QSet arches; + pGetMachineTypeAttributes_t pGetMachineTypeAttributes = + reinterpret_cast(GetProcAddress( + GetModuleHandleW(L"kernel32"), "GetMachineTypeAttributes")); + if (pGetMachineTypeAttributes) { + // the direct way + int result; + if (pGetMachineTypeAttributes(IMAGE_FILE_MACHINE_I386, &result) == 0 && result & win32_flag_UserEnabled) + arches.insert("i386"); + if (pGetMachineTypeAttributes(IMAGE_FILE_MACHINE_AMD64, &result) == 0 && result & win32_flag_UserEnabled) + arches.insert("x86_64"); + if (pGetMachineTypeAttributes(IMAGE_FILE_MACHINE_ARMNT, &result) == 0 && result & win32_flag_UserEnabled) + arches.insert("arm"); + if (pGetMachineTypeAttributes(IMAGE_FILE_MACHINE_ARM64, &result) == 0 && result & win32_flag_UserEnabled) + arches.insert("arm64"); + } else { + pIsWow64GuestMachineSupported_t pIsWow64GuestMachineSupported = + reinterpret_cast(GetProcAddress( + GetModuleHandleW(L"kernel32"), "IsWow64GuestMachineSupported")); + pIsWow64Process2_t pIsWow64Process2 = + reinterpret_cast(GetProcAddress( + GetModuleHandleW(L"kernel32"), "IsWow64Process2")); + if (pIsWow64GuestMachineSupported && pIsWow64Process2) { + // recent Windows 10, native + WoW64 arches + // IsWow64Process2 returns real native architecture under xtajit, while GetNativeSystemInfo does not. + USHORT processMachineResult, nativeMachineResult; + if (pIsWow64Process2(GetCurrentProcess(), &processMachineResult, &nativeMachineResult)) + switch (nativeMachineResult) { + case IMAGE_FILE_MACHINE_I386: + arches.insert("i386"); + break; + case IMAGE_FILE_MACHINE_AMD64: + arches.insert("x86_64"); + break; + case IMAGE_FILE_MACHINE_ARM64: + arches.insert("arm64"); + break; + } + BOOL wow64SupportResult; + if (pIsWow64GuestMachineSupported(IMAGE_FILE_MACHINE_I386, &wow64SupportResult) == S_OK && wow64SupportResult) + arches.insert("i386"); + if (pIsWow64GuestMachineSupported(IMAGE_FILE_MACHINE_ARMNT, &wow64SupportResult) == S_OK && wow64SupportResult) + arches.insert("arm"); + } else { + // legacy Windows, hardcode + SYSTEM_INFO si; + GetNativeSystemInfo(&si); + switch (si.wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_INTEL: + arches.insert("i386"); + break; + case PROCESSOR_ARCHITECTURE_AMD64: + arches.insert("i386"); + arches.insert("x86_64"); + break; + case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: // undocumented but found in Qt source + case PROCESSOR_ARCHITECTURE_IA64: + // does Red Panda C++ run on IA-64? + arches.insert("i386"); + arches.insert("ia64"); + break; + } + } + } + QStringList result{arches.begin(), arches.end()}; + AddOn::RaiiLuaState::push(L, result); + return 1; +#elif defined(Q_OS_MACOS) + QStringList result; + if (QSysInfo::currentCpuArchitecture() == "arm64") + result = QStringList{"arm64", "x86_64"}; + else if (QVersionNumber::fromString(QSysInfo::kernelVersion()) >= QVersionNumber(19, 0)) + // macOS 10.15+ + result = QStringList{"x86_64"}; + else + result = QStringList{"i386", "x86_64"}; + AddOn::RaiiLuaState::push(L, result); + return 1; +#else + QSet arches; + arches.insert(QSysInfo::currentCpuArchitecture()); + arches.insert(QSysInfo::buildCpuArchitecture()); + + // read binfmt_misc registry + QDir binfmt_misc("/proc/sys/fs/binfmt_misc"); + if (binfmt_misc.exists()) { + QFileInfoList entries = binfmt_misc.entryInfoList(QDir::Files); + for (const QFileInfo &entry : entries) { + if (entry.fileName().startsWith("qemu-")) { + QString arch = entry.fileName().mid(5); + arches.insert(qtArchitectureNormalization(arch)); + } + } + } + + // workaround for Debian 10 Qt 5.11, better to be + // QStringList result{arches.begin(), arches.end()}; + QStringList result = arches.toList(); + AddOn::RaiiLuaState::push(L, result); + return 1; +#endif +} + +#ifdef Q_OS_WINDOWS +// C_System.readRegistry(string, string) -> string|nil +extern "C" int luaApi_System_readRegistry(lua_State *L) noexcept +{ + QString subKey = AddOn::RaiiLuaState::fetchString(L, 1); + QString name = AddOn::RaiiLuaState::fetchString(L, 2); + QString value; + bool success = readRegistry(HKEY_CURRENT_USER, subKey, name, value); + if (success) + AddOn::RaiiLuaState::push(L, value); + else { + success = readRegistry(HKEY_LOCAL_MACHINE, subKey, name, value); + if (success) + AddOn::RaiiLuaState::push(L, value); + else + AddOn::RaiiLuaState::push(L, nullptr); + } + return 1; +} +#endif + +// C_Util.format(string, ...) -> string +extern "C" int luaApi_Util_format(lua_State *L) noexcept +{ + 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()); + } + AddOn::RaiiLuaState::push(L, s); + return 1; +} diff --git a/RedPandaIDE/addon/api.h b/RedPandaIDE/addon/api.h index 86b9a251..c147703c 100644 --- a/RedPandaIDE/addon/api.h +++ b/RedPandaIDE/addon/api.h @@ -27,4 +27,19 @@ extern "C" int luaApi_Desktop_qtStyleList(lua_State *L) noexcept; extern "C" int luaApi_Desktop_systemAppMode(lua_State *L) noexcept; 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_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_supportedAppArchList(lua_State *L) noexcept; +#ifdef Q_OS_WINDOWS +extern "C" int luaApi_System_readRegistry(lua_State *L) noexcept; +#endif + +extern "C" int luaApi_Util_format(lua_State *L) noexcept; + #endif // ADDON_API_H diff --git a/RedPandaIDE/addon/executor.cpp b/RedPandaIDE/addon/executor.cpp index 2a897a99..0c941a79 100644 --- a/RedPandaIDE/addon/executor.cpp +++ b/RedPandaIDE/addon/executor.cpp @@ -24,20 +24,42 @@ namespace AddOn { static QMap> apiGroups{ - {"C_Debug", - { - {"debug", &luaApi_Debug_debug}, // (string) -> () - }}, - {"C_Desktop", - { - {"desktopEnvironment", - &luaApi_Desktop_desktopEnvironment}, // () -> string - {"language", &luaApi_Desktop_language}, // () -> string - {"qtStyleList", &luaApi_Desktop_qtStyleList}, // () -> [string] - {"systemAppMode", &luaApi_Desktop_systemAppMode}, // () -> string - {"systemStyle", &luaApi_Desktop_systemStyle}, // () -> string - }}, - }; + {"C_Debug", + { + {"debug", &luaApi_Debug_debug}, // (string) -> () + }}, + {"C_Desktop", + { + {"desktopEnvironment", + &luaApi_Desktop_desktopEnvironment}, // () -> string + {"language", &luaApi_Desktop_language}, // () -> string + {"qtStyleList", &luaApi_Desktop_qtStyleList}, // () -> [string] + {"systemAppMode", &luaApi_Desktop_systemAppMode}, // () -> string + {"systemStyle", &luaApi_Desktop_systemStyle}, // () -> string + }}, + {"C_FileSystem", + { + {"exists", &luaApi_FileSystem_exists}, // (string) -> bool + {"isExecutable", &luaApi_FileSystem_isExecutable}, // (string) -> bool + }}, + {"C_System", + { + {"appArch", &luaApi_System_appArch}, // () -> string + {"appDir", &luaApi_System_appDir}, // () -> string + {"appLibexecDir", &luaApi_System_appLibexecDir}, // () -> string + {"appResourceDir", &luaApi_System_appResourceDir}, // () -> string + {"osArch", &luaApi_System_osArch}, // () -> string + {"supportedAppArchList", + &luaApi_System_supportedAppArchList}, // () -> string +#ifdef Q_OS_WINDOWS + {"readRegistry", + &luaApi_System_readRegistry}, // (string, string) -> string|nil +#endif + }}, + {"C_Util", + { + {"format", &luaApi_Util_format}, // (string, ...) -> string + }}}; static void registerApiGroup(RaiiLuaState &L, const QString &name) { L.push(apiGroups[name]); @@ -80,7 +102,7 @@ QJsonValue SimpleExecutor::runScript(const QByteArray &script, int retLoad = L.loadBuffer(script, name); if (retLoad != 0) - throw LuaError("Lua script load error."); + throw LuaError(QString("Lua script 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); @@ -91,4 +113,15 @@ QJsonValue SimpleExecutor::runScript(const QByteArray &script, return L.fetch(1); } +CompilerHintExecutor::CompilerHintExecutor() : SimpleExecutor({"C_Debug", "C_Desktop", "C_FileSystem", "C_System", "C_Util"}) {} + +QJsonObject CompilerHintExecutor::operator()(const QByteArray &script) { + using namespace std::chrono_literals; + QJsonValue result = SimpleExecutor::runScript(script, "compiler_hint.lua", 1s); + if (result.isObject() || result.isNull()) + return result.toObject(); + else + throw LuaError("Compiler hint script must return an object."); +} + } // namespace AddOn diff --git a/RedPandaIDE/addon/executor.h b/RedPandaIDE/addon/executor.h index f0dc5ced..671305b6 100644 --- a/RedPandaIDE/addon/executor.h +++ b/RedPandaIDE/addon/executor.h @@ -43,6 +43,12 @@ public: QJsonObject operator()(const QByteArray &script, const QString &name); }; +class CompilerHintExecutor : private SimpleExecutor { +public: + CompilerHintExecutor(); + QJsonObject operator()(const QByteArray &script); +}; + } #endif // ADDON_EXECUTOR_H diff --git a/RedPandaIDE/addon/runtime.cpp b/RedPandaIDE/addon/runtime.cpp index 43a3264c..752dbb59 100644 --- a/RedPandaIDE/addon/runtime.cpp +++ b/RedPandaIDE/addon/runtime.cpp @@ -70,7 +70,22 @@ QString RaiiLuaState::fetchString(int index) QJsonValue RaiiLuaState::fetch(int index) { - return fetchValueImpl(index, 0); + return fetchValueImpl(mLua, index, 0); +} + +bool RaiiLuaState::fetchBoolean(lua_State *L, int index) +{ + return lua_toboolean(L, index); +} + +long long RaiiLuaState::fetchInteger(lua_State *L, int index) +{ + return lua_tointeger(L, index); +} + +double RaiiLuaState::fetchNumber(lua_State *L, int index) +{ + return lua_tonumber(L, index); } QString RaiiLuaState::fetchString(lua_State *L, int index) @@ -78,6 +93,11 @@ QString RaiiLuaState::fetchString(lua_State *L, int index) return lua_tostring(L, index); } +QJsonValue RaiiLuaState::fetch(lua_State *L, int index) +{ + return fetchValueImpl(L, index, 0); +} + bool RaiiLuaState::popBoolean() { bool value = lua_toboolean(mLua, -1); @@ -99,6 +119,13 @@ QJsonValue RaiiLuaState::pop() return value; } +QJsonValue RaiiLuaState::pop(lua_State *L) +{ + QJsonValue value = fetch(L, -1); + lua_pop(L, 1); + return value; +} + void RaiiLuaState::push(decltype(nullptr)) { lua_pushnil(mLua); @@ -114,6 +141,16 @@ void RaiiLuaState::push(const QMap &value) } } +void RaiiLuaState::push(lua_State *L, decltype(nullptr)) +{ + lua_pushnil(L); +} + +void RaiiLuaState::push(lua_State *L, bool value) +{ + lua_pushboolean(L, value); +} + void RaiiLuaState::push(lua_State *L, const QString &value) { lua_pushstring(L, value.toUtf8().constData()); @@ -129,6 +166,16 @@ void RaiiLuaState::push(lua_State *L, const QStringList &value) } } +int RaiiLuaState::getTop() +{ + return lua_gettop(mLua); +} + +int RaiiLuaState::getTop(lua_State *L) +{ + return lua_gettop(L); +} + int RaiiLuaState::loadBuffer(const QByteArray &buff, const QString &name) { return luaL_loadbuffer(mLua, buff.constData(), buff.size(), name.toUtf8().constData()); @@ -166,15 +213,15 @@ LuaExtraState &RaiiLuaState::extraState(lua_State *lua) { return mExtraState[lua]; } -QJsonValue RaiiLuaState::fetchTableImpl(int index, int depth) +QJsonValue RaiiLuaState::fetchTableImpl(lua_State *L, int index, int depth) { if (depth == 1) // check stack size at first recursion to avoid multiple reallocations - lua_checkstack(mLua, LUA_STACK_SIZE); + lua_checkstack(L, LUA_STACK_SIZE); if (depth > TABLE_MAX_DEPTH) throw LuaError("Lua runtime error: table nested too deeply"); - push(nullptr); // make sure lua_next starts at beginning + push(L, nullptr); // make sure lua_next starts at beginning int newIndex = index < 0 ? index - 1 : index; // after push negative index changes QJsonObject hashPart; @@ -182,15 +229,15 @@ QJsonValue RaiiLuaState::fetchTableImpl(int index, int depth) // here we take the fact that Lua iterates array part first bool processingArrayPart = true; - while (lua_next(mLua, newIndex)) { - QJsonValue v = pop(); - if (processingArrayPart && lua_isinteger(mLua, -1) && fetchInteger(-1) == arrayPart.size() + 1) + while (lua_next(L, newIndex)) { + QJsonValue v = pop(L); + if (processingArrayPart && lua_isinteger(L, -1) && fetchInteger(L, -1) == arrayPart.size() + 1) // we are still in array part arrayPart.push_back(v); else { // we have stepped in hash part processingArrayPart = false; - QString k = fetchString(-1); + QString k = fetchString(L, -1); hashPart[k] = v; } } @@ -212,22 +259,24 @@ QJsonValue RaiiLuaState::fetchTableImpl(int index, int depth) return {}; } -QJsonValue RaiiLuaState::fetchValueImpl(int index, int depth) +QJsonValue RaiiLuaState::fetchValueImpl(lua_State *L, int index, int depth) { - if (lua_isnil(mLua, index)) + if (lua_isnil(L, index)) return {}; - else if (lua_isboolean(mLua, index)) - return fetchBoolean(index); - else if (lua_isinteger(mLua, index)) - return fetchInteger(index); - else if (lua_isnumber(mLua, index)) - return fetchNumber(index); - else if (lua_isstring(mLua, index)) - return fetchString(index); - else if (lua_istable(mLua, index)) - return fetchTableImpl(index, depth + 1); + else if (lua_isboolean(L, index)) + return fetchBoolean(L, index); + else if (lua_isinteger(L, index)) + return fetchInteger(L, index); + // lua_isnumber treats strings that can be converted to numbers as numbers + // use lua_type to detect numbers + else if (lua_type(L, index) == LUA_TNUMBER) + return fetchNumber(L, index); + else if (lua_isstring(L, index)) + 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(mLua, index))); + throw LuaError(QString("Lua type error: unknown type %1.").arg(lua_typename(L, index))); } QHash RaiiLuaState::mExtraState; diff --git a/RedPandaIDE/addon/runtime.h b/RedPandaIDE/addon/runtime.h index 564b81f5..3eae0137 100644 --- a/RedPandaIDE/addon/runtime.h +++ b/RedPandaIDE/addon/runtime.h @@ -64,7 +64,11 @@ public: QJsonObject fetchObject(int index); QJsonValue fetch(int index); + static bool fetchBoolean(lua_State *L, int index); + 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 QJsonValue fetch(lua_State *L, int index); bool popBoolean(); long long popInteger(); @@ -74,12 +78,19 @@ public: QJsonObject popObject(); QJsonValue pop(); + static QJsonValue pop(lua_State *L); + void push(decltype(nullptr)); void push(const QMap &value); + static void push(lua_State *L, decltype(nullptr)); + 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); + int getTop(); + static int getTop(lua_State *L); + int loadBuffer(const QByteArray &buff, const QString &name); void openLibs(); int pCall(int nargs, int nresults, int msgh); @@ -91,8 +102,8 @@ public: static LuaExtraState &extraState(lua_State *lua); private: - QJsonValue fetchTableImpl(int index, int depth); - QJsonValue fetchValueImpl(int index, int depth); + static QJsonValue fetchTableImpl(lua_State *L, int index, int depth); + static QJsonValue fetchValueImpl(lua_State *L, int index, int depth); private: lua_State *mLua; diff --git a/RedPandaIDE/compiler/compilerinfo.cpp b/RedPandaIDE/compiler/compilerinfo.cpp index 4e21b1d8..ff4ea55c 100644 --- a/RedPandaIDE/compiler/compilerinfo.cpp +++ b/RedPandaIDE/compiler/compilerinfo.cpp @@ -177,17 +177,13 @@ void CompilerInfo::prepareCompilerOptions() sl.append(QPair("Strong","-strong")); sl.append(QPair("All","-all")); addOption(CC_CMD_OPT_STACK_PROTECTOR , QObject::tr("Check for stack smashing attacks (-fstack-protector)"), groupName, false, false, true, "-fstack-protector", CompilerOptionType::Choice, sl); -#if defined(Q_OS_UNIX) sl.clear(); sl.append(QPair("Address","address")); -# ifdef __aarch64__ - sl.append(QPair("Hardware-assisted Address","hwaddress")); -# endif + sl.append(QPair("Hardware-assisted address (arm64 only)","hwaddress")); sl.append(QPair("Thread","thread")); sl.append(QPair("Leak","leak")); sl.append(QPair("Undefined","undefined")); addOption(CC_CMD_OPT_ADDRESS_SANITIZER , QObject::tr("Enable Sanitizer (-fsanitize=)"), groupName, true, true, true, "-fsanitize=",CompilerOptionType::Choice,sl); -#endif // Output //groupName = QObject::tr("Output"); diff --git a/RedPandaIDE/debugger.cpp b/RedPandaIDE/debugger.cpp index d548dd2f..bf241db9 100644 --- a/RedPandaIDE/debugger.cpp +++ b/RedPandaIDE/debugger.cpp @@ -90,6 +90,8 @@ bool Debugger::start(int compilerSetIndex, const QString& inferior, const QStrin setDebuggerType(DebuggerType::LLDB_MI); else setDebuggerType(DebuggerType::GDB); + // force to lldb-server if using lldb-mi, which creates new console but does not bind inferior’s stdio to the new console on Windows. + setUseDebugServer(pSettings->debugger().useGDBServer() || debuggerType() == DebuggerType::LLDB_MI); mExecuting = true; QString debuggerPath = compilerSet->debugger(); //QFile debuggerProgram(debuggerPath); @@ -112,7 +114,7 @@ bool Debugger::start(int compilerSetIndex, const QString& inferior, const QStrin +tr("Please check the \"program\" page of compiler settings.")); return false; } - if (pSettings->debugger().useGDBServer()) { + if (useDebugServer()) { if (!isTextAllAscii(compilerSet->debugServer())) { mExecuting = false; QMessageBox::critical(pMainWindow, @@ -134,7 +136,7 @@ bool Debugger::start(int compilerSetIndex, const QString& inferior, const QStrin } mMemoryModel->reset(); mWatchModel->resetAllVarInfos(); - if (pSettings->debugger().useGDBServer()) { + if (useDebugServer()) { //deleted when thread finished QStringList params; if (pSettings->executor().useParams()) @@ -561,6 +563,16 @@ void Debugger::fetchVarChildren(const QString &varName) } } +bool Debugger::useDebugServer() const +{ + return mUseDebugServer; +} + +void Debugger::setUseDebugServer(bool newUseDebugServer) +{ + mUseDebugServer = newUseDebugServer; +} + bool Debugger::debugInfosUsingUTF8() const { return mDebugInfosUsingUTF8; @@ -1391,7 +1403,7 @@ void DebugReader::runNextCmd() emit cmdFinished(); } if (mCmdQueue.isEmpty()) { - if (pSettings->debugger().useGDBServer() && mInferiorRunning && !mAsyncUpdated) { + if (mDebugger->useDebugServer() && mInferiorRunning && !mAsyncUpdated) { mAsyncUpdated = true; QTimer::singleShot(50,this,&DebugReader::asyncUpdate); } @@ -3062,11 +3074,20 @@ void DebugTarget::run() mErrorOccured = false; //find first available port - QStringList execArgs = QStringList{ - mGDBServer, - QString("localhost:%1").arg(mPort), - mInferior, - } + mArguments; + QStringList execArgs; + if (mGDBServer.endsWith(LLDB_SERVER_PROGRAM)) + execArgs = QStringList{ + mGDBServer, + "gdbserver", + QString("localhost:%1").arg(mPort), + mInferior, + } + mArguments; + else + execArgs = QStringList{ + mGDBServer, + QString("localhost:%1").arg(mPort), + mInferior, + } + mArguments; QString cmd; QStringList arguments; std::unique_ptr fileOwner; diff --git a/RedPandaIDE/debugger.h b/RedPandaIDE/debugger.h index 32e4bd13..9b74417b 100644 --- a/RedPandaIDE/debugger.h +++ b/RedPandaIDE/debugger.h @@ -375,6 +375,9 @@ public: bool debugInfosUsingUTF8() const; void setDebugInfosUsingUTF8(bool newDebugInfosUsingUTF8); + bool useDebugServer() const; + void setUseDebugServer(bool newUseDebugServer); + signals: void evalValueReady(const QString& s); void memoryExamineReady(const QStringList& s); @@ -418,6 +421,7 @@ private: DebugTarget *mTarget; bool mForceUTF8; bool mDebugInfosUsingUTF8; + bool mUseDebugServer; DebuggerType mDebuggerType; int mLeftPageIndexBackup; qint64 mLastLoadtime; diff --git a/RedPandaIDE/mainwindow.cpp b/RedPandaIDE/mainwindow.cpp index 6e285ccf..25a225cb 100644 --- a/RedPandaIDE/mainwindow.cpp +++ b/RedPandaIDE/mainwindow.cpp @@ -985,7 +985,8 @@ void MainWindow::applySettings() && pSettings->executor().enableProblemSet()); ui->cbProblemCaseValidateType->setCurrentIndex((int)(pSettings->executor().problemCaseValidateType())); - ui->actionInterrupt->setVisible(pSettings->debugger().useGDBServer()); + if (mDebugger != nullptr) + ui->actionInterrupt->setVisible(mDebugger->useDebugServer()); //icon sets for editors updateEditorSettings(); updateDebuggerSettings(); @@ -2506,7 +2507,7 @@ void MainWindow::debug() mDebugger->sendCommand("-gdb-set", QString("print elements %1").arg(pSettings->debugger().arrayElements())); // limit array elements to 30 mDebugger->sendCommand("-gdb-set", QString("print characters %1").arg(pSettings->debugger().characters())); // limit array elements to 300 mDebugger->sendCommand("-environment-cd", QString("\"%1\"").arg(extractFileDir(filePath))); // restore working directory - if (pSettings->debugger().useGDBServer()) { + if (mDebugger->useDebugServer()) { mDebugger->sendCommand("-target-select",QString("remote localhost:%1").arg(pSettings->debugger().GDBServerPort())); if (!debugInferiorhasBreakpoint() || !debugEnabled) { mDebugger->sendCommand("-break-insert","-t main"); @@ -5165,8 +5166,7 @@ void MainWindow::disableDebugActions() void MainWindow::enableDebugActions() { - if (pSettings->debugger().useGDBServer()) - ui->actionInterrupt->setEnabled(mDebugger->inferiorRunning()); + ui->actionInterrupt->setEnabled(mDebugger->useDebugServer() && mDebugger->inferiorRunning()); ui->actionStep_Into->setEnabled(!mDebugger->inferiorRunning()); ui->actionStep_Over->setEnabled(!mDebugger->inferiorRunning()); ui->actionStep_Out->setEnabled(!mDebugger->inferiorRunning()); diff --git a/RedPandaIDE/settings.cpp b/RedPandaIDE/settings.cpp index f3894b09..3b889b96 100644 --- a/RedPandaIDE/settings.cpp +++ b/RedPandaIDE/settings.cpp @@ -33,6 +33,10 @@ #ifdef Q_OS_LINUX #include #endif +#ifdef ENABLE_LUA_ADDON +# include "addon/executor.h" +# include "addon/runtime.h" +#endif const char ValueToChar[28] = {'0', '1', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', @@ -1768,6 +1772,127 @@ Settings::CompilerSet::CompilerSet(const Settings::CompilerSet &set): } +Settings::CompilerSet::CompilerSet(const QJsonObject &set) : + mFullLoaded{true}, + mCCompiler{set["cCompiler"].toString()}, + mCppCompiler{set["cxxCompiler"].toString()}, + mMake{set["make"].toString()}, + mDebugger{set["debugger"].toString()}, + mResourceCompiler{set["resourceCompiler"].toString()}, + mDebugServer{set["debugServer"].toString()}, + + mBinDirs{}, // handle later + mCIncludeDirs{}, // handle later + mCppIncludeDirs{}, // handle later + mLibDirs{}, // handle later + mDefaultLibDirs{}, // handle later + mDefaultCIncludeDirs{}, // handle later + mDefaultCppIncludeDirs{}, // handle later + + mDumpMachine{set["dumpMachine"].toString()}, + mVersion{set["version"].toString()}, + mType{set["type"].toString()}, + mName{set["name"].toString()}, + mTarget{set["target"].toString()}, + mCompilerType{}, // handle later + + mUseCustomCompileParams{!set["customCompileParams"].toArray().isEmpty()}, + mUseCustomLinkParams{!set["customLinkParams"].toArray().isEmpty()}, + mCustomCompileParams{}, // handle later + mCustomLinkParams{}, // handle later + mAutoAddCharsetParams{!set["execCharset"].toString().isEmpty()}, + mExecCharset{}, // handle later + mStaticLink{set["staticLink"].toBool()}, + mPersistInAutoFind{false}, + + mPreprocessingSuffix{set["preprocessingSuffix"].toString()}, + mCompilationProperSuffix{set["compilationProperSuffix"].toString()}, + mAssemblingSuffix{set["assemblingSuffix"].toString()}, + mExecutableSuffix{set["executableSuffix"].toString()}, + mCompilationStage{CompilationStage(set["compilationStage"].toInt())}, + mCompileOptions{} // handle later +{ + for (const QJsonValue &dir : set["binDirs"].toArray()) + mBinDirs.append(dir.toString()); + for (const QJsonValue &dir : set["cIncludeDirs"].toArray()) + mCIncludeDirs.append(dir.toString()); + for (const QJsonValue &dir : set["cxxIncludeDirs"].toArray()) + mCppIncludeDirs.append(dir.toString()); + for (const QJsonValue &dir : set["libDirs"].toArray()) + mLibDirs.append(dir.toString()); + for (const QJsonValue &dir : set["defaultLibDirs"].toArray()) + mDefaultLibDirs.append(dir.toString()); + for (const QJsonValue &dir : set["defaultCIncludeDirs"].toArray()) + mDefaultCIncludeDirs.append(dir.toString()); + for (const QJsonValue &dir : set["defaultCxxIncludeDirs"].toArray()) + mDefaultCppIncludeDirs.append(dir.toString()); + + QString compilerType = set["compilerType"].toString(); + if (compilerType == "GCC") { + mCompilerType = CompilerType::GCC; + } else if (compilerType == "GCC_UTF8") { + mCompilerType = CompilerType::GCC_UTF8; + } else if (compilerType == "Clang") { + mCompilerType = CompilerType::Clang; + } +#if ENABLE_SDCC + else if (compilerType == "SDCC") { + mCompilerType = CompilerType::SDCC; + } +#endif + else { + mCompilerType = CompilerType::Unknown; + mFullLoaded = false; + } + + QStringList escapedCompileParams; + for (const QJsonValue ¶m : set["customCompileParams"].toArray()) + escapedCompileParams.append(escapeArgument(param.toString(), false)); + mCustomCompileParams = escapedCompileParams.join(' '); + QStringList escapedLinkParams; + for (const QJsonValue ¶m : set["customLinkParams"].toArray()) + escapedLinkParams.append(escapeArgument(param.toString(), false)); + mCustomLinkParams = escapedLinkParams.join(' '); + + if (!mAutoAddCharsetParams) + mExecCharset = "UTF-8"; + else + mExecCharset = set["execCharset"].toString(); + + const static QMap optionMap = { + {CC_CMD_OPT_OPTIMIZE, "ccCmdOptOptimize"}, + {CC_CMD_OPT_STD, "ccCmdOptStd"}, + {C_CMD_OPT_STD, "cCmdOptStd"}, + {CC_CMD_OPT_INSTRUCTION, "ccCmdOptInstruction"}, + + {CC_CMD_OPT_POINTER_SIZE, "ccCmdOptPointerSize"}, + + {CC_CMD_OPT_DEBUG_INFO, "ccCmdOptDebugInfo"}, + {CC_CMD_OPT_PROFILE_INFO, "ccCmdOptProfileInfo"}, + {CC_CMD_OPT_SYNTAX_ONLY, "ccCmdOptSyntaxOnly"}, + + {CC_CMD_OPT_INHIBIT_ALL_WARNING, "ccCmdOptInhibitAllWarning"}, + {CC_CMD_OPT_WARNING_ALL, "ccCmdOptWarningAll"}, + {CC_CMD_OPT_WARNING_EXTRA, "ccCmdOptWarningExtra"}, + {CC_CMD_OPT_CHECK_ISO_CONFORMANCE, "ccCmdOptCheckIsoConformance"}, + {CC_CMD_OPT_WARNING_AS_ERROR, "ccCmdOptWarningAsError"}, + {CC_CMD_OPT_ABORT_ON_ERROR, "ccCmdOptAbortOnError"}, + {CC_CMD_OPT_STACK_PROTECTOR, "ccCmdOptStackProtector"}, + {CC_CMD_OPT_ADDRESS_SANITIZER, "ccCmdOptAddressSanitizer"}, + + {CC_CMD_OPT_USE_PIPE, "ccCmdOptUsePipe"}, + {LINK_CMD_OPT_NO_LINK_STDLIB, "linkCmdOptNoLinkStdlib"}, + {LINK_CMD_OPT_NO_CONSOLE, "linkCmdOptNoConsole"}, + {LINK_CMD_OPT_STRIP_EXE, "linkCmdOptStripExe"}, + }; + for (const QString &key : optionMap.keys()) { + const QString &jsonKey = optionMap[key]; + QString value = set[jsonKey].toString(); + if (!value.isEmpty()) + setCompileOption(key, value); + } +} + void Settings::CompilerSet::resetCompileOptionts() { mCompileOptions.clear(); @@ -2995,6 +3120,13 @@ Settings::PCompilerSet Settings::CompilerSets::addSet(const PCompilerSet &pSet) return p; } +Settings::PCompilerSet Settings::CompilerSets::addSet(const QJsonObject &set) +{ + PCompilerSet p = std::make_shared(set); + mList.push_back(p); + return p; +} + static void set64_32Options(Settings::PCompilerSet pSet) { pSet->setCompileOption(CC_CMD_OPT_POINTER_SIZE,"32"); } @@ -3133,39 +3265,72 @@ Settings::CompilerSetList Settings::CompilerSets::clearSets() void Settings::CompilerSets::findSets() { CompilerSetList persisted = clearSets(); + // canonical paths that has been searched. + // use canonical paths here to resolve symbolic links. QSet searched; +#ifdef ENABLE_LUA_ADDON + QJsonObject compilerHint; + if ( + QFile scriptFile(pSettings->dirs().appLibexecDir() + "/compiler_hint.lua"); + scriptFile.exists() && scriptFile.open(QFile::ReadOnly) + ) { + QByteArray script = scriptFile.readAll(); + try { + compilerHint = AddOn::CompilerHintExecutor{}(script); + } catch (const AddOn::LuaError &e) { + qDebug() << "Error in compiler_hint.lua:" << e.reason(); + } + if (!compilerHint.empty()) { + QJsonArray compilerList = compilerHint["compilerList"].toArray(); + for (const QJsonValue &value : compilerList) { + addSet(value.toObject()); + } + QJsonArray noSearch = compilerHint["noSearch"].toArray(); + QString canonicalPath; + for (const QJsonValue &value : noSearch) { + canonicalPath = QDir(value.toString()).canonicalPath(); + if (!canonicalPath.isEmpty()) + searched.insert(canonicalPath); + } + } + } +#endif + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); QString path = env.value("PATH"); QStringList pathList = path.split(PATH_SEPARATOR); - QString folder; +#ifdef Q_OS_WIN + pathList = QStringList{ + mSettings->dirs().appDir() + "/Clang64/bin", + mSettings->dirs().appDir() + "/MinGW64/bin", + mSettings->dirs().appDir() + "/MinGW32/bin", + } + pathList; +#endif + QString folder, canonicalFolder; for (int i=pathList.count()-1;i>=0;i--) { - folder = QFileInfo(pathList[i]).absoluteFilePath(); - if (searched.contains(folder)) + folder = QDir(pathList[i]).absolutePath(); + canonicalFolder = QDir(pathList[i]).canonicalPath(); + if (canonicalFolder.isEmpty()) continue; - searched.insert(folder); - if (folder!="/bin") { // /bin/gcc is symbolic link to /usr/bin/gcc - addSets(folder); - } + if (searched.contains(canonicalFolder)) + continue; + searched.insert(canonicalFolder); + // but use absolute path to search so compiler set can survive system upgrades. + // during search: + // /opt/gcc-13 -> /opt/gcc-13.1.0 + // after upgrade: + // /opt/gcc-13 -> /opt/gcc-13.2.0 + addSets(folder); } -#ifdef Q_OS_WIN - folder = includeTrailingPathDelimiter(mSettings->dirs().appDir())+"MinGW32"+QDir::separator()+"bin"; - folder = QFileInfo(folder).absoluteFilePath(); - if (!searched.contains(folder)) { - addSets(folder); - searched.insert(folder); - } - folder = includeTrailingPathDelimiter(mSettings->dirs().appDir())+"MinGW64"+QDir::separator()+"bin"; - folder = QFileInfo(folder).absoluteFilePath(); - if (!searched.contains(folder)) { - addSets(folder); - searched.insert(folder); - } - folder = includeTrailingPathDelimiter(mSettings->dirs().appDir())+"Clang64"+QDir::separator()+"bin"; - if (!searched.contains(folder)) { - addSets(folder); - searched.insert(folder); +#ifdef ENABLE_LUA_ADDON + if ( + // note that array index starts from 1 in Lua + int preferCompilerInLua = compilerHint["preferCompiler"].toInt(); + preferCompilerInLua >= 1 && preferCompilerInLua <= (int)mList.size() + ) { + mDefaultIndex = preferCompilerInLua - 1; } #endif @@ -3644,11 +3809,10 @@ void Settings::Environment::doLoad() } #ifdef Q_OS_WINDOWS -# if defined (__aarch64__) || defined(_M_ARM64) - // the only native MinGW toolchain (LLVM-MinGW) does not have local codepage support +# ifdef WINDOWS_PREFER_OPENCONSOLE // prefer UTF-8 compatible OpenConsole.exe mUseCustomTerminal = boolValue("use_custom_terminal", true); -# else // x86 or x64 +# else mUseCustomTerminal = boolValue("use_custom_terminal", false); # endif #else // UNIX diff --git a/RedPandaIDE/settings.h b/RedPandaIDE/settings.h index 8c10a4b0..19759e1d 100644 --- a/RedPandaIDE/settings.h +++ b/RedPandaIDE/settings.h @@ -1357,6 +1357,7 @@ public: explicit CompilerSet(); explicit CompilerSet(const QString& compilerFolder, const QString& c_prog); explicit CompilerSet(const CompilerSet& set); + explicit CompilerSet(const QJsonObject& set); CompilerSet& operator= (const CompilerSet& ) = delete; CompilerSet& operator= (const CompilerSet&& ) = delete; @@ -1562,6 +1563,7 @@ public: private: PCompilerSet addSet(const QString& folder, const QString& c_prog); PCompilerSet addSet(const PCompilerSet &pSet); + PCompilerSet addSet(const QJsonObject &set); bool addSets(const QString& folder, const QString& c_prog); void savePath(const QString& name, const QString& path); void savePathList(const QString& name, const QStringList& pathList); diff --git a/RedPandaIDE/translations/RedPandaIDE_pt_BR.ts b/RedPandaIDE/translations/RedPandaIDE_pt_BR.ts index ebd88492..ac341853 100644 --- a/RedPandaIDE/translations/RedPandaIDE_pt_BR.ts +++ b/RedPandaIDE/translations/RedPandaIDE_pt_BR.ts @@ -12,8 +12,8 @@ <html><head/><body><h1 style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:xx-large; font-weight:600;">Red Panda C++</span></h1></body></html> - Based on Qt %1 (%2) - Com base em Qt %1 (%2) + Based on Qt %1 (%2) rodando em %3 + Com base em Qt %1 (%2) executado em %3 Build time: %1 %2 diff --git a/RedPandaIDE/translations/RedPandaIDE_zh_CN.ts b/RedPandaIDE/translations/RedPandaIDE_zh_CN.ts index 1d55b6c0..977b991f 100644 --- a/RedPandaIDE/translations/RedPandaIDE_zh_CN.ts +++ b/RedPandaIDE/translations/RedPandaIDE_zh_CN.ts @@ -62,8 +62,8 @@ p, li { white-space: pre-wrap; } - Based on Qt %1 (%2) - 基于Qt %1 (%2) + Based on Qt %1 (%2) running on %3 + 基于 Qt %1 (%2) 运行于 %3 diff --git a/RedPandaIDE/translations/RedPandaIDE_zh_TW.ts b/RedPandaIDE/translations/RedPandaIDE_zh_TW.ts index d3f4cde2..de4fd0cf 100644 --- a/RedPandaIDE/translations/RedPandaIDE_zh_TW.ts +++ b/RedPandaIDE/translations/RedPandaIDE_zh_TW.ts @@ -12,8 +12,8 @@ - Based on Qt %1 (%2) - + Based on Qt %1 (%2) running on %3 + 基於 Qt %1 (%2) 執行於 %3 Build time: %1 %2 diff --git a/RedPandaIDE/utils.cpp b/RedPandaIDE/utils.cpp index 9287f674..4f251ef1 100644 --- a/RedPandaIDE/utils.cpp +++ b/RedPandaIDE/utils.cpp @@ -17,6 +17,12 @@ #include #endif +#ifdef Q_OS_WIN +using pIsWow64Process2_t = BOOL (WINAPI *)( + HANDLE hProcess, USHORT *pProcessMachine, USHORT *pNativeMachine +); +#endif + QStringList splitProcessCommand(const QString &cmd) { QStringList result; @@ -387,21 +393,19 @@ bool isGreenEdition() { #ifdef Q_OS_WIN if (!gIsGreenEditionInited) { - QString keyString = QString("Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\RedPanda-C++"); - QString value; - if (!readRegistry(HKEY_LOCAL_MACHINE, keyString, "UninstallString", value)) { - keyString = "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\RedPanda-C++"; - if (!readRegistry(HKEY_LOCAL_MACHINE, keyString, "UninstallString", value)) { - value=""; - } - } - if (!value.isEmpty()) { - QString regPath = extractFileDir(value); - - QString appPath = QApplication::instance()->applicationDirPath(); - gIsGreenEdition = excludeTrailingPathDelimiter(regPath).compare(excludeTrailingPathDelimiter(appPath), - Qt::CaseInsensitive)!=0; - } + QString appPath = QApplication::instance()->applicationDirPath(); + appPath = excludeTrailingPathDelimiter(appPath); + QString keyString = R"(Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++)"; + QString systemInstallPath; + readRegistry(HKEY_LOCAL_MACHINE, keyString, "UninstallString", systemInstallPath); + if (!systemInstallPath.isEmpty()) + systemInstallPath = excludeTrailingPathDelimiter(extractFileDir(systemInstallPath)); + QString userInstallPath; + readRegistry(HKEY_CURRENT_USER, keyString, "UninstallString", userInstallPath); + if (!userInstallPath.isEmpty()) + userInstallPath = excludeTrailingPathDelimiter(extractFileDir(userInstallPath)); + gIsGreenEdition = appPath.compare(systemInstallPath, Qt::CaseInsensitive) != 0 && + appPath.compare(userInstallPath, Qt::CaseInsensitive) != 0; gIsGreenEditionInited = true; } return gIsGreenEdition; @@ -747,3 +751,37 @@ QString defaultShell() return "sh"; #endif } + +QString appArch() +{ +#ifdef _M_ARM64EC + return "arm64ec"; +#else + return QSysInfo::buildCpuArchitecture(); +#endif +} + +QString osArch() +{ +#ifdef Q_OS_WINDOWS + pIsWow64Process2_t pIsWow64Process2 = + reinterpret_cast(GetProcAddress(GetModuleHandleW(L"kernel32"), "IsWow64Process2")); + if (pIsWow64Process2) { + // IsWow64Process2 returns real native architecture under xtajit, while GetNativeSystemInfo does not. + USHORT processMachineResult, nativeMachineResult; + if (pIsWow64Process2(GetCurrentProcess(), &processMachineResult, &nativeMachineResult)) + switch (nativeMachineResult) { + case IMAGE_FILE_MACHINE_I386: + return "i386"; + case IMAGE_FILE_MACHINE_AMD64: + return "x86_64"; + case IMAGE_FILE_MACHINE_ARM64: + return "arm64"; + } + return QSysInfo::currentCpuArchitecture(); + } else + return QSysInfo::currentCpuArchitecture(); +#else + return QSysInfo::currentCpuArchitecture(); +#endif +} diff --git a/RedPandaIDE/utils.h b/RedPandaIDE/utils.h index 7ffa511b..80f51d06 100644 --- a/RedPandaIDE/utils.h +++ b/RedPandaIDE/utils.h @@ -173,4 +173,7 @@ QString escapeArgument(const QString &arg, bool isFirstArg); QString defaultShell(); +QString appArch(); +QString osArch(); + #endif // UTILS_H diff --git a/RedPandaIDE/widgets/aboutdialog.cpp b/RedPandaIDE/widgets/aboutdialog.cpp index 1a57b867..c4a6d392 100644 --- a/RedPandaIDE/widgets/aboutdialog.cpp +++ b/RedPandaIDE/widgets/aboutdialog.cpp @@ -31,25 +31,29 @@ AboutDialog::AboutDialog(QWidget *parent) : #if defined(__clang__) // Clang always pretends to be GCC/MSVC. Check it first. # if defined(_MSC_VER) - QString templ = "Clang %1.%2.%3 MSVC ABI"; + QString templ = "Clang %1.%2.%3 %4 MSVC ABI"; # elif defined(__apple_build_version__) - QString templ = "Apple Clang %1.%2.%3"; + QString templ = "Apple Clang %1.%2.%3 %4"; # else - QString templ = "Clang %1.%2.%3"; + QString templ = "Clang %1.%2.%3 %4"; # endif ui->lblQt->setText(ui->lblQt->text() .arg(qVersion()) .arg(templ .arg(__clang_major__) .arg(__clang_minor__) - .arg(__clang_patchlevel__))); + .arg(__clang_patchlevel__) + .arg(appArch())) + .arg(osArch())); #elif defined(__GNUC__) ui->lblQt->setText(ui->lblQt->text() .arg(qVersion()) - .arg(QString("GCC %1.%2.%3") + .arg(QString("GCC %1.%2.%3 %4") .arg(__GNUC__) .arg(__GNUC_MINOR__) - .arg(__GNUC_PATCHLEVEL__))); + .arg(__GNUC_PATCHLEVEL__) + .arg(appArch())) + .arg(osArch())); #elif defined(_MSC_VER) # if (_MSC_VER >= 1940) QString name = tr("Next Generation Microsoft Visual C++"); @@ -64,11 +68,13 @@ AboutDialog::AboutDialog(QWidget *parent) : # endif ui->lblQt->setText(ui->lblQt->text() .arg(qVersion()) - .arg(name)); + .arg(name + " " + appArch()) + .arg(osArch())); #else ui->lblQt->setText(ui->lblQt->text() .arg(qVersion()) - .arg(tr("Non-GCC Compiler"))); + .arg(tr("Non-GCC Compiler")) + .arg(osArch())); #endif ui->lblCompileTime->setText(ui->lblCompileTime->text() .arg(__DATE__, __TIME__)); diff --git a/RedPandaIDE/widgets/aboutdialog.ui b/RedPandaIDE/widgets/aboutdialog.ui index 715595b0..8b5a4fc3 100644 --- a/RedPandaIDE/widgets/aboutdialog.ui +++ b/RedPandaIDE/widgets/aboutdialog.ui @@ -32,7 +32,7 @@ - Based on Qt %1 (%2) + Based on Qt %1 (%2) running on %3 diff --git a/Red_Panda_CPP.pro b/Red_Panda_CPP.pro index 8e4d86da..543a5c6a 100644 --- a/Red_Panda_CPP.pro +++ b/Red_Panda_CPP.pro @@ -89,6 +89,7 @@ win32: { resources.files += platform/windows/installer-scripts/config.nsh resources.files += platform/windows/installer-scripts/config32.nsh resources.files += platform/windows/installer-scripts/config-clang.nsh + resources.files += platform/windows/qt.conf resources.files += README.md resources.files += NEWS.md resources.files += LICENSE diff --git a/docs/addon.md b/docs/addon.md new file mode 100644 index 00000000..88c72552 --- /dev/null +++ b/docs/addon.md @@ -0,0 +1,193 @@ +# Add-on Interface (Unstable) + +## Basic + +- Add-on is a Lua script. +- Values passed or returned to Red Panda C++ must be JSON compatible, i.e., + - `nil` (maps to `null`) + - `boolean` + - `number` + - `string` + - empty `table` (maps to `null`) + - `table` with only array part (maps to `array`) + - `table` with only hash part (maps to `object`) +- 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, + -- ... + } + ``` + +## Simple Add-on + +A simple add-on is a Lua script that returns a single value. + +### Compiler Hint Add-on + +If `$appLibexecDir/compiler_hint.lua` exists, it will be executed as compiler hint add-on when searching for compiler. + +Available API groups: +- `C_Debug` +- `C_Desktop` +- `C_FileSystem` +- `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. diff --git a/packages/archlinux/PKGBUILD.in b/packages/archlinux/PKGBUILD.in index 9a78096d..8251957b 100644 --- a/packages/archlinux/PKGBUILD.in +++ b/packages/archlinux/PKGBUILD.in @@ -8,15 +8,41 @@ url="https://github.com/royqh1979/$_pkgname" license=('GPL3') depends=(qt5-base qt5-svg gcc gdb) makedepends=(qt5-tools) +makedepends_x86_64=(mingw-w64-gcc) optdepends=( 'clang: C/C++ compiler (alternative)' ) +optdepends_x86_64=( + 'mingw-w64-gcc: Windows C/C++ compiler' + 'wine: run Windows executable' + 'aarch64-linux-gnu-gcc: AArch64 C/C++ compiler' + 'qemu-user-static: run AArch64 executable' + 'qemu-user-static-binfmt: run AArch64 executable' +) conflicts=("${_pkgname,,}") provides=("${_pkgname,,}") -source=("$_pkgname.tar.gz") -sha256sums=('SKIP') +source=( + "$_pkgname.tar.gz" + 'compiler_hint.lua' +) +sha256sums=( + 'SKIP' + 'SKIP' +) + +prepare() { + sed -i '/CONFIG += ENABLE_LUA_ADDON/ { s/^#\s*// }' RedPanda-CPP/RedPandaIDE/RedPandaIDE.pro +} build() { + local _utf8dir="$srcdir/$_pkgname/platform/windows/utf8" + if [[ "$CARCH" == 'x86_64' ]]; then + x86_64-w64-mingw32-gcc -Os -fno-exceptions -nodefaultlibs -nostdlib -c -o utf8init.x86_64.o "$_utf8dir/utf8init.cpp" + x86_64-w64-mingw32-windres -O coff -o utf8manifest.x86_64.o "$_utf8dir/utf8manifest.rc" + i686-w64-mingw32-gcc -Os -fno-exceptions -nodefaultlibs -nostdlib -c -o utf8init.i686.o "$_utf8dir/utf8init.cpp" + i686-w64-mingw32-windres -O coff -o utf8manifest.i686.o "$_utf8dir/utf8manifest.rc" + fi + mkdir redpanda-build cd redpanda-build qmake \ @@ -28,6 +54,16 @@ build() { } package() { + local _libexecdir="$pkgdir/usr/lib/RedPandaCPP" + install -Dm644 "compiler_hint.lua" "$_libexecdir/compiler_hint.lua" + + if [[ "$CARCH" == 'x86_64' ]]; then + install -Dm644 "utf8init.x86_64.o" "$_libexecdir/x86_64-w64-mingw32/utf8init.o" + install -Dm644 "utf8manifest.x86_64.o" "$_libexecdir/x86_64-w64-mingw32/utf8manifest.o" + install -Dm644 "utf8init.i686.o" "$_libexecdir/i686-w64-mingw32/utf8init.o" + install -Dm644 "utf8manifest.i686.o" "$_libexecdir/i686-w64-mingw32/utf8manifest.o" + fi + cd redpanda-build make INSTALL_ROOT="$pkgdir" install } diff --git a/packages/archlinux/buildpkg.sh b/packages/archlinux/buildpkg.sh index 28af7aba..aa7b68bb 100755 --- a/packages/archlinux/buildpkg.sh +++ b/packages/archlinux/buildpkg.sh @@ -10,6 +10,7 @@ VERSION=$(git describe --long --tags | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g') sed "s/__VERSION__/$VERSION/g" packages/archlinux/PKGBUILD.in >"$TMP_FOLDER/PKGBUILD" git archive --prefix="RedPanda-CPP/" -o "$TMP_FOLDER/RedPanda-CPP.tar.gz" HEAD +cp packages/archlinux/compiler_hint.lua "$TMP_FOLDER/" cd "$TMP_FOLDER" makepkg -s --noconfirm diff --git a/packages/archlinux/compiler_hint.lua b/packages/archlinux/compiler_hint.lua new file mode 100644 index 00000000..3e7a0071 --- /dev/null +++ b/packages/archlinux/compiler_hint.lua @@ -0,0 +1,271 @@ +local arch = C_System.osArch() +local libexecDir = C_System.appLibexecDir() +local lang = C_Desktop.language() + +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 偵錯", + }, +} + +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 +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) +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 + +-- 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 + +-- 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 + +-- 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", + } + local release, debug, debugWithAsan = generateConfig( + (nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " x86_64", + "/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", + }, + } + ) + 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", + "/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", + }, + } + ) + 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 diff --git a/packages/msys/domain/build.sh b/packages/msys/domain/build.sh new file mode 100644 index 00000000..59f57d8e --- /dev/null +++ b/packages/msys/domain/build.sh @@ -0,0 +1,247 @@ +#!/bin/bash + +case $MSYSTEM in + MINGW32|CLANG32) + _NATIVE_ARCH=i686 + _DISPLAY_ARCH=x86 + ;; + MINGW64|UCRT64|CLANG64) + _NATIVE_ARCH=x86_64 + _DISPLAY_ARCH=x64 + ;; + CLANGARM64) + _NATIVE_ARCH=aarch64 + _DISPLAY_ARCH=arm64 + ;; + *) + echo "This script must be run from one of following MSYS2 shells:" + echo " - MINGW32/CLANG32" + echo " - MINGW64/UCRT64/CLANG64" + echo " - CLANGARM64" + exit 1 + ;; +esac + +set -euxo pipefail + +GCC_VERSION="13.2.0" +MINGW_VERSION="rt_v11-rev1" +LLVM_MINGW_TAG="20231128" +WINDOWS_TERMINAL_VERSION="1.18.3181.0" + +_QMAKE="$MINGW_PREFIX/qt5-static/bin/qmake" +_NSIS="/mingw32/bin/makensis" + +_MINGW32_DIR="mingw32" +_MINGW32_TRIPLET="i686-w64-mingw32" +_MINGW32_ARCHIVE="i686-$GCC_VERSION-release-posix-dwarf-ucrt-$MINGW_VERSION.7z" +_MINGW32_URL="https://github.com/niXman/mingw-builds-binaries/releases/download/$GCC_VERSION-$MINGW_VERSION/$_MINGW32_ARCHIVE" + +_MINGW64_DIR="mingw64" +_MINGW64_TRIPLET="x86_64-w64-mingw32" +_MINGW64_ARCHIVE="x86_64-$GCC_VERSION-release-posix-seh-ucrt-$MINGW_VERSION.7z" +_MINGW64_URL="https://github.com/niXman/mingw-builds-binaries/releases/download/$GCC_VERSION-$MINGW_VERSION/$_MINGW64_ARCHIVE" + +_LLVM_DIR="llvm-mingw" +_LLVM_ARCHES=("x86_64" "i686" "aarch64" "armv7") +_LLVM_ORIGINAL_DIR="llvm-mingw-$LLVM_MINGW_TAG-ucrt-$_NATIVE_ARCH" +_LLVM_ARCHIVE="$_LLVM_ORIGINAL_DIR.zip" +_LLVM_URL="https://github.com/mstorsjo/llvm-mingw/releases/download/$LLVM_MINGW_TAG/$_LLVM_ARCHIVE" + +_WINDOWS_TERMINAL_DIR="terminal-${WINDOWS_TERMINAL_VERSION}" +_WINDOWS_TERMINAL_ARCHIVE="Microsoft.WindowsTerminal_${WINDOWS_TERMINAL_VERSION}_$_DISPLAY_ARCH.zip" +_WINDOWS_TERMINAL_URL="https://github.com/microsoft/terminal/releases/download/v${WINDOWS_TERMINAL_VERSION}/${_WINDOWS_TERMINAL_ARCHIVE}" + +_SRCDIR="$PWD" +_ASSETSDIR="$PWD/assets" +_BUILDDIR="$TEMP/$MINGW_PACKAGE_PREFIX-redpanda-build" +_PKGDIR="$TEMP/$MINGW_PACKAGE_PREFIX-redpanda-pkg" +_DISTDIR="$PWD/dist" + +_REDPANDA_VERSION=$(sed -nr -e '/APP_VERSION\s*=/ s/APP_VERSION\s*=\s*(([0-9]+\.)*[0-9]+)\s*/\1/p' "$_SRCDIR"/Red_Panda_CPP.pro) +_REDPANDA_TESTVERSION=$(sed -nr -e '/TEST_VERSION\s*=/ s/TEST_VERSION\s*=\s*([A-Za-z0-9]*)\s*/\1/p' "$_SRCDIR"/Red_Panda_CPP.pro) +if [[ -n $_REDPANDA_TESTVERSION ]]; then + _REDPANDA_VERSION="$_REDPANDA_VERSION.$_REDPANDA_TESTVERSION" +fi + +_CLEAN=0 +_SKIP_DEPS_CHECK=0 +_7Z_REPACK=0 +while [[ $# -gt 0 ]]; do + case $1 in + --clean) + _CLEAN=1 + shift + ;; + --skip-deps-check) + _SKIP_DEPS_CHECK=1 + shift + ;; + --7z) + _7Z_REPACK=1 + shift + ;; + *) + echo "Unknown argument: $1" + exit 1 + ;; + esac +done + +function check-deps() { + # MSYS2’s `pacman -Q` is 100x slower than Arch Linux. Allow skipping the check. + [[ $_SKIP_DEPS_CHECK -eq 1 ]] && return + case $MSYSTEM in + MINGW32|MINGW64|UCRT64) + local compiler=gcc + ;; + CLANG32|CLANG64|CLANGARM64) + local compiler=clang + ;; + esac + local deps=( + $MINGW_PACKAGE_PREFIX-{$compiler,make,qt5-static} + mingw-w64-i686-nsis + ) + [[ _7Z_REPACK -eq 1 ]] || deps+=("$MINGW_PACKAGE_PREFIX-7zip") + for dep in "${deps[@]}"; do + pacman -Q "$dep" >/dev/null 2>&1 || ( + echo "Missing dependency: $dep" + exit 1 + ) + done +} + +function prepare-dirs() { + [[ $_CLEAN -eq 1 ]] && rm -rf "$_BUILDDIR" "$_PKGDIR" || true + mkdir -p "$_ASSETSDIR" "$_BUILDDIR" "$_PKGDIR" "$_DISTDIR" +} + +function download-assets() { + if [[ $_NATIVE_ARCH == i686 ]]; then + [[ -f "$_ASSETSDIR/$_MINGW32_ARCHIVE" ]] || curl -L -o "$_ASSETSDIR/$_MINGW32_ARCHIVE" "$_MINGW32_URL" + fi + if [[ $_NATIVE_ARCH == x86_64 ]]; then + [[ -f "$_ASSETSDIR/$_MINGW64_ARCHIVE" ]] || curl -L -o "$_ASSETSDIR/$_MINGW64_ARCHIVE" "$_MINGW64_URL" + fi + [[ -f "$_ASSETSDIR/$_LLVM_ARCHIVE" ]] || curl -L -o "$_ASSETSDIR/$_LLVM_ARCHIVE" "$_LLVM_URL" + [[ -f "$_ASSETSDIR/$_WINDOWS_TERMINAL_ARCHIVE" ]] || curl -L -o "$_ASSETSDIR/$_WINDOWS_TERMINAL_ARCHIVE" "$_WINDOWS_TERMINAL_URL" +} + +function prepare-mingw() { + local bit="$1" + eval local mingw_dir="\$_MINGW${bit}_DIR" + eval local mingw_archive="\$_MINGW${bit}_ARCHIVE" + eval local mingw_triplet="\$_MINGW${bit}_TRIPLET" + local mingw_dir="$_BUILDDIR/$mingw_dir" + local mingw_lib_dir="$mingw_dir/$mingw_triplet/lib" + local mingw_shared_dir="$mingw_dir/$mingw_triplet/bin" + local mingw_include_dir="$mingw_dir/$mingw_triplet/include" + if [[ ! -d "$mingw_dir" ]]; then + bsdtar -C "$_BUILDDIR" -xf "$_ASSETSDIR/$mingw_archive" + local old_path="$PATH" + export PATH="$mingw_dir/bin:$PATH" + { + gcc -Os -fno-exceptions -nodefaultlibs -nostdlib -c -o "$mingw_lib_dir/utf8init.o" "$_SRCDIR/platform/windows/utf8/utf8init.cpp" + windres -O coff -o "$mingw_lib_dir/utf8manifest.o" "$_SRCDIR/platform/windows/utf8/utf8manifest.rc" + } + export PATH="$old_path" + fi +} + +function prepare-llvm-mingw() { + local llvm_dir="$_BUILDDIR/$_LLVM_DIR" + if [[ ! -d "$llvm_dir" ]]; then + bsdtar -C "$_BUILDDIR" -xf "$_ASSETSDIR/$_LLVM_ARCHIVE" + mv "$_BUILDDIR/$_LLVM_ORIGINAL_DIR" "$llvm_dir" + local old_path="$PATH" + export PATH="$llvm_dir/bin:$PATH" + for arch in "${_LLVM_ARCHES[@]}"; do + local triplet="$arch-w64-mingw32" + local lib_dir="$llvm_dir/$triplet/lib" + $triplet-clang -Os -fno-exceptions -nodefaultlibs -nostdlib -c -o "$lib_dir/utf8init.o" "$_SRCDIR/platform/windows/utf8/utf8init.cpp" + $triplet-windres -O coff -o "$lib_dir/utf8manifest.o" "$_SRCDIR/platform/windows/utf8/utf8manifest.rc" + + local msvc_triplet="$arch-pc-windows-msvc" + local lib_dir="$llvm_dir/$msvc_triplet/lib" + mkdir -p "$lib_dir" + $triplet-clang -target $msvc_triplet -Os -fno-exceptions -nodefaultlibs -nostdlib -c -o "$lib_dir/utf8init.o" "$_SRCDIR/platform/windows/utf8/utf8init.cpp" + $triplet-windres -O coff -o "$lib_dir/utf8manifest.o" "$_SRCDIR/platform/windows/utf8/utf8manifest.rc" + done + { + local triplet="x86_64-w64-mingw32" + local msvc_triplet="arm64ec-pc-windows-msvc" + local lib_dir="$llvm_dir/$msvc_triplet/lib" + mkdir -p "$lib_dir" + $triplet-clang -target $msvc_triplet -Os -fno-exceptions -nodefaultlibs -nostdlib -c -o "$lib_dir/utf8init.o" "$_SRCDIR/platform/windows/utf8/utf8init.cpp" + $triplet-windres -O coff -o "$lib_dir/utf8manifest.o" "$_SRCDIR/platform/windows/utf8/utf8manifest.rc" + } + export PATH="$old_path" + fi +} + +function prepare-openconsole() { + local windows_terminal_dir="$_BUILDDIR/$_WINDOWS_TERMINAL_DIR" + if [[ ! -d "$windows_terminal_dir" ]]; then + bsdtar -C "$_BUILDDIR" -xf "$_ASSETSDIR/$_WINDOWS_TERMINAL_ARCHIVE" + fi +} + +function prepare-src() { + cp "$_SRCDIR"/RedPandaIDE/RedPandaIDE.pro{,.bak} + sed -i '/CONFIG += ENABLE_LUA_ADDON/ { s/^#\s*// }' "$_SRCDIR"/RedPandaIDE/RedPandaIDE.pro +} + +function restore-src() { + mv "$_SRCDIR"/RedPandaIDE/RedPandaIDE.pro{.bak,} +} + +function build() { + pushd "$_BUILDDIR" + "$_QMAKE" PREFIX="$_PKGDIR" "$_SRCDIR" + time mingw32-make WINDOWS_PREFER_OPENCONSOLE=ON -j$(nproc) + mingw32-make install + + cp "$_SRCDIR"/packages/msys/domain/{main.nsi,lang.nsh,compiler_hint.lua} "$_PKGDIR" + cp "$_WINDOWS_TERMINAL_DIR/OpenConsole.exe" "$_PKGDIR" + if [[ $_NATIVE_ARCH == i686 ]]; then + [[ -d "$_PKGDIR/mingw32" ]] || cp -r "mingw32" "$_PKGDIR" + fi + if [[ $_NATIVE_ARCH == x86_64 ]]; then + [[ -d "$_PKGDIR/mingw64" ]] || cp -r "mingw64" "$_PKGDIR" + fi + [[ -d "$_PKGDIR/llvm-mingw" ]] || cp -r "llvm-mingw" "$_PKGDIR" + popd +} + +function package() { + pushd "$_PKGDIR" + "$_NSIS" -DVERSION="$_REDPANDA_VERSION" -DARCH="$_DISPLAY_ARCH" main.nsi & + "$_NSIS" -DVERSION="$_REDPANDA_VERSION" -DARCH="$_DISPLAY_ARCH" -DUSER_MODE main.nsi & + wait + if [[ _7Z_REPACK -eq 1 ]]; then + 7z x "redpanda-cpp-$_REDPANDA_VERSION-$_DISPLAY_ARCH-user.exe" -o"RedPanda-CPP" -xr'!$PLUGINSDIR' -x"!uninstall.exe" + 7z a -t7z -mx=9 -ms=on -mqs=on -mf=BCJ2 -m0="LZMA2:d=128m:fb=273:c=2g" "redpanda-cpp-$_REDPANDA_VERSION-$_DISPLAY_ARCH.7z" "RedPanda-CPP" + rm -rf "RedPanda-CPP" + fi + popd +} + +function dist() { + cp "$_PKGDIR/redpanda-cpp-$_REDPANDA_VERSION-$_DISPLAY_ARCH-system.exe" "$_DISTDIR" + cp "$_PKGDIR/redpanda-cpp-$_REDPANDA_VERSION-$_DISPLAY_ARCH-user.exe" "$_DISTDIR" + [[ _7Z_REPACK -eq 1 ]] && cp "$_PKGDIR/redpanda-cpp-$_REDPANDA_VERSION-$_DISPLAY_ARCH.7z" "$_DISTDIR" +} + +check-deps +prepare-dirs +download-assets +[[ $_NATIVE_ARCH == i686 ]] && prepare-mingw 32 +[[ $_NATIVE_ARCH == x86_64 ]] && prepare-mingw 64 +prepare-llvm-mingw +prepare-openconsole +prepare-src +trap restore-src EXIT INT TERM +build +package +dist diff --git a/packages/msys/domain/compiler_hint.lua b/packages/msys/domain/compiler_hint.lua new file mode 100644 index 00000000..c5532b79 --- /dev/null +++ b/packages/msys/domain/compiler_hint.lua @@ -0,0 +1,386 @@ +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() + +local gnuArchMap = { + 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 偵錯", + }, +} + +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, +} + +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 +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) +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, + { + 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 + + 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}, + } + 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, + { + 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" + 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, + { + 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) + + 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}, + } + 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, + { + 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 diff --git a/packages/msys/domain/lang.nsh b/packages/msys/domain/lang.nsh new file mode 100644 index 00000000..b718b1c5 --- /dev/null +++ b/packages/msys/domain/lang.nsh @@ -0,0 +1,65 @@ +/* English 1033 */ +LangString MessageAppName 1033 "Red Panda C++" +LangString Message64bitBuildWarning 1033 "Note: installing Red Panda C++ x86 on 64-bit OS. There are 64-bit builds." +LangString MessageArm64BuildWarning 1033 "Note: installing Red Panda C++ x64 on ARM64. There is native ARM64 build." +LangString MessageWin10v1809RecommendedWarning 1033 "Note: Windows 10 version 1809 or later recommeded." +LangString Message64bitRequiredError 1033 "Unsupported operating system! 64-bit OS required." +LangString MessageArm64RequiredError 1033 "Unsupported operating system! ARM64 OS required." +LangString MessageWin7RequiredError 1033 "Unsupported operating system! Windows 7 or later required." +LangString MessageWin11RequiredError 1033 "Unsupported operating system! Windows 11 or later required." +LangString MessageSectionMain 1033 "The Red Panda C++ IDE (Integrated Development Environment)." +LangString MessageSectionOpenConsole 1033 "UTF-8 compatible OpenConsole.exe terminal emulator from Windows Terminal." +LangString MessageSectionMingw32 1033 "The MinGW GCC x86 compiler and associated tools, headers and libraries." +LangString MessageSectionMingw64 1033 "The MinGW GCC x64 compiler and associated tools, headers and libraries." +LangString MessageSectionLlvm 1033 "The LLVM MinGW compiler and associated tools, headers and libraries." +LangString MessageSectionAssocs 1033 "Use Red Panda C++ as the default application for opening these types of files." +LangString MessageSectionShortcuts 1033 "Create shortcuts to Red Panda C++ in various folders." +LangString MessageSectionConfig 1033 "Remove all leftover configuration files from previous installs." +LangString MessageUninstallText 1033 "This program will uninstall Red Panda C++, continue?" +LangString MessageUninstallExisting 1033 "Red Panda C++ is already installed.$\n$\nClick OK to remove the previous version or Cancel to cancel the installation." +LangString MessageRemoveConfig 1033 "Do you want to remove all the remaining configuration files?" +LangString SectionMainName 1033 "Program files (required)" +LangString SectionOpenConsoleName 1033 "OpenConsole.exe terminal emulator" +LangString SectionMingw32Name 1033 "MinGW GCC x86 compiler" +LangString SectionMingw64Name 1033 "MinGW GCC x64 compiler" +LangString SectionLlvmName 1033 "LLVM MinGW compiler" +LangString SectionAssocsName 1033 "Associate files to Red Panda C++" +LangString SectionAssocExtNameBegin 1033 "Associate" +LangString SectionAssocExtNameEnd 1033 "files to Red Panda C++" +LangString SectionShortcutsName 1033 "Shortcuts" +LangString SectionMenuLaunchName 1033 "Create Start Menu shortcuts" +LangString SectionDesktopLaunchName 1033 "Create Desktop shortcut" +LangString SectionConfigName 1033 "Remove old configuration files" + +/* Simplified Chinese 2052 */ +LangString MessageAppName 2052 "小熊猫 C++" +LangString Message64bitBuildWarning 2052 "提示:正在把 x86 小熊猫 C++ 安装到 64 位操作系统上。小熊猫 C++ 有 64 位版本。" +LangString MessageArm64BuildWarning 2052 "提示:正在把 x64 小熊猫 C++ 安装到 ARM64 系统上。小熊猫 C++ 有原生 ARM64 版本。" +LangString MessageWin10v1809RecommendedWarning 2052 "提示:建议使用 Windows 10 1809 或更高版本。" +LangString Message64bitRequiredError 2052 "不支持的操作系统!需要 64 位操作系统。" +LangString MessageArm64RequiredError 2052 "不支持的操作系统!需要 ARM64 操作系统。" +LangString MessageWin7RequiredError 2052 "不支持的操作系统!需要 Windows 7 或更高版本。" +LangString MessageWin11RequiredError 2052 "不支持的操作系统!需要 Windows 11 或更高版本。" +LangString MessageSectionMain 2052 "小熊猫 C++ IDE(集成开发环境)。" +LangString MessageSectionOpenConsole 2052 "兼容 UTF-8 的 OpenConsole.exe 终端模拟器,来自 Windows 终端。" +LangString MessageSectionMingw32 2052 "MinGW GCC x86 编译器和相关的工具、头文件和库。" +LangString MessageSectionMingw64 2052 "MinGW GCC x64 编译器和相关的工具、头文件和库。" +LangString MessageSectionLlvm 2052 "LLVM MinGW 编译器和相关的工具、头文件和库。" +LangString MessageSectionAssocs 2052 "使用小熊猫 C++ 打开这些文件。" +LangString MessageSectionShortcuts 2052 "开始菜单和快捷方式。" +LangString MessageSectionConfig 2052 "删除之前安装遗留的所有配置文件。" +LangString MessageUninstallText 2052 "将要删除小熊猫 C++,是否继续?" +LangString MessageUninstallExisting 2052 "本机上已经安装了旧版本小熊猫 C++。 $\n$\n点击“确定”以将其删除并继续,或者“取消”中止安装。" +LangString MessageRemoveConfig 2052 "你想要删除所有的配置文件吗?" +LangString SectionMainName 2052 "程序文件(必需)" +LangString SectionOpenConsoleName 2052 "OpenConsole.exe 终端模拟器" +LangString SectionMingw32Name 2052 "MinGW GCC x86 编译器" +LangString SectionMingw64Name 2052 "MinGW GCC x64 编译器" +LangString SectionLlvmName 2052 "LLVM MinGW 编译器" +LangString SectionAssocsName 2052 "关联文件到小熊猫C++" +LangString SectionAssocExtNameBegin 2052 "将" +LangString SectionAssocExtNameEnd 2052 "文件关联到小熊猫 C++" +LangString SectionShortcutsName 2052 "快捷方式" +LangString SectionMenuLaunchName 2052 "创建开始菜单程序项" +LangString SectionDesktopLaunchName 2052 "创建桌面快捷方式" +LangString SectionConfigName 2052 "删除原有配置文件" diff --git a/packages/msys/domain/main.nsi b/packages/msys/domain/main.nsi new file mode 100644 index 00000000..400ca71f --- /dev/null +++ b/packages/msys/domain/main.nsi @@ -0,0 +1,456 @@ +#################################################################### +# Startup + +CRCCheck on +SetCompressor /SOLID /FINAL lzma +SetCompressorDictSize 128 +SetDatablockOptimize on +Unicode True + +!ifdef USER_MODE + !define MODE "user" +!else + !define MODE "system" +!endif +!define FINALNAME "redpanda-cpp-${VERSION}-${ARCH}-${MODE}.exe" +!define DISPLAY_NAME "Red Panda C++ ${VERSION} (${ARCH} ${MODE})" + +!include "x64.nsh" +!include "WinVer.nsh" +!include "MUI2.nsh" +!include "lang.nsh" + +!define MUI_CUSTOMFUNCTION_GUIINIT myGuiInit + +#################################################################### +# Installer Attributes + +Name "${DISPLAY_NAME}" +OutFile "${FINALNAME}" +Caption "${DISPLAY_NAME}" + +LicenseData "LICENSE" + +!ifdef USER_MODE + RequestExecutionLevel user + InstallDir "$LOCALAPPDATA\RedPanda-CPP" +!else + RequestExecutionLevel admin + !if "${ARCH}" == "x86" + InstallDir "$PROGRAMFILES\RedPanda-CPP" + !else + InstallDir "$PROGRAMFILES64\RedPanda-CPP" + !endif +!endif + +#################################################################### +# Interface Settings + +ShowInstDetails show +AutoCloseWindow false +SilentInstall normal +SetOverwrite try +XPStyle on +ManifestDPIAware true + +InstType "Full" ;1 +InstType "Minimal" ;2 + +## Remember the installer language +!ifdef USER_MODE + !define MUI_LANGDLL_REGISTRY_ROOT "HKCU" +!else + !define MUI_LANGDLL_REGISTRY_ROOT "HKLM" +!endif +!define MUI_LANGDLL_REGISTRY_KEY "Software\RedPanda-C++" +!define MUI_LANGDLL_REGISTRY_VALUENAME "Installer Language" + +#################################################################### +# Pages + +!define MUI_ICON "devcpp.ico" +!define MUI_UNICON "devcpp.ico" +!define MUI_ABORTWARNING +!define MUI_LANGDLL_ALLLANGUAGES +!define MUI_FINISHPAGE_RUN "$INSTDIR\RedPandaIDE.exe" +!define MUI_FINISHPAGE_NOREBOOTSUPPORT +!define MUI_COMPONENTSPAGE_SMALLDESC + +!insertmacro MUI_PAGE_LICENSE "LICENSE" +!insertmacro MUI_PAGE_COMPONENTS +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_PAGE_FINISH +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +#################################################################### +# Languages + +!insertmacro MUI_LANGUAGE "English" +!insertmacro MUI_LANGUAGE "SimpChinese" + +#################################################################### +# Files, by option section + +Section "$(SectionMainName)" SectionMain + SectionIn 1 2 RO + + SetOutPath $INSTDIR + + ; Allways create an uninstaller + WriteUninstaller "$INSTDIR\uninstall.exe" +!ifdef USER_MODE + WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++" "DisplayName" "Red Panda C++" + WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++" "InstallLocation" "$INSTDIR" + WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++" "UninstallString" "$INSTDIR\uninstall.exe" + WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++" "DisplayVersion" "${VERSION}" + WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++" "DisplayIcon" "$INSTDIR\RedPandaIDE.exe" + WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++" "Publisher" "Roy Qu (royqh1979@gmail.com)" +!else + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++" "DisplayName" "Red Panda C++" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++" "InstallLocation" "$INSTDIR" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++" "UninstallString" "$INSTDIR\uninstall.exe" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++" "DisplayVersion" "${VERSION}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++" "DisplayIcon" "$INSTDIR\RedPandaIDE.exe" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++" "Publisher" "Roy Qu (royqh1979@gmail.com)" +!endif + + ; Write required files + File "RedPandaIDE.exe" + File "consolepauser.exe" + File "redpanda-win-git-askpass.exe" + File "astyle.exe" + File "qt.conf" + File "LICENSE" + File "NEWS.md" + File "README.md" + File "compiler_hint.lua" + + ; Write required paths + SetOutPath $INSTDIR\templates + File /nonfatal /r "templates\*" +SectionEnd + +Section "$(SectionOpenConsoleName)" SectionOpenConsole + SectionIn 1 + SetOutPath $INSTDIR + File "OpenConsole.exe" +SectionEnd + +!if "${ARCH}" == "x86" +Section "$(SectionMingw32Name)" SectionMingw32 + SectionIn 1 + SetOutPath $INSTDIR\mingw32 + File /nonfatal /r "mingw32\*" +SectionEnd +!endif + +!if "${ARCH}" == "x64" +Section "$(SectionMingw64Name)" SectionMingw64 + SectionIn 1 + SetOutPath $INSTDIR\mingw64 + File /nonfatal /r "mingw64\*" +SectionEnd +!endif + +Section "$(SectionLlvmName)" SectionLlvm + SectionIn 1 + SetOutPath $INSTDIR\llvm-mingw + File /nonfatal /r "llvm-mingw\*" +SectionEnd + +#################################################################### +# File association +SectionGroup "$(SectionAssocsName)" SectionAssocs + Section "$(SectionAssocExtNameBegin) .dev $(SectionAssocExtNameEnd)" + SectionIn 1 + + StrCpy $0 $INSTDIR\RedPandaIDE.exe + WriteRegStr HKCR ".dev" "" "DevCpp.dev" + WriteRegStr HKCR "DevCpp.dev" "" "Dev-C++ Project File" + WriteRegStr HKCR "DevCpp.dev\DefaultIcon" "" '$0,3' + WriteRegStr HKCR "DevCpp.dev\Shell\Open\Command" "" '$0 "%1"' + SectionEnd + + Section "$(SectionAssocExtNameBegin) .c $(SectionAssocExtNameEnd)" + SectionIn 1 + + StrCpy $0 $INSTDIR\RedPandaIDE.exe + WriteRegStr HKCR ".c" "" "DevCpp.c" + WriteRegStr HKCR "DevCpp.c" "" "C Source File" + WriteRegStr HKCR "DevCpp.c\DefaultIcon" "" '$0,4' + WriteRegStr HKCR "DevCpp.c\Shell\Open\Command" "" '$0 "%1"' + SectionEnd + + Section "$(SectionAssocExtNameBegin) .cpp $(SectionAssocExtNameEnd)" + SectionIn 1 + + StrCpy $0 $INSTDIR\RedPandaIDE.exe + WriteRegStr HKCR ".cpp" "" "DevCpp.cpp" + WriteRegStr HKCR "DevCpp.cpp" "" "C++ Source File" + WriteRegStr HKCR "DevCpp.cpp\DefaultIcon" "" '$0,5' + WriteRegStr HKCR "DevCpp.cpp\Shell\Open\Command" "" '$0 "%1"' + SectionEnd + + Section "$(SectionAssocExtNameBegin) .cxx $(SectionAssocExtNameEnd)" + SectionIn 1 + + StrCpy $0 $INSTDIR\RedPandaIDE.exe + WriteRegStr HKCR ".cxx" "" "DevCpp.cxx" + WriteRegStr HKCR "DevCpp.cxx" "" "C++ Source File" + WriteRegStr HKCR "DevCpp.cxx\DefaultIcon" "" '$0,5' + WriteRegStr HKCR "DevCpp.cxx\Shell\Open\Command" "" '$0 "%1"' + SectionEnd + + Section "$(SectionAssocExtNameBegin) .cc $(SectionAssocExtNameEnd)" + SectionIn 1 + + StrCpy $0 $INSTDIR\RedPandaIDE.exe + WriteRegStr HKCR ".cc" "" "DevCpp.cc" + WriteRegStr HKCR "DevCpp.cc" "" "C++ Source File" + WriteRegStr HKCR "DevCpp.cc\DefaultIcon" "" '$0,5' + WriteRegStr HKCR "DevCpp.cc\Shell\Open\Command" "" '$0 "%1"' + SectionEnd + + Section "$(SectionAssocExtNameBegin) .hxx $(SectionAssocExtNameEnd)" + SectionIn 1 + + StrCpy $0 $INSTDIR\RedPandaIDE.exe + WriteRegStr HKCR ".hxx" "" "DevCpp.hxx" + WriteRegStr HKCR "DevCpp.hxx" "" "C++ Header File" + WriteRegStr HKCR "DevCpp.hxx\DefaultIcon" "" '$0,7' + WriteRegStr HKCR "DevCpp.hxx\Shell\Open\Command" "" '$0 "%1"' + SectionEnd + + Section "$(SectionAssocExtNameBegin) .h $(SectionAssocExtNameEnd)" + SectionIn 1 + + StrCpy $0 $INSTDIR\RedPandaIDE.exe + WriteRegStr HKCR ".h" "" "DevCpp.h" + WriteRegStr HKCR "DevCpp.h" "" "C Header File" + WriteRegStr HKCR "DevCpp.h\DefaultIcon" "" '$0,6' + WriteRegStr HKCR "DevCpp.h\Shell\Open\Command" "" '$0 "%1"' + SectionEnd + + Section "$(SectionAssocExtNameBegin) .hpp $(SectionAssocExtNameEnd)" + SectionIn 1 + + StrCpy $0 $INSTDIR\RedPandaIDE.exe + WriteRegStr HKCR ".hpp" "" "DevCpp.hpp" + WriteRegStr HKCR "DevCpp.hpp" "" "C++ Header File" + WriteRegStr HKCR "DevCpp.hpp\DefaultIcon" "" '$0,7' + WriteRegStr HKCR "DevCpp.hpp\Shell\Open\Command" "" '$0 "%1"' + SectionEnd +SectionGroupEnd + +#################################################################### +# Shortcuts +SectionGroup "$(SectionShortcutsName)" SectionShortcuts + Section "$(SectionMenuLaunchName)" SectionMenuLaunch + SectionIn 1 2 + + StrCpy $0 $SMPROGRAMS ; start menu Programs folder + CreateDirectory "$0\$(MessageAppName)" + CreateShortCut "$0\$(MessageAppName)\$(MessageAppName).lnk" "$INSTDIR\RedPandaIDE.exe" + CreateShortCut "$0\$(MessageAppName)\License.lnk" "$INSTDIR\LICENSE" + CreateShortCut "$0\$(MessageAppName)\Uninstall $(MessageAppName).lnk" "$INSTDIR\uninstall.exe" + SectionEnd + + Section "$(SectionDesktopLaunchName)" SectionDesktopLaunch + SectionIn 1 2 + + CreateShortCut "$DESKTOP\$(MessageAppName).lnk" "$INSTDIR\RedPandaIDE.exe" + SectionEnd +SectionGroupEnd + +!ifdef USER_MODE +Section "$(SectionConfigName)" SectionConfig + RMDir /r "$APPDATA\RedPandaIDE" +SectionEnd +!endif + +#################################################################### + +!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN +!insertmacro MUI_DESCRIPTION_TEXT ${SectionMain} "$(MessageSectionMain)" +!insertmacro MUI_DESCRIPTION_TEXT ${SectionOpenConsole} "$(MessageSectionOpenConsole)" +!if "${ARCH}" == "x86" +!insertmacro MUI_DESCRIPTION_TEXT ${SectionMingw32} "$(MessageSectionMingw32)" +!endif +!if "${ARCH}" == "x64" +!insertmacro MUI_DESCRIPTION_TEXT ${SectionMingw64} "$(MessageSectionMingw64)" +!endif +!insertmacro MUI_DESCRIPTION_TEXT ${SectionLlvm} "$(MessageSectionLlvm)" +!insertmacro MUI_DESCRIPTION_TEXT ${SectionShortcuts} "$(MessageSectionShortcuts)" +!insertmacro MUI_DESCRIPTION_TEXT ${SectionAssocs} "$(MessageSectionAssocs)" +!ifdef USER_MODE +!insertmacro MUI_DESCRIPTION_TEXT ${SectionConfig} "$(MessageSectionConfig)" +!endif +!insertmacro MUI_FUNCTION_DESCRIPTION_END + +#################################################################### +# Functions, utilities + +Function .onInit + !insertmacro MUI_LANGDLL_DISPLAY +!if "${ARCH}" != "x86" + SetRegView 64 +!endif +!ifdef USER_MODE + SetShellVarContext current +!else + SetShellVarContext all +!endif + ${IfNot} ${AtLeastBuild} 17763 ; OpenConsole.exe requires Windows 10 v1809 ConPTY +!if "${ARCH}" == "x86" + ${OrIfNot} ${IsNativeIA32} ; OpenConsole.exe x86 only works on x86, while x64 works on arm64 +!endif + SectionSetFlags ${SectionOpenConsole} ${SF_RO} + ${EndIf} +FunctionEnd + +Function myGuiInit + ${IfNot} ${AtLeastWin7} + MessageBox MB_OK|MB_ICONSTOP "$(MessageWin7RequiredError)" + Abort + ${EndIf} + ${IfNot} ${AtLeastBuild} 17763 + MessageBox MB_OK|MB_ICONSTOP "$(MessageWin10v1809RecommendedWarning)" + ${EndIf} +!if "${ARCH}" == "x86" + ${If} ${IsNativeAMD64} + ${OrIf} ${IsNativeARM64} + # note user if installing x86 on 64-bit OS + MessageBox MB_OK|MB_ICONINFORMATION "$(Message64bitBuildWarning)" + ${EndIf} +!else if "${ARCH}" == "x64" + ${If} ${IsNativeIA32} + MessageBox MB_OK|MB_ICONSTOP "$(Message64bitRequiredError)" + Abort + ${ElseIf} ${IsNativeARM64} + # x64 can be installed on arm64 ... + MessageBox MB_OK|MB_ICONINFORMATION "$(MessageArm64BuildWarning)" + # ... but only works since Windows 11 + ${IfNot} ${AtLeastBuild} 22000 + MessageBox MB_OK|MB_ICONSTOP "$(MessageWin11RequiredError)" + Abort + ${EndIf} + ${EndIf} +!else if "${ARCH}" == "arm64" + ${IfNot} ${IsNativeARM64} + MessageBox MB_OK|MB_ICONSTOP "$(MessageArm64RequiredError)" + Abort + ${EndIf} +!endif + + SetRegView 32 + Call UninstallExisting + SetRegView 64 + Call UninstallExisting +!if "${ARCH}" == "x86" + SetRegView 32 +!endif +FunctionEnd + +Function .onSelChange + ${IfNot} ${AtLeastBuild} 17763 +!if "${ARCH}" == "x86" + ${OrIfNot} ${IsNativeIA32} +!endif + SectionSetFlags ${SectionOpenConsole} ${SF_RO} + ${EndIf} +FunctionEnd + +Function un.onInit + !insertmacro MUI_UNGETLANGUAGE +!if "${ARCH}" != "x86" + SetRegView 64 +!endif +!ifdef USER_MODE + SetShellVarContext current +!else + SetShellVarContext all +!endif +FunctionEnd + +Function UninstallExisting +!ifdef USER_MODE + ReadRegStr $R0 HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++" "UninstallString" +!else + ReadRegStr $R0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++" "UninstallString" +!endif + ${If} $R0 != "" + GetFullPathName $R1 "$R0\.." ; remove \uninstall.exe + MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \ + "$(MessageUninstallExisting)" \ + IDOK uninst + Abort + uninst: + ClearErrors + ExecWait '"$R0" /S _?=$R1' + ${EndIf} +FunctionEnd + +#################################################################### +# uninstall + +UninstallText "$(MessageUninstallText)" +ShowUninstDetails show + +Section "Uninstall" + ; Remove uninstaller + Delete "$INSTDIR\uninstall.exe" + + ; Remove start menu stuff + Delete "$SMPROGRAMS\$(MessageAppName)\$(MessageAppName).lnk" + Delete "$SMPROGRAMS\$(MessageAppName)\License.lnk" + Delete "$SMPROGRAMS\$(MessageAppName)\Uninstall $(MessageAppName).lnk" + RMDir "$SMPROGRAMS\$(MessageAppName)" + + ; Remove desktop stuff + Delete "$QUICKLAUNCH\$(MessageAppName).lnk" + Delete "$DESKTOP\$(MessageAppName).lnk" + + DeleteRegKey HKCR "DevCpp.dev" + DeleteRegKey HKCR "DevCpp.c" + DeleteRegKey HKCR "DevCpp.cpp" + DeleteRegKey HKCR "DevCpp.cxx" + DeleteRegKey HKCR "DevCpp.cc" + DeleteRegKey HKCR "DevCpp.h" + DeleteRegKey HKCR "DevCpp.hpp" + DeleteRegKey HKCR "DevCpp.hxx" + + Delete "$INSTDIR\NEWS.md" + Delete "$INSTDIR\RedPandaIDE.exe" + Delete "$INSTDIR\consolepauser.exe" + Delete "$INSTDIR\OpenConsole.exe" + Delete "$INSTDIR\redpanda-win-git-askpass.exe" + Delete "$INSTDIR\astyle.exe" + Delete "$INSTDIR\qt.conf" + Delete "$INSTDIR\LICENSE" + Delete "$INSTDIR\README.md" + Delete "$INSTDIR\compiler_hint.lua" + + RMDir /r "$INSTDIR\templates" + RMDir /r "$INSTDIR\mingw32" + RMDir /r "$INSTDIR\mingw64" + RMDir /r "$INSTDIR\llvm-mingw" + + RMDir "$INSTDIR" + + ; Remove registry keys +!ifdef USER_MODE + DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++" + DeleteRegKey HKCU "Software\RedPanda-C++" +!else + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++" + DeleteRegKey HKLM "Software\RedPanda-C++" +!endif + +!ifdef USER_MODE + MessageBox MB_YESNO "$(MessageRemoveConfig)" /SD IDNO IDNO SkipRemoveConfig + RMDir /r "$APPDATA\RedPandaIDE" +SkipRemoveConfig: +!endif +SectionEnd diff --git a/platform/windows/qt.conf b/platform/windows/qt.conf new file mode 100644 index 00000000..c58673ac --- /dev/null +++ b/platform/windows/qt.conf @@ -0,0 +1,2 @@ +[Platforms] +WindowsArguments = darkmode=2,fontengine=freetype diff --git a/platform/windows/utf8/utf8init.cpp b/platform/windows/utf8/utf8init.cpp new file mode 100644 index 00000000..e5c63b8e --- /dev/null +++ b/platform/windows/utf8/utf8init.cpp @@ -0,0 +1,25 @@ +extern "C" { + unsigned __stdcall GetConsoleCP() noexcept; + unsigned __stdcall GetConsoleOutputCP() noexcept; + int __stdcall SetConsoleCP(unsigned) noexcept; + int __stdcall SetConsoleOutputCP(unsigned) noexcept; +} + +constexpr int CP_UTF8 = 65001; + +struct __utf8init_t { + unsigned saved_input_cp; + unsigned saved_output_cp; + + __utf8init_t() noexcept { + saved_input_cp = GetConsoleCP(); + saved_output_cp = GetConsoleOutputCP(); + SetConsoleCP(CP_UTF8); + SetConsoleOutputCP(CP_UTF8); + } + + ~__utf8init_t() noexcept { + SetConsoleCP(saved_input_cp); + SetConsoleOutputCP(saved_output_cp); + } +} __utf8init; diff --git a/platform/windows/utf8/utf8manifest.rc b/platform/windows/utf8/utf8manifest.rc new file mode 100644 index 00000000..37d002bc --- /dev/null +++ b/platform/windows/utf8/utf8manifest.rc @@ -0,0 +1,12 @@ +1 24 MOVEABLE PURE DISCARDABLE +BEGIN + "" + "" + "" + "" + "" + "UTF-8" + "" + "" + "" +END