Flesh out the package kit solver API quite a bit more

* Reorganize things a bit:
  - BSolver is now an abstract base class.
  - A libsolv based implementation, LibsolvSolver, lives in a new
    add-on, which is loaded lazily.
  - Get rid of libpackage_solver. Save for LibsolvSolver everything
    is moved to libpackage.
  - This is a nicer solution for the cyclic dependency caused by
    libsolv (libsolvext to be precise) using the package kit for
    reading repositories and package files.
* Add a solver result data structure and and an accessor the solver.
* Add problem reporting support to the solver. There aren't data
  structures for the problem solutions yet and support for selecting
  solutions and re-solving is missing as well.
This commit is contained in:
Ingo Weinhold
2013-04-03 02:15:57 +00:00
parent 2d0563f357
commit 1a4d020daf
17 changed files with 1321 additions and 351 deletions
+1
View File
@@ -101,6 +101,7 @@ PRIVATE_SYSTEM_LIBS =
libfluidsynth.so
libilmimf.so
liblpsolve55.so
libpackage-add-on-libsolv.so
libroot-addon-icu.so
;
SYSTEM_SERVERS = app_server cddb_daemon debug_server input_server mail_daemon
+22 -10
View File
@@ -13,30 +13,42 @@ namespace BPackageKit {
class BSolverPackageSpecifierList;
class BSolverProblem;
class BSolverRepository;
class BSolverResult;
class BSolver {
public:
BSolver();
~BSolver();
virtual ~BSolver();
status_t Init();
static status_t Create(BSolver*& _solver);
status_t AddRepository(BSolverRepository* repository);
virtual status_t Init() = 0;
status_t Install(
virtual status_t AddRepository(
BSolverRepository* repository) = 0;
virtual status_t Install(
const BSolverPackageSpecifierList&
packages);
packages) = 0;
private:
class Implementation;
bool HasProblems() const
{ return CountProblems() > 0; }
virtual int32 CountProblems() const = 0;
virtual BSolverProblem* ProblemAt(int32 index) const = 0;
private:
Implementation* fImplementation;
virtual status_t GetResult(BSolverResult& _result) = 0;
protected:
BSolver();
};
// function exported by the libsolv based add-on
extern "C" BSolver* create_solver();
} // namespace BPackageKit
+42
View File
@@ -0,0 +1,42 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _PACKAGE__SOLVER_PACKAGE_H_
#define _PACKAGE__SOLVER_PACKAGE_H_
#include <package/PackageInfo.h>
namespace BPackageKit {
class BSolverRepository;
class BSolverPackage {
public:
BSolverPackage(BSolverRepository* repository,
const BPackageInfo& packageInfo);
BSolverPackage(const BSolverPackage& other);
~BSolverPackage();
BSolverRepository* Repository() const;
const BPackageInfo& Info() const;
BString Name() const;
BString VersionedName() const;
BSolverPackage& operator=(const BSolverPackage& other);
private:
BSolverRepository* fRepository;
BPackageInfo fInfo;
};
} // namespace BPackageKit
#endif // _PACKAGE__SOLVER_PACKAGE_H_
@@ -28,6 +28,7 @@ public:
bool AppendSpecifier(
const BSolverPackageSpecifier& specifier);
void MakeEmpty();
BSolverPackageSpecifierList& operator=(
const BSolverPackageSpecifierList& other);
+69
View File
@@ -0,0 +1,69 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _PACKAGE__SOLVER_PROBLEM_H_
#define _PACKAGE__SOLVER_PROBLEM_H_
#include <package/PackageResolvableExpression.h>
namespace BPackageKit {
class BSolverPackage;
class BSolverProblem {
public:
enum BType {
B_UNSPECIFIED,
B_NOT_IN_DISTUPGRADE_REPOSITORY,
B_INFERIOR_ARCHITECTURE,
B_INSTALLED_PACKAGE_PROBLEM,
B_CONFLICTING_REQUESTS,
B_REQUESTED_RESOLVABLE_NOT_PROVIDED,
B_REQUESTED_RESOLVABLE_PROVIDED_BY_SYSTEM,
B_DEPENDENCY_PROBLEM,
B_PACKAGE_NOT_INSTALLABLE,
B_DEPENDENCY_NOT_PROVIDED,
B_PACKAGE_NAME_CLASH,
B_PACKAGE_CONFLICT,
B_PACKAGE_OBSOLETES_RESOLVABLE,
B_INSTALLED_PACKAGE_OBSOLETES_RESOLVABLE,
B_PACKAGE_IMPLICITLY_OBSOLETES_RESOLVABLE,
B_DEPENDENCY_NOT_INSTALLABLE,
B_SELF_CONFLICT
};
public:
BSolverProblem(BType type,
BSolverPackage* sourcePackage,
BSolverPackage* targetPackage = NULL);
BSolverProblem(BType type,
BSolverPackage* sourcePackage,
BSolverPackage* targetPackage,
const BPackageResolvableExpression&
dependency);
~BSolverProblem();
BType Type() const;
BSolverPackage* SourcePackage() const;
BSolverPackage* TargetPackage() const;
const BPackageResolvableExpression& Dependency() const;
BString ToString() const;
private:
BType fType;
BSolverPackage* fSourcePackage;
BSolverPackage* fTargetPackage;
BPackageResolvableExpression fDependency;
};
} // namespace BPackageKit
#endif // _PACKAGE__SOLVER_PROBLEM_H_
+10 -2
View File
@@ -6,6 +6,7 @@
#define _PACKAGE__SOLVER_REPOSITORY_H_
#include <ObjectList.h>
#include <package/PackageDefs.h>
#include <package/PackageInfoSet.h>
#include <String.h>
@@ -16,6 +17,7 @@ namespace BPackageKit {
class BPackageInfo;
class BRepositoryConfig;
class BSolverPackage;
class BSolverRepository {
@@ -45,21 +47,27 @@ public:
status_t InitCheck();
bool IsInstalled() const;
void SetInstalled(bool isInstalled);
BString Name() const;
uint8 Priority() const;
bool IsEmpty() const;
int32 CountPackages() const;
BSolverPackage* PackageAt(int32 index) const;
status_t AddPackage(const BPackageInfo& info);
status_t AddPackages(
BPackageInstallationLocation location);
Iterator GetIterator() const;
private:
typedef BObjectList<BSolverPackage> PackageList;
private:
BString fName;
uint8 fPriority;
bool fIsInstalled;
BPackageInfoSet fPackages;
PackageList fPackages;
};
+67
View File
@@ -0,0 +1,67 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _PACKAGE__SOLVER_RESULT_H_
#define _PACKAGE__SOLVER_RESULT_H_
#include <ObjectList.h>
namespace BPackageKit {
class BSolverPackage;
class BSolverResultElement {
public:
enum BType {
B_TYPE_INSTALL,
B_TYPE_UNINSTALL
};
public:
BSolverResultElement(BType type,
BSolverPackage* package);
BSolverResultElement(
const BSolverResultElement& other);
~BSolverResultElement();
BType Type() const;
BSolverPackage* Package() const;
BSolverResultElement& operator=(const BSolverResultElement& other);
private:
BType fType;
BSolverPackage* fPackage;
};
class BSolverResult {
public:
BSolverResult();
~BSolverResult();
bool IsEmpty() const;
int32 CountElements() const;
const BSolverResultElement* ElementAt(int32 index) const;
void MakeEmpty();
bool AppendElement(
const BSolverResultElement& element);
private:
typedef BObjectList<BSolverResultElement> ElementList;
private:
ElementList fElements;
};
} // namespace BPackageKit
#endif // _PACKAGE__SOLVER_RESULT_H_
+9
View File
@@ -7,6 +7,7 @@ UsePrivateHeaders
shared ;
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits package hpkg ] ;
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits package solver ] ;
HPKG_SOURCES =
AttributeDataReader.cpp
@@ -76,6 +77,14 @@ SharedLibrary libpackage.so
NoErrorOutput.cpp
StandardErrorOutput.cpp
# solver
Solver.cpp
SolverPackage.cpp
SolverPackageSpecifier.cpp
SolverPackageSpecifierList.cpp
SolverProblem.cpp
SolverRepository.cpp
SolverResult.cpp
:
libshared.a be z $(TARGET_LIBSTDC++)
;
+9 -12
View File
@@ -1,23 +1,20 @@
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 ;
# add-on implementing a libsolv based BSolver
SubDirSysHdrs $(HAIKU_LIBSOLV_HEADERS) ;
SubDirHdrs $(HAIKU_LIBSOLV_HEADERS)/solv ;
UsePrivateHeaders shared ;
SharedLibrary libpackage_solver.so
SharedLibrary libpackage-add-on-libsolv.so
:
Solver.cpp
SolverPackageSpecifier.cpp
SolverPackageSpecifierList.cpp
SolverRepository.cpp
LibsolvSolver.cpp
:
package $(HAIKU_LIBSOLV_LIBS) be $(TARGET_LIBSTDC++)
;
Includes [ FGristFiles LibsolvSolver.cpp ]
: $(HAIKU_LIBSOLV_HEADERS_DEPENDENCY) ;
+619
View File
@@ -0,0 +1,619 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <[email protected]>
*/
#include "LibsolvSolver.h"
#include <errno.h>
#include <new>
#include <solv/poolarch.h>
#include <solv/repo.h>
#include <solv/repo_haiku.h>
#include <solv/selection.h>
#include <solv/solverdebug.h>
#include <package/PackageResolvableExpression.h>
#include <package/RepositoryCache.h>
#include <package/solver/SolverPackage.h>
#include <package/solver/SolverPackageSpecifier.h>
#include <package/solver/SolverPackageSpecifierList.h>
#include <package/solver/SolverProblem.h>
#include <package/solver/SolverRepository.h>
#include <package/solver/SolverResult.h>
#include <AutoDeleter.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.
BSolver*
BPackageKit::create_solver()
{
return new(std::nothrow) LibsolvSolver;
}
struct LibsolvSolver::SolvQueue : Queue {
SolvQueue()
{
queue_init(this);
}
~SolvQueue()
{
queue_free(this);
}
};
struct LibsolvSolver::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 - LibsolvSolver
LibsolvSolver::LibsolvSolver()
:
fPool(NULL),
fSolver(NULL),
fRepositoryInfos(10, true),
fInstalledRepository(NULL),
fProblems(10, true)
{
}
LibsolvSolver::~LibsolvSolver()
{
_Cleanup();
}
status_t
LibsolvSolver::Init()
{
_Cleanup();
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
LibsolvSolver::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
LibsolvSolver::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
fSolver = solver_create(fPool);
solver_set_flag(fSolver, SOLVER_FLAG_SPLITPROVIDES, 1);
solver_set_flag(fSolver, SOLVER_FLAG_BEST_OBEY_POLICY, 1);
// get the problems (if any)
fProblems.MakeEmpty();
int problemCount = solver_solve(fSolver, &jobs);
for (Id problemId = 1; problemId <= problemCount; problemId++) {
error = _AddProblem(problemId);
if (error != B_OK)
return error;
}
return B_OK;
}
int32
LibsolvSolver::CountProblems() const
{
return fProblems.CountItems();
}
BSolverProblem*
LibsolvSolver::ProblemAt(int32 index) const
{
return fProblems.ItemAt(index);
}
status_t
LibsolvSolver::GetResult(BSolverResult& _result)
{
if (HasProblems())
return B_BAD_VALUE;
_result.MakeEmpty();
Transaction* transaction = solver_create_transaction(fSolver);
CObjectDeleter<Transaction> transactionDeleter(transaction,
&transaction_free);
if (transaction->steps.count == 0)
return B_OK;
// Get the packages that end up in the installation. The result queue
// contains newPackageCount new packages to install first, followed by the
// kept packages.
SolvQueue installedPackages;
int newPackageCount = transaction_installedresult(transaction,
&installedPackages);
transaction_order(transaction, 0);
for (int i = 0; i < transaction->steps.count; i++) {
Id solvableId = transaction->steps.elements[i];
Solvable* solvable = pool_id2solvable(fPool, solvableId);
switch (transaction_type(transaction, solvableId,
SOLVER_TRANSACTION_RPM_ONLY)) {
case SOLVER_TRANSACTION_ERASE:
{
BSolverPackage* package = _GetPackage(solvable);
if (package == NULL)
return B_ERROR;
status_t error = _result.AppendElement(
BSolverResultElement(BSolverResultElement::B_TYPE_UNINSTALL,
package));
if (error != B_OK)
return error;
break;
}
case SOLVER_TRANSACTION_INSTALL:
case SOLVER_TRANSACTION_MULTIINSTALL:
{
// check, if it really is a new package
// TODO: Is this check really necessary?
bool foundPackage = false;
for (int j = 0; j < newPackageCount; j++) {
if (installedPackages.elements[j] == solvableId) {
foundPackage = true;
break;
}
}
if (!foundPackage)
continue;
BSolverPackage* package = _GetPackage(solvable);
if (package == NULL)
return B_ERROR;
if (!_result.AppendElement(
BSolverResultElement(
BSolverResultElement::B_TYPE_INSTALL, package))) {
return B_NO_MEMORY;
}
break;
}
default:
break;
}
}
return B_OK;
}
void
LibsolvSolver::_Cleanup()
{
fProblems.MakeEmpty();
fSolvablePackages.clear();
fInstalledRepository = NULL;
fRepositoryInfos.MakeEmpty();
if (fSolver != NULL) {
solver_free(fSolver);
fSolver = NULL;
}
if (fPool != NULL) {
pool_free(fPool);
fPool = NULL;
}
}
status_t
LibsolvSolver::_AddRepositories()
{
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;
int32 packageCount = repository->CountPackages();
for (int32 k = 0; k < packageCount; k++) {
BSolverPackage* package = repository->PackageAt(k);
Id solvableId = repo_add_haiku_package_info(repo, package->Info(),
REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE);
Solvable* solvable = pool_id2solvable(fPool, solvableId);
try {
fSolvablePackages[solvable] = package;
} catch (std::bad_alloc&) {
return B_NO_MEMORY;
}
}
repo_internalize(repo);
if (repository->IsInstalled())
pool_set_installed(fPool, repo);
}
return B_OK;
}
LibsolvSolver::RepositoryInfo*
LibsolvSolver::_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;
}
BSolverPackage*
LibsolvSolver::_GetPackage(Solvable* solvable) const
{
SolvableMap::const_iterator it = fSolvablePackages.find(solvable);
return it != fSolvablePackages.end() ? it->second : NULL;
}
BSolverPackage*
LibsolvSolver::_GetPackage(Id solvableId) const
{
Solvable* solvable = pool_id2solvable(fPool, solvableId);
return solvable != NULL ? _GetPackage(solvable) : NULL;
}
status_t
LibsolvSolver::_AddProblem(Id problemId)
{
enum {
NEED_SOURCE = 0x1,
NEED_TARGET = 0x2,
NEED_DEPENDENCY = 0x4
};
Id ruleId = solver_findproblemrule(fSolver, problemId);
Id sourceId;
Id targetId;
Id dependencyId;
BSolverProblem::BType problemType = BSolverProblem::B_UNSPECIFIED;
uint32 needed = 0;
switch (solver_ruleinfo(fSolver, ruleId, &sourceId, &targetId,
&dependencyId)) {
case SOLVER_RULE_DISTUPGRADE:
problemType = BSolverProblem::B_NOT_IN_DISTUPGRADE_REPOSITORY;
needed = NEED_SOURCE;
break;
case SOLVER_RULE_INFARCH:
problemType = BSolverProblem::B_INFERIOR_ARCHITECTURE;
needed = NEED_SOURCE;
break;
case SOLVER_RULE_UPDATE:
problemType = BSolverProblem::B_INSTALLED_PACKAGE_PROBLEM;
needed = NEED_SOURCE;
break;
case SOLVER_RULE_JOB:
problemType = BSolverProblem::B_CONFLICTING_REQUESTS;
break;
case SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP:
problemType = BSolverProblem::B_REQUESTED_RESOLVABLE_NOT_PROVIDED;
needed = NEED_DEPENDENCY;
break;
case SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM:
problemType
= BSolverProblem::B_REQUESTED_RESOLVABLE_PROVIDED_BY_SYSTEM;
needed = NEED_DEPENDENCY;
break;
case SOLVER_RULE_RPM:
problemType = BSolverProblem::B_DEPENDENCY_PROBLEM;
break;
case SOLVER_RULE_RPM_NOT_INSTALLABLE:
problemType = BSolverProblem::B_PACKAGE_NOT_INSTALLABLE;
needed = NEED_SOURCE;
break;
case SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP:
problemType = BSolverProblem::B_DEPENDENCY_NOT_PROVIDED;
needed = NEED_SOURCE | NEED_DEPENDENCY;
break;
case SOLVER_RULE_RPM_SAME_NAME:
problemType = BSolverProblem::B_PACKAGE_NAME_CLASH;
needed = NEED_SOURCE | NEED_TARGET;
break;
case SOLVER_RULE_RPM_PACKAGE_CONFLICT:
problemType = BSolverProblem::B_PACKAGE_CONFLICT;
needed = NEED_SOURCE | NEED_TARGET | NEED_DEPENDENCY;
break;
case SOLVER_RULE_RPM_PACKAGE_OBSOLETES:
problemType = BSolverProblem::B_PACKAGE_OBSOLETES_RESOLVABLE;
needed = NEED_SOURCE | NEED_TARGET | NEED_DEPENDENCY;
break;
case SOLVER_RULE_RPM_INSTALLEDPKG_OBSOLETES:
problemType
= BSolverProblem::B_INSTALLED_PACKAGE_OBSOLETES_RESOLVABLE;
needed = NEED_SOURCE | NEED_TARGET | NEED_DEPENDENCY;
break;
case SOLVER_RULE_RPM_IMPLICIT_OBSOLETES:
problemType
= BSolverProblem::B_PACKAGE_IMPLICITLY_OBSOLETES_RESOLVABLE;
needed = NEED_SOURCE | NEED_TARGET | NEED_DEPENDENCY;
break;
case SOLVER_RULE_RPM_PACKAGE_REQUIRES:
problemType = BSolverProblem::B_DEPENDENCY_NOT_INSTALLABLE;
needed = NEED_SOURCE | NEED_DEPENDENCY;
break;
case SOLVER_RULE_RPM_SELF_CONFLICT:
problemType = BSolverProblem::B_SELF_CONFLICT;
needed = NEED_SOURCE | NEED_DEPENDENCY;
break;
case SOLVER_RULE_UNKNOWN:
case SOLVER_RULE_FEATURE:
case SOLVER_RULE_LEARNT:
case SOLVER_RULE_CHOICE:
case SOLVER_RULE_BEST:
problemType = BSolverProblem::B_UNSPECIFIED;
break;
}
BSolverPackage* sourcePackage = NULL;
if ((needed & NEED_SOURCE) != 0) {
sourcePackage = _GetPackage(sourceId);
if (sourcePackage == NULL)
return B_ERROR;
}
BSolverPackage* targetPackage = NULL;
if ((needed & NEED_TARGET) != 0) {
targetPackage = _GetPackage(targetId);
if (targetPackage == NULL)
return B_ERROR;
}
BPackageResolvableExpression dependency;
if ((needed & NEED_DEPENDENCY) != 0) {
status_t error = _GetResolvableExpression(dependencyId, dependency);
if (error != B_OK)
return error;
}
BSolverProblem* problem = new(std::nothrow) BSolverProblem(problemType,
sourcePackage, targetPackage, dependency);
if (problem == NULL || !fProblems.AddItem(problem)) {
delete problem;
return B_NO_MEMORY;
}
return B_OK;
}
status_t
LibsolvSolver::_GetResolvableExpression(Id id,
BPackageResolvableExpression& _expression) const
{
// Try to translate the libsolv ID to a resolvable expression. Generally
// that doesn't work, since libsolv is more expressive, but all the stuff
// we feed libsolv we should be able to translate back.
if (!ISRELDEP(id)) {
// just a string
_expression.SetTo(pool_id2str(fPool, id));
return B_OK;
}
// a composite -- analyze it
Reldep* reldep = GETRELDEP(fPool, id);
// No support for more than one level, so both name and evr must be strings.
if (ISRELDEP(reldep->name) || ISRELDEP(reldep->evr))
return B_NOT_SUPPORTED;
const char* name = pool_id2str(fPool, reldep->name);
const char* versionString = pool_id2str(fPool, reldep->evr);
if (name == NULL || versionString == NULL)
return B_NOT_SUPPORTED;
// get the operator -- we don't support all libsolv supports
BPackageResolvableOperator op;
switch (reldep->flags) {
case 1:
op = B_PACKAGE_RESOLVABLE_OP_GREATER;
break;
case 2:
op = B_PACKAGE_RESOLVABLE_OP_EQUAL;
break;
case 3:
op = B_PACKAGE_RESOLVABLE_OP_GREATER_EQUAL;
break;
case 4:
op = B_PACKAGE_RESOLVABLE_OP_LESS;
break;
case 5:
op = B_PACKAGE_RESOLVABLE_OP_NOT_EQUAL;
break;
case 6:
op = B_PACKAGE_RESOLVABLE_OP_LESS_EQUAL;
break;
default:
return B_NOT_SUPPORTED;
}
// get the version (cut off the empty epoch)
if (versionString[0] == ':')
versionString++;
BPackageVersion version;
status_t error = version.SetTo(versionString, true);
if (error != B_OK)
return error == B_BAD_DATA ? B_NOT_SUPPORTED : error;
_expression.SetTo(name, op, version);
return B_OK;
}
+77
View File
@@ -0,0 +1,77 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef HAIKU_LIBSOLV_SOLVER_H
#define HAIKU_LIBSOLV_SOLVER_H
#include <map>
#include <ObjectList.h>
#include <package/solver/Solver.h>
#include <solv/pool.h>
#include <solv/solver.h>
using namespace BPackageKit;
namespace BPackageKit {
class BPackageResolvableExpression;
class BSolverPackage;
}
class LibsolvSolver : public BSolver {
public:
LibsolvSolver();
virtual ~LibsolvSolver();
virtual status_t Init();
virtual status_t AddRepository(BSolverRepository* repository);
virtual status_t Install(
const BSolverPackageSpecifierList&
packages);
virtual int32 CountProblems() const;
virtual BSolverProblem* ProblemAt(int32 index) const;
virtual status_t GetResult(BSolverResult& _result);
private:
struct SolvQueue;
struct RepositoryInfo;
typedef BObjectList<RepositoryInfo> RepositoryInfoList;
typedef BObjectList<BSolverProblem> ProblemList;
typedef std::map<Solvable*, BSolverPackage*> SolvableMap;
private:
void _Cleanup();
status_t _AddRepositories();
RepositoryInfo* _GetRepositoryInfo(
BSolverRepository* repository) const;
BSolverPackage* _GetPackage(Solvable* solvable) const;
BSolverPackage* _GetPackage(Id solvableId) const;
status_t _AddProblem(Id problemId);
status_t _GetResolvableExpression(Id id,
BPackageResolvableExpression& _expression)
const;
private:
Pool* fPool;
Solver* fSolver;
RepositoryInfoList fRepositoryInfos;
RepositoryInfo* fInstalledRepository;
SolvableMap fSolvablePackages;
ProblemList fProblems;
};
#endif // HAIKU_LIBSOLV_SOLVER_H
+27 -313
View File
@@ -9,348 +9,62 @@
#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.
#include <dlfcn.h>
#include <pthread.h>
namespace BPackageKit {
// #pragma mark - BSolver::Implementation
typedef BSolver* CreateSolverFunction();
static CreateSolverFunction* sCreateSolver = NULL;
static pthread_once_t sLoadLibsolvSolverAddOnInitOnce = PTHREAD_ONCE_INIT;
class BSolver::Implementation {
public:
Implementation();
~Implementation();
static void
load_libsolv_solver_add_on()
{
void* imageHandle = dlopen("libpackage-add-on-libsolv.so", 0);
if (imageHandle == NULL)
return;
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
sCreateSolver = (CreateSolverFunction*)dlsym(imageHandle, "create_solver");
if (sCreateSolver == NULL)
dlclose(imageHandle);
}
BSolver::BSolver()
:
fImplementation(new(std::nothrow) Implementation)
{
}
BSolver::~BSolver()
{
delete fImplementation;
}
status_t
BSolver::Init()
/*static*/ status_t
BSolver::Create(BSolver*& _solver)
{
return fImplementation != NULL ? fImplementation->Init() : B_NO_MEMORY;
}
pthread_once(&sLoadLibsolvSolverAddOnInitOnce, &load_libsolv_solver_add_on);
if (sCreateSolver == NULL)
return B_NOT_SUPPORTED;
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)
BSolver* solver = sCreateSolver();
if (solver == 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)
status_t error = solver->Init();
if (error != B_OK) {
delete solver;
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);
}
_solver = solver;
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
+78
View File
@@ -0,0 +1,78 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <[email protected]>
*/
#include <package/solver/SolverPackage.h>
namespace BPackageKit {
BSolverPackage::BSolverPackage(BSolverRepository* repository,
const BPackageInfo& packageInfo)
:
fRepository(repository),
fInfo(packageInfo)
{
}
BSolverPackage::BSolverPackage(const BSolverPackage& other)
:
fRepository(other.fRepository),
fInfo(other.fInfo)
{
}
BSolverPackage::~BSolverPackage()
{
}
BSolverRepository*
BSolverPackage::Repository() const
{
return fRepository;
}
const BPackageInfo&
BSolverPackage::Info() const
{
return fInfo;
}
BString
BSolverPackage::Name() const
{
return fInfo.Name();
}
BString
BSolverPackage::VersionedName() const
{
if (fInfo.Version().InitCheck() != B_OK)
return Name();
BString result = Name();
return result << '-' << fInfo.Version().ToString();
}
BSolverPackage&
BSolverPackage::operator=(const BSolverPackage& other)
{
fRepository = other.fRepository;
fInfo = other.fInfo;
return *this;
}
} // namespace BPackageKit
@@ -101,6 +101,13 @@ BSolverPackageSpecifierList::AppendSpecifier(
}
void
BSolverPackageSpecifierList::MakeEmpty()
{
fSpecifiers->clear();
}
BSolverPackageSpecifierList&
BSolverPackageSpecifierList::operator=(const BSolverPackageSpecifierList& other)
{
+118
View File
@@ -0,0 +1,118 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <[email protected]>
*/
#include <package/solver/SolverProblem.h>
#include <stdio.h>
#include <package/solver/SolverPackage.h>
static const char* const kToStringTexts[] = {
"unspecified problem",
"%source% does not belong to a distupgrade repository",
"%source% has inferior architecture",
"problem with installed package %source%",
"conflicting requests",
"nothing provides requested %dependency%",
"%dependency% is provided by the system",
"dependency problem",
"package %source% is not installable",
"nothing provides %dependency% needed by %source%",
"cannot install both %source% and %target%",
"package %source% conflicts with %dependency% provided by %target%",
"package %source% obsoletes %dependency% provided by %target%",
"installed package %source% obsoletes %dependency% provided by %target%",
"package %source% implicitly obsoletes %dependency% provided by %target%",
"package %source% requires %dependency%, but none of the providers can be "
"installed",
"package %source% conflicts with %dependency% provided by itself"
};
namespace BPackageKit {
BSolverProblem::BSolverProblem(BType type, BSolverPackage* sourcePackage,
BSolverPackage* targetPackage)
:
fType(type),
fSourcePackage(sourcePackage),
fTargetPackage(targetPackage),
fDependency()
{
}
BSolverProblem::BSolverProblem(BType type, BSolverPackage* sourcePackage,
BSolverPackage* targetPackage,
const BPackageResolvableExpression& dependency)
:
fType(type),
fSourcePackage(sourcePackage),
fTargetPackage(targetPackage),
fDependency(dependency)
{
}
BSolverProblem::~BSolverProblem()
{
}
BSolverProblem::BType
BSolverProblem::Type() const
{
return fType;
}
BSolverPackage*
BSolverProblem::SourcePackage() const
{
return fSourcePackage;
}
BSolverPackage*
BSolverProblem::TargetPackage() const
{
return fTargetPackage;
}
const BPackageResolvableExpression&
BSolverProblem::Dependency() const
{
return fDependency;
}
BString
BSolverProblem::ToString() const
{
size_t index = fType;
if (index >= sizeof(kToStringTexts) / sizeof(kToStringTexts[0]))
index = 0;
return BString(kToStringTexts[index])
.ReplaceAll("%source%",
fSourcePackage != NULL
? fSourcePackage->VersionedName().String() : "?")
.ReplaceAll("%target%",
fTargetPackage != NULL
? fTargetPackage->VersionedName().String() : "?")
.ReplaceAll("%dependency%",
fDependency.InitCheck() == B_OK
? fDependency.ToString().String() : "?");
}
} // namespace BPackageKit
+45 -14
View File
@@ -13,6 +13,10 @@
#include <package/PackageRoster.h>
#include <package/RepositoryCache.h>
#include <package/RepositoryConfig.h>
#include <package/solver/SolverPackage.h>
static const int32 kInitialPackageListSize = 40;
namespace BPackageKit {
@@ -23,7 +27,7 @@ BSolverRepository::BSolverRepository()
fName(),
fPriority(0),
fIsInstalled(false),
fPackages()
fPackages(kInitialPackageListSize, true)
{
}
@@ -33,7 +37,7 @@ BSolverRepository::BSolverRepository(const BString& name)
fName(),
fPriority(0),
fIsInstalled(false),
fPackages()
fPackages(kInitialPackageListSize, true)
{
SetTo(name);
}
@@ -44,7 +48,7 @@ BSolverRepository::BSolverRepository(BPackageInstallationLocation location)
fName(),
fPriority(0),
fIsInstalled(false),
fPackages()
fPackages(kInitialPackageListSize, true)
{
SetTo(location);
}
@@ -55,7 +59,7 @@ BSolverRepository::BSolverRepository(BAllInstallationLocations)
fName(),
fPriority(0),
fIsInstalled(false),
fPackages()
fPackages(kInitialPackageListSize, true)
{
SetTo(B_ALL_INSTALLATION_LOCATIONS);
}
@@ -66,7 +70,7 @@ BSolverRepository::BSolverRepository(const BRepositoryConfig& config)
fName(),
fPriority(0),
fIsInstalled(false),
fPackages()
fPackages(kInitialPackageListSize, true)
{
SetTo(config);
}
@@ -184,6 +188,13 @@ BSolverRepository::IsInstalled() const
}
void
BSolverRepository::SetInstalled(bool isInstalled)
{
fIsInstalled = isInstalled;
}
BString
BSolverRepository::Name() const
{
@@ -198,10 +209,37 @@ BSolverRepository::Priority() const
}
bool
BSolverRepository::IsEmpty() const
{
return fPackages.IsEmpty();
}
int32
BSolverRepository::CountPackages() const
{
return fPackages.CountItems();
}
BSolverPackage*
BSolverRepository::PackageAt(int32 index) const
{
return fPackages.ItemAt(index);
}
status_t
BSolverRepository::AddPackage(const BPackageInfo& info)
{
return fPackages.AddInfo(info);
BSolverPackage* package = new(std::nothrow) BSolverPackage(this, info);
if (package == NULL || !fPackages.AddItem(package)) {
delete package;
return B_NO_MEMORY;
}
return B_OK;
}
@@ -216,7 +254,7 @@ BSolverRepository::AddPackages(BPackageInstallationLocation location)
BRepositoryCache::Iterator it = packageInfos.GetIterator();
while (const BPackageInfo* packageInfo = it.Next()) {
error = fPackages.AddInfo(*packageInfo);
error = AddPackage(*packageInfo);
if (error != B_OK)
return error;
}
@@ -225,11 +263,4 @@ BSolverRepository::AddPackages(BPackageInstallationLocation location)
}
BSolverRepository::Iterator
BSolverRepository::GetIterator() const
{
return fPackages.GetIterator();
}
} // namespace BPackageKit
+120
View File
@@ -0,0 +1,120 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <[email protected]>
*/
#include <package/solver/SolverResult.h>
namespace BPackageKit {
// #pragma mark - BSolverResultElement
BSolverResultElement::BSolverResultElement(BType type, BSolverPackage* package)
:
fType(type),
fPackage(package)
{
}
BSolverResultElement::BSolverResultElement(const BSolverResultElement& other)
:
fType(other.fType),
fPackage(other.fPackage)
{
}
BSolverResultElement::~BSolverResultElement()
{
}
BSolverResultElement::BType
BSolverResultElement::Type() const
{
return fType;
}
BSolverPackage*
BSolverResultElement::Package() const
{
return fPackage;
}
BSolverResultElement&
BSolverResultElement::operator=(const BSolverResultElement& other)
{
fType = other.fType;
fPackage = other.fPackage;
return *this;
}
// #pragma mark - BSolverResult
BSolverResult::BSolverResult()
:
fElements(20, true)
{
}
BSolverResult::~BSolverResult()
{
}
bool
BSolverResult::IsEmpty() const
{
return fElements.IsEmpty();
}
int32
BSolverResult::CountElements() const
{
return fElements.CountItems();
}
const BSolverResultElement*
BSolverResult::ElementAt(int32 index) const
{
return fElements.ItemAt(index);
}
void
BSolverResult::MakeEmpty()
{
fElements.MakeEmpty();
}
bool
BSolverResult::AppendElement(const BSolverResultElement& element)
{
BSolverResultElement* newElement
= new(std::nothrow) BSolverResultElement(element);
if (newElement == NULL || !fElements.AddItem(newElement)) {
delete newElement;
return false;
}
return true;
}
} // namespace BPackageKit