2015-09-07 19:36:45 +02:00
|
|
|
#include "./resolvebuildorder.h"
|
2015-08-19 02:13:28 +02:00
|
|
|
|
2015-12-25 01:58:19 +01:00
|
|
|
#include "./packagefinder.h"
|
2015-09-07 19:36:45 +02:00
|
|
|
#include "./manager.h"
|
|
|
|
#include "./config.h"
|
|
|
|
#include "./utilities.h"
|
2015-12-25 01:58:19 +01:00
|
|
|
#include "./repository.h"
|
2015-08-19 02:13:28 +02:00
|
|
|
|
|
|
|
#include <c++utilities/misc/memory.h>
|
|
|
|
|
2015-12-25 01:58:19 +01:00
|
|
|
#include <QStringBuilder>
|
|
|
|
|
2015-08-19 02:13:28 +02:00
|
|
|
#include <iostream>
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace ApplicationUtilities;
|
|
|
|
|
2015-09-05 17:25:05 +02:00
|
|
|
namespace RepoIndex {
|
2015-08-19 02:13:28 +02:00
|
|
|
|
|
|
|
class TaskInfo
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
TaskInfo(QString name, bool onlyDependency = false, const QList<TaskInfo *> &deps = QList<TaskInfo *>());
|
|
|
|
|
|
|
|
const QString &name() const;
|
2015-09-05 17:25:05 +02:00
|
|
|
void setName(const QString &name);
|
2015-08-19 02:13:28 +02:00
|
|
|
const QList<TaskInfo *> deps() const;
|
|
|
|
void addDep(TaskInfo *dep);
|
|
|
|
bool isDone() const;
|
|
|
|
bool isVisited() const;
|
|
|
|
bool isOnlyDependency() const;
|
2015-12-25 01:58:19 +01:00
|
|
|
void setIsOnlyDependency(bool isOnlyDependency);
|
2015-12-08 00:24:45 +01:00
|
|
|
bool isPackageRequested() const;
|
|
|
|
void setPackageRequested();
|
2015-08-19 02:13:28 +02:00
|
|
|
void add(QList<TaskInfo *> &results);
|
2015-12-25 01:58:19 +01:00
|
|
|
Package *associatedPackage() const;
|
|
|
|
void associatePackage(Package *package);
|
2015-08-19 02:13:28 +02:00
|
|
|
static void addAll(const QList<TaskInfo *> &tasks, QList<TaskInfo *> &results);
|
|
|
|
static TaskInfo *find(const QList<TaskInfo *> &tasks, const QString &name);
|
|
|
|
|
|
|
|
private:
|
|
|
|
QString m_name;
|
|
|
|
QList<TaskInfo *> m_deps;
|
|
|
|
bool m_done;
|
|
|
|
bool m_visited;
|
|
|
|
bool m_onlyDep;
|
2015-12-08 00:24:45 +01:00
|
|
|
bool m_pkgRequested;
|
2015-12-25 01:58:19 +01:00
|
|
|
Package *m_associatedPackage;
|
2015-08-19 02:13:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
inline TaskInfo::TaskInfo(QString name, bool onlyDependency, const QList<TaskInfo *> &deps) :
|
|
|
|
m_name(name),
|
|
|
|
m_deps(deps),
|
|
|
|
m_done(false),
|
|
|
|
m_visited(false),
|
2015-12-08 00:24:45 +01:00
|
|
|
m_onlyDep(onlyDependency),
|
2015-12-25 01:58:19 +01:00
|
|
|
m_pkgRequested(false),
|
|
|
|
m_associatedPackage(nullptr)
|
2015-08-19 02:13:28 +02:00
|
|
|
{}
|
|
|
|
|
|
|
|
inline const QString &TaskInfo::name() const
|
|
|
|
{
|
|
|
|
return m_name;
|
|
|
|
}
|
|
|
|
|
2015-09-05 17:25:05 +02:00
|
|
|
inline void TaskInfo::setName(const QString &name)
|
|
|
|
{
|
|
|
|
m_name = name;
|
|
|
|
}
|
|
|
|
|
2015-08-19 02:13:28 +02:00
|
|
|
inline const QList<TaskInfo *> TaskInfo::deps() const
|
|
|
|
{
|
|
|
|
return m_deps;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void TaskInfo::addDep(TaskInfo *dep)
|
|
|
|
{
|
|
|
|
m_deps << dep;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool TaskInfo::isDone() const
|
|
|
|
{
|
|
|
|
return m_done;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool TaskInfo::isVisited() const
|
|
|
|
{
|
|
|
|
return m_visited;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool TaskInfo::isOnlyDependency() const
|
|
|
|
{
|
|
|
|
return m_onlyDep;
|
|
|
|
}
|
|
|
|
|
2015-12-25 01:58:19 +01:00
|
|
|
inline void TaskInfo::setIsOnlyDependency(bool isOnlyDependency)
|
|
|
|
{
|
|
|
|
m_onlyDep = isOnlyDependency;
|
|
|
|
}
|
|
|
|
|
2015-12-08 00:24:45 +01:00
|
|
|
inline bool TaskInfo::isPackageRequested() const
|
|
|
|
{
|
|
|
|
return m_pkgRequested;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void TaskInfo::setPackageRequested()
|
|
|
|
{
|
|
|
|
m_pkgRequested = true;
|
|
|
|
}
|
|
|
|
|
2015-08-19 02:13:28 +02:00
|
|
|
void TaskInfo::add(QList<TaskInfo *> &results)
|
|
|
|
{
|
2015-09-05 17:25:05 +02:00
|
|
|
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;
|
|
|
|
}
|
2015-08-19 02:13:28 +02:00
|
|
|
} else {
|
|
|
|
m_visited = true;
|
|
|
|
}
|
2015-09-05 17:25:05 +02:00
|
|
|
for(auto *dep : deps()) {
|
2015-08-19 02:13:28 +02:00
|
|
|
dep->add(results);
|
|
|
|
}
|
|
|
|
m_done = true;
|
2015-09-05 17:25:05 +02:00
|
|
|
if(!isOnlyDependency()) {
|
|
|
|
results << this;
|
|
|
|
}
|
2015-08-19 02:13:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-25 01:58:19 +01:00
|
|
|
inline Package *TaskInfo::associatedPackage() const
|
|
|
|
{
|
|
|
|
return m_associatedPackage;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TaskInfo::associatePackage(Package *package)
|
|
|
|
{
|
|
|
|
m_associatedPackage = package;
|
|
|
|
// update the name to ensure we have the acutal package name and not just a "provides" name
|
|
|
|
m_name = package->name();
|
|
|
|
}
|
|
|
|
|
2015-08-19 02:13:28 +02:00
|
|
|
void TaskInfo::addAll(const QList<TaskInfo *> &tasks, QList<TaskInfo *> &results)
|
|
|
|
{
|
|
|
|
for(auto *task : tasks) {
|
2015-09-05 17:25:05 +02:00
|
|
|
task->add(results);
|
2015-08-19 02:13:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TaskInfo *TaskInfo::find(const QList<TaskInfo *> &tasks, const QString &name)
|
|
|
|
{
|
|
|
|
for(auto *task : tasks) {
|
|
|
|
if(task->name() == name) {
|
|
|
|
return task;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<class ListType>
|
|
|
|
class DestroyList
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
DestroyList(ListType &list) :
|
|
|
|
m_list(list)
|
|
|
|
{}
|
|
|
|
|
|
|
|
~DestroyList()
|
|
|
|
{
|
|
|
|
qDeleteAll(m_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
ListType &m_list;
|
|
|
|
};
|
|
|
|
|
2015-12-25 01:58:19 +01:00
|
|
|
BuildOrderResolver::BuildOrderResolver(Manager &manager, const StringVector &packages, bool addSourceOnlyDeps) :
|
|
|
|
m_manager(manager),
|
|
|
|
m_finder(nullptr),
|
|
|
|
m_addSourceOnlyDeps(addSourceOnlyDeps)
|
2015-08-19 02:13:28 +02:00
|
|
|
{
|
2015-09-05 17:25:05 +02:00
|
|
|
cerr << shchar << "Getting package information ..." << endl;
|
2015-12-08 00:24:45 +01:00
|
|
|
m_tasks.clear();
|
|
|
|
m_tasks.reserve(packages.size() * 2);
|
2015-12-25 01:58:19 +01:00
|
|
|
// add a task for each specified package
|
|
|
|
for(const auto &pkgName : packages) {
|
|
|
|
m_tasks << new TaskInfo(QString::fromLocal8Bit(pkgName.data()));
|
|
|
|
}
|
|
|
|
}
|
2015-12-08 00:24:45 +01:00
|
|
|
|
2015-12-25 01:58:19 +01:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
2015-08-19 02:13:28 +02:00
|
|
|
}
|
2015-12-25 01:58:19 +01:00
|
|
|
cerr << endl;
|
2015-08-19 02:13:28 +02:00
|
|
|
}
|
|
|
|
|
2015-12-25 01:58:19 +01:00
|
|
|
void BuildOrderResolver::printResults()
|
2015-09-07 19:36:45 +02:00
|
|
|
{
|
|
|
|
if(useShSyntax) {
|
2015-12-25 01:58:19 +01:00
|
|
|
Utilities::printBashArray(cout, "REPOINDEX_RESULTS", resultNames());
|
2015-09-07 19:36:45 +02:00
|
|
|
} else {
|
2015-12-25 01:58:19 +01:00
|
|
|
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) {
|
|
|
|
if(!addDependenciesToTask(m_tasks.at(i))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// request dependencies to be requested
|
|
|
|
requestDependenciesToBeRequested();
|
|
|
|
}
|
|
|
|
|
|
|
|
Package *BuildOrderResolver::findPackageForDependency(const Dependency &dependency)
|
|
|
|
{
|
|
|
|
Package *pkg;
|
|
|
|
if((pkg = m_manager.packageProviding(dependency))) {
|
|
|
|
return pkg;
|
|
|
|
} else if(m_finder && (pkg = m_finder->packageProviding(dependency))) {
|
|
|
|
return pkg;
|
2015-09-07 19:36:45 +02:00
|
|
|
}
|
2015-12-25 01:58:19 +01:00
|
|
|
return nullptr;
|
2015-09-07 19:36:45 +02:00
|
|
|
}
|
|
|
|
|
2015-12-08 00:24:45 +01:00
|
|
|
/*!
|
|
|
|
* \brief Finds the package for the specified \a task and then adds dependencies.
|
|
|
|
*/
|
2015-12-25 01:58:19 +01:00
|
|
|
bool BuildOrderResolver::addDependenciesToTask(TaskInfo *task)
|
2015-08-19 02:13:28 +02:00
|
|
|
{
|
2015-12-25 01:58:19 +01:00
|
|
|
// check whether dependencies have already been added
|
|
|
|
if(task->associatedPackage()) {
|
|
|
|
// package associated -> dependencies already added
|
|
|
|
return true;
|
|
|
|
}
|
2015-12-08 00:24:45 +01:00
|
|
|
Dependency dep(task->name(), QString());
|
2015-12-25 01:58:19 +01:00
|
|
|
if(const auto pkg = findPackageForDependency(dep)) {
|
|
|
|
task->associatePackage(pkg);
|
|
|
|
if(m_addSourceOnlyDeps && pkg->repository()->isSourceOnly()) {
|
|
|
|
task->setIsOnlyDependency(false);
|
|
|
|
}
|
|
|
|
// add dependencies to task
|
|
|
|
addDependenciesToTask(task, pkg->dependencies());
|
2015-12-08 00:24:45 +01:00
|
|
|
} else if(!task->isPackageRequested()) {
|
|
|
|
task->setPackageRequested();
|
|
|
|
m_dependenciesToBeRequested << dep;
|
2015-08-19 02:13:28 +02:00
|
|
|
} else {
|
2015-12-25 01:58:19 +01:00
|
|
|
emit resolvingFailed(QStringLiteral("The specified package \"") % task->name() % QStringLiteral("\" could not be found."));
|
|
|
|
return false;
|
2015-08-19 02:13:28 +02:00
|
|
|
}
|
2015-12-25 01:58:19 +01:00
|
|
|
return true;
|
2015-08-19 02:13:28 +02:00
|
|
|
}
|
|
|
|
|
2015-12-08 00:24:45 +01:00
|
|
|
/*!
|
|
|
|
* \brief Finds packages for the the specified \a dependencies and adds them to the specified \a task.
|
|
|
|
*/
|
2015-12-25 01:58:19 +01:00
|
|
|
bool BuildOrderResolver::addDependenciesToTask(TaskInfo *task, const QList<Dependency> &dependencies)
|
2015-08-19 02:13:28 +02:00
|
|
|
{
|
2015-09-05 17:25:05 +02:00
|
|
|
for(auto &dep : dependencies) {
|
2015-12-25 01:58:19 +01:00
|
|
|
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);
|
|
|
|
}
|
2015-09-05 17:25:05 +02:00
|
|
|
// add dependencies of the dependency
|
2015-12-25 01:58:19 +01:00
|
|
|
addDependenciesToTask(depTask, depPkg->dependencies());
|
2015-09-05 17:25:05 +02:00
|
|
|
}
|
2015-12-25 01:58:19 +01:00
|
|
|
}
|
|
|
|
if(!depPkg) {
|
|
|
|
if(!depTask->isPackageRequested()) {
|
|
|
|
depTask->setPackageRequested();
|
|
|
|
m_dependenciesToBeRequested << dep;
|
|
|
|
} else {
|
|
|
|
emit resolvingFailed(QStringLiteral("The specified package \"") % task->name() % QStringLiteral("\" could not be found."));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Requests dependencies to be requested.
|
|
|
|
*/
|
|
|
|
void BuildOrderResolver::requestDependenciesToBeRequested()
|
|
|
|
{
|
|
|
|
// check whether there are dependencies to be requested
|
|
|
|
if(!m_dependenciesToBeRequested.isEmpty()) {
|
|
|
|
// print dependencies to be requested
|
|
|
|
if(m_manager.config().isVerbose()) {
|
|
|
|
cerr << shchar << "Dependencies to be requested:";
|
|
|
|
for(const Dependency &dep : m_dependenciesToBeRequested) {
|
|
|
|
cerr << ' ' << dep.name.toStdString();
|
|
|
|
}
|
|
|
|
cerr << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
// do requests (using package finder)
|
|
|
|
m_finder = make_unique<PackageFinder>(m_manager, m_dependenciesToBeRequested);
|
|
|
|
|
|
|
|
// all dependencies requested -> clear dependencies to be requested
|
|
|
|
m_dependenciesToBeRequested.clear();
|
|
|
|
|
|
|
|
// add results
|
|
|
|
if(m_finder->areAllResultsAvailable()) {
|
|
|
|
// results are immediately available (already cached)
|
|
|
|
addRequestedPackages();
|
2015-09-05 17:25:05 +02:00
|
|
|
} else {
|
2015-12-25 01:58:19 +01:00
|
|
|
// need to request actually
|
|
|
|
connect(m_finder.get(), &PackageFinder::resultsAvailable, this, &BuildOrderResolver::addRequestedPackages);
|
2015-09-05 17:25:05 +02:00
|
|
|
}
|
2015-12-25 01:58:19 +01:00
|
|
|
|
|
|
|
} else {
|
|
|
|
// there are no dependencies to be requested -> ready to resolve build order
|
|
|
|
emit ready();
|
2015-08-19 02:13:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace PackageManagement
|
|
|
|
|