2023-12-07 11:41:09 +01:00
|
|
|
#include "PluginManager.hpp"
|
|
|
|
#include "../helpers/Colors.hpp"
|
2024-10-13 14:23:33 +02:00
|
|
|
#include "../helpers/StringUtils.hpp"
|
2023-12-07 11:41:09 +01:00
|
|
|
#include "../progress/CProgressBar.hpp"
|
|
|
|
#include "Manifest.hpp"
|
|
|
|
#include "DataState.hpp"
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
#include <cstdio>
|
2023-12-07 11:41:09 +01:00
|
|
|
#include <iostream>
|
|
|
|
#include <array>
|
|
|
|
#include <filesystem>
|
2024-10-13 14:23:33 +02:00
|
|
|
#include <print>
|
2023-12-07 11:41:09 +01:00
|
|
|
#include <thread>
|
|
|
|
#include <fstream>
|
|
|
|
#include <algorithm>
|
2024-02-01 20:38:43 +01:00
|
|
|
#include <format>
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <pwd.h>
|
2024-04-30 15:14:31 +02:00
|
|
|
#include <unistd.h>
|
2024-04-28 21:27:44 +02:00
|
|
|
|
2023-12-07 11:41:09 +01:00
|
|
|
#include <toml++/toml.hpp>
|
|
|
|
|
2024-06-11 17:17:45 +02:00
|
|
|
#include <hyprutils/string/String.hpp>
|
2024-11-09 18:11:38 +01:00
|
|
|
#include <hyprutils/os/Process.hpp>
|
2024-06-11 17:17:45 +02:00
|
|
|
using namespace Hyprutils::String;
|
2024-11-09 18:11:38 +01:00
|
|
|
using namespace Hyprutils::OS;
|
2023-12-21 22:01:50 +01:00
|
|
|
|
|
|
|
static std::string execAndGet(std::string cmd) {
|
2023-12-07 11:41:09 +01:00
|
|
|
cmd += " 2>&1";
|
2024-11-09 18:11:38 +01:00
|
|
|
|
|
|
|
CProcess proc("/bin/sh", {cmd});
|
2024-11-10 16:54:00 +01:00
|
|
|
|
2024-11-09 18:11:38 +01:00
|
|
|
if (!proc.runSync())
|
|
|
|
return "error";
|
|
|
|
|
|
|
|
return proc.stdOut();
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
SHyprlandVersion CPluginManager::getHyprlandVersion() {
|
|
|
|
static SHyprlandVersion ver;
|
|
|
|
static bool once = false;
|
|
|
|
|
|
|
|
if (once)
|
|
|
|
return ver;
|
|
|
|
|
|
|
|
once = true;
|
|
|
|
const auto HLVERCALL = execAndGet("hyprctl version");
|
|
|
|
if (m_bVerbose)
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("{}", verboseString("version returned: {}", HLVERCALL));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
|
|
|
if (!HLVERCALL.contains("Tag:")) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "\n{}", failureString("You don't seem to be running Hyprland."));
|
2023-12-07 11:41:09 +01:00
|
|
|
return SHyprlandVersion{};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string hlcommit = HLVERCALL.substr(HLVERCALL.find("at commit") + 10);
|
|
|
|
hlcommit = hlcommit.substr(0, hlcommit.find_first_of(' '));
|
|
|
|
|
|
|
|
std::string hlbranch = HLVERCALL.substr(HLVERCALL.find("from branch") + 12);
|
|
|
|
hlbranch = hlbranch.substr(0, hlbranch.find(" at commit "));
|
|
|
|
|
2024-04-10 18:33:50 +02:00
|
|
|
std::string hldate = HLVERCALL.substr(HLVERCALL.find("Date: ") + 6);
|
|
|
|
hldate = hldate.substr(0, hldate.find("\n"));
|
|
|
|
|
2024-04-05 01:44:21 +02:00
|
|
|
std::string hlcommits;
|
|
|
|
|
|
|
|
if (HLVERCALL.contains("commits:")) {
|
2024-04-05 05:40:44 +02:00
|
|
|
hlcommits = HLVERCALL.substr(HLVERCALL.find("commits:") + 9);
|
2024-04-05 01:44:21 +02:00
|
|
|
hlcommits = hlcommits.substr(0, hlcommits.find(" "));
|
|
|
|
}
|
|
|
|
|
|
|
|
int commits = 0;
|
|
|
|
try {
|
|
|
|
commits = std::stoi(hlcommits);
|
|
|
|
} catch (...) { ; }
|
|
|
|
|
2023-12-07 11:41:09 +01:00
|
|
|
if (m_bVerbose)
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("{}", verboseString("parsed commit {} at branch {} on {}, commits {}", hlcommit, hlbranch, hldate, commits));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-04-10 18:33:50 +02:00
|
|
|
ver = SHyprlandVersion{hlbranch, hlcommit, hldate, commits};
|
2023-12-07 11:41:09 +01:00
|
|
|
return ver;
|
|
|
|
}
|
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
bool CPluginManager::createSafeDirectory(const std::string& path) {
|
|
|
|
if (path.empty() || !path.starts_with("/tmp"))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (std::filesystem::exists(path))
|
|
|
|
std::filesystem::remove_all(path);
|
|
|
|
|
|
|
|
if (std::filesystem::exists(path))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (mkdir(path.c_str(), S_IRWXU) < 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-03-06 13:01:04 +01:00
|
|
|
bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& rev) {
|
2024-02-01 20:38:43 +01:00
|
|
|
const auto HLVER = getHyprlandVersion();
|
|
|
|
|
2024-04-16 17:58:57 +02:00
|
|
|
if (!hasDeps()) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio"));
|
2024-04-16 17:58:57 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-12-07 11:41:09 +01:00
|
|
|
if (DataState::pluginRepoExists(url)) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Repository already installed."));
|
2023-12-07 11:41:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto GLOBALSTATE = DataState::getGlobalState();
|
|
|
|
if (!GLOBALSTATE.dontWarnInstall) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("{}!{} Disclaimer: {}", Colors::YELLOW, Colors::RED, Colors::RESET);
|
|
|
|
std::println("plugins, especially not official, have no guarantee of stability, availablity or security.\n"
|
|
|
|
"Run them at your own risk.\n"
|
|
|
|
"This message will not appear again.");
|
2023-12-07 11:41:09 +01:00
|
|
|
GLOBALSTATE.dontWarnInstall = true;
|
|
|
|
DataState::updateGlobalState(GLOBALSTATE);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << Colors::GREEN << "✔" << Colors::RESET << Colors::RED << " adding a new plugin repository " << Colors::RESET << "from " << url << "\n " << Colors::RED
|
|
|
|
<< "MAKE SURE" << Colors::RESET << " that you trust the authors. " << Colors::RED << "DO NOT" << Colors::RESET
|
|
|
|
<< " install random plugins without verifying the code and author.\n "
|
|
|
|
<< "Are you sure? [Y/n] ";
|
|
|
|
std::fflush(stdout);
|
|
|
|
std::string input;
|
|
|
|
std::getline(std::cin, input);
|
|
|
|
|
|
|
|
if (input.size() > 0 && input[0] != 'Y' && input[0] != 'y') {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "Aborting.");
|
2023-12-07 11:41:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CProgressBar progress;
|
|
|
|
progress.m_iMaxSteps = 5;
|
|
|
|
progress.m_iSteps = 0;
|
|
|
|
progress.m_szCurrentMessage = "Cloning the plugin repository";
|
|
|
|
|
|
|
|
progress.print();
|
|
|
|
|
|
|
|
if (!std::filesystem::exists("/tmp/hyprpm")) {
|
|
|
|
std::filesystem::create_directory("/tmp/hyprpm");
|
|
|
|
std::filesystem::permissions("/tmp/hyprpm", std::filesystem::perms::all, std::filesystem::perm_options::replace);
|
2024-04-28 21:27:44 +02:00
|
|
|
} else if (!std::filesystem::is_directory("/tmp/hyprpm")) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "\n{}", failureString("Could not prepare working dir for hyprpm"));
|
2024-04-28 21:27:44 +02:00
|
|
|
return false;
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
const std::string USERNAME = getpwuid(getuid())->pw_name;
|
|
|
|
|
|
|
|
m_szWorkingPluginDirectory = "/tmp/hyprpm/" + USERNAME;
|
|
|
|
|
|
|
|
if (!createSafeDirectory(m_szWorkingPluginDirectory)) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "\n{}", failureString("Could not prepare working dir for repo"));
|
2024-04-28 21:27:44 +02:00
|
|
|
return false;
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(infoString("Cloning {}", url));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive " + url + " " + USERNAME);
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. shell returned:\n{}", ret));
|
2023-12-07 11:41:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-03-06 13:01:04 +01:00
|
|
|
if (!rev.empty()) {
|
2024-04-28 21:27:44 +02:00
|
|
|
std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules " + rev);
|
2024-03-06 13:01:04 +01:00
|
|
|
if (ret.compare(0, 6, "fatal:") == 0) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "\n{}", failureString("Could not check out revision {}. shell returned:\n{}", rev, ret));
|
2024-03-06 13:01:04 +01:00
|
|
|
return false;
|
|
|
|
}
|
2024-07-21 16:42:43 +02:00
|
|
|
ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " submodule update --init");
|
|
|
|
if (m_bVerbose)
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("{}", verboseString("git submodule update --init returned: {}", ret));
|
2024-03-06 13:01:04 +01:00
|
|
|
}
|
|
|
|
|
2023-12-07 11:41:09 +01:00
|
|
|
progress.m_iSteps = 1;
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("cloned"));
|
2023-12-07 11:41:09 +01:00
|
|
|
progress.m_szCurrentMessage = "Reading the manifest";
|
|
|
|
progress.print();
|
|
|
|
|
|
|
|
std::unique_ptr<CManifest> pManifest;
|
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprpm.toml")) {
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("found hyprpm manifest"));
|
2024-04-28 21:27:44 +02:00
|
|
|
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRPM, m_szWorkingPluginDirectory + "/hyprpm.toml");
|
|
|
|
} else if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprload.toml")) {
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("found hyprload manifest"));
|
2024-04-28 21:27:44 +02:00
|
|
|
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRLOAD, m_szWorkingPluginDirectory + "/hyprload.toml");
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!pManifest) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "\n{}", failureString("The provided plugin repository does not have a valid manifest"));
|
2023-12-07 11:41:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pManifest->m_bGood) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "\n{}", failureString("The provided plugin repository has a corrupted manifest"));
|
2023-12-07 11:41:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
progress.m_iSteps = 2;
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("parsed manifest, found " + std::to_string(pManifest->m_vPlugins.size()) + " plugins:"));
|
2024-08-26 20:24:30 +02:00
|
|
|
for (auto const& pl : pManifest->m_vPlugins) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::string message = "→ " + pl.name + " by ";
|
2024-08-26 20:24:30 +02:00
|
|
|
for (auto const& a : pl.authors) {
|
2023-12-07 11:41:09 +01:00
|
|
|
message += a + ", ";
|
|
|
|
}
|
|
|
|
if (pl.authors.size() > 0) {
|
|
|
|
message.pop_back();
|
|
|
|
message.pop_back();
|
|
|
|
}
|
|
|
|
message += " version " + pl.version;
|
|
|
|
progress.printMessageAbove(message);
|
|
|
|
}
|
2024-02-01 20:38:43 +01:00
|
|
|
|
|
|
|
if (!pManifest->m_sRepository.commitPins.empty()) {
|
|
|
|
// check commit pins
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_sRepository.commitPins.size()));
|
2024-02-01 20:38:43 +01:00
|
|
|
|
2024-08-26 20:24:30 +02:00
|
|
|
for (auto const& [hl, plugin] : pManifest->m_sRepository.commitPins) {
|
2024-02-01 20:38:43 +01:00
|
|
|
if (hl != HLVER.hash)
|
|
|
|
continue;
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("commit pin {} matched hl, resetting", plugin));
|
2024-02-01 20:38:43 +01:00
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
execAndGet("cd " + m_szWorkingPluginDirectory + " && git reset --hard --recurse-submodules " + plugin);
|
2024-07-21 16:42:43 +02:00
|
|
|
|
|
|
|
ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " submodule update --init");
|
|
|
|
if (m_bVerbose)
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("{}", verboseString("git submodule update --init returned: {}", ret));
|
2024-07-21 16:42:43 +02:00
|
|
|
|
|
|
|
break;
|
2024-02-01 20:38:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-07 11:41:09 +01:00
|
|
|
progress.m_szCurrentMessage = "Verifying headers";
|
|
|
|
progress.print();
|
|
|
|
|
|
|
|
const auto HEADERSSTATUS = headersValid();
|
|
|
|
|
|
|
|
if (HEADERSSTATUS != HEADERS_OK) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("\n{}", headerError(HEADERSSTATUS));
|
2023-12-07 11:41:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
progress.m_iSteps = 3;
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("Hyprland headers OK"));
|
2023-12-07 11:41:09 +01:00
|
|
|
progress.m_szCurrentMessage = "Building plugin(s)";
|
|
|
|
progress.print();
|
|
|
|
|
|
|
|
for (auto& p : pManifest->m_vPlugins) {
|
|
|
|
std::string out;
|
|
|
|
|
2024-04-05 04:00:34 +02:00
|
|
|
if (p.since > HLVER.commits && HLVER.commits >= 1 /* for --depth 1 clones, we can't check this. */) {
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(failureString("Not building {}: your Hyprland version is too old.\n", p.name));
|
2024-04-05 01:44:21 +02:00
|
|
|
p.failed = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(infoString("Building {}", p.name));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-08-26 20:24:30 +02:00
|
|
|
for (auto const& bs : p.buildSteps) {
|
2024-10-13 14:23:33 +02:00
|
|
|
const std::string& cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs);
|
2024-02-01 20:38:43 +01:00
|
|
|
out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n";
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
|
2024-03-19 23:12:26 +01:00
|
|
|
if (m_bVerbose)
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("{}", verboseString("shell returned: " + out));
|
2024-03-19 23:12:26 +01:00
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/" + p.output)) {
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(failureString("Plugin {} failed to build.\n"
|
|
|
|
" This likely means that the plugin is either outdated, not yet available for your version, or broken.\n"
|
|
|
|
" If you are on -git, update first\n"
|
|
|
|
" Try re-running with -v to see more verbose output.\n",
|
|
|
|
p.name));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-01-07 18:15:51 +01:00
|
|
|
p.failed = true;
|
|
|
|
continue;
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("built {} into {}", p.name, p.output));
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("all plugins built"));
|
2023-12-07 11:41:09 +01:00
|
|
|
progress.m_iSteps = 4;
|
|
|
|
progress.m_szCurrentMessage = "Installing repository";
|
|
|
|
progress.print();
|
|
|
|
|
|
|
|
// add repo toml to DataState
|
|
|
|
SPluginRepository repo;
|
2024-04-28 21:27:44 +02:00
|
|
|
std::string repohash = execAndGet("cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD");
|
2023-12-07 11:41:09 +01:00
|
|
|
if (repohash.length() > 0)
|
|
|
|
repohash.pop_back();
|
|
|
|
repo.name = pManifest->m_sRepository.name.empty() ? url.substr(url.find_last_of('/') + 1) : pManifest->m_sRepository.name;
|
|
|
|
repo.url = url;
|
2024-03-06 13:01:04 +01:00
|
|
|
repo.rev = rev;
|
2023-12-07 11:41:09 +01:00
|
|
|
repo.hash = repohash;
|
2024-08-26 20:24:30 +02:00
|
|
|
for (auto const& p : pManifest->m_vPlugins) {
|
2024-04-28 21:27:44 +02:00
|
|
|
repo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, false, p.failed});
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
DataState::addNewPluginRepo(repo);
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("installed repository"));
|
|
|
|
progress.printMessageAbove(successString("you can now enable the plugin(s) with hyprpm enable"));
|
2023-12-07 11:41:09 +01:00
|
|
|
progress.m_iSteps = 5;
|
|
|
|
progress.m_szCurrentMessage = "Done!";
|
|
|
|
progress.print();
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
std::print("\n");
|
2023-12-07 11:41:09 +01:00
|
|
|
|
|
|
|
// remove build files
|
2024-04-28 21:27:44 +02:00
|
|
|
std::filesystem::remove_all(m_szWorkingPluginDirectory);
|
2023-12-07 11:41:09 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::removePluginRepo(const std::string& urlOrName) {
|
|
|
|
if (!DataState::pluginRepoExists(urlOrName)) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "\n{}", failureString("Could not remove the repository. Repository is not installed."));
|
2023-12-07 11:41:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << Colors::YELLOW << "!" << Colors::RESET << Colors::RED << " removing a plugin repository: " << Colors::RESET << urlOrName << "\n "
|
|
|
|
<< "Are you sure? [Y/n] ";
|
|
|
|
std::fflush(stdout);
|
|
|
|
std::string input;
|
|
|
|
std::getline(std::cin, input);
|
|
|
|
|
|
|
|
if (input.size() > 0 && input[0] != 'Y' && input[0] != 'y') {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("Aborting.");
|
2023-12-07 11:41:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
DataState::removePluginRepo(urlOrName);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
eHeadersErrors CPluginManager::headersValid() {
|
|
|
|
const auto HLVER = getHyprlandVersion();
|
|
|
|
|
2024-02-01 20:38:43 +01:00
|
|
|
if (!std::filesystem::exists(DataState::getHeadersPath() + "/share/pkgconfig/hyprland.pc"))
|
|
|
|
return HEADERS_MISSING;
|
|
|
|
|
2023-12-07 11:41:09 +01:00
|
|
|
// find headers commit
|
2024-10-13 14:23:33 +02:00
|
|
|
const std::string& cmd = std::format("PKG_CONFIG_PATH=\"{}/share/pkgconfig\" pkgconf --cflags --keep-system-cflags hyprland", DataState::getHeadersPath());
|
|
|
|
auto headers = execAndGet(cmd.c_str());
|
2023-12-07 11:41:09 +01:00
|
|
|
|
|
|
|
if (!headers.contains("-I/"))
|
|
|
|
return HEADERS_MISSING;
|
|
|
|
|
|
|
|
headers.pop_back(); // pop newline
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
std::string verHeader;
|
2023-12-07 11:41:09 +01:00
|
|
|
|
|
|
|
while (!headers.empty()) {
|
|
|
|
const auto PATH = headers.substr(0, headers.find(" -I/", 3));
|
|
|
|
|
|
|
|
if (headers.find(" -I/", 3) != std::string::npos)
|
|
|
|
headers = headers.substr(headers.find("-I/", 3));
|
|
|
|
else
|
|
|
|
headers = "";
|
|
|
|
|
2024-07-21 13:09:54 +02:00
|
|
|
if (PATH.ends_with("protocols"))
|
2023-12-07 11:41:09 +01:00
|
|
|
continue;
|
|
|
|
|
2024-06-11 17:17:45 +02:00
|
|
|
verHeader = trim(PATH.substr(2)) + "/hyprland/src/version.h";
|
2023-12-07 11:41:09 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verHeader.empty())
|
|
|
|
return HEADERS_CORRUPTED;
|
|
|
|
|
|
|
|
// read header
|
|
|
|
std::ifstream ifs(verHeader);
|
|
|
|
if (!ifs.good())
|
|
|
|
return HEADERS_CORRUPTED;
|
|
|
|
|
|
|
|
std::string verHeaderContent((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
|
|
|
|
ifs.close();
|
|
|
|
|
2024-03-30 04:09:22 +01:00
|
|
|
const auto HASHPOS = verHeaderContent.find("#define GIT_COMMIT_HASH");
|
|
|
|
|
|
|
|
if (HASHPOS == std::string::npos || HASHPOS + 23 >= verHeaderContent.length())
|
|
|
|
return HEADERS_CORRUPTED;
|
|
|
|
|
|
|
|
std::string hash = verHeaderContent.substr(HASHPOS + 23);
|
2023-12-07 11:41:09 +01:00
|
|
|
hash = hash.substr(0, hash.find_first_of('\n'));
|
|
|
|
hash = hash.substr(hash.find_first_of('"') + 1);
|
|
|
|
hash = hash.substr(0, hash.find_first_of('"'));
|
|
|
|
|
2024-01-29 11:30:31 +01:00
|
|
|
if (hash != HLVER.hash)
|
2023-12-07 11:41:09 +01:00
|
|
|
return HEADERS_MISMATCHED;
|
|
|
|
|
|
|
|
return HEADERS_OK;
|
|
|
|
}
|
|
|
|
|
2024-01-28 03:04:35 +01:00
|
|
|
bool CPluginManager::updateHeaders(bool force) {
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-02-01 20:38:43 +01:00
|
|
|
DataState::ensureStateStoreExists();
|
|
|
|
|
2023-12-07 11:41:09 +01:00
|
|
|
const auto HLVER = getHyprlandVersion();
|
|
|
|
|
2024-04-16 17:58:57 +02:00
|
|
|
if (!hasDeps()) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("\n{}", failureString("Could not update. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio"));
|
2024-04-16 17:58:57 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-12-07 11:41:09 +01:00
|
|
|
if (!std::filesystem::exists("/tmp/hyprpm")) {
|
|
|
|
std::filesystem::create_directory("/tmp/hyprpm");
|
|
|
|
std::filesystem::permissions("/tmp/hyprpm", std::filesystem::perms::all, std::filesystem::perm_options::replace);
|
|
|
|
}
|
|
|
|
|
2024-01-28 03:04:35 +01:00
|
|
|
if (!force && headersValid() == HEADERS_OK) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("\n{}", successString("Headers up to date."));
|
2023-12-07 11:41:09 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
CProgressBar progress;
|
|
|
|
progress.m_iMaxSteps = 5;
|
|
|
|
progress.m_iSteps = 0;
|
|
|
|
progress.m_szCurrentMessage = "Cloning the hyprland repository";
|
|
|
|
progress.print();
|
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
const std::string USERNAME = getpwuid(getuid())->pw_name;
|
|
|
|
const auto WORKINGDIR = "/tmp/hyprpm/hyprland-" + USERNAME;
|
|
|
|
|
|
|
|
if (!createSafeDirectory(WORKINGDIR)) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("\n{}", failureString("Could not prepare working dir for hl"));
|
2024-04-28 21:27:44 +02:00
|
|
|
return false;
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(statusString("!", Colors::YELLOW, "Cloning https://github.com/hyprwm/Hyprland, this might take a moment."));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-10-10 12:01:13 +02:00
|
|
|
const bool bShallow = (HLVER.branch == "main") && !m_bNoShallow;
|
2024-05-12 01:53:50 +02:00
|
|
|
|
2024-04-16 16:41:11 +02:00
|
|
|
// let us give a bit of leg-room for shallowing
|
|
|
|
// due to timezones, etc.
|
2024-10-11 13:19:16 +02:00
|
|
|
const std::string SHALLOW_DATE = trim(HLVER.date).empty() ? "" : execAndGet("LC_TIME=\"en_US.UTF-8\" date --date='" + HLVER.date + " - 1 weeks' '+%a %b %d %H:%M:%S %Y'");
|
2024-04-16 16:41:11 +02:00
|
|
|
|
2024-05-12 01:53:50 +02:00
|
|
|
if (m_bVerbose && bShallow)
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(verboseString("will shallow since: {}", SHALLOW_DATE));
|
2024-04-16 16:41:11 +02:00
|
|
|
|
2024-05-12 01:53:50 +02:00
|
|
|
std::string ret =
|
2024-10-13 14:23:33 +02:00
|
|
|
execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/hyprwm/Hyprland hyprland-" + USERNAME + (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : ""));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
if (!std::filesystem::exists(WORKINGDIR)) {
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(failureString("Clone failed. Retrying without shallow."));
|
2024-04-28 21:27:44 +02:00
|
|
|
ret = execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/hyprwm/hyprland hyprland-" + USERNAME);
|
2024-04-15 02:57:04 +02:00
|
|
|
}
|
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
if (!std::filesystem::exists(WORKINGDIR + "/.git")) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "\n{}", failureString("Could not clone the Hyprland repository. shell returned:\n{}", ret));
|
2023-12-07 11:41:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("Hyprland cloned"));
|
2023-12-07 11:41:09 +01:00
|
|
|
progress.m_iSteps = 2;
|
|
|
|
progress.m_szCurrentMessage = "Checking out sources";
|
|
|
|
progress.print();
|
|
|
|
|
2024-07-30 12:05:23 +02:00
|
|
|
if (m_bVerbose)
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(verboseString("will run: cd {} && git checkout {} 2>&1", WORKINGDIR, HLVER.hash));
|
2024-07-30 12:05:23 +02:00
|
|
|
|
2024-07-28 13:03:30 +02:00
|
|
|
ret = execAndGet("cd " + WORKINGDIR + " && git checkout " + HLVER.hash + " 2>&1");
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-07-30 12:05:23 +02:00
|
|
|
if (ret.contains("fatal: unable to read tree")) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "\n{}",
|
|
|
|
failureString("Could not checkout the running Hyprland commit. If you are on -git, try updating.\n"
|
|
|
|
"You can also try re-running hyprpm update with --no-shallow."));
|
2024-07-30 12:05:23 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-12-07 11:41:09 +01:00
|
|
|
if (m_bVerbose)
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(verboseString("git returned (co): {}", ret));
|
2024-04-15 19:49:19 +02:00
|
|
|
|
2024-07-30 12:11:38 +02:00
|
|
|
ret = execAndGet("cd " + WORKINGDIR + " ; git rm subprojects/tracy ; git submodule update --init 2>&1 ; git reset --hard --recurse-submodules " + HLVER.hash);
|
2024-04-15 19:49:19 +02:00
|
|
|
|
|
|
|
if (m_bVerbose)
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(verboseString("git returned (rs): {}", ret));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("checked out to running ver"));
|
2023-12-07 11:41:09 +01:00
|
|
|
progress.m_iSteps = 3;
|
|
|
|
progress.m_szCurrentMessage = "Building Hyprland";
|
|
|
|
progress.print();
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(statusString("!", Colors::YELLOW, "configuring Hyprland"));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-02-01 20:38:43 +01:00
|
|
|
if (m_bVerbose)
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(verboseString("setting PREFIX for cmake to {}", DataState::getHeadersPath()));
|
2024-02-01 20:38:43 +01:00
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
ret = execAndGet(std::format("cd {} && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=\"{}\" -S . -B ./build -G Ninja", WORKINGDIR,
|
|
|
|
DataState::getHeadersPath()));
|
2024-01-28 04:00:05 +01:00
|
|
|
if (m_bVerbose)
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(verboseString("cmake returned: {}", ret));
|
2024-01-28 04:00:05 +01:00
|
|
|
|
2024-06-13 11:33:20 +02:00
|
|
|
if (ret.contains("CMake Error at")) {
|
2024-06-09 09:42:14 +02:00
|
|
|
// missing deps, let the user know.
|
2024-06-13 11:33:20 +02:00
|
|
|
std::string missing = ret.substr(ret.find("CMake Error at"));
|
|
|
|
missing = ret.substr(ret.find_first_of('\n') + 1);
|
|
|
|
missing = missing.substr(0, missing.find("-- Configuring incomplete"));
|
2024-06-09 09:42:14 +02:00
|
|
|
missing = missing.substr(0, missing.find_last_of('\n'));
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "\n{}",
|
|
|
|
failureString("Could not configure the hyprland source, cmake complained:\n{}\n\n"
|
|
|
|
"This likely means that you are missing the above dependencies or they are out of date.",
|
|
|
|
missing));
|
2024-06-09 09:42:14 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("configured Hyprland"));
|
2023-12-07 11:41:09 +01:00
|
|
|
progress.m_iSteps = 4;
|
|
|
|
progress.m_szCurrentMessage = "Installing sources";
|
|
|
|
progress.print();
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
const std::string& cmd =
|
2024-04-28 22:32:22 +02:00
|
|
|
std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" {}/Makefile && cd {} && make installheaders", DataState::getHeadersPath(), WORKINGDIR, WORKINGDIR);
|
2024-02-01 20:38:43 +01:00
|
|
|
if (m_bVerbose)
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(verboseString("installation will run: {}", cmd));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-02-01 20:38:43 +01:00
|
|
|
ret = execAndGet(cmd);
|
2023-12-07 11:41:09 +01:00
|
|
|
|
|
|
|
if (m_bVerbose)
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("{}", verboseString("installer returned: {}", ret));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
|
|
|
// remove build files
|
2024-04-28 21:27:44 +02:00
|
|
|
std::filesystem::remove_all(WORKINGDIR);
|
2023-12-07 11:41:09 +01:00
|
|
|
|
|
|
|
auto HEADERSVALID = headersValid();
|
|
|
|
if (HEADERSVALID == HEADERS_OK) {
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("installed headers"));
|
2023-12-07 11:41:09 +01:00
|
|
|
progress.m_iSteps = 5;
|
|
|
|
progress.m_szCurrentMessage = "Done!";
|
|
|
|
progress.print();
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
std::print("\n");
|
2023-12-07 11:41:09 +01:00
|
|
|
} else {
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(failureString("failed to install headers with error code {} ({})", (int)HEADERSVALID, headerErrorShort(HEADERSVALID)));
|
2023-12-07 11:41:09 +01:00
|
|
|
progress.m_iSteps = 5;
|
|
|
|
progress.m_szCurrentMessage = "Failed";
|
|
|
|
progress.print();
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
std::print(stderr, "\n\n{}", headerError(HEADERSVALID));
|
2023-12-13 03:32:57 +01:00
|
|
|
|
2023-12-07 11:41:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
|
|
|
if (headersValid() != HEADERS_OK) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("{}", failureString("headers are not up-to-date, please run hyprpm update."));
|
2023-12-07 11:41:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto REPOS = DataState::getAllRepositories();
|
|
|
|
|
|
|
|
if (REPOS.size() < 1) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("{}", failureString("No repos to update."));
|
2023-12-07 11:41:09 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto HLVER = getHyprlandVersion();
|
|
|
|
|
|
|
|
CProgressBar progress;
|
2024-01-30 00:36:55 +01:00
|
|
|
progress.m_iMaxSteps = REPOS.size() * 2 + 2;
|
2023-12-07 11:41:09 +01:00
|
|
|
progress.m_iSteps = 0;
|
|
|
|
progress.m_szCurrentMessage = "Updating repositories";
|
|
|
|
progress.print();
|
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
const std::string USERNAME = getpwuid(getuid())->pw_name;
|
|
|
|
m_szWorkingPluginDirectory = "/tmp/hyprpm/" + USERNAME;
|
|
|
|
|
2024-08-26 20:24:30 +02:00
|
|
|
for (auto const& repo : REPOS) {
|
2023-12-07 11:41:09 +01:00
|
|
|
bool update = forceUpdateAll;
|
|
|
|
|
|
|
|
progress.m_iSteps++;
|
|
|
|
progress.m_szCurrentMessage = "Updating " + repo.name;
|
|
|
|
progress.print();
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(infoString("checking for updates for {}", repo.name));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
createSafeDirectory(m_szWorkingPluginDirectory);
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(infoString("Cloning {}", repo.url));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive " + repo.url + " " + USERNAME);
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("{}", failureString("could not clone repo: shell returned: {}", ret));
|
2023-12-07 11:41:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-03-06 13:01:04 +01:00
|
|
|
if (!repo.rev.empty()) {
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(infoString("Plugin has revision set, resetting: {}", repo.rev));
|
2024-03-06 13:01:04 +01:00
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules " + repo.rev);
|
2024-03-06 13:01:04 +01:00
|
|
|
if (ret.compare(0, 6, "fatal:") == 0) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "\n{}", failureString("could not check out revision {}: shell returned:\n{}", repo.rev, ret));
|
|
|
|
|
2024-03-06 13:01:04 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-07 11:41:09 +01:00
|
|
|
if (!update) {
|
|
|
|
// check if git has updates
|
2024-04-28 21:27:44 +02:00
|
|
|
std::string hash = execAndGet("cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD");
|
2023-12-07 11:41:09 +01:00
|
|
|
if (!hash.empty())
|
|
|
|
hash.pop_back();
|
|
|
|
|
|
|
|
update = update || hash != repo.hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!update) {
|
2024-04-28 21:27:44 +02:00
|
|
|
std::filesystem::remove_all(m_szWorkingPluginDirectory);
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("repository {} is up-to-date.", repo.name));
|
2023-12-07 11:41:09 +01:00
|
|
|
progress.m_iSteps++;
|
|
|
|
progress.print();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we need to update
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("repository {} has updates.", repo.name));
|
|
|
|
progress.printMessageAbove(infoString("Building {}", repo.name));
|
2023-12-07 11:41:09 +01:00
|
|
|
progress.m_iSteps++;
|
|
|
|
progress.print();
|
|
|
|
|
|
|
|
std::unique_ptr<CManifest> pManifest;
|
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprpm.toml")) {
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("found hyprpm manifest"));
|
2024-04-28 21:27:44 +02:00
|
|
|
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRPM, m_szWorkingPluginDirectory + "/hyprpm.toml");
|
|
|
|
} else if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprload.toml")) {
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("found hyprload manifest"));
|
2024-04-28 21:27:44 +02:00
|
|
|
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRLOAD, m_szWorkingPluginDirectory + "/hyprload.toml");
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!pManifest) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "\n{}", failureString("The provided plugin repository does not have a valid manifest"));
|
2023-12-07 11:41:09 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pManifest->m_bGood) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "\n{}", failureString("The provided plugin repository has a corrupted manifest"));
|
2023-12-07 11:41:09 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2024-03-06 13:01:04 +01:00
|
|
|
if (repo.rev.empty() && !pManifest->m_sRepository.commitPins.empty()) {
|
|
|
|
// check commit pins unless a revision is specified
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_sRepository.commitPins.size()));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-08-26 20:24:30 +02:00
|
|
|
for (auto const& [hl, plugin] : pManifest->m_sRepository.commitPins) {
|
2023-12-07 11:41:09 +01:00
|
|
|
if (hl != HLVER.hash)
|
|
|
|
continue;
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("commit pin {} matched hl, resetting", plugin));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
execAndGet("cd " + m_szWorkingPluginDirectory + " && git reset --hard --recurse-submodules " + plugin);
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& p : pManifest->m_vPlugins) {
|
|
|
|
std::string out;
|
|
|
|
|
2024-04-15 02:57:04 +02:00
|
|
|
if (p.since > HLVER.commits && HLVER.commits >= 1000 /* for shallow clones, we can't check this. 1000 is an arbitrary number I chose. */) {
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(failureString("Not building {}: your Hyprland version is too old.\n", p.name));
|
2024-04-05 01:44:21 +02:00
|
|
|
p.failed = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(infoString("Building {}", p.name));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-08-26 20:24:30 +02:00
|
|
|
for (auto const& bs : p.buildSteps) {
|
2024-10-13 14:23:33 +02:00
|
|
|
const std::string& cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs);
|
2024-02-01 20:38:43 +01:00
|
|
|
out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n";
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
|
2024-03-19 23:12:26 +01:00
|
|
|
if (m_bVerbose)
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("{}", verboseString("shell returned: {}", out));
|
2024-03-19 23:12:26 +01:00
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/" + p.output)) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr,
|
|
|
|
"\n{}\n"
|
|
|
|
" This likely means that the plugin is either outdated, not yet available for your version, or broken.\n"
|
|
|
|
"If you are on -git, update first.\n"
|
|
|
|
"Try re-running with -v to see more verbose output.",
|
2024-10-16 23:13:59 +02:00
|
|
|
failureString("Plugin {} failed to build.", p.name));
|
2024-04-05 01:44:21 +02:00
|
|
|
p.failed = true;
|
|
|
|
continue;
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("built {} into {}", p.name, p.output));
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// add repo toml to DataState
|
|
|
|
SPluginRepository newrepo = repo;
|
|
|
|
newrepo.plugins.clear();
|
2024-04-28 21:27:44 +02:00
|
|
|
execAndGet("cd " + m_szWorkingPluginDirectory +
|
|
|
|
" && git pull --recurse-submodules && git reset --hard --recurse-submodules"); // repo hash in the state.toml has to match head and not any pin
|
|
|
|
std::string repohash = execAndGet("cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD");
|
2023-12-07 11:41:09 +01:00
|
|
|
if (repohash.length() > 0)
|
|
|
|
repohash.pop_back();
|
|
|
|
newrepo.hash = repohash;
|
2024-08-26 20:24:30 +02:00
|
|
|
for (auto const& p : pManifest->m_vPlugins) {
|
2023-12-07 11:41:09 +01:00
|
|
|
const auto OLDPLUGINIT = std::find_if(repo.plugins.begin(), repo.plugins.end(), [&](const auto& other) { return other.name == p.name; });
|
2024-04-28 21:27:44 +02:00
|
|
|
newrepo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false});
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
DataState::removePluginRepo(newrepo.name);
|
|
|
|
DataState::addNewPluginRepo(newrepo);
|
|
|
|
|
2024-04-28 21:27:44 +02:00
|
|
|
std::filesystem::remove_all(m_szWorkingPluginDirectory);
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
progress.printMessageAbove(successString("updated {}", repo.name));
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
|
2024-01-30 00:36:55 +01:00
|
|
|
progress.m_iSteps++;
|
|
|
|
progress.m_szCurrentMessage = "Updating global state...";
|
|
|
|
progress.print();
|
|
|
|
|
|
|
|
auto GLOBALSTATE = DataState::getGlobalState();
|
|
|
|
GLOBALSTATE.headersHashCompiled = HLVER.hash;
|
|
|
|
DataState::updateGlobalState(GLOBALSTATE);
|
|
|
|
|
2023-12-07 11:41:09 +01:00
|
|
|
progress.m_iSteps++;
|
|
|
|
progress.m_szCurrentMessage = "Done!";
|
|
|
|
progress.print();
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
std::print("\n");
|
2023-12-07 11:41:09 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::enablePlugin(const std::string& name) {
|
|
|
|
bool ret = DataState::setPluginEnabled(name, true);
|
|
|
|
if (ret)
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("{}", successString("Enabled {}", name));
|
2023-12-07 11:41:09 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::disablePlugin(const std::string& name) {
|
|
|
|
bool ret = DataState::setPluginEnabled(name, false);
|
|
|
|
if (ret)
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("{}", successString("Disabled {}", name));
|
2023-12-07 11:41:09 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
|
|
|
|
if (headersValid() != HEADERS_OK) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "\n{}", failureString("headers are not up-to-date, please run hyprpm update."));
|
2023-12-07 11:41:09 +01:00
|
|
|
return LOADSTATE_HEADERS_OUTDATED;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto HOME = getenv("HOME");
|
|
|
|
const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
|
|
|
if (!HOME || !HIS) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(stderr, "PluginManager: no $HOME or HIS");
|
2023-12-07 11:41:09 +01:00
|
|
|
return LOADSTATE_FAIL;
|
|
|
|
}
|
|
|
|
const auto HYPRPMPATH = DataState::getDataStatePath() + "/";
|
|
|
|
|
|
|
|
auto pluginLines = execAndGet("hyprctl plugins list | grep Plugin");
|
|
|
|
|
|
|
|
std::vector<std::string> loadedPlugins;
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("{}", successString("Ensuring plugin load state"));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
|
|
|
// iterate line by line
|
|
|
|
while (!pluginLines.empty()) {
|
2024-10-13 14:23:33 +02:00
|
|
|
auto plLine = pluginLines.substr(0, pluginLines.find('\n'));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
if (pluginLines.find('\n') != std::string::npos)
|
|
|
|
pluginLines = pluginLines.substr(pluginLines.find('\n') + 1);
|
2023-12-07 11:41:09 +01:00
|
|
|
else
|
|
|
|
pluginLines = "";
|
|
|
|
|
|
|
|
if (plLine.back() != ':')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
plLine = plLine.substr(7);
|
|
|
|
plLine = plLine.substr(0, plLine.find(" by "));
|
|
|
|
|
|
|
|
loadedPlugins.push_back(plLine);
|
|
|
|
}
|
|
|
|
|
|
|
|
// get state
|
|
|
|
const auto REPOS = DataState::getAllRepositories();
|
|
|
|
|
|
|
|
auto enabled = [REPOS](const std::string& plugin) -> bool {
|
2024-08-26 20:24:30 +02:00
|
|
|
for (auto const& r : REPOS) {
|
|
|
|
for (auto const& p : r.plugins) {
|
2023-12-07 11:41:09 +01:00
|
|
|
if (p.name == plugin && p.enabled)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
auto repoForName = [REPOS](const std::string& name) -> std::string {
|
2024-08-26 20:24:30 +02:00
|
|
|
for (auto const& r : REPOS) {
|
|
|
|
for (auto const& p : r.plugins) {
|
2023-12-07 11:41:09 +01:00
|
|
|
if (p.name == name)
|
|
|
|
return r.name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "";
|
|
|
|
};
|
|
|
|
|
|
|
|
// unload disabled plugins
|
2024-08-26 20:24:30 +02:00
|
|
|
for (auto const& p : loadedPlugins) {
|
2023-12-07 11:41:09 +01:00
|
|
|
if (!enabled(p)) {
|
|
|
|
// unload
|
|
|
|
loadUnloadPlugin(HYPRPMPATH + repoForName(p) + "/" + p + ".so", false);
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("{}", successString("Unloaded {}", p));
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// load enabled plugins
|
2024-08-26 20:24:30 +02:00
|
|
|
for (auto const& r : REPOS) {
|
|
|
|
for (auto const& p : r.plugins) {
|
2023-12-07 11:41:09 +01:00
|
|
|
if (!p.enabled)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (std::find_if(loadedPlugins.begin(), loadedPlugins.end(), [&](const auto& other) { return other == p.name; }) != loadedPlugins.end())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
loadUnloadPlugin(HYPRPMPATH + repoForName(p.name) + "/" + p.filename, true);
|
2024-10-16 23:13:59 +02:00
|
|
|
std::println("{}", successString("Loaded {}", p.name));
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("{}", successString("Plugin load state ensured"));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
|
|
|
return LOADSTATE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::loadUnloadPlugin(const std::string& path, bool load) {
|
|
|
|
if (load)
|
|
|
|
execAndGet("hyprctl plugin load " + path);
|
|
|
|
else
|
|
|
|
execAndGet("hyprctl plugin unload " + path);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::listAllPlugins() {
|
|
|
|
const auto REPOS = DataState::getAllRepositories();
|
|
|
|
|
2024-08-26 20:24:30 +02:00
|
|
|
for (auto const& r : REPOS) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println("{}", infoString("Repository {}:", r.name));
|
2023-12-07 11:41:09 +01:00
|
|
|
|
2024-08-26 20:24:30 +02:00
|
|
|
for (auto const& p : r.plugins) {
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(" │ Plugin {}", p.name);
|
2024-01-07 18:15:51 +01:00
|
|
|
|
|
|
|
if (!p.failed)
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(" └─ enabled: {}", (p.enabled ? std::string{Colors::GREEN} + "true" : std::string{Colors::RED} + "false"));
|
2024-01-07 18:15:51 +01:00
|
|
|
else
|
2024-10-13 14:23:33 +02:00
|
|
|
std::println(" └─ enabled: {}Plugin failed to build", Colors::RED);
|
|
|
|
|
|
|
|
std::println("{}", Colors::RESET);
|
2023-12-07 11:41:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::notify(const eNotifyIcons icon, uint32_t color, int durationMs, const std::string& message) {
|
|
|
|
execAndGet("hyprctl notify " + std::to_string((int)icon) + " " + std::to_string(durationMs) + " " + std::to_string(color) + " " + message);
|
2023-12-13 03:32:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string CPluginManager::headerError(const eHeadersErrors err) {
|
|
|
|
switch (err) {
|
2024-10-13 14:23:33 +02:00
|
|
|
case HEADERS_CORRUPTED: return failureString("Headers corrupted. 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_MISSING: return failureString("Headers missing. Please run hyprpm update to fix those.\n");
|
2023-12-13 03:32:57 +01:00
|
|
|
case HEADERS_DUPLICATED: {
|
2024-10-13 14:23:33 +02:00
|
|
|
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"
|
|
|
|
"If the above doesn't apply, check your /usr/include and /usr/local/include directories\n and remove all the hyprland headers.\n");
|
2023-12-13 03:32:57 +01:00
|
|
|
}
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
|
2024-10-13 14:23:33 +02:00
|
|
|
return failureString("Unknown header error. Please run hyprpm update to fix those.\n");
|
2023-12-30 15:19:53 +01:00
|
|
|
}
|
2024-04-16 17:58:57 +02:00
|
|
|
|
2024-07-30 11:54:28 +02:00
|
|
|
std::string CPluginManager::headerErrorShort(const eHeadersErrors err) {
|
|
|
|
switch (err) {
|
|
|
|
case HEADERS_CORRUPTED: return "Headers corrupted";
|
|
|
|
case HEADERS_MISMATCHED: return "Headers version mismatched";
|
|
|
|
case HEADERS_NOT_HYPRLAND: return "Not running on Hyprland";
|
|
|
|
case HEADERS_MISSING: return "Headers missing";
|
|
|
|
case HEADERS_DUPLICATED: return "Headers duplicated";
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
return "?";
|
|
|
|
}
|
|
|
|
|
2024-04-16 17:58:57 +02:00
|
|
|
bool CPluginManager::hasDeps() {
|
2024-08-25 13:13:48 +02:00
|
|
|
std::vector<std::string> deps = {"meson", "cpio", "cmake", "pkg-config"};
|
2024-08-26 20:24:30 +02:00
|
|
|
for (auto const& d : deps) {
|
2024-08-25 13:13:48 +02:00
|
|
|
if (!execAndGet("command -v " + d).contains("/"))
|
2024-04-16 17:58:57 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|