restructured some classes, overall improvements

This commit is contained in:
Martchus 2015-09-05 17:25:05 +02:00
parent c6d3ea2d8e
commit fde2f592ed
32 changed files with 650 additions and 275 deletions

View File

@ -6,12 +6,15 @@
#include <c++utilities/misc/memory.h>
#include "alpm.h"
#include <QList>
#include <QJsonObject>
#include <QtConcurrent>
using namespace std;
namespace PackageManagement {
namespace RepoIndex {
using namespace Utilities;
@ -21,15 +24,48 @@ using namespace Utilities;
* All packages returned by the AlpmDataBase class are AlpmPackage instances.
*/
class LoadPackage
{
public:
LoadPackage(AlpmDatabase *db, QMutex *mutex) :
m_db(db),
m_mutex(mutex)
{}
void operator()(alpm_pkg_t *pkg)
{
auto res = make_unique<AlpmPackage>(pkg, m_db);
QMutexLocker locker(m_mutex);
for(const auto &grpName : res->groups()) {
m_db->groups()[grpName] << res.get();
}
m_db->packages().emplace(res->name(), move(res));
}
private:
AlpmDatabase *m_db;
QMutex *m_mutex;
};
PackageLoader::PackageLoader(AlpmDatabase *db)
{
for(auto *pkg : PackageList(alpm_db_get_pkgcache(db->ptr()))) {
m_packages << pkg;
}
m_future = QtConcurrent::map(m_packages, LoadPackage(db, &m_mutex));
}
/*!
* \brief Creates a new instance wrapping the specified database struct.
*/
PackageManagement::AlpmDataBase::AlpmDataBase(alpm_db_t *dataBase, const QString &dbPath, QObject *parent) :
RepoIndex::AlpmDatabase::AlpmDatabase(alpm_db_t *dataBase, const QString &dbPath, QObject *parent) :
Repository(QString::fromLocal8Bit(alpm_db_get_name(dataBase)), parent),
m_ptr(dataBase),
m_dbFile(QStringLiteral("%1/sync/%2").arg(dbPath, m_name))
{}
PackageLoader *AlpmDatabase::init()
{
if(alpm_db_get_usage(dataBase, &m_usage)) {
if(alpm_db_get_usage(m_ptr, &m_usage)) {
m_usage = static_cast<decltype(m_usage)>(ALPM_DB_USAGE_ALL);
}
if(m_name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
@ -41,39 +77,48 @@ PackageManagement::AlpmDataBase::AlpmDataBase(alpm_db_t *dataBase, const QString
m_description = QStringLiteral("Database »%1«").arg(m_name);
}
}
for(auto *nativePkg : PackageList(alpm_db_get_pkgcache(m_ptr))) {
auto pkg = make_unique<AlpmPackage>(nativePkg, this);
QString pkgName = pkg->name();
for(const auto &grpName : pkg->groups()) {
m_groups[grpName] << pkg.get();
}
m_packages.emplace(pkgName, move(pkg));
}
// for(auto *nativePkg : PackageList(alpm_db_get_pkgcache(m_ptr))) {
// auto pkg = make_unique<AlpmPackage>(nativePkg, this);
// QString pkgName = pkg->name();
// for(const auto &grpName : pkg->groups()) {
// m_groups[grpName] << pkg.get();
// }
// m_packages.emplace(pkgName, move(pkg));
// }
for(const char *str : servers()) {
m_serverUrls << qstr(str);
}
m_sigLevel = alpm_db_get_siglevel(m_ptr);
return new PackageLoader(this);
}
//AlpmDataBase::AlpmDataBase(const QString &dataBaseFile, QObject *parent) :
//AlpmDatabase::AlpmDatabase(const QString &dataBaseFile, QObject *parent) :
// PackageSource(parent),
// m_ptr(nullptr)
//{}
RepositoryType AlpmDataBase::type() const
RepositoryType AlpmDatabase::type() const
{
return RepositoryType::AlpmDataBase;
return RepositoryType::AlpmDatabase;
}
bool AlpmDataBase::isSourceOnly() const
PackageDetailAvailability AlpmDatabase::requestsRequired(PackageDetail packageDetail) const
{
return false;
switch(packageDetail) {
case PackageDetail::Basics:
case PackageDetail::Dependencies:
case PackageDetail::PackageInfo:
return PackageDetailAvailability::Immediately;
default:
return PackageDetailAvailability::Never;
}
}
/*!
* \brief Adds the specified server URLs.
*/
bool AlpmDataBase::addServerUrls(const QStringList &urls)
bool AlpmDatabase::addServerUrls(const QStringList &urls)
{
bool res = true;
for(const auto &url : urls) {

View File

@ -4,24 +4,48 @@
#include "repository.h"
#include "list.h"
#include <alpm.h>
#include <QJsonArray>
#include <QMutex>
#include <QFuture>
namespace PackageManagement {
#include <memory>
class AlpmDataBase : public Repository
namespace RepoIndex {
class AlpmPackage;
class AlpmDatabase;
class PackageLoader
{
public:
explicit AlpmDataBase(alpm_db_t *dataBase, const QString &dbPath, QObject *parent = nullptr);
// explicit AlpmDataBase(const QString &dataBaseFile, QObject *parent = nullptr);
PackageLoader(AlpmDatabase *db);
QFuture<void> &future();
private:
QMutex m_mutex;
QList<alpm_pkg_t *> m_packages;
QFuture<void> m_future;
};
inline QFuture<void> &PackageLoader::future()
{
return m_future;
}
class AlpmDatabase : public Repository
{
friend class EmplacePackage;
public:
explicit AlpmDatabase(alpm_db_t *dataBase, const QString &dbPath, QObject *parent = nullptr);
PackageLoader *init();
// explicit AlpmDatabase(const QString &dataBaseFile, QObject *parent = nullptr);
RepositoryType type() const;
bool isSourceOnly() const;
PackageDetailAvailability requestsRequired(PackageDetail packageDetail) const;
// operators
bool operator ==(const AlpmDataBase &other) const;
bool operator !=(const AlpmDataBase &other) const;
bool operator ==(const AlpmDatabase &other) const;
bool operator !=(const AlpmDatabase &other) const;
// database meta data
alpm_db_t *ptr();
@ -31,6 +55,9 @@ public:
bool addServerUrls(const QStringList &urls);
PackageList search(StringList terms) const;
signals:
void initiated();
private:
alpm_db_t *m_ptr;
QString m_dbFile;
@ -39,7 +66,7 @@ private:
/*!
* \brief Checks whether the specified ALPM database is equal the current instance.
*/
inline bool AlpmDataBase::operator ==(const AlpmDataBase &other) const
inline bool AlpmDatabase::operator ==(const AlpmDatabase &other) const
{
return m_ptr == other.m_ptr;
}
@ -47,7 +74,7 @@ inline bool AlpmDataBase::operator ==(const AlpmDataBase &other) const
/*!
* \brief Checks whether the specified ALPM database is not equal to the current instance.
*/
inline bool AlpmDataBase::operator !=(const AlpmDataBase &other) const
inline bool AlpmDatabase::operator !=(const AlpmDatabase &other) const
{
return m_ptr != other.m_ptr;
}
@ -55,7 +82,7 @@ inline bool AlpmDataBase::operator !=(const AlpmDataBase &other) const
/*!
* \brief Returns the pointer to the underlying database struct.
*/
inline alpm_db_t *AlpmDataBase::ptr()
inline alpm_db_t *AlpmDatabase::ptr()
{
return m_ptr;
}
@ -63,7 +90,7 @@ inline alpm_db_t *AlpmDataBase::ptr()
/*!
* \brief Returns the path of the data base file.
*/
inline const QString &AlpmDataBase::dataBaseFile() const
inline const QString &AlpmDatabase::dataBaseFile() const
{
return m_dbFile;
}
@ -71,7 +98,7 @@ inline const QString &AlpmDataBase::dataBaseFile() const
/*!
* \brief Returns the servers of the database.
*/
inline StringList AlpmDataBase::servers() const
inline StringList AlpmDatabase::servers() const
{
return alpm_db_get_servers(m_ptr);
}
@ -79,7 +106,7 @@ inline StringList AlpmDataBase::servers() const
/*!
* \brief Sets the servers of the database.
*/
inline bool AlpmDataBase::setServers(StringList servers)
inline bool AlpmDatabase::setServers(StringList servers)
{
return alpm_db_set_servers(m_ptr, servers.begin().ptr()) == 0;
}
@ -87,7 +114,7 @@ inline bool AlpmDataBase::setServers(StringList servers)
/*!
* \brief Performs a search using the build in ALPM function.
*/
inline PackageList AlpmDataBase::search(StringList terms) const
inline PackageList AlpmDatabase::search(StringList terms) const
{
return alpm_db_search(m_ptr, terms.begin().ptr());
}

View File

@ -6,7 +6,7 @@
using namespace ChronoUtilities;
namespace PackageManagement {
namespace RepoIndex {
/*!
* \cond
@ -38,7 +38,7 @@ using namespace Utilities;
/*!
* \brief Constructs a new package instance for the specified ALPM \a package.
*/
AlpmPackage::AlpmPackage(alpm_pkg_t *package, AlpmDataBase *source) :
AlpmPackage::AlpmPackage(alpm_pkg_t *package, AlpmDatabase *source) :
Package(QString::fromLocal8Bit(alpm_pkg_get_name(package)), source),
m_ptr(package)
{

View File

@ -3,14 +3,14 @@
#include "package.h"
namespace PackageManagement {
namespace RepoIndex {
class AlpmDataBase;
class AlpmDatabase;
class AlpmPackage : public Package
{
public:
AlpmPackage(alpm_pkg_t *package, AlpmDataBase *source = nullptr);
AlpmPackage(alpm_pkg_t *package, AlpmDatabase *source = nullptr);
// ALPM specific meta data
const alpm_pkg_t *ptr() const;

View File

@ -6,7 +6,7 @@
using namespace ChronoUtilities;
namespace PackageManagement {
namespace RepoIndex {
/*!
* \brief The AurPackage class holds information about AUR packages. It allows to convert the information

View File

@ -3,7 +3,7 @@
#include "package.h"
namespace PackageManagement {
namespace RepoIndex {
class UserRepository;

View File

@ -14,7 +14,10 @@ using namespace std;
using namespace ApplicationUtilities;
using namespace ConversionUtilities;
namespace PackageManagement {
namespace RepoIndex {
bool useShSyntax = false;
const char *shchar = "";
/*!
* \class Alpm::Config
@ -28,23 +31,26 @@ 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"),
upgradeLookupArg("upgrade-lookup", "u", "checks available upgrades for the specified repository"),
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)"),
pacmanConfArg("pacman-conf", "p", "specifies the path of the pacman config file (default is /etc/pacman.conf"),
websocketAddrArg("addr", "a", "specifies the listening address for the websocket server, default is 127.0.0.1"),
websocketPortArg("port", "p", "specifies the listening port for the websocket server, default is 1234"),
websocketAddrArg("addr", string(), "specifies the listening address for the websocket server, default is 127.0.0.1"),
websocketPortArg("port", string(), "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"),
aurArg("aur", "u", "enables/disables AUR queries"),
aurArg("aur", string(), "enables/disables AUR queries"),
verboseArg("verbose", "v", "be verbose"),
outputFileArg("output-file", "f", "specifies the output file"),
targetDirArg("target-dir", "t", "the directory to store the target archive"),
targetNameArg("target-name", "n", "specifies the name of the target archive"),
targetFormatArg("target-format", "e", "specifies the format of the target archive"),
iconThemesArg("icon-packages", "i", "specifies the names of the icon packages to include")
iconThemesArg("icon-packages", "i", "specifies the names of the icon packages to include"),
shSyntaxArg("sh-syntax", string(), "prints the output using shell syntax: export REPOINDEX_RESULTS=('res1' 'res2' 'res3') or export REPOINDEX_ERROR='some error message'"),
repoArg("repo", string(), "specifies the repository")
{
const initializer_list<string> pathValueName = {"path"};
const initializer_list<string> pkgValueNames = {"package 1", "package 2", "package 3"};
@ -52,6 +58,7 @@ ConfigArgs::ConfigArgs(ArgumentParser &parser) :
buildOrderArg.setRequiredValueCount(-1);
buildOrderArg.setValueNames(pkgValueNames);
serverArg.setDenotesOperation(true);
upgradeLookupArg.setDenotesOperation(true);
mingwBundleArg.setDenotesOperation(true);
mingwBundleArg.setRequiredValueCount(-1);
mingwBundleArg.setValueNames(pkgValueNames);
@ -101,8 +108,12 @@ ConfigArgs::ConfigArgs(ArgumentParser &parser) :
iconThemesArg.setCombinable(true);
iconThemesArg.setRequiredValueCount(-1);
iconThemesArg.setValueNames(pkgValueNames);
serverArg.setSecondaryArguments({&rootdirArg, &dbpathArg, &pacmanConfArg, &certFileArg, &keyFileArg, &insecureArg, &aurArg});
buildOrderArg.setSecondaryArguments({&aurArg, &verboseArg});
shSyntaxArg.setCombinable(true);
repoArg.setRequiredValueCount(1);
repoArg.setValueNames({"repo name"});
serverArg.setSecondaryArguments({&rootdirArg, &dbpathArg, &pacmanConfArg, &certFileArg, &keyFileArg, &websocketAddrArg, &websocketPortArg, &insecureArg, &aurArg});
upgradeLookupArg.setSecondaryArguments({&repoArg, &shSyntaxArg});
buildOrderArg.setSecondaryArguments({&aurArg, &verboseArg, &shSyntaxArg});
mingwBundleArg.setSecondaryArguments({&targetDirArg, &targetNameArg, &targetFormatArg, &iconThemesArg});
parser.setMainArguments({&buildOrderArg, &serverArg, &mingwBundleArg, &repoindexConfArg, &repoindexConfArg, &helpArg});
}
@ -139,7 +150,7 @@ inline void assign(quint16 &num, const QJsonValue &val)
if(n > 0 && n <= static_cast<quint16>(-1)) {
num = static_cast<quint16>(n);
} else {
cerr << "Error: The specified value \"" << n << "\" is not a number." << endl;
cerr << shchar << "Error: The specified value \"" << n << "\" is not a number." << endl;
}
}
}
@ -147,7 +158,7 @@ inline void assign(quint16 &num, const QJsonValue &val)
inline void assign(QHostAddress &addr, const QString &val)
{
if(!val.isEmpty() && !addr.setAddress(val)) {
cerr << "Error: Unable to parse the specified host address \"" << val.toStdString() << "\"." << endl;
cerr << shchar << "Error: Unable to parse the specified host address \"" << val.toStdString() << "\"." << endl;
}
}
@ -164,7 +175,7 @@ inline void assign(quint16 &num, const Argument &arg)
try {
num = stringToNumber<quint16>(arg.values().front());
} catch(const ConversionException &) {
cerr << "Error: The specified argument value \"" << arg.values().front() << "\" is not a number." << endl;
cerr << shchar << "Error: The specified argument value \"" << arg.values().front() << "\" is not a number." << endl;
}
}
}
@ -208,11 +219,11 @@ void Config::loadFromConfigFile(const QString &configFilePath)
m_repoEntries.back().load(repo);
}
} else {
cerr << "Error: Unable to parse config file \"" << configFilePath.toLocal8Bit().data() << "\": "
cerr << shchar << "Error: Unable to parse config file \"" << configFilePath.toLocal8Bit().data() << "\": "
<< error.errorString().toLocal8Bit().data() << " at character " << error.offset << endl;
}
} else {
cerr << "Error: Unable to open config file \"" << configFilePath.toLocal8Bit().data() << "\"." << endl;
cerr << shchar << "Error: Unable to open config file \"" << configFilePath.toLocal8Bit().data() << "\"." << endl;
}
}
@ -252,7 +263,7 @@ void Config::loadFromArgs(const ConfigArgs &args)
} 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;
cerr << shchar << "Warning: The specified value for the argument --aur-enabled is invalid and will be ignored." << endl;
}
}
assign(m_websocketServerListeningPort, args.websocketPortArg);

View File

@ -9,7 +9,11 @@
QT_FORWARD_DECLARE_CLASS(QJsonValue)
namespace PackageManagement {
namespace RepoIndex {
// these are needed from the beginning and are initialized in the main()
extern bool useShSyntax;
extern const char *shchar;
class ConfigArgs
{
@ -18,6 +22,7 @@ public:
ApplicationUtilities::HelpArgument helpArg;
ApplicationUtilities::Argument buildOrderArg;
ApplicationUtilities::Argument serverArg;
ApplicationUtilities::Argument upgradeLookupArg;
ApplicationUtilities::Argument mingwBundleArg;
ApplicationUtilities::Argument repoindexConfArg;
ApplicationUtilities::Argument rootdirArg;
@ -35,6 +40,8 @@ public:
ApplicationUtilities::Argument targetNameArg;
ApplicationUtilities::Argument targetFormatArg;
ApplicationUtilities::Argument iconThemesArg;
ApplicationUtilities::Argument shSyntaxArg;
ApplicationUtilities::Argument repoArg;
};
class Config;
@ -120,6 +127,7 @@ public:
bool isAurEnabled() const;
bool isVerbose() const;
bool runServer() const;
const char *shSyntax() const;
void loadFromConfigFile(const QString &args);
void loadFromConfigFile(const ConfigArgs &args);

View File

@ -3,7 +3,7 @@
#include <QJsonArray>
namespace PackageManagement {
namespace RepoIndex {
/*!
* \class AlpmGroup

View File

@ -10,7 +10,7 @@
QT_FORWARD_DECLARE_CLASS(QJsonArray)
namespace PackageManagement {
namespace RepoIndex {
class AlpmGroup
{

View File

@ -3,13 +3,15 @@
#include <alpm.h>
namespace PackageManagement {
#include <iterator>
namespace RepoIndex {
/*!
* \brief The ListIterator class is used to iterate Alpm::List instances.
*/
template <class T = const char *>
class AlpmListIterator
class AlpmListIterator : public std::iterator<std::output_iterator_tag, T>
{
public:
/*!

View File

@ -12,8 +12,8 @@
#include <QList>
#include <QSysInfo>
#include <QtConcurrent/QtConcurrent>
#include <QMutexLocker>
#include <fstream>
#include <iostream>
#include <unordered_map>
@ -23,7 +23,7 @@ using namespace std;
using namespace IoUtilities;
using namespace ConversionUtilities;
namespace PackageManagement {
namespace RepoIndex {
/*!
* \cond
@ -63,7 +63,7 @@ Manager::Manager(const Config &config) :
if(!(m_handle = alpm_initialize(config.alpmRootDir().toLocal8Bit().data(), config.alpmDbPath().toLocal8Bit().data(), &err))) {
throw runtime_error(string("Cannot initialize ALPM: ") + alpm_strerror(err));
}
m_localDb = make_unique<AlpmDataBase>(alpm_get_localdb(m_handle), config.alpmDbPath());
m_localDb = make_unique<AlpmDatabase>(alpm_get_localdb(m_handle), config.alpmDbPath());
if(config.isAurEnabled()) {
m_userRepo = make_unique<UserRepository>(m_networkAccessManager);
}
@ -78,11 +78,11 @@ Manager::~Manager()
}
/*!
* \brief Returns the package with the specified name from the specified database.
* \brief Returns the first package with the specified name from the specified database.
*/
AlpmPackage *Manager::packageFromDataBase(const QString &dbName, const QString &pkgName)
AlpmPackage *Manager::packageFromDatabase(const QString &dbName, const QString &pkgName)
{
if(auto *db = dataBaseByName(dbName)) {
if(auto *db = databaseByName(dbName)) {
return static_cast<AlpmPackage *>(db->packageByName(pkgName));
} else {
return nullptr;
@ -90,11 +90,11 @@ AlpmPackage *Manager::packageFromDataBase(const QString &dbName, const QString &
}
/*!
* \brief Returns the package with the specified name from the specified database.
* \brief Returns the first package with the specified name from the specified database.
*/
const AlpmPackage *Manager::packageFromDataBase(const QString &dbName, const QString &pkgName) const
const AlpmPackage *Manager::packageFromDatabase(const QString &dbName, const QString &pkgName) const
{
if(const auto *db = dataBaseByName(dbName)) {
if(const auto *db = databaseByName(dbName)) {
return static_cast<const AlpmPackage *>(db->packageByName(pkgName));
} else {
return nullptr;
@ -102,11 +102,11 @@ const AlpmPackage *Manager::packageFromDataBase(const QString &dbName, const QSt
}
/*!
* \brief Gets the package with the specified \a name from one of the sync databases.
* \brief Returns the first package with the specified \a name from one of the sync databases.
*/
AlpmPackage *Manager::packageFromSyncDataBases(const QString &pkgName)
AlpmPackage *Manager::packageFromSyncDatabases(const QString &pkgName)
{
for(const auto &dbEntry : syncDataBases()) {
for(const auto &dbEntry : syncDatabases()) {
if(auto *pkg = dbEntry.second->packageByName(pkgName)) {
return static_cast<AlpmPackage *>(pkg);
}
@ -115,11 +115,11 @@ AlpmPackage *Manager::packageFromSyncDataBases(const QString &pkgName)
}
/*!
* \brief Gets the package with the specified \a name from one of the sync databases.
* \brief Returns the first package with the specified \a name from one of the sync databases.
*/
const AlpmPackage *Manager::packageFromSyncDataBases(const QString &pkgName) const
const AlpmPackage *Manager::packageFromSyncDatabases(const QString &pkgName) const
{
for(const auto &dbEntry : syncDataBases()) {
for(const auto &dbEntry : syncDatabases()) {
if(const auto *pkg = dbEntry.second->packageByName(pkgName)) {
return static_cast<const AlpmPackage *>(pkg);
}
@ -142,6 +142,36 @@ unique_ptr<AlpmOwnershipPackage> Manager::packageFromFile(const char *fileName,
}
}
/*!
* \brief Returns the first package satisfiing the specified dependency from one of the available package sources (excluding the local database).
*/
Package *Manager::packageProviding(const Dependency &dependency)
{
for(auto &dbEntry : syncDatabases()) {
if(auto *pkg = dbEntry.second->packageProviding(dependency)) {
return pkg;
}
}
if(config().isAurEnabled()) {
// TODO: check AUR
}
return nullptr;
}
/*!
* \brief Returns the first package satisfiing the specified dependency from one of the available package sources (excluding the local database
* and sources requirering requests such as the AUR).
*/
const Package *Manager::packageProviding(const Dependency &dependency) const
{
for(const auto &dbEntry : syncDatabases()) {
if(const auto *pkg = dbEntry.second->packageProviding(dependency)) {
return pkg;
}
}
return nullptr;
}
/*!
* \brief Sets the install reason for the specified \a package.
*/
@ -222,7 +252,7 @@ int Manager::parseSigLevel(const string &sigLevelStr)
sigLevel |= ALPM_SIG_DATABASE_MARGINAL_OK | ALPM_SIG_DATABASE_UNKNOWN_OK;
}
} else {
cerr << "Warning: Invalid value \"" << part << "\" for \"SigLevel\" in pacman config file will be ignored." << endl;
cerr << shchar << "Warning: Invalid value \"" << part << "\" for \"SigLevel\" in pacman config file will be ignored." << endl;
}
}
return sigLevel;
@ -245,7 +275,7 @@ int Manager::parseUsage(const string &usageStr)
} else if(part == "Upgrade") {
usage |= ALPM_DB_USAGE_UPGRADE;
} else {
cerr << "Warning: Invalid value \"" << part << "\" for \"Usage\" in pacman config file will be ignored." << endl;
cerr << shchar << "Warning: Invalid value \"" << part << "\" for \"Usage\" in pacman config file will be ignored." << endl;
}
}
return usage ? usage : ALPM_DB_USAGE_ALL;
@ -295,11 +325,11 @@ void Manager::applyPacmanConfig()
// read and validate database name
QString dbName = QString::fromLocal8Bit(scope.first.c_str());
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;
cerr << shchar << "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"), 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;
cerr << shchar << "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;
cerr << shchar << "Error: Unable to add database from pacman config: Database names must be unique. Ignoring second occurance of database \"" << scope.first << "\"." << endl;
} else {
// read sig level and usage
const auto &sigLevelStr = lastValue(scope.second, sigLevelKey);
@ -310,11 +340,11 @@ void Manager::applyPacmanConfig()
// set usage
if(alpm_db_set_usage(db, static_cast<alpm_db_usage_t>(usage)) == 0) {
if(m_config.isVerbose() || m_config.runServer()) {
cerr << "Added database [" << scope.first << "]" << endl;
cerr << shchar << "Added database [" << scope.first << "]" << endl;
}
} else {
if(m_config.isVerbose() || m_config.runServer()) {
cerr << "Warning: Added database [" << scope.first << "] but failed to set usage" << endl;
cerr << shchar << "Warning: Added database [" << scope.first << "] but failed to set usage" << endl;
}
}
// add servers
@ -324,7 +354,7 @@ void Manager::applyPacmanConfig()
findAndReplace<string>(url, "$arch", arch);
alpm_db_add_server(db, url.c_str());
if(m_config.isVerbose() || m_config.runServer()) {
cerr << "Added server: " << url << endl;
cerr << shchar << "Added server: " << url << endl;
}
}
// add included servers
@ -338,7 +368,7 @@ void Manager::applyPacmanConfig()
includedFile.open(path, ios_base::in);
includedIni.parse(includedFile);
} catch (const ios_base::failure &) {
cerr << "Error: An IO exception occured when parsing the included file \"" << path << "\"." << endl;
cerr << shchar << "Error: An IO exception occured when parsing the included file \"" << path << "\"." << endl;
}
}
try {
@ -349,21 +379,21 @@ void Manager::applyPacmanConfig()
findAndReplace<string>(url, "$arch", arch);
alpm_db_add_server(db, url.c_str());
if(m_config.isVerbose() || m_config.runServer()) {
cerr << "Added server: " << url << endl;
cerr << shchar << "Added server: " << url << endl;
}
}
} catch (const out_of_range &) {
cerr << "Warning: Included file \"" << path << "\" has no values." << endl;
cerr << shchar << "Warning: Included file \"" << path << "\" has no values." << endl;
}
}
auto emplaced = m_syncDbs.emplace(dbName, make_unique<AlpmDataBase>(db, m_config.alpmDbPath()));
auto emplaced = m_syncDbs.emplace(dbName, make_unique<AlpmDatabase>(db, m_config.alpmDbPath()));
// add sync db to internal map
if(usage & ALPM_DB_USAGE_UPGRADE) {
// -> db is used to upgrade local database
localDataBase()->upgradeSources() << emplaced.first->second.get();
localDatabase()->upgradeSources() << emplaced.first->second.get();
}
} else {
cerr << "Error: Unable to add sync database [" << scope.first << "]" << endl;
cerr << shchar << "Error: Unable to add sync database [" << scope.first << "]" << endl;
}
}
}
@ -380,35 +410,35 @@ void Manager::applyRepoIndexConfig()
{
// check whether an entry already exists, if not create a new one
for(const RepoEntry &repoEntry : m_config.repoEntries()) {
AlpmDataBase *syncDb = nullptr;
AlpmDatabase *syncDb = nullptr;
try {
syncDb = m_syncDbs.at(repoEntry.name()).get();
cerr << "Applying config for database [" << syncDb->name() << "]" << endl;
cerr << shchar << "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 \""
cerr << shchar << "Warning: Can't use data base path specified in repo index config because the repo \""
<< repoEntry.name() << "\" has already been added from the Pacman config." << endl;
}
if(repoEntry.sigLevel()) {
cerr << "Warning: Can't use sig level specified in repo index config because the repo \""
cerr << shchar << "Warning: Can't use sig level specified in repo index config because the repo \""
<< repoEntry.name() << "\" 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;
cerr << shchar << "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;
cerr << shchar << "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 {
// TODO: database path
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(), make_unique<AlpmDataBase>(db, m_config.alpmDbPath()));
auto emplaced = m_syncDbs.emplace(repoEntry.name(), make_unique<AlpmDatabase>(db, m_config.alpmDbPath()));
if(emplaced.second) {
syncDb = emplaced.first->second.get();
syncDb->setSourcesDirectory(repoEntry.sourceDir());
syncDb->setPackagesDirectory(repoEntry.packageDir());
if(m_config.isVerbose() || m_config.runServer()) {
cerr << "Added database [" << repoEntry.name() << "]" << endl;
cerr << shchar << "Added database [" << repoEntry.name() << "]" << endl;
}
}
}
@ -425,7 +455,7 @@ void Manager::applyRepoIndexConfig()
if(auto *source = repositoryByName(upgradeSourceName)) {
upgradeSources << source;
} else {
cerr << "Warning: The specified upgrade source \"" << upgradeSourceName << "\" can not be found and will be ignored." << endl;
cerr << shchar << "Warning: The specified upgrade source \"" << upgradeSourceName << "\" can not be found and will be ignored." << endl;
}
}
} catch(const out_of_range &) {
@ -435,6 +465,24 @@ void Manager::applyRepoIndexConfig()
}
/*!
* \brief Initiates all ALPM data bases.
* \remarks Must be called, after all relevant sync data bases have been registered (eg. via applyPacmanConfig()).
*/
void Manager::initAlpmDataBases()
{
QList<PackageLoader *> loaders;
loaders.reserve(m_syncDbs.size() + 1);
loaders << localDatabase()->init();
for(auto &syncDbEntry : m_syncDbs) {
loaders << syncDbEntry.second->init();
}
for(auto *loader : loaders) {
loader->future().waitForFinished();
delete loader;
}
}
/*!
* \brief Unregisters all registred sync databases.
*/
@ -449,7 +497,7 @@ void Manager::unregisterSyncDataBases()
* \brief Returns a list of all sync databases.
* \remarks Sync databases must be registered with parsePacmanConfig() before.
*/
const map<QString, unique_ptr<AlpmDataBase> > &Manager::syncDataBases() const
const map<QString, unique_ptr<AlpmDatabase> > &Manager::syncDatabases() const
{
return m_syncDbs; // m_syncDbs has been filled when the databases were registered
}
@ -466,9 +514,9 @@ const QJsonArray &Manager::basicRepoInfo() const
QMutexLocker locker(&m_basicRepoInfoMutex);
if(m_basicRepoInfo.isEmpty()) {
// add local data base
m_basicRepoInfo << localDataBase()->basicInfo();
m_basicRepoInfo << localDatabase()->basicInfo();
// add sync data bases
for(const auto &syncDb : syncDataBases()) {
for(const auto &syncDb : syncDatabases()) {
// 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)) {
@ -533,7 +581,7 @@ const QJsonArray &Manager::groupInfo() const
if(m_groupInfo.empty()) {
QMutexLocker locker(&m_groupInfoMutex);
if(m_groupInfo.empty()) {
m_groupInfo << localDataBase()->groupInfo();
m_groupInfo << localDatabase()->groupInfo();
for(const auto &db : m_syncDbs) {
m_groupInfo << db.second->groupInfo();
}
@ -545,10 +593,10 @@ const QJsonArray &Manager::groupInfo() const
/*!
* \brief Returns the ALPM database with the specified name.
*/
const AlpmDataBase *Manager::dataBaseByName(const QString &dbName) const
const AlpmDatabase *Manager::databaseByName(const QString &dbName) const
{
if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
return localDataBase();
return localDatabase();
} else {
try {
return m_syncDbs.at(dbName).get();
@ -561,10 +609,10 @@ const AlpmDataBase *Manager::dataBaseByName(const QString &dbName) const
/*!
* \brief Returns the ALPM database with the specified name.
*/
AlpmDataBase *Manager::dataBaseByName(const QString &dbName)
AlpmDatabase *Manager::databaseByName(const QString &dbName)
{
if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
return localDataBase();
return localDatabase();
} else {
try {
return m_syncDbs.at(dbName).get();
@ -580,7 +628,7 @@ AlpmDataBase *Manager::dataBaseByName(const QString &dbName)
const Repository *Manager::repositoryByName(const QString &name) const
{
if(name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
return localDataBase();
return localDatabase();
} else if(config().isAurEnabled() && (name.compare(QLatin1String("aur"), Qt::CaseInsensitive) == 0)) {
return userRepository();
} else {
@ -598,7 +646,7 @@ const Repository *Manager::repositoryByName(const QString &name) const
Repository *Manager::repositoryByName(const QString &name)
{
if(name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
return localDataBase();
return localDatabase();
} else if(config().isAurEnabled() && (name.compare(QLatin1String("aur"), Qt::CaseInsensitive) == 0)) {
return userRepository();
} else {
@ -610,12 +658,40 @@ Repository *Manager::repositoryByName(const QString &name)
}
}
/*!
* \brief Returns a list of all repositories excluding the local database.
*/
QList<const Repository *> Manager::repositories() const
{
QList<const Repository *> repos;
repos.reserve(m_syncDbs.size() + 1);
for(const auto &dbEntry : m_syncDbs) {
repos << dbEntry.second.get();
}
repos << m_userRepo.get();
return repos;
}
/*!
* \brief Returns a list of all repositories excluding the local database.
*/
QList<Repository *> Manager::repositories()
{
QList<Repository *> repos;
repos.reserve(m_syncDbs.size() + 1);
for(auto &dbEntry : m_syncDbs) {
repos << dbEntry.second.get();
}
repos << m_userRepo.get();
return repos;
}
/*!
* \brief Checks the specified database for upgrades.
*
* Appropriate upgrade sources will be determined automatically; does not check the AUR.
*/
const UpgradeLookupResults Manager::checkForUpgrades(AlpmDataBase *db) const
const UpgradeLookupResults Manager::checkForUpgrades(AlpmDatabase *db) const
{
UpgradeLookupResults results;
db->checkForUpgrades(results);

View File

@ -14,11 +14,11 @@
#include <map>
#include <memory>
namespace PackageManagement {
namespace RepoIndex {
class Config;
class UserRepository;
class AlpmDataBase;
class AlpmDatabase;
class Manager
{
@ -40,29 +40,34 @@ public:
static int parseUsage(const std::string &usageStr);
void applyPacmanConfig();
void applyRepoIndexConfig();
void initAlpmDataBases();
void unregisterSyncDataBases();
const UserRepository *userRepository() const;
UserRepository *userRepository();
// package lookup
AlpmPackage *packageFromDataBase(const QString &dbName, const QString &pkgName);
const AlpmPackage *packageFromDataBase(const QString &dbName, const QString &pkgName) const;
AlpmPackage *packageFromSyncDataBases(const QString &pkgName);
const AlpmPackage *packageFromSyncDataBases(const QString &pkgName) const;
AlpmPackage *packageFromDatabase(const QString &dbName, const QString &pkgName);
const AlpmPackage *packageFromDatabase(const QString &dbName, const QString &pkgName) const;
AlpmPackage *packageFromSyncDatabases(const QString &pkgName);
const AlpmPackage *packageFromSyncDatabases(const QString &pkgName) const;
std::unique_ptr<AlpmOwnershipPackage> packageFromFile(const char *fileName, bool verifyIntegrity = false);
Package *packageProviding(const Dependency &dependency);
const Package *packageProviding(const Dependency &dependency) const;
bool isPackageIgnored(const AlpmPackage *package) const;
void setInstallReason(AlpmPackage *package, alpm_pkgreason_t reason);
// data base lookup
const AlpmDataBase *localDataBase() const;
AlpmDataBase *localDataBase();
const std::map<QString, std::unique_ptr<AlpmDataBase> > &syncDataBases() const;
const AlpmDataBase *dataBaseByName(const QString &dbName) const;
AlpmDataBase *dataBaseByName(const QString &dbName);
// repository lookup
const AlpmDatabase *localDatabase() const;
AlpmDatabase *localDatabase();
const std::map<QString, std::unique_ptr<AlpmDatabase> > &syncDatabases() const;
const AlpmDatabase *databaseByName(const QString &dbName) const;
AlpmDatabase *databaseByName(const QString &dbName);
const Repository *repositoryByName(const QString &name) const;
Repository *repositoryByName(const QString &name);
const UpgradeLookupResults checkForUpgrades(AlpmDataBase *db) const;
const UserRepository *userRepository() const;
UserRepository *userRepository();
QList<const Repository *> repositories() const;
QList<Repository *> repositories();
const UpgradeLookupResults checkForUpgrades(AlpmDatabase *db) const;
// JSON serialization, handling JSON requests
const QJsonObject basicRepoInfo(const Repository *packageSource) const;
@ -80,8 +85,8 @@ private:
QString m_pacmanCacheDir;
QNetworkAccessManager m_networkAccessManager;
std::unique_ptr<UserRepository> m_userRepo;
std::unique_ptr<AlpmDataBase> m_localDb;
std::map<QString, std::unique_ptr<AlpmDataBase> > m_syncDbs;
std::unique_ptr<AlpmDatabase> m_localDb;
std::map<QString, std::unique_ptr<AlpmDatabase> > m_syncDbs;
mutable QJsonArray m_basicRepoInfo;
mutable QMutex m_basicRepoInfoMutex;
mutable QJsonArray m_groupInfo;
@ -167,7 +172,7 @@ inline UserRepository *Manager::userRepository()
/*!
* \brief Returns the local data base.
*/
inline const AlpmDataBase *Manager::localDataBase() const
inline const AlpmDatabase *Manager::localDatabase() const
{
return m_localDb.get();
}
@ -175,7 +180,7 @@ inline const AlpmDataBase *Manager::localDataBase() const
/*!
* \brief Returns the local data base.
*/
inline AlpmDataBase *Manager::localDataBase()
inline AlpmDatabase *Manager::localDatabase()
{
return m_localDb.get();
}

View File

@ -1,6 +1,7 @@
#include "mingwbundle.h"
#include "utilities.h"
#include "manager.h"
#include "config.h"
#include <c++utilities/conversion/stringconversion.h>
#include <c++utilities/misc/memory.h>
@ -20,7 +21,7 @@
using namespace std;
namespace PackageManagement {
namespace RepoIndex {
using namespace Utilities;
@ -29,12 +30,12 @@ const string prefix("mingw-w64-");
MingwBundle::MingwBundle(const Manager &manager, const ApplicationUtilities::StringVector &packages, const ApplicationUtilities::StringVector &iconPackages) :
m_manager(manager)
{
cerr << "Resolving dependencies ..." << endl;
cerr << shchar << "Resolving dependencies ..." << endl;
string missing;
// add mingw-w64 packages
for(const auto &pkgName : packages) {
bool found = false;
for(const auto &syncDb : manager.syncDataBases()) {
for(const auto &syncDb : manager.syncDatabases()) {
if(auto *pkg = syncDb.second->packageByName(QString::fromLocal8Bit(ConversionUtilities::startsWith(pkgName, prefix) ? pkgName.data() : (prefix + pkgName).data()))) {
if(missing.empty()) {
decltype(m_packages)::value_type entry(syncDb.second.get(), pkg);
@ -55,7 +56,7 @@ MingwBundle::MingwBundle(const Manager &manager, const ApplicationUtilities::Str
// add additional icon packages
for(const auto &pkgName : iconPackages) {
bool found = false;
for(const auto &syncDb : manager.syncDataBases()) {
for(const auto &syncDb : manager.syncDatabases()) {
if(auto *pkg = syncDb.second->packageByName(QString::fromLocal8Bit(pkgName.data()))) {
if(missing.empty()) {
decltype(m_packages)::value_type entry(syncDb.second.get(), pkg);
@ -75,11 +76,11 @@ MingwBundle::MingwBundle(const Manager &manager, const ApplicationUtilities::Str
if(!missing.empty()) {
throw runtime_error("The following packages can not be found:" + missing);
} else {
cerr << "Adding the following packages:";
cerr << shchar << "Adding the following packages:";
for(const auto &pkg : m_packages) {
cerr << ' ' << pkg.second->name().toLocal8Bit().data();
cerr << shchar << ' ' << pkg.second->name().toLocal8Bit().data();
}
cerr << endl;
cerr << shchar << endl;
}
}
@ -89,7 +90,7 @@ void MingwBundle::addDependencies(const Package *pkg)
for(const auto &dep : pkg->dependencies()) {
if(dep.name.startsWith(QLatin1String("mingw-w64-"), Qt::CaseInsensitive)) {
bool found = false;
for(const auto &syncDbEntry : m_manager.syncDataBases()) {
for(const auto &syncDbEntry : m_manager.syncDatabases()) {
if(const auto *pkg = syncDbEntry.second->packageProviding(dep)) {
if(missing.empty()) {
decltype(m_packages)::value_type entry(syncDbEntry.second.get(), pkg);
@ -284,7 +285,7 @@ void getFiles(PkgFileInfo &pkgFileInfo)
void MingwBundle::createBundle(const string &targetDir, const string &targetName, const string &targetFormat) const
{
cerr << "Gathering relevant files ..." << endl;
cerr << shchar << "Gathering relevant files ..." << endl;
// get package files
list<PkgFileInfo> pkgFiles;
for(const auto &entry : m_packages) {
@ -333,7 +334,7 @@ void MingwBundle::createBundle(const string &targetDir, const string &targetName
};
for(const auto &root : roots) {
QString targetPath = qstr(targetDir) % QChar('/') % root.second % QChar('-') % qstr(targetName) % QChar('.') % qstr(targetFormat);
cerr << "Making archive \"" << targetPath.toLocal8Bit().data() << "\" ..." << endl;
cerr << shchar << "Making archive \"" << targetPath.toLocal8Bit().data() << "\" ..." << endl;
unique_ptr<KArchive> targetArchive;
if(targetFormat == "7z") {
targetArchive = make_unique<K7Zip>(targetPath);

View File

@ -8,7 +8,7 @@
#include <QList>
namespace PackageManagement {
namespace RepoIndex {
class Manager;
@ -23,7 +23,7 @@ private:
void addDependencies(const Package *pkg);
const Manager &m_manager;
std::list<std::pair<const AlpmDataBase *, const Package *> > m_packages;
std::list<std::pair<const AlpmDatabase *, const Package *> > m_packages;
};
} // namespace PackageManagement

View File

@ -11,7 +11,7 @@
using namespace std;
using namespace ChronoUtilities;
namespace PackageManagement {
namespace RepoIndex {
/*!
* \class The Package class holds meta information about an
@ -46,13 +46,16 @@ Package::~Package()
bool Package::matches(const QString &name, const QString &version, const Dependency &dependency)
{
if(name == QStringLiteral("gst-plugins-base-libs") && dependency.name == QStringLiteral("gst-plugins-base-libs")) {
dependency.name.begin();
}
if(name == dependency.name) {
PackageVersionComparsion cmp;
switch(dependency.mode) {
case ALPM_DEP_MOD_ANY:
return true;
case ALPM_DEP_MOD_EQ:
return version == dependency.version;
return PackageVersion(version).compare(PackageVersion(dependency.version)) == PackageVersionComparsion::Equal;
case ALPM_DEP_MOD_GE:
return (cmp = PackageVersion(version).compare(PackageVersion(dependency.version))) == PackageVersionComparsion::Equal || cmp == PackageVersionComparsion::NewerThenSyncVersion;
case ALPM_DEP_MOD_LE:
@ -234,15 +237,18 @@ PackageVersion::PackageVersion(const QString &versionStr)
release = QString::fromUtf16(releaseBeg);
} else {
version = QString::fromUtf16(versionBeg);
release = QStringLiteral("1");
}
} else {
// epoch not present
epoch = QStringLiteral("0");
if(releaseBeg) {
// release present
version = QString::fromUtf16(str, releaseBeg - str - 1);
release = QString::fromUtf16(releaseBeg);
} else {
version = QString::fromUtf16(str);
release = QStringLiteral("1");
}
}
}

View File

@ -7,17 +7,16 @@
#include <alpm.h>
#include <list>
#include <string>
#include <QString>
#include <QHash>
#include <QJsonArray>
#include <map>
QT_FORWARD_DECLARE_CLASS(QJsonObject)
QT_FORWARD_DECLARE_CLASS(QJsonValue)
namespace PackageManagement {
namespace RepoIndex {
class Repository;
@ -77,7 +76,7 @@ public:
class Dependency
{
public:
explicit Dependency(const QString &name, const QString &version, _alpm_depmod_t mode = ALPM_DEP_MOD_ANY);
explicit Dependency(const QString &name, const QString &version = QString(), _alpm_depmod_t mode = ALPM_DEP_MOD_ANY);
QString name;
QString version;
_alpm_depmod_t mode;
@ -145,6 +144,7 @@ public:
ChronoUtilities::DateTime firstSubmitted() const;
ChronoUtilities::DateTime lastModified() const;
const QString &tarUrl() const;
const std::map<QString, QByteArray> &sourceFiles() const;
// version comparsion
PackageVersionComparsion compareVersion(const Package *syncPackage) const;
@ -211,6 +211,7 @@ protected:
ChronoUtilities::DateTime m_firstSubmitted;
ChronoUtilities::DateTime m_lastModified;
QString m_tarUrl;
std::map<QString, QByteArray> m_sourceFiles;
};
/*!
@ -508,7 +509,7 @@ inline const QString &Package::baseName() const
/*!
* \brief Returns the architecutes (from the PKGBUILD file).
* \remarks For the architecture of the particular binary package
* \remarks For the architecture of the particular package file
* see buildArchitecture().
*/
inline const QStringList &Package::architectures() const
@ -580,16 +581,33 @@ inline const QString &Package::tarUrl() const
return m_tarUrl;
}
/*!
* \brief Returns the source files of the package.
*/
inline const std::map<QString, QByteArray> &Package::sourceFiles() const
{
return m_sourceFiles;
}
/*!
* \brief Compares the version of the package with the specified sync package.
*/
inline PackageVersionComparsion Package::compareVersion(const Package *syncPackage) const
{
return PackageVersion(version()).compare(PackageVersion(syncPackage->version()));
}
/*!
* \brief Compares the version of the package with the version of the specified \a dependency.
*/
inline PackageVersionComparsion Package::compareVersion(const Dependency &dependency) const
{
return PackageVersion(version()).compare(PackageVersion(dependency.version));
}
/*!
* \brief Checks whether the package matches the specified \a dependency.
*/
inline bool Package::matches(const Dependency &dependency)
{
return matches(name(), version(), dependency);

View File

@ -7,27 +7,40 @@
using namespace std;
namespace PackageManagement {
namespace RepoIndex {
/*!
* \brief Constructs a new reply.
* \brief Constructs a new reply for a single network reply.
*/
Reply::Reply(QNetworkReply *networkReply) :
m_networkReply(networkReply)
Reply::Reply(QNetworkReply *networkReply)
{
networkReply->setParent(this);
connect(networkReply, &QNetworkReply::finished, this, &Reply::processData);
m_networkReplies.reserve(1);
m_networkReplies << networkReply;
}
/*!
* \fn PackageSource::type()
* \brief Constructs a new reply for multiple network replies.
*/
Reply::Reply(const QList<QNetworkReply *> networkReplies) :
m_networkReplies(networkReplies)
{
for(auto *networkReply : networkReplies) {
networkReply->setParent(this);
connect(networkReply, &QNetworkReply::finished, this, &Reply::processData);
}
}
/*!
* \fn Repository::type()
* \brief Returns the type of the package source.
*/
/*!
* \brief Returns a list of all package names.
*/
const QStringList PackageManagement::Repository::packageNames() const
const QStringList RepoIndex::Repository::packageNames() const
{
QStringList names;
names.reserve(m_packages.size());
@ -63,32 +76,26 @@ Repository::Repository(const QString &name, QObject *parent) :
{}
/*!
* \brief Destroys the package source.
* \brief Destroys the repository.
*/
Repository::~Repository()
{}
/*!
* \brief Returns whether the repository only has sources but no binary packages.
*/
bool Repository::isSourceOnly() const
{
return true;
}
/*!
* \brief Returns whether requests are required.
* \brief Returns whether explicit requests are required to get the specified information
* about the package of this repository.
*
* AlpmDataBase instances load all available packages in the cache
* at the beginning and hence do not require explicit requests.
* at the beginning and hence do not require explicit requests for package names, version,
* description, dependencies and most other information. However make dependencies are not available at all.
*
* UserRepository instances on the other hand have an empty package
* cache at the beginning so packages must be requested explicitely
* using the requestPackageInfo() method.
*/
bool Repository::requestsRequired() const
PackageDetailAvailability Repository::requestsRequired(PackageDetail ) const
{
return false;
return PackageDetailAvailability::Never;
}
/*!
@ -116,7 +123,7 @@ PackageReply *Repository::requestPackageInfo(const QStringList &, bool ) const
* requested again. If it turns out, that all packages are already cached, nullptr
* is returned in this case.
*/
PackageReply *Repository::requestFullPackageInfo(const QString &, bool ) const
PackageReply *Repository::requestFullPackageInfo(const QStringList &, bool ) const
{
return nullptr;
}
@ -143,6 +150,28 @@ const Package *Repository::packageProviding(const Dependency &dependency) const
return nullptr;
}
/*!
* \brief Returns the first package providing the specified \a dependency.
* \remarks Returns nullptr if no packages provides the \a dependency.
*/
Package *Repository::packageProviding(const Dependency &dependency)
{
for(auto &entry : m_packages) {
if(entry.second->matches(dependency)) {
// check whether package matches "directly"
return entry.second.get();
} else {
// check whether at least one of the provides matches
for(auto &provide : entry.second->provides()) {
if(Package::matches(provide.name, provide.version, dependency)) {
return entry.second.get();
}
}
}
}
return nullptr;
}
/*!
* \brief Returns all packages providing the specified \a dependency.
*/
@ -267,7 +296,7 @@ QJsonObject Repository::basicInfo() const
put(info, QStringLiteral("sigLevel"), Utilities::sigLevelStrings(sigLevel()));
put(info, QStringLiteral("upgradeSources"), upgradeSourcesJsonArray());
put(info, QStringLiteral("packages"), packageNamesJsonArray());
put(info, QStringLiteral("requestRequired"), requestsRequired());
put(info, QStringLiteral("requestRequired"), requestsRequired(PackageDetail::Basics) != PackageDetailAvailability::Immediately);
put(info, QStringLiteral("srcOnly"), isSourceOnly());
return info;
}

View File

@ -11,7 +11,7 @@
QT_FORWARD_DECLARE_CLASS(QNetworkReply)
namespace PackageManagement {
namespace RepoIndex {
class UpgradeLookupResults;
@ -20,6 +20,7 @@ class Reply : public QObject
Q_OBJECT
public:
Reply(QNetworkReply *networkReply);
Reply(const QList<QNetworkReply *> networkReplies);
const QString &error() const;
signals:
@ -29,7 +30,7 @@ private slots:
virtual void processData() = 0;
protected:
QNetworkReply *m_networkReply;
QList<QNetworkReply *> m_networkReplies;
QString m_error;
};
@ -43,6 +44,7 @@ class PackageReply : public Reply
Q_OBJECT
public:
PackageReply(QNetworkReply *networkReply, std::map<QString, std::unique_ptr<Package> > &packages);
PackageReply(const QList<QNetworkReply *> &networkReplies, std::map<QString, std::unique_ptr<Package> > &packages);
const std::map<QString, std::unique_ptr<Package> > &packages() const;
protected:
@ -54,6 +56,11 @@ inline PackageReply::PackageReply(QNetworkReply *networkReply, std::map<QString,
m_packages(packages)
{}
inline PackageReply::PackageReply(const QList<QNetworkReply *> &networkReplies, std::map<QString, std::unique_ptr<Package> > &packages) :
Reply(networkReplies),
m_packages(packages)
{}
inline const std::map<QString, std::unique_ptr<Package> > &PackageReply::packages() const
{
return m_packages;
@ -79,13 +86,39 @@ inline const QJsonArray &SuggestionsReply::suggestions() const
return m_suggestions;
}
/*!
* \brief The RepositoryType enum specifies the type of a repository object.
*/
enum class RepositoryType
{
AlpmDataBase, /*! The repository is an AlpmDataBase instance. */
AlpmDatabase, /*! The repository is an AlpmDataBase instance. */
UserRepository, /*! The repository is a UserRepository instance. */
Other /*! The repository type is unknown. */
};
/*!
* \brief The PackageDetail enum is used to refer to some kind of information about a package.
*/
enum class PackageDetail
{
Basics, /*! Basic information about the package such as knowing it exists, its name, version and description. */
Dependencies, /*! The runtime dependencies of the package. */
SourceInfo, /*! The source info such as make dependencies of the package. */
PackageInfo /*! Information related to a specific package (pkg-file). */
};
/*!
* \brief The PackageDetailAvailability enum describes when some kind of information about a package
* becomes available.
*/
enum class PackageDetailAvailability
{
Never, /*! The information is not available and can't be requested. */
Immediately, /*! The information is available immediately after the repository object has been constructed. */
Request, /*! The information is available after the requestPackageInfo() method has been called for the package. */
FullRequest /*! The information is available after the requestFullPackageInfo() method has been called for the package. */
};
class Repository : public QObject
{
Q_OBJECT
@ -101,21 +134,23 @@ public:
std::map<QString, std::unique_ptr<Package> > &packages();
const QStringList packageNames() const;
alpm_db_usage_t usage() const;
virtual bool isSourceOnly() const;
bool isSourceOnly() const;
std::map<QString, QList<Package *> > &groups();
const std::map<QString, QList<Package *> > &groups() const;
const QStringList &serverUrls() const;
alpm_siglevel_t sigLevel() const;
// gathering data
virtual bool requestsRequired() const;
virtual PackageDetailAvailability requestsRequired(PackageDetail packageDetail = PackageDetail::Basics) const;
virtual SuggestionsReply *requestSuggestions(const QString &phrase) const;
virtual PackageReply *requestPackageInfo(const QStringList &packageNames, bool forceUpdate = false) const;
virtual PackageReply *requestFullPackageInfo(const QString &package, bool forceUpdate = false) const;
virtual PackageReply *requestFullPackageInfo(const QStringList &package, bool forceUpdate = false) const;
// package search
const Package *packageByName(const QString &name) const;
Package *packageByName(const QString &name);
const Package *packageProviding(const Dependency &dependency) const;
Package *packageProviding(const Dependency &dependency);
QList<const Package *> packagesProviding(const Dependency &dependency) const;
QList<Package *> packageByFilter(std::function<bool (const Package *)> pred);
@ -168,6 +203,14 @@ inline const QString &Repository::description() const
return m_description;
}
/*!
* \brief Returns whether the repository only has sources but no built packages.
*/
inline bool Repository::isSourceOnly() const
{
return requestsRequired(PackageDetail::PackageInfo) == PackageDetailAvailability::Never;
}
/*!
* \brief Returns the packages.
*/
@ -215,6 +258,11 @@ inline alpm_db_usage_t Repository::usage() const
return m_usage;
}
inline std::map<QString, QList<Package *> > &Repository::groups()
{
return m_groups;
}
inline const std::map<QString, QList<Package *> > &Repository::groups() const
{
return m_groups;

View File

@ -11,7 +11,7 @@
using namespace std;
using namespace ApplicationUtilities;
namespace PackageManagement {
namespace RepoIndex {
class TaskInfo
{
@ -19,6 +19,7 @@ public:
TaskInfo(QString name, bool onlyDependency = false, const QList<TaskInfo *> &deps = QList<TaskInfo *>());
const QString &name() const;
void setName(const QString &name);
const QList<TaskInfo *> deps() const;
void addDep(TaskInfo *dep);
bool isDone() const;
@ -49,6 +50,11 @@ inline const QString &TaskInfo::name() const
return m_name;
}
inline void TaskInfo::setName(const QString &name)
{
m_name = name;
}
inline const QList<TaskInfo *> TaskInfo::deps() const
{
return m_deps;
@ -76,26 +82,32 @@ inline bool TaskInfo::isOnlyDependency() const
void TaskInfo::add(QList<TaskInfo *> &results)
{
if(!m_done) {
if(m_visited) {
throw *this; // cyclic dependency
if(!isDone()) {
if(isVisited()) {
// cyclic dependency
if(isOnlyDependency()) {
// if this is only a dependency (which we don't want to build) don't care about it
return;
} else {
throw *this;
}
} else {
m_visited = true;
}
for(auto *dep : m_deps) {
for(auto *dep : deps()) {
dep->add(results);
}
m_done = true;
results << this;
if(!isOnlyDependency()) {
results << this;
}
}
}
void TaskInfo::addAll(const QList<TaskInfo *> &tasks, QList<TaskInfo *> &results)
{
for(auto *task : tasks) {
if(!task->m_done) {
task->add(results);
}
task->add(results);
}
}
@ -132,7 +144,7 @@ BuildOrderResolver::BuildOrderResolver(const Manager &manager) :
QStringList BuildOrderResolver::resolve(const StringVector &packages) const
{
cerr << "Getting package information ..." << endl;
cerr << shchar << "Getting package information ..." << endl;
QList<TaskInfo *> tasks;
tasks.reserve(packages.size());
try {
@ -141,18 +153,20 @@ QStringList BuildOrderResolver::resolve(const StringVector &packages) const
tasks << new TaskInfo(QString::fromLocal8Bit(pkgName.data()));
}
// find specified packages and their dependencies
for(auto *task : tasks) {
addDeps(tasks, task);
for(int i = 0, size = tasks.size(); i != size; ++i) {
addDeps(tasks, tasks.at(i));
}
cerr << "Relevant packages: ";
for(const auto *task : tasks) {
cerr << task->name().toLocal8Bit().data() << ' ';
if(m_manager.config().isVerbose()) {
cerr << shchar << "Relevant packages: ";
for(const auto *task : tasks) {
cerr << task->name().toLocal8Bit().data() << ' ';
}
cerr << endl;
}
cerr << endl;
// topo sort
QList<TaskInfo *> results;
results.reserve(tasks.size());
try {
results.reserve(packages.size());
TaskInfo::addAll(tasks, results);
QStringList names;
names.reserve(results.size());
@ -171,30 +185,38 @@ QStringList BuildOrderResolver::resolve(const StringVector &packages) const
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);
}
}
if(const auto pkg = m_manager.packageProviding(Dependency(task->name()))) {
task->setName(pkg->name()); // update the name to ensure we have the acutal package name and not just a "provides" name
addDeps(tasks, task, pkg->dependencies());
} 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";
ss << "The specified 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 QString &depName) const
void BuildOrderResolver::addDeps(QList<TaskInfo *> &tasks, TaskInfo *task, const QList<Dependency> &dependencies) 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;
for(auto &dep : dependencies) {
if(const auto depPkg = m_manager.packageProviding(dep)) {
auto *depTask = TaskInfo::find(tasks, depPkg->name());
if(depTask) {
// we've already added a task for this dependency
// adds dependency task to the dependencies of "parent" task
task->addDep(depTask);
} else {
// create new task
tasks << (depTask = new TaskInfo(depPkg->name(), true));
// adds dependency task to the dependencies of "parent" task
task->addDep(depTask);
// add dependencies of the dependency
addDeps(tasks, depTask, depPkg->dependencies());
}
} else {
stringstream ss;
ss << "The dependency \"" << dep.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());
}
}
}

View File

@ -6,10 +6,11 @@
#include <QString>
#include <QList>
namespace PackageManagement {
namespace RepoIndex {
class Manager;
class TaskInfo;
class Dependency;
class BuildOrderResolver
{
@ -20,7 +21,7 @@ public:
private:
void addDeps(QList<TaskInfo *> &tasks, TaskInfo *task) const;
TaskInfo *addDep(QList<TaskInfo *> &pkgInfos, const QString &depName) const;
void addDeps(QList<TaskInfo *> &tasks, TaskInfo *task, const QList<Dependency> &dependencies) const;
const Manager &m_manager;
};

View File

@ -9,7 +9,7 @@
using namespace std;
namespace PackageManagement {
namespace RepoIndex {
QJsonObject UpgradeResult::json() const
{
@ -27,14 +27,26 @@ UpgradeLookupProcess::UpgradeLookupProcess(UpgradeLookup *upgradeLookup, const R
m_watcher(new QFutureWatcher<void>(this))
{
connect(this, &UpgradeLookupProcess::finished, upgradeLookup, &UpgradeLookup::processFinished);
if(m_upgradeSource->requestsRequired()) {
if((m_reply = m_upgradeSource->requestPackageInfo(m_toCheck->packageNames()))) {
m_reply->setParent(this);
connect(m_reply, &PackageReply::resultsAvailable, this, &UpgradeLookupProcess::sourceReady);
return;
}
switch(m_upgradeSource->requestsRequired()) {
case PackageDetailAvailability::Request:
m_reply = m_upgradeSource->requestPackageInfo(m_toCheck->packageNames());
break;
case PackageDetailAvailability::FullRequest:
m_reply = m_upgradeSource->requestFullPackageInfo(m_toCheck->packageNames());
break;
case PackageDetailAvailability::Never:
m_results.errors << QStringLiteral("Repository \"%1\" does not provide the required information.").arg(m_upgradeSource->name());
emit finished();
return;
case PackageDetailAvailability::Immediately:
break;
}
if(m_reply) {
m_reply->setParent(this);
connect(m_reply, &PackageReply::resultsAvailable, this, &UpgradeLookupProcess::sourceReady);
} else {
sourceReady();
}
sourceReady();
}
const UpgradeLookupResults &UpgradeLookupProcess::results() const

View File

@ -8,7 +8,7 @@
#include <QJsonArray>
#include <QFutureWatcher>
namespace PackageManagement {
namespace RepoIndex {
class Manager;
class UpgradeLookup;

View File

@ -6,7 +6,7 @@
using namespace std;
namespace PackageManagement {
namespace RepoIndex {
namespace Utilities {

View File

@ -10,7 +10,7 @@
QT_FORWARD_DECLARE_CLASS(QJsonArray)
namespace PackageManagement {
namespace RepoIndex {
namespace Utilities {

View File

@ -7,6 +7,7 @@
#include "network/server.h"
#include <c++utilities/application/argumentparser.h>
#include <c++utilities/conversion/stringconversion.h>
#include <c++utilities/application/failure.h>
#include <QCoreApplication>
@ -16,11 +17,18 @@
using namespace std;
using namespace ApplicationUtilities;
using namespace PackageManagement;
using namespace Network;
using namespace RepoIndex;
int main(int argc, char *argv[])
{
// check whether shell syntax is enabled
for(char **arg = argv, **end = argv + argc; arg != end; ++arg) {
if(strcmp(*arg, "--sh-syntax") == 0) {
useShSyntax = true;
shchar = "# ";
break;
}
}
// setup the argument parser
ArgumentParser parser;
ConfigArgs configArgs(parser);
@ -29,7 +37,7 @@ int main(int argc, char *argv[])
try {
parser.parseArgs(argc, argv);
} catch (Failure &e) {
cerr << "Unable to parse arguments: " << e.what() << endl;
cerr << shchar << "Unable to parse arguments: " << e.what() << endl;
return 2;
}
try {
@ -40,13 +48,14 @@ int main(int argc, char *argv[])
if(find_if(parser.mainArguments().cbegin(), parser.mainArguments().cend(), [&configArgs] (const Argument *arg) {
return arg != &configArgs.helpArg && arg->isPresent();
}) != parser.mainArguments().cend()) {
cerr << "Loading databases ..." << endl;
// create app
QCoreApplication application(argc, argv);
// setup ALPM
Manager manager(config);
manager.applyPacmanConfig();
manager.applyRepoIndexConfig();
cerr << shchar << "Loading databases ..." << endl;
manager.initAlpmDataBases();
if(configArgs.serverArg.isPresent()) {
// setup the server
Server server(manager, manager.config());
@ -57,22 +66,42 @@ int main(int argc, char *argv[])
BuildOrderResolver resolver(manager);
const QStringList results = resolver.resolve(configArgs.buildOrderArg.values());
// print results
cout << "Results: ";
for(const auto &pkgName : results) {
cout << pkgName.toLocal8Bit().data() << ' ';
if(useShSyntax) {
cout << "export REPOINDEX_RESULTS=(";
for(const auto &pkgName : results) {
cout << ' ' << '\'' << pkgName.toLocal8Bit().data() << '\'';
}
cout << ' ' << ')' << endl;
} else {
cout << "Results: ";
for(const auto &pkgName : results) {
cout << pkgName.toLocal8Bit().data() << ' ';
}
cout << endl;
}
cout << endl;
} else if(configArgs.mingwBundleArg.isPresent()) {
MingwBundle bundle(manager, configArgs.mingwBundleArg.values(), configArgs.iconThemesArg.values());
bundle.createBundle(configArgs.targetDirArg.isPresent() ? configArgs.targetDirArg.values().front() : string("."),
configArgs.targetNameArg.values().front(),
configArgs.targetFormatArg.isPresent() ? configArgs.targetFormatArg.values().front() : string("zip"));
} else if(configArgs.upgradeLookupArg.isPresent()) {
cerr << shchar << "TODO" << endl;
}
} else if(!configArgs.helpArg.isPresent()) {
if(useShSyntax) {
cerr << "export REPOINDEX_ERROR='No command line arguments specified. See --help for available commands.'" << endl;
} else {
cerr << "No command line arguments specified. See --help for available commands." << endl;
}
} else {
cout << "No command line arguments specified. See --help for available commands." << endl;
}
} catch (std::exception &e) {
cerr << "Error: " << e.what() << endl;
if(useShSyntax) {
string error = e.what();
ConversionUtilities::findAndReplace<string>(error, "'", "\'");
cerr << "export REPOINDEX_ERROR='" << error << '\'' << endl;
} else {
cerr << shchar << "Error: " << e.what() << endl;
}
return 1;
}
}

View File

@ -8,9 +8,7 @@
#include <QJsonArray>
#include <QWebSocket>
using namespace PackageManagement;
namespace Network {
namespace RepoIndex {
Connection::Connection(const Manager &alpmManager, QWebSocket *socket, QObject *parent) :
QObject(parent),

View File

@ -7,18 +7,16 @@
QT_FORWARD_DECLARE_CLASS(QWebSocket)
namespace PackageManagement {
class Manager;
}
namespace RepoIndex {
namespace Network {
class Manager;
class Connection : public QObject
{
Q_OBJECT
public:
Connection(const PackageManagement::Manager &alpmManager, QWebSocket *socket, QObject *parent = nullptr);
Connection(const RepoIndex::Manager &alpmManager, QWebSocket *socket, QObject *parent = nullptr);
private slots:
void processTextMessage(const QString &message);
@ -33,7 +31,7 @@ private:
void handleQuery(const QJsonObject &obj);
QWebSocket *m_socket;
const PackageManagement::Manager &m_alpmManager;
const RepoIndex::Manager &m_alpmManager;
bool m_repoInfoUpdatesRequested;
bool m_groupInfoUpdatesRequested;

View File

@ -12,9 +12,9 @@
using namespace std;
namespace Network {
namespace RepoIndex {
Server::Server(const PackageManagement::Manager &alpmManager, const PackageManagement::Config &config, QObject *parent) :
Server::Server(const RepoIndex::Manager &alpmManager, const RepoIndex::Config &config, QObject *parent) :
QObject(parent),
m_server(new QWebSocketServer(QStringLiteral("Repository index server"),
config.serverInsecure() ? QWebSocketServer::NonSecureMode : QWebSocketServer::SecureMode,
@ -48,11 +48,11 @@ Server::Server(const PackageManagement::Manager &alpmManager, const PackageManag
sslConfiguration.setProtocol(QSsl::TlsV1SslV3);
m_server->setSslConfiguration(sslConfiguration);
} else {
cerr << "Warning: The server is running in insecure mode." << endl;
cerr << shchar << "Warning: The server is running in insecure mode." << endl;
}
// start listening
if(m_server->listen(config.websocketServerListeningAddr(), config.websocketServerListeningPort())) {
cerr << m_server->serverName().toLocal8Bit().data() << " is listening on port " << m_server->serverPort() << endl;
cerr << shchar << m_server->serverName().toLocal8Bit().data() << " is listening on port " << m_server->serverPort() << endl;
connect(m_server, &QWebSocketServer::newConnection, this, &Server::incomingConnection);
connect(m_server, &QWebSocketServer::closed, this, &Server::closed);
} else {

View File

@ -6,14 +6,14 @@
#include <QHostAddress>
#include <QWebSocketServer>
namespace PackageManagement {
namespace RepoIndex {
class Config;
class Manager;
}
namespace Network {
namespace RepoIndex {
class Connection;
@ -22,7 +22,7 @@ class Server : public QObject
Q_OBJECT
public:
Server(const PackageManagement::Manager &alpmManager, const PackageManagement::Config &config, QObject *parent = nullptr);
Server(const RepoIndex::Manager &alpmManager, const RepoIndex::Config &config, QObject *parent = nullptr);
~Server();
signals:
@ -34,7 +34,7 @@ private slots:
private:
QWebSocketServer *m_server;
const PackageManagement::Manager &m_alpmManager;
const RepoIndex::Manager &m_alpmManager;
};
}

View File

@ -15,7 +15,7 @@
using namespace std;
namespace PackageManagement {
namespace RepoIndex {
const char *requestTypeProp = "type";
const QString rpcRequestTypeKey(QStringLiteral("type"));
@ -27,6 +27,7 @@ const QString pkgbuildRequestType(QString("pkgbuild"));
QUrl UserRepository::m_aurRpcUrl = QUrl(QStringLiteral("https://aur.archlinux.org/rpc.php"));
QUrl UserRepository::m_aurPkgbuildUrl = QUrl(QStringLiteral("https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD"));
QUrl UserRepository::m_aurSrcInfoUrl = QUrl(QStringLiteral("https://aur.archlinux.org/cgit/aur.git/plain/.SRCINFO"));
QString UserRepository::m_aurSnapshotPath = QStringLiteral("https://aur.archlinux.org/cgit/aur.git/snapshot/%1.tar.gz");
AurPackageReply::AurPackageReply(QNetworkReply *networkReply, UserRepository *userRepo) :
PackageReply(networkReply, userRepo->packages()),
@ -35,12 +36,13 @@ AurPackageReply::AurPackageReply(QNetworkReply *networkReply, UserRepository *us
void AurPackageReply::processData()
{
if(m_networkReply->error() == QNetworkReply::NoError) {
auto *reply = m_networkReplies.front();
if(reply->error() == QNetworkReply::NoError) {
QJsonParseError error;
//QByteArray data = m_networkReply->readAll();
//cerr << "AUR reply: " << data.data() << endl;
//cerr << shchar << "AUR reply: " << data.data() << endl;
//const QJsonDocument doc = QJsonDocument::fromJson(data, &error);
const QJsonDocument doc = QJsonDocument::fromJson(m_networkReply->readAll(), &error);
const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &error);
if(error.error == QJsonParseError::NoError) {
for(const auto &result : doc.object().value(QStringLiteral("results")).toArray()) {
auto package = make_unique<AurPackage>(result, m_userRepo);
@ -52,30 +54,42 @@ void AurPackageReply::processData()
m_error = QStringLiteral("Error: Unable to parse JSON received from AUR: ") % error.errorString() % QStringLiteral(" at character ") % QString::number(error.offset);
}
} else {
m_error = QStringLiteral("Error: Unable to request data from AUR: ") + m_networkReply->errorString();
m_error = QStringLiteral("Error: Unable to request data from AUR: ") + reply->errorString();
}
emit resultsAvailable();
}
AurFullPackageReply::AurFullPackageReply(const QList<QNetworkReply *> &networkReplies, UserRepository *userRepo) :
PackageReply(networkReplies, userRepo->packages()),
m_userRepo(userRepo)
{}
void AurFullPackageReply::processData()
{
//auto *reply = static_cast<QNetworkReply *>(sender());
// TODO
}
AurSuggestionsReply::AurSuggestionsReply(QNetworkReply *networkReply) :
SuggestionsReply(networkReply)
{}
void AurSuggestionsReply::processData()
{
if(m_networkReply->error() == QNetworkReply::NoError) {
auto *reply = m_networkReplies.front();
if(reply->error() == QNetworkReply::NoError) {
QJsonParseError error;
//QByteArray data = m_networkReply->readAll();
//cerr << "AUR reply: " << data.data() << endl;
//cerr << shchar << "AUR reply: " << data.data() << endl;
//const QJsonDocument doc = QJsonDocument::fromJson(data, &error);
const QJsonDocument doc = QJsonDocument::fromJson(m_networkReply->readAll(), &error);
const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &error);
if(error.error == QJsonParseError::NoError) {
m_suggestions = doc.array();
} else {
m_error = QStringLiteral("Error: Unable to parse JSON received from AUR: ") % error.errorString() % QStringLiteral(" at character ") % QString::number(error.offset);
}
} else {
m_error = QStringLiteral("Error: Unable to request data from AUR: ") + m_networkReply->errorString();
m_error = QStringLiteral("Error: Unable to request data from AUR: ") + reply->errorString();
}
emit resultsAvailable();
}
@ -92,9 +106,17 @@ RepositoryType UserRepository::type() const
return RepositoryType::UserRepository;
}
bool UserRepository::requestsRequired() const
PackageDetailAvailability UserRepository::requestsRequired(PackageDetail packageDetail) const
{
return true;
switch(packageDetail) {
case PackageDetail::Basics:
return PackageDetailAvailability::Request;
case PackageDetail::Dependencies:
case PackageDetail::SourceInfo:
return PackageDetailAvailability::FullRequest;
case PackageDetail::PackageInfo:
return PackageDetailAvailability::Never;
}
}
AurSuggestionsReply *UserRepository::requestSuggestions(const QString &phrase) const
@ -125,20 +147,24 @@ AurPackageReply *UserRepository::requestPackageInfo(const QStringList &packageNa
}
}
AurPackageReply *UserRepository::requestFullPackageInfo(const QString &package, bool forceUpdate) const
AurFullPackageReply *UserRepository::requestFullPackageInfo(const QStringList &packageNames, bool forceUpdate) const
{
try {
const auto &pkg = m_packages.at(package);
if(pkg->hasGeneralInfo() && !forceUpdate) {
return nullptr;
QList<QNetworkReply *> replies;
for(const auto &packageName : packageNames) {
try {
const auto &pkg = m_packages.at(packageName);
if(!pkg->hasGeneralInfo() || !pkg->hasSourceRelatedMetaData() || forceUpdate) {
if(pkg->tarUrl().isEmpty()) {
replies << m_networkAccessManager.get(QNetworkRequest(m_aurSnapshotPath.arg(pkg->name())));
} else {
replies << m_networkAccessManager.get(QNetworkRequest(pkg->tarUrl()));
}
}
} catch(const out_of_range &) {
replies << m_networkAccessManager.get(QNetworkRequest(m_aurSnapshotPath.arg(packageName)));
}
} catch(const out_of_range &) {
}
auto url = m_aurPkgbuildUrl;
QUrlQuery query;
query.addQueryItem(QStringLiteral("h"), package);
url.setQuery(query);
return new AurPackageReply(m_networkAccessManager.get(QNetworkRequest(url)), const_cast<UserRepository *>(this));
return new AurFullPackageReply(replies, const_cast<UserRepository *>(this));
}
} // namespace Alpm

View File

@ -13,7 +13,7 @@
QT_FORWARD_DECLARE_CLASS(QNetworkAccessManager)
QT_FORWARD_DECLARE_CLASS(QNetworkReply)
namespace PackageManagement {
namespace RepoIndex {
class UserRepository;
@ -22,7 +22,19 @@ class AurPackageReply : public PackageReply
Q_OBJECT
public:
AurPackageReply(QNetworkReply *networkReply, UserRepository *userRepo);
const std::map<QString, std::unique_ptr<Package> > &packages() const;
private slots:
void processData();
private:
UserRepository *m_userRepo;
};
class AurFullPackageReply : public PackageReply
{
Q_OBJECT
public:
AurFullPackageReply(const QList<QNetworkReply *> &networkReplies, UserRepository *userRepo);
private slots:
void processData();
@ -52,16 +64,17 @@ public:
static const QUrl aurRpcUrl();
static void setAurRpcUrl(const QUrl &aurRpcUrl);
bool requestsRequired() const;
PackageDetailAvailability requestsRequired(PackageDetail packageDetail) const;
AurSuggestionsReply *requestSuggestions(const QString &phrase) const;
AurPackageReply *requestPackageInfo(const QStringList &packageNames, bool forceUpdate = false) const;
AurPackageReply *requestFullPackageInfo(const QString &package, bool forceUpdate = false) const;
AurFullPackageReply *requestFullPackageInfo(const QStringList &packageNames, bool forceUpdate = false) const;
private:
QNetworkAccessManager &m_networkAccessManager;
QNetworkAccessManager &m_networkAccessManager;
static QUrl m_aurRpcUrl;
static QUrl m_aurPkgbuildUrl;
static QUrl m_aurSrcInfoUrl;
static QString m_aurSnapshotPath;
};
inline const QUrl UserRepository::aurRpcUrl()