improved build-order and mingw-w64-bundle
This commit is contained in:
parent
1f1d46c5dd
commit
39e3ccc77c
|
@ -91,9 +91,15 @@ void MingwBundle::addDependencies(const Package *pkg)
|
|||
enum class RelevantFileType
|
||||
{
|
||||
Binary,
|
||||
Data,
|
||||
Translation,
|
||||
QtTranslation,
|
||||
QtPlugin,
|
||||
Plugin,
|
||||
SharedData,
|
||||
GLib2Data,
|
||||
GLib2Schemas,
|
||||
Theme,
|
||||
IconTheme,
|
||||
ConfigFile
|
||||
};
|
||||
|
@ -122,6 +128,7 @@ struct RelevantFile
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString name;
|
||||
QByteArray data;
|
||||
RelevantFileType fileType;
|
||||
|
@ -136,6 +143,7 @@ struct PkgFileInfo
|
|||
path(path),
|
||||
failure(false)
|
||||
{}
|
||||
|
||||
QString name;
|
||||
QString path;
|
||||
unique_ptr<KTar> archive;
|
||||
|
@ -143,15 +151,17 @@ struct PkgFileInfo
|
|||
bool failure;
|
||||
};
|
||||
|
||||
void addEntries(PkgFileInfo &pkgFileInfo, RelevantFileType fileType, const KArchiveDirectory *dir, const QString &relPath = QString())
|
||||
void addEntries(PkgFileInfo &pkgFileInfo, RelevantFileType fileType, const KArchiveDirectory *dir, const QString &relPath = QString(), const QString &ext = QString(), RelevantFileArch relevantArch = RelevantFileArch::Any)
|
||||
{
|
||||
QString newPath = relPath.isEmpty() ? dir->name() : relPath % QChar('/') % dir->name();
|
||||
const QString newPath = relPath.isEmpty() ? dir->name() : relPath % QChar('/') % dir->name();
|
||||
for(const auto &entryName : dir->entries()) {
|
||||
if(auto *entry = dir->entry(entryName)) {
|
||||
if(entry->isDirectory()) {
|
||||
addEntries(pkgFileInfo, fileType, static_cast<const KArchiveDirectory *>(entry), newPath);
|
||||
addEntries(pkgFileInfo, fileType, static_cast<const KArchiveDirectory *>(entry), newPath, ext, relevantArch);
|
||||
} else if(entry->isFile()) {
|
||||
pkgFileInfo.relevantFiles.emplace_back(static_cast<const KArchiveFile *>(entry), dir, fileType, RelevantFileArch::Any, newPath);
|
||||
if(ext.isEmpty() || entry->name().endsWith(ext)) {
|
||||
pkgFileInfo.relevantFiles.emplace_back(static_cast<const KArchiveFile *>(entry), dir, fileType, relevantArch, newPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,10 +175,14 @@ void getFiles(PkgFileInfo &pkgFileInfo)
|
|||
make_pair(RelevantFileArch::x86_64, QStringLiteral("/usr/x86_64-w64-mingw32")),
|
||||
make_pair(RelevantFileArch::i686, QStringLiteral("/usr/i686-w64-mingw32"))
|
||||
};
|
||||
// add files for each architecture separately
|
||||
for(const auto &root : roots) {
|
||||
// get root entry
|
||||
const auto *rootEntry = pkgFileInfo.archive->directory()->entry(root.second);
|
||||
if(rootEntry && rootEntry->isDirectory()) {
|
||||
const auto *rootDir = static_cast<const KArchiveDirectory *>(rootEntry);
|
||||
|
||||
// binary directory: *.exe and *.dll files required
|
||||
const auto *binEntry = rootDir->entry(QStringLiteral("bin"));
|
||||
if(binEntry && binEntry->isDirectory()) {
|
||||
const auto *binDir = static_cast<const KArchiveDirectory *>(binEntry);
|
||||
|
@ -182,8 +196,11 @@ void getFiles(PkgFileInfo &pkgFileInfo)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// library directory: Qt, GTK and application specific libs/plugins required
|
||||
const auto *libEntry = rootDir->entry(QStringLiteral("lib"));
|
||||
if(libEntry && libEntry->isDirectory()) {
|
||||
// Qt 5 plugins
|
||||
const auto *libDir = static_cast<const KArchiveDirectory *>(libEntry);
|
||||
const auto *qtEntry = libDir->entry("qt");
|
||||
if(qtEntry && qtEntry->isDirectory()) {
|
||||
|
@ -206,7 +223,38 @@ void getFiles(PkgFileInfo &pkgFileInfo)
|
|||
}
|
||||
}
|
||||
}
|
||||
// Qt 4 plugins
|
||||
qtEntry = libDir->entry(QStringLiteral("qt4"));
|
||||
if(qtEntry && qtEntry->isDirectory()) {
|
||||
addEntries(pkgFileInfo, RelevantFileType::Plugin, static_cast<const KArchiveDirectory *>(qtEntry), QString(), QString(), root.first);
|
||||
}
|
||||
|
||||
// GTK plugins
|
||||
const auto *gtkEntry = libDir->entry(QStringLiteral("gtk-2.0"));
|
||||
if(gtkEntry && gtkEntry->isDirectory()) {
|
||||
addEntries(pkgFileInfo, RelevantFileType::Plugin, static_cast<const KArchiveDirectory *>(gtkEntry), QString(), QStringLiteral(".dll"), root.first);
|
||||
}
|
||||
gtkEntry = libDir->entry(QStringLiteral("gtk-3.0"));
|
||||
if(gtkEntry && gtkEntry->isDirectory()) {
|
||||
addEntries(pkgFileInfo, RelevantFileType::Plugin, static_cast<const KArchiveDirectory *>(gtkEntry), QString(), QStringLiteral(".dll"), root.first);
|
||||
}
|
||||
|
||||
// app specific libs/plugins
|
||||
const auto *evinceEntry = libDir->entry(pkgFileInfo.name);
|
||||
if(evinceEntry && evinceEntry->isDirectory()) {
|
||||
addEntries(pkgFileInfo, RelevantFileType::Plugin, static_cast<const KArchiveDirectory *>(evinceEntry), QString(), QStringLiteral(".dll"), root.first);
|
||||
// required by evince
|
||||
addEntries(pkgFileInfo, RelevantFileType::Plugin, static_cast<const KArchiveDirectory *>(evinceEntry), QString(), QStringLiteral(".evince-backend"), root.first);
|
||||
}
|
||||
}
|
||||
|
||||
// data directory: required by geany
|
||||
const auto *dataEntry = rootDir->entry(QStringLiteral("data"));
|
||||
if(dataEntry && dataEntry->isDirectory()) {
|
||||
addEntries(pkgFileInfo, RelevantFileType::Data, static_cast<const KArchiveDirectory *>(dataEntry), QString(), QString(), root.first);
|
||||
}
|
||||
|
||||
// shared directory: translations, themes, icons, schemas
|
||||
const auto *shareEntry = rootDir->entry(QStringLiteral("share"));
|
||||
if(shareEntry && shareEntry->isDirectory()) {
|
||||
const auto *shareDir = static_cast<const KArchiveDirectory *>(shareEntry);
|
||||
|
@ -227,16 +275,46 @@ void getFiles(PkgFileInfo &pkgFileInfo)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto *themesEntry = shareDir->entry(QStringLiteral("themes"));
|
||||
if(themesEntry && themesEntry->isDirectory()) {
|
||||
const auto *themesDir = static_cast<const KArchiveDirectory *>(themesEntry);
|
||||
for(const auto &themeName : themesDir->entries()) {
|
||||
const auto *themeEntry = themesDir->entry(themeName);
|
||||
if(themeEntry && themeEntry->isDirectory()) {
|
||||
addEntries(pkgFileInfo, RelevantFileType::Theme, static_cast<const KArchiveDirectory *>(themeEntry), QString(), QString(), root.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto *iconsEntry = shareDir->entry(QStringLiteral("icons"));
|
||||
if(iconsEntry && iconsEntry->isDirectory()) {
|
||||
const auto *iconsDir = static_cast<const KArchiveDirectory *>(iconsEntry);
|
||||
for(const auto &themeName : iconsDir->entries()) {
|
||||
const auto *themeEntry = iconsDir->entry(themeName);
|
||||
if(themeEntry && themeEntry->isDirectory()) {
|
||||
addEntries(pkgFileInfo, RelevantFileType::IconTheme, static_cast<const KArchiveDirectory *>(themeEntry));
|
||||
addEntries(pkgFileInfo, RelevantFileType::IconTheme, static_cast<const KArchiveDirectory *>(themeEntry), QString(), QString(), root.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto *glib2Entry = shareDir->entry(QStringLiteral("glib-2.0"));
|
||||
if(glib2Entry && glib2Entry->isDirectory()) {
|
||||
const auto *glib2Dir = static_cast<const KArchiveDirectory *>(glib2Entry);
|
||||
for(const auto &glib2SubEntryName : glib2Dir->entries()) {
|
||||
const auto *glib2SubEntry = glib2Dir->entry(glib2SubEntryName);
|
||||
if(glib2SubEntry->isDirectory()) {
|
||||
if(glib2SubEntry->name() == QLatin1String("schemas")) {
|
||||
addEntries(pkgFileInfo, RelevantFileType::GLib2Schemas, static_cast<const KArchiveDirectory *>(glib2SubEntry), QStringLiteral("glib-2.0"), QString(), root.first);
|
||||
} else {
|
||||
addEntries(pkgFileInfo, RelevantFileType::GLib2Data, static_cast<const KArchiveDirectory *>(glib2SubEntry), QStringLiteral("glib-2.0"), QString(), root.first);
|
||||
}
|
||||
} else if(glib2SubEntry->isFile()) {
|
||||
pkgFileInfo.relevantFiles.emplace_back(static_cast<const KArchiveFile *>(glib2SubEntry), glib2Dir, RelevantFileType::GLib2Data, root.first, QStringLiteral("glib-2.0"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(pkgFileInfo.name.compare(QLatin1String("qt"))) {
|
||||
const auto *appEntry = shareDir->entry(pkgFileInfo.name);
|
||||
if(appEntry && appEntry->isDirectory()) {
|
||||
|
@ -285,9 +363,16 @@ void getFiles(PkgFileInfo &pkgFileInfo)
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
const auto *localeEntry = shareDir->entry(QStringLiteral("locale"));
|
||||
if(localeEntry && localeEntry->isDirectory()) {
|
||||
addEntries(pkgFileInfo, RelevantFileType::SharedData, static_cast<const KArchiveDirectory *>(localeEntry), QString(), QString(), root.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// icons from regular package
|
||||
const auto *iconsEntry = pkgFileInfo.archive->directory()->entry(QStringLiteral("usr/share/icons"));
|
||||
if(iconsEntry && iconsEntry->isDirectory()) {
|
||||
const auto *iconsDir = static_cast<const KArchiveDirectory *>(iconsEntry);
|
||||
|
@ -305,6 +390,7 @@ void getFiles(PkgFileInfo &pkgFileInfo)
|
|||
|
||||
void makeArchive(const list<PkgFileInfo> &pkgFiles, const QByteArray &pkgList, const QByteArray &indexFile, RelevantFileArch arch, const QString &root, const string &targetDir, const string &targetName, const string &targetFormat)
|
||||
{
|
||||
// prepare archive
|
||||
QString targetPath = qstr(targetDir) % QChar('/') % root % QChar('-') % qstr(targetName) % QChar('.') % qstr(targetFormat);
|
||||
cerr << shchar << "Making archive \"" << targetPath.toLocal8Bit().data() << "\" ..." << endl;
|
||||
unique_ptr<KArchive> targetArchive;
|
||||
|
@ -317,20 +403,63 @@ void makeArchive(const list<PkgFileInfo> &pkgFiles, const QByteArray &pkgList, c
|
|||
} else {
|
||||
throw runtime_error("Specified archive format \"" + targetFormat + "\" is unknown.");
|
||||
}
|
||||
|
||||
// add files to archive
|
||||
if(targetArchive->open(QIODevice::WriteOnly)) {
|
||||
// add note
|
||||
// -> compile glib2 schemas and add
|
||||
vector<const RelevantFile *> glib2SchemaFiles;
|
||||
glib2SchemaFiles.reserve(16);
|
||||
for(const auto &pkgFile : pkgFiles) {
|
||||
for(const RelevantFile &relevantFile : pkgFile.relevantFiles) {
|
||||
if(relevantFile.arch == RelevantFileArch::Any || relevantFile.arch == arch) {
|
||||
switch(relevantFile.fileType) {
|
||||
case RelevantFileType::GLib2Schemas:
|
||||
glib2SchemaFiles.push_back(&relevantFile);
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!glib2SchemaFiles.empty()) {
|
||||
QTemporaryDir tempDir;
|
||||
if(tempDir.isValid()) {
|
||||
for(const RelevantFile *schemaFile : glib2SchemaFiles) {
|
||||
QFile file(tempDir.path() % QChar('/') % schemaFile->name);
|
||||
if(!file.open(QFile::WriteOnly) || !file.write(schemaFile->data)) {
|
||||
cerr << shchar << "Unable to create glib2 schema file \"" << schemaFile->name.toLocal8Bit().data() << "\"." << endl;
|
||||
}
|
||||
}
|
||||
QProcess compiler;
|
||||
compiler.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
compiler.start(QStringLiteral("glib-compile-schemas"), QStringList() << tempDir.path());
|
||||
compiler.waitForFinished();
|
||||
QFile compiledSchemas(tempDir.path() + QStringLiteral("/gschemas.compiled"));
|
||||
if(compiledSchemas.open(QFile::ReadOnly)) {
|
||||
targetArchive->writeFile(root + QStringLiteral("/share/glib-2.0/schemas/gschemas.compiled"), compiledSchemas.readAll());
|
||||
} else {
|
||||
cerr << shchar << "Unable to compile glib2 schemas: Compiled schemas (gschemas.compiled) not found." << endl;
|
||||
}
|
||||
|
||||
} else {
|
||||
cerr << shchar << "Unable to compile glib2 schemas: Can't create temp dir." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// -> add note
|
||||
targetArchive->writeFile(root + QStringLiteral("/note.txt"), QByteArray("This archive has been created with Martchus' repository indexing tool.\n"
|
||||
"List of included packages: var/lib/repoindex/packages.list\n"
|
||||
"More info: http://martchus.netai.net/page.php?name=programming"));
|
||||
// add package list
|
||||
// -> add package list
|
||||
if(!pkgList.isEmpty()) {
|
||||
targetArchive->writeFile(root + QStringLiteral("/var/lib/repoindex/packages.list"), pkgList);
|
||||
}
|
||||
// set default icon theme
|
||||
// -> set default icon theme
|
||||
if(!indexFile.isEmpty()) {
|
||||
targetArchive->writeFile(root + QStringLiteral("/share/icons/default/index.theme"), indexFile);
|
||||
}
|
||||
// add relevant files from packages
|
||||
// -> add relevant files from packages
|
||||
for(const auto &pkgFile : pkgFiles) {
|
||||
for(const RelevantFile &relevantFile : pkgFile.relevantFiles) {
|
||||
if(relevantFile.arch == RelevantFileArch::Any || relevantFile.arch == arch) {
|
||||
|
@ -341,6 +470,10 @@ void makeArchive(const list<PkgFileInfo> &pkgFiles, const QByteArray &pkgList, c
|
|||
path = root % QStringLiteral("/bin/") % relevantFile.name;
|
||||
mode = 0100755;
|
||||
break;
|
||||
case RelevantFileType::Data:
|
||||
path = root % QStringLiteral("/") % relevantFile.subDir % QChar('/') % relevantFile.name;
|
||||
mode = 0100644;
|
||||
break;
|
||||
case RelevantFileType::Translation:
|
||||
path = root % QStringLiteral("/share/") % pkgFile.name % QStringLiteral("/translations/") % relevantFile.name;
|
||||
mode = 0100644;
|
||||
|
@ -353,6 +486,20 @@ void makeArchive(const list<PkgFileInfo> &pkgFiles, const QByteArray &pkgList, c
|
|||
path = root % QStringLiteral("/bin/") % relevantFile.subDir % QChar('/') % relevantFile.name;
|
||||
mode = 0100755;
|
||||
break;
|
||||
case RelevantFileType::Plugin:
|
||||
path = root % QStringLiteral("/lib/") % relevantFile.subDir % QChar('/') % relevantFile.name;
|
||||
mode = 0100755;
|
||||
break;
|
||||
case RelevantFileType::SharedData:
|
||||
case RelevantFileType::GLib2Data:
|
||||
case RelevantFileType::GLib2Schemas:
|
||||
path = root % QStringLiteral("/share/") % relevantFile.subDir % QChar('/') % relevantFile.name;
|
||||
mode = 0100644;
|
||||
break;
|
||||
case RelevantFileType::Theme:
|
||||
path = root % QStringLiteral("/share/themes/") % relevantFile.subDir % QChar('/') % relevantFile.name;
|
||||
mode = 0100644;
|
||||
break;
|
||||
case RelevantFileType::IconTheme:
|
||||
path = root % QStringLiteral("/share/icons/") % relevantFile.subDir % QChar('/') % relevantFile.name;
|
||||
mode = 0100644;
|
||||
|
|
|
@ -123,6 +123,9 @@ void Package::computeRequiredBy(Manager &manager)
|
|||
m_requiredByComputed = true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Checks whether the specified \a name and the specified \a version match the specified \a dependency.
|
||||
*/
|
||||
bool Package::matches(const QString &name, const QString &version, const Dependency &dependency)
|
||||
{
|
||||
if(name == dependency.name) {
|
||||
|
@ -147,6 +150,25 @@ bool Package::matches(const QString &name, const QString &version, const Depende
|
|||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Checks whether the package matches the specified \a dependency.
|
||||
*/
|
||||
bool Package::matches(const Dependency &dependency)
|
||||
{
|
||||
// check whether package matches "directly"
|
||||
if(matches(name(), version(), dependency)) {
|
||||
return true;
|
||||
} else {
|
||||
// check whether at least one of the provides matches
|
||||
for(const auto &provide : provides()) {
|
||||
if(matches(provide.name, provide.version, dependency)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \cond
|
||||
*/
|
||||
|
@ -663,6 +685,24 @@ Dependency::Dependency(const QString &dependency)
|
|||
}
|
||||
}
|
||||
|
||||
QString Dependency::toString() const
|
||||
{
|
||||
switch(mode) {
|
||||
case ALPM_DEP_MOD_EQ:
|
||||
return name % QChar('=') % version;
|
||||
case ALPM_DEP_MOD_GE:
|
||||
return name % QChar('>') % QChar('=') % version;
|
||||
case ALPM_DEP_MOD_LE:
|
||||
return name % QChar('<') % QChar('=') % version;
|
||||
case ALPM_DEP_MOD_GT:
|
||||
return name % QChar('>') % version;
|
||||
case ALPM_DEP_MOD_LT:
|
||||
return name % QChar('<') % version;
|
||||
default:
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns a JSON object for the current instance.
|
||||
*/
|
||||
|
@ -690,6 +730,8 @@ QJsonObject Dependency::toJson() const
|
|||
case ALPM_DEP_MOD_LT:
|
||||
obj.insert(QStringLiteral("mod"), QStringLiteral("lt"));
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
if(!description.isEmpty()) {
|
||||
obj.insert(QStringLiteral("desc"), description);
|
||||
|
|
|
@ -81,6 +81,9 @@ public:
|
|||
explicit Dependency(const QString &name, const QString &version, _alpm_depmod_t mode = ALPM_DEP_MOD_ANY, const QString &description = QString());
|
||||
explicit Dependency(const QString &dependency);
|
||||
|
||||
bool operator ==(const Dependency &other) const;
|
||||
|
||||
QString toString() const;
|
||||
QJsonObject toJson() const;
|
||||
|
||||
QString name;
|
||||
|
@ -96,6 +99,16 @@ inline Dependency::Dependency(const QString &name, const QString &version, _alpm
|
|||
description(description)
|
||||
{}
|
||||
|
||||
inline bool Dependency::operator ==(const Dependency &other) const
|
||||
{
|
||||
return other.name == name && other.description == description && other.mode == mode;
|
||||
}
|
||||
|
||||
inline uint qHash(const Dependency &dependency, uint seed)
|
||||
{
|
||||
return qHash(dependency.name, seed) ^ qHash(dependency.version, seed) ^ qHash(dependency.mode, seed) ^ qHash(dependency.description, seed);
|
||||
}
|
||||
|
||||
class Manager;
|
||||
|
||||
class Package
|
||||
|
@ -160,6 +173,7 @@ public:
|
|||
ChronoUtilities::DateTime lastModified() const;
|
||||
const QString &tarUrl() const;
|
||||
const std::map<QString, QByteArray> &sourceFiles() const;
|
||||
QList<const QList<Dependency> *> allDependencies() const;
|
||||
|
||||
// version comparsion
|
||||
PackageVersionComparsion compareVersion(const Package *syncPackage) const;
|
||||
|
@ -651,6 +665,14 @@ inline const std::map<QString, QByteArray> &Package::sourceFiles() const
|
|||
return m_sourceFiles;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns all dependencies.
|
||||
*/
|
||||
inline QList<const QList<Dependency> *> Package::allDependencies() const
|
||||
{
|
||||
return {&dependencies(), &makeDependencies(), &checkDependencies()};
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Compares the version of the package with the specified sync package.
|
||||
*/
|
||||
|
@ -667,14 +689,6 @@ inline PackageVersionComparsion Package::compareVersion(const Dependency &depend
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // ALPM_PACKAGE_H
|
||||
|
|
|
@ -160,13 +160,6 @@ const Package *Repository::packageProviding(const Dependency &dependency) const
|
|||
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(const auto &provide : entry.second->provides()) {
|
||||
if(Package::matches(provide.name, provide.version, dependency)) {
|
||||
return entry.second.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
@ -182,13 +175,6 @@ Package *Repository::packageProviding(const Dependency &dependency)
|
|||
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;
|
||||
|
@ -202,16 +188,7 @@ QList<const Package *> Repository::packagesProviding(const Dependency &dependenc
|
|||
QList<const Package *> res;
|
||||
for(const auto &entry : m_packages) {
|
||||
if(entry.second->matches(dependency)) {
|
||||
// check whether package matches "directly"
|
||||
res << entry.second.get();
|
||||
} else {
|
||||
// check whether at least one of the provides matches
|
||||
for(const auto &provide : entry.second->provides()) {
|
||||
if(Package::matches(provide.name, provide.version, dependency)) {
|
||||
res << entry.second.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <c++utilities/misc/memory.h>
|
||||
|
||||
#include <QStringBuilder>
|
||||
#include <QEventLoop>
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
@ -18,14 +19,17 @@ using namespace ApplicationUtilities;
|
|||
|
||||
namespace RepoIndex {
|
||||
|
||||
/*!
|
||||
* \brief The TaskInfo class defines a build task and provides the topo-sort required to resolve the build order.
|
||||
*/
|
||||
class TaskInfo
|
||||
{
|
||||
public:
|
||||
TaskInfo(QString name, bool onlyDependency = false, const QList<TaskInfo *> &deps = QList<TaskInfo *>());
|
||||
TaskInfo(const QString &name, bool onlyDependency = false, const QSet<TaskInfo *> &deps = QSet<TaskInfo *>());
|
||||
|
||||
const QString &name() const;
|
||||
void setName(const QString &name);
|
||||
const QList<TaskInfo *> deps() const;
|
||||
const QSet<TaskInfo *> deps() const;
|
||||
void addDep(TaskInfo *dep);
|
||||
bool isDone() const;
|
||||
bool isVisited() const;
|
||||
|
@ -33,23 +37,29 @@ public:
|
|||
void setIsOnlyDependency(bool isOnlyDependency);
|
||||
bool isPackageRequested() const;
|
||||
void setPackageRequested();
|
||||
QSet<Dependency> requiredFor() const;
|
||||
void addRequiredFor(const Dependency &dependency);
|
||||
void add(QList<TaskInfo *> &results);
|
||||
Package *associatedPackage() const;
|
||||
void associatePackage(Package *package);
|
||||
bool associatePackage(Package *package);
|
||||
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;
|
||||
QSet<TaskInfo *> m_deps;
|
||||
bool m_done;
|
||||
bool m_visited;
|
||||
bool m_onlyDep;
|
||||
bool m_pkgRequested;
|
||||
QSet<Dependency> m_requiredFor;
|
||||
Package *m_associatedPackage;
|
||||
};
|
||||
|
||||
inline TaskInfo::TaskInfo(QString name, bool onlyDependency, const QList<TaskInfo *> &deps) :
|
||||
/*!
|
||||
* \brief Constructs a new task.
|
||||
*/
|
||||
inline TaskInfo::TaskInfo(const QString &name, bool onlyDependency, const QSet<TaskInfo *> &deps) :
|
||||
m_name(name),
|
||||
m_deps(deps),
|
||||
m_done(false),
|
||||
|
@ -59,6 +69,13 @@ inline TaskInfo::TaskInfo(QString name, bool onlyDependency, const QList<TaskInf
|
|||
m_associatedPackage(nullptr)
|
||||
{}
|
||||
|
||||
/*!
|
||||
* \brief Returns the name of the task.
|
||||
*
|
||||
* This is usually the name which has been specified when constructing
|
||||
* the task. However, the name might be adjusted to the package name
|
||||
* when a package is associated.
|
||||
*/
|
||||
inline const QString &TaskInfo::name() const
|
||||
{
|
||||
return m_name;
|
||||
|
@ -69,46 +86,105 @@ inline void TaskInfo::setName(const QString &name)
|
|||
m_name = name;
|
||||
}
|
||||
|
||||
inline const QList<TaskInfo *> TaskInfo::deps() const
|
||||
/*!
|
||||
* \brief Returns the dependencies added via addDep().
|
||||
*/
|
||||
inline const QSet<TaskInfo *> TaskInfo::deps() const
|
||||
{
|
||||
return m_deps;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Adds another task as dependency.
|
||||
*/
|
||||
inline void TaskInfo::addDep(TaskInfo *dep)
|
||||
{
|
||||
m_deps << dep;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether the task already has been added to the resulting list of tasks.
|
||||
* \sa add()
|
||||
*/
|
||||
inline bool TaskInfo::isDone() const
|
||||
{
|
||||
return m_done;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether the task has been visited.
|
||||
* \remarks Used to track circular dependencies (within add()).
|
||||
*/
|
||||
inline bool TaskInfo::isVisited() const
|
||||
{
|
||||
return m_visited;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether the task is only a dependency and none of the packages to be build.
|
||||
* \remarks Circular dependencies between packages which are only dependencies can be ignored.
|
||||
*/
|
||||
inline bool TaskInfo::isOnlyDependency() const
|
||||
{
|
||||
return m_onlyDep;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets whether the package is only a dependency.
|
||||
* \sa isOnlyDependency()
|
||||
*/
|
||||
inline void TaskInfo::setIsOnlyDependency(bool isOnlyDependency)
|
||||
{
|
||||
m_onlyDep = isOnlyDependency;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether the package for this task has been requested yet.
|
||||
*
|
||||
* Used to avoid requesting the same package twice after the package
|
||||
* couldn't be found when requesting it the first time.
|
||||
*/
|
||||
inline bool TaskInfo::isPackageRequested() const
|
||||
{
|
||||
return m_pkgRequested;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets whether the package has been requested.
|
||||
*
|
||||
* This is set just before a package for this task is requrested.
|
||||
*/
|
||||
inline void TaskInfo::setPackageRequested()
|
||||
{
|
||||
m_pkgRequested = true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the dependencies the task has been added for.
|
||||
* \remarks
|
||||
* - The same task can be added for multiple dependencies, eg.
|
||||
* mingw-w64-extra-cmake-modules and mingw-w64-extra-cmake-modules=5.15.0.
|
||||
* - The dependencies must be set explicitely using addRequiredFor().
|
||||
*/
|
||||
inline QSet<Dependency> TaskInfo::requiredFor() const
|
||||
{
|
||||
return m_requiredFor;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Adds the specified \a dependency to the list of dependencies the task has been added for.
|
||||
* \sa requiredFor()
|
||||
*/
|
||||
inline void TaskInfo::addRequiredFor(const Dependency &dependency)
|
||||
{
|
||||
m_requiredFor << dependency;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Adds the task to the specified list of results. Ensures that all dependencies are added before.
|
||||
* \throws If a circular dependency between the build tasks has been detected, the causing task is thrown.
|
||||
* \remarks Dependencies must be added via addDep() before calling this method.
|
||||
*/
|
||||
void TaskInfo::add(QList<TaskInfo *> &results)
|
||||
{
|
||||
if(!isDone()) {
|
||||
|
@ -133,18 +209,57 @@ void TaskInfo::add(QList<TaskInfo *> &results)
|
|||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the package associated via associatePackage().
|
||||
*/
|
||||
inline Package *TaskInfo::associatedPackage() const
|
||||
{
|
||||
return m_associatedPackage;
|
||||
}
|
||||
|
||||
void TaskInfo::associatePackage(Package *package)
|
||||
/*!
|
||||
* \brief Associates the specified \a package with this build task.
|
||||
*
|
||||
* Does nothing if there is already a package assigned which matches all
|
||||
* dependencies the task has been added for. Returns true in this case.
|
||||
*
|
||||
* Fails if the specified \a package does not match all dependencies the task
|
||||
* has been added for.
|
||||
*
|
||||
* \return Returns whether either the currenlty assigned package or the specified
|
||||
* \a package matches all dependencies the task has been added for.
|
||||
*/
|
||||
bool TaskInfo::associatePackage(Package *package)
|
||||
{
|
||||
// check whether an appropriate package is already assigned
|
||||
// and whether the package matches all dependencies this task is required for
|
||||
bool assignmentRequired = !m_associatedPackage, assignmentPossible = true;
|
||||
for(const Dependency &dep : m_requiredFor) {
|
||||
if(!assignmentRequired && !m_associatedPackage->matches(dep)) {
|
||||
assignmentRequired = true;
|
||||
}
|
||||
if(!package->matches(dep)) {
|
||||
assignmentPossible = false;
|
||||
}
|
||||
}
|
||||
if(!assignmentRequired) {
|
||||
return true;
|
||||
}
|
||||
if(!assignmentPossible) {
|
||||
return false;
|
||||
}
|
||||
m_associatedPackage = package;
|
||||
// update the name to ensure we have the acutal package name and not just a "provides" name
|
||||
m_name = package->name();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Adds the specified \a tasks and their dependencies to the specified list of results. Ensures that all
|
||||
* tasks are added in the right order.
|
||||
* \throws If a circular dependency between the build tasks has been detected, the causing task is thrown.
|
||||
* \remarks Dependencies must be added to the \a tasks via addDep() before calling this method.
|
||||
*/
|
||||
void TaskInfo::addAll(const QList<TaskInfo *> &tasks, QList<TaskInfo *> &results)
|
||||
{
|
||||
for(auto *task : tasks) {
|
||||
|
@ -152,6 +267,10 @@ void TaskInfo::addAll(const QList<TaskInfo *> &tasks, QList<TaskInfo *> &results
|
|||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Finds the task with the specified \a name in the specified list of \a tasks.
|
||||
* \remarks Does not search dependencies the list does not contain.
|
||||
*/
|
||||
TaskInfo *TaskInfo::find(const QList<TaskInfo *> &tasks, const QString &name)
|
||||
{
|
||||
for(auto *task : tasks) {
|
||||
|
@ -162,104 +281,53 @@ TaskInfo *TaskInfo::find(const QList<TaskInfo *> &tasks, const QString &name)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
template<class ListType>
|
||||
class DestroyList
|
||||
{
|
||||
public:
|
||||
DestroyList(ListType &list) :
|
||||
m_list(list)
|
||||
{}
|
||||
/*!
|
||||
* \class BuildOrderResolver
|
||||
* \brief The BuildOrderResolver class resolves the build order for a specified array of packages.
|
||||
*
|
||||
* Resolving the build order is done in the following steps:
|
||||
* 1. instantiation: initial tasks are added
|
||||
* 2. findDependencies(): dependencies for each task are added, some packages might have to be requested first
|
||||
* 3. if some packages must be requested: wait for requested packages, if requested packages are available, go to 2.
|
||||
* 4. all dependencies are added: signal ready() is emitted
|
||||
* 5. resolve(): build order is resolved, either resolvingFinished() or resolvingFailed() is emitted
|
||||
*/
|
||||
|
||||
~DestroyList()
|
||||
{
|
||||
qDeleteAll(m_list);
|
||||
}
|
||||
|
||||
private:
|
||||
ListType &m_list;
|
||||
};
|
||||
|
||||
BuildOrderResolver::BuildOrderResolver(Manager &manager, const StringVector &packages, bool addSourceOnlyDeps) :
|
||||
/*!
|
||||
* \brief Creates a new BuildOrderResolver using the specified \a manager.
|
||||
*/
|
||||
BuildOrderResolver::BuildOrderResolver(Manager &manager, bool addSourceOnlyDeps) :
|
||||
m_manager(manager),
|
||||
m_finder(nullptr),
|
||||
m_addSourceOnlyDeps(addSourceOnlyDeps)
|
||||
m_addSourceOnlyDeps(addSourceOnlyDeps),
|
||||
m_hasFinished(false)
|
||||
{}
|
||||
|
||||
/*!
|
||||
* \brief Adds the specified \a errorMessage.
|
||||
*/
|
||||
void BuildOrderResolver::addError(const QString &errorMessage)
|
||||
{
|
||||
cerr << shchar << "Getting package information ..." << endl;
|
||||
m_tasks.clear();
|
||||
m_tasks.reserve(packages.size() * 2);
|
||||
// add a task for each specified package
|
||||
for(const auto &pkgName : packages) {
|
||||
m_tasks << new TaskInfo(QString::fromLocal8Bit(pkgName.data()));
|
||||
if(!m_errorMessage.isEmpty()) {
|
||||
m_errorMessage.append(QStringLiteral(" \u2192 "));
|
||||
}
|
||||
m_errorMessage.append(errorMessage);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Destroys the object.
|
||||
*/
|
||||
BuildOrderResolver::~BuildOrderResolver()
|
||||
{
|
||||
qDeleteAll(m_tasks);
|
||||
}
|
||||
|
||||
void BuildOrderResolver::cli()
|
||||
{
|
||||
if(m_manager.config().isVerbose()) {
|
||||
connect(this, &BuildOrderResolver::ready, this, &BuildOrderResolver::printRelevantPackages);
|
||||
}
|
||||
connect(this, &BuildOrderResolver::ready, this, &BuildOrderResolver::resolve);
|
||||
connect(this, &BuildOrderResolver::resolvingFinished, this, &BuildOrderResolver::printResults);
|
||||
connect(this, &BuildOrderResolver::resolvingFailed, static_cast<void(*)(const QString &)>(Utilities::printError));
|
||||
findDependencies();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Finds the dependencies of all packages.
|
||||
*
|
||||
* Also internally called after requested packages are available.
|
||||
*/
|
||||
void BuildOrderResolver::findDependencies()
|
||||
{
|
||||
// find specified packages and their dependencies
|
||||
for(int i = 0, size = m_tasks.size(); i != size; ++i) {
|
||||
addDependenciesToTask(m_tasks.at(i));
|
||||
}
|
||||
// request dependencies to be requested
|
||||
requestDependenciesToBeRequested();
|
||||
}
|
||||
|
||||
QStringList BuildOrderResolver::resultNames()
|
||||
{
|
||||
QStringList names;
|
||||
names.reserve(m_results.size());
|
||||
for(const auto *res : m_results) {
|
||||
names << res->name();
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
void BuildOrderResolver::printRelevantPackages()
|
||||
{
|
||||
cerr << shchar << "Relevant packages:";
|
||||
for(const auto *task : m_tasks) {
|
||||
cerr << ' ' << task->name().toLocal8Bit().data();
|
||||
}
|
||||
cerr << endl;
|
||||
}
|
||||
|
||||
void BuildOrderResolver::printResults()
|
||||
{
|
||||
if(useShSyntax) {
|
||||
Utilities::printBashArray(cout, "REPOINDEX_RESULTS", resultNames());
|
||||
} else {
|
||||
Utilities::printValues(cout, "Results", resultNames());
|
||||
}
|
||||
}
|
||||
|
||||
void BuildOrderResolver::resolve()
|
||||
{
|
||||
try {
|
||||
m_results.clear();
|
||||
m_results.reserve(m_tasks.size());
|
||||
TaskInfo::addAll(m_tasks, m_results);
|
||||
emit resolvingFinished();
|
||||
} catch(const TaskInfo &cyclic) {
|
||||
emit resolvingFailed(QStringLiteral("Can't resolve build order; the package ") % cyclic.name() % QStringLiteral(" is a cyclic dependency."));
|
||||
}
|
||||
}
|
||||
|
||||
void BuildOrderResolver::addRequestedPackages()
|
||||
{
|
||||
// find specified packages and their dependencies
|
||||
for(int i = 0, size = m_tasks.size(); i != size; ++i) {
|
||||
|
@ -271,6 +339,71 @@ void BuildOrderResolver::addRequestedPackages()
|
|||
requestDependenciesToBeRequested();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the resulting build order.
|
||||
* \remarks Build order must have been resolved yet.
|
||||
*/
|
||||
QStringList BuildOrderResolver::resultNames()
|
||||
{
|
||||
QStringList names;
|
||||
names.reserve(m_results.size());
|
||||
for(const auto *res : m_results) {
|
||||
names << res->name();
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Prints relevant packages.
|
||||
* \remarks All dependencies must have been found yet.
|
||||
*/
|
||||
void BuildOrderResolver::printRelevantPackages()
|
||||
{
|
||||
cerr << shchar << "Relevant packages:";
|
||||
for(const auto *task : m_tasks) {
|
||||
cerr << ' ' << task->name().toLocal8Bit().data();
|
||||
}
|
||||
cerr << endl;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Prints the resulting build order.
|
||||
* \remarks Build order must have been resolved yet.
|
||||
*/
|
||||
void BuildOrderResolver::printResults()
|
||||
{
|
||||
if(useShSyntax) {
|
||||
Utilities::printBashArray(cout, "REPOINDEX_RESULTS", resultNames());
|
||||
} else {
|
||||
Utilities::printValues(cout, "Results", resultNames());
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Resolves the build order.
|
||||
* \remarks All dependencies must have been found yet.
|
||||
*
|
||||
* Emits either resolvingFinished() or resolvingFailed() depending on whether
|
||||
* the operation succeeded.
|
||||
*/
|
||||
void BuildOrderResolver::resolve()
|
||||
{
|
||||
try {
|
||||
m_results.clear();
|
||||
m_results.reserve(m_tasks.size());
|
||||
TaskInfo::addAll(m_tasks, m_results);
|
||||
m_hasFinished = true;
|
||||
emit resolvingFinished();
|
||||
} catch(const TaskInfo &cyclic) {
|
||||
addError(QStringLiteral("Can't resolve build order; the package ") % cyclic.name() % QStringLiteral(" is a cyclic dependency."));
|
||||
m_hasFinished = true;
|
||||
emit resolvingFailed(m_errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the package for the specified \a dependency or nullptr if no package could be found.
|
||||
*/
|
||||
Package *BuildOrderResolver::findPackageForDependency(const Dependency &dependency)
|
||||
{
|
||||
Package *pkg;
|
||||
|
@ -283,7 +416,7 @@ Package *BuildOrderResolver::findPackageForDependency(const Dependency &dependen
|
|||
}
|
||||
|
||||
/*!
|
||||
* \brief Finds the package for the specified \a task and then adds dependencies.
|
||||
* \brief Finds the package for the specified \a task and then adds dependencies recursively.
|
||||
*/
|
||||
bool BuildOrderResolver::addDependenciesToTask(TaskInfo *task)
|
||||
{
|
||||
|
@ -294,17 +427,31 @@ bool BuildOrderResolver::addDependenciesToTask(TaskInfo *task)
|
|||
}
|
||||
Dependency dep(task->name(), QString());
|
||||
if(const auto pkg = findPackageForDependency(dep)) {
|
||||
task->associatePackage(pkg);
|
||||
if(m_addSourceOnlyDeps && pkg->repository()->isSourceOnly()) {
|
||||
task->setIsOnlyDependency(false);
|
||||
task->addRequiredFor(dep);
|
||||
if(task->associatePackage(pkg)) {
|
||||
if(m_addSourceOnlyDeps && pkg->repository()->isSourceOnly()) {
|
||||
task->setIsOnlyDependency(false);
|
||||
}
|
||||
// add dependencies to task
|
||||
if(!addDependenciesToTask(task, pkg->allDependencies())) {
|
||||
addError(QStringLiteral("Can not add dependencies of the dependency \"") % dep.toString() % QStringLiteral("\"."));
|
||||
m_hasFinished = true;
|
||||
emit resolvingFailed(errorMessage());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
addError(QStringLiteral("Can not associate the task \"") % task->name() % QStringLiteral("\" with the package required by dependency \"") % dep.toString() % QStringLiteral("\"."));
|
||||
m_hasFinished = true;
|
||||
emit resolvingFailed(errorMessage());
|
||||
return false;
|
||||
}
|
||||
// add dependencies to task
|
||||
addDependenciesToTask(task, pkg->dependencies());
|
||||
} else if(!task->isPackageRequested()) {
|
||||
task->setPackageRequested();
|
||||
m_dependenciesToBeRequested << dep;
|
||||
} else {
|
||||
emit resolvingFailed(QStringLiteral("The specified package \"") % task->name() % QStringLiteral("\" could not be found."));
|
||||
addError(QStringLiteral("The specified package \"") % task->name() % QStringLiteral("\" could not be found."));
|
||||
m_hasFinished = true;
|
||||
emit resolvingFailed(errorMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -312,38 +459,53 @@ bool BuildOrderResolver::addDependenciesToTask(TaskInfo *task)
|
|||
|
||||
/*!
|
||||
* \brief Finds packages for the the specified \a dependencies and adds them to the specified \a task.
|
||||
* \remarks Adds dependencies of the dependencies recursively.
|
||||
*/
|
||||
bool BuildOrderResolver::addDependenciesToTask(TaskInfo *task, const QList<Dependency> &dependencies)
|
||||
bool BuildOrderResolver::addDependenciesToTask(TaskInfo *task, const QList<const QList<Dependency> *> &dependencies)
|
||||
{
|
||||
for(auto &dep : dependencies) {
|
||||
const auto depPkg = findPackageForDependency(dep);
|
||||
const QString taskName = depPkg ? depPkg->name() : dep.name;
|
||||
auto *depTask = TaskInfo::find(m_tasks, taskName);
|
||||
if(depTask) {
|
||||
// we've already added a task for this dependency
|
||||
// -> add dependency task to the dependencies of "parent" task
|
||||
task->addDep(depTask);
|
||||
} else {
|
||||
// create new task
|
||||
m_tasks << (depTask = new TaskInfo(taskName, true));
|
||||
// adds dependency task to the dependencies of "parent" task
|
||||
task->addDep(depTask);
|
||||
if(depPkg) {
|
||||
depTask->associatePackage(depPkg);
|
||||
if(m_addSourceOnlyDeps && depPkg->repository()->isSourceOnly()) {
|
||||
depTask->setIsOnlyDependency(false);
|
||||
}
|
||||
// add dependencies of the dependency
|
||||
addDependenciesToTask(depTask, depPkg->dependencies());
|
||||
}
|
||||
}
|
||||
if(!depPkg) {
|
||||
if(!depTask->isPackageRequested()) {
|
||||
depTask->setPackageRequested();
|
||||
m_dependenciesToBeRequested << dep;
|
||||
for(auto *deps : dependencies) {
|
||||
for(auto &dep : *deps) {
|
||||
auto *depPkg = findPackageForDependency(dep);
|
||||
const QString taskName = depPkg ? depPkg->name() : dep.name;
|
||||
auto *depTask = TaskInfo::find(m_tasks, dep.name);
|
||||
bool newTask;
|
||||
if(depTask) {
|
||||
// we've already added a task for this dependency
|
||||
newTask = false;
|
||||
} else {
|
||||
emit resolvingFailed(QStringLiteral("The specified package \"") % task->name() % QStringLiteral("\" could not be found."));
|
||||
return false;
|
||||
// create new task
|
||||
m_tasks << (depTask = new TaskInfo(taskName, true));
|
||||
newTask = true;
|
||||
}
|
||||
// add dependency task to the dependencies of "parent" task
|
||||
task->addDep(depTask);
|
||||
depTask->addRequiredFor(dep);
|
||||
if(depPkg) {
|
||||
if(depTask->associatePackage(depPkg)) {
|
||||
if(m_addSourceOnlyDeps && depPkg->repository() && depPkg->repository()->isSourceOnly()) {
|
||||
depTask->setIsOnlyDependency(false);
|
||||
}
|
||||
if(newTask) {
|
||||
// add dependencies of the dependency
|
||||
if(!addDependenciesToTask(depTask, depPkg->allDependencies())) {
|
||||
addError(QStringLiteral("Can not add dependencies of the dependency \"") % dep.toString() % QStringLiteral("\"."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addError(QStringLiteral("Can not associate the task \"") % depTask->name() % QStringLiteral("\" with the package required by dependency \"") % dep.toString() % QStringLiteral("\"."));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if(!m_dependenciesToBeRequested.contains(dep)) {
|
||||
if(!depTask->isPackageRequested()) {
|
||||
depTask->setPackageRequested();
|
||||
m_dependenciesToBeRequested << dep;
|
||||
} else {
|
||||
addError(QStringLiteral("The specified package \"") % dep.toString() % QStringLiteral("\" could not be found."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -375,10 +537,10 @@ void BuildOrderResolver::requestDependenciesToBeRequested()
|
|||
// add results
|
||||
if(m_finder->areAllResultsAvailable()) {
|
||||
// results are immediately available (already cached)
|
||||
addRequestedPackages();
|
||||
findDependencies();
|
||||
} else {
|
||||
// need to request actually
|
||||
connect(m_finder.get(), &PackageFinder::resultsAvailable, this, &BuildOrderResolver::addRequestedPackages);
|
||||
connect(m_finder.get(), &PackageFinder::resultsAvailable, this, &BuildOrderResolver::findDependencies);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -387,5 +549,50 @@ void BuildOrderResolver::requestDependenciesToBeRequested()
|
|||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \class BuildOrderResolverCli
|
||||
* \brief The BuildOrderResolverCli class resolves the build order of the specified packages and prints the results stdout.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Creates a new BuildOrderResolverCli for the specified \a packages using the specified \a manager.
|
||||
*/
|
||||
BuildOrderResolverCli::BuildOrderResolverCli(Manager &manager, const StringVector &packages, bool addSourceOnlyDeps) :
|
||||
BuildOrderResolver(manager, addSourceOnlyDeps)
|
||||
{
|
||||
cerr << shchar << "Getting package information ..." << endl;
|
||||
tasks().clear();
|
||||
tasks().reserve(packages.size() * 2);
|
||||
// add a task for each specified package
|
||||
for(const auto &pkgName : packages) {
|
||||
tasks() << new TaskInfo(QString::fromLocal8Bit(pkgName.data()));
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Determines dependencies, prints relevant packages, resolves build order, prints results/errors.
|
||||
* \remarks Does not return until everything is done.
|
||||
*/
|
||||
int BuildOrderResolverCli::exec()
|
||||
{
|
||||
if(manager().config().isVerbose()) {
|
||||
connect(this, &BuildOrderResolver::ready, this, &BuildOrderResolver::printRelevantPackages);
|
||||
}
|
||||
connect(this, &BuildOrderResolver::ready, this, &BuildOrderResolver::resolve);
|
||||
connect(this, &BuildOrderResolver::resolvingFinished, this, &BuildOrderResolver::printResults);
|
||||
connect(this, &BuildOrderResolver::resolvingFailed, static_cast<void(*)(const QString &)>(Utilities::printError));
|
||||
QEventLoop loop;
|
||||
connect(this, &BuildOrderResolver::resolvingFinished, &loop, &QEventLoop::quit);
|
||||
connect(this, &BuildOrderResolver::resolvingFailed, &loop, &QEventLoop::quit);
|
||||
findDependencies();
|
||||
if(hasFinished()) {
|
||||
// resolving might have been finished immidiately
|
||||
return errorMessage().isEmpty();
|
||||
} else {
|
||||
// resolving not finished yet (deps must be requested)
|
||||
return loop.exec();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace PackageManagement
|
||||
|
||||
|
|
|
@ -21,15 +21,14 @@ class BuildOrderResolver : public QObject
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BuildOrderResolver(Manager &manager, const ApplicationUtilities::StringVector &packages, bool addSourceOnlyDeps = false);
|
||||
~BuildOrderResolver();
|
||||
|
||||
void cli();
|
||||
|
||||
void findDependencies();
|
||||
QStringList resultNames();
|
||||
bool hasFinished() const;
|
||||
const QString &errorMessage() const;
|
||||
|
||||
public slots:
|
||||
void findDependencies();
|
||||
void printRelevantPackages();
|
||||
void printResults();
|
||||
void resolve();
|
||||
|
@ -39,13 +38,16 @@ signals:
|
|||
void resolvingFinished();
|
||||
void resolvingFailed(const QString &message);
|
||||
|
||||
private slots:
|
||||
void addRequestedPackages();
|
||||
protected:
|
||||
BuildOrderResolver(Manager &manager, bool addSourceOnlyDeps = false);
|
||||
Manager &manager();
|
||||
QList<TaskInfo *> &tasks();
|
||||
|
||||
private:
|
||||
void addError(const QString &errorMessage);
|
||||
Package *findPackageForDependency(const Dependency &dependency);
|
||||
bool addDependenciesToTask(TaskInfo *task);
|
||||
bool addDependenciesToTask(TaskInfo *task, const QList<Dependency> &dependencies);
|
||||
bool addDependenciesToTask(TaskInfo *task, const QList<const QList<Dependency> *> &dependencies);
|
||||
void requestDependenciesToBeRequested();
|
||||
|
||||
Manager &m_manager;
|
||||
|
@ -54,6 +56,49 @@ private:
|
|||
std::unique_ptr<PackageFinder> m_finder;
|
||||
QList<TaskInfo *> m_results;
|
||||
bool m_addSourceOnlyDeps;
|
||||
bool m_hasFinished;
|
||||
QString m_errorMessage;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Returns whether adding dependencies/resolving the build order has been finished.
|
||||
* \remarks Also true when an error occured. In this case errorMessage() is not empty.
|
||||
*/
|
||||
inline bool BuildOrderResolver::hasFinished() const
|
||||
{
|
||||
return m_hasFinished;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the error message if an error occured.
|
||||
*/
|
||||
inline const QString &BuildOrderResolver::errorMessage() const
|
||||
{
|
||||
return m_errorMessage;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the manager used to find packages.
|
||||
*/
|
||||
inline Manager &BuildOrderResolver::manager()
|
||||
{
|
||||
return m_manager;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the tasks.
|
||||
*/
|
||||
inline QList<TaskInfo *> &BuildOrderResolver::tasks()
|
||||
{
|
||||
return m_tasks;
|
||||
}
|
||||
|
||||
class BuildOrderResolverCli : public BuildOrderResolver
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BuildOrderResolverCli(Manager &manager, const ApplicationUtilities::StringVector &packages, bool addSourceOnlyDeps = false);
|
||||
int exec();
|
||||
};
|
||||
|
||||
} // namespace PackageManagement
|
||||
|
|
8
main.cpp
8
main.cpp
|
@ -77,12 +77,8 @@ int main(int argc, char *argv[])
|
|||
// run Qt loop
|
||||
return application.exec();
|
||||
} else if(configArgs.buildOrderArg.isPresent()) {
|
||||
BuildOrderResolver resolver(manager, configArgs.buildOrderArg.values(), configArgs.addSourceOnlyDeps.isPresent());
|
||||
resolver.cli();
|
||||
QObject::connect(&resolver, &BuildOrderResolver::resolvingFinished, &application, &QCoreApplication::quit);
|
||||
QObject::connect(&resolver, &BuildOrderResolver::resolvingFailed, &application, &QCoreApplication::quit);
|
||||
// run Qt loop
|
||||
return application.exec();
|
||||
BuildOrderResolverCli resolver(manager, configArgs.buildOrderArg.values(), configArgs.addSourceOnlyDeps.isPresent());
|
||||
return resolver.exec();
|
||||
//BuildOrderResolver::printResults(resolver.resolve(configArgs.buildOrderArg.values()));
|
||||
} else if(configArgs.mingwBundleArg.isPresent()) {
|
||||
MingwBundle bundle(manager, configArgs.mingwBundleArg.values(), configArgs.iconThemesArg.values(), configArgs.extraPackagesArg.values());
|
||||
|
|
|
@ -4,7 +4,7 @@ appname = "Repository Index"
|
|||
appauthor = Martchus
|
||||
appurl = "https://github.com/$${appauthor}/$${projectname}"
|
||||
QMAKE_TARGET_DESCRIPTION = "Provides a web interface to browse Arch Linux package repositories."
|
||||
VERSION = 1.0.0
|
||||
VERSION = 0.0.1
|
||||
|
||||
# include ../../common.pri when building as part of a subdirs project; otherwise include general.pri
|
||||
!include(../../common.pri) {
|
||||
|
|
|
@ -50,7 +50,7 @@ span.glyphicon {
|
|||
background-color: #f9f9db!important;
|
||||
cursor: pointer;
|
||||
}
|
||||
#info_container th, #info_container td {
|
||||
.info-container th, .info-container td {
|
||||
border: none!important;
|
||||
font-size: 90%;
|
||||
padding: 5px;
|
||||
|
@ -95,10 +95,10 @@ span.glyphicon {
|
|||
/*
|
||||
* Package info
|
||||
*/
|
||||
#info_container {
|
||||
.info-container {
|
||||
margin-left: 5px;
|
||||
}
|
||||
#info_container {
|
||||
.info-container {
|
||||
float: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -110,6 +110,13 @@ span.glyphicon {
|
|||
.file-tree ul ul {
|
||||
margin-left: 5px;
|
||||
}
|
||||
#multi_package_info_container > div {
|
||||
float: left;
|
||||
width: 350px;
|
||||
}
|
||||
#packages_info {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tree
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
<li id="nav_packages" class="active"><a href="#packages">Packages <span class="sr-only">(current)</span></a></li>
|
||||
<li id="nav_groups"><a href="#groups">Groups</a></li>
|
||||
<li id="nav_repositories"><a href="#repositories">Repositories</a></li>
|
||||
<li id="nav_settings"><a id="link_settings" href="#" aria-label="Settings" data-toggle="popover" data-placement="bottom" title="Settings" data-content="TODO"><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span></a></li>
|
||||
<li id="nav_settings"><a id="link_settings" href="#" onclick="return false;" aria-label="Settings" data-toggle="popover" data-placement="bottom" title="Settings" data-content="TODO"><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span></a></li>
|
||||
<li id="nav_about"><a id="link_settings" href="#" onclick="$('#dlg_about').modal('show'); return false;"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span></a></li>
|
||||
</ul>
|
||||
<form class="navbar-form navbar-left" target="#" onsubmit="repoindex.pageManager.applySearchTerm(this.searchtermInput.value, this.searchtermExact.checked, true); return false;">
|
||||
|
@ -112,6 +112,7 @@
|
|||
</div>
|
||||
<!-- begin of packages page -->
|
||||
<div id="page_packages" class="row">
|
||||
<div id="multi_package_info_container"><!-- package info panel generated by JavaScript --></div>
|
||||
<!-- left column -->
|
||||
<div id="left_column_container_packages">
|
||||
<!-- package list -->
|
||||
|
@ -138,25 +139,8 @@
|
|||
</div>
|
||||
<!-- right column -->
|
||||
<div id="right_column_container_packages">
|
||||
<!-- package info -->
|
||||
<div id="info_container" class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
Package info
|
||||
<button type="button" class="close" onclick="repoindex.pageManager.hidePackageInfo();" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<!-- package info table (stub) -->
|
||||
<table class="table">
|
||||
<colgroup>
|
||||
<col style="width:30%">
|
||||
<col style="width:70%">
|
||||
</colgroup>
|
||||
<tbody id="pkg_tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- package info container (right column) -->
|
||||
<div id="single_package_info_container"><!-- package info panel generated by JavaScript --></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- begin of groups page -->
|
||||
|
@ -195,24 +179,8 @@
|
|||
<nav class="pagination-nav"><ul class="pagination pagination-sm" id="repos_pagination"><!-- generated by JavaScript --></ul></nav>
|
||||
</div>
|
||||
<div id="right_column_container_repos">
|
||||
<div id="info_container" class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
Repository info
|
||||
<button type="button" class="close" onclick="repoindex.pageManager.hideRepoInfo();" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<!-- repo info table (stub) -->
|
||||
<table class="table">
|
||||
<colgroup>
|
||||
<col style="width:30%">
|
||||
<col style="width:70%">
|
||||
</colgroup>
|
||||
<tbody id="repo_tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- repo info container (right column) -->
|
||||
<div id="single_repo_info_container"><!-- repo info panel generated by JavaScript --></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -261,7 +229,7 @@
|
|||
</div>
|
||||
<div class="modal-body" style="text-align: center;">
|
||||
<h2>Repository Index</h2>
|
||||
<p>1.0.0</p>
|
||||
<p>@META_APP_VERSION_STR@</p>
|
||||
<p style="margin: 25px 0px;">
|
||||
<em>Tool to browse Arch Linux repositories.</em><br>
|
||||
<a href="https://github.com/Martchus/" onclick="window.open(this.href); return false;">Project website</a>
|
||||
|
|
|
@ -120,12 +120,16 @@
|
|||
// check whether specified entry index is valid
|
||||
var entry = this.entryByIndex(entryIndex);
|
||||
if(entry) {
|
||||
// show properties in "Package info" box
|
||||
// show properties
|
||||
var setProperties = function() {
|
||||
// -> find info container and make info table
|
||||
var infoContainer = document.getElementById("single_package_info_container");
|
||||
infoContainer.wipeChildren();
|
||||
var infoTable = repoindex.makeInfoTable();
|
||||
var tb = infoTable.tbodyElement;
|
||||
|
||||
// -> basic package info
|
||||
var basics = entry.info.basics ? entry.info.basics : {};
|
||||
var tb = document.getElementById("pkg_tbody");
|
||||
tb.wipeChildren();
|
||||
repoindex.addPackageNames(tb, "Name", [entry.name]);
|
||||
switch(entry.info.repo) {
|
||||
case "AUR":
|
||||
|
@ -197,6 +201,11 @@
|
|||
downloadElement.appendChild(spanElement);
|
||||
}
|
||||
}
|
||||
|
||||
// -> make info panel
|
||||
var infoPanel = repoindex.makeInfoPanel("Package info", repoindex.bind(repoindex.pageManager, repoindex.pageManager.hidePackageInfo));
|
||||
infoPanel.bodyElement.appendChild(infoTable.tableElement);
|
||||
infoContainer.appendChild(infoPanel.element);
|
||||
};
|
||||
setProperties();
|
||||
if(!entry.info.basics || !entry.info.details) {
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
this.rightColumnContainerPackages = document.getElementById("right_column_container_packages");
|
||||
this.leftColumnContainerRepos = document.getElementById("left_column_container_repos");
|
||||
this.rightColumnContainerRepos = document.getElementById("right_column_container_repos");
|
||||
this.packageInfoContainer = document.getElementById("container_package_info");
|
||||
|
||||
// provide a function to add an error message (shown in red box)
|
||||
this.addError = function(msg) {
|
||||
|
@ -281,14 +282,14 @@
|
|||
this.setPage(repoindex.Pages.Packages);
|
||||
// ensure right column is visible
|
||||
this.showRightColumn(this.leftColumnContainerPackages, this.rightColumnContainerPackages);
|
||||
var tb = document.getElementById("pkg_tbody");
|
||||
if(tb.lastChild) {
|
||||
if(scroll && !checkVisibility(tb.lastChild)) {
|
||||
$('html, body').animate({
|
||||
scrollTop: $("#" + this.rightColumnContainerPackages.id).offset().top
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
//var tb = document.getElementById("pkg_tbody");
|
||||
//if(tb.lastChild) {
|
||||
// if(scroll && !checkVisibility(tb.lastChild)) {
|
||||
// $('html, body').animate({
|
||||
// scrollTop: $("#" + this.rightColumnContainerPackages.id).offset().top
|
||||
// }, 500);
|
||||
// }
|
||||
//}
|
||||
};
|
||||
|
||||
this.hideRepoInfo = function() {
|
||||
|
|
|
@ -180,9 +180,13 @@
|
|||
this.showRepoInfoForIndex = function(entryIndex) {
|
||||
var entry = this.entryByIndex(entryIndex);
|
||||
if(entry) {
|
||||
// -> find info container and make info table
|
||||
var infoContainer = document.getElementById("single_repo_info_container");
|
||||
infoContainer.wipeChildren();
|
||||
var infoTable = repoindex.makeInfoTable();
|
||||
var tb = infoTable.tbodyElement;
|
||||
|
||||
var info = entry.info;
|
||||
var tb = document.getElementById("repo_tbody");
|
||||
tb.wipeChildren();
|
||||
repoindex.addField(tb, "Name", repoindex.makeStr(entry.name));
|
||||
repoindex.addField(tb, "Description", repoindex.makeStr(info.desc));
|
||||
repoindex.addField(tb, "Package count", repoindex.makeStr(info.packageCount));
|
||||
|
@ -199,6 +203,11 @@
|
|||
this.currentInfo = info;
|
||||
// ensures, that the "Package Info" box (with the properties just set) is shown
|
||||
repoindex.pageManager.showRepoInfo();
|
||||
|
||||
// -> make info panel
|
||||
var infoPanel = repoindex.makeInfoPanel("Repository info", repoindex.bind(repoindex.pageManager, repoindex.pageManager.hideRepoInfo));
|
||||
infoPanel.bodyElement.appendChild(infoTable.tableElement);
|
||||
infoContainer.appendChild(infoPanel.element);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -301,6 +301,67 @@
|
|||
repoindex.setPackageNames(repoindex.addField(element, fieldName), packageNames, pageName);
|
||||
};
|
||||
|
||||
repoindex.makeInfoPanel = function(headingText, closeFunc) {
|
||||
var panelElement = document.createElement("div");
|
||||
panelElement.className = "panel panel-default info-container";
|
||||
var panelHeadingElement = document.createElement("div");
|
||||
panelHeadingElement.className = "panel-heading";
|
||||
panelHeadingElement.appendChild(document.createTextNode(headingText));
|
||||
var closeButton = document.createElement("button");
|
||||
closeButton.setAttribute("type", "button");
|
||||
closeButton.setAttribute("class", "close");
|
||||
closeButton.setAttribute("aria-label", "Close");
|
||||
closeButton.onclick = function() {
|
||||
if(panelElement.parentNode) {
|
||||
panelElement.parentNode.removeChild(panelElement);
|
||||
if(closeFunc) {
|
||||
closeFunc();
|
||||
}
|
||||
}
|
||||
};
|
||||
var closeButtonSpan = document.createElement("span");
|
||||
closeButtonSpan.setAttribute("aria-hidden", "true");
|
||||
closeButtonSpan.appendChild(document.createTextNode("\u00D7"));
|
||||
closeButton.appendChild(closeButtonSpan);
|
||||
panelHeadingElement.appendChild(closeButton);
|
||||
panelElement.appendChild(panelHeadingElement);
|
||||
var panelBodyElement = document.createElement("div");
|
||||
panelBodyElement.className = "panel-body";
|
||||
panelElement.appendChild(panelBodyElement);
|
||||
return {
|
||||
element: panelElement,
|
||||
headingElement: panelHeadingElement,
|
||||
bodyElement: panelBodyElement
|
||||
};
|
||||
};
|
||||
|
||||
repoindex.makeColumnGroup = function(columnWidths) {
|
||||
var colgroupElement = document.createElement("colgroup");
|
||||
for(var i = 0; i < columnWidths.length; ++i) {
|
||||
var colElement = document.createElement("col");
|
||||
colElement.setAttribute("style", "width:" + columnWidths[i]);
|
||||
colgroupElement.appendChild(colElement);
|
||||
}
|
||||
return colgroupElement;
|
||||
};
|
||||
|
||||
repoindex.makeInfoTable = function() {
|
||||
var tableElement = document.createElement("table");
|
||||
tableElement.appendChild(repoindex.makeColumnGroup(["30%", "70%"]));
|
||||
var tbodyElement = document.createElement("tbody");
|
||||
tableElement.appendChild(tbodyElement);
|
||||
return {
|
||||
tableElement: tableElement,
|
||||
tbodyElement: tbodyElement
|
||||
};
|
||||
};
|
||||
|
||||
repoindex.bind = function(obj, func, args) {
|
||||
return function() {
|
||||
func.call(obj, args);
|
||||
}
|
||||
}
|
||||
|
||||
return repoindex;
|
||||
|
||||
})(repoindex || {});
|
||||
|
|
Loading…
Reference in New Issue