Add compiler hint interface for packager (#175)

* add compiler hint addon interface

* add architecture info in about dialog

* detect user install

* add qmake variable to control OpenConsole.exe preference

* enable asan/hwasan option on all platforms for cross toolchain

* fix lldb-server

* force to lldb-server when using lldb-mi

* add qt.conf for windows

* add windows domain installer with compiler hint

* add compiler hint for arch linux

* fix mainwindow actionInterrupt visibility

* update news

* update arch linux packaging

* update windows domain packaging

* allow parallel packaging in windows domain installer

* fix compiler set persistence in compiler hint interface
This commit is contained in:
Cyano Hao 2024-01-18 16:14:36 +08:00 committed by GitHub
parent 4c58ebf0ba
commit e2e5c46820
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 2408 additions and 114 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
/.vs
/.vscode
/assets
/dist
/packages/appimage/*/*.AppImage
*.bak

View File

@ -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.

View File

@ -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++。

View File

@ -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

View File

@ -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

View File

@ -20,13 +20,37 @@
#include <QApplication>
#include <QPalette>
#include <QStyleFactory>
#include <QJsonValue>
#ifdef Q_OS_WINDOWS
# include <windows.h>
#endif
#include <lua/lua.hpp>
#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<QString> arches;
pGetMachineTypeAttributes_t pGetMachineTypeAttributes =
reinterpret_cast<pGetMachineTypeAttributes_t>(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<pIsWow64GuestMachineSupported_t>(GetProcAddress(
GetModuleHandleW(L"kernel32"), "IsWow64GuestMachineSupported"));
pIsWow64Process2_t pIsWow64Process2 =
reinterpret_cast<pIsWow64Process2_t>(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<QString> 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;
}

View File

@ -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

View File

@ -37,7 +37,29 @@ static QMap<QString, QMap<QString, lua_CFunction>> apiGroups{
{"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

View File

@ -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

View File

@ -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<QString, lua_CFunction> &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<lua_State *, LuaExtraState> RaiiLuaState::mExtraState;

View File

@ -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<QString, lua_CFunction> &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;

View File

@ -177,17 +177,13 @@ void CompilerInfo::prepareCompilerOptions()
sl.append(QPair<QString,QString>("Strong","-strong"));
sl.append(QPair<QString,QString>("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<QString,QString>("Address","address"));
# ifdef __aarch64__
sl.append(QPair<QString,QString>("Hardware-assisted Address","hwaddress"));
# endif
sl.append(QPair<QString,QString>("Hardware-assisted address (arm64 only)","hwaddress"));
sl.append(QPair<QString,QString>("Thread","thread"));
sl.append(QPair<QString,QString>("Leak","leak"));
sl.append(QPair<QString,QString>("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");

View File

@ -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 inferiors 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,7 +3074,16 @@ void DebugTarget::run()
mErrorOccured = false;
//find first available port
QStringList execArgs = QStringList{
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,

View File

@ -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;

View File

@ -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());

View File

@ -33,6 +33,10 @@
#ifdef Q_OS_LINUX
#include <sys/sysinfo.h>
#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 &param : set["customCompileParams"].toArray())
escapedCompileParams.append(escapeArgument(param.toString(), false));
mCustomCompileParams = escapedCompileParams.join(' ');
QStringList escapedLinkParams;
for (const QJsonValue &param : 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<QString, QString> 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<CompilerSet>(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<QString> 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
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

View File

@ -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);

View File

@ -12,8 +12,8 @@
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;h1 style=&quot; margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:xx-large; font-weight:600;&quot;&gt;Red Panda C++&lt;/span&gt;&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<source>Based on Qt %1 (%2)</source>
<translation>Com base em Qt %1 (%2)</translation>
<source>Based on Qt %1 (%2) rodando em %3</source>
<translation>Com base em Qt %1 (%2) executado em %3</translation>
</message>
<message>
<source>Build time: %1 %2</source>

View File

@ -62,8 +62,8 @@ p, li { white-space: pre-wrap; }
</message>
<message>
<location filename="../widgets/aboutdialog.ui" line="35"/>
<source>Based on Qt %1 (%2)</source>
<translation>Qt %1 (%2)</translation>
<source>Based on Qt %1 (%2) running on %3</source>
<translation> Qt %1 (%2) %3</translation>
</message>
<message>
<location filename="../widgets/aboutdialog.ui" line="47"/>

View File

@ -12,8 +12,8 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>Based on Qt %1 (%2)</source>
<translation type="unfinished"></translation>
<source>Based on Qt %1 (%2) running on %3</source>
<translation> Qt %1 (%2) %3</translation>
</message>
<message>
<source>Build time: %1 %2</source>

View File

@ -17,6 +17,12 @@
#include <windows.h>
#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;
}
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<pIsWow64Process2_t>(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
}

View File

@ -173,4 +173,7 @@ QString escapeArgument(const QString &arg, bool isFirstArg);
QString defaultShell();
QString appArch();
QString osArch();
#endif // UTILS_H

View File

@ -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__));

View File

@ -32,7 +32,7 @@
</font>
</property>
<property name="text">
<string>Based on Qt %1 (%2)</string>
<string>Based on Qt %1 (%2) running on %3</string>
</property>
</widget>
</item>

View File

@ -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

193
docs/addon.md Normal file
View File

@ -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. Its packagers 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.

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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() {
# MSYS2s `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

View File

@ -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

View File

@ -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 "删除原有配置文件"

View File

@ -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

2
platform/windows/qt.conf Normal file
View File

@ -0,0 +1,2 @@
[Platforms]
WindowsArguments = darkmode=2,fontengine=freetype

View File

@ -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;

View File

@ -0,0 +1,12 @@
1 24 MOVEABLE PURE DISCARDABLE
BEGIN
"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>"
"<assembly manifestVersion=""1.0"" xmlns=""urn:schemas-microsoft-com:asm.v1"">"
"<assemblyIdentity type=""win32"" name=""..."" version=""6.0.0.0""/>"
"<application>"
"<windowsSettings>"
"<activeCodePage xmlns=""http://schemas.microsoft.com/SMI/2019/WindowsSettings"">UTF-8</activeCodePage>"
"</windowsSettings>"
"</application>"
"</assembly>"
END