Beginnings of the PackageKit dependency solver

Not functional (or tested) yet. The libsolv setup for a somewhat
simplified installation case should be more or less complete, though.
The solution conversion to to-be-created Haiku data structures and the
handling of problems is still missing, though.
This commit is contained in:
Ingo Weinhold 2013-04-01 00:25:37 +00:00
parent 10efbe6c5e
commit 479ca8169c
10 changed files with 1025 additions and 0 deletions

View File

@ -0,0 +1,43 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _PACKAGE__SOLVER_H_
#define _PACKAGE__SOLVER_H_
#include <SupportDefs.h>
namespace BPackageKit {
class BSolverPackageSpecifierList;
class BSolverRepository;
class BSolver {
public:
BSolver();
~BSolver();
status_t Init();
status_t AddRepository(BSolverRepository* repository);
status_t Install(
const BSolverPackageSpecifierList&
packages);
private:
class Implementation;
private:
Implementation* fImplementation;
};
} // namespace BPackageKit
#endif // _PACKAGE__SOLVER_H_

View File

@ -0,0 +1,47 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _PACKAGE__SOLVER_PACKAGE_SPECIFIER_H_
#define _PACKAGE__SOLVER_PACKAGE_SPECIFIER_H_
#include <package/PackageResolvableExpression.h>
namespace BPackageKit {
class BSolverRepository;
class BSolverPackageSpecifier {
public:
BSolverPackageSpecifier();
BSolverPackageSpecifier(
const BPackageResolvableExpression&
expression);
BSolverPackageSpecifier(
BSolverRepository* repository,
const BPackageResolvableExpression&
expression);
BSolverPackageSpecifier(
const BSolverPackageSpecifier& other);
~BSolverPackageSpecifier();
BSolverRepository* Repository() const;
const BPackageResolvableExpression& Expression() const;
BSolverPackageSpecifier& operator=(
const BSolverPackageSpecifier& other);
private:
BSolverRepository* fRepository;
BPackageResolvableExpression fExpression;
};
} // namespace BPackageKit
#endif // _PACKAGE__SOLVER_PACKAGE_SPECIFIER_H_

View File

@ -0,0 +1,46 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _PACKAGE__SOLVER_PACKAGE_SPECIFIER_LIST_H_
#define _PACKAGE__SOLVER_PACKAGE_SPECIFIER_LIST_H_
#include <SupportDefs.h>
namespace BPackageKit {
class BSolverPackageSpecifier;
class BSolverPackageSpecifierList {
public:
BSolverPackageSpecifierList();
BSolverPackageSpecifierList(
const BSolverPackageSpecifierList& other);
~BSolverPackageSpecifierList();
bool IsEmpty() const;
int32 CountSpecifiers() const;
const BSolverPackageSpecifier* SpecifierAt(int32 index) const;
bool AppendSpecifier(
const BSolverPackageSpecifier& specifier);
BSolverPackageSpecifierList& operator=(
const BSolverPackageSpecifierList& other);
private:
class Vector;
private:
Vector* fSpecifiers;
};
} // namespace BPackageKit
#endif // _PACKAGE__SOLVER_PACKAGE_SPECIFIER_LIST_H_

View File

@ -0,0 +1,69 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _PACKAGE__SOLVER_REPOSITORY_H_
#define _PACKAGE__SOLVER_REPOSITORY_H_
#include <package/PackageDefs.h>
#include <package/PackageInfoSet.h>
#include <String.h>
namespace BPackageKit {
class BPackageInfo;
class BRepositoryConfig;
class BSolverRepository {
public:
enum BAllInstallationLocations {
B_ALL_INSTALLATION_LOCATIONS
};
typedef BPackageInfoSet::Iterator Iterator;
public:
BSolverRepository();
BSolverRepository(const BString& name);
BSolverRepository(
BPackageInstallationLocation location);
BSolverRepository(BAllInstallationLocations);
BSolverRepository(
const BRepositoryConfig& config);
~BSolverRepository();
status_t SetTo(const BString& name);
status_t SetTo(BPackageInstallationLocation location);
status_t SetTo(BAllInstallationLocations);
status_t SetTo(const BRepositoryConfig& config);
void Unset();
status_t InitCheck();
bool IsInstalled() const;
BString Name() const;
uint8 Priority() const;
status_t AddPackage(const BPackageInfo& info);
status_t AddPackages(
BPackageInstallationLocation location);
Iterator GetIterator() const;
private:
BString fName;
uint8 fPriority;
bool fIsInstalled;
BPackageInfoSet fPackages;
};
} // namespace BPackageKit
#endif // _PACKAGE__SOLVER_REPOSITORY_H_

View File

@ -74,3 +74,5 @@ SharedLibrary libpackage.so
:
libshared.a be z $(TARGET_LIBSTDC++)
;
HaikuSubInclude solver ;

View File

@ -0,0 +1,23 @@
SubDir HAIKU_TOP src kits package solver ;
UsePrivateHeaders shared ;
# TODO: Add properly to BuildFeatures and remove here!
HAIKU_LIBSOLV_INSTALL_DIR ?= /Transfer/ports/libsolv-install/boot/common ;
HAIKU_LIBSOLV_HEADERS ?= $(HAIKU_LIBSOLV_INSTALL_DIR)/include ;
HAIKU_LIBSOLV_LIBS ?= $(HAIKU_LIBSOLV_INSTALL_DIR)/lib/libsolv.so
$(HAIKU_LIBSOLV_INSTALL_DIR)/lib/libsolvext.so ;
SubDirSysHdrs $(HAIKU_LIBSOLV_HEADERS) ;
SubDirHdrs $(HAIKU_LIBSOLV_HEADERS)/solv ;
SharedLibrary libpackage_solver.so
:
Solver.cpp
SolverPackageSpecifier.cpp
SolverPackageSpecifierList.cpp
SolverRepository.cpp
:
package $(HAIKU_LIBSOLV_LIBS) be $(TARGET_LIBSTDC++)
;

View File

@ -0,0 +1,356 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#include <package/solver/Solver.h>
#include <new>
#include <solv/pool.h>
#include <solv/poolarch.h>
#include <solv/repo.h>
#include <solv/repo_haiku.h>
#include <solv/selection.h>
#include <solv/solver.h>
#include <package/RepositoryCache.h>
#include <package/solver/SolverPackageSpecifier.h>
#include <package/solver/SolverPackageSpecifierList.h>
#include <package/solver/SolverRepository.h>
#include <ObjectList.h>
// TODO: libsolv doesn't have any helpful out-of-memory handling. It just just
// abort()s. Obviously that isn't good behavior for a library.
namespace BPackageKit {
// #pragma mark - BSolver::Implementation
class BSolver::Implementation {
public:
Implementation();
~Implementation();
status_t Init();
status_t AddRepository(BSolverRepository* repository);
status_t Install(
const BSolverPackageSpecifierList&
packages);
private:
struct SolvQueue;
struct RepositoryInfo;
typedef BObjectList<RepositoryInfo> RepositoryInfoList;
private:
status_t _AddRepositories();
RepositoryInfo* _GetRepositoryInfo(
BSolverRepository* repository) const;
private:
Pool* fPool;
RepositoryInfoList fRepositoryInfos;
RepositoryInfo* fInstalledRepository;
};
struct BSolver::Implementation::SolvQueue : Queue {
SolvQueue()
{
queue_init(this);
}
~SolvQueue()
{
queue_free(this);
}
};
struct BSolver::Implementation::RepositoryInfo {
RepositoryInfo(BSolverRepository* repository)
:
fRepository(repository),
fSolvRepo(NULL)
{
}
BSolverRepository* Repository() const
{
return fRepository;
}
Repo* SolvRepo()
{
return fSolvRepo;
}
void SetSolvRepo(Repo* repo)
{
fSolvRepo = repo;
}
private:
BSolverRepository* fRepository;
Repo* fSolvRepo;
};
// #pragma mark - BSolver
BSolver::BSolver()
:
fImplementation(new(std::nothrow) Implementation)
{
}
BSolver::~BSolver()
{
delete fImplementation;
}
status_t
BSolver::Init()
{
return fImplementation != NULL ? fImplementation->Init() : B_NO_MEMORY;
}
status_t
BSolver::AddRepository(BSolverRepository* repository)
{
return fImplementation != NULL
? fImplementation->AddRepository(repository) : B_NO_MEMORY;
}
status_t
BSolver::Install(const BSolverPackageSpecifierList& packages)
{
return fImplementation != NULL
? fImplementation->Install(packages) : B_NO_MEMORY;
}
// #pragma mark - BSolver::Implementation
BSolver::Implementation::Implementation()
:
fPool(NULL),
fRepositoryInfos(),
fInstalledRepository(NULL)
{
}
BSolver::Implementation::~Implementation()
{
if (fPool != NULL)
pool_free(fPool);
}
status_t
BSolver::Implementation::Init()
{
// already initialized?
if (fPool != NULL)
return B_BAD_VALUE;
fPool = pool_create();
// Set the system architecture. We use what uname() returns unless we're on
// x86 gcc2.
{
const char* arch;
#ifdef __HAIKU_ARCH_X86
#if (B_HAIKU_ABI & B_HAIKU_ABI_MAJOR) == B_HAIKU_ABI_GCC_2
arch = "x86_gcc2";
#else
arch = "x86";
#endif
#else
struct utsname info;
if (uname(&info) != 0)
return errno;
arch = info.machine;
#endif
pool_setarchpolicy(fPool, arch);
}
return B_OK;
}
status_t
BSolver::Implementation::AddRepository(BSolverRepository* repository)
{
if (repository == NULL || repository->InitCheck() != B_OK)
return B_BAD_VALUE;
// If the repository represents installed packages, check, if we already
// have such a repository.
if (repository->IsInstalled() && fInstalledRepository != NULL)
return B_BAD_VALUE;
// add the repository info
RepositoryInfo* info = new(std::nothrow) RepositoryInfo(repository);
if (info == NULL)
return B_NO_MEMORY;
if (!fRepositoryInfos.AddItem(info)) {
delete info;
return B_NO_MEMORY;
}
if (repository->IsInstalled())
fInstalledRepository = info;
return B_OK;
}
status_t
BSolver::Implementation::Install(const BSolverPackageSpecifierList& packages)
{
if (packages.IsEmpty())
return B_BAD_VALUE;
// TODO: Clean up first?
// add repositories to pool
status_t error = _AddRepositories();
if (error != B_OK)
return error;
// prepare pool for solving
pool_createwhatprovides(fPool);
// add the packages to install to the job queue
SolvQueue jobs;
int32 packageCount = packages.CountSpecifiers();
for (int32 i = 0; i < packageCount; i++) {
const BSolverPackageSpecifier& specifier = *packages.SpecifierAt(i);
// find matching packages
SolvQueue matchingPackages;
int flags = SELECTION_NAME | SELECTION_PROVIDES | SELECTION_GLOB
| SELECTION_CANON | SELECTION_DOTARCH | SELECTION_REL;
// TODO: All flags needed/useful?
/*int matchFlags =*/ selection_make(fPool, &matchingPackages,
specifier.Expression().Name(), flags);
// TODO: Don't just match the name, but also the version, if given!
if (matchingPackages.count == 0)
return B_NAME_NOT_FOUND;
// restrict to the matching repository
if (BSolverRepository* repository = specifier.Repository()) {
RepositoryInfo* repositoryInfo = _GetRepositoryInfo(repository);
if (repositoryInfo == NULL)
return B_BAD_VALUE;
SolvQueue repoFilter;
queue_push2(&repoFilter,
SOLVER_SOLVABLE_REPO/* | SOLVER_SETREPO | SOLVER_SETVENDOR*/,
repositoryInfo->SolvRepo()->repoid);
selection_filter(fPool, &matchingPackages, &repoFilter);
if (matchingPackages.count == 0)
return B_NAME_NOT_FOUND;
}
for (int j = 0; j < matchingPackages.count; j++)
queue_push(&jobs, matchingPackages.elements[j]);
}
// add solver mode to job queue elements
int solverMode = SOLVER_INSTALL;
for (int i = 0; i < jobs.count; i += 2) {
jobs.elements[i] |= solverMode;
// if (cleandeps)
// jobs.elements[i] |= SOLVER_CLEANDEPS;
// if (forcebest)
// jobs.elements[i] |= SOLVER_FORCEBEST;
}
// create the solver and solve
Solver* solver = solver_create(fPool);
solver_set_flag(solver, SOLVER_FLAG_SPLITPROVIDES, 1);
solver_set_flag(solver, SOLVER_FLAG_BEST_OBEY_POLICY, 1);
int problemCount = solver_solve(solver, &jobs);
solver_free(solver);
// TODO: Problem support!
return problemCount == 0 ? B_OK : B_BAD_VALUE;
}
status_t
BSolver::Implementation::_AddRepositories()
{
if (fInstalledRepository == NULL)
return B_BAD_VALUE;
int32 repositoryCount = fRepositoryInfos.CountItems();
for (int32 i = 0; i < repositoryCount; i++) {
RepositoryInfo* repositoryInfo = fRepositoryInfos.ItemAt(i);
BSolverRepository* repository = repositoryInfo->Repository();
Repo* repo = repo_create(fPool, repository->Name());
repositoryInfo->SetSolvRepo(repo);
repo->priority = 256 - repository->Priority();
repo->appdata = (void*)repositoryInfo;
BRepositoryCache::Iterator it = repository->GetIterator();
while (const BPackageInfo* packageInfo = it.Next()) {
repo_add_haiku_package_info(repo, *packageInfo,
REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE);
}
repo_internalize(repo);
if (repository->IsInstalled())
pool_set_installed(fPool, repo);
}
return B_OK;
}
BSolver::Implementation::RepositoryInfo*
BSolver::Implementation::_GetRepositoryInfo(BSolverRepository* repository) const
{
int32 repositoryCount = fRepositoryInfos.CountItems();
for (int32 i = 0; i < repositoryCount; i++) {
RepositoryInfo* repositoryInfo = fRepositoryInfos.ItemAt(i);
if (repository == repositoryInfo->Repository())
return repositoryInfo;
}
return NULL;
}
} // namespace BPackageKit

View File

@ -0,0 +1,79 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#include <package/solver/SolverPackageSpecifier.h>
namespace BPackageKit {
BSolverPackageSpecifier::BSolverPackageSpecifier()
:
fRepository(NULL),
fExpression()
{
}
BSolverPackageSpecifier::BSolverPackageSpecifier(
const BPackageResolvableExpression& expression)
:
fRepository(NULL),
fExpression(expression)
{
}
BSolverPackageSpecifier::BSolverPackageSpecifier(BSolverRepository* repository,
const BPackageResolvableExpression& expression)
:
fRepository(repository),
fExpression(expression)
{
}
BSolverPackageSpecifier::BSolverPackageSpecifier(
const BSolverPackageSpecifier& other)
:
fRepository(other.fRepository),
fExpression(other.fExpression)
{
}
BSolverPackageSpecifier::~BSolverPackageSpecifier()
{
}
BSolverRepository*
BSolverPackageSpecifier::Repository() const
{
return fRepository;
}
const BPackageResolvableExpression&
BSolverPackageSpecifier::Expression() const
{
return fExpression;
}
BSolverPackageSpecifier&
BSolverPackageSpecifier::operator=(const BSolverPackageSpecifier& other)
{
fRepository = other.fRepository;
fExpression = other.fExpression;
return *this;
}
} // namespace BPackageKit

View File

@ -0,0 +1,125 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#include <package/solver/SolverPackageSpecifierList.h>
#include <new>
#include <vector>
#include <package/solver/SolverPackageSpecifier.h>
namespace BPackageKit {
class BSolverPackageSpecifierList::Vector
: public std::vector<BSolverPackageSpecifier> {
public:
Vector()
:
std::vector<BSolverPackageSpecifier>()
{
}
Vector(const std::vector<BSolverPackageSpecifier>& other)
:
std::vector<BSolverPackageSpecifier>(other)
{
}
};
BSolverPackageSpecifierList::BSolverPackageSpecifierList()
:
fSpecifiers(NULL)
{
}
BSolverPackageSpecifierList::BSolverPackageSpecifierList(
const BSolverPackageSpecifierList& other)
:
fSpecifiers(NULL)
{
*this = other;
}
BSolverPackageSpecifierList::~BSolverPackageSpecifierList()
{
delete fSpecifiers;
}
bool
BSolverPackageSpecifierList::IsEmpty() const
{
return fSpecifiers == NULL || fSpecifiers->empty();
}
int32
BSolverPackageSpecifierList::CountSpecifiers() const
{
return fSpecifiers != NULL ? fSpecifiers->size() : 0;
}
const BSolverPackageSpecifier*
BSolverPackageSpecifierList::SpecifierAt(int32 index) const
{
if (fSpecifiers == NULL || index < 0
|| (size_t)index >= fSpecifiers->size()) {
return NULL;
}
return &(*fSpecifiers)[index];
}
bool
BSolverPackageSpecifierList::AppendSpecifier(
const BSolverPackageSpecifier& specifier)
{
try {
if (fSpecifiers == NULL) {
fSpecifiers = new(std::nothrow) Vector;
if (fSpecifiers == NULL)
return false;
}
fSpecifiers->push_back(specifier);
return true;
} catch (std::bad_alloc&) {
return false;
}
}
BSolverPackageSpecifierList&
BSolverPackageSpecifierList::operator=(const BSolverPackageSpecifierList& other)
{
if (this == &other)
return *this;
delete fSpecifiers;
fSpecifiers = NULL;
if (other.fSpecifiers == NULL)
return *this;
try {
fSpecifiers = new(std::nothrow) Vector(*other.fSpecifiers);
} catch (std::bad_alloc&) {
}
return *this;
}
} // namespace BPackageKit

View File

@ -0,0 +1,235 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#include <package/solver/SolverRepository.h>
#include <package/PackageDefs.h>
#include <package/PackageRoster.h>
#include <package/RepositoryCache.h>
#include <package/RepositoryConfig.h>
namespace BPackageKit {
BSolverRepository::BSolverRepository()
:
fName(),
fPriority(0),
fIsInstalled(false),
fPackages()
{
}
BSolverRepository::BSolverRepository(const BString& name)
:
fName(),
fPriority(0),
fIsInstalled(false),
fPackages()
{
SetTo(name);
}
BSolverRepository::BSolverRepository(BPackageInstallationLocation location)
:
fName(),
fPriority(0),
fIsInstalled(false),
fPackages()
{
SetTo(location);
}
BSolverRepository::BSolverRepository(BAllInstallationLocations)
:
fName(),
fPriority(0),
fIsInstalled(false),
fPackages()
{
SetTo(B_ALL_INSTALLATION_LOCATIONS);
}
BSolverRepository::BSolverRepository(const BRepositoryConfig& config)
:
fName(),
fPriority(0),
fIsInstalled(false),
fPackages()
{
SetTo(config);
}
BSolverRepository::~BSolverRepository()
{
}
status_t
BSolverRepository::SetTo(const BString& name)
{
Unset();
fName = name;
return fName.IsEmpty() ? B_BAD_VALUE : B_OK;
}
status_t
BSolverRepository::SetTo(BPackageInstallationLocation location)
{
Unset();
fName = "Installed";
status_t error = AddPackages(location);
if (error != B_OK) {
Unset();
return error;
}
fIsInstalled = true;
return B_OK;
}
status_t
BSolverRepository::SetTo(BAllInstallationLocations)
{
status_t error = SetTo(B_PACKAGE_INSTALLATION_LOCATION_SYSTEM);
if (error != B_OK)
return error;
error = AddPackages(B_PACKAGE_INSTALLATION_LOCATION_COMMON);
if (error != B_OK) {
Unset();
return error;
}
error = AddPackages(B_PACKAGE_INSTALLATION_LOCATION_HOME);
if (error != B_OK) {
Unset();
return error;
}
return B_OK;
}
status_t
BSolverRepository::SetTo(const BRepositoryConfig& config)
{
Unset();
if (config.InitCheck() != B_OK)
return B_BAD_VALUE;
fName = config.Name();
fPriority = config.Priority();
BPackageRoster roster;
BRepositoryCache cache;
status_t error = roster.GetRepositoryCache(config.Name(), &cache);
if (error != B_OK) {
Unset();
return error;
}
BRepositoryCache::Iterator it = cache.GetIterator();
while (const BPackageInfo* packageInfo = it.Next()) {
error = AddPackage(*packageInfo);
if (error != B_OK) {
Unset();
return error;
}
}
return B_OK;
}
void
BSolverRepository::Unset()
{
fName = BString();
fPriority = 0;
fIsInstalled = false;
fPackages.MakeEmpty();
}
status_t
BSolverRepository::InitCheck()
{
return fName.IsEmpty() ? B_NO_INIT : B_OK;
}
bool
BSolverRepository::IsInstalled() const
{
return fIsInstalled;
}
BString
BSolverRepository::Name() const
{
return fName;
}
uint8
BSolverRepository::Priority() const
{
return fPriority;
}
status_t
BSolverRepository::AddPackage(const BPackageInfo& info)
{
return fPackages.AddInfo(info);
}
status_t
BSolverRepository::AddPackages(BPackageInstallationLocation location)
{
BPackageRoster roster;
BPackageInfoSet packageInfos;
status_t error = roster.GetActivePackages(location, packageInfos);
if (error != B_OK)
return error;
BRepositoryCache::Iterator it = packageInfos.GetIterator();
while (const BPackageInfo* packageInfo = it.Next()) {
error = fPackages.AddInfo(*packageInfo);
if (error != B_OK)
return error;
}
return B_OK;
}
BSolverRepository::Iterator
BSolverRepository::GetIterator() const
{
return fPackages.GetIterator();
}
} // namespace BPackageKit