add Lua api version check and type defs (#183)

This commit is contained in:
Cyano Hao 2024-01-21 18:02:51 +08:00 committed by GitHub
parent 423f87763e
commit e2a23ed4ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 2886 additions and 1069 deletions

View File

@ -21,6 +21,7 @@
#include <QPalette> #include <QPalette>
#include <QStyleFactory> #include <QStyleFactory>
#include <QJsonValue> #include <QJsonValue>
#include <QMessageBox>
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
# include <windows.h> # include <windows.h>
@ -51,12 +52,127 @@ using pIsWow64Process2_t = BOOL (WINAPI *)(
); );
#endif #endif
// C_Debug.debug(string) -> () static QString luaDump(const QJsonValue &value) {
if (value.isNull())
return "nil";
if (value.isBool())
return value.toBool() ? "true" : "false";
if (value.isDouble())
return QString::number(value.toDouble());
if (value.isString()) {
QString s = value.toString();
s.replace('\\', "\\\\");
s.replace('"', "\\\"");
s.replace('\n', "\\n");
s.replace('\r', "\\r");
s.replace('\t', "\\t");
return '"' + s + '"';
}
if (value.isArray()) {
QString s = "{";
for (const QJsonValue &v : value.toArray())
s += luaDump(v) + ',';
s += '}';
return s;
}
if (value.isObject()) {
QJsonObject o = value.toObject();
QString s = "{";
for (const QString &k : o.keys())
s += '[' + luaDump(k) + "]=" + luaDump(o[k]) + ',';
s += '}';
return s;
}
return "nil";
}
static QString stringify(const QJsonValue &value) {
if (value.isString())
return value.toString();
if (value.isNull())
return "[nil]";
if (value.isBool())
return value.toBool() ? "[true]" : "[false]";
if (value.isDouble())
return QString("[number %1]").arg(value.toDouble());
if (value.isArray() || value.isObject())
return QString("[table %1]").arg(luaDump(value));
return "[unknown]";
}
/* https://stackoverflow.com/questions/15687223/explicit-call-to-destructor-before-longjmp-croak
*
* C++11 18.10/4: A setjmp/longjmp call pair has undefined behavior if
* replacing the setjmp and longjmp by catch and throw would invoke any
* non-trivial destructors for any automatic objects.
*
* Use impl function so the API boundary contains only POD types.
*/
void luaApiImpl_Debug_debug(lua_State *L) {
QString s = AddOn::RaiiLuaState::fetchString(L, 1);
int nArgs = lua_gettop(L);
for (int i = 2; i <= nArgs; ++i) {
QJsonValue arg;
try {
arg = AddOn::RaiiLuaState::fetch(L, i);
} catch (const AddOn::LuaError &e) {
QString reason = e.reason() + QString(" ('C_Debug.debug' argument #%1)").arg(i);
throw AddOn::LuaError(reason);
}
s = s.arg(stringify(arg));
}
qDebug().noquote() << s;
}
// C_Debug.debug(string, ...) -> ()
extern "C" int luaApi_Debug_debug(lua_State *L) noexcept { extern "C" int luaApi_Debug_debug(lua_State *L) noexcept {
QString info = AddOn::RaiiLuaState::fetchString(L, 1); bool error = false;
qDebug() << info; try {
luaApiImpl_Debug_debug(L);
} catch (const AddOn::LuaError &e) {
error = true;
AddOn::RaiiLuaState::push(L, e.reason());
}
if (error) {
lua_error(L); // longjmp, never returns
__builtin_unreachable();
} else {
return 0; return 0;
} }
}
void luaApiImpl_Debug_messageBox(lua_State *L) {
QString s = AddOn::RaiiLuaState::fetchString(L, 1);
int nArgs = lua_gettop(L);
for (int i = 2; i <= nArgs; ++i) {
QJsonValue arg;
try {
arg = AddOn::RaiiLuaState::fetch(L, i);
} catch (const AddOn::LuaError &e) {
QString reason = e.reason() + QString(" ('C_Debug.messageBox' argument #%1)").arg(i);
throw AddOn::LuaError(reason);
}
s = s.arg(stringify(arg));
}
QMessageBox::information(nullptr, "Debug", s);
}
// C_Debug.messageBox(string, ...) -> ()
extern "C" int luaApi_Debug_messageBox(lua_State *L) noexcept {
bool error = false;
try {
luaApiImpl_Debug_messageBox(L);
} catch (const AddOn::LuaError &e) {
error = true;
AddOn::RaiiLuaState::push(L, e.reason());
}
if (error) {
lua_error(L); // longjmp, never returns
__builtin_unreachable();
} else {
return 0;
}
}
// C_Desktop.desktopEnvironment() -> string // C_Desktop.desktopEnvironment() -> string
extern "C" int luaApi_Desktop_desktopEnvironment(lua_State *L) noexcept { extern "C" int luaApi_Desktop_desktopEnvironment(lua_State *L) noexcept {
@ -262,9 +378,7 @@ extern "C" int luaApi_System_supportedAppArchList(lua_State *L) noexcept {
} }
} }
// workaround for Debian 10 Qt 5.11, better to be QStringList result = arches.values();
// QStringList result{arches.begin(), arches.end()};
QStringList result = arches.toList();
AddOn::RaiiLuaState::push(L, result); AddOn::RaiiLuaState::push(L, result);
return 1; return 1;
#endif #endif
@ -291,15 +405,37 @@ extern "C" int luaApi_System_readRegistry(lua_State *L) noexcept
} }
#endif #endif
// C_Util.format(string, ...) -> string void luaApiImpl_Util_format(lua_State *L)
extern "C" int luaApi_Util_format(lua_State *L) noexcept
{ {
QString s = AddOn::RaiiLuaState::fetchString(L, 1); QString s = AddOn::RaiiLuaState::fetchString(L, 1);
int nArgs = lua_gettop(L); int nArgs = lua_gettop(L);
for (int i = 2; i <= nArgs; ++i) { for (int i = 2; i <= nArgs; ++i) {
QJsonValue arg = AddOn::RaiiLuaState::fetch(L, i); QJsonValue arg;
s = s.arg(arg.toString()); try {
arg = AddOn::RaiiLuaState::fetch(L, i);
} catch (const AddOn::LuaError &e) {
QString reason = e.reason() + QString(" ('C_Util.format' argument #%1)").arg(i);
throw AddOn::LuaError(reason);
}
s = s.arg(stringify(arg));
} }
AddOn::RaiiLuaState::push(L, s); AddOn::RaiiLuaState::push(L, s);
}
// C_Util.format(string, ...) -> string
extern "C" int luaApi_Util_format(lua_State *L) noexcept
{
bool error = false;
try {
luaApiImpl_Util_format(L);
} catch (const AddOn::LuaError &e) {
error = true;
AddOn::RaiiLuaState::push(L, e.reason());
}
if (error) {
lua_error(L); // longjmp, never returns
__builtin_unreachable();
} else {
return 1; return 1;
} }
}

View File

@ -20,6 +20,7 @@
struct lua_State; struct lua_State;
extern "C" int luaApi_Debug_debug(lua_State *L) noexcept; extern "C" int luaApi_Debug_debug(lua_State *L) noexcept;
extern "C" int luaApi_Debug_messageBox(lua_State *L) noexcept;
extern "C" int luaApi_Desktop_desktopEnvironment(lua_State *L) noexcept; extern "C" int luaApi_Desktop_desktopEnvironment(lua_State *L) noexcept;
extern "C" int luaApi_Desktop_language(lua_State *L) noexcept; extern "C" int luaApi_Desktop_language(lua_State *L) noexcept;

View File

@ -26,7 +26,8 @@ namespace AddOn {
static QMap<QString, QMap<QString, lua_CFunction>> apiGroups{ static QMap<QString, QMap<QString, lua_CFunction>> apiGroups{
{"C_Debug", {"C_Debug",
{ {
{"debug", &luaApi_Debug_debug}, // (string) -> () {"debug", &luaApi_Debug_debug}, // (string, ...) -> ()
{"messageBox", &luaApi_Debug_messageBox}, // (string, ...) -> ()
}}, }},
{"C_Desktop", {"C_Desktop",
{ {
@ -80,7 +81,11 @@ extern "C" void luaHook_timeoutKiller(lua_State *L, lua_Debug *ar [[maybe_unused
} }
}; };
ThemeExecutor::ThemeExecutor() : SimpleExecutor({"C_Debug", "C_Desktop"}) {} ThemeExecutor::ThemeExecutor() : SimpleExecutor(
"theme", 0, 1,
{"C_Debug", "C_Desktop", "C_Util"})
{
}
QJsonObject ThemeExecutor::operator()(const QByteArray &script, QJsonObject ThemeExecutor::operator()(const QByteArray &script,
const QString &name) { const QString &name) {
@ -92,28 +97,74 @@ QJsonObject ThemeExecutor::operator()(const QByteArray &script,
throw LuaError("Theme script must return an object."); throw LuaError("Theme script must return an object.");
} }
SimpleExecutor::SimpleExecutor(const QString &kind, int major, int minor, const QList<QString> &apis)
: mKind(kind), mMajor(major), mMinor(minor), mApis(apis)
{
}
bool SimpleExecutor::apiVersionCheck(const QJsonObject &addonApi) {
const QString &addonKind = addonApi["kind"].toString();
if (addonKind != mKind)
return false;
if (!addonApi.contains("major") || !addonApi.contains("minor"))
return false;
int addonMajor = addonApi["major"].toInt();
int addonMinor = addonApi["minor"].toInt();
if (mMajor == 0)
return addonMajor == mMajor && addonMinor == mMinor;
else
return addonMajor == mMajor && addonMinor <= mMinor;
}
QJsonValue SimpleExecutor::runScript(const QByteArray &script, QJsonValue SimpleExecutor::runScript(const QByteArray &script,
const QString &name, const QString &name,
std::chrono::microseconds timeLimit) { std::chrono::microseconds timeLimit) {
RaiiLuaState L(name, timeLimit); RaiiLuaState L(name, timeLimit);
L.openLibs();
for (auto &api : mApis)
registerApiGroup(L, api);
int retLoad = L.loadBuffer(script, name); int retLoad = L.loadBuffer(script, name);
if (retLoad != 0) if (retLoad != 0)
throw LuaError(QString("Lua script load error: %1.").arg(L.popString())); throw LuaError(QString("Lua load error: %1.").arg(L.popString()));
L.setHook(&luaHook_timeoutKiller, LUA_MASKCOUNT, 1'000'000); // ~5ms on early 2020s desktop CPUs L.setHook(&luaHook_timeoutKiller, LUA_MASKCOUNT, 1'000'000); // ~5ms on early 2020s desktop CPUs
L.setTimeStart(); L.setTimeStart();
int callResult = L.pCall(0, 1, 0); int callResult = L.pCall(0, 0, 0);
if (callResult != 0) { if (callResult != 0) {
throw LuaError(QString("Lua error: %1.").arg(L.popString())); throw LuaError(QString("Lua error: %1.").arg(L.popString()));
} }
// call `apiVersion()` to check compatibility
int type = L.getGlobal("apiVersion");
if (type != LUA_TFUNCTION) {
throw LuaError("Add-on interface error: `apiVersion` is not a function.");
}
callResult = L.pCall(0, 1, 0);
if (callResult != 0) {
throw LuaError(QString("Lua error: %1.").arg(L.popString()));
}
QJsonObject addonApi = L.popObject();
if (!apiVersionCheck(addonApi)) {
throw LuaError(QString("Add-on interface error: API version incompatible with %1:%2.%3.")
.arg(mKind).arg(mMajor).arg(mMinor));
}
// inject APIs and call `main()`
L.openLibs();
for (auto &api : mApis)
registerApiGroup(L, api);
type = L.getGlobal("main");
if (type != LUA_TFUNCTION) {
throw LuaError("Add-on interface error: `main` is not a function.");
}
callResult = L.pCall(0, 1, 0);
if (callResult != 0) {
throw LuaError(QString("Lua error: %1.").arg(L.popString()));
}
return L.fetch(1); return L.fetch(1);
} }
CompilerHintExecutor::CompilerHintExecutor() : SimpleExecutor({"C_Debug", "C_Desktop", "C_FileSystem", "C_System", "C_Util"}) {} CompilerHintExecutor::CompilerHintExecutor() : SimpleExecutor(
"compiler_hint", 0, 1,
{"C_Debug", "C_Desktop", "C_FileSystem", "C_System", "C_Util"})
{
}
QJsonObject CompilerHintExecutor::operator()(const QByteArray &script) { QJsonObject CompilerHintExecutor::operator()(const QByteArray &script) {
using namespace std::chrono_literals; using namespace std::chrono_literals;

View File

@ -27,13 +27,17 @@ namespace AddOn {
// simple, stateless Lua executor // simple, stateless Lua executor
class SimpleExecutor { class SimpleExecutor {
protected: protected:
SimpleExecutor(const QList<QString> &apis): mApis(apis) {} SimpleExecutor(const QString &kind, int major, int minor, const QList<QString> &apis);
bool apiVersionCheck(const QJsonObject &addonApi);
// run a Lua script and fetch its return value as type R
QJsonValue runScript(const QByteArray &script, const QString &name, QJsonValue runScript(const QByteArray &script, const QString &name,
std::chrono::microseconds timeLimit); std::chrono::microseconds timeLimit);
private: private:
QString mKind;
int mMajor;
int mMinor;
QStringList mApis; QStringList mApis;
}; };

View File

@ -68,6 +68,11 @@ QString RaiiLuaState::fetchString(int index)
return lua_tostring(mLua, index); return lua_tostring(mLua, index);
} }
QJsonObject RaiiLuaState::fetchObject(int index)
{
return fetchTableImpl(mLua, index, 0).toObject();
}
QJsonValue RaiiLuaState::fetch(int index) QJsonValue RaiiLuaState::fetch(int index)
{ {
return fetchValueImpl(mLua, index, 0); return fetchValueImpl(mLua, index, 0);
@ -93,6 +98,11 @@ QString RaiiLuaState::fetchString(lua_State *L, int index)
return lua_tostring(L, index); return lua_tostring(L, index);
} }
QJsonObject RaiiLuaState::fetchObject(lua_State *L, int index)
{
return fetchTableImpl(L, index, 0).toObject();
}
QJsonValue RaiiLuaState::fetch(lua_State *L, int index) QJsonValue RaiiLuaState::fetch(lua_State *L, int index)
{ {
return fetchValueImpl(L, index, 0); return fetchValueImpl(L, index, 0);
@ -112,6 +122,13 @@ QString RaiiLuaState::popString()
return value; return value;
} }
QJsonObject RaiiLuaState::popObject()
{
QJsonObject value = fetchObject(-1);
lua_pop(mLua, 1);
return value;
}
QJsonValue RaiiLuaState::pop() QJsonValue RaiiLuaState::pop()
{ {
QJsonValue value = fetch(-1); QJsonValue value = fetch(-1);
@ -191,6 +208,11 @@ int RaiiLuaState::pCall(int nargs, int nresults, int msgh)
return lua_pcall(mLua, nargs, nresults, msgh); return lua_pcall(mLua, nargs, nresults, msgh);
} }
int RaiiLuaState::getGlobal(const QString &name)
{
return lua_getglobal(mLua, name.toUtf8().constData());
}
void RaiiLuaState::setGlobal(const QString &name) void RaiiLuaState::setGlobal(const QString &name)
{ {
return lua_setglobal(mLua, name.toUtf8().constData()); return lua_setglobal(mLua, name.toUtf8().constData());
@ -230,7 +252,16 @@ QJsonValue RaiiLuaState::fetchTableImpl(lua_State *L, int index, int depth)
// here we take the fact that Lua iterates array part first // here we take the fact that Lua iterates array part first
bool processingArrayPart = true; bool processingArrayPart = true;
while (lua_next(L, newIndex)) { while (lua_next(L, newIndex)) {
QJsonValue v = pop(L); QJsonValue v;
try {
v = fetchValueImpl(L, -1, depth);
} catch (const LuaError &e) {
QString key = fetchString(L, -2);
QString reason = e.reason() + QString(" (at table key '%1')").arg(key);
lua_pop(L, 2);
throw LuaError(reason);
}
lua_pop(L, 1);
if (processingArrayPart && lua_isinteger(L, -1) && fetchInteger(L, -1) == arrayPart.size() + 1) if (processingArrayPart && lua_isinteger(L, -1) && fetchInteger(L, -1) == arrayPart.size() + 1)
// we are still in array part // we are still in array part
arrayPart.push_back(v); arrayPart.push_back(v);
@ -275,8 +306,11 @@ QJsonValue RaiiLuaState::fetchValueImpl(lua_State *L, int index, int depth)
return fetchString(L, index); return fetchString(L, index);
else if (lua_istable(L, index)) else if (lua_istable(L, index))
return fetchTableImpl(L, index, depth + 1); return fetchTableImpl(L, index, depth + 1);
else else {
throw LuaError(QString("Lua type error: unknown type %1.").arg(lua_typename(L, index))); int type = lua_type(L, index);
const char *name = lua_typename(L, type);
throw LuaError(QString("Lua type error: unknown type %1.").arg(name));
}
} }
QHash<lua_State *, LuaExtraState> RaiiLuaState::mExtraState; QHash<lua_State *, LuaExtraState> RaiiLuaState::mExtraState;

View File

@ -68,6 +68,7 @@ public:
static long long fetchInteger(lua_State *L, int index); static long long fetchInteger(lua_State *L, int index);
static double fetchNumber(lua_State *L, int index); static double fetchNumber(lua_State *L, int index);
static QString fetchString(lua_State *L, int index); static QString fetchString(lua_State *L, int index);
static QJsonObject fetchObject(lua_State *L, int index);
static QJsonValue fetch(lua_State *L, int index); static QJsonValue fetch(lua_State *L, int index);
bool popBoolean(); bool popBoolean();
@ -94,6 +95,7 @@ public:
int loadBuffer(const QByteArray &buff, const QString &name); int loadBuffer(const QByteArray &buff, const QString &name);
void openLibs(); void openLibs();
int pCall(int nargs, int nresults, int msgh); int pCall(int nargs, int nresults, int msgh);
int getGlobal(const QString &name);
void setGlobal(const QString &name); void setGlobal(const QString &name);
void setHook(lua_Hook f, int mask, int count); void setHook(lua_Hook f, int mask, int count);

View File

@ -3279,7 +3279,9 @@ void Settings::CompilerSets::findSets()
try { try {
compilerHint = AddOn::CompilerHintExecutor{}(script); compilerHint = AddOn::CompilerHintExecutor{}(script);
} catch (const AddOn::LuaError &e) { } catch (const AddOn::LuaError &e) {
qDebug() << "Error in compiler_hint.lua:" << e.reason(); QMessageBox::critical(nullptr,
QObject::tr("Error executing platform compiler hint add-on"),
e.reason());
} }
if (!compilerHint.empty()) { if (!compilerHint.empty()) {
QJsonArray compilerList = compilerHint["compilerList"].toArray(); QJsonArray compilerList = compilerHint["compilerList"].toArray();

View File

@ -1,10 +1,19 @@
function apiVersion()
return {
kind = "theme",
major = 0,
minor = 1,
}
end
local nameMap = { local nameMap = {
en_US = "Contrast", en_US = "Contrast",
pt_BR = "Contraste", pt_BR = "Contraste",
zh_CN = "高对比度", zh_CN = "高对比度",
zh_TW = "高對比度" zh_TW = "高對比度",
} }
function main()
local lang = C_Desktop.language() local lang = C_Desktop.language()
return { return {
@ -38,6 +47,7 @@ return {
PaletteTextDisabled = "#9DA9B5", PaletteTextDisabled = "#9DA9B5",
PaletteMid = "#FFFFFF", PaletteMid = "#FFFFFF",
PaletteLight = "#505050", PaletteLight = "#505050",
PaletteMidLight = "#00ff00" PaletteMidlight = "#00ff00",
} },
} }
end

View File

@ -1,10 +1,19 @@
function apiVersion()
return {
kind = "theme",
major = 0,
minor = 1,
}
end
local nameMap = { local nameMap = {
en_US = "Dark", en_US = "Dark",
pt_BR = "Escura", pt_BR = "Escura",
zh_CN = "深色", zh_CN = "深色",
zh_TW = "深色" zh_TW = "深色",
} }
function main()
local lang = C_Desktop.language() local lang = C_Desktop.language()
return { return {
@ -38,6 +47,7 @@ return {
PaletteTextDisabled = "#9DA9B5", PaletteTextDisabled = "#9DA9B5",
PaletteMid = "#707070", PaletteMid = "#707070",
PaletteLight = "#505050", PaletteLight = "#505050",
PaletteMidLight = "#00ff00" PaletteMidlight = "#00ff00",
} },
} }
end

View File

@ -1,10 +1,19 @@
function apiVersion()
return {
kind = "theme",
major = 0,
minor = 1,
}
end
local nameMap = { local nameMap = {
en_US = "Light", en_US = "Light",
pt_BR = "Clara", pt_BR = "Clara",
zh_CN = "浅色", zh_CN = "浅色",
zh_TW = "淺色" zh_TW = "淺色",
} }
function main()
local lang = C_Desktop.language() local lang = C_Desktop.language()
return { return {
@ -26,7 +35,7 @@ return {
PaletteLink = "#0000ff", PaletteLink = "#0000ff",
PaletteLinkVisited = "#ff00ff", PaletteLinkVisited = "#ff00ff",
PaletteLight = "#ffffff", PaletteLight = "#ffffff",
PaletteMidLight = "#cacaca", PaletteMidlight = "#cacaca",
PaletteDark = "#9f9f9f", PaletteDark = "#9f9f9f",
PaletteMid = "#b8b8b8", PaletteMid = "#b8b8b8",
PaletteWindowDisabled = "#efefef", PaletteWindowDisabled = "#efefef",
@ -36,6 +45,7 @@ return {
PaletteButtonDisabled = "#efefef", PaletteButtonDisabled = "#efefef",
PaletteButtonTextDisabled = "#bebebe", PaletteButtonTextDisabled = "#bebebe",
PaletteHighlight = "#dddddd", PaletteHighlight = "#dddddd",
PaletteHighlightedText = "#000000" PaletteHighlightedText = "#000000",
} },
} }
end

View File

@ -1,10 +1,19 @@
function apiVersion()
return {
kind = "theme",
major = 0,
minor = 1,
}
end
local nameMap = { local nameMap = {
en_US = "MoLo", en_US = "MoLo",
pt_BR = "Molo", pt_BR = "Molo",
zh_CN = "墨落", zh_CN = "墨落",
zh_TW = "墨落" zh_TW = "墨落",
} }
function main()
local lang = C_Desktop.language() local lang = C_Desktop.language()
return { return {
@ -38,6 +47,7 @@ return {
PaletteTextDisabled = "#9DA9B5", PaletteTextDisabled = "#9DA9B5",
PaletteMid = "#FFFFFF", PaletteMid = "#FFFFFF",
PaletteLight = "#505050", PaletteLight = "#505050",
PaletteMidLight = "#00ff00" PaletteMidlight = "#00ff00",
} },
} }
end

View File

@ -1,16 +1,44 @@
function rgbFromString(color) function apiVersion()
return {
kind = "theme",
major = 0,
minor = 1,
}
end
local function rgbFromString(color)
local r, g, b = color:match("#(%x%x)(%x%x)(%x%x)") local r, g, b = color:match("#(%x%x)(%x%x)(%x%x)")
return {tonumber(r, 16) / 255, tonumber(g, 16) / 255, tonumber(b, 16) / 255} return {
r = tonumber(r, 16) / 255,
g = tonumber(g, 16) / 255,
b = tonumber(b, 16) / 255,
}
end end
function rgbToString(rgb) local function rgbToString(rgb)
return string.format("#%02x%02x%02x", return string.format(
math.floor(rgb[1] * 255), math.floor(rgb[2] * 255), math.floor(rgb[3] * 255) "#%02x%02x%02x",
) math.floor(rgb.r * 255),
math.floor(rgb.g * 255),
math.floor(rgb.b * 255))
end end
function hsvToRgb(h, s, v) local function hsvToRgb(hsv)
local r, g, b local r, g, b
local h, s, v = hsv.h, hsv.s, hsv.v
local i = math.floor(h * 6) local i = math.floor(h * 6)
local f = h * 6 - i local f = h * 6 - i
local p = v * (1 - s) local p = v * (1 - s)
@ -30,29 +58,26 @@ function hsvToRgb(h, s, v)
elseif i == 5 then elseif i == 5 then
r, g, b = v, p, q r, g, b = v, p, q
end end
return {r, g, b} return { r = r, g = g, b = b }
end end
function blend(lower, upper, alpha) local function blend(lower, upper, alpha)
local r = (1 - alpha) * lower[1] + alpha * upper[1] local r = (1 - alpha) * lower.r + alpha * upper.r
local g = (1 - alpha) * lower[2] + alpha * upper[2] local g = (1 - alpha) * lower.g + alpha * upper.g
local b = (1 - alpha) * lower[3] + alpha * upper[3] local b = (1 - alpha) * lower.b + alpha * upper.b
return {r, g, b} return { r = r, g = g, b = b }
end end
local hue = math.random() local function transform(color, upperColor)
local upperColor = hsvToRgb(hue, 0.6, 1)
function transform(color)
local lowerColor = rgbFromString(color) local lowerColor = rgbFromString(color)
local blended = blend(lowerColor, upperColor, 0.1) local blended = blend(lowerColor, upperColor, 0.1)
return rgbToString(blended) return rgbToString(blended)
end end
function transformPalette(palette) local function transformPalette(palette, upperColor)
local transformed = {} local transformed = {}
for key, value in pairs(palette) do for key, value in pairs(palette) do
transformed[key] = transform(value) transformed[key] = transform(value, upperColor)
end end
return transformed return transformed
end end
@ -71,7 +96,7 @@ local originalPalette = {
PaletteLink = "#0000ff", PaletteLink = "#0000ff",
PaletteLinkVisited = "#ff00ff", PaletteLinkVisited = "#ff00ff",
PaletteLight = "#ffffff", PaletteLight = "#ffffff",
PaletteMidLight = "#cacaca", PaletteMidlight = "#cacaca",
PaletteDark = "#9f9f9f", PaletteDark = "#9f9f9f",
PaletteMid = "#b8b8b8", PaletteMid = "#b8b8b8",
PaletteWindowDisabled = "#efefef", PaletteWindowDisabled = "#efefef",
@ -81,16 +106,20 @@ local originalPalette = {
PaletteButtonDisabled = "#efefef", PaletteButtonDisabled = "#efefef",
PaletteButtonTextDisabled = "#bebebe", PaletteButtonTextDisabled = "#bebebe",
PaletteHighlight = "#dddddd", PaletteHighlight = "#dddddd",
PaletteHighlightedText = "#000000" PaletteHighlightedText = "#000000",
} }
local nameMap = { local nameMap = {
en_US = "Random Light", en_US = "Random Light",
pt_BR = "Clara aleatória", pt_BR = "Clara aleatória",
zh_CN = "随机浅色", zh_CN = "随机浅色",
zh_TW = "隨機淺色" zh_TW = "隨機淺色",
} }
function main()
local hue = math.random()
local upperColor = hsvToRgb({ h = hue, s = 0.6, v = 1 })
local lang = C_Desktop.language() local lang = C_Desktop.language()
return { return {
@ -98,5 +127,6 @@ return {
["style"] = "RedPandaLightFusion", ["style"] = "RedPandaLightFusion",
["default scheme"] = "Adaptive", ["default scheme"] = "Adaptive",
["default iconset"] = "newlook", ["default iconset"] = "newlook",
["palette"] = transformPalette(originalPalette) ["palette"] = transformPalette(originalPalette, upperColor),
} }
end

View File

@ -1,10 +1,33 @@
function apiVersion()
return {
kind = "theme",
major = 0,
minor = 1,
}
end
local nameMap = {
en_US = "System Style and Color",
pt_BR = "Estilo e Cor do Sistema",
zh_CN = "跟随系统样式和颜色",
zh_TW = "跟隨系統樣式和顏色",
}
local nameMapNoStyle = {
en_US = "System Color",
pt_BR = "Cor do Sistema",
zh_CN = "跟随系统颜色",
zh_TW = "跟隨系統顏色",
}
function main()
local desktopEnvironment = C_Desktop.desktopEnvironment() local desktopEnvironment = C_Desktop.desktopEnvironment()
local useSystemStyle = desktopEnvironment == "xdg" or desktopEnvironment == "macos" local useSystemStyle = desktopEnvironment == "xdg" or desktopEnvironment == "macos"
local systemAppMode = C_Desktop.systemAppMode() local systemAppMode = C_Desktop.systemAppMode()
local isDarkMode = systemAppMode == "dark" local isDarkMode = systemAppMode == "dark"
function getStyle() local function getStyle()
if useSystemStyle then if useSystemStyle then
return C_Desktop.systemStyle() return C_Desktop.systemStyle()
else else
@ -16,11 +39,11 @@ function getStyle()
end end
end end
function getPalette() local function getPalette()
if useSystemStyle then if useSystemStyle then
return {} return {}
elseif isDarkMode then elseif isDarkMode then
return { -- palette from `dark.lua` return {
PaletteWindow = "#19232D", PaletteWindow = "#19232D",
PaletteWindowText = "#E0E1E3", PaletteWindowText = "#E0E1E3",
PaletteBase = "#1E1E1E", PaletteBase = "#1E1E1E",
@ -46,10 +69,10 @@ function getPalette()
PaletteTextDisabled = "#9DA9B5", PaletteTextDisabled = "#9DA9B5",
PaletteMid = "#707070", PaletteMid = "#707070",
PaletteLight = "#505050", PaletteLight = "#505050",
PaletteMidLight = "#00ff00" PaletteMidlight = "#00ff00",
} }
else else
return { -- palette from `default.lua` return {
PaletteWindow = "#efefef", PaletteWindow = "#efefef",
PaletteWindowText = "#000000", PaletteWindowText = "#000000",
PaletteBase = "#ffffff", PaletteBase = "#ffffff",
@ -63,7 +86,7 @@ function getPalette()
PaletteLink = "#0000ff", PaletteLink = "#0000ff",
PaletteLinkVisited = "#ff00ff", PaletteLinkVisited = "#ff00ff",
PaletteLight = "#ffffff", PaletteLight = "#ffffff",
PaletteMidLight = "#cacaca", PaletteMidlight = "#cacaca",
PaletteDark = "#9f9f9f", PaletteDark = "#9f9f9f",
PaletteMid = "#b8b8b8", PaletteMid = "#b8b8b8",
PaletteWindowDisabled = "#efefef", PaletteWindowDisabled = "#efefef",
@ -73,25 +96,11 @@ function getPalette()
PaletteButtonDisabled = "#efefef", PaletteButtonDisabled = "#efefef",
PaletteButtonTextDisabled = "#bebebe", PaletteButtonTextDisabled = "#bebebe",
PaletteHighlight = "#dddddd", PaletteHighlight = "#dddddd",
PaletteHighlightedText = "#000000" PaletteHighlightedText = "#000000",
} }
end end
end end
local nameMap = {
en_US = "System Style and Color",
pt_BR = "Estilo e Cor do Sistema",
zh_CN = "跟随系统样式和颜色",
zh_TW = "跟隨系統樣式和顏色"
}
local nameMapNoStyle = {
en_US = "System Color",
pt_BR = "Cor do Sistema",
zh_CN = "跟随系统颜色",
zh_TW = "跟隨系統顏色"
}
local lang = C_Desktop.language() local lang = C_Desktop.language()
return { return {
@ -99,5 +108,6 @@ return {
["style"] = getStyle(), ["style"] = getStyle(),
["default scheme"] = "Adaptive", ["default scheme"] = "Adaptive",
["default iconset"] = "newlook", ["default iconset"] = "newlook",
["palette"] = getPalette() ["palette"] = getPalette(),
} }
end

View File

@ -7110,6 +7110,10 @@
<source>Failed to detect terminal arguments pattern for %1.</source> <source>Failed to detect terminal arguments pattern for %1.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Error executing platform compiler hint add-on</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>RegisterModel</name> <name>RegisterModel</name>

View File

@ -9679,6 +9679,10 @@ p, li { white-space: pre-wrap; }
<source>Failed to detect terminal arguments pattern for %1.</source> <source>Failed to detect terminal arguments pattern for %1.</source>
<translation> %1 </translation> <translation> %1 </translation>
</message> </message>
<message>
<source>Error executing platform compiler hint add-on</source>
<translation></translation>
</message>
</context> </context>
<context> <context>
<name>RegisterModel</name> <name>RegisterModel</name>

View File

@ -6622,6 +6622,10 @@
<source>Failed to detect terminal arguments pattern for %1.</source> <source>Failed to detect terminal arguments pattern for %1.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Error executing platform compiler hint add-on</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>RegisterModel</name> <name>RegisterModel</name>

View File

@ -176,4 +176,8 @@ QString defaultShell();
QString appArch(); QString appArch();
QString osArch(); QString osArch();
#ifdef _MSC_VER
#define __builtin_unreachable() (__assume(0))
#endif
#endif // UTILS_H #endif // UTILS_H

View File

@ -0,0 +1,313 @@
global function apiVersion(): ApiVersion
return {
kind = "compiler_hint",
major = 0,
minor = 1,
}
end
local nameMap: {string:{string:string}} = {
systemGcc = {
en_US = "System GCC",
pt_BR = "GCC do sistema",
zh_CN = "系统 GCC",
zh_TW = "系統 GCC",
},
systemClang = {
en_US = "System Clang",
pt_BR = "Clang do sistema",
zh_CN = "系统 Clang",
zh_TW = "系統 Clang",
},
multilibGcc = {
en_US = "Multilib GCC",
pt_BR = "GCC multilib",
zh_CN = "Multilib GCC",
zh_TW = "Multilib GCC",
},
multilibClang = {
en_US = "Multilib Clang",
pt_BR = "Clang multilib",
zh_CN = "Multilib Clang",
zh_TW = "Multilib Clang",
},
crossGcc = {
en_US = "Cross GCC",
pt_BR = "GCC cruzado",
zh_CN = "交叉编译 GCC",
zh_TW = "交叉編譯 GCC",
},
mingwGcc = {
en_US = "MinGW GCC",
pt_BR = "GCC MinGW",
zh_CN = "MinGW GCC",
zh_TW = "MinGW GCC",
},
mingwClang = {
en_US = "MinGW Clang",
pt_BR = "Clang MinGW",
zh_CN = "MinGW Clang",
zh_TW = "MinGW Clang",
},
release = {
en_US = ", release",
pt_BR = ", lançamento",
zh_CN = ",发布",
zh_TW = ",發佈",
},
debug = {
en_US = ", debug",
pt_BR = ", depuração",
zh_CN = ",调试",
zh_TW = ",偵錯",
},
debugWithAsan = {
en_US = ", debug with ASan",
pt_BR = ", depuração com ASan",
zh_CN = "ASan 调试",
zh_TW = "ASan 偵錯",
},
}
local function mergeCompilerSet(compilerSet: CompilerHint.CompilerSet, other: CompilerHint.CompilerSet)
local c = compilerSet as {string:any}
local o = other as {string:any}
for k, v in pairs(o) do
c[k] = v
end
end
local record Config
isMultilib: boolean | nil
isMingw: boolean | nil
isClang: boolean | nil
customCompileParams: {string} | nil
customLinkParams: {string} | nil
triplet: string | nil
end
local function generateConfig(
name: string, lang: string,
cCompiler: string, cxxCompiler: string,
config: Config
): CompilerHint.CompilerSet, CompilerHint.CompilerSet, CompilerHint.CompilerSet
local commonOptions: CompilerHint.CompilerSet = {
cCompiler = cCompiler,
cxxCompiler = cxxCompiler,
debugger = "/usr/bin/gdb",
debugServer = "/usr/bin/gdbserver",
make = "/usr/bin/make",
compilerType = config.isClang and "Clang" or "GCC_UTF8",
preprocessingSuffix = ".i",
compilationProperSuffix = ".s",
assemblingSuffix = ".o",
executableSuffix = config.isMingw and ".exe" or "",
compilationStage = 3,
ccCmdOptUsePipe = "on",
ccCmdOptWarningAll = "on",
ccCmdOptWarningExtra = "on",
ccCmdOptCheckIsoConformance = "on",
binDirs = {"/usr/bin"},
}
if config.isMultilib then
commonOptions.ccCmdOptPointerSize = "32"
end
if config.isMingw then
commonOptions.resourceCompiler = "/usr/bin/" .. config.triplet .. "-windres"
end
if config.customCompileParams then
commonOptions.customCompileParams = config.customCompileParams
end
if config.customLinkParams then
commonOptions.customLinkParams = config.customLinkParams
end
local release = {
name = name .. (nameMap.release[lang] or nameMap.release.en_US),
staticLink = true,
linkCmdOptStripExe = "on",
ccCmdOptOptimize = "2",
}
local debug_ = {
name = name .. (nameMap.debug[lang] or nameMap.debug.en_US),
ccCmdOptDebugInfo = "on",
}
local debugWithAsan = {
name = name .. (nameMap.debugWithAsan[lang] or nameMap.debugWithAsan.en_US),
ccCmdOptDebugInfo = "on",
ccCmdOptAddressSanitizer = "address",
}
mergeCompilerSet(release, commonOptions)
mergeCompilerSet(debug_, commonOptions)
mergeCompilerSet(debugWithAsan, commonOptions)
return release, debug_, debugWithAsan
end
global function main(): CompilerHint
local arch = C_System.osArch()
local libexecDir = C_System.appLibexecDir()
local lang = C_Desktop.language()
local compilerList = {}
do
local release, debug_, debugWithAsan = generateConfig(
nameMap.systemGcc[lang] or nameMap.systemGcc.en_US, lang,
"/usr/bin/gcc", "/usr/bin/g++",
{}
)
table.insert(compilerList, release)
table.insert(compilerList, debug_)
table.insert(compilerList, debugWithAsan)
end
if C_FileSystem.isExecutable("/usr/bin/clang") then
local release, debug_, debugWithAsan = generateConfig(
nameMap.systemClang[lang] or nameMap.systemClang.en_US, lang,
"/usr/bin/clang", "/usr/bin/clang++",
{isClang = true}
)
table.insert(compilerList, release)
table.insert(compilerList, debug_)
table.insert(compilerList, debugWithAsan)
end
-- with lib32-gcc-libs installed, system GCC and Clang can target 32-bit
if arch == "x86_64" and C_FileSystem.isExecutable("/usr/lib32/libstdc++.so") then
do
local release, debug_, debugWithAsan = generateConfig(
nameMap.multilibGcc[lang] or nameMap.multilibGcc.en_US, lang,
"/usr/bin/gcc", "/usr/bin/g++",
{isMultilib = true}
)
table.insert(compilerList, release)
table.insert(compilerList, debug_)
table.insert(compilerList, debugWithAsan)
end
if C_FileSystem.isExecutable("/usr/bin/clang") then
local release, debug_, debugWithAsan = generateConfig(
nameMap.multilibClang[lang] or nameMap.multilibClang.en_US, lang,
"/usr/bin/clang", "/usr/bin/clang++",
{isClang = true, isMultilib = true}
)
table.insert(compilerList, release)
table.insert(compilerList, debug_)
table.insert(compilerList, debugWithAsan)
end
end
-- cross GCC
if (
arch == "x86_64" and
C_FileSystem.exists("/proc/sys/fs/binfmt_misc/qemu-aarch64") and
C_FileSystem.isExecutable("/usr/bin/aarch64-linux-gnu-gcc")
) then
local release, _, _ = generateConfig(
(nameMap.crossGcc[lang] or nameMap.crossGcc.en_US) .. " aarch64", lang,
"/usr/bin/aarch64-linux-gnu-gcc", "/usr/bin/aarch64-linux-gnu-g++",
{}
)
table.insert(compilerList, release)
end
-- with wine or WSL init registered in binfmt_misc, Windows binaries can run seamlessly
if (
arch == "x86_64" and (
C_FileSystem.exists("/proc/sys/fs/binfmt_misc/DOSWin") or
C_FileSystem.exists("/proc/sys/fs/binfmt_misc/WSLInterop")
)
) then
if C_FileSystem.isExecutable("/usr/bin/x86_64-w64-mingw32-gcc") then
local extraObjects = {
utf8init = libexecDir .. "/x86_64-w64-mingw32/utf8init.o",
utf8manifest = libexecDir .. "/x86_64-w64-mingw32/utf8manifest.o",
}
do
local release, _, _ = generateConfig(
(nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " x86_64", lang,
"/usr/bin/x86_64-w64-mingw32-gcc", "/usr/bin/x86_64-w64-mingw32-g++",
{
isMingw = true,
triplet = "x86_64-w64-mingw32",
customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest},
}
)
table.insert(compilerList, release)
end
-- system Clang can target Windows
if C_FileSystem.isExecutable("/usr/bin/clang") then
local release, _, _ = generateConfig(
(nameMap.mingwClang[lang] or nameMap.mingwClang.en_US) .. " x86_64", lang,
"/usr/bin/clang", "/usr/bin/clang++",
{
isClang = true,
isMingw = true,
triplet = "x86_64-w64-mingw32",
customCompileParams = {"-target", "x86_64-w64-mingw32"},
customLinkParams = {
"-target", "x86_64-w64-mingw32",
extraObjects.utf8init, extraObjects.utf8manifest,
"-lstdc++", "-lwinpthread",
},
}
)
table.insert(compilerList, release)
end
end
if C_FileSystem.isExecutable("/usr/bin/i686-w64-mingw32-gcc") then
local extraObjects = {
utf8init = libexecDir .. "/i686-w64-mingw32/utf8init.o",
utf8manifest = libexecDir .. "/i686-w64-mingw32/utf8manifest.o",
}
do
local release, _, _ = generateConfig(
(nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " i686", lang,
"/usr/bin/i686-w64-mingw32-gcc", "/usr/bin/i686-w64-mingw32-g++",
{
isMingw = true,
triplet = "i686-w64-mingw32",
customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest},
}
)
table.insert(compilerList, release)
end
-- system Clang can target Windows
if C_FileSystem.isExecutable("/usr/bin/clang") then
local release, _, _ = generateConfig(
(nameMap.mingwClang[lang] or nameMap.mingwClang.en_US) .. " i686", lang,
"/usr/bin/clang", "/usr/bin/clang++",
{
isClang = true,
isMingw = true,
triplet = "i686-w64-mingw32",
customCompileParams = {"-target", "i686-w64-mingw32"},
customLinkParams = {
"-target", "i686-w64-mingw32",
extraObjects.utf8init, extraObjects.utf8manifest,
"-lstdc++", "-lwinpthread",
},
}
)
table.insert(compilerList, release)
end
end
end
local result = {
compilerList = compilerList,
noSearch = {
"/usr/bin",
"/opt/cuda/bin",
"/usr/lib/ccache/bin",
},
preferCompiler = 3, -- System GCC Debug with ASan
}
return result
end

View File

@ -0,0 +1,430 @@
global function apiVersion(): ApiVersion
return {
kind = "compiler_hint",
major = 0,
minor = 1,
}
end
local gnuArchMap: {string:string} = {
i386 = "i686",
x86_64 = "x86_64",
arm = "armv7",
arm64 = "aarch64",
}
local profileNameMap: {string:{string:string}} = {
release = {
en_US = "release",
pt_BR = "lançamento",
zh_CN = "发布",
zh_TW = "發佈",
},
debug = {
en_US = "debug",
pt_BR = "depuração",
zh_CN = "调试",
zh_TW = "偵錯",
},
debugWithAsan = {
en_US = "debug with ASan",
pt_BR = "depuração com ASan",
zh_CN = "ASan 调试",
zh_TW = "ASan 偵錯",
},
}
local function nameGeneratorMingwGcc(lang: string, arch: string, profile: string, isUtf8: boolean): string
local template: {string:string} = {
en_US = "MinGW GCC %1 in %2, %3",
pt_BR = "GCC MinGW %1 em %2, %3",
zh_CN = "%2 MinGW GCC %1%3",
zh_TW = "%2 MinGW GCC %1%3",
}
local systemCodePage: {string:string} = {
en_US = "system code page",
pt_Br = "página de código do sistema",
zh_CN = "系统代码页",
zh_TW = "系統代碼頁",
}
return C_Util.format(
template[lang] or template.en_US,
gnuArchMap[arch],
isUtf8 and "UTF-8" or systemCodePage[lang] or systemCodePage.en_US,
profileNameMap[profile][lang] or profileNameMap[profile].en_US
)
end
local function nameGeneratorClang(lang: string, arch: string, profile: string, isMingw: boolean): string
local template: {string:string} = {
en_US = "%1 Clang %2, %3",
pt_BR = "Clang %2 %1, %3",
zh_CN = "%1 Clang %2%3",
zh_TW = "%1 Clang %2%3",
}
local msvcCompatible: {string:string} = {
en_US = "MSVC-compatible",
pt_BR = "compatível com MSVC",
zh_CN = "兼容 MSVC 的",
zh_TW = "相容 MSVC 的",
}
return C_Util.format(
template[lang] or template.en_US,
isMingw and "LLVM-MinGW" or msvcCompatible[lang] or msvcCompatible.en_US,
gnuArchMap[arch],
profileNameMap[profile][lang] or profileNameMap[profile].en_US
)
end
local function mergeCompilerSet(compilerSet: CompilerHint.CompilerSet, other: CompilerHint.CompilerSet)
local c = compilerSet as {string:any}
local o = other as {string:any}
for k, v in pairs(o) do
c[k] = v
end
end
local record Programs
cCompiler: string
cxxCompiler: string
make: string
debugger: string
debugServer: string
resourceCompiler: string
binDirs: {string}
libDirs: {string} | nil
end
local record Config
arch: string
isAnsi: boolean | nil
isClang: boolean | nil
customCompileParams: {string} | nil
customLinkParams: {string} | nil
end
local function generateConfig(
nameGen: (function (arch: string, profile: string): string),
programs: Programs,
config: Config
): CompilerHint.CompilerSet, CompilerHint.CompilerSet, CompilerHint.CompilerSet
local commonOptions : CompilerHint.CompilerSet = {
cCompiler = programs.cCompiler,
cxxCompiler = programs.cxxCompiler,
debugger = programs.debugger,
debugServer = programs.debugServer,
make = programs.make,
resourceCompiler = programs.resourceCompiler,
binDirs = programs.binDirs,
compilerType = config.isClang and "Clang" or "GCC_UTF8",
preprocessingSuffix = ".i",
compilationProperSuffix = ".s",
assemblingSuffix = ".o",
executableSuffix = ".exe",
compilationStage = 3,
ccCmdOptUsePipe = "on",
ccCmdOptWarningAll = "on",
ccCmdOptWarningExtra = "on",
ccCmdOptCheckIsoConformance = "on",
}
if programs.libDirs then
commonOptions.libDirs = programs.libDirs
end
if config.isAnsi then
commonOptions.execCharset = "SYSTEM"
end
if config.customCompileParams then
commonOptions.customCompileParams = config.customCompileParams
end
if config.customLinkParams then
commonOptions.customLinkParams = config.customLinkParams
end
local release: CompilerHint.CompilerSet = {
name = nameGen(config.arch, "release"),
staticLink = true,
linkCmdOptStripExe = "on",
ccCmdOptOptimize = "2",
}
local debug_: CompilerHint.CompilerSet = {
name = nameGen(config.arch, "debug"),
ccCmdOptDebugInfo = "on",
}
local debugWithAsan: CompilerHint.CompilerSet = {
name = nameGen(config.arch, "debugWithAsan"),
ccCmdOptDebugInfo = "on",
ccCmdOptAddressSanitizer = "address",
}
mergeCompilerSet(release, commonOptions)
mergeCompilerSet(debug_, commonOptions)
mergeCompilerSet(debugWithAsan, commonOptions)
return release, debug_, debugWithAsan
end
global function main(): CompilerHint
local appArch = C_System.appArch()
local libexecDir = C_System.appLibexecDir()
local lang = C_Desktop.language()
local supportedAppArches = C_System.supportedAppArchList()
local compilerList = {}
local noSearch = {}
local preferCompiler = 0
local function checkAndAddMingw(arch: string)
local binDir: string
local libDir: string
local excludeBinDir: string
if arch == "i386" then
binDir = libexecDir .. "/mingw32/bin" -- must match case because Windows filesystem can be case sensitive
libDir = libexecDir .. "/mingw32/i686-w64-mingw32/lib"
excludeBinDir = libexecDir .. "/MinGW32/bin" -- workaround for path check
elseif arch == "x86_64" then
binDir = libexecDir .. "/mingw64/bin"
libDir = libexecDir .. "/mingw64/x86_64-w64-mingw32/lib"
excludeBinDir = libexecDir .. "/MinGW64/bin"
else
return
end
if not C_FileSystem.isExecutable(binDir .. "/gcc.exe") then
return
end
local programs: Programs = {
cCompiler = binDir .. "/gcc.exe",
cxxCompiler = binDir .. "/g++.exe",
make = binDir .. "/mingw32-make.exe",
debugger = binDir .. "/gdb.exe",
debugServer = binDir .. "/gdbserver.exe",
resourceCompiler = binDir .. "/windres.exe",
binDirs = {binDir},
}
local extraObjects = {
utf8init = libDir .. "/utf8init.o",
utf8manifest = libDir .. "/utf8manifest.o",
}
local release, debug_, debugWithAsan = generateConfig(
function (arch_: string, profile: string): string
return nameGeneratorMingwGcc(lang, arch_, profile, true)
end,
programs,
{
arch = arch,
customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest},
}
)
table.insert(compilerList, release)
table.insert(compilerList, debug_)
if preferCompiler == 0 then
preferCompiler = 2
end
release, debug_, debugWithAsan = generateConfig(
function (arch_: string, profile: string): string
return nameGeneratorMingwGcc(lang, arch_, profile, false)
end,
programs,
{
arch = arch,
isAnsi = true,
}
)
table.insert(compilerList, release)
table.insert(compilerList, debug_)
table.insert(noSearch, excludeBinDir)
end
local function checkAndAddClang()
if not C_FileSystem.isExecutable(libexecDir .. "/llvm-mingw/bin/clang.exe") then
return
end
local binDir = libexecDir .. "/llvm-mingw/bin"
local appTriplet = gnuArchMap[appArch] .. "-w64-mingw32"
do
-- appArch is always debuggable
local libDir = libexecDir .. "/llvm-mingw/" .. appTriplet .. "/lib"
local programs: Programs = {
cCompiler = binDir .. "/" .. appTriplet .. "-clang.exe",
cxxCompiler = binDir .. "/" .. appTriplet .. "-clang++.exe",
make = binDir .. "/mingw32-make.exe",
debugger = binDir .. "/lldb-mi.exe",
debugServer = binDir .. "/lldb-server.exe",
resourceCompiler = binDir .. "/" .. appTriplet .. "-windres.exe",
binDirs = {binDir},
}
local extraObjects = {
utf8init = libDir .. "/utf8init.o",
utf8manifest = libDir .. "/utf8manifest.o",
}
local release, debug_, debugWithAsan = generateConfig(
function (arch_: string, profile: string): string
return nameGeneratorClang(lang, arch_, profile, true)
end,
programs,
{
arch = appArch,
customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest},
isClang = true,
}
)
table.insert(compilerList, release)
table.insert(compilerList, debug_)
if appArch ~= "arm64" then
table.insert(compilerList, debugWithAsan)
if preferCompiler == 0 then
preferCompiler = 3
end
else
if preferCompiler == 0 then
preferCompiler = 2
end
end
end
for _, foreignArch in ipairs(supportedAppArches) do
if foreignArch ~= appArch then
local foreignTriplet = gnuArchMap[foreignArch] .. "-w64-mingw32"
local libDir = libexecDir .. "/llvm-mingw/" .. foreignTriplet .. "/lib"
local programs: Programs = {
cCompiler = binDir .. "/" .. foreignTriplet .. "-clang.exe",
cxxCompiler = binDir .. "/" .. foreignTriplet .. "-clang++.exe",
make = binDir .. "/mingw32-make.exe",
debugger = binDir .. "/lldb-mi.exe",
debugServer = binDir .. "/lldb-server.exe",
resourceCompiler = binDir .. "/" .. foreignTriplet .. "-windres.exe",
binDirs = {binDir},
}
local extraObjects = {
utf8init = libDir .. "/utf8init.o",
utf8manifest = libDir .. "/utf8manifest.o",
}
local release, _, _ = generateConfig(
function (arch_: string, profile: string): string
return nameGeneratorClang(lang, arch_, profile, true)
end,
programs,
{
arch = foreignArch,
customLinkParams = {extraObjects.utf8init, extraObjects.utf8manifest},
isClang = true,
}
)
table.insert(compilerList, release)
end
end
table.insert(noSearch, binDir)
local llvmOrgPath = C_System.readRegistry([[Software\LLVM\LLVM]], "") or C_System.readRegistry([[Software\Wow6432Node\LLVM\LLVM]], "")
if not llvmOrgPath then
return
end
local llvmOrgBinDir = llvmOrgPath .. "/bin"
do
local msvcTriplet = gnuArchMap[appArch] .. "-pc-windows-msvc"
local libDir = libexecDir .. "/llvm-mingw/" .. msvcTriplet .. "/lib"
local programs: Programs = {
cCompiler = llvmOrgBinDir .. "/clang.exe",
cxxCompiler = llvmOrgBinDir .. "/clang++.exe",
make = binDir .. "/mingw32-make.exe",
debugger = binDir .. "/lldb-mi.exe",
debugServer = binDir .. "/lldb-server.exe",
resourceCompiler = binDir .. "/" .. appTriplet .. "-windres.exe",
binDirs = {llvmOrgBinDir},
libDirs = {libDir},
}
local extraObjects = {
utf8init = libDir .. "/utf8init.o",
utf8manifest = libDir .. "/utf8manifest.o",
}
local release, debug_, _ = generateConfig(
function (arch: string, profile: string): string
return nameGeneratorClang(lang, arch, profile, false)
end,
programs,
{
arch = appArch,
customCompileParams = {
"-target", msvcTriplet,
"-fms-extensions",
"-fms-compatibility",
"-fdelayed-template-parsing",
},
customLinkParams = {
"-target", msvcTriplet,
extraObjects.utf8init, extraObjects.utf8manifest,
},
isClang = true,
}
)
table.insert(compilerList, release)
table.insert(compilerList, debug_)
end
for _, foreignArch in ipairs(supportedAppArches) do
if foreignArch ~= appArch then
local foreignTriplet = gnuArchMap[foreignArch] .. "-w64-mingw32"
local msvcTriplet = gnuArchMap[foreignArch] .. "-pc-windows-msvc"
local libDir = libexecDir .. "/llvm-mingw/" .. msvcTriplet .. "/lib"
local programs: Programs = {
cCompiler = llvmOrgBinDir .. "/clang.exe",
cxxCompiler = llvmOrgBinDir .. "/clang++.exe",
make = binDir .. "/mingw32-make.exe",
debugger = binDir .. "/lldb-mi.exe",
debugServer = binDir .. "/lldb-server.exe",
resourceCompiler = binDir .. "/" .. foreignTriplet .. "-windres.exe",
binDirs = {llvmOrgBinDir},
libDirs = {libDir},
}
local extraObjects = {
utf8init = libDir .. "/utf8init.o",
utf8manifest = libDir .. "/utf8manifest.o",
}
local release, _, _ = generateConfig(
function (arch: string, profile: string): string
return nameGeneratorClang(lang, arch, profile, false)
end,
programs,
{
arch = foreignArch,
customCompileParams = {
"-target", msvcTriplet,
"-fms-extensions",
"-fms-compatibility",
"-fdelayed-template-parsing",
},
customLinkParams = {
"-target", msvcTriplet,
extraObjects.utf8init, extraObjects.utf8manifest,
},
isClang = true,
}
)
table.insert(compilerList, release)
end
end
table.insert(noSearch, llvmOrgBinDir)
end
if appArch == "x86_64" then
checkAndAddMingw("x86_64")
checkAndAddClang()
elseif appArch == "arm64" then
checkAndAddClang()
else
checkAndAddMingw("i386")
checkAndAddClang()
end
local result = {
compilerList = compilerList,
noSearch = noSearch,
preferCompiler = preferCompiler,
}
return result
end

View File

@ -0,0 +1,107 @@
local env = require("defs.global_env")
global C_Debug = env.C_Debug
global C_Desktop = env.C_Desktop
global C_FileSystem = env.C_FileSystem
global C_System = env.C_System
global C_Util = env.C_Util
global record CompilerHint
-- found compiler sets
compilerList: {CompilerSet}
-- do not search in these directories anymore
noSearch: {string}
-- prefer compiler set index (in Lua, 1-based) in compilerList
-- 0 for no preference
preferCompiler: integer
record CompilerSet
name: string
-- internal
-- e.g. "x86_64-linux-gnu", "x86_64-w64-mingw32"
dumpMachine: string
-- e.g. "13.2.1", "17.0.6"
version: string
-- e.g. "TDM-GCC", "MinGW"
type: string
-- e.g. "x86_64", "aarch64"
target: string
compilerType: CompilerType
-- general
staticLink: boolean
-- automatically sets useCustomCompileParams
customCompileParams: {string}
-- automatically sets useCustomLinkParams
customLinkParams: {string}
-- automatically sets autoAddCharsetParams
execCharset: string
-- setting: code generation
ccCmdOptOptimize: string
ccCmdOptStd: string
cCmdOptStd: string
ccCmdOptInstruction: string
ccCmdOptPointerSize: string
ccCmdOptDebugInfo: string
ccCmdOptProfileInfo: string
ccCmdOptSyntaxOnly: string
-- setting: warnings
ccCmdOptInhibitAllWarning: string
ccCmdOptWarningAll: string
ccCmdOptWarningExtra: string
ccCmdOptCheckIsoConformance: string
ccCmdOptWarningAsError: string
ccCmdOptAbortOnError: string
ccCmdOptStackProtector: string
ccCmdOptAddressSanitizer: string
-- setting: linker
ccCmdOptUsePipe: string
linkCmdOptNoLinkStdlib: string
linkCmdOptNoConsole: string
linkCmdOptStripExe: string
-- directory
binDirs: {string}
cIncludeDirs: {string}
cxxIncludeDirs: {string}
libDirs: {string}
defaultLibDirs: {string}
defaultCIncludeDirs: {string}
defaultCxxIncludeDirs: {string}
-- program
cCompiler: string
cxxCompiler: string
make: string
debugger: string
debugServer: string
resourceCompiler: string
-- output
preprocessingSuffix: string
compilationProperSuffix: string
assemblingSuffix: string
executableSuffix: string
compilationStage: integer
enum CompilerType
"GCC"
"GCC_UTF8"
"Clang"
end
end
end

100
addon/defs/global_env.d.tl Normal file
View File

@ -0,0 +1,100 @@
global record ApiVersion
kind: Kind
major: integer
minor: integer
enum Kind
"theme"
"compiler_hint"
end
end
local record env
record C_Debug
-- print message to console, with Qt-style string format
debug: function (format: string, ...: any): nil
-- show message box, with Qt-style string format
messageBox: function (format: string, ...: any): nil
end
record C_Desktop
-- return desktoop environment name
desktopEnvironment: function (): DesktopEnvironment
-- return language code, e.g. "en_US", "zh_CN"
language: function (): string
-- return available Qt styles, e.g. {"breeze", "fusion", "windows"}
qtStyleList: function (): {string}
-- return system app mode, light or dark
systemAppMode: function (): AppMode
-- return default Qt style, e.g. "fusion"
systemStyle: function (): string
enum DesktopEnvironment
"windows" -- Windows Win32
"macos" -- macOS
"xdg" -- XDG-compliant desktop environment (e.g. GNOME, KDE Plasma)
"unknown" -- other desktops or non-desktop environments (e.g. Windows UWP, Android)
end
enum AppMode
"light"
"dark"
end
end
record C_FileSystem
-- return whether the path exists
exists: function (path: string): boolean
-- return whether the path is executable
isExecutable: function (path: string): boolean
end
record C_System
-- returns the architecture of Red Panda C++, name following `QSysInfo`
-- e.g. "i386", "x86_64", "arm", "arm64", "riscv64", "loongarch64"
-- though unsupported, MSVC arm64ec is handled correctly (returns "arm64ec")
appArch: function (): string
-- returns the directory of Red Panda C++
-- e.g. "/usr/bin", "C:/Program Files/RedPanda-Cpp", "C:/Users/中文/AppData/Local/RedPanda-CPP"
appDir: function (): string
-- returns the libexec directory of Red Panda C++
-- e.g. "/usr/libexec/RedPandaCPP", "C:/Program Files/RedPanda-Cpp"
appLibexecDir: function (): string
-- returns the resource directory of Red Panda C++
-- e.g. "/usr/share/RedPandaCPP", "C:/Program Files/RedPanda-Cpp"
appResourceDir: function (): string
-- returns the architecture of the OS, name following `QSysInfo`
-- e.g. "i386", "x86_64", "arm", "arm64"
-- Windows arm64 is handled correctly even if Red Panda C++ runs under emulation
osArch: function (): string
-- returns supported application architectures by OS, name following `QSysInfo`
-- e.g. {"i386", "x86_64", "arm64"}
-- Windows 10 1709 or later: accurate result
-- Legacy Windows: hardcoded result, i.e. "i386" is always included even though WoW64 is not available
-- macOS: accurate result supposed, but not tested
-- Linux: osArch + appArch + QEMU user mode emulation, no multilib detection
-- other (BSD): osArch + appArch, no multilib detection
supportedAppArchList: function (): {string}
-- read `subKey\name` from HKEY_CURRENT_USER and HKEY_LOCAL_MACHINE in order (Windows only)
readRegistry: function (subKey: string, name: string): string | nil
end
record C_Util
-- Qt-style string format, replace %1, %2, etc. with arguments
format: function (format: string, ...: any): string
end
end
return env

63
addon/defs/theme.d.tl Normal file
View File

@ -0,0 +1,63 @@
local env = require("defs.global_env")
global C_Debug = env.C_Debug
global C_Desktop = env.C_Desktop
global C_Util = env.C_Util
global record Theme
name: string
style: string
["default scheme"]: string
["default iconset"]: BuiltInIconSet
palette: Palette
enum BuiltInIconSet
"newlook"
"contrast"
"bluesky"
end
record Palette
PaletteWindow: string | nil
PaletteWindowText: string | nil
PaletteBase: string | nil
PaletteAlternateBase: string | nil
PaletteToolTipBase: string | nil
PaletteToolTipText: string | nil
PaletteText: string | nil
PaletteButton: string | nil
PaletteButtonText: string | nil
PaletteBrightText: string | nil
PaletteHighlight: string | nil
PaletteHighlightedText: string | nil
PaletteLink: string | nil
PaletteLinkVisited: string | nil
PaletteLight: string | nil
PaletteMidlight: string | nil
PaletteDark: string | nil
PaletteMid: string | nil
PaletteShadow: string | nil
PaletteWindowDisabled: string | nil
PaletteWindowTextDisabled: string | nil
PaletteBaseDisabled: string | nil
PaletteAlternateBaseDisabled: string | nil
PaletteToolTipBaseDisabled: string | nil
PaletteToolTipTextDisabled: string | nil
PaletteTextDisabled: string | nil
PaletteButtonDisabled: string | nil
PaletteButtonTextDisabled: string | nil
PaletteBrightTextDisabled: string | nil
PaletteHighlightDisabled: string | nil
PaletteHighlightedTextDisabled: string | nil
PaletteLinkDisabled: string | nil
PaletteLinkVisitedDisabled: string | nil
PaletteLightDisabled: string | nil
PaletteMidlightDisabled: string | nil
PaletteDarkDisabled: string | nil
PaletteMidDisabled: string | nil
PaletteShadowDisabled: string | nil
end
end

29
addon/gen.sh Executable file
View File

@ -0,0 +1,29 @@
#!/bin/bash
set -euo pipefail
function gen-theme() {
local file="$1"
local bn="$(basename $file)"
local out="RedPandaIDE/themes/${bn%.tl}.lua"
echo -e "\033[1;33mChecking $file\033[0m"
tl check --include-dir addon --global-env-def defs/theme --quiet "$file"
echo -e "\033[1;32mCompiling $file\033[0m"
tl gen --include-dir addon --global-env-def defs/theme --gen-compat off --gen-target 5.4 -o "$out" "$file"
}
for file in addon/theme/*.tl; do
gen-theme "$file"
done
function gen-compiler-hint() {
local file="$1"
local out="$2"
echo -e "\033[1;33mChecking $file\033[0m"
tl check --include-dir addon --global-env-def defs/compiler_hint --quiet "$file"
echo -e "\033[1;32mCompiling $file\033[0m"
tl gen --include-dir addon --global-env-def defs/compiler_hint --gen-compat off --gen-target 5.4 -o "$out" "$file"
}
gen-compiler-hint addon/compiler_hint/windows_domain.tl packages/msys/domain/compiler_hint.lua
gen-compiler-hint addon/compiler_hint/archlinux.tl packages/archlinux/compiler_hint.lua

53
addon/theme/contrast.tl Normal file
View File

@ -0,0 +1,53 @@
global function apiVersion(): ApiVersion
return {
kind = "theme",
major = 0,
minor = 1,
}
end
local nameMap: {string:string} = {
en_US = "Contrast",
pt_BR = "Contraste",
zh_CN = "高对比度",
zh_TW = "高對比度",
}
global function main(): Theme
local lang = C_Desktop.language()
return {
["name"] = nameMap[lang] or nameMap.en_US,
["style"] = "RedPandaDarkFusion",
["default scheme"] = "Twilight",
["default iconset"] = "contrast",
["palette"] = {
PaletteWindow = "#000000",
PaletteWindowText = "#FFFFFF",
PaletteBase = "#141414",
PaletteAlternateBase = "#191919",
PaletteButton = "#141414",
PaletteButtonDisabled = "#141414",
PaletteBrightText = "#ff0000",
PaletteText = "#FFFFFF",
PaletteButtonText = "#FFFFFF",
PaletteButtonTextDisabled = "#9DA9B5",
PaletteHighlight = "#aa1f75cc",
PaletteDark = "#232323",
PaletteHighlightedText = "#e7e7e7",
PaletteToolTipBase = "#66000000",
PaletteToolTipText = "#e7e7e7",
PaletteLink = "#007af4",
PaletteLinkVisited = "#a57aff",
PaletteWindowDisabled = "#0A0A0A",
PaletteWindowTextDisabled = "#9DA9B5",
PaletteHighlightDisabled = "#9DA9B5",
PaletteHighlightedTextDisabled = "#9DA9B5",
PaletteBaseDisabled = "#000000",
PaletteTextDisabled = "#9DA9B5",
PaletteMid = "#FFFFFF",
PaletteLight = "#505050",
PaletteMidlight = "#00ff00",
},
}
end

53
addon/theme/dark.tl Normal file
View File

@ -0,0 +1,53 @@
global function apiVersion(): ApiVersion
return {
kind = "theme",
major = 0,
minor = 1,
}
end
local nameMap: {string:string} = {
en_US = "Dark",
pt_BR = "Escura",
zh_CN = "深色",
zh_TW = "深色",
}
global function main(): Theme
local lang = C_Desktop.language()
return {
["name"] = nameMap[lang] or nameMap.en_US,
["style"] = "RedPandaDarkFusion",
["default scheme"] = "VS Code",
["default iconset"] = "contrast",
["palette"] = {
PaletteWindow = "#19232D",
PaletteWindowText = "#E0E1E3",
PaletteBase = "#1E1E1E",
PaletteAlternateBase = "#303030",
PaletteButton = "#19232D",
PaletteButtonDisabled = "#19232D",
PaletteBrightText = "#ff0000",
PaletteText = "#e7e7e7",
PaletteButtonText = "#d3d3d3",
PaletteButtonTextDisabled = "#9DA9B5",
PaletteHighlight = "#aa1f75cc",
PaletteDark = "#232323",
PaletteHighlightedText = "#e7e7e7",
PaletteToolTipBase = "#66000000",
PaletteToolTipText = "#e7e7e7",
PaletteLink = "#007af4",
PaletteLinkVisited = "#a57aff",
PaletteWindowDisabled = "#333333",
PaletteWindowTextDisabled = "#9DA9B5",
PaletteHighlightDisabled = "#26486B",
PaletteHighlightedTextDisabled = "#9DA9B5",
PaletteBaseDisabled = "#19232D",
PaletteTextDisabled = "#9DA9B5",
PaletteMid = "#707070",
PaletteLight = "#505050",
PaletteMidlight = "#00ff00",
},
}
end

51
addon/theme/default.tl Normal file
View File

@ -0,0 +1,51 @@
global function apiVersion(): ApiVersion
return {
kind = "theme",
major = 0,
minor = 1,
}
end
local nameMap: {string:string} = {
en_US = "Light",
pt_BR = "Clara",
zh_CN = "浅色",
zh_TW = "淺色",
}
global function main(): Theme
local lang = C_Desktop.language()
return {
["name"] = nameMap[lang] or nameMap.en_US,
["style"] = "RedPandaLightFusion",
["default scheme"] = "Intellij Classic",
["default iconset"] = "newlook",
["palette"] = {
PaletteWindow = "#efefef",
PaletteWindowText = "#000000",
PaletteBase = "#ffffff",
PaletteAlternateBase = "#f7f7f7",
PaletteToolTipBase = "#ffffdc",
PaletteToolTipText = "#000000",
PaletteText = "#000000",
PaletteButton = "#efefef",
PaletteButtonText = "#000000",
PaletteBrightText = "#ffffff",
PaletteLink = "#0000ff",
PaletteLinkVisited = "#ff00ff",
PaletteLight = "#ffffff",
PaletteMidlight = "#cacaca",
PaletteDark = "#9f9f9f",
PaletteMid = "#b8b8b8",
PaletteWindowDisabled = "#efefef",
PaletteWindowTextDisabled = "#bebebe",
PaletteBaseDisabled = "#efefef",
PaletteTextDisabled = "#bebebe",
PaletteButtonDisabled = "#efefef",
PaletteButtonTextDisabled = "#bebebe",
PaletteHighlight = "#dddddd",
PaletteHighlightedText = "#000000",
},
}
end

53
addon/theme/molo.tl Normal file
View File

@ -0,0 +1,53 @@
global function apiVersion(): ApiVersion
return {
kind = "theme",
major = 0,
minor = 1,
}
end
local nameMap: {string:string} = {
en_US = "MoLo",
pt_BR = "Molo",
zh_CN = "墨落",
zh_TW = "墨落",
}
global function main(): Theme
local lang = C_Desktop.language()
return {
["name"] = nameMap[lang] or nameMap.en_US,
["style"] = "RedPandaDarkFusion",
["default scheme"] = "MoLo CodeBlack",
["default iconset"] = "newlook",
["palette"] = {
PaletteWindow = "#000000",
PaletteWindowText = "#FFFFFF",
PaletteBase = "#141414",
PaletteAlternateBase = "#191919",
PaletteButton = "#141414",
PaletteButtonDisabled = "#141414",
PaletteBrightText = "#ff0000",
PaletteText = "#FFFFFF",
PaletteButtonText = "#FFFFFF",
PaletteButtonTextDisabled = "#9DA9B5",
PaletteHighlight = "#aa1f75cc",
PaletteDark = "#232323",
PaletteHighlightedText = "#e7e7e7",
PaletteToolTipBase = "#66000000",
PaletteToolTipText = "#e7e7e7",
PaletteLink = "#007af4",
PaletteLinkVisited = "#a57aff",
PaletteWindowDisabled = "#0A0A0A",
PaletteWindowTextDisabled = "#9DA9B5",
PaletteHighlightDisabled = "#9DA9B5",
PaletteHighlightedTextDisabled = "#9DA9B5",
PaletteBaseDisabled = "#000000",
PaletteTextDisabled = "#9DA9B5",
PaletteMid = "#FFFFFF",
PaletteLight = "#505050",
PaletteMidlight = "#00ff00",
},
}
end

132
addon/theme/random_light.tl Normal file
View File

@ -0,0 +1,132 @@
global function apiVersion(): ApiVersion
return {
kind = "theme",
major = 0,
minor = 1,
}
end
local record Rgb
r: number
g: number
b: number
end
local record Hsv
h: number
s: number
v: number
end
local function rgbFromString(color: string): Rgb
local r, g, b = color:match("#(%x%x)(%x%x)(%x%x)")
return {
r = tonumber(r, 16) / 255,
g = tonumber(g, 16) / 255,
b = tonumber(b, 16) / 255,
}
end
local function rgbToString(rgb: Rgb): string
return string.format(
"#%02x%02x%02x",
math.floor(rgb.r * 255),
math.floor(rgb.g * 255),
math.floor(rgb.b * 255)
)
end
local function hsvToRgb(hsv: Hsv): Rgb
local r, g, b: number, number, number
local h, s, v = hsv.h, hsv.s, hsv.v
local i = math.floor(h * 6)
local f = h * 6 - i
local p = v * (1 - s)
local q = v * (1 - f * s)
local t = v * (1 - (1 - f) * s)
i = i % 6
if i == 0 then
r, g, b = v, t, p
elseif i == 1 then
r, g, b = q, v, p
elseif i == 2 then
r, g, b = p, v, t
elseif i == 3 then
r, g, b = p, q, v
elseif i == 4 then
r, g, b = t, p, v
elseif i == 5 then
r, g, b = v, p, q
end
return {r = r, g = g, b = b}
end
local function blend(lower: Rgb, upper: Rgb, alpha: number): Rgb
local r = (1 - alpha) * lower.r + alpha * upper.r
local g = (1 - alpha) * lower.g + alpha * upper.g
local b = (1 - alpha) * lower.b + alpha * upper.b
return {r = r, g = g, b = b}
end
local function transform(color: string, upperColor: Rgb): string
local lowerColor = rgbFromString(color)
local blended = blend(lowerColor, upperColor, 0.1)
return rgbToString(blended)
end
local function transformPalette(palette: Theme.Palette, upperColor: Rgb): Theme.Palette
local transformed = {}
for key, value in pairs(palette as {string:string}) do
transformed[key] = transform(value, upperColor)
end
return transformed as Theme.Palette
end
local originalPalette: Theme.Palette = {
PaletteWindow = "#efefef",
PaletteWindowText = "#000000",
PaletteBase = "#ffffff",
PaletteAlternateBase = "#f7f7f7",
PaletteToolTipBase = "#ffffdc",
PaletteToolTipText = "#000000",
PaletteText = "#000000",
PaletteButton = "#efefef",
PaletteButtonText = "#000000",
PaletteBrightText = "#ffffff",
PaletteLink = "#0000ff",
PaletteLinkVisited = "#ff00ff",
PaletteLight = "#ffffff",
PaletteMidlight = "#cacaca",
PaletteDark = "#9f9f9f",
PaletteMid = "#b8b8b8",
PaletteWindowDisabled = "#efefef",
PaletteWindowTextDisabled = "#bebebe",
PaletteBaseDisabled = "#efefef",
PaletteTextDisabled = "#bebebe",
PaletteButtonDisabled = "#efefef",
PaletteButtonTextDisabled = "#bebebe",
PaletteHighlight = "#dddddd",
PaletteHighlightedText = "#000000",
}
local nameMap: {string:string} = {
en_US = "Random Light",
pt_BR = "Clara aleatória",
zh_CN = "随机浅色",
zh_TW = "隨機淺色",
}
global function main(): Theme
local hue = math.random()
local upperColor = hsvToRgb({h = hue, s = 0.6, v = 1})
local lang = C_Desktop.language()
return {
["name"] = nameMap[lang] or nameMap.en_US,
["style"] = "RedPandaLightFusion",
["default scheme"] = "Adaptive",
["default iconset"] = "newlook",
["palette"] = transformPalette(originalPalette, upperColor),
}
end

113
addon/theme/system.tl Normal file
View File

@ -0,0 +1,113 @@
global function apiVersion(): ApiVersion
return {
kind = "theme",
major = 0,
minor = 1,
}
end
local nameMap: {string:string} = {
en_US = "System Style and Color",
pt_BR = "Estilo e Cor do Sistema",
zh_CN = "跟随系统样式和颜色",
zh_TW = "跟隨系統樣式和顏色",
}
local nameMapNoStyle: {string:string} = {
en_US = "System Color",
pt_BR = "Cor do Sistema",
zh_CN = "跟随系统颜色",
zh_TW = "跟隨系統顏色",
}
global function main(): Theme
local desktopEnvironment = C_Desktop.desktopEnvironment()
local useSystemStyle = desktopEnvironment == "xdg" or desktopEnvironment == "macos"
local systemAppMode = C_Desktop.systemAppMode()
local isDarkMode = systemAppMode == "dark"
local function getStyle(): string
if useSystemStyle then
return C_Desktop.systemStyle()
else
if isDarkMode then
return "RedPandaDarkFusion"
else
return "RedPandaLightFusion"
end
end
end
local function getPalette(): Theme.Palette
if useSystemStyle then
return {}
elseif isDarkMode then
return { -- palette from `dark.lua`
PaletteWindow = "#19232D",
PaletteWindowText = "#E0E1E3",
PaletteBase = "#1E1E1E",
PaletteAlternateBase = "#303030",
PaletteButton = "#19232D",
PaletteButtonDisabled = "#19232D",
PaletteBrightText = "#ff0000",
PaletteText = "#e7e7e7",
PaletteButtonText = "#d3d3d3",
PaletteButtonTextDisabled = "#9DA9B5",
PaletteHighlight = "#aa1f75cc",
PaletteDark = "#232323",
PaletteHighlightedText = "#e7e7e7",
PaletteToolTipBase = "#66000000",
PaletteToolTipText = "#e7e7e7",
PaletteLink = "#007af4",
PaletteLinkVisited = "#a57aff",
PaletteWindowDisabled = "#333333",
PaletteWindowTextDisabled = "#9DA9B5",
PaletteHighlightDisabled = "#26486B",
PaletteHighlightedTextDisabled = "#9DA9B5",
PaletteBaseDisabled = "#19232D",
PaletteTextDisabled = "#9DA9B5",
PaletteMid = "#707070",
PaletteLight = "#505050",
PaletteMidlight = "#00ff00",
}
else
return { -- palette from `default.lua`
PaletteWindow = "#efefef",
PaletteWindowText = "#000000",
PaletteBase = "#ffffff",
PaletteAlternateBase = "#f7f7f7",
PaletteToolTipBase = "#ffffdc",
PaletteToolTipText = "#000000",
PaletteText = "#000000",
PaletteButton = "#efefef",
PaletteButtonText = "#000000",
PaletteBrightText = "#ffffff",
PaletteLink = "#0000ff",
PaletteLinkVisited = "#ff00ff",
PaletteLight = "#ffffff",
PaletteMidlight = "#cacaca",
PaletteDark = "#9f9f9f",
PaletteMid = "#b8b8b8",
PaletteWindowDisabled = "#efefef",
PaletteWindowTextDisabled = "#bebebe",
PaletteBaseDisabled = "#efefef",
PaletteTextDisabled = "#bebebe",
PaletteButtonDisabled = "#efefef",
PaletteButtonTextDisabled = "#bebebe",
PaletteHighlight = "#dddddd",
PaletteHighlightedText = "#000000",
}
end
end
local lang = C_Desktop.language()
return {
["name"] = useSystemStyle and (nameMap[lang] or nameMap.en_US) or (nameMapNoStyle[lang] or nameMapNoStyle.en_US),
["style"] = getStyle(),
["default scheme"] = "Adaptive",
["default iconset"] = "newlook",
["palette"] = getPalette()
}
end

View File

@ -14,10 +14,50 @@
- Red Panda C++ APIs exposed to add-on are organized by groups. - Red Panda C++ APIs exposed to add-on are organized by groups.
- Each group is a Lua table. - Each group is a Lua table.
- Each API is a function in the table. - Each API is a function in the table.
- Available API groups vary by add-on type. - Available API groups vary by add-on kind.
- Localization is handled by add-on. e.g. - See [`env.d.tl`](../addon/defs/global_env.d.tl) for API definitions.
- Add-on can be executed only if the API version is compatible.
- Localization is handled by add-on.
### API Version Check
Add-on must implement `apiVersion` that takes no argument and returns `ApiVersion` ([`env.d.tl`](../addon/defs/global_env.d.tl)).
Before execution, Red Panda C++ will call `apiVersion()` _without injecting any API group_[^1] to check whether the add-on is compatible with current Red Panda C++ (host).
[^1]: Thus do not call any API (incl. Lua standard library) in `apiVersion` and file scope.
Add-on is compatible with host if and only if:
```
(add-on kind = host kind) ∧ (
((add-on major = host major = 0) ∧ (add-on minor = host minor))
((add-on major = host major ≥ 1) ∧ (add-on minor ≤ host minor))
)
```
That is to say:
- API version is kind-specific.
- For a given kind, API major reported by add-on must be equal to host major.
- API major = 0 means unstable, minor updates may break backward compatibility.
- API major ≥ 1 means stable, minor updates keep backward compatibility.
### Types
Types in Red Panda C++ add-on interface are defined in [Teal](https://github.com/teal-language/tl) language, a typed dialect of Lua.
To make use of the type definitions, add-on can be written in Teal. To check and compile Teal add-on:
```bash
tl check --include-dir /path/to/RedPanda-CPP/addon --global-env-def defs/theme addon.tl
tl gen --include-dir /path/to/RedPanda-CPP/addon --global-env-def defs/theme --gen-compat off --gen-target 5.4 addon.tl
```
### Localization
Example:
```lua ```lua
local lang = C_Desktop.language() local lang = C_Desktop.language()
-- note: explicitly declare as `{string:string}` for Teal
local localizedName = { local localizedName = {
en_US = "System", en_US = "System",
pt_BR = "Sistema", pt_BR = "Sistema",
@ -32,10 +72,23 @@
## Simple Add-on ## Simple Add-on
A simple add-on is a Lua script that returns a single value. A simple add-on is a Lua script with a `main` function returning single value.
### Theme Add-on
Current API version: `theme:0.1`.
Available API groups:
- `C_Debug`
- `C_Desktop`
- `C_Util`
`main` function takes no argument and returns `Theme` ([`theme.d.tl`](../addon/defs/theme.d.tl)).
### Compiler Hint Add-on ### Compiler Hint Add-on
Current API version: `compiler_hint:0.1`.
If `$appLibexecDir/compiler_hint.lua` exists, it will be executed as compiler hint add-on when searching for compiler. If `$appLibexecDir/compiler_hint.lua` exists, it will be executed as compiler hint add-on when searching for compiler.
Available API groups: Available API groups:
@ -45,149 +98,4 @@ Available API groups:
- `C_System` - `C_System`
- `C_Util` - `C_Util`
Return value schema: `main` function takes no argument and returns `CompilerHint` ([`compiler_hint.d.tl`](../addon/defs/compiler_hint.d.tl)).
```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

@ -1,6 +1,10 @@
local arch = C_System.osArch() function apiVersion()
local libexecDir = C_System.appLibexecDir() return {
local lang = C_Desktop.language() kind = "compiler_hint",
major = 0,
minor = 1,
}
end
local nameMap = { local nameMap = {
systemGcc = { systemGcc = {
@ -65,7 +69,28 @@ local nameMap = {
}, },
} }
function generateConfig(name, cCompiler, cxxCompiler, config) local function mergeCompilerSet(compilerSet, other)
local c = compilerSet
local o = other
for k, v in pairs(o) do
c[k] = v
end
end
local function generateConfig(
name, lang,
cCompiler, cxxCompiler,
config)
local commonOptions = { local commonOptions = {
cCompiler = cCompiler, cCompiler = cCompiler,
cxxCompiler = cxxCompiler, cxxCompiler = cxxCompiler,
@ -102,7 +127,7 @@ function generateConfig(name, cCompiler, cxxCompiler, config)
linkCmdOptStripExe = "on", linkCmdOptStripExe = "on",
ccCmdOptOptimize = "2", ccCmdOptOptimize = "2",
} }
local debug = { local debug_ = {
name = name .. (nameMap.debug[lang] or nameMap.debug.en_US), name = name .. (nameMap.debug[lang] or nameMap.debug.en_US),
ccCmdOptDebugInfo = "on", ccCmdOptDebugInfo = "on",
} }
@ -111,100 +136,110 @@ function generateConfig(name, cCompiler, cxxCompiler, config)
ccCmdOptDebugInfo = "on", ccCmdOptDebugInfo = "on",
ccCmdOptAddressSanitizer = "address", ccCmdOptAddressSanitizer = "address",
} }
for k, v in pairs(commonOptions) do mergeCompilerSet(release, commonOptions)
release[k] = v mergeCompilerSet(debug_, commonOptions)
debug[k] = v mergeCompilerSet(debugWithAsan, commonOptions)
debugWithAsan[k] = v return release, debug_, debugWithAsan
end
return release, debug, debugWithAsan
end end
function main()
local arch = C_System.osArch()
local libexecDir = C_System.appLibexecDir()
local lang = C_Desktop.language()
local compilerList = {} local compilerList = {}
do do
local release, debug, debugWithAsan = generateConfig( local release, debug_, debugWithAsan = generateConfig(
nameMap.systemGcc[lang] or nameMap.systemGcc.en_US, nameMap.systemGcc[lang] or nameMap.systemGcc.en_US, lang,
"/usr/bin/gcc", "/usr/bin/g++", "/usr/bin/gcc", "/usr/bin/g++",
{} {})
)
table.insert(compilerList, release) table.insert(compilerList, release)
table.insert(compilerList, debug) table.insert(compilerList, debug_)
table.insert(compilerList, debugWithAsan) table.insert(compilerList, debugWithAsan)
end end
if C_FileSystem.isExecutable("/usr/bin/clang") then if C_FileSystem.isExecutable("/usr/bin/clang") then
local release, debug, debugWithAsan = generateConfig( local release, debug_, debugWithAsan = generateConfig(
nameMap.systemClang[lang] or nameMap.systemClang.en_US, nameMap.systemClang[lang] or nameMap.systemClang.en_US, lang,
"/usr/bin/clang", "/usr/bin/clang++", "/usr/bin/clang", "/usr/bin/clang++",
{isClang = true} { isClang = true })
)
table.insert(compilerList, release) table.insert(compilerList, release)
table.insert(compilerList, debug) table.insert(compilerList, debug_)
table.insert(compilerList, debugWithAsan) table.insert(compilerList, debugWithAsan)
end 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 if arch == "x86_64" and C_FileSystem.isExecutable("/usr/lib32/libstdc++.so") then
local release, debug, debugWithAsan = generateConfig( do
nameMap.multilibGcc[lang] or nameMap.multilibGcc.en_US, local release, debug_, debugWithAsan = generateConfig(
nameMap.multilibGcc[lang] or nameMap.multilibGcc.en_US, lang,
"/usr/bin/gcc", "/usr/bin/g++", "/usr/bin/gcc", "/usr/bin/g++",
{isMultilib = true} { isMultilib = true })
)
table.insert(compilerList, release) table.insert(compilerList, release)
table.insert(compilerList, debug) table.insert(compilerList, debug_)
table.insert(compilerList, debugWithAsan) table.insert(compilerList, debugWithAsan)
end
if C_FileSystem.isExecutable("/usr/bin/clang") then if C_FileSystem.isExecutable("/usr/bin/clang") then
local release, debug, debugWithAsan = generateConfig( local release, debug_, debugWithAsan = generateConfig(
nameMap.multilibClang[lang] or nameMap.multilibClang.en_US, nameMap.multilibClang[lang] or nameMap.multilibClang.en_US, lang,
"/usr/bin/clang", "/usr/bin/clang++", "/usr/bin/clang", "/usr/bin/clang++",
{isClang = true, isMultilib = true} { isClang = true, isMultilib = true })
)
table.insert(compilerList, release) table.insert(compilerList, release)
table.insert(compilerList, debug) table.insert(compilerList, debug_)
table.insert(compilerList, debugWithAsan) table.insert(compilerList, debugWithAsan)
end end
end end
-- cross GCC
if ( if (
arch == "x86_64" and arch == "x86_64" and
C_FileSystem.exists("/proc/sys/fs/binfmt_misc/qemu-aarch64") and C_FileSystem.exists("/proc/sys/fs/binfmt_misc/qemu-aarch64") and
C_FileSystem.isExecutable("/usr/bin/aarch64-linux-gnu-gcc") C_FileSystem.isExecutable("/usr/bin/aarch64-linux-gnu-gcc")) then
) then
local release, debug, debugWithAsan = generateConfig( local release, _, _ = generateConfig(
(nameMap.crossGcc[lang] or nameMap.crossGcc.en_US) .. " aarch64", (nameMap.crossGcc[lang] or nameMap.crossGcc.en_US) .. " aarch64", lang,
"/usr/bin/aarch64-linux-gnu-gcc", "/usr/bin/aarch64-linux-gnu-g++", "/usr/bin/aarch64-linux-gnu-gcc", "/usr/bin/aarch64-linux-gnu-g++",
{} {})
)
table.insert(compilerList, release) table.insert(compilerList, release)
end end
-- with wine or WSL init registered in binfmt_misc, Windows binaries can run seamlessly
if ( if (
arch == "x86_64" and ( arch == "x86_64" and (
C_FileSystem.exists("/proc/sys/fs/binfmt_misc/DOSWin") or C_FileSystem.exists("/proc/sys/fs/binfmt_misc/DOSWin") or
C_FileSystem.exists("/proc/sys/fs/binfmt_misc/WSLInterop") C_FileSystem.exists("/proc/sys/fs/binfmt_misc/WSLInterop"))) then
)
) then
if C_FileSystem.isExecutable("/usr/bin/x86_64-w64-mingw32-gcc") then if C_FileSystem.isExecutable("/usr/bin/x86_64-w64-mingw32-gcc") then
local extraObjects = { local extraObjects = {
utf8init = libexecDir .. "/x86_64-w64-mingw32/utf8init.o", utf8init = libexecDir .. "/x86_64-w64-mingw32/utf8init.o",
utf8manifest = libexecDir .. "/x86_64-w64-mingw32/utf8manifest.o", utf8manifest = libexecDir .. "/x86_64-w64-mingw32/utf8manifest.o",
} }
local release, debug, debugWithAsan = generateConfig(
(nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " x86_64", do
local release, _, _ = generateConfig(
(nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " x86_64", lang,
"/usr/bin/x86_64-w64-mingw32-gcc", "/usr/bin/x86_64-w64-mingw32-g++", "/usr/bin/x86_64-w64-mingw32-gcc", "/usr/bin/x86_64-w64-mingw32-g++",
{ {
isMingw = true, isMingw = true,
triplet = "x86_64-w64-mingw32", triplet = "x86_64-w64-mingw32",
customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest }, customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest },
} })
)
table.insert(compilerList, release) table.insert(compilerList, release)
-- system Clang can target Windows end
if C_FileSystem.isExecutable("/usr/bin/clang") then if C_FileSystem.isExecutable("/usr/bin/clang") then
local release, debug, debugWithAsan = generateConfig( local release, _, _ = generateConfig(
(nameMap.mingwClang[lang] or nameMap.mingwClang.en_US) .. " x86_64", (nameMap.mingwClang[lang] or nameMap.mingwClang.en_US) .. " x86_64", lang,
"/usr/bin/clang", "/usr/bin/clang++", "/usr/bin/clang", "/usr/bin/clang++",
{ {
isClang = true, isClang = true,
@ -216,30 +251,35 @@ if (
extraObjects.utf8init, extraObjects.utf8manifest, extraObjects.utf8init, extraObjects.utf8manifest,
"-lstdc++", "-lwinpthread", "-lstdc++", "-lwinpthread",
}, },
} })
)
table.insert(compilerList, release) table.insert(compilerList, release)
end end
end end
if C_FileSystem.isExecutable("/usr/bin/i686-w64-mingw32-gcc") then if C_FileSystem.isExecutable("/usr/bin/i686-w64-mingw32-gcc") then
local extraObjects = { local extraObjects = {
utf8init = libexecDir .. "/i686-w64-mingw32/utf8init.o", utf8init = libexecDir .. "/i686-w64-mingw32/utf8init.o",
utf8manifest = libexecDir .. "/i686-w64-mingw32/utf8manifest.o", utf8manifest = libexecDir .. "/i686-w64-mingw32/utf8manifest.o",
} }
local release, debug, debugWithAsan = generateConfig(
(nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " i686", do
local release, _, _ = generateConfig(
(nameMap.mingwGcc[lang] or nameMap.mingwGcc.en_US) .. " i686", lang,
"/usr/bin/i686-w64-mingw32-gcc", "/usr/bin/i686-w64-mingw32-g++", "/usr/bin/i686-w64-mingw32-gcc", "/usr/bin/i686-w64-mingw32-g++",
{ {
isMingw = true, isMingw = true,
triplet = "i686-w64-mingw32", triplet = "i686-w64-mingw32",
customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest }, customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest },
} })
)
table.insert(compilerList, release) table.insert(compilerList, release)
-- system Clang can target Windows end
if C_FileSystem.isExecutable("/usr/bin/clang") then if C_FileSystem.isExecutable("/usr/bin/clang") then
local release, debug, debugWithAsan = generateConfig( local release, _, _ = generateConfig(
(nameMap.mingwClang[lang] or nameMap.mingwClang.en_US) .. " i686", (nameMap.mingwClang[lang] or nameMap.mingwClang.en_US) .. " i686", lang,
"/usr/bin/clang", "/usr/bin/clang++", "/usr/bin/clang", "/usr/bin/clang++",
{ {
isClang = true, isClang = true,
@ -251,8 +291,8 @@ if (
extraObjects.utf8init, extraObjects.utf8manifest, extraObjects.utf8init, extraObjects.utf8manifest,
"-lstdc++", "-lwinpthread", "-lstdc++", "-lwinpthread",
}, },
} })
)
table.insert(compilerList, release) table.insert(compilerList, release)
end end
end end
@ -265,7 +305,9 @@ local result = {
"/opt/cuda/bin", "/opt/cuda/bin",
"/usr/lib/ccache/bin", "/usr/lib/ccache/bin",
}, },
preferCompiler = 3, -- System GCC Debug with ASan preferCompiler = 3,
} }
return result return result
end

View File

@ -1,8 +1,10 @@
local arch = C_System.osArch() function apiVersion()
local appArch = C_System.appArch() return {
local libexecDir = C_System.appLibexecDir() kind = "compiler_hint",
local lang = C_Desktop.language() major = 0,
local supportedAppArches = C_System.supportedAppArchList() minor = 1,
}
end
local gnuArchMap = { local gnuArchMap = {
i386 = "i686", i386 = "i686",
@ -32,8 +34,7 @@ local profileNameMap = {
}, },
} }
local nameGenerator = { local function nameGeneratorMingwGcc(lang, arch, profile, isUtf8)
mingwGcc = function (lang, arch, profile, isUtf8)
local template = { local template = {
en_US = "MinGW GCC %1 in %2, %3", en_US = "MinGW GCC %1 in %2, %3",
pt_BR = "GCC MinGW %1 em %2, %3", pt_BR = "GCC MinGW %1 em %2, %3",
@ -50,15 +51,16 @@ local nameGenerator = {
template[lang] or template.en_US, template[lang] or template.en_US,
gnuArchMap[arch], gnuArchMap[arch],
isUtf8 and "UTF-8" or systemCodePage[lang] or systemCodePage.en_US, isUtf8 and "UTF-8" or systemCodePage[lang] or systemCodePage.en_US,
profileNameMap[profile][lang] or profileNameMap[profile].en_US profileNameMap[profile][lang] or profileNameMap[profile].en_US)
)
end, end
clang = function (lang, arch, profile, isMingw)
local function nameGeneratorClang(lang, arch, profile, isMingw)
local template = { local template = {
en_US = "%1 Clang %2, %3", en_US = "%1 Clang %2, %3",
pt_BR = "Clang %2 %1, %3", pt_BR = "Clang %2 %1, %3",
zh_CN = "%1 Clang %2%3", zh_CN = "%1 Clang %2%3",
zh_CN = "%1 Clang %2%3", zh_TW = "%1 Clang %2%3",
} }
local msvcCompatible = { local msvcCompatible = {
en_US = "MSVC-compatible", en_US = "MSVC-compatible",
@ -70,12 +72,42 @@ local nameGenerator = {
template[lang] or template.en_US, template[lang] or template.en_US,
isMingw and "LLVM-MinGW" or msvcCompatible[lang] or msvcCompatible.en_US, isMingw and "LLVM-MinGW" or msvcCompatible[lang] or msvcCompatible.en_US,
gnuArchMap[arch], gnuArchMap[arch],
profileNameMap[profile][lang] or profileNameMap[profile].en_US profileNameMap[profile][lang] or profileNameMap[profile].en_US)
)
end, end
}
local function mergeCompilerSet(compilerSet, other)
local c = compilerSet
local o = other
for k, v in pairs(o) do
c[k] = v
end
end
local function generateConfig(
nameGen,
programs,
config)
function generateConfig(nameGen, programs, config)
local commonOptions = { local commonOptions = {
cCompiler = programs.cCompiler, cCompiler = programs.cCompiler,
cxxCompiler = programs.cxxCompiler, cxxCompiler = programs.cxxCompiler,
@ -113,7 +145,7 @@ function generateConfig(nameGen, programs, config)
linkCmdOptStripExe = "on", linkCmdOptStripExe = "on",
ccCmdOptOptimize = "2", ccCmdOptOptimize = "2",
} }
local debug = { local debug_ = {
name = nameGen(config.arch, "debug"), name = nameGen(config.arch, "debug"),
ccCmdOptDebugInfo = "on", ccCmdOptDebugInfo = "on",
} }
@ -122,35 +154,30 @@ function generateConfig(nameGen, programs, config)
ccCmdOptDebugInfo = "on", ccCmdOptDebugInfo = "on",
ccCmdOptAddressSanitizer = "address", ccCmdOptAddressSanitizer = "address",
} }
for k, v in pairs(commonOptions) do mergeCompilerSet(release, commonOptions)
release[k] = v mergeCompilerSet(debug_, commonOptions)
debug[k] = v mergeCompilerSet(debugWithAsan, commonOptions)
debugWithAsan[k] = v return release, debug_, debugWithAsan
end
return release, debug, debugWithAsan
end end
function contains(t, v) function main()
for _, vv in ipairs(t) do local appArch = C_System.appArch()
if vv == v then local libexecDir = C_System.appLibexecDir()
return true local lang = C_Desktop.language()
end local supportedAppArches = C_System.supportedAppArchList()
end
return false
end
local compilerList = {} local compilerList = {}
local noSearch = {} local noSearch = {}
local preferCompiler = 0 local preferCompiler = 0
function checkAndAddMingw(arch) local function checkAndAddMingw(arch)
local binDir local binDir
local libDir local libDir
local excludeBinDir local excludeBinDir
if arch == "i386" then if arch == "i386" then
binDir = libexecDir .. "/mingw32/bin" -- must match case because Windows filesystem can be case sensitive binDir = libexecDir .. "/mingw32/bin"
libDir = libexecDir .. "/mingw32/i686-w64-mingw32/lib" libDir = libexecDir .. "/mingw32/i686-w64-mingw32/lib"
excludeBinDir = libexecDir .. "/MinGW32/bin" -- workaround for path check excludeBinDir = libexecDir .. "/MinGW32/bin"
elseif arch == "x86_64" then elseif arch == "x86_64" then
binDir = libexecDir .. "/mingw64/bin" binDir = libexecDir .. "/mingw64/bin"
libDir = libexecDir .. "/mingw64/x86_64-w64-mingw32/lib" libDir = libexecDir .. "/mingw64/x86_64-w64-mingw32/lib"
@ -177,42 +204,47 @@ function checkAndAddMingw(arch)
utf8manifest = libDir .. "/utf8manifest.o", utf8manifest = libDir .. "/utf8manifest.o",
} }
local release, debug, debugWithAsan = generateConfig( local release, debug_, debugWithAsan = generateConfig(
function (arch, profile) return nameGenerator.mingwGcc(lang, arch, profile, true) end, function(arch_, profile)
return nameGeneratorMingwGcc(lang, arch_, profile, true)
end,
programs, programs,
{ {
arch = arch, arch = arch,
customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest }, customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest },
} })
)
table.insert(compilerList, release) table.insert(compilerList, release)
table.insert(compilerList, debug) table.insert(compilerList, debug_)
if preferCompiler == 0 then if preferCompiler == 0 then
preferCompiler = 2 preferCompiler = 2
end end
release, debug, debugWithAsan = generateConfig( release, debug_, debugWithAsan = generateConfig(
function (arch, profile) return nameGenerator.mingwGcc(lang, arch, profile, false) end, function(arch_, profile)
return nameGeneratorMingwGcc(lang, arch_, profile, false)
end,
programs, programs,
{ {
arch = arch, arch = arch,
isAnsi = true, isAnsi = true,
} })
)
table.insert(compilerList, release) table.insert(compilerList, release)
table.insert(compilerList, debug) table.insert(compilerList, debug_)
table.insert(noSearch, excludeBinDir) table.insert(noSearch, excludeBinDir)
end end
function checkAndAddClang() local function checkAndAddClang()
if not C_FileSystem.isExecutable(libexecDir .. "/llvm-mingw/bin/clang.exe") then if not C_FileSystem.isExecutable(libexecDir .. "/llvm-mingw/bin/clang.exe") then
return return
end end
-- appArch is always debuggable
local appTriplet = gnuArchMap[appArch] .. "-w64-mingw32"
local binDir = libexecDir .. "/llvm-mingw/bin" local binDir = libexecDir .. "/llvm-mingw/bin"
local appTriplet = gnuArchMap[appArch] .. "-w64-mingw32"
do
local libDir = libexecDir .. "/llvm-mingw/" .. appTriplet .. "/lib" local libDir = libexecDir .. "/llvm-mingw/" .. appTriplet .. "/lib"
local programs = { local programs = {
cCompiler = binDir .. "/" .. appTriplet .. "-clang.exe", cCompiler = binDir .. "/" .. appTriplet .. "-clang.exe",
@ -227,17 +259,19 @@ function checkAndAddClang()
utf8init = libDir .. "/utf8init.o", utf8init = libDir .. "/utf8init.o",
utf8manifest = libDir .. "/utf8manifest.o", utf8manifest = libDir .. "/utf8manifest.o",
} }
local release, debug, debugWithAsan = generateConfig( local release, debug_, debugWithAsan = generateConfig(
function (arch, profile) return nameGenerator.clang(lang, arch, profile, true) end, function(arch_, profile)
return nameGeneratorClang(lang, arch_, profile, true)
end,
programs, programs,
{ {
arch = appArch, arch = appArch,
customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest }, customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest },
isClang = true, isClang = true,
} })
)
table.insert(compilerList, release) table.insert(compilerList, release)
table.insert(compilerList, debug) table.insert(compilerList, debug_)
if appArch ~= "arm64" then if appArch ~= "arm64" then
table.insert(compilerList, debugWithAsan) table.insert(compilerList, debugWithAsan)
if preferCompiler == 0 then if preferCompiler == 0 then
@ -248,6 +282,7 @@ function checkAndAddClang()
preferCompiler = 2 preferCompiler = 2
end end
end end
end
for _, foreignArch in ipairs(supportedAppArches) do for _, foreignArch in ipairs(supportedAppArches) do
if foreignArch ~= appArch then if foreignArch ~= appArch then
@ -266,15 +301,17 @@ function checkAndAddClang()
utf8init = libDir .. "/utf8init.o", utf8init = libDir .. "/utf8init.o",
utf8manifest = libDir .. "/utf8manifest.o", utf8manifest = libDir .. "/utf8manifest.o",
} }
local release, debug, debugWithAsan = generateConfig( local release, _, _ = generateConfig(
function (arch, profile) return nameGenerator.clang(lang, arch, profile, true) end, function(arch_, profile)
return nameGeneratorClang(lang, arch_, profile, true)
end,
programs, programs,
{ {
arch = foreignArch, arch = foreignArch,
customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest }, customLinkParams = { extraObjects.utf8init, extraObjects.utf8manifest },
isClang = true, isClang = true,
} })
)
table.insert(compilerList, release) table.insert(compilerList, release)
end end
end end
@ -285,8 +322,9 @@ function checkAndAddClang()
if not llvmOrgPath then if not llvmOrgPath then
return return
end end
local llvmOrgBinDir = llvmOrgPath .. "/bin" local llvmOrgBinDir = llvmOrgPath .. "/bin"
do
local msvcTriplet = gnuArchMap[appArch] .. "-pc-windows-msvc" local msvcTriplet = gnuArchMap[appArch] .. "-pc-windows-msvc"
local libDir = libexecDir .. "/llvm-mingw/" .. msvcTriplet .. "/lib" local libDir = libexecDir .. "/llvm-mingw/" .. msvcTriplet .. "/lib"
local programs = { local programs = {
@ -303,8 +341,10 @@ function checkAndAddClang()
utf8init = libDir .. "/utf8init.o", utf8init = libDir .. "/utf8init.o",
utf8manifest = libDir .. "/utf8manifest.o", utf8manifest = libDir .. "/utf8manifest.o",
} }
local release, debug, debugWithAsan = generateConfig( local release, debug_, _ = generateConfig(
function (arch, profile) return nameGenerator.clang(lang, arch, profile, false) end, function(arch, profile)
return nameGeneratorClang(lang, arch, profile, false)
end,
programs, programs,
{ {
arch = appArch, arch = appArch,
@ -319,10 +359,11 @@ function checkAndAddClang()
extraObjects.utf8init, extraObjects.utf8manifest, extraObjects.utf8init, extraObjects.utf8manifest,
}, },
isClang = true, isClang = true,
} })
)
table.insert(compilerList, release) table.insert(compilerList, release)
table.insert(compilerList, debug) table.insert(compilerList, debug_)
end
for _, foreignArch in ipairs(supportedAppArches) do for _, foreignArch in ipairs(supportedAppArches) do
if foreignArch ~= appArch then if foreignArch ~= appArch then
@ -343,8 +384,10 @@ function checkAndAddClang()
utf8init = libDir .. "/utf8init.o", utf8init = libDir .. "/utf8init.o",
utf8manifest = libDir .. "/utf8manifest.o", utf8manifest = libDir .. "/utf8manifest.o",
} }
local release, debug, debugWithAsan = generateConfig( local release, _, _ = generateConfig(
function (arch, profile) return nameGenerator.clang(lang, arch, profile, false) end, function(arch, profile)
return nameGeneratorClang(lang, arch, profile, false)
end,
programs, programs,
{ {
arch = foreignArch, arch = foreignArch,
@ -359,8 +402,8 @@ function checkAndAddClang()
extraObjects.utf8init, extraObjects.utf8manifest, extraObjects.utf8init, extraObjects.utf8manifest,
}, },
isClang = true, isClang = true,
} })
)
table.insert(compilerList, release) table.insert(compilerList, release)
end end
end end
@ -384,3 +427,4 @@ local result = {
} }
return result return result
end