hyprpm: check for abi strings in headersValid (#12504)

---------

Co-authored-by: Virt <41426325+VirtCode@users.noreply.github.com>
This commit is contained in:
Vaxry 2025-12-04 18:00:15 +00:00 committed by GitHub
parent d9657a95cb
commit 9cd070fd31
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 51 additions and 43 deletions

View file

@ -13,7 +13,7 @@ pkg_check_modules(hyprpm_deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0
find_package(glaze QUIET) find_package(glaze QUIET)
if (NOT glaze_FOUND) if (NOT glaze_FOUND)
set(GLAZE_VERSION v5.1.1) set(GLAZE_VERSION v6.1.0)
message(STATUS "glaze dependency not found, retrieving ${GLAZE_VERSION} with FetchContent") message(STATUS "glaze dependency not found, retrieving ${GLAZE_VERSION} with FetchContent")
include(FetchContent) include(FetchContent)
FetchContent_Declare( FetchContent_Declare(

View file

@ -181,7 +181,7 @@ void DataState::updateGlobalState(const SGlobalState& state) {
// clang-format off // clang-format off
auto DATA = toml::table{ auto DATA = toml::table{
{"state", toml::table{ {"state", toml::table{
{"hash", state.headersHashCompiled}, {"hash", state.headersAbiCompiled},
{"dont_warn_install", state.dontWarnInstall} {"dont_warn_install", state.dontWarnInstall}
}} }}
}; };
@ -206,8 +206,8 @@ SGlobalState DataState::getGlobalState() {
auto DATA = toml::parse_file(stateFile.c_str()); auto DATA = toml::parse_file(stateFile.c_str());
SGlobalState state; SGlobalState state;
state.headersHashCompiled = DATA["state"]["hash"].value_or(""); state.headersAbiCompiled = DATA["state"]["hash"].value_or("");
state.dontWarnInstall = DATA["state"]["dont_warn_install"].value_or(false); state.dontWarnInstall = DATA["state"]["dont_warn_install"].value_or(false);
return state; return state;
} }

View file

@ -5,8 +5,8 @@
#include "Plugin.hpp" #include "Plugin.hpp"
struct SGlobalState { struct SGlobalState {
std::string headersHashCompiled = ""; std::string headersAbiCompiled = "";
bool dontWarnInstall = false; bool dontWarnInstall = false;
}; };
namespace DataState { namespace DataState {

View file

@ -78,40 +78,30 @@ SHyprlandVersion CPluginManager::getHyprlandVersion(bool running) {
else else
onceInstalled = true; onceInstalled = true;
const auto HLVERCALL = running ? NHyprlandSocket::send("/version") : execAndGet("Hyprland --version"); const auto HLVERCALL = running ? NHyprlandSocket::send("j/version") : execAndGet("Hyprland --version-json");
if (m_bVerbose)
std::println("{}", verboseString("{} version returned: {}", running ? "running" : "installed", HLVERCALL));
if (!HLVERCALL.contains("Tag:")) { auto jsonQuery = glz::read_json<glz::generic>(HLVERCALL);
std::println(stderr, "\n{}", failureString("You don't seem to be running Hyprland."));
if (!jsonQuery) {
std::println("{}", failureString("failed to get the current hyprland version. Are you running hyprland?"));
return SHyprlandVersion{}; return SHyprlandVersion{};
} }
std::string hlcommit = HLVERCALL.substr(HLVERCALL.find("at commit") + 10); auto hlbranch = (*jsonQuery)["branch"].get_string();
hlcommit = hlcommit.substr(0, hlcommit.find_first_of(' ')); auto hlcommit = (*jsonQuery)["commit"].get_string();
auto abiHash = (*jsonQuery)["abiHash"].get_string();
auto hldate = (*jsonQuery)["commit_date"].get_string();
auto hlcommits = (*jsonQuery)["commits"].get_string();
std::string hlbranch = HLVERCALL.substr(HLVERCALL.find("from branch") + 12); size_t commits = 0;
hlbranch = hlbranch.substr(0, hlbranch.find(" at commit "));
std::string hldate = HLVERCALL.substr(HLVERCALL.find("Date: ") + 6);
hldate = hldate.substr(0, hldate.find('\n'));
std::string hlcommits;
if (HLVERCALL.contains("commits:")) {
hlcommits = HLVERCALL.substr(HLVERCALL.find("commits:") + 9);
hlcommits = hlcommits.substr(0, hlcommits.find(' '));
}
int commits = 0;
try { try {
commits = std::stoi(hlcommits); commits = std::stoull(hlcommits);
} catch (...) { ; } } catch (...) { ; }
if (m_bVerbose) if (m_bVerbose)
std::println("{}", verboseString("parsed commit {} at branch {} on {}, commits {}", hlcommit, hlbranch, hldate, commits)); std::println("{}", verboseString("parsed commit {} at branch {} on {}, commits {}", hlcommit, hlbranch, hldate, commits));
auto ver = SHyprlandVersion{hlbranch, hlcommit, hldate, commits}; auto ver = SHyprlandVersion{hlbranch, hlcommit, hldate, abiHash, commits};
if (running) if (running)
verRunning = ver; verRunning = ver;
@ -161,7 +151,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
DataState::updateGlobalState(GLOBALSTATE); DataState::updateGlobalState(GLOBALSTATE);
} }
if (GLOBALSTATE.headersHashCompiled.empty()) { if (GLOBALSTATE.headersAbiCompiled.empty()) {
std::println("\n{}", failureString("Cannot find headers in the global state. Try running hyprpm update first.")); std::println("\n{}", failureString("Cannot find headers in the global state. Try running hyprpm update first."));
return false; return false;
} }
@ -444,6 +434,12 @@ eHeadersErrors CPluginManager::headersValid() {
if (hash != HLVER.hash) if (hash != HLVER.hash)
return HEADERS_MISMATCHED; return HEADERS_MISMATCHED;
// check ABI hash too
const auto GLOBALSTATE = DataState::getGlobalState();
if (GLOBALSTATE.headersAbiCompiled != HLVER.abiHash)
return HEADERS_ABI_MISMATCH;
return HEADERS_OK; return HEADERS_OK;
} }
@ -589,14 +585,14 @@ bool CPluginManager::updateHeaders(bool force) {
std::filesystem::remove_all(WORKINGDIR); std::filesystem::remove_all(WORKINGDIR);
auto HEADERSVALID = headersValid(); auto HEADERSVALID = headersValid();
if (HEADERSVALID == HEADERS_OK) { if (HEADERSVALID == HEADERS_OK || HEADERSVALID == HEADERS_MISMATCHED || HEADERSVALID == HEADERS_ABI_MISMATCH) {
progress.printMessageAbove(successString("installed headers")); progress.printMessageAbove(successString("installed headers"));
progress.m_iSteps = 5; progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Done!"; progress.m_szCurrentMessage = "Done!";
progress.print(); progress.print();
auto GLOBALSTATE = DataState::getGlobalState(); auto GLOBALSTATE = DataState::getGlobalState();
GLOBALSTATE.headersHashCompiled = HLVER.hash; GLOBALSTATE.headersAbiCompiled = HLVER.abiHash;
DataState::updateGlobalState(GLOBALSTATE); DataState::updateGlobalState(GLOBALSTATE);
std::print("\n"); std::print("\n");
@ -787,8 +783,8 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
progress.m_szCurrentMessage = "Updating global state..."; progress.m_szCurrentMessage = "Updating global state...";
progress.print(); progress.print();
auto GLOBALSTATE = DataState::getGlobalState(); auto GLOBALSTATE = DataState::getGlobalState();
GLOBALSTATE.headersHashCompiled = HLVER.hash; GLOBALSTATE.headersAbiCompiled = HLVER.abiHash;
DataState::updateGlobalState(GLOBALSTATE); DataState::updateGlobalState(GLOBALSTATE);
progress.m_iSteps++; progress.m_iSteps++;
@ -828,7 +824,7 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState(bool forceReload)
} }
const auto HYPRPMPATH = DataState::getDataStatePath(); const auto HYPRPMPATH = DataState::getDataStatePath();
const auto json = glz::read_json<glz::json_t::array_t>(NHyprlandSocket::send("j/plugins list")); const auto json = glz::read_json<glz::generic::array_t>(NHyprlandSocket::send("j/plugins list"));
if (!json) { if (!json) {
std::println(stderr, "PluginManager: couldn't parse plugin list output"); std::println(stderr, "PluginManager: couldn't parse plugin list output");
return LOADSTATE_FAIL; return LOADSTATE_FAIL;
@ -913,9 +909,9 @@ bool CPluginManager::loadUnloadPlugin(const std::string& path, bool load) {
auto state = DataState::getGlobalState(); auto state = DataState::getGlobalState();
auto HLVER = getHyprlandVersion(true); auto HLVER = getHyprlandVersion(true);
if (state.headersHashCompiled != HLVER.hash) { if (state.headersAbiCompiled != HLVER.abiHash) {
if (load) if (load)
std::println("{}", infoString("Running Hyprland version ({}) differs from plugin state ({}), please restart Hyprland.", HLVER.hash, state.headersHashCompiled)); std::println("{}", infoString("Running Hyprland version ({}) differs from plugin state ({}), please restart Hyprland.", HLVER.hash, state.headersAbiCompiled));
return false; return false;
} }
@ -956,6 +952,7 @@ std::string CPluginManager::headerError(const eHeadersErrors err) {
case HEADERS_MISMATCHED: return failureString("Headers version mismatch. Please run hyprpm update to fix those.\n"); case HEADERS_MISMATCHED: return failureString("Headers version mismatch. Please run hyprpm update to fix those.\n");
case HEADERS_NOT_HYPRLAND: return failureString("It doesn't seem you are running on hyprland.\n"); case HEADERS_NOT_HYPRLAND: return failureString("It doesn't seem you are running on hyprland.\n");
case HEADERS_MISSING: return failureString("Headers missing. Please run hyprpm update to fix those.\n"); case HEADERS_MISSING: return failureString("Headers missing. Please run hyprpm update to fix those.\n");
case HEADERS_ABI_MISMATCH: return failureString("ABI is mismatched. Please run hyprpm update to fix that.\n");
case HEADERS_DUPLICATED: { case HEADERS_DUPLICATED: {
return failureString("Headers duplicated!!! This is a very bad sign.\n" return failureString("Headers duplicated!!! This is a very bad sign.\n"
"This could be due to e.g. installing hyprland manually while a system package of hyprland is also installed.\n" "This could be due to e.g. installing hyprland manually while a system package of hyprland is also installed.\n"

View file

@ -11,6 +11,7 @@ enum eHeadersErrors {
HEADERS_MISSING, HEADERS_MISSING,
HEADERS_CORRUPTED, HEADERS_CORRUPTED,
HEADERS_MISMATCHED, HEADERS_MISMATCHED,
HEADERS_ABI_MISMATCH,
HEADERS_DUPLICATED HEADERS_DUPLICATED
}; };
@ -36,6 +37,7 @@ struct SHyprlandVersion {
std::string branch; std::string branch;
std::string hash; std::string hash;
std::string date; std::string date;
std::string abiHash;
int commits = 0; int commits = 0;
}; };

View file

@ -106,7 +106,7 @@ int main(int argc, char** argv, char** envp) {
const auto HLVER = g_pPluginManager->getHyprlandVersion(); const auto HLVER = g_pPluginManager->getHyprlandVersion();
auto GLOBALSTATE = DataState::getGlobalState(); auto GLOBALSTATE = DataState::getGlobalState();
if (GLOBALSTATE.headersHashCompiled != HLVER.hash) { if (GLOBALSTATE.headersAbiCompiled != HLVER.abiHash) {
std::println(stderr, "{}", failureString("Headers outdated, please run hyprpm update.")); std::println(stderr, "{}", failureString("Headers outdated, please run hyprpm update."));
return 1; return 1;
} }
@ -137,7 +137,7 @@ int main(int argc, char** argv, char** envp) {
if (headers) { if (headers) {
const auto HLVER = g_pPluginManager->getHyprlandVersion(false); const auto HLVER = g_pPluginManager->getHyprlandVersion(false);
auto GLOBALSTATE = DataState::getGlobalState(); auto GLOBALSTATE = DataState::getGlobalState();
const auto COMPILEDOUTDATED = HLVER.hash != GLOBALSTATE.headersHashCompiled; const auto COMPILEDOUTDATED = HLVER.abiHash != GLOBALSTATE.headersAbiCompiled;
bool ret1 = g_pPluginManager->updatePlugins(!headersValid || force || COMPILEDOUTDATED); bool ret1 = g_pPluginManager->updatePlugins(!headersValid || force || COMPILEDOUTDATED);

View file

@ -1063,6 +1063,9 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\n"; result += "\n";
result += getBuiltSystemLibraryNames(); result += getBuiltSystemLibraryNames();
result += "\n"; result += "\n";
result += "Version ABI string: ";
result += __hyprland_api_get_hash();
result += "\n";
#if (!ISDEBUG && !defined(NO_XWAYLAND)) #if (!ISDEBUG && !defined(NO_XWAYLAND))
result += "no flags were set\n"; result += "no flags were set\n";
@ -1097,10 +1100,12 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
"systemHyprutils": "{}", "systemHyprutils": "{}",
"systemHyprcursor": "{}", "systemHyprcursor": "{}",
"systemHyprgraphics": "{}", "systemHyprgraphics": "{}",
"abiHash": "{}",
"flags": [)#", "flags": [)#",
GIT_BRANCH, GIT_COMMIT_HASH, HYPRLAND_VERSION, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_COMMIT_DATE, GIT_TAG, GIT_BRANCH, GIT_COMMIT_HASH, HYPRLAND_VERSION, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_COMMIT_DATE, GIT_TAG,
GIT_COMMITS, AQUAMARINE_VERSION, HYPRLANG_VERSION, HYPRUTILS_VERSION, HYPRCURSOR_VERSION, HYPRGRAPHICS_VERSION, getSystemLibraryVersion("aquamarine"), GIT_COMMITS, AQUAMARINE_VERSION, HYPRLANG_VERSION, HYPRUTILS_VERSION, HYPRCURSOR_VERSION, HYPRGRAPHICS_VERSION, getSystemLibraryVersion("aquamarine"),
getSystemLibraryVersion("hyprlang"), getSystemLibraryVersion("hyprutils"), getSystemLibraryVersion("hyprcursor"), getSystemLibraryVersion("hyprgraphics")); getSystemLibraryVersion("hyprlang"), getSystemLibraryVersion("hyprutils"), getSystemLibraryVersion("hyprcursor"), getSystemLibraryVersion("hyprgraphics"),
__hyprland_api_get_hash());
#if ISDEBUG #if ISDEBUG
result += "\"debug\","; result += "\"debug\",";

View file

@ -25,7 +25,7 @@ using namespace Hyprutils::Memory;
static void help() { static void help() {
std::println("usage: Hyprland [arg [...]].\n"); std::println("usage: Hyprland [arg [...]].\n");
std::println(R"(Arguments: std::println(R"#(Arguments:
--help -h - Show this message again --help -h - Show this message again
--config FILE -c FILE - Specify config file to use --config FILE -c FILE - Specify config file to use
--socket NAME - Sets the Wayland socket name (for Wayland socket handover) --socket NAME - Sets the Wayland socket name (for Wayland socket handover)
@ -33,7 +33,8 @@ static void help() {
--systeminfo - Prints system infos --systeminfo - Prints system infos
--i-am-really-stupid - Omits root user privileges check (why would you do that?) --i-am-really-stupid - Omits root user privileges check (why would you do that?)
--verify-config - Do not run Hyprland, only print if the config has any errors --verify-config - Do not run Hyprland, only print if the config has any errors
--version -v - Print this binary's version)"); --version -v - Print this binary's version
--version-json - Print this binary's version as json)#");
} }
static void reapZombieChildrenAutomatically() { static void reapZombieChildrenAutomatically() {
@ -142,6 +143,9 @@ int main(int argc, char** argv) {
} else if (value == "-v" || value == "--version") { } else if (value == "-v" || value == "--version") {
std::println("{}", versionRequest(eHyprCtlOutputFormat::FORMAT_NORMAL, "")); std::println("{}", versionRequest(eHyprCtlOutputFormat::FORMAT_NORMAL, ""));
return 0; return 0;
} else if (value == "--version-json") {
std::println("{}", versionRequest(eHyprCtlOutputFormat::FORMAT_JSON, ""));
return 0;
} else if (value == "--systeminfo") { } else if (value == "--systeminfo") {
std::println("{}", systemInfoRequest(eHyprCtlOutputFormat::FORMAT_NORMAL, "")); std::println("{}", systemInfoRequest(eHyprCtlOutputFormat::FORMAT_NORMAL, ""));
return 0; return 0;