added previous version to update results; started to work on build order resolver and mingw-w64 installer
This commit is contained in:
parent
b42ef62cd6
commit
8f32dd0da0
|
@ -1,4 +1,5 @@
|
|||
#include "config.h"
|
||||
#include "manager.h"
|
||||
|
||||
#include <c++utilities/conversion/stringconversion.h>
|
||||
|
||||
|
@ -25,7 +26,9 @@ namespace PackageManagement {
|
|||
*/
|
||||
ConfigArgs::ConfigArgs(ArgumentParser &parser) :
|
||||
helpArg(parser),
|
||||
buildOrderArg("build-order", "b", "calculates the build order to build the specified packages"),
|
||||
serverArg("server", "s", "runs a websocket server providing the web interface with information"),
|
||||
mingwBundleArg("mingw-w64-bundle", "m", "creates an archive with the runtime-relevant files from the specified mingw-w64-packages and their dependencies"),
|
||||
repoindexConfArg("repoindex-conf", "c", "specifies the path of the repo index config file (default is /etc/repoindex.conf"),
|
||||
rootdirArg("root-dir", "r", "specifies the root directory (default is /)"),
|
||||
dbpathArg("db-path", "d", "specifies the pacman database path (default is /var/lib/pacman)"),
|
||||
|
@ -34,10 +37,20 @@ ConfigArgs::ConfigArgs(ArgumentParser &parser) :
|
|||
websocketPortArg("port", "p", "specifies the listening port for the websocket server, default is 1234"),
|
||||
certFileArg("cert-file", string(), "specifies the SSL certificate"),
|
||||
keyFileArg("key-file", string(), "specifies the private SSL key"),
|
||||
insecureArg("insecure", string(), "forces the server to run in insecure mode")
|
||||
insecureArg("insecure", string(), "forces the server to run in insecure mode"),
|
||||
aurArg("aur", "u", "enables/disables AUR queries"),
|
||||
verboseArg("verbose", "v", "be verbose"),
|
||||
outputFileArg("output-file", "f", "specifies the output file")
|
||||
{
|
||||
initializer_list<string> pathValueName = {"path"};
|
||||
const initializer_list<string> pathValueName = {"path"};
|
||||
const initializer_list<string> pkgValueNames = {"package 1", "package 2", "package 3"};
|
||||
buildOrderArg.setDenotesOperation(true);
|
||||
buildOrderArg.setRequiredValueCount(-1);
|
||||
buildOrderArg.setValueNames(pkgValueNames);
|
||||
serverArg.setDenotesOperation(true);
|
||||
mingwBundleArg.setDenotesOperation(true);
|
||||
mingwBundleArg.setRequiredValueCount(-1);
|
||||
mingwBundleArg.setValueNames(pkgValueNames);
|
||||
repoindexConfArg.setCombinable(true);
|
||||
repoindexConfArg.setValueNames(pathValueName);
|
||||
repoindexConfArg.setRequiredValueCount(1);
|
||||
|
@ -63,8 +76,18 @@ ConfigArgs::ConfigArgs(ArgumentParser &parser) :
|
|||
keyFileArg.setValueNames(pathValueName);
|
||||
keyFileArg.setRequiredValueCount(1);
|
||||
insecureArg.setCombinable(true);
|
||||
serverArg.setSecondaryArguments({&rootdirArg, &dbpathArg, &pacmanConfArg, &certFileArg, &keyFileArg, &insecureArg});
|
||||
parser.setMainArguments({&serverArg, &repoindexConfArg, &helpArg});
|
||||
aurArg.setCombinable(true);
|
||||
aurArg.setRequiredValueCount(1);
|
||||
aurArg.setValueNames({"enabled/disabled"});
|
||||
verboseArg.setCombinable(true);
|
||||
outputFileArg.setCombinable(true);
|
||||
outputFileArg.setRequired(true);
|
||||
outputFileArg.setRequiredValueCount(1);
|
||||
outputFileArg.setValueNames({"path"});
|
||||
serverArg.setSecondaryArguments({&rootdirArg, &dbpathArg, &pacmanConfArg, &certFileArg, &keyFileArg, &insecureArg, &aurArg});
|
||||
buildOrderArg.setSecondaryArguments({&aurArg, &verboseArg});
|
||||
mingwBundleArg.setSecondaryArguments({&outputFileArg});
|
||||
parser.setMainArguments({&buildOrderArg, &serverArg, &mingwBundleArg, &repoindexConfArg, &repoindexConfArg, &helpArg});
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -77,12 +100,15 @@ ConfigArgs::ConfigArgs(ArgumentParser &parser) :
|
|||
*/
|
||||
Config::Config() :
|
||||
m_alpmRootDir(QStringLiteral("/")),
|
||||
m_alpmDbPath(QStringLiteral("/var/lib/pacman/")),
|
||||
m_alpmDbPath(QStringLiteral("/var/lib/pacman")),
|
||||
m_pacmanConfFile(QStringLiteral("/etc/pacman.conf")),
|
||||
m_websocketServerListeningAddr(QHostAddress::LocalHost),
|
||||
m_websocketServerListeningPort(1234),
|
||||
m_serverInsecure(false),
|
||||
m_reposFromPacmanConf(false)
|
||||
m_reposFromPacmanConf(false),
|
||||
m_aurEnabled(true),
|
||||
m_verbose(false),
|
||||
m_runServer(false)
|
||||
{}
|
||||
|
||||
/*!
|
||||
|
@ -150,6 +176,8 @@ void Config::loadFromConfigFile(const QString &configFilePath)
|
|||
m_alpmRootDir = alpmObj.value(QStringLiteral("rootDir")).toString(m_alpmRootDir);
|
||||
m_alpmDbPath = alpmObj.value(QStringLiteral("dbPath")).toString(m_alpmDbPath);
|
||||
m_pacmanConfFile = alpmObj.value(QStringLiteral("pacmanConfigFile")).toString(m_pacmanConfFile);
|
||||
auto aurObj = mainObj.value(QStringLiteral("aur")).toObject();
|
||||
m_aurEnabled = aurObj.value(QStringLiteral("enabled")).toBool(m_aurEnabled);
|
||||
auto serverObj = mainObj.value(QStringLiteral("server")).toObject();
|
||||
assign(m_websocketServerListeningAddr, serverObj.value(QStringLiteral("websocketListeningAddr")).toString());
|
||||
assign(m_websocketServerListeningPort, serverObj.value(QStringLiteral("websocketListeningPort")));
|
||||
|
@ -164,7 +192,7 @@ void Config::loadFromConfigFile(const QString &configFilePath)
|
|||
}
|
||||
} else {
|
||||
cerr << "Error: Unable to parse config file \"" << configFilePath.toLocal8Bit().data() << "\": "
|
||||
<< error.errorString().toLocal8Bit().data() << endl;
|
||||
<< error.errorString().toLocal8Bit().data() << " at character " << error.offset << endl;
|
||||
}
|
||||
} else {
|
||||
cerr << "Error: Unable to open config file \"" << configFilePath.toLocal8Bit().data() << "\"." << endl;
|
||||
|
@ -200,6 +228,16 @@ void Config::loadFromArgs(const ConfigArgs &args)
|
|||
assign(m_alpmRootDir, args.rootdirArg);
|
||||
assign(m_alpmDbPath, args.dbpathArg);
|
||||
assign(m_pacmanConfFile, args.pacmanConfArg);
|
||||
if(args.aurArg.isPresent()) {
|
||||
auto val = args.aurArg.values().front();
|
||||
if(val == "enabled") {
|
||||
m_aurEnabled = true;
|
||||
} else if(val == "disabled") {
|
||||
m_aurEnabled = false;
|
||||
} else {
|
||||
cerr << "Warning: The specified value for the argument --aur-enabled is invalid and will be ignored." << endl;
|
||||
}
|
||||
}
|
||||
assign(m_websocketServerListeningPort, args.websocketPortArg);
|
||||
assign(m_serverCertFile, args.certFileArg);
|
||||
assign(m_serverKeyFile, args.keyFileArg);
|
||||
|
@ -207,6 +245,8 @@ void Config::loadFromArgs(const ConfigArgs &args)
|
|||
if(args.websocketAddrArg.isPresent()) {
|
||||
assign(m_websocketServerListeningAddr, QString::fromLocal8Bit(args.websocketAddrArg.values().front().data(), args.websocketAddrArg.values().front().size()));
|
||||
}
|
||||
m_verbose = args.verboseArg.isPresent();
|
||||
m_runServer = args.serverArg.isPresent();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -216,15 +256,22 @@ void RepoEntry::load(const QJsonValue &jsonValue)
|
|||
{
|
||||
auto obj = jsonValue.toObject();
|
||||
m_name = obj.value(QStringLiteral("name")).toString(m_name);
|
||||
m_dbPath = obj.value(QStringLiteral("dbpath")).toString(m_dbPath);
|
||||
m_srcPath = obj.value(QStringLiteral("srcpath")).toString(m_srcPath);
|
||||
m_pkgPath = obj.value(QStringLiteral("pkgpath")).toString(m_pkgPath);
|
||||
m_dataBaseFile = obj.value(QStringLiteral("dataBaseFile")).toString(m_dataBaseFile);
|
||||
m_sourceDir = obj.value(QStringLiteral("sourcesDir")).toString(m_sourceDir);
|
||||
m_packageDir = obj.value(QStringLiteral("packagesDir")).toString(m_packageDir);
|
||||
for(const auto &server : obj.value(QStringLiteral("servers")).toArray()) {
|
||||
auto str = server.toString();
|
||||
if(!str.isEmpty()) {
|
||||
m_servers << str;
|
||||
}
|
||||
}
|
||||
for(const auto &upgradeSources : obj.value(QStringLiteral("upgradeSources")).toArray()) {
|
||||
auto str = upgradeSources.toString();
|
||||
if(!str.isEmpty()) {
|
||||
m_upgradeSources << str;
|
||||
}
|
||||
}
|
||||
m_sigLevel = Manager::parseSigLevel(obj.value(QStringLiteral("sigLevel")).toString().toLocal8Bit().data());
|
||||
}
|
||||
|
||||
} // namespace Alpm
|
||||
|
|
|
@ -18,7 +18,9 @@ class ConfigArgs
|
|||
public:
|
||||
ConfigArgs(ApplicationUtilities::ArgumentParser &parser);
|
||||
ApplicationUtilities::HelpArgument helpArg;
|
||||
ApplicationUtilities::Argument buildOrderArg;
|
||||
ApplicationUtilities::Argument serverArg;
|
||||
ApplicationUtilities::Argument mingwBundleArg;
|
||||
ApplicationUtilities::Argument repoindexConfArg;
|
||||
ApplicationUtilities::Argument rootdirArg;
|
||||
ApplicationUtilities::Argument dbpathArg;
|
||||
|
@ -28,6 +30,9 @@ public:
|
|||
ApplicationUtilities::Argument certFileArg;
|
||||
ApplicationUtilities::Argument keyFileArg;
|
||||
ApplicationUtilities::Argument insecureArg;
|
||||
ApplicationUtilities::Argument aurArg;
|
||||
ApplicationUtilities::Argument verboseArg;
|
||||
ApplicationUtilities::Argument outputFileArg;
|
||||
};
|
||||
|
||||
class Config;
|
||||
|
@ -36,39 +41,48 @@ class RepoEntry
|
|||
{
|
||||
friend class Config;
|
||||
public:
|
||||
RepoEntry();
|
||||
const QString &name() const;
|
||||
const QString &dbPath() const;
|
||||
const QString &srcPath() const;
|
||||
const QString &pkgPath() const;
|
||||
const QString &dataBasePath() const;
|
||||
const QString &sourceDir() const;
|
||||
const QString &packageDir() const;
|
||||
const QStringList &servers() const;
|
||||
const QStringList &upgradeSources() const;
|
||||
int sigLevel() const;
|
||||
void load(const QJsonValue &jsonValue);
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
QString m_dbPath;
|
||||
QString m_srcPath;
|
||||
QString m_pkgPath;
|
||||
QString m_dataBaseFile;
|
||||
QString m_sourceDir;
|
||||
QString m_packageDir;
|
||||
QStringList m_servers;
|
||||
QStringList m_upgradeSources;
|
||||
int m_sigLevel;
|
||||
};
|
||||
|
||||
inline RepoEntry::RepoEntry() :
|
||||
m_sigLevel(0)
|
||||
{}
|
||||
|
||||
inline const QString &RepoEntry::name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
inline const QString &RepoEntry::dbPath() const
|
||||
inline const QString &RepoEntry::dataBasePath() const
|
||||
{
|
||||
return m_dbPath;
|
||||
return m_dataBaseFile;
|
||||
}
|
||||
|
||||
inline const QString &RepoEntry::srcPath() const
|
||||
inline const QString &RepoEntry::sourceDir() const
|
||||
{
|
||||
return m_srcPath;
|
||||
return m_sourceDir;
|
||||
}
|
||||
|
||||
inline const QString &RepoEntry::pkgPath() const
|
||||
inline const QString &RepoEntry::packageDir() const
|
||||
{
|
||||
return m_pkgPath;
|
||||
return m_packageDir;
|
||||
}
|
||||
|
||||
inline const QStringList &RepoEntry::servers() const
|
||||
|
@ -76,6 +90,16 @@ inline const QStringList &RepoEntry::servers() const
|
|||
return m_servers;
|
||||
}
|
||||
|
||||
inline const QStringList &RepoEntry::upgradeSources() const
|
||||
{
|
||||
return m_upgradeSources;
|
||||
}
|
||||
|
||||
inline int RepoEntry::sigLevel() const
|
||||
{
|
||||
return m_sigLevel;
|
||||
}
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
|
@ -91,6 +115,9 @@ public:
|
|||
bool serverInsecure() const;
|
||||
bool reposFromPacmanConf() const;
|
||||
const QList<RepoEntry> &repoEntries() const;
|
||||
bool isAurEnabled() const;
|
||||
bool isVerbose() const;
|
||||
bool runServer() const;
|
||||
|
||||
void loadFromConfigFile(const QString &args);
|
||||
void loadFromConfigFile(const ConfigArgs &args);
|
||||
|
@ -107,9 +134,11 @@ private:
|
|||
QString m_serverKeyFile;
|
||||
bool m_serverInsecure;
|
||||
|
||||
bool m_reposFromPacmanConf;
|
||||
QList<RepoEntry> m_repoEntries;
|
||||
|
||||
bool m_reposFromPacmanConf;
|
||||
bool m_aurEnabled;
|
||||
bool m_verbose;
|
||||
bool m_runServer;
|
||||
};
|
||||
|
||||
inline const QString &Config::alpmRootDir() const
|
||||
|
@ -162,6 +191,21 @@ inline const QList<RepoEntry> &Config::repoEntries() const
|
|||
return m_repoEntries;
|
||||
}
|
||||
|
||||
inline bool Config::isAurEnabled() const
|
||||
{
|
||||
return m_aurEnabled;
|
||||
}
|
||||
|
||||
inline bool Config::isVerbose() const
|
||||
{
|
||||
return m_verbose;
|
||||
}
|
||||
|
||||
inline bool Config::runServer() const
|
||||
{
|
||||
return m_runServer;
|
||||
}
|
||||
|
||||
} // namespace Alpm
|
||||
|
||||
#endif // ALPM_CONFIG_H
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "database.h"
|
||||
#include "group.h"
|
||||
#include "updatelookup.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QJsonObject>
|
||||
|
@ -38,6 +39,20 @@ const QJsonArray &AlpmDataBase::serverUrls() const
|
|||
return m_serverUrls;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Adds the specified server URLs.
|
||||
*/
|
||||
bool AlpmDataBase::addServerUrls(const QStringList &urls)
|
||||
{
|
||||
m_serverUrls = QJsonArray();
|
||||
for(const auto &url : urls) {
|
||||
if(alpm_db_add_server(m_ptr, url.toLocal8Bit().data()) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the packages of the database.
|
||||
*/
|
||||
|
@ -95,7 +110,7 @@ QJsonObject AlpmDataBase::groupInfo() const
|
|||
return groupInfo;
|
||||
}
|
||||
|
||||
void AlpmDataBase::checkForUpgrades(const QList<const AlpmDataBase *> &syncDbs, UpdateLookupResults &results) const
|
||||
void AlpmDataBase::checkForUpgrades(const QList<const AlpmDataBase *> &syncDbs, UpdateLookupResults<AlpmPackage> &results) const
|
||||
{
|
||||
if(syncDbs.isEmpty()) {
|
||||
results.noSources = true;
|
||||
|
@ -110,13 +125,13 @@ void AlpmDataBase::checkForUpgrades(const QList<const AlpmDataBase *> &syncDbs,
|
|||
case PackageVersionComparsion::Equal:
|
||||
break; // ignore equal packages
|
||||
case PackageVersionComparsion::SoftwareUpgrade:
|
||||
results.newVersions << syncPkg;
|
||||
results.newVersions << makeUpdateResult(syncPkg, dbPkg.second.version());
|
||||
break;
|
||||
case PackageVersionComparsion::PackageUpgradeOnly:
|
||||
results.newReleases << syncPkg;
|
||||
results.newReleases << makeUpdateResult(syncPkg, dbPkg.second.version());
|
||||
break;
|
||||
case PackageVersionComparsion::NewerThenSyncVersion:
|
||||
results.downgrades << syncPkg;
|
||||
results.downgrades << makeUpdateResult(syncPkg, dbPkg.second.version());
|
||||
}
|
||||
orphaned = false;
|
||||
} catch(out_of_range &) {
|
||||
|
|
139
alpm/database.h
139
alpm/database.h
|
@ -2,7 +2,7 @@
|
|||
#define ALPM_DATABASE_H
|
||||
|
||||
#include "list.h"
|
||||
#include "package.h"
|
||||
#include "updatelookup.h"
|
||||
|
||||
#include <alpm.h>
|
||||
|
||||
|
@ -13,51 +13,10 @@
|
|||
|
||||
namespace PackageManagement {
|
||||
|
||||
class UpdateLookupResults
|
||||
{
|
||||
public:
|
||||
UpdateLookupResults();
|
||||
|
||||
/*!
|
||||
* \brief Indicates that there are no upgrade sources available.
|
||||
*/
|
||||
bool noSources;
|
||||
|
||||
/*!
|
||||
* \brief Packages providing a software upgrade (new version).
|
||||
*/
|
||||
QList<AlpmPackage> newVersions;
|
||||
|
||||
/*!
|
||||
* \brief Package upgrades only (new release).
|
||||
*/
|
||||
QList<AlpmPackage> newReleases;
|
||||
|
||||
/*!
|
||||
* \brief Downgrades (older version in sync db).
|
||||
*/
|
||||
QList<AlpmPackage> downgrades;
|
||||
|
||||
/*!
|
||||
* \brief Orphaned packages (could not be found in any of the sync dbs).
|
||||
*/
|
||||
QList<AlpmPackage> orphaned;
|
||||
|
||||
QStringList warnings;
|
||||
QStringList errors;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Constructs new update lookup results.
|
||||
*/
|
||||
inline UpdateLookupResults::UpdateLookupResults() :
|
||||
noSources(false)
|
||||
{}
|
||||
|
||||
class AlpmDataBase
|
||||
{
|
||||
public:
|
||||
AlpmDataBase(alpm_db_t *dataBase = nullptr);
|
||||
AlpmDataBase(alpm_db_t *dataBase = nullptr, const QString &dbFile = QString(), const QString &srcDir = QString(), const QString &pkgDir = QString());
|
||||
|
||||
// operators
|
||||
operator bool() const;
|
||||
|
@ -67,11 +26,17 @@ public:
|
|||
// database properties
|
||||
alpm_db_t *ptr() const;
|
||||
const char *name() const;
|
||||
const QString &dataBaseFile() const;
|
||||
const QString &sourcesDirectory() const;
|
||||
void setSourcesDirectory(const QString &dir);
|
||||
const QString &packagesDirectory() const;
|
||||
void setPackagesDirectory(const QString &dir);
|
||||
alpm_siglevel_t sigLevel() const;
|
||||
alpm_db_usage_t usage() const;
|
||||
StringList servers() const;
|
||||
const QJsonArray &serverUrls() const;
|
||||
bool setServers(StringList servers);
|
||||
const QJsonArray &serverUrls() const;
|
||||
bool addServerUrls(const QStringList &urls);
|
||||
alpm_pkg_t *package(const char *name) const;
|
||||
const std::map<QString, AlpmPackage> &packages() const;
|
||||
QStringList packageNames() const;
|
||||
|
@ -84,11 +49,16 @@ public:
|
|||
// upgrade lookup
|
||||
const QStringList &upgradeSources() const;
|
||||
QStringList &upgradeSources();
|
||||
void checkForUpgrades(const QList<const AlpmDataBase *> &syncDbs, UpdateLookupResults &results) const;
|
||||
template<class SyncPackageType>
|
||||
void checkForUpgrades(const std::map<QString, SyncPackageType> &syncDbPkgs, UpdateLookupResults<SyncPackageType> &results) const;
|
||||
void checkForUpgrades(const QList<const AlpmDataBase *> &syncDbs, UpdateLookupResults<AlpmPackage> &results) const;
|
||||
|
||||
private:
|
||||
alpm_db_t *m_ptr;
|
||||
QStringList m_updateSources;
|
||||
QString m_dbFile;
|
||||
QString m_srcDir;
|
||||
QString m_pkgDir;
|
||||
QStringList m_upgradeSources;
|
||||
mutable QJsonArray m_serverUrls;
|
||||
mutable std::map<QString, AlpmPackage> m_packages;
|
||||
mutable QJsonArray m_packageNamesJsonArray;
|
||||
|
@ -98,8 +68,11 @@ private:
|
|||
/*!
|
||||
* \brief Creates a new instance wrapping the specified database struct.
|
||||
*/
|
||||
inline AlpmDataBase::AlpmDataBase(alpm_db_t *dataBase) :
|
||||
m_ptr(dataBase)
|
||||
inline AlpmDataBase::AlpmDataBase(alpm_db_t *dataBase, const QString &dbFile, const QString &srcDir, const QString &pkgDir) :
|
||||
m_ptr(dataBase),
|
||||
m_dbFile(dbFile),
|
||||
m_srcDir(srcDir),
|
||||
m_pkgDir(pkgDir)
|
||||
{}
|
||||
|
||||
/*!
|
||||
|
@ -134,6 +107,46 @@ inline const char *AlpmDataBase::name() const
|
|||
return alpm_db_get_name(m_ptr);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the path of the data base file.
|
||||
*/
|
||||
inline const QString &AlpmDataBase::dataBaseFile() const
|
||||
{
|
||||
return m_dbFile;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the path of the local sources directory.
|
||||
*/
|
||||
inline const QString &AlpmDataBase::sourcesDirectory() const
|
||||
{
|
||||
return m_srcDir;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets the path of the local sources directory.
|
||||
*/
|
||||
inline void AlpmDataBase::setSourcesDirectory(const QString &dir)
|
||||
{
|
||||
m_srcDir = dir;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the path of the local packages directory.
|
||||
*/
|
||||
inline const QString &AlpmDataBase::packagesDirectory() const
|
||||
{
|
||||
return m_pkgDir;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets the path of the local packages directory.
|
||||
*/
|
||||
inline void AlpmDataBase::setPackagesDirectory(const QString &dir)
|
||||
{
|
||||
m_pkgDir = dir;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the signature level of the database.
|
||||
*/
|
||||
|
@ -155,7 +168,7 @@ inline StringList AlpmDataBase::servers() const
|
|||
*/
|
||||
inline bool AlpmDataBase::setServers(StringList servers)
|
||||
{
|
||||
return alpm_db_set_servers(m_ptr, servers.begin().ptr()) != 0;
|
||||
return alpm_db_set_servers(m_ptr, servers.begin().ptr()) == 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -195,7 +208,7 @@ inline PackageList AlpmDataBase::search(StringList terms) const
|
|||
*/
|
||||
inline const QStringList &AlpmDataBase::upgradeSources() const
|
||||
{
|
||||
return m_updateSources;
|
||||
return m_upgradeSources;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -203,7 +216,7 @@ inline const QStringList &AlpmDataBase::upgradeSources() const
|
|||
*/
|
||||
inline QStringList &AlpmDataBase::upgradeSources()
|
||||
{
|
||||
return m_updateSources;
|
||||
return m_upgradeSources;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -214,6 +227,30 @@ inline PackageManagement::AlpmDataBase::operator bool() const
|
|||
return m_ptr != nullptr;
|
||||
}
|
||||
|
||||
template<class SyncPackageType>
|
||||
void AlpmDataBase::checkForUpgrades(const std::map<QString, SyncPackageType> &syncDbPkgs, UpdateLookupResults<SyncPackageType> &results) const
|
||||
{
|
||||
for(const auto &dbPkg : packages()) {
|
||||
try {
|
||||
const auto &syncPkg = syncDbPkgs.at(dbPkg.first);
|
||||
switch(dbPkg.second.compareVersion(syncPkg)) {
|
||||
case PackageVersionComparsion::Equal:
|
||||
break; // ignore equal packages
|
||||
case PackageVersionComparsion::SoftwareUpgrade:
|
||||
results.newVersions << makeUpdateResult(syncPkg, dbPkg.second.version());
|
||||
break;
|
||||
case PackageVersionComparsion::PackageUpgradeOnly:
|
||||
results.newReleases << makeUpdateResult(syncPkg, dbPkg.second.version());
|
||||
break;
|
||||
case PackageVersionComparsion::NewerThenSyncVersion:
|
||||
results.downgrades << makeUpdateResult(syncPkg, dbPkg.second.version());
|
||||
}
|
||||
} catch(std::out_of_range &) {
|
||||
results.orphaned << dbPkg.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Alpm
|
||||
|
||||
#endif // ALPM_DATABASE_H
|
||||
|
|
226
alpm/manager.cpp
226
alpm/manager.cpp
|
@ -8,10 +8,9 @@
|
|||
#include <c++utilities/conversion/stringconversion.h>
|
||||
|
||||
#include <QList>
|
||||
#include <QJsonObject>
|
||||
#include <QSysInfo>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
#include <QMutexLocker>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
|
@ -38,8 +37,7 @@ constexpr int defaultSigLevel = ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL |
|
|||
* \param rootdir Specifies the root directory.
|
||||
* \param dbpath Specifies the database directory.
|
||||
*/
|
||||
Manager::Manager(const Config &config, QObject *parent) :
|
||||
QObject(parent),
|
||||
Manager::Manager(const Config &config) :
|
||||
m_config(config),
|
||||
m_sigLevel(defaultSigLevel),
|
||||
m_localFileSigLevel(ALPM_SIG_USE_DEFAULT),
|
||||
|
@ -62,7 +60,7 @@ Manager::~Manager()
|
|||
/*!
|
||||
* \brief Returns the package with the specified name from the specified database.
|
||||
*/
|
||||
AlpmPackage Manager::packageFromSyncDataBase(const QString &dbName, const QString &pkgName)
|
||||
AlpmPackage Manager::packageFromSyncDataBase(const QString &dbName, const QString &pkgName) const
|
||||
{
|
||||
try {
|
||||
if(dbName == QLatin1String("local")) {
|
||||
|
@ -115,7 +113,7 @@ const string &lastValue(const multimap<string, string> &mm, const string &key)
|
|||
/*!
|
||||
* \brief Parses a "SigLevel" denotation from pacman config file.
|
||||
*/
|
||||
int parseSigLevel(const string &sigLevelStr = string())
|
||||
int Manager::parseSigLevel(const string &sigLevelStr)
|
||||
{
|
||||
int sigLevel = defaultSigLevel;
|
||||
// split sig level denotation into parts
|
||||
|
@ -179,7 +177,7 @@ int parseSigLevel(const string &sigLevelStr = string())
|
|||
/*!
|
||||
* \brief Parses a "Usage" denotation from pacman config file.
|
||||
*/
|
||||
int parseUsage(const string &usageStr)
|
||||
int Manager::parseUsage(const string &usageStr)
|
||||
{
|
||||
int usage = 0;
|
||||
const auto parts = splitString<list<string> >(usageStr, " ");
|
||||
|
@ -200,9 +198,9 @@ int parseUsage(const string &usageStr)
|
|||
}
|
||||
|
||||
/*!
|
||||
* \brief Parses the Pacman configuration. Registers the listed sync databases.
|
||||
* \brief Parses and applies the Pacman configuration. Registers the listed sync databases.
|
||||
*/
|
||||
void Manager::parsePacmanConfig()
|
||||
void Manager::applyPacmanConfig()
|
||||
{
|
||||
// open config file and parse as ini
|
||||
try {
|
||||
|
@ -229,7 +227,7 @@ void Manager::parsePacmanConfig()
|
|||
}
|
||||
const auto &specifiedDir = lastValue(options, "CacheDir");
|
||||
if(!specifiedDir.empty()) {
|
||||
m_cacheDir = QString::fromStdString(specifiedDir);
|
||||
m_pacmanCacheDir = QString::fromStdString(specifiedDir);
|
||||
}
|
||||
globalSigLevel = parseSigLevel(lastValue(options, sigLevelKey));
|
||||
} catch(const out_of_range &) {
|
||||
|
@ -242,9 +240,9 @@ void Manager::parsePacmanConfig()
|
|||
if(scope.first != "options") {
|
||||
// read and validate database name
|
||||
QString dbName = QString::fromLocal8Bit(scope.first.c_str());
|
||||
if(dbName == QLatin1String("local")) {
|
||||
if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
|
||||
cerr << "Error: Unable to add database from pacman config: The database name mustn't be \"local\" because this name is reserved for the local database." << endl;
|
||||
} else if(dbName.startsWith(QLatin1String("aur"))) {
|
||||
} else if(dbName.startsWith(QLatin1String("aur"), Qt::CaseInsensitive)) {
|
||||
cerr << "Error: Unable to add database from pacman config: The database name mustn't start with \"aur\" because this name is reserved for the Arch Linux User Repository." << endl;
|
||||
} else if(m_syncDbs.count(dbName)) {
|
||||
cerr << "Error: Unable to add database from pacman config: Database names must be unique. Ignoring second occurance of database \"" << scope.first << "\"." << endl;
|
||||
|
@ -257,9 +255,13 @@ void Manager::parsePacmanConfig()
|
|||
if(alpm_db_t *db = alpm_register_syncdb(m_handle, scope.first.c_str(), static_cast<alpm_siglevel_t>(sigLevel))) {
|
||||
// set usage
|
||||
if(alpm_db_set_usage(db, static_cast<alpm_db_usage_t>(usage)) == 0) {
|
||||
cerr << "Added database [" << scope.first << "]" << endl;
|
||||
if(m_config.isVerbose() || m_config.runServer()) {
|
||||
cerr << "Added database [" << scope.first << "]" << endl;
|
||||
}
|
||||
} else {
|
||||
cerr << "Warning: Added database [" << scope.first << "] but failed to set usage" << endl;
|
||||
if(m_config.isVerbose() || m_config.runServer()) {
|
||||
cerr << "Warning: Added database [" << scope.first << "] but failed to set usage" << endl;
|
||||
}
|
||||
}
|
||||
// add servers
|
||||
for(auto range = scope.second.equal_range("Server"); range.first != range.second; ++range.first) {
|
||||
|
@ -267,7 +269,9 @@ void Manager::parsePacmanConfig()
|
|||
findAndReplace<string>(url, "$repo", scope.first);
|
||||
findAndReplace<string>(url, "$arch", arch);
|
||||
alpm_db_add_server(db, url.c_str());
|
||||
cerr << "Added server: " << url << endl;
|
||||
if(m_config.isVerbose() || m_config.runServer()) {
|
||||
cerr << "Added server: " << url << endl;
|
||||
}
|
||||
}
|
||||
// add included servers
|
||||
for(auto range = scope.second.equal_range("Include"); range.first != range.second; ++range.first) {
|
||||
|
@ -290,7 +294,9 @@ void Manager::parsePacmanConfig()
|
|||
findAndReplace<string>(url, "$repo", scope.first);
|
||||
findAndReplace<string>(url, "$arch", arch);
|
||||
alpm_db_add_server(db, url.c_str());
|
||||
cerr << "Added server: " << url << endl;
|
||||
if(m_config.isVerbose() || m_config.runServer()) {
|
||||
cerr << "Added server: " << url << endl;
|
||||
}
|
||||
}
|
||||
} catch (const out_of_range &) {
|
||||
cerr << "Warning: Included file \"" << path << "\" has no values." << endl;
|
||||
|
@ -301,15 +307,58 @@ void Manager::parsePacmanConfig()
|
|||
// -> db is used to upgrade local database
|
||||
localDataBase().upgradeSources() << dbName;
|
||||
}
|
||||
m_syncDbs.emplace(dbName, db);
|
||||
m_syncDbs.emplace(dbName, AlpmDataBase(db, QStringLiteral("%1/sync/%2").arg(m_config.alpmDbPath(), dbName)));
|
||||
} else {
|
||||
cerr << "Unable to add sync database [" << scope.first << "]" << endl;
|
||||
cerr << "Error: Unable to add sync database [" << scope.first << "]" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const ios_base::failure &) {
|
||||
throw ios_base::failure("An IO exception occured when parsing the config file.");
|
||||
throw ios_base::failure("Error: An IO exception occured when parsing the config file.");
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Applies the repository index configuration.
|
||||
*/
|
||||
void Manager::applyRepoIndexConfig()
|
||||
{
|
||||
for(const RepoEntry &repoEntry : m_config.repoEntries()) {
|
||||
AlpmDataBase *syncDb;
|
||||
try {
|
||||
syncDb = &m_syncDbs.at(repoEntry.name());
|
||||
cerr << "Applying config for database [" << syncDb->name() << "]" << endl;
|
||||
if(!repoEntry.dataBasePath().isEmpty()) {
|
||||
cerr << "Warning: Can't use data base path specified in repo index config because the repo \""
|
||||
<< repoEntry.name().toLocal8Bit().data() << "\" has already been added from the Pacman config." << endl;
|
||||
}
|
||||
if(repoEntry.sigLevel()) {
|
||||
cerr << "Warning: Can't use sig level path specified in repo index config because the repo \""
|
||||
<< repoEntry.name().toLocal8Bit().data() << "\" has already been added from the Pacman config." << endl;
|
||||
}
|
||||
syncDb->setPackagesDirectory(repoEntry.packageDir());
|
||||
syncDb->setSourcesDirectory(repoEntry.sourceDir());
|
||||
} catch(const out_of_range &) {
|
||||
if(repoEntry.name().compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
|
||||
cerr << "Error: Unable to add database from repo index config: The database name mustn't be \"local\" because this name is reserved for the local database." << endl;
|
||||
} else if(repoEntry.name().startsWith(QLatin1String("aur"), Qt::CaseInsensitive)) {
|
||||
cerr << "Error: Unable to add database from repo index config: The database name mustn't start with \"aur\" because this name is reserved for the Arch Linux User Repository." << endl;
|
||||
} else {
|
||||
auto *db = alpm_register_syncdb(m_handle, repoEntry.name().toLocal8Bit().data(), static_cast<alpm_siglevel_t>(repoEntry.sigLevel()));
|
||||
auto emplaced = m_syncDbs.emplace(repoEntry.name(), AlpmDataBase(db, repoEntry.dataBasePath(), repoEntry.sourceDir(), repoEntry.packageDir()));
|
||||
if(emplaced.second) {
|
||||
syncDb = &emplaced.first->second;
|
||||
if(m_config.isVerbose() || m_config.runServer()) {
|
||||
cerr << "Added database [" << repoEntry.name().toLocal8Bit().data() << "]" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(syncDb) {
|
||||
syncDb->addServerUrls(repoEntry.servers());
|
||||
syncDb->upgradeSources() << repoEntry.upgradeSources();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -329,7 +378,10 @@ void Manager::unregisterSyncDataBases()
|
|||
const AlpmDataBase &Manager::localDataBase() const
|
||||
{
|
||||
if(!m_localDb) {
|
||||
m_localDb = alpm_get_localdb(m_handle);
|
||||
QMutexLocker locker(&m_localDbMutex);
|
||||
if(!m_localDb) {
|
||||
m_localDb = alpm_get_localdb(m_handle);
|
||||
}
|
||||
}
|
||||
return m_localDb;
|
||||
}
|
||||
|
@ -365,6 +417,7 @@ QJsonObject Manager::basicRepoInfo(AlpmDataBase db, const QString &name, const Q
|
|||
repoInfo.insert(QStringLiteral("servers"), db.serverUrls());
|
||||
repoInfo.insert(QStringLiteral("usage"), Utilities::usageStrings(db.usage()));
|
||||
repoInfo.insert(QStringLiteral("sigLevel"), Utilities::sigLevelStrings(db.sigLevel()));
|
||||
repoInfo.insert(QStringLiteral("upgradeSources"), QJsonArray::fromStringList(db.upgradeSources()));
|
||||
repoInfo.insert(QStringLiteral("packages"), db.packageNameJsonArray());
|
||||
return repoInfo;
|
||||
}
|
||||
|
@ -378,15 +431,18 @@ QJsonObject Manager::basicRepoInfo(AlpmDataBase db, const QString &name, const Q
|
|||
const QJsonArray &Manager::basicRepoInfo() const
|
||||
{
|
||||
if(m_basicRepoInfo.isEmpty()) {
|
||||
m_basicRepoInfo << basicRepoInfo(localDataBase(), QStringLiteral("local"), QStringLiteral("The local database."));
|
||||
auto const &syncDbs = syncDataBases();
|
||||
for(const auto &syncDb : syncDbs) {
|
||||
// check if the "sync" database is actually used for syncing
|
||||
auto usage = syncDb.second.usage();
|
||||
if((usage & ALPM_DB_USAGE_SYNC) || (usage & ALPM_DB_USAGE_INSTALL) || (usage & ALPM_DB_USAGE_UPGRADE)) {
|
||||
m_basicRepoInfo << basicRepoInfo(syncDb.second, syncDb.first, QStringLiteral("The sync database »%1«.").arg(syncDb.first));
|
||||
} else {
|
||||
m_basicRepoInfo << basicRepoInfo(syncDb.second, syncDb.first, QStringLiteral("The database »%1«.").arg(syncDb.first));
|
||||
QMutexLocker locker(&m_basicRepoInfoMutex);
|
||||
if(m_basicRepoInfo.isEmpty()) {
|
||||
m_basicRepoInfo << basicRepoInfo(localDataBase(), QStringLiteral("local"), QStringLiteral("The local database."));
|
||||
auto const &syncDbs = syncDataBases();
|
||||
for(const auto &syncDb : syncDbs) {
|
||||
// check if the "sync" database is actually used for syncing
|
||||
auto usage = syncDb.second.usage();
|
||||
if((usage & ALPM_DB_USAGE_SYNC) || (usage & ALPM_DB_USAGE_INSTALL) || (usage & ALPM_DB_USAGE_UPGRADE)) {
|
||||
m_basicRepoInfo << basicRepoInfo(syncDb.second, syncDb.first, QStringLiteral("The sync database »%1«.").arg(syncDb.first));
|
||||
} else {
|
||||
m_basicRepoInfo << basicRepoInfo(syncDb.second, syncDb.first, QStringLiteral("The database »%1«.").arg(syncDb.first));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -396,7 +452,7 @@ const QJsonArray &Manager::basicRepoInfo() const
|
|||
/*!
|
||||
* \brief Returns package information for the specified selection of packages.
|
||||
*/
|
||||
const QJsonArray Manager::packageInfo(const QJsonArray &pkgSelection, bool full)
|
||||
const QJsonArray Manager::packageInfo(const QJsonArray &pkgSelection, bool full) const
|
||||
{
|
||||
QJsonArray pkgInfos;
|
||||
for(const auto &pkgSelJsonVal : pkgSelection) {
|
||||
|
@ -426,9 +482,12 @@ const QJsonArray Manager::packageInfo(const QJsonArray &pkgSelection, bool full)
|
|||
const QJsonArray &Manager::groupInfo() const
|
||||
{
|
||||
if(m_groupInfo.empty()) {
|
||||
m_groupInfo << localDataBase().groupInfo();
|
||||
for(const auto &db : m_syncDbs) {
|
||||
m_groupInfo << db.second.groupInfo();
|
||||
QMutexLocker locker(&m_groupInfoMutex);
|
||||
if(m_groupInfo.empty()) {
|
||||
m_groupInfo << localDataBase().groupInfo();
|
||||
for(const auto &db : m_syncDbs) {
|
||||
m_groupInfo << db.second.groupInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
return m_groupInfo;
|
||||
|
@ -468,103 +527,19 @@ AlpmDataBase &Manager::dataBaseByName(const QString &dbName)
|
|||
* - syncdbs: Array with the names of the databases used as upgrade sources.
|
||||
* If not present, appropriate upgrade sources will be determined automatically.
|
||||
*/
|
||||
QJsonObject Manager::invokeUpgradeLookup(const QJsonObject &request) const
|
||||
void Manager::invokeUpgradeLookup(const QJsonObject &request, UpdateLookupCallback callback) const
|
||||
{
|
||||
QJsonObject result;
|
||||
QJsonArray errors;
|
||||
const AlpmDataBase &db = dataBaseByName(request.value(QStringLiteral("db")).toString());
|
||||
QJsonValue syncDbsValue = request.value(QStringLiteral("syncdbs"));
|
||||
if(!db) {
|
||||
errors << QStringLiteral("Database to be checked not found.");
|
||||
} else {
|
||||
QJsonArray warnings;
|
||||
bool searchAur = request.value(QStringLiteral("aur")).toBool(false);
|
||||
if(searchAur) {
|
||||
m_aur.requestPackageInfo(db.packageNames());
|
||||
}
|
||||
const auto results = checkForUpgrades(db, syncDbsValue);
|
||||
if(searchAur || !results.noSources) {
|
||||
QJsonArray softwareUpdates;
|
||||
QJsonArray packageOnlyUpdates;
|
||||
QJsonArray downgrades;
|
||||
QJsonArray orphanedPackages;
|
||||
if(!results.noSources) {
|
||||
for(const auto pkg : results.newVersions) {
|
||||
softwareUpdates << pkg.basicInfo(true);
|
||||
}
|
||||
for(const auto pkg : results.newReleases) {
|
||||
packageOnlyUpdates << pkg.basicInfo(true);
|
||||
}
|
||||
for(const auto pkg : results.downgrades) {
|
||||
downgrades << pkg.basicInfo(true);
|
||||
}
|
||||
for(const auto pkg : results.orphaned) {
|
||||
orphanedPackages << pkg.basicInfo(true);
|
||||
}
|
||||
}
|
||||
if(!warnings.isEmpty()) {
|
||||
result.insert(QStringLiteral("warnings"), warnings);
|
||||
}
|
||||
result.insert(QStringLiteral("softwareUpdates"), softwareUpdates);
|
||||
result.insert(QStringLiteral("packageOnlyUpdates"), packageOnlyUpdates);
|
||||
result.insert(QStringLiteral("downgrades"), downgrades);
|
||||
result.insert(QStringLiteral("orphanedPackages"), orphanedPackages);
|
||||
} else {
|
||||
errors << QStringLiteral("No update sources associated for database \"%1\".").arg(QString::fromLocal8Bit(db.name()));
|
||||
}
|
||||
}
|
||||
if(!errors.isEmpty()) {
|
||||
result.insert(QStringLiteral("errors"), errors);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Checks the specified database for upgrades.
|
||||
*/
|
||||
const UpdateLookupResults Manager::checkForUpgrades(const AlpmDataBase &db, const QJsonValue &syncDbsValue) const
|
||||
{
|
||||
UpdateLookupResults results;
|
||||
const auto &syncDbs = syncDataBases();
|
||||
QList<const AlpmDataBase *> syncDbSel;
|
||||
if(syncDbsValue.type() == QJsonValue::Array) {
|
||||
for(const auto &syncDbVal : syncDbsValue.toArray()) {
|
||||
const auto syncDbName = syncDbVal.toString();
|
||||
if(syncDbName == QLatin1String("local")) {
|
||||
syncDbSel << &(localDataBase());
|
||||
} else {
|
||||
try {
|
||||
syncDbSel << &(syncDbs.at(syncDbName));
|
||||
} catch(out_of_range &) {
|
||||
results.warnings << QStringLiteral("The specified sync database \"%1\" can not be found.").arg(syncDbName);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(const auto &syncDbName : db.upgradeSources()) {
|
||||
if(syncDbName == QLatin1String("local")) {
|
||||
syncDbSel << &(localDataBase());
|
||||
} else {
|
||||
try {
|
||||
syncDbSel << &(syncDbs.at(syncDbName));
|
||||
} catch(out_of_range &) {
|
||||
results.warnings << QStringLiteral("The associated upgrade database \"%1\" can not be found.").arg(syncDbName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
db.checkForUpgrades(syncDbSel, results);
|
||||
return results;
|
||||
new UpdateLookup(*this, request, callback); // this object will delete itself
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Checks the specified database for upgrades.
|
||||
*
|
||||
* Appropriate upgrade sources will be determined automatically.
|
||||
* Appropriate upgrade sources will be determined automatically; does not check the AUR.
|
||||
*/
|
||||
const UpdateLookupResults Manager::checkForUpgrades(const AlpmDataBase &db) const
|
||||
const UpdateLookupResults<AlpmPackage> Manager::checkForUpgrades(const AlpmDataBase &db) const
|
||||
{
|
||||
UpdateLookupResults results;
|
||||
UpdateLookupResults<AlpmPackage> results;
|
||||
QList<const AlpmDataBase *> syncDbSel;
|
||||
for(const auto &syncDbName : db.upgradeSources()) {
|
||||
try {
|
||||
|
@ -577,11 +552,4 @@ const UpdateLookupResults Manager::checkForUpgrades(const AlpmDataBase &db) cons
|
|||
return results;
|
||||
}
|
||||
|
||||
//void Manager::addTask()
|
||||
//{
|
||||
// QUuid uuid;
|
||||
// while(m_tasks.find(uuid = QUuid::createUuid()) != m_tasks.cend());
|
||||
// m_tasks.emplace(uuid);
|
||||
//}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,45 +1,52 @@
|
|||
#ifndef ALPM_MANAGER_H
|
||||
#define ALPM_MANAGER_H
|
||||
|
||||
#include "package.h"
|
||||
#include "database.h"
|
||||
#include "updatelookup.h"
|
||||
|
||||
#include "network/aurquery.h"
|
||||
|
||||
#include <alpm.h>
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QFuture>
|
||||
#include <QUuid>
|
||||
#include <QMutex>
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace PackageManagement {
|
||||
|
||||
class Config;
|
||||
template<class Package> class UpdateLookupResults;
|
||||
|
||||
class Manager : QObject
|
||||
class Manager
|
||||
{
|
||||
public:
|
||||
Manager(const Config &config, QObject *parent = nullptr);
|
||||
explicit Manager(const Config &config);
|
||||
Manager(const Manager &other) = delete;
|
||||
Manager(Manager &&other) = delete;
|
||||
Manager &operator =(const Manager &other) = delete;
|
||||
~Manager();
|
||||
|
||||
// configuration, signature level, etc
|
||||
const Config &config() const;
|
||||
int sigLevel() const;
|
||||
void setSigLevel(int sigLevel);
|
||||
int localFileSigLevel() const;
|
||||
void setLocalFileSigLevel(int sigLevel);
|
||||
const QString &cacheDir() const;
|
||||
void parsePacmanConfig();
|
||||
const QString &pacmanCacheDir() const;
|
||||
static int parseSigLevel(const std::string &sigLevelStr = std::string());
|
||||
static int parseUsage(const std::string &usageStr);
|
||||
void applyPacmanConfig();
|
||||
void applyRepoIndexConfig();
|
||||
|
||||
void unregisterSyncDataBases();
|
||||
const AurQuery &aurQuery() const;
|
||||
AurQuery &aurQuery();
|
||||
|
||||
// package lookup
|
||||
AlpmPackage packageFromSyncDataBase(const QString &dbName, const QString &pkgName);
|
||||
AlpmPackage packageFromSyncDataBase(const QString &dbName, const QString &pkgName) const;
|
||||
AlpmPackage packageFromSyncDataBases(const char *name) const;
|
||||
AlpmOwnershipPackage packageFromFile(const char *fileName, bool verifyIntegrity = false);
|
||||
bool isPackageIgnored(AlpmPackage package) const;
|
||||
|
@ -51,18 +58,14 @@ public:
|
|||
const std::map<QString, AlpmDataBase> &syncDataBases() const;
|
||||
const AlpmDataBase &dataBaseByName(const QString &dbName) const;
|
||||
AlpmDataBase &dataBaseByName(const QString &dbName);
|
||||
const UpdateLookupResults checkForUpgrades(const AlpmDataBase &db) const;
|
||||
|
||||
// asynchronous tasks
|
||||
// void addTask();
|
||||
const UpdateLookupResults<AlpmPackage> checkForUpgrades(const AlpmDataBase &db) const;
|
||||
|
||||
// JSON serialization, handling JSON requests
|
||||
QJsonObject basicRepoInfo(AlpmDataBase db, const QString &name, const QString &desc = QString()) const;
|
||||
const QJsonArray &basicRepoInfo() const;
|
||||
const QJsonArray packageInfo(const QJsonArray &pkgSelection, bool full = true);
|
||||
const QJsonArray packageInfo(const QJsonArray &pkgSelection, bool full = true) const;
|
||||
const QJsonArray &groupInfo() const;
|
||||
QJsonObject invokeUpgradeLookup(const QJsonObject &request) const;
|
||||
const UpdateLookupResults checkForUpgrades(const AlpmDataBase &db, const QJsonValue &syncDbsValue) const;
|
||||
void invokeUpgradeLookup(const QJsonObject &request, UpdateLookupCallback callback) const;
|
||||
|
||||
private:
|
||||
void cleanup();
|
||||
|
@ -71,16 +74,27 @@ private:
|
|||
alpm_handle_t *m_handle;
|
||||
int m_sigLevel;
|
||||
int m_localFileSigLevel;
|
||||
QString m_cacheDir;
|
||||
QString m_pacmanCacheDir;
|
||||
QNetworkAccessManager m_networkAccessManager;
|
||||
AurQuery m_aur;
|
||||
mutable AlpmDataBase m_localDb;
|
||||
mutable QMutex m_localDbMutex;
|
||||
mutable std::map<QString, AlpmDataBase> m_syncDbs;
|
||||
mutable QJsonArray m_basicRepoInfo;
|
||||
mutable QJsonArray m_packageInfo;
|
||||
mutable QMutex m_basicRepoInfoMutex;
|
||||
mutable QJsonArray m_groupInfo;
|
||||
mutable QMutex m_groupInfoMutex;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Returns the configuration of the manager.
|
||||
* \remarks The configuration has been specified when constructing the manager.
|
||||
*/
|
||||
inline const Config &Manager::config() const
|
||||
{
|
||||
return m_config;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the signature level.
|
||||
*
|
||||
|
@ -138,12 +152,22 @@ inline bool Manager::isPackageIgnored(AlpmPackage package) const
|
|||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the cache directory.
|
||||
* \brief Returns the Pacman cache directory.
|
||||
* \remarks This is read from the Pacman configuration.
|
||||
*/
|
||||
inline const QString &Manager::cacheDir() const
|
||||
inline const QString &Manager::pacmanCacheDir() const
|
||||
{
|
||||
return m_cacheDir;
|
||||
return m_pacmanCacheDir;
|
||||
}
|
||||
|
||||
inline const AurQuery &Manager::aurQuery() const
|
||||
{
|
||||
return m_aur;
|
||||
}
|
||||
|
||||
inline AurQuery &Manager::aurQuery()
|
||||
{
|
||||
return m_aur;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
#include "mingwbundle.h"
|
||||
#include "manager.h"
|
||||
|
||||
#include <c++utilities/conversion/stringconversion.h>
|
||||
#include <c++utilities/misc/memory.h>
|
||||
|
||||
#include <KTar>
|
||||
|
||||
#include <QFile>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace PackageManagement {
|
||||
|
||||
const string prefix("mingw-w64-");
|
||||
|
||||
MingwBundle::MingwBundle(const Manager &manager, const ApplicationUtilities::StringVector &packages) :
|
||||
m_manager(manager)
|
||||
{
|
||||
string missing;
|
||||
for(const auto &pkgName : packages) {
|
||||
bool found = false;
|
||||
for(const auto &syncDb : manager.syncDataBases()) {
|
||||
if(auto pkg = syncDb.second.package(ConversionUtilities::startsWith(pkgName, prefix) ? pkgName.data() : (prefix + pkgName).data())) {
|
||||
if(missing.empty()) {
|
||||
m_packages.emplace_back(syncDb.second, pkg);
|
||||
}
|
||||
addDependencies(pkg);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
missing.push_back(' ');
|
||||
missing.append(pkgName);
|
||||
}
|
||||
}
|
||||
if(!missing.empty()) {
|
||||
throw runtime_error("The following packages can not be found:" + missing);
|
||||
} else {
|
||||
cerr << "Adding the following packages:";
|
||||
for(const auto &pkg : m_packages) {
|
||||
cerr << ' ' << pkg.second.name();
|
||||
}
|
||||
cerr << endl;
|
||||
}
|
||||
}
|
||||
|
||||
enum class RelevantFileType
|
||||
{
|
||||
Binary,
|
||||
Translation
|
||||
};
|
||||
|
||||
enum class RelevantFileArch
|
||||
{
|
||||
x86_64,
|
||||
i686
|
||||
};
|
||||
|
||||
struct RelevantFile
|
||||
{
|
||||
RelevantFile(const KArchiveFile *file, const RelevantFileType type, const RelevantFileArch arch) :
|
||||
file(file),
|
||||
fileType(type),
|
||||
arch(arch)
|
||||
{}
|
||||
const KArchiveFile *file;
|
||||
RelevantFileType fileType;
|
||||
RelevantFileArch arch;
|
||||
};
|
||||
|
||||
struct PkgFileInfo
|
||||
{
|
||||
PkgFileInfo(const QString &path) : path(path), failure(false) {}
|
||||
QString path;
|
||||
unique_ptr<KTar> archive;
|
||||
list<RelevantFile> relevantFiles;
|
||||
bool failure;
|
||||
};
|
||||
|
||||
void getFiles(PkgFileInfo &pkgFileInfo)
|
||||
{
|
||||
pkgFileInfo.archive = make_unique<KTar>(pkgFileInfo.path);
|
||||
if(pkgFileInfo.archive->open(QIODevice::ReadOnly)) {
|
||||
static const pair<RelevantFileArch, QString> roots[] = {
|
||||
make_pair(RelevantFileArch::x86_64, QStringLiteral("/usr/x86_64-w64-mingw32")),
|
||||
make_pair(RelevantFileArch::i686, QStringLiteral("/usr/i686-w64-mingw32"))
|
||||
};
|
||||
for(const auto &root : roots) {
|
||||
const auto *rootEntry = pkgFileInfo.archive->directory()->entry(root.second);
|
||||
if(rootEntry && rootEntry->isDirectory()) {
|
||||
const auto *rootDir = static_cast<const KArchiveDirectory *>(rootEntry);
|
||||
const auto *binEntry = rootDir->entry(QStringLiteral("bin"));
|
||||
if(binEntry && binEntry->isDirectory()) {
|
||||
const auto *binDir = static_cast<const KArchiveDirectory *>(binEntry);
|
||||
for(const auto &entryName : binDir->entries()) {
|
||||
if(entryName.endsWith(QLatin1String(".exe")) || entryName.endsWith(QLatin1String(".dll"))) {
|
||||
if(const auto *entry = binDir->entry(entryName)) {
|
||||
if(entry->isFile()) {
|
||||
const auto *binFile = static_cast<const KArchiveFile *>(entry);
|
||||
pkgFileInfo.relevantFiles.emplace_back(binFile, RelevantFileType::Binary, root.first);
|
||||
cerr << entryName.toLocal8Bit().data() << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
pkgFileInfo.failure = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MingwBundle::createBundle(const string &path) const
|
||||
{
|
||||
list<PkgFileInfo> pkgFiles;
|
||||
for(const auto &entry : m_packages) {
|
||||
QString pkgFile;
|
||||
if(!entry.first.packagesDirectory().isEmpty()) {
|
||||
pkgFile = QStringLiteral("%1/%2").arg(entry.first.packagesDirectory(), QString::fromLocal8Bit(entry.second.fileName()));
|
||||
}
|
||||
if(pkgFile.isEmpty() || !QFile::exists(pkgFile)) {
|
||||
if(!m_manager.pacmanCacheDir().isEmpty()) {
|
||||
pkgFile = QStringLiteral("%1/%2").arg(m_manager.pacmanCacheDir(), QString::fromLocal8Bit(entry.second.fileName()));
|
||||
}
|
||||
if(pkgFile.isEmpty() || !QFile::exists(pkgFile)) {
|
||||
throw runtime_error("The package file " + string(entry.second.fileName()) + " can't be found.");
|
||||
// TODO: download package from mirror
|
||||
}
|
||||
}
|
||||
pkgFiles.emplace_back(pkgFile);
|
||||
}
|
||||
QtConcurrent::blockingMap(pkgFiles, getFiles);
|
||||
}
|
||||
|
||||
void MingwBundle::addDependencies(const AlpmPackage &pkg)
|
||||
{
|
||||
string missing;
|
||||
for(const auto &depInfo : pkg.dependencies()) {
|
||||
bool found = false;
|
||||
for(const auto &syncDb : m_manager.syncDataBases()) {
|
||||
if(auto pkg = syncDb.second.package(depInfo->name)) {
|
||||
if(missing.empty()) {
|
||||
m_packages.emplace_back(syncDb.second, pkg);
|
||||
}
|
||||
addDependencies(pkg);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
missing.push_back(' ');
|
||||
missing.append(depInfo->name);
|
||||
}
|
||||
}
|
||||
if(!missing.empty()) {
|
||||
throw runtime_error("The following dependencies of the " + string(pkg.name()) + " package can not be resolved:" + missing);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace PackageManagement
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef PACKAGEMANAGEMENT_MINGWBUNDLE_H
|
||||
#define PACKAGEMANAGEMENT_MINGWBUNDLE_H
|
||||
|
||||
#include "package.h"
|
||||
#include "database.h"
|
||||
|
||||
#include <c++utilities/application/argumentparser.h>
|
||||
|
||||
#include <QList>
|
||||
|
||||
namespace PackageManagement {
|
||||
|
||||
class Manager;
|
||||
|
||||
class MingwBundle
|
||||
{
|
||||
public:
|
||||
MingwBundle(const Manager &manager, const ApplicationUtilities::StringVector &packages);
|
||||
|
||||
void createBundle(const std::string &path) const;
|
||||
|
||||
private:
|
||||
void addDependencies(const AlpmPackage &pkg);
|
||||
|
||||
const Manager &m_manager;
|
||||
std::list<std::pair<const AlpmDataBase &, AlpmPackage> > m_packages;
|
||||
};
|
||||
|
||||
} // namespace PackageManagement
|
||||
|
||||
#endif // PACKAGEMANAGEMENT_MINGWBUNDLE_H
|
218
alpm/package.cpp
218
alpm/package.cpp
|
@ -11,10 +11,93 @@ using namespace ChronoUtilities;
|
|||
|
||||
namespace PackageManagement {
|
||||
|
||||
/*!
|
||||
* \cond
|
||||
*/
|
||||
|
||||
inline QString qstr(const char *str)
|
||||
{
|
||||
return QString::fromLocal8Bit(str);
|
||||
}
|
||||
|
||||
inline QJsonArray qjarry(StringList list)
|
||||
{
|
||||
QJsonArray jsonArray;
|
||||
for(const char *str : list) {
|
||||
jsonArray << qstr(str);
|
||||
}
|
||||
return jsonArray;
|
||||
}
|
||||
|
||||
inline QJsonArray qjarry(DependencyList list)
|
||||
{
|
||||
QJsonArray jsonArray;
|
||||
for(alpm_depend_t *dep : list) {
|
||||
QJsonObject depObj;
|
||||
depObj.insert(QStringLiteral("name"), dep->name);
|
||||
depObj.insert(QStringLiteral("desc"), dep->desc);
|
||||
depObj.insert(QStringLiteral("ver"), dep->version);
|
||||
switch(dep->mod) {
|
||||
case ALPM_DEP_MOD_ANY:
|
||||
depObj.insert(QStringLiteral("mod"), QStringLiteral("any"));
|
||||
break;
|
||||
case ALPM_DEP_MOD_EQ:
|
||||
depObj.insert(QStringLiteral("mod"), QStringLiteral("eq"));
|
||||
break;
|
||||
case ALPM_DEP_MOD_GE:
|
||||
depObj.insert(QStringLiteral("mod"), QStringLiteral("ge"));
|
||||
break;
|
||||
case ALPM_DEP_MOD_LE:
|
||||
depObj.insert(QStringLiteral("mod"), QStringLiteral("le"));
|
||||
break;
|
||||
case ALPM_DEP_MOD_GT:
|
||||
depObj.insert(QStringLiteral("mod"), QStringLiteral("gt"));
|
||||
break;
|
||||
case ALPM_DEP_MOD_LT:
|
||||
depObj.insert(QStringLiteral("mod"), QStringLiteral("lt"));
|
||||
break;
|
||||
}
|
||||
jsonArray << depObj;
|
||||
}
|
||||
return jsonArray;
|
||||
}
|
||||
|
||||
inline QJsonArray qjarry(_alpm_pkgvalidation_t validation)
|
||||
{
|
||||
QJsonArray jsonArray;
|
||||
if(validation & 0x1) {
|
||||
jsonArray << QStringLiteral("none");
|
||||
}
|
||||
if(validation & 0x2) {
|
||||
jsonArray << QStringLiteral("MD5");
|
||||
}
|
||||
if(validation & 0x4) {
|
||||
jsonArray << QStringLiteral("SHA256");
|
||||
}
|
||||
if(validation & 0x8) {
|
||||
jsonArray << QStringLiteral("signature");
|
||||
}
|
||||
if(jsonArray.empty()) {
|
||||
jsonArray << QStringLiteral("unknown");
|
||||
}
|
||||
return jsonArray;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \endcond
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief The PackageVersion class helps parsing package versions.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new PackageVersion instance from the specified \a versionStr.
|
||||
*/
|
||||
PackageVersion::PackageVersion(const QString &versionStr) :
|
||||
PackageVersion(versionStr.toLocal8Bit().data())
|
||||
{}
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new PackageVersion instance from the specified \a versionStr.
|
||||
*/
|
||||
|
@ -158,10 +241,12 @@ AurPackage::AurPackage(const QJsonValue &aurJsonValue) :
|
|||
QJsonObject obj = aurJsonValue.toObject();
|
||||
m_id = obj.value(QStringLiteral("ID")).toInt(-1);
|
||||
m_categoryId = obj.value(QStringLiteral("CategoryID")).toInt(-1);
|
||||
m_name = obj.value(QStringLiteral("Name")).toString();
|
||||
m_version = obj.value(QStringLiteral("Version")).toString();
|
||||
m_description = obj.value(QStringLiteral("Description")).toString();
|
||||
m_upstreamUrl = obj.value(QStringLiteral("URL")).toString();
|
||||
m_votes = obj.value(QStringLiteral("NumVotes")).toInt(0);
|
||||
m_outOfDate = obj.value(QStringLiteral("OutOfDate")).toInt() != 0;
|
||||
m_outOfDate = DateTime::fromTimeStamp(obj.value(QStringLiteral("OutOfDate")).toInt());
|
||||
m_maintainer = obj.value(QStringLiteral("Maintainer")).toString();
|
||||
m_firstSubmitted = DateTime::fromTimeStamp(obj.value(QStringLiteral("FirstSubmitted")).toInt());
|
||||
m_lastModified = DateTime::fromTimeStamp(obj.value(QStringLiteral("LastModified")).toInt());
|
||||
|
@ -169,87 +254,65 @@ AurPackage::AurPackage(const QJsonValue &aurJsonValue) :
|
|||
m_tarUrl = obj.value(QStringLiteral("URLPath")).toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Returns basic information about the packages as JSON object.
|
||||
*/
|
||||
QJsonObject AurPackage::basicInfo(bool includeRepoAndName) const
|
||||
{
|
||||
QJsonObject packageInfo;
|
||||
if(includeRepoAndName) {
|
||||
packageInfo.insert(QStringLiteral("repo"), QStringLiteral("aur"));
|
||||
packageInfo.insert(QStringLiteral("name"), name());
|
||||
}
|
||||
packageInfo.insert(QStringLiteral("arch"), QStringLiteral("n.a."));
|
||||
packageInfo.insert(QStringLiteral("ver"), version());
|
||||
packageInfo.insert(QStringLiteral("desc"), description());
|
||||
packageInfo.insert(QStringLiteral("bdate"), QStringLiteral("n.a."));
|
||||
packageInfo.insert(QStringLiteral("flagdate"), outOfDate().isNull() ? QStringLiteral("none") : qstr(outOfDate().toString().data()));
|
||||
return packageInfo;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns full information about the package as JSON object.
|
||||
*/
|
||||
QJsonObject AurPackage::fullInfo(bool includeRepoAndName) const
|
||||
{
|
||||
QJsonObject packageInfo;
|
||||
if(includeRepoAndName) {
|
||||
packageInfo.insert(QStringLiteral("repo"), QStringLiteral("aur"));
|
||||
packageInfo.insert(QStringLiteral("name"), name());
|
||||
}
|
||||
packageInfo.insert(QStringLiteral("arch"), QStringLiteral("n.a."));
|
||||
packageInfo.insert(QStringLiteral("ver"), version());
|
||||
packageInfo.insert(QStringLiteral("desc"), description());
|
||||
packageInfo.insert(QStringLiteral("bdate"), QStringLiteral("n.a."));
|
||||
packageInfo.insert(QStringLiteral("idate"), QStringLiteral("n.a."));
|
||||
packageInfo.insert(QStringLiteral("isize"), QStringLiteral("n.a."));
|
||||
packageInfo.insert(QStringLiteral("url"), upstreamUrl());
|
||||
packageInfo.insert(QStringLiteral("lic"), license());
|
||||
packageInfo.insert(QStringLiteral("grp"), QStringLiteral("TODO"));
|
||||
packageInfo.insert(QStringLiteral("prov"), QStringLiteral("TODO"));
|
||||
packageInfo.insert(QStringLiteral("optd"), QStringLiteral("TODO"));
|
||||
packageInfo.insert(QStringLiteral("deps"), QStringLiteral("TODO"));
|
||||
packageInfo.insert(QStringLiteral("requ"), QStringLiteral("TODO"));
|
||||
packageInfo.insert(QStringLiteral("optf"), QStringLiteral("TODO"));
|
||||
packageInfo.insert(QStringLiteral("conf"), QStringLiteral("TODO"));
|
||||
packageInfo.insert(QStringLiteral("repl"), QStringLiteral("TODO"));
|
||||
packageInfo.insert(QStringLiteral("pack"), QStringLiteral("n.a."));
|
||||
packageInfo.insert(QStringLiteral("expl"), QStringLiteral("n.a."));
|
||||
packageInfo.insert(QStringLiteral("scri"), QStringLiteral("TODO"));
|
||||
packageInfo.insert(QStringLiteral("sig"), QStringLiteral("TODO"));
|
||||
packageInfo.insert(QStringLiteral("file"), QStringLiteral("n.a."));
|
||||
return packageInfo;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief The AlpmPackage class helps getting information about ALPM packages. It allows to convert the
|
||||
* information to JSON objects used by the network classes and the web interface.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \cond
|
||||
*/
|
||||
|
||||
inline QString qstr(const char *str)
|
||||
{
|
||||
return QString::fromLocal8Bit(str);
|
||||
}
|
||||
|
||||
inline QJsonArray qjarry(StringList list)
|
||||
{
|
||||
QJsonArray jsonArray;
|
||||
for(const char *str : list) {
|
||||
jsonArray << qstr(str);
|
||||
}
|
||||
return jsonArray;
|
||||
}
|
||||
|
||||
inline QJsonArray qjarry(DependencyList list)
|
||||
{
|
||||
QJsonArray jsonArray;
|
||||
for(alpm_depend_t *dep : list) {
|
||||
QJsonObject depObj;
|
||||
depObj.insert(QStringLiteral("name"), dep->name);
|
||||
depObj.insert(QStringLiteral("desc"), dep->desc);
|
||||
depObj.insert(QStringLiteral("ver"), dep->version);
|
||||
switch(dep->mod) {
|
||||
case ALPM_DEP_MOD_ANY:
|
||||
depObj.insert(QStringLiteral("mod"), QStringLiteral("any"));
|
||||
break;
|
||||
case ALPM_DEP_MOD_EQ:
|
||||
depObj.insert(QStringLiteral("mod"), QStringLiteral("eq"));
|
||||
break;
|
||||
case ALPM_DEP_MOD_GE:
|
||||
depObj.insert(QStringLiteral("mod"), QStringLiteral("ge"));
|
||||
break;
|
||||
case ALPM_DEP_MOD_LE:
|
||||
depObj.insert(QStringLiteral("mod"), QStringLiteral("le"));
|
||||
break;
|
||||
case ALPM_DEP_MOD_GT:
|
||||
depObj.insert(QStringLiteral("mod"), QStringLiteral("gt"));
|
||||
break;
|
||||
case ALPM_DEP_MOD_LT:
|
||||
depObj.insert(QStringLiteral("mod"), QStringLiteral("lt"));
|
||||
break;
|
||||
}
|
||||
jsonArray << depObj;
|
||||
}
|
||||
return jsonArray;
|
||||
}
|
||||
|
||||
inline QJsonArray qjarry(_alpm_pkgvalidation_t validation)
|
||||
{
|
||||
QJsonArray jsonArray;
|
||||
if(validation & 0x1) {
|
||||
jsonArray << QStringLiteral("none");
|
||||
}
|
||||
if(validation & 0x2) {
|
||||
jsonArray << QStringLiteral("MD5");
|
||||
}
|
||||
if(validation & 0x4) {
|
||||
jsonArray << QStringLiteral("SHA256");
|
||||
}
|
||||
if(validation & 0x8) {
|
||||
jsonArray << QStringLiteral("signature");
|
||||
}
|
||||
if(jsonArray.empty()) {
|
||||
jsonArray << QStringLiteral("unknown");
|
||||
}
|
||||
return jsonArray;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \endcond
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Returns basic information about the packages as JSON object.
|
||||
*/
|
||||
|
@ -264,6 +327,7 @@ QJsonObject AlpmPackage::basicInfo(bool includeRepoAndName) const
|
|||
packageInfo.insert(QStringLiteral("ver"), qstr(version()));
|
||||
packageInfo.insert(QStringLiteral("desc"), qstr(description()));
|
||||
packageInfo.insert(QStringLiteral("bdate"), qstr(buildDate().toString().c_str()));
|
||||
packageInfo.insert(QStringLiteral("flagdate"), QStringLiteral("n.a."));
|
||||
return packageInfo;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <string>
|
||||
|
||||
#include <QString>
|
||||
#include <QHash>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QJsonObject;
|
||||
|
@ -43,6 +44,7 @@ enum class PackageVersionPartComparsion
|
|||
class PackageVersion
|
||||
{
|
||||
public:
|
||||
PackageVersion(const QString &versionStr);
|
||||
PackageVersion(const char *versionStr);
|
||||
|
||||
static PackageVersionPartComparsion compareParts(const QString &part1, const QString &part2);
|
||||
|
@ -60,39 +62,49 @@ public:
|
|||
AurPackage(const QJsonValue &aurJsonValue);
|
||||
|
||||
bool isValid() const;
|
||||
bool hasFullInfo() const;
|
||||
int id() const;
|
||||
int categoryId() const;
|
||||
const QString &name() const;
|
||||
const QString &version() const;
|
||||
template<class Package>
|
||||
PackageVersionComparsion compareVersion(const Package &syncPackage) const;
|
||||
const QString &description() const;
|
||||
const QString &upstreamUrl() const;
|
||||
int votes() const;
|
||||
bool isOutOfDate() const;
|
||||
ChronoUtilities::DateTime outOfDate() const;
|
||||
const QString &maintainer() const;
|
||||
ChronoUtilities::DateTime firstSubmitted() const;
|
||||
ChronoUtilities::DateTime lastModified() const;
|
||||
const QString &license() const;
|
||||
const QString &tarUrl() const;
|
||||
|
||||
// JSON serialization
|
||||
QJsonObject basicInfo(bool includeRepoAndName) const;
|
||||
QJsonObject fullInfo(bool includeRepoAndName = false) const;
|
||||
|
||||
private:
|
||||
bool m_fullInfo;
|
||||
int m_id;
|
||||
int m_categoryId;
|
||||
QString m_name;
|
||||
QString m_version;
|
||||
QString m_description;
|
||||
QString m_upstreamUrl;
|
||||
int m_votes;
|
||||
bool m_outOfDate;
|
||||
ChronoUtilities::DateTime m_outOfDate;
|
||||
QString m_maintainer;
|
||||
ChronoUtilities::DateTime m_firstSubmitted;
|
||||
ChronoUtilities::DateTime m_lastModified;
|
||||
QString m_license;
|
||||
QString m_tarUrl;
|
||||
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Constructs a empty, invalid AUR package.
|
||||
*/
|
||||
inline AurPackage::AurPackage() :
|
||||
m_fullInfo(false),
|
||||
m_id(-1),
|
||||
m_categoryId(-1),
|
||||
m_votes(-1),
|
||||
|
@ -107,6 +119,14 @@ inline bool AurPackage::isValid() const
|
|||
return m_id >= 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns an indication whether full package info is available.
|
||||
*/
|
||||
inline bool AurPackage::hasFullInfo() const
|
||||
{
|
||||
return m_fullInfo;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the ID of the package in the AUR.
|
||||
*/
|
||||
|
@ -131,6 +151,20 @@ inline const QString &AurPackage::name() const
|
|||
return m_name;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the version of the package.
|
||||
*/
|
||||
inline const QString &AurPackage::version() const
|
||||
{
|
||||
return m_version;
|
||||
}
|
||||
|
||||
template<class Package>
|
||||
inline PackageVersionComparsion AurPackage::compareVersion(const Package &syncPackage) const
|
||||
{
|
||||
return PackageVersion(version()).compare(PackageVersion(syncPackage.version()));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the description of the package.
|
||||
*/
|
||||
|
@ -158,7 +192,7 @@ inline int AurPackage::votes() const
|
|||
/*!
|
||||
* \brief Returns wheter the package is flagged as out-of-date.
|
||||
*/
|
||||
inline bool AurPackage::isOutOfDate() const
|
||||
inline ChronoUtilities::DateTime AurPackage::outOfDate() const
|
||||
{
|
||||
return m_outOfDate;
|
||||
}
|
||||
|
@ -209,12 +243,14 @@ public:
|
|||
AlpmPackage(alpm_pkg_t *package = nullptr);
|
||||
|
||||
// package properties
|
||||
const alpm_pkg_t *ptr() const;
|
||||
alpm_pkg_t *ptr();
|
||||
bool hasInstallScript() const;
|
||||
const char *fileName() const;
|
||||
const char *name() const;
|
||||
const char *version() const;
|
||||
PackageVersionComparsion compareVersion(const AlpmPackage &syncPackage) const;
|
||||
template<class Package>
|
||||
PackageVersionComparsion compareVersion(const Package &syncPackage) const;
|
||||
alpm_pkgfrom_t origin() const;
|
||||
const char *description() const;
|
||||
const char *upstreamUrl() const;
|
||||
|
@ -256,6 +292,11 @@ protected:
|
|||
alpm_pkg_t *m_ptr;
|
||||
};
|
||||
|
||||
inline uint qHash(const AlpmPackage &key)
|
||||
{
|
||||
return qHash(key.ptr());
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new package instance for the specified ALPM \a package.
|
||||
*/
|
||||
|
@ -263,6 +304,11 @@ inline AlpmPackage::AlpmPackage(alpm_pkg_t *package) :
|
|||
m_ptr(package)
|
||||
{}
|
||||
|
||||
inline const alpm_pkg_t *AlpmPackage::ptr() const
|
||||
{
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
inline alpm_pkg_t *AlpmPackage::ptr()
|
||||
{
|
||||
return m_ptr;
|
||||
|
@ -293,7 +339,8 @@ inline const char *AlpmPackage::version() const
|
|||
*
|
||||
* This method distinguishes between software upgrades and package releases. See Alpm::PackageVersionComparsion enum.
|
||||
*/
|
||||
inline PackageVersionComparsion AlpmPackage::compareVersion(const AlpmPackage &syncPackage) const
|
||||
template<class Package>
|
||||
inline PackageVersionComparsion AlpmPackage::compareVersion(const Package &syncPackage) const
|
||||
{
|
||||
return PackageVersion(version()).compare(PackageVersion(syncPackage.version()));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
#include "resolvebuildorder.h"
|
||||
|
||||
#include "manager.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <c++utilities/misc/memory.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
using namespace ApplicationUtilities;
|
||||
|
||||
namespace PackageManagement {
|
||||
|
||||
class TaskInfo
|
||||
{
|
||||
public:
|
||||
TaskInfo(QString name, bool onlyDependency = false, const QList<TaskInfo *> &deps = QList<TaskInfo *>());
|
||||
|
||||
const QString &name() const;
|
||||
const QList<TaskInfo *> deps() const;
|
||||
void addDep(TaskInfo *dep);
|
||||
bool isDone() const;
|
||||
bool isVisited() const;
|
||||
bool isOnlyDependency() const;
|
||||
void add(QList<TaskInfo *> &results);
|
||||
static void addAll(const QList<TaskInfo *> &tasks, QList<TaskInfo *> &results);
|
||||
static TaskInfo *find(const QList<TaskInfo *> &tasks, const QString &name);
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
QList<TaskInfo *> m_deps;
|
||||
bool m_done;
|
||||
bool m_visited;
|
||||
bool m_onlyDep;
|
||||
};
|
||||
|
||||
inline TaskInfo::TaskInfo(QString name, bool onlyDependency, const QList<TaskInfo *> &deps) :
|
||||
m_name(name),
|
||||
m_deps(deps),
|
||||
m_done(false),
|
||||
m_visited(false),
|
||||
m_onlyDep(onlyDependency)
|
||||
{}
|
||||
|
||||
inline const QString &TaskInfo::name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
inline const QList<TaskInfo *> TaskInfo::deps() const
|
||||
{
|
||||
return m_deps;
|
||||
}
|
||||
|
||||
inline void TaskInfo::addDep(TaskInfo *dep)
|
||||
{
|
||||
m_deps << dep;
|
||||
}
|
||||
|
||||
inline bool TaskInfo::isDone() const
|
||||
{
|
||||
return m_done;
|
||||
}
|
||||
|
||||
inline bool TaskInfo::isVisited() const
|
||||
{
|
||||
return m_visited;
|
||||
}
|
||||
|
||||
inline bool TaskInfo::isOnlyDependency() const
|
||||
{
|
||||
return m_onlyDep;
|
||||
}
|
||||
|
||||
void TaskInfo::add(QList<TaskInfo *> &results)
|
||||
{
|
||||
if(!m_done) {
|
||||
if(m_visited) {
|
||||
throw *this; // cyclic dependency
|
||||
} else {
|
||||
m_visited = true;
|
||||
}
|
||||
for(auto *dep : m_deps) {
|
||||
dep->add(results);
|
||||
}
|
||||
m_done = true;
|
||||
results << this;
|
||||
}
|
||||
}
|
||||
|
||||
void TaskInfo::addAll(const QList<TaskInfo *> &tasks, QList<TaskInfo *> &results)
|
||||
{
|
||||
for(auto *task : tasks) {
|
||||
if(!task->m_done) {
|
||||
task->add(results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TaskInfo *TaskInfo::find(const QList<TaskInfo *> &tasks, const QString &name)
|
||||
{
|
||||
for(auto *task : tasks) {
|
||||
if(task->name() == name) {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<class ListType>
|
||||
class DestroyList
|
||||
{
|
||||
public:
|
||||
DestroyList(ListType &list) :
|
||||
m_list(list)
|
||||
{}
|
||||
|
||||
~DestroyList()
|
||||
{
|
||||
qDeleteAll(m_list);
|
||||
}
|
||||
|
||||
private:
|
||||
ListType &m_list;
|
||||
};
|
||||
|
||||
BuildOrderResolver::BuildOrderResolver(const Manager &manager) :
|
||||
m_manager(manager)
|
||||
{}
|
||||
|
||||
QStringList BuildOrderResolver::resolve(const StringVector &packages) const
|
||||
{
|
||||
cerr << "Getting package information ..." << endl;
|
||||
QList<TaskInfo *> tasks;
|
||||
tasks.reserve(packages.size());
|
||||
try {
|
||||
// add a task for each specified package
|
||||
for(const auto &pkgName : packages) {
|
||||
tasks << new TaskInfo(QString::fromLocal8Bit(pkgName.data()));
|
||||
}
|
||||
// find specified packages and their dependencies
|
||||
for(auto *task : tasks) {
|
||||
addDeps(tasks, task);
|
||||
}
|
||||
cerr << "Relevant packages: ";
|
||||
for(const auto *task : tasks) {
|
||||
cerr << task->name().toLocal8Bit().data() << ' ';
|
||||
}
|
||||
cerr << endl;
|
||||
// topo sort
|
||||
QList<TaskInfo *> results;
|
||||
results.reserve(tasks.size());
|
||||
try {
|
||||
TaskInfo::addAll(tasks, results);
|
||||
QStringList names;
|
||||
names.reserve(results.size());
|
||||
for(const auto *res : results) {
|
||||
names << res->name();
|
||||
}
|
||||
return names;
|
||||
} catch (const TaskInfo &cyclic) {
|
||||
throw runtime_error("Can't resolve build order; the package " + cyclic.name().toStdString() + " is a cyclic dependency.");
|
||||
}
|
||||
} catch(...) {
|
||||
qDeleteAll(tasks);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void BuildOrderResolver::addDeps(QList<TaskInfo *> &tasks, TaskInfo *task) const
|
||||
{
|
||||
if(const auto pkg = m_manager.packageFromSyncDataBases(task->name().toLocal8Bit().data())) {
|
||||
for(auto dep : pkg.dependencies()) {
|
||||
if(auto *depTask = addDep(tasks, dep->name)) {
|
||||
task->addDep(depTask);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stringstream ss;
|
||||
ss << "The package \"" << task->name().toLocal8Bit().data() << "\" could not be found; TODO: search AUR for package, add AUR deps to the packages we want to build";
|
||||
throw runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
TaskInfo *BuildOrderResolver::addDep(QList<TaskInfo *> &tasks, const char *depName) const
|
||||
{
|
||||
if(auto *task = TaskInfo::find(tasks, depName)) {
|
||||
// we've already added a task for this dependency
|
||||
return task;
|
||||
} else {
|
||||
// create new task
|
||||
//task = new TaskInfo(QString::fromLocal8Bit(depName));
|
||||
//tasks << task;
|
||||
//return task;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace PackageManagement
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef PACKAGEMANAGEMENT_RESOLVEBUILDORDER_H
|
||||
#define PACKAGEMANAGEMENT_RESOLVEBUILDORDER_H
|
||||
|
||||
#include <c++utilities/application/argumentparser.h>
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
|
||||
namespace PackageManagement {
|
||||
|
||||
class Manager;
|
||||
class TaskInfo;
|
||||
|
||||
class BuildOrderResolver
|
||||
{
|
||||
public:
|
||||
BuildOrderResolver(const Manager &manager);
|
||||
|
||||
QStringList resolve(const ApplicationUtilities::StringVector &packages) const;
|
||||
|
||||
private:
|
||||
void addDeps(QList<TaskInfo *> &tasks, TaskInfo *task) const;
|
||||
TaskInfo *addDep(QList<TaskInfo *> &pkgInfos, const char *depName) const;
|
||||
const Manager &m_manager;
|
||||
};
|
||||
|
||||
} // namespace PackageManagement
|
||||
|
||||
#endif // PACKAGEMANAGEMENT_RESOLVEBUILDORDER_H
|
|
@ -0,0 +1,185 @@
|
|||
#include "updatelookup.h"
|
||||
#include "manager.h"
|
||||
#include "database.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <QNetworkReply>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace PackageManagement {
|
||||
|
||||
UpdateLookup::UpdateLookup(const Manager &manager, const QJsonObject &request, const UpdateLookupCallback callback, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_manager(manager),
|
||||
m_request(request),
|
||||
m_callback(callback),
|
||||
m_db(nullptr),
|
||||
m_syncDbsWatcher(new QFutureWatcher<UpdateLookupResults<AlpmPackage> >(this)),
|
||||
m_aurWatcher(new QFutureWatcher<UpdateLookupResults<AurPackage> >(this)),
|
||||
m_aurReply(nullptr),
|
||||
m_sourcesAvailable(false)
|
||||
{
|
||||
QJsonArray errors;
|
||||
const auto dbName = request.value(QStringLiteral("db")).toString();
|
||||
try {
|
||||
m_db = &manager.dataBaseByName(dbName);
|
||||
} catch (const out_of_range &) {
|
||||
errors << QStringLiteral("Database \"%1\" can not be found.").arg(dbName);
|
||||
}
|
||||
if(errors.isEmpty()) {
|
||||
// invoke syncdb lookup
|
||||
connect(m_syncDbsWatcher, &QFutureWatcher<UpdateLookupResults<AlpmPackage> >::finished, this, &UpdateLookup::lookupDone);
|
||||
m_syncDbsWatcher->setFuture(QtConcurrent::run(this, &UpdateLookup::checkSyncDbs, request.value(QStringLiteral("syncdbs"))));
|
||||
// invoke AUR lookup
|
||||
if(m_db->upgradeSources().contains(QStringLiteral("aur"), Qt::CaseInsensitive) || request.value(QStringLiteral("aur")).toBool(false)) {
|
||||
if(manager.config().isAurEnabled()) {
|
||||
connect(&m_manager.aurQuery(), &AurQuery::packageInfoAvailable, this, &UpdateLookup::aurPackageInfoAvailable);
|
||||
connect(m_aurWatcher, &QFutureWatcher<UpdateLookupResults<AurPackage> >::finished, this, &UpdateLookup::lookupDone);
|
||||
if(!(m_aurReply = m_manager.aurQuery().requestPackageInfo(m_db->packageNames()))) {
|
||||
aurPackageInfoAvailable(nullptr);
|
||||
}
|
||||
} else {
|
||||
errors << QStringLiteral("The AUR is configured as upgrade source for the database \"%1\" but AUR queries are not enabled.").arg(dbName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QJsonObject results;
|
||||
results.insert(QStringLiteral("errors"), errors);
|
||||
callback(move(results));
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
UpdateLookupResults<AlpmPackage> UpdateLookup::checkSyncDbs(const QJsonValue &syncDbsValue)
|
||||
{
|
||||
UpdateLookupResults<AlpmPackage> results;
|
||||
const auto &syncDbs = m_manager.syncDataBases();
|
||||
QList<const AlpmDataBase *> syncDbSel;
|
||||
if(syncDbsValue.type() == QJsonValue::Array) {
|
||||
for(const auto &syncDbVal : syncDbsValue.toArray()) {
|
||||
const auto syncDbName = syncDbVal.toString();
|
||||
if(syncDbName == QLatin1String("local")) {
|
||||
syncDbSel << &(m_manager.localDataBase());
|
||||
} else if(syncDbName == QLatin1String("aur")) {
|
||||
continue; // the AUR is checked separately
|
||||
} else {
|
||||
try {
|
||||
syncDbSel << &(syncDbs.at(syncDbName));
|
||||
} catch(out_of_range &) {
|
||||
results.warnings << QStringLiteral("The specified sync database \"%1\" can not be found.").arg(syncDbName);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(const auto &syncDbName : m_db->upgradeSources()) {
|
||||
if(syncDbName == QLatin1String("local")) {
|
||||
syncDbSel << &(m_manager.localDataBase());
|
||||
} else if(syncDbName == QLatin1String("aur")) {
|
||||
continue; // the AUR is checked separately
|
||||
} else {
|
||||
try {
|
||||
syncDbSel << &(syncDbs.at(syncDbName));
|
||||
} catch(out_of_range &) {
|
||||
results.warnings << QStringLiteral("The associated upgrade database \"%1\" can not be found.").arg(syncDbName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_db->checkForUpgrades(syncDbSel, results);
|
||||
return results;
|
||||
}
|
||||
|
||||
void UpdateLookup::aurPackageInfoAvailable(const QNetworkReply *reply)
|
||||
{
|
||||
// check whether the package info requested by THIS INSTANCE is available
|
||||
if(m_aurReply == reply) {
|
||||
m_aurWatcher->setFuture(QtConcurrent::run(this, &UpdateLookup::checkAur));
|
||||
}
|
||||
}
|
||||
|
||||
UpdateLookupResults<AurPackage> UpdateLookup::checkAur()
|
||||
{
|
||||
UpdateLookupResults<AurPackage> results;
|
||||
m_db->checkForUpgrades(m_manager.aurQuery().packages(), results);
|
||||
return results;
|
||||
}
|
||||
|
||||
void UpdateLookup::lookupDone()
|
||||
{
|
||||
const auto *sender = this->sender();
|
||||
if(sender == static_cast<QObject *>(m_syncDbsWatcher)) {
|
||||
// add results from syncdb lookup
|
||||
m_syncDbsResults = m_syncDbsWatcher->result();
|
||||
if(!m_syncDbsResults.noSources) {
|
||||
m_sourcesAvailable = true;
|
||||
for(const auto pkg : m_syncDbsResults.newVersions) {
|
||||
m_softwareUpdates << pkg.json();
|
||||
}
|
||||
for(const auto pkg : m_syncDbsResults.newReleases) {
|
||||
m_packageOnlyUpdates << pkg.json();
|
||||
}
|
||||
for(const auto pkg : m_syncDbsResults.downgrades) {
|
||||
m_downgrades << pkg.json();
|
||||
}
|
||||
for(const auto &warning : m_syncDbsResults.warnings) {
|
||||
m_warnings << warning;
|
||||
}
|
||||
for(const auto &error : m_syncDbsResults.errors) {
|
||||
m_errors << error;
|
||||
}
|
||||
}
|
||||
} else if(sender == static_cast<QObject *>(m_aurWatcher)) {
|
||||
// add results from AUR lookup
|
||||
m_aurResults = m_aurWatcher->result();
|
||||
if(!m_aurResults.noSources) {
|
||||
m_sourcesAvailable = true;
|
||||
for(const auto pkg : m_aurResults.newVersions) {
|
||||
m_softwareUpdates << pkg.json();
|
||||
}
|
||||
for(const auto pkg : m_aurResults.newReleases) {
|
||||
m_packageOnlyUpdates << pkg.json();
|
||||
}
|
||||
for(const auto pkg : m_aurResults.downgrades) {
|
||||
m_downgrades << pkg.json();
|
||||
}
|
||||
for(const auto &warning : m_aurResults.warnings) {
|
||||
m_warnings << warning;
|
||||
}
|
||||
for(const auto &error : m_aurResults.errors) {
|
||||
m_errors << error;
|
||||
}
|
||||
}
|
||||
}
|
||||
// check whether everything is done
|
||||
if(m_syncDbsWatcher->isFinished() && (!m_aurReply || m_aurWatcher->isFinished())) {
|
||||
QJsonObject results;
|
||||
// determine orphaned packages
|
||||
for(const auto pkg : m_aurReply ? m_aurResults.orphaned.intersect(m_syncDbsResults.orphaned) : m_syncDbsResults.orphaned) {
|
||||
m_orphanedPackages << pkg.basicInfo(true);
|
||||
}
|
||||
// add results to results QJsonObject
|
||||
results.insert(QStringLiteral("softwareUpdates"), m_softwareUpdates);
|
||||
results.insert(QStringLiteral("packageOnlyUpdates"), m_packageOnlyUpdates);
|
||||
results.insert(QStringLiteral("downgrades"), m_downgrades);
|
||||
results.insert(QStringLiteral("orphanedPackages"), m_orphanedPackages);
|
||||
if(!m_sourcesAvailable) {
|
||||
m_errors << QStringLiteral("No update sources associated for database \"%1\".").arg(QString::fromLocal8Bit(m_db->name()));
|
||||
}
|
||||
if(!m_warnings.isEmpty()) {
|
||||
results.insert(QStringLiteral("warnings"), m_warnings);
|
||||
}
|
||||
if(!m_errors.isEmpty()) {
|
||||
results.insert(QStringLiteral("errors"), m_errors);
|
||||
}
|
||||
m_callback(move(results));
|
||||
// lookup done, delete this helper object
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace PackageManagement
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
#ifndef PACKAGEMANAGEMENT_UPDATELOOKUP_H
|
||||
#define PACKAGEMANAGEMENT_UPDATELOOKUP_H
|
||||
|
||||
#include "package.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QFutureWatcher>
|
||||
|
||||
#include <functional>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QNetworkReply)
|
||||
|
||||
namespace PackageManagement {
|
||||
|
||||
class Manager;
|
||||
class AlpmDataBase;
|
||||
|
||||
typedef std::function<void (const QJsonObject &&results)> UpdateLookupCallback;
|
||||
|
||||
template<class Package, class VersionType = QString>
|
||||
class UpdateResult
|
||||
{
|
||||
public:
|
||||
UpdateResult(const Package &package, const VersionType &previousVersion);
|
||||
Package package;
|
||||
VersionType previousVersion;
|
||||
QJsonObject json() const;
|
||||
};
|
||||
|
||||
template<class Package, class VersionType>
|
||||
inline UpdateResult<Package, VersionType>::UpdateResult(const Package &package, const VersionType &previousVersion) :
|
||||
package(package),
|
||||
previousVersion(previousVersion)
|
||||
{}
|
||||
|
||||
template<class Package, class VersionType>
|
||||
QJsonObject UpdateResult<Package, VersionType>::json() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj.insert(QStringLiteral("pkg"), package.basicInfo(true));
|
||||
obj.insert(QStringLiteral("prevVersion"), previousVersion);
|
||||
return obj;
|
||||
}
|
||||
|
||||
template<class Package>
|
||||
inline UpdateResult<Package> makeUpdateResult(const Package &package, const QString &previousVersion)
|
||||
{
|
||||
return UpdateResult<Package>(package, previousVersion);
|
||||
}
|
||||
|
||||
template<class Package>
|
||||
inline UpdateResult<Package> makeUpdateResult(const Package &package, const char *previousVersion)
|
||||
{
|
||||
return UpdateResult<Package>(package, QString::fromLocal8Bit(previousVersion));
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Package>
|
||||
class UpdateLookupResults
|
||||
{
|
||||
public:
|
||||
UpdateLookupResults();
|
||||
|
||||
/*!
|
||||
* \brief Indicates that there are no upgrade sources available.
|
||||
*/
|
||||
bool noSources;
|
||||
|
||||
/*!
|
||||
* \brief Packages providing a software upgrade (new version).
|
||||
*/
|
||||
QList<UpdateResult<Package> > newVersions;
|
||||
|
||||
/*!
|
||||
* \brief Package upgrades only (new release).
|
||||
*/
|
||||
QList<UpdateResult<Package> > newReleases;
|
||||
|
||||
/*!
|
||||
* \brief Downgrades (older version in sync db).
|
||||
*/
|
||||
QList<UpdateResult<Package> > downgrades;
|
||||
|
||||
/*!
|
||||
* \brief Orphaned packages (could not be found in any of the sync dbs).
|
||||
*/
|
||||
QSet<AlpmPackage> orphaned;
|
||||
|
||||
/*!
|
||||
* \brief Warnings occured when checking for updates.
|
||||
*/
|
||||
QStringList warnings;
|
||||
|
||||
/*!
|
||||
* \brief Errors occured when checking for updates.
|
||||
*/
|
||||
QStringList errors;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Constructs new update lookup results.
|
||||
*/
|
||||
template<class Package>
|
||||
inline UpdateLookupResults<Package>::UpdateLookupResults() :
|
||||
noSources(false)
|
||||
{}
|
||||
|
||||
class UpdateLookup : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit UpdateLookup(const Manager &manager, const QJsonObject &request, const UpdateLookupCallback callback, QObject *parent = nullptr);
|
||||
|
||||
private slots:
|
||||
UpdateLookupResults<AlpmPackage> checkSyncDbs(const QJsonValue &syncDbsValue);
|
||||
void aurPackageInfoAvailable(const QNetworkReply *reply);
|
||||
UpdateLookupResults<AurPackage> checkAur();
|
||||
|
||||
void lookupDone();
|
||||
|
||||
private:
|
||||
const Manager &m_manager;
|
||||
const QJsonObject m_request;
|
||||
const UpdateLookupCallback m_callback;
|
||||
const AlpmDataBase *m_db;
|
||||
QFutureWatcher<UpdateLookupResults<AlpmPackage> > *m_syncDbsWatcher;
|
||||
QFutureWatcher<UpdateLookupResults<AurPackage> > *m_aurWatcher;
|
||||
UpdateLookupResults<AlpmPackage> m_syncDbsResults;
|
||||
UpdateLookupResults<AurPackage> m_aurResults;
|
||||
QNetworkReply *m_aurReply;
|
||||
bool m_sourcesAvailable;
|
||||
QJsonArray m_warnings;
|
||||
QJsonArray m_errors;
|
||||
QJsonArray m_softwareUpdates;
|
||||
QJsonArray m_packageOnlyUpdates;
|
||||
QJsonArray m_downgrades;
|
||||
QJsonArray m_orphanedPackages;
|
||||
};
|
||||
|
||||
} // namespace PackageManagement
|
||||
|
||||
#endif // PACKAGEMANAGEMENT_UPDATELOOKUP_H
|
40
main.cpp
40
main.cpp
|
@ -1,6 +1,8 @@
|
|||
#include "alpm/manager.h"
|
||||
#include "alpm/utilities.h"
|
||||
#include "alpm/config.h"
|
||||
#include "alpm/resolvebuildorder.h"
|
||||
#include "alpm/mingwbundle.h"
|
||||
#include "network/server.h"
|
||||
|
||||
#include <c++utilities/application/argumentparser.h>
|
||||
|
@ -9,6 +11,7 @@
|
|||
#include <QCoreApplication>
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
using namespace ApplicationUtilities;
|
||||
|
@ -33,17 +36,34 @@ int main(int argc, char *argv[])
|
|||
Config config;
|
||||
config.loadFromConfigFile(configArgs);
|
||||
config.loadFromArgs(configArgs);
|
||||
// run websocket server
|
||||
if(configArgs.serverArg.isPresent()) {
|
||||
// setup ALPM
|
||||
Manager alpmManager(config);
|
||||
alpmManager.parsePacmanConfig();
|
||||
// setup the server
|
||||
if(find_if(parser.mainArguments().cbegin(), parser.mainArguments().cend(), [&configArgs] (const Argument *arg) {
|
||||
return arg != &configArgs.helpArg && arg->isPresent();
|
||||
}) != parser.mainArguments().cend()) {
|
||||
// create app
|
||||
QCoreApplication application(argc, argv);
|
||||
Server server(alpmManager, config);
|
||||
QObject::connect(&server, &Server::closed, &application, &QCoreApplication::quit);
|
||||
// run Qt loop
|
||||
return application.exec();
|
||||
// setup ALPM
|
||||
Manager manager(config);
|
||||
manager.applyPacmanConfig();
|
||||
manager.applyRepoIndexConfig();
|
||||
if(configArgs.serverArg.isPresent()) {
|
||||
// setup the server
|
||||
Server server(manager, manager.config());
|
||||
QObject::connect(&server, &Server::closed, &application, &QCoreApplication::quit);
|
||||
// run Qt loop
|
||||
return application.exec();
|
||||
} else if(configArgs.buildOrderArg.isPresent()) {
|
||||
BuildOrderResolver resolver(manager);
|
||||
const QStringList results = resolver.resolve(configArgs.buildOrderArg.values());
|
||||
// print results
|
||||
cout << "Results: ";
|
||||
for(const auto &pkgName : results) {
|
||||
cout << pkgName.toLocal8Bit().data() << ' ';
|
||||
}
|
||||
cout << endl;
|
||||
} else if(configArgs.mingwBundleArg.isPresent()) {
|
||||
MingwBundle bundle(manager, configArgs.mingwBundleArg.values());
|
||||
bundle.createBundle(configArgs.outputFileArg.values().front());
|
||||
}
|
||||
} else {
|
||||
cout << "No command line arguments specified. See --help for available commands." << endl;
|
||||
}
|
||||
|
|
|
@ -16,53 +16,105 @@ using namespace std;
|
|||
|
||||
namespace PackageManagement {
|
||||
|
||||
QUrl AurQuery::m_aur4RequestUrl = QUrl(QStringLiteral("https://aur.archlinux.org/rpc.php"));
|
||||
QUrl AurQuery::m_aurRpcUrl = QUrl(QStringLiteral("https://aur.archlinux.org/rpc.php"));
|
||||
|
||||
QUrl AurQuery::m_aurPkgbuildUrl = QUrl(QStringLiteral("https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD"));
|
||||
|
||||
const char *requestTypeProp = "type";
|
||||
const QString rpcRequestTypeKey(QStringLiteral("type"));
|
||||
const QString rpcRequestTypeSuggest(QStringLiteral("suggest"));
|
||||
const QString rpcRequestTypeMultiInfo(QStringLiteral("multiinfo"));
|
||||
const QString rpcArgKey(QStringLiteral("arg"));
|
||||
const QString rpcArgArray(QStringLiteral("arg[]"));
|
||||
const QString pkgbuildRequestType(QString("pkgbuild"));
|
||||
|
||||
AurQuery::AurQuery(QNetworkAccessManager &networkAccessManager, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_networkAccessManager(networkAccessManager)
|
||||
{}
|
||||
|
||||
void AurQuery::requestSuggestions(const QString &phrase) const
|
||||
QNetworkReply *AurQuery::requestSuggestions(const QString &phrase) const
|
||||
{
|
||||
auto url = m_aur4RequestUrl;
|
||||
auto url = m_aurRpcUrl;
|
||||
QUrlQuery query;
|
||||
query.addQueryItem(QStringLiteral("type"), QStringLiteral("suggest"));
|
||||
query.addQueryItem(QStringLiteral("arg"), phrase);
|
||||
//auto *reply = m_networkAccessManager.get(QNetworkRequest(url));
|
||||
//connect(reply, &QNetworkReply::finished, this, &AurEnquiry::suggestionsReceived);
|
||||
}
|
||||
|
||||
void AurQuery::requestPackageInfo(const QStringList &packageNames) const
|
||||
{
|
||||
auto url = m_aur4RequestUrl;
|
||||
QUrlQuery query;
|
||||
query.addQueryItem(QStringLiteral("type"), QStringLiteral("multiinfo"));
|
||||
for(const auto &packageName : packageNames) {
|
||||
query.addQueryItem(QStringLiteral("arg[]"), packageName);
|
||||
}
|
||||
query.addQueryItem(rpcRequestTypeKey, rpcRequestTypeSuggest);
|
||||
query.addQueryItem(rpcArgKey, phrase);
|
||||
url.setQuery(query);
|
||||
auto *reply = m_networkAccessManager.get(QNetworkRequest(url));
|
||||
reply->setProperty("type", QStringLiteral("packageinfo"));
|
||||
connect(reply, &QNetworkReply::finished, this, &AurQuery::packageInfoReceived);
|
||||
reply->setProperty(requestTypeProp, rpcRequestTypeSuggest);
|
||||
connect(reply, &QNetworkReply::finished, this, &AurQuery::dataReceived);
|
||||
return reply;
|
||||
}
|
||||
|
||||
void AurQuery::packageInfoReceived()
|
||||
/*!
|
||||
* \brief Requests package information for the specified packages from the AUR.
|
||||
* \returns Returns the QNetworkReply used for the request.
|
||||
* \remarks
|
||||
* If \a forceUpdate is true, package information which has already been retrieved
|
||||
* and is still cached is requested again. Otherwise these packages will not be
|
||||
* requested again. If it turns out, that all packages are already cached, nullptr
|
||||
* is returned in this case.
|
||||
*/
|
||||
QNetworkReply *AurQuery::requestPackageInfo(const QStringList &packageNames, bool forceUpdate) const
|
||||
{
|
||||
QUrlQuery query;
|
||||
for(const auto &packageName : packageNames) {
|
||||
if(forceUpdate || !m_packages.count(packageName)) {
|
||||
query.addQueryItem(rpcArgArray, packageName);
|
||||
}
|
||||
}
|
||||
if(query.isEmpty()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
auto url = m_aurRpcUrl;
|
||||
query.addQueryItem(rpcRequestTypeKey, rpcRequestTypeMultiInfo);
|
||||
url.setQuery(query);
|
||||
auto *reply = m_networkAccessManager.get(QNetworkRequest(url));
|
||||
reply->setProperty(requestTypeProp, rpcRequestTypeMultiInfo);
|
||||
connect(reply, &QNetworkReply::finished, this, &AurQuery::dataReceived);
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
|
||||
QNetworkReply *AurQuery::requestFullPackageInfo(const QString &package, bool forceUpdate) const
|
||||
{
|
||||
try {
|
||||
const auto &pkg = m_packages.at(package);
|
||||
if(pkg.hasFullInfo() && !forceUpdate) {
|
||||
return nullptr;
|
||||
}
|
||||
} catch(const out_of_range &) {
|
||||
}
|
||||
auto url = m_aurPkgbuildUrl;
|
||||
QUrlQuery query;
|
||||
query.addQueryItem(QStringLiteral("h"), package);
|
||||
url.setQuery(query);
|
||||
auto *reply = m_networkAccessManager.get(QNetworkRequest(url));
|
||||
reply->setProperty(requestTypeProp, pkgbuildRequestType);
|
||||
connect(reply, &QNetworkReply::finished, this, &AurQuery::dataReceived);
|
||||
return reply;
|
||||
}
|
||||
|
||||
void AurQuery::dataReceived()
|
||||
{
|
||||
if(auto *reply = qobject_cast<QNetworkReply *>(sender())) {
|
||||
if(reply->error() == QNetworkReply::NoError) {
|
||||
QJsonParseError error;
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &error);
|
||||
QByteArray data = reply->readAll();
|
||||
cerr << "AUR reply: " << data.data() << endl;
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(data, &error);
|
||||
//const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &error);
|
||||
if(error.error == QJsonParseError::NoError) {
|
||||
const auto results = doc.object().value(QStringLiteral("results")).toArray();
|
||||
for(const auto &result : results) {
|
||||
AurPackage package(result);
|
||||
if(!package.name().isEmpty()) {
|
||||
m_packages.emplace(package.name(), move(package));
|
||||
}
|
||||
const QString requestType = reply->property(requestTypeProp).toString();
|
||||
if(requestType == rpcRequestTypeSuggest) {
|
||||
emit suggestionsAvailable(reply, doc.array());
|
||||
} else if(requestType == rpcRequestTypeMultiInfo) {
|
||||
processPackageInfo(reply, doc.object().value(QStringLiteral("results")).toArray());
|
||||
} else {
|
||||
cerr << "Error: Reply has invalid type. (should never happen)" << endl;
|
||||
}
|
||||
emit packageInfoAvailable();
|
||||
} else {
|
||||
cerr << "Error: Unable to parse JSON received from AUR: " << error.errorString().toLocal8Bit().data() << endl;
|
||||
cerr << "Error: Unable to parse JSON received from AUR: " << error.errorString().toLocal8Bit().data() << " at character " << error.offset << endl;
|
||||
}
|
||||
} else {
|
||||
cerr << "Error: Unable to request data from AUR: " << reply->errorString().toLocal8Bit().data() << endl;
|
||||
|
@ -70,5 +122,16 @@ void AurQuery::packageInfoReceived()
|
|||
}
|
||||
}
|
||||
|
||||
void AurQuery::processPackageInfo(const QNetworkReply *reply, const QJsonArray &results)
|
||||
{
|
||||
for(const auto &result : results) {
|
||||
AurPackage package(result);
|
||||
if(!package.name().isEmpty()) {
|
||||
m_packages[package.name()] = move(package);
|
||||
}
|
||||
}
|
||||
emit packageInfoAvailable(reply);
|
||||
}
|
||||
|
||||
} // namespace Alpm
|
||||
|
||||
|
|
|
@ -9,49 +9,58 @@
|
|||
#include <vector>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QNetworkAccessManager)
|
||||
QT_FORWARD_DECLARE_CLASS(QNetworkReply)
|
||||
|
||||
namespace PackageManagement {
|
||||
|
||||
class AurQuery : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AurQuery(QNetworkAccessManager &networkAccessManager, QObject *parent = nullptr);
|
||||
|
||||
static const QUrl aur4RequestUrl();
|
||||
static void setAur4RequestUrl(const QUrl &aur4RequestUrl);
|
||||
|
||||
void requestSuggestions(const QString &phrase) const;
|
||||
void requestPackageInfo(const QStringList &packageNames) const;
|
||||
static const QUrl aurRpcUrl();
|
||||
static void setAurRpcUrl(const QUrl &aurRpcUrl);
|
||||
|
||||
QNetworkReply *requestSuggestions(const QString &phrase) const;
|
||||
QNetworkReply *requestPackageInfo(const QStringList &packageNames, bool forceUpdate = false) const;
|
||||
QNetworkReply *requestFullPackageInfo(const QString &package, bool forceUpdate = false) const;
|
||||
const std::map<QString, AurPackage> &packages() const;
|
||||
std::map<QString, AurPackage> &packages();
|
||||
|
||||
signals:
|
||||
void suggestionsAvailable(const QJsonArray &suggestions);
|
||||
void packageInfoAvailable();
|
||||
|
||||
void suggestionsAvailable(const QNetworkReply *reply, const QJsonArray &suggestions);
|
||||
void packageInfoAvailable(const QNetworkReply *reply);
|
||||
|
||||
private slots:
|
||||
void packageInfoReceived();
|
||||
void dataReceived();
|
||||
|
||||
private:
|
||||
void processPackageInfo(const QNetworkReply *reply, const QJsonArray &results);
|
||||
|
||||
QNetworkAccessManager &m_networkAccessManager;
|
||||
std::map<QString, AurPackage> m_packages;
|
||||
|
||||
static QUrl m_aurRequstUrl;
|
||||
static QUrl m_aur4RequestUrl;
|
||||
|
||||
static QUrl m_aurRpcUrl;
|
||||
static QUrl m_aurPkgbuildUrl;
|
||||
};
|
||||
|
||||
inline const QUrl AurQuery::aur4RequestUrl()
|
||||
inline const QUrl AurQuery::aurRpcUrl()
|
||||
{
|
||||
return m_aur4RequestUrl;
|
||||
return m_aurRpcUrl;
|
||||
}
|
||||
|
||||
inline void AurQuery::setAur4RequestUrl(const QUrl &aur4Url)
|
||||
inline void AurQuery::setAurRpcUrl(const QUrl &aur4Url)
|
||||
{
|
||||
m_aur4RequestUrl = aur4Url;
|
||||
m_aurRpcUrl = aur4Url;
|
||||
}
|
||||
|
||||
inline const std::map<QString, AurPackage> &AurQuery::packages() const
|
||||
{
|
||||
return m_packages;
|
||||
}
|
||||
|
||||
|
||||
inline std::map<QString, AurPackage> &AurQuery::packages()
|
||||
{
|
||||
return m_packages;
|
||||
|
|
|
@ -13,7 +13,7 @@ using namespace std;
|
|||
|
||||
namespace Network {
|
||||
|
||||
Connection::Connection(PackageManagement::Manager &alpmManager, QWebSocket *socket, QObject *parent) :
|
||||
Connection::Connection(const PackageManagement::Manager &alpmManager, QWebSocket *socket, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_socket(socket),
|
||||
m_alpmManager(alpmManager),
|
||||
|
@ -78,8 +78,8 @@ void Connection::handleQuery(const QJsonObject &obj)
|
|||
m_groupInfoUpdatesRequested = obj.value(QStringLiteral("updates")).toBool(m_groupInfoUpdatesRequested);
|
||||
sendResults(what, id, m_alpmManager.groupInfo());
|
||||
} else if(what == QLatin1String("checkforupdates")) {
|
||||
m_alpmManager.invokeUpgradeLookup(obj);
|
||||
//m_alpmManager.invokeUpgradeLookup(obj, bind(&Connection::sendResult, this, what, id, placeholders::_1));
|
||||
//sendResult(what, id, m_alpmManager.invokeUpgradeLookup(obj));
|
||||
m_alpmManager.invokeUpgradeLookup(obj, bind(&Connection::sendResult, this, what, id, placeholders::_1));
|
||||
//m_alpmManager.checkForUpgrades(obj, [this] (const QJsonObject &results) {
|
||||
// sendResult(what, id, results);
|
||||
//};
|
||||
|
|
|
@ -18,7 +18,7 @@ class Connection : public QObject
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Connection(PackageManagement::Manager &alpmManager, QWebSocket *socket, QObject *parent = nullptr);
|
||||
Connection(const PackageManagement::Manager &alpmManager, QWebSocket *socket, QObject *parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void processTextMessage(const QString &message);
|
||||
|
@ -33,7 +33,7 @@ private:
|
|||
void handleQuery(const QJsonObject &obj);
|
||||
|
||||
QWebSocket *m_socket;
|
||||
PackageManagement::Manager &m_alpmManager;
|
||||
const PackageManagement::Manager &m_alpmManager;
|
||||
bool m_repoInfoUpdatesRequested;
|
||||
bool m_groupInfoUpdatesRequested;
|
||||
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
#include "query.h"
|
||||
|
||||
namespace Network {
|
||||
|
||||
Query::Query(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
} // namespace Network
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
#ifndef NETWORK_QUERY_H
|
||||
#define NETWORK_QUERY_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace Network {
|
||||
|
||||
class Query : public QObject
|
||||
{
|
||||
public:
|
||||
Query(QObject *parent = nullptr);
|
||||
|
||||
private:
|
||||
QString m_what;
|
||||
QString m_clientId;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Network
|
||||
|
||||
#endif // NETWORK_QUERY_H
|
|
@ -14,7 +14,7 @@ using namespace std;
|
|||
|
||||
namespace Network {
|
||||
|
||||
Server::Server(PackageManagement::Manager &alpmManager, const PackageManagement::Config &config, QObject *parent) :
|
||||
Server::Server(const PackageManagement::Manager &alpmManager, const PackageManagement::Config &config, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_server(new QWebSocketServer(QStringLiteral("Repository index server"),
|
||||
config.serverInsecure() ? QWebSocketServer::NonSecureMode : QWebSocketServer::SecureMode,
|
||||
|
|
|
@ -22,7 +22,7 @@ class Server : public QObject
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Server(PackageManagement::Manager &alpmManager, const PackageManagement::Config &config, QObject *parent = nullptr);
|
||||
Server(const PackageManagement::Manager &alpmManager, const PackageManagement::Config &config, QObject *parent = nullptr);
|
||||
~Server();
|
||||
|
||||
signals:
|
||||
|
@ -34,7 +34,7 @@ private slots:
|
|||
|
||||
private:
|
||||
QWebSocketServer *m_server;
|
||||
PackageManagement::Manager &m_alpmManager;
|
||||
const PackageManagement::Manager &m_alpmManager;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,50 +1,36 @@
|
|||
{// ALPM related config
|
||||
alpm: {
|
||||
// the root directory for ALPM
|
||||
//rootDir: "/",
|
||||
{
|
||||
"alpm": {
|
||||
"rootDir": "/",
|
||||
"dbPath": "/var/lib/pacman",
|
||||
"pacmanConfigFile": "/etc/pacman.conf"
|
||||
},
|
||||
|
||||
// the database directory for ALPM
|
||||
//dbPath: "/var/lib/pacman/",
|
||||
"aur": {
|
||||
"enabled": true
|
||||
},
|
||||
|
||||
// the pacman config file
|
||||
//pacmanConfigFile: "/etc/pacman.conf"
|
||||
},
|
||||
"server": {
|
||||
"listeningPort": 1234,
|
||||
"certFile": "some.cert",
|
||||
"keyFile": "some.key",
|
||||
"insecure": false
|
||||
},
|
||||
|
||||
// server related config
|
||||
server: {
|
||||
// the listening port
|
||||
//listeningPort: 1234,
|
||||
"repos": {
|
||||
"fromPacmanConfig": true,
|
||||
|
||||
// the SSL certificate
|
||||
//certFile: "some.cert",
|
||||
|
||||
// the private SSL key
|
||||
//keyFile: "some.key",
|
||||
|
||||
// forces the server to run in insecure mode
|
||||
//insecure: false
|
||||
}
|
||||
|
||||
// repositories related config
|
||||
repos: {
|
||||
// indicates whether the repositories listed in
|
||||
// the pacman config file should be loaded
|
||||
//fromPacmanConfig: true,
|
||||
|
||||
// lists additional repos to be loaded
|
||||
add: [
|
||||
/* example repo entry
|
||||
{name: "reponame",
|
||||
dbpath: "path/to/database/file",
|
||||
srcpath: "path/to/local/source/files",
|
||||
pkgpath: "path/to/local/pkg/files",
|
||||
servers: [
|
||||
"https://some/mirror",
|
||||
"https://another/mirror
|
||||
]
|
||||
}
|
||||
*/
|
||||
]
|
||||
}
|
||||
"add": [
|
||||
{"name": "examplerepo",
|
||||
"dataBaseFile": "path/to/database/file",
|
||||
"sourcesDir": "path/to/local/source/dir",
|
||||
"packagesDir": "path/to/local/pkg/dir",
|
||||
"upgradeSources": ["aur"],
|
||||
"server": [
|
||||
"https://some/mirror",
|
||||
"https://another/mirror"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ TEMPLATE = app
|
|||
|
||||
CONFIG += console # enables qDebug()
|
||||
|
||||
QT += core network websockets
|
||||
QT += core network websockets concurrent KArchive
|
||||
|
||||
SOURCES += main.cpp \
|
||||
alpm/manager.cpp \
|
||||
|
@ -23,8 +23,10 @@ SOURCES += main.cpp \
|
|||
network/connection.cpp \
|
||||
alpm/group.cpp \
|
||||
alpm/config.cpp \
|
||||
network/query.cpp \
|
||||
network/aurquery.cpp
|
||||
network/aurquery.cpp \
|
||||
alpm/updatelookup.cpp \
|
||||
alpm/resolvebuildorder.cpp \
|
||||
alpm/mingwbundle.cpp
|
||||
|
||||
HEADERS += \
|
||||
alpm/manager.h \
|
||||
|
@ -36,8 +38,10 @@ HEADERS += \
|
|||
network/connection.h \
|
||||
alpm/group.h \
|
||||
alpm/config.h \
|
||||
network/query.h \
|
||||
network/aurquery.h
|
||||
network/aurquery.h \
|
||||
alpm/updatelookup.h \
|
||||
alpm/resolvebuildorder.h \
|
||||
alpm/mingwbundle.h
|
||||
|
||||
DISTFILES += \
|
||||
README.md \
|
||||
|
@ -58,9 +62,9 @@ DISTFILES += \
|
|||
|
||||
# libs and includepath
|
||||
CONFIG(debug, debug|release) {
|
||||
LIBS += -L../../ -lc++utilitiesd -lalpm
|
||||
LIBS += -L../../ -lc++utilitiesd -lalpm -lKF5Archive
|
||||
} else {
|
||||
LIBS += -L../../ -lc++utilities -lalpm
|
||||
LIBS += -L../../ -lc++utilities -lalpm -lKF5Archive
|
||||
}
|
||||
INCLUDEPATH += ../
|
||||
|
||||
|
|
|
@ -91,6 +91,10 @@ span.glyphicon {
|
|||
.navbar-fixed-top {
|
||||
border-bottom: 5px solid #337ab7;
|
||||
}
|
||||
.navbar-form {
|
||||
padding-left: 0px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Package info
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="#">Repository index</a>
|
||||
<!--<a class="navbar-brand collapsed" href="#">Repository index</a>-->
|
||||
</div>
|
||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||
|
@ -80,7 +80,7 @@
|
|||
<button type="button" class="close" onclick="repoindex.pageManager.hideErrors()" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<strong>Errors</strong>
|
||||
<strong>Notifications</strong>
|
||||
<ul id="error_list"></ul>
|
||||
</div>
|
||||
<!-- repositories -->
|
||||
|
@ -232,8 +232,9 @@
|
|||
<tr><th>Package count</th><td id="repo_pkgcount"></td></tr>
|
||||
<tr><th>Usage</th><td id="repo_usage"></td></tr>
|
||||
<tr><th>Signature level</th><td id="repo_siglevel"></td></tr>
|
||||
<tr><th>Upgrade sources</th><td id="repo_upgrade_sources"></td></tr>
|
||||
<tr>
|
||||
<th>Updates</th>
|
||||
<th>Upgrades</th>
|
||||
<td><span id="repo_checkforupdates"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
var repoindex = (function(repoindex) {
|
||||
|
||||
repoindex.removeFilterButtonHtml = '<button type="button" class="close" onclick="repoindex.pageManager.applySearchTerm(null, false, true);" aria-label="Close"><span aria-hidden="true">×</span></button>';
|
||||
|
||||
repoindex.Entry = function(info, enabled) {
|
||||
// basic initialization
|
||||
this.info = info;
|
||||
|
@ -82,15 +84,15 @@
|
|||
|
||||
// provide functions to apply filter and update UI
|
||||
this.applyFilter = function() {
|
||||
if(this.customSelection) {
|
||||
this.filteredEntries = this.customSelection;
|
||||
} else {
|
||||
//if(this.customSelection) {
|
||||
// this.filteredEntries = this.customSelection;
|
||||
//} else {
|
||||
if((this.filterName || this.filterRepos) && this.filterPred) {
|
||||
this.filteredEntries = this.entries.filter(this.filterPred, this);
|
||||
this.filteredEntries = this.relevantEntries().filter(this.filterPred, this);
|
||||
} else {
|
||||
this.filteredEntries = this.entries;
|
||||
this.filteredEntries = this.relevantEntries();
|
||||
}
|
||||
}
|
||||
//}
|
||||
// update pagination (the pageSelected method defined above will be invoked here automatically)
|
||||
this.pagination.entryCount = this.filteredEntries.length;
|
||||
this.pagination.update();
|
||||
|
@ -140,17 +142,16 @@
|
|||
} else {
|
||||
var entryText = this.customSelection ? (this.filteredEntries.length === 1 ? this.customSelectionName : this.customSelectionNamePlural) : (this.filteredEntries.length === 1 ? this.entryName : this.entryNamePlural);
|
||||
if(this.filterDescr) {
|
||||
var removeFilterButton = '<button type="button" class="close" onclick="repoindex.pageManager.applySearchTerm()" aria-label="Close"><span aria-hidden="true">×</span></button>';
|
||||
if(this.filterName) {
|
||||
return "<b>" + this.filteredEntries.length + "</b> " + entryText
|
||||
+ containerText + " "
|
||||
+ repoindex.escapeHtml(this.filterDescr) + " <i>"+ repoindex.escapeHtml(this.filterName) + "</i>."
|
||||
+ removeFilterButton;
|
||||
+ repoindex.removeFilterButtonHtml;
|
||||
} else {
|
||||
return "<b>" + this.filteredEntries.length + "</b> " + entryText
|
||||
+ containerText + " "
|
||||
+ repoindex.escapeHtml(this.filterDescr) + "."
|
||||
+ removeFilterButton;
|
||||
+ repoindex.removeFilterButtonHtml;
|
||||
}
|
||||
} else {
|
||||
return "Showing <b>"
|
||||
|
|
|
@ -22,13 +22,13 @@
|
|||
this.initTableRow = function() {
|
||||
var values = [this.info.arch, this.info.repo, this.info.name, this.info.ver, this.info.desc, this.info.bdate, ""];
|
||||
for(var i = 0; i < 7; ++i) {
|
||||
this.rowElement.addCell(values[i]);
|
||||
this.rowElement.addCell(repoindex.makeStr(values[i]));
|
||||
}
|
||||
};
|
||||
|
||||
this.initTableRow();
|
||||
|
||||
this.applyBasicInfo = function(info) {
|
||||
this.applyBasicInfo = function(info, noUpdate) {
|
||||
if(info.repo) this.info.repo = info.repo;
|
||||
if(info.name) this.info.name = info.name;
|
||||
if(info.arch) this.info.arch = info.arch;
|
||||
|
@ -36,10 +36,12 @@
|
|||
if(info.desc) this.info.desc = info.desc;
|
||||
if(info.bdate) this.info.bdate = info.bdate;
|
||||
this.info.basic = true;
|
||||
this.updateTableRow();
|
||||
if(!noUpdate) {
|
||||
this.updateTableRow();
|
||||
}
|
||||
};
|
||||
|
||||
this.applyFullInfo = function(info) {
|
||||
this.applyFullInfo = function(info, noUpdate) {
|
||||
this.applyBasicInfo(info);
|
||||
if(info.idate) this.info.idate = info.idate;
|
||||
if(info.isize) this.info.isize = info.isize;
|
||||
|
@ -60,7 +62,9 @@
|
|||
if(info.file) this.info.file = info.file;
|
||||
if(info.files) this.info.files = info.files;
|
||||
this.info.full = true;
|
||||
this.updateTableRow();
|
||||
if(!noUpdate) {
|
||||
this.updateTableRow();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -82,13 +86,13 @@
|
|||
this.addEntry = function(repoName, packageName) {
|
||||
var packageInfo = {
|
||||
index: this.entries.length,
|
||||
arch: "",
|
||||
arch: undefined,
|
||||
repo: repoName,
|
||||
name: packageName,
|
||||
version: "",
|
||||
desc: "",
|
||||
builddate: "",
|
||||
flagdate: "",
|
||||
version: undefined,
|
||||
desc: undefined,
|
||||
builddate: undefined,
|
||||
flagdate: undefined,
|
||||
received: false
|
||||
};
|
||||
this.entries.push(new PackageEntry(packageInfo));
|
||||
|
@ -169,7 +173,7 @@
|
|||
if(i.url) {
|
||||
repoindex.setLink("pkg_url", i.url, i.url, window.open);
|
||||
} else {
|
||||
repoindex.setText("pkg_url", "none");
|
||||
repoindex.setText("pkg_url", "unknown");
|
||||
}
|
||||
repoindex.setText("pkg_lic", repoindex.makeArray(i.lic));
|
||||
repoindex.setPackageNames("pkg_grp", repoindex.pack(i.grp), repoindex.Pages.Groups);
|
||||
|
@ -208,7 +212,15 @@
|
|||
entry.applyFullInfo(info);
|
||||
}
|
||||
} else {
|
||||
repoindex.pageManager.addError("Error: " + info.error);
|
||||
var errorMsg;
|
||||
switch(info.error) {
|
||||
case "na":
|
||||
errorMsg = "The server can't find the requested (full) package info."
|
||||
break;
|
||||
default:
|
||||
errorMsg = "The server can't deliver the requested (full) package info."
|
||||
};
|
||||
repoindex.pageManager.addError("Error: " + errorMsg);
|
||||
}
|
||||
}
|
||||
// set properties (again) to actually show applied info
|
||||
|
@ -260,8 +272,9 @@
|
|||
};
|
||||
|
||||
this.showMirrorsForIndex = function(entryIndex) {
|
||||
if(entryIndex >= 0 && entryIndex < this.entries.length) {
|
||||
var info = this.entries[entryIndex].info;
|
||||
var entry = this.entryByIndex(entryIndex);
|
||||
if(entry) {
|
||||
var info = entry.info;
|
||||
repoindex.setText("title_mirror_selection", "Mirrors for package <i>" + repoindex.escapeHtml(info.name) + "</i>", true);
|
||||
var listMirrorSelection = document.getElementById("list_mirror_selection");
|
||||
listMirrorSelection.wipeChildren();
|
||||
|
@ -286,19 +299,29 @@
|
|||
listMirrorSelection.appendChild(liElement);
|
||||
}
|
||||
mirrorsAvailable = 1;
|
||||
} else if(info.repo === "local") {
|
||||
mirrorsAvailable = -1;
|
||||
}
|
||||
}
|
||||
if(mirrorsAvailable === 1) {
|
||||
repoindex.setText("status_mirror_selection", "Select a mirror:");
|
||||
} else if(mirrorsAvailable === -1) {
|
||||
repoindex.setText("status_mirror_selection", "The package entry belongs to the <i>local</i> database. Hence there are no mirrors available.", true);
|
||||
} else {
|
||||
repoindex.setText("status_mirror_selection", "No mirrors available.");
|
||||
}
|
||||
$("#dlg_mirror_selection").modal("show");
|
||||
}
|
||||
if(mirrorsAvailable !== 1) {
|
||||
// no mirrors available?
|
||||
if(info.repo === "local" || info.repo === "LOCAL") {
|
||||
// no surprise because its an package from the local database
|
||||
mirrorsAvailable = -1;
|
||||
} else if(info.repo === "aur" || info.repo === "AUR") {
|
||||
// no surprise because its an AUR package
|
||||
mirrorsAvailable = -2;
|
||||
}
|
||||
}
|
||||
if(mirrorsAvailable === 1) {
|
||||
repoindex.setText("status_mirror_selection", "Select a mirror:");
|
||||
} else if(mirrorsAvailable === -1) {
|
||||
repoindex.setText("status_mirror_selection", "The package belongs to the <i>local</i> database. Hence there are no mirrors available.", true);
|
||||
} else if(mirrorsAvailable === -2) {
|
||||
repoindex.setText("status_mirror_selection", "The package is from the <i>Arch Linux User Repository</i>. Hence there are no mirrors available.", true);
|
||||
} else {
|
||||
repoindex.setText("status_mirror_selection", "No mirrors available.");
|
||||
}
|
||||
$("#dlg_mirror_selection").modal("show");
|
||||
};
|
||||
|
||||
this.showPackageNotFound = function(repo, name) {
|
||||
|
|
|
@ -234,7 +234,7 @@
|
|||
};
|
||||
|
||||
// provide a function to apply an entered search term
|
||||
this.applySearchTerm = function(searchTerm, exact) {
|
||||
this.applySearchTerm = function(searchTerm, exact, reset) {
|
||||
var mgr = undefined;
|
||||
switch(this.currentPage) {
|
||||
case repoindex.Pages.Packages:
|
||||
|
@ -250,7 +250,9 @@
|
|||
;
|
||||
}
|
||||
if(mgr) {
|
||||
mgr.customSelection = undefined;
|
||||
if(reset) {
|
||||
mgr.customSelection = undefined;
|
||||
}
|
||||
mgr.filterName = searchTerm;
|
||||
mgr.filterNameExact = exact;
|
||||
mgr.filterDescr = searchTerm ? (exact ? "with the name" : "for the search term") : undefined;
|
||||
|
|
|
@ -175,6 +175,7 @@
|
|||
repoindex.setText("repo_pkgcount", i.packages.length);
|
||||
repoindex.setText("repo_usage", repoindex.makeArray(i.usage, ", "));
|
||||
repoindex.setText("repo_siglevel", repoindex.makeArray(i.sigLevel, ", "));
|
||||
repoindex.setText("repo_upgrade_sources", repoindex.makeArray(i.upgradeSources, ", "));
|
||||
var invokeUpdateLookupParams = {repo: i.name, invoke: "updatelookup"};
|
||||
repoindex.setDownloadButton("repo_checkforupdates", "check for updates", repoindex.makeHash(repoindex.Pages.Repositories, invokeUpdateLookupParams, true), function() {
|
||||
repoindex.pageManager.repoManager.showAvailableUpdates(i.name);
|
||||
|
@ -241,7 +242,15 @@
|
|||
for(var i2 = 0; i2 < updates[i1].entries.length; ++i2) {
|
||||
var newEntry = pkgMgr.createCustomEntry(updates[i1].color);
|
||||
newEntry.info.index = updateEntries.length;
|
||||
newEntry.applyBasicInfo(updates[i1].entries[i2]);
|
||||
if(updates[i1].entries[i2].pkg) {
|
||||
newEntry.applyBasicInfo(updates[i1].entries[i2].pkg, true);
|
||||
if(updates[i1].entries[i2].prevVersion) {
|
||||
newEntry.info.ver = updates[i1].entries[i2].prevVersion + " → " + (newEntry.info.ver ? newEntry.info.ver : "?");
|
||||
}
|
||||
newEntry.updateTableRow();
|
||||
} else {
|
||||
newEntry.applyBasicInfo(updates[i1].entries[i2]);
|
||||
}
|
||||
updateEntries.push(newEntry);
|
||||
}
|
||||
}
|
||||
|
@ -251,14 +260,17 @@
|
|||
}
|
||||
}
|
||||
// show updates via package manager
|
||||
pkgMgr.filterName = undefined;
|
||||
pkgMgr.customSelection = updateEntries;
|
||||
pkgMgr.customSelectionName = "upgrade";
|
||||
pkgMgr.customSelectionNamePlural = "upgrades";
|
||||
var quandityInfo = repoindex.makeQuandityInfo(updates);
|
||||
if(quandityInfo) {
|
||||
pkgMgr.customSelectionInfoText = "Updates for the repository <i>" + repoindex.escapeHtml(repo) + "</i>: " + quandityInfo;
|
||||
pkgMgr.customSelectionInfoText = "Updates for the repository <i>"
|
||||
+ repoindex.escapeHtml(repo)
|
||||
+ "</i>: " + quandityInfo
|
||||
+ repoindex.removeFilterButtonHtml;
|
||||
}
|
||||
pkgMgr.filterName = repo;
|
||||
pkgMgr.filterDescr = "for the repository"
|
||||
pkgMgr.invalidate();
|
||||
repoindex.pageManager.setPage(repoindex.Pages.Packages);
|
||||
|
|
|
@ -212,8 +212,8 @@
|
|||
}
|
||||
};
|
||||
|
||||
repoindex.makeStr = function(str) {
|
||||
return str ? str : "";
|
||||
repoindex.makeStr = function(str, na) {
|
||||
return str ? str : (na ? na : "unknown");
|
||||
};
|
||||
|
||||
repoindex.makeArray = function(array, separator) {
|
||||
|
@ -234,7 +234,7 @@
|
|||
return (sizeInByte / 1099511627776.0).toFixed(2) + " TiB";
|
||||
}
|
||||
} else {
|
||||
return "";
|
||||
return "unknown";
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue