* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#include "LibsolvSolver.h"
#include <errno.h>
#include <sys/utsname.h>
#include <new>
#include <solv/policy.h>
#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>
BSolver*
BPackageKit::create_solver()
{
return new(std::nothrow) LibsolvSolver;
}
struct LibsolvSolver::SolvQueue : Queue {
SolvQueue()
{
queue_init(this);
}
~SolvQueue()
{
queue_free(this);
}
};
struct LibsolvSolver::SolvDataIterator : Dataiterator {
SolvDataIterator(Pool* pool, Repo* repo, Id solvableId, Id keyname,
const char* match, int flags)
{
dataiterator_init(this, pool, repo, solvableId, keyname, match, flags);
}
~SolvDataIterator()
{
dataiterator_free(this);
}
};
struct LibsolvSolver::RepositoryInfo {
RepositoryInfo(BSolverRepository* repository)
:
fRepository(repository),
fSolvRepo(NULL),
fChangeCount(repository->ChangeCount())
{
}
BSolverRepository* Repository() const
{
return fRepository;
}
Repo* SolvRepo()
{
return fSolvRepo;
}
void SetSolvRepo(Repo* repo)
{
fSolvRepo = repo;
}
bool HasChanged() const
{
return fChangeCount != fRepository->ChangeCount() || fSolvRepo == NULL;
}
void SetUnchanged()
{
fChangeCount = fRepository->ChangeCount();
}
private:
BSolverRepository* fRepository;
Repo* fSolvRepo;
uint64 fChangeCount;
};
struct LibsolvSolver::Problem : public BSolverProblem {
Problem(::Id id, BType type, BSolverPackage* sourcePackage,
BSolverPackage* targetPackage,
const BPackageResolvableExpression& dependency)
:
BSolverProblem(type, sourcePackage, targetPackage, dependency),
fId(id),
fSelectedSolution(NULL)
{
}
::Id Id() const
{
return fId;
}
const Solution* SelectedSolution() const
{
return fSelectedSolution;
}
void SetSelectedSolution(const Solution* solution)
{
fSelectedSolution = solution;
}
private:
::Id fId;
const Solution* fSelectedSolution;
};
struct LibsolvSolver::Solution : public BSolverProblemSolution {
Solution(::Id id, LibsolvSolver::Problem* problem)
:
BSolverProblemSolution(),
fId(id),
fProblem(problem)
{
}
::Id Id() const
{
return fId;
}
LibsolvSolver::Problem* Problem() const
{
return fProblem;
}
private:
::Id fId;
LibsolvSolver::Problem* fProblem;
};
LibsolvSolver::LibsolvSolver()
:
fPool(NULL),
fSolver(NULL),
fJobs(NULL),
fRepositoryInfos(10),
fInstalledRepository(NULL),
fSolvablePackages(),
fPackageSolvables(),
fProblems(10),
fDebugLevel(0)
{
}
LibsolvSolver::~LibsolvSolver()
{
_Cleanup();
}
status_t
LibsolvSolver::Init()
{
_Cleanup();
return B_OK;
}
void
LibsolvSolver::SetDebugLevel(int32 level)
{
fDebugLevel = level;
if (fPool != NULL)
pool_setdebuglevel(fPool, fDebugLevel);
}
status_t
LibsolvSolver::AddRepository(BSolverRepository* repository)
{
if (repository == NULL || repository->InitCheck() != B_OK)
return B_BAD_VALUE;
if (repository->IsInstalled() && _InstalledRepository() != NULL)
return B_BAD_VALUE;
RepositoryInfo* info = new(std::nothrow) RepositoryInfo(repository);
if (info == NULL)
return B_NO_MEMORY;
if (!fRepositoryInfos.AddItem(info)) {
delete info;
return B_NO_MEMORY;
}
return B_OK;
}
status_t
LibsolvSolver::FindPackages(const char* searchString, uint32 flags,
BObjectList<BSolverPackage>& _packages)
{
status_t error = _AddRepositories();
if (error != B_OK)
return error;
int iteratorFlags = SEARCH_SUBSTRING;
if ((flags & B_FIND_CASE_INSENSITIVE) != 0)
iteratorFlags |= SEARCH_NOCASE;
SolvDataIterator iterator(fPool, 0, 0, 0, searchString, iteratorFlags);
SolvQueue selection;
if ((flags & B_FIND_IN_NAME) != 0) {
dataiterator_set_keyname(&iterator, SOLVABLE_NAME);
dataiterator_set_search(&iterator, 0, 0);
while (dataiterator_step(&iterator))
queue_push2(&selection, SOLVER_SOLVABLE, iterator.solvid);
}
if ((flags & B_FIND_IN_SUMMARY) != 0) {
dataiterator_set_keyname(&iterator, SOLVABLE_SUMMARY);
dataiterator_set_search(&iterator, 0, 0);
while (dataiterator_step(&iterator))
queue_push2(&selection, SOLVER_SOLVABLE, iterator.solvid);
}
if ((flags & B_FIND_IN_DESCRIPTION) != 0) {
dataiterator_set_keyname(&iterator, SOLVABLE_DESCRIPTION);
dataiterator_set_search(&iterator, 0, 0);
while (dataiterator_step(&iterator))
queue_push2(&selection, SOLVER_SOLVABLE, iterator.solvid);
}
if ((flags & B_FIND_IN_PROVIDES) != 0) {
dataiterator_set_keyname(&iterator, SOLVABLE_PROVIDES);
dataiterator_set_search(&iterator, 0, 0);
while (dataiterator_step(&iterator))
queue_push2(&selection, SOLVER_SOLVABLE, iterator.solvid);
}
if ((flags & B_FIND_IN_REQUIRES) != 0) {
dataiterator_set_keyname(&iterator, SOLVABLE_REQUIRES);
dataiterator_set_search(&iterator, 0, 0);
while (dataiterator_step(&iterator))
queue_push2(&selection, SOLVER_SOLVABLE, iterator.solvid);
}
return _GetFoundPackages(selection, flags, _packages);
}
status_t
LibsolvSolver::FindPackages(const BSolverPackageSpecifierList& packages,
uint32 flags, BObjectList<BSolverPackage>& _packages,
const BSolverPackageSpecifier** _unmatched)
{
if (_unmatched != NULL)
*_unmatched = NULL;
if ((flags & B_FIND_INSTALLED_ONLY) != 0 && _InstalledRepository() == NULL)
return B_BAD_VALUE;
status_t error = _AddRepositories();
if (error != B_OK)
return error;
error = _InitJobQueue();
if (error != B_OK)
return error;
error = _AddSpecifiedPackages(packages, _unmatched,
(flags & B_FIND_INSTALLED_ONLY) != 0 ? SELECTION_INSTALLED_ONLY : 0);
if (error != B_OK)
return error;
return _GetFoundPackages(*fJobs, flags, _packages);
}
status_t
LibsolvSolver::Install(const BSolverPackageSpecifierList& packages,
const BSolverPackageSpecifier** _unmatched)
{
if (_unmatched != NULL)
*_unmatched = NULL;
if (packages.IsEmpty())
return B_BAD_VALUE;
status_t error = _AddRepositories();
if (error != B_OK)
return error;
error = _InitJobQueue();
if (error != B_OK)
return error;
error = _AddSpecifiedPackages(packages, _unmatched, 0);
if (error != B_OK)
return error;
_SetJobsSolverMode(SOLVER_INSTALL);
_InitSolver();
return _Solve();
}
status_t
LibsolvSolver::Uninstall(const BSolverPackageSpecifierList& packages,
const BSolverPackageSpecifier** _unmatched)
{
if (_unmatched != NULL)
*_unmatched = NULL;
if (_InstalledRepository() == NULL || packages.IsEmpty())
return B_BAD_VALUE;
status_t error = _AddRepositories();
if (error != B_OK)
return error;
error = _InitJobQueue();
if (error != B_OK)
return error;
error = _AddSpecifiedPackages(packages, _unmatched,
SELECTION_INSTALLED_ONLY);
if (error != B_OK)
return error;
_SetJobsSolverMode(SOLVER_ERASE);
_InitSolver();
solver_set_flag(fSolver, SOLVER_FLAG_ALLOW_UNINSTALL, 1);
return _Solve();
}
status_t
LibsolvSolver::Update(const BSolverPackageSpecifierList& packages,
bool installNotYetInstalled, const BSolverPackageSpecifier** _unmatched)
{
if (_unmatched != NULL)
*_unmatched = NULL;
status_t error = _AddRepositories();
if (error != B_OK)
return error;
error = _InitJobQueue();
if (error != B_OK)
return error;
if (packages.IsEmpty()) {
queue_push2(fJobs, SOLVER_SOLVABLE_ALL, 0);
} else {
error = _AddSpecifiedPackages(packages, _unmatched, 0);
if (error != B_OK)
return error;
}
_SetJobsSolverMode(SOLVER_UPDATE);
if (installNotYetInstalled) {
for (int i = 0; i < fJobs->count; i += 2) {
if (pool_isemptyupdatejob(fPool, fJobs->elements[i],
fJobs->elements[i + 1])) {
fJobs->elements[i] &= ~SOLVER_JOBMASK;
fJobs->elements[i] |= SOLVER_INSTALL;
}
}
}
_InitSolver();
return _Solve();
}
status_t
LibsolvSolver::FullSync()
{
status_t error = _AddRepositories();
if (error != B_OK)
return error;
error = _InitJobQueue();
if (error != B_OK)
return error;
queue_push2(fJobs, SOLVER_SOLVABLE_ALL, 0);
_SetJobsSolverMode(SOLVER_DISTUPGRADE);
_InitSolver();
return _Solve();
}
status_t
LibsolvSolver::VerifyInstallation(uint32 flags)
{
if (_InstalledRepository() == NULL)
return B_BAD_VALUE;
status_t error = _AddRepositories();
if (error != B_OK)
return error;
error = _InitJobQueue();
if (error != B_OK)
return error;
queue_push2(fJobs, SOLVER_SOLVABLE_ALL, 0);
_SetJobsSolverMode(SOLVER_VERIFY);
_InitSolver();
if ((flags & B_VERIFY_ALLOW_UNINSTALL) != 0)
solver_set_flag(fSolver, SOLVER_FLAG_ALLOW_UNINSTALL, 1);
return _Solve();
}
status_t
LibsolvSolver::SelectProblemSolution(BSolverProblem* _problem,
const BSolverProblemSolution* _solution)
{
if (_problem == NULL)
return B_BAD_VALUE;
Problem* problem = static_cast<Problem*>(_problem);
if (_solution == NULL) {
problem->SetSelectedSolution(NULL);
return B_OK;
}
const Solution* solution = static_cast<const Solution*>(_solution);
if (solution->Problem() != problem)
return B_BAD_VALUE;
problem->SetSelectedSolution(solution);
return B_OK;
}
status_t
LibsolvSolver::SolveAgain()
{
if (fSolver == NULL || fJobs == NULL)
return B_BAD_VALUE;
int32 problemCount = fProblems.CountItems();
for (int32 i = 0; i < problemCount; i++) {
Problem* problem = fProblems.ItemAt(i);
if (const Solution* solution = problem->SelectedSolution())
solver_take_solution(fSolver, problem->Id(), solution->Id(), fJobs);
}
return _Solve();
}
int32
LibsolvSolver::CountProblems() const
{
return fProblems.CountItems();
}
BSolverProblem*
LibsolvSolver::ProblemAt(int32 index) const
{
return fProblems.ItemAt(index);
}
status_t
LibsolvSolver::GetResult(BSolverResult& _result)
{
if (fSolver == NULL || HasProblems())
return B_BAD_VALUE;
_result.MakeEmpty();
Transaction* transaction = solver_create_transaction(fSolver);
CObjectDeleter<Transaction, void, transaction_free>
transactionDeleter(transaction);
if (transaction->steps.count == 0)
return B_OK;
transaction_order(transaction, 0);
for (int i = 0; i < transaction->steps.count; i++) {
Id solvableId = transaction->steps.elements[i];
if (fPool->installed
&& fPool->solvables[solvableId].repo == fPool->installed) {
BSolverPackage* package = _GetPackage(solvableId);
if (package == NULL)
return B_ERROR;
if (!_result.AppendElement(
BSolverResultElement(
BSolverResultElement::B_TYPE_UNINSTALL, package))) {
return B_NO_MEMORY;
}
} else {
BSolverPackage* package = _GetPackage(solvableId);
if (package == NULL)
return B_ERROR;
if (!_result.AppendElement(
BSolverResultElement(
BSolverResultElement::B_TYPE_INSTALL, package))) {
return B_NO_MEMORY;
}
}
}
return B_OK;
}
status_t
LibsolvSolver::_InitPool()
{
_CleanupPool();
fPool = pool_create();
pool_setdebuglevel(fPool, fDebugLevel);
{
const char* arch;
#ifdef HAIKU_TARGET_PLATFORM_HAIKU
#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
#else
arch = HAIKU_PACKAGING_ARCH;
#endif
pool_setarchpolicy(fPool, arch);
}
return B_OK;
}
status_t
LibsolvSolver::_InitJobQueue()
{
_CleanupJobQueue();
fJobs = new(std::nothrow) SolvQueue;
return fJobs != NULL ? B_OK : B_NO_MEMORY;;
}
void
LibsolvSolver::_InitSolver()
{
_CleanupSolver();
fSolver = solver_create(fPool);
solver_set_flag(fSolver, SOLVER_FLAG_SPLITPROVIDES, 1);
solver_set_flag(fSolver, SOLVER_FLAG_BEST_OBEY_POLICY, 1);
}
void
LibsolvSolver::_Cleanup()
{
_CleanupPool();
fInstalledRepository = NULL;
fRepositoryInfos.MakeEmpty();
}
void
LibsolvSolver::_CleanupPool()
{
_CleanupJobQueue();
fSolvablePackages.clear();
fPackageSolvables.clear();
int32 repositoryCount = fRepositoryInfos.CountItems();
for (int32 i = 0; i < repositoryCount; i++)
fRepositoryInfos.ItemAt(i)->SetSolvRepo(NULL);
if (fPool != NULL) {
pool_free(fPool);
fPool = NULL;
}
}
void
LibsolvSolver::_CleanupJobQueue()
{
_CleanupSolver();
delete fJobs;
fJobs = NULL;
}
void
LibsolvSolver::_CleanupSolver()
{
fProblems.MakeEmpty();
if (fSolver != NULL) {
solver_free(fSolver);
fSolver = NULL;
}
}
bool
LibsolvSolver::_HaveRepositoriesChanged() const
{
int32 repositoryCount = fRepositoryInfos.CountItems();
for (int32 i = 0; i < repositoryCount; i++) {
RepositoryInfo* repositoryInfo = fRepositoryInfos.ItemAt(i);
if (repositoryInfo->HasChanged())
return true;
}
return false;
}
status_t
LibsolvSolver::_AddRepositories()
{
if (fPool != NULL && !_HaveRepositoriesChanged())
return B_OK;
status_t error = _InitPool();
if (error != B_OK)
return error;
fInstalledRepository = NULL;
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 = -1 - 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);
try {
fSolvablePackages[solvableId] = package;
fPackageSolvables[package] = solvableId;
} catch (std::bad_alloc&) {
return B_NO_MEMORY;
}
}
repo_internalize(repo);
if (repository->IsInstalled()) {
fInstalledRepository = repositoryInfo;
pool_set_installed(fPool, repo);
}
repositoryInfo->SetUnchanged();
}
pool_createwhatprovides(fPool);
return B_OK;
}
LibsolvSolver::RepositoryInfo*
LibsolvSolver::_InstalledRepository() const
{
int32 repositoryCount = fRepositoryInfos.CountItems();
for (int32 i = 0; i < repositoryCount; i++) {
RepositoryInfo* repositoryInfo = fRepositoryInfos.ItemAt(i);
if (repositoryInfo->Repository()->IsInstalled())
return repositoryInfo;
}
return NULL;
}
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(Id solvableId) const
{
SolvableMap::const_iterator it = fSolvablePackages.find(solvableId);
return it != fSolvablePackages.end() ? it->second : NULL;
}
Id
LibsolvSolver::_GetSolvable(BSolverPackage* package) const
{
PackageMap::const_iterator it = fPackageSolvables.find(package);
return it != fPackageSolvables.end() ? it->second : 0;
}
status_t
LibsolvSolver::_AddSpecifiedPackages(
const BSolverPackageSpecifierList& packages,
const BSolverPackageSpecifier** _unmatched, int additionalFlags)
{
int32 packageCount = packages.CountSpecifiers();
for (int32 i = 0; i < packageCount; i++) {
const BSolverPackageSpecifier& specifier = *packages.SpecifierAt(i);
switch (specifier.Type()) {
case BSolverPackageSpecifier::B_UNSPECIFIED:
return B_BAD_VALUE;
case BSolverPackageSpecifier::B_PACKAGE:
{
BSolverPackage* package = specifier.Package();
Id solvableId;
if (package == NULL
|| (solvableId = _GetSolvable(package)) == 0) {
return B_BAD_VALUE;
}
queue_push2(fJobs, SOLVER_SOLVABLE, solvableId);
break;
}
case BSolverPackageSpecifier::B_SELECT_STRING:
{
SolvQueue matchingPackages;
int flags = SELECTION_NAME | SELECTION_PROVIDES | SELECTION_GLOB
| SELECTION_CANON | SELECTION_DOTARCH | SELECTION_REL
| additionalFlags;
selection_make(fPool, &matchingPackages,
specifier.SelectString().String(), flags);
if (matchingPackages.count == 0) {
if (_unmatched != NULL)
*_unmatched = &specifier;
return B_NAME_NOT_FOUND;
}
#if 0
if (BSolverRepository* repository = specifier.Repository()) {
RepositoryInfo* repositoryInfo
= _GetRepositoryInfo(repository);
if (repositoryInfo == NULL)
return B_BAD_VALUE;
SolvQueue repoFilter;
queue_push2(&repoFilter,
SOLVER_SOLVABLE_REPO
,
repositoryInfo->SolvRepo()->repoid);
selection_filter(fPool, &matchingPackages, &repoFilter);
if (matchingPackages.count == 0)
return B_NAME_NOT_FOUND;
}
#endif
for (int j = 0; j < matchingPackages.count; j++)
queue_push(fJobs, matchingPackages.elements[j]);
}
}
}
return B_OK;
}
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;
}
Problem* problem = new(std::nothrow) Problem(problemId, problemType,
sourcePackage, targetPackage, dependency);
if (problem == NULL || !fProblems.AddItem(problem)) {
delete problem;
return B_NO_MEMORY;
}
int solutionCount = solver_solution_count(fSolver, problemId);
for (Id solutionId = 1; solutionId <= solutionCount; solutionId++) {
status_t error = _AddSolution(problem, solutionId);
if (error != B_OK)
return error;
}
return B_OK;
}
status_t
LibsolvSolver::_AddSolution(Problem* problem, Id solutionId)
{
Solution* solution = new(std::nothrow) Solution(solutionId, problem);
if (solution == NULL || !problem->AppendSolution(solution)) {
delete solution;
return B_NO_MEMORY;
}
Id elementId = 0;
for (;;) {
Id sourceId;
Id targetId;
elementId = solver_next_solutionelement(fSolver, problem->Id(),
solutionId, elementId, &sourceId, &targetId);
if (elementId == 0)
break;
status_t error = _AddSolutionElement(solution, sourceId, targetId);
if (error != B_OK)
return error;
}
return B_OK;
}
status_t
LibsolvSolver::_AddSolutionElement(Solution* solution, Id sourceId, Id targetId)
{
typedef BSolverProblemSolutionElement Element;
if (sourceId == SOLVER_SOLUTION_JOB
|| sourceId == SOLVER_SOLUTION_POOLJOB) {
if (sourceId == SOLVER_SOLUTION_JOB)
targetId += fSolver->pooljobcnt;
Id how = fSolver->job.elements[targetId - 1];
Id what = fSolver->job.elements[targetId];
Id select = how & SOLVER_SELECTMASK;
switch (how & SOLVER_JOBMASK) {
case SOLVER_INSTALL:
if (select == SOLVER_SOLVABLE && fInstalledRepository != NULL
&& fPool->solvables[what].repo
== fInstalledRepository->SolvRepo()) {
return _AddSolutionElement(solution, Element::B_DONT_KEEP,
what, 0, NULL);
}
return _AddSolutionElement(solution,
Element::B_DONT_INSTALL, 0, 0,
solver_select2str(fPool, select, what));
case SOLVER_ERASE:
{
if (select == SOLVER_SOLVABLE
&& (fInstalledRepository == NULL
|| fPool->solvables[what].repo
!= fInstalledRepository->SolvRepo())) {
return _AddSolutionElement(solution,
Element::B_DONT_FORBID_INSTALLATION, what, 0, NULL);
}
Element::BType type = select == SOLVER_SOLVABLE_PROVIDES
? Element::B_DONT_DEINSTALL_ALL : Element::B_DONT_DEINSTALL;
return _AddSolutionElement(solution, type, 0, 0,
solver_select2str(fPool, select, what));
}
case SOLVER_UPDATE:
return _AddSolutionElement(solution,
Element::B_DONT_INSTALL_MOST_RECENT, 0, 0,
solver_select2str(fPool, select, what));
case SOLVER_LOCK:
return _AddSolutionElement(solution, Element::B_DONT_LOCK, 0, 0,
solver_select2str(fPool, select, what));
default:
return _AddSolutionElement(solution, Element::B_UNSPECIFIED, 0,
0, NULL);
}
}
Solvable* target = targetId != 0 ? fPool->solvables + targetId : NULL;
bool targetInstalled = target != NULL && fInstalledRepository
&& target->repo == fInstalledRepository->SolvRepo();
if (sourceId == SOLVER_SOLUTION_INFARCH) {
return _AddSolutionElement(solution,
targetInstalled
? Element::B_KEEP_INFERIOR_ARCHITECTURE
: Element::B_INSTALL_INFERIOR_ARCHITECTURE,
targetId, 0, NULL);
}
if (sourceId == SOLVER_SOLUTION_DISTUPGRADE) {
return _AddSolutionElement(solution,
targetInstalled
? Element::B_KEEP_EXCLUDED : Element::B_INSTALL_EXCLUDED,
targetId, 0, NULL);
}
if (sourceId == SOLVER_SOLUTION_BEST) {
return _AddSolutionElement(solution,
targetInstalled ? Element::B_KEEP_OLD : Element::B_INSTALL_OLD,
targetId, 0, NULL);
}
Solvable* source = fPool->solvables + sourceId;
if (target == NULL) {
return _AddSolutionElement(solution, Element::B_ALLOW_DEINSTALLATION,
sourceId, 0, NULL);
}
int illegalMask = policy_is_illegal(fSolver, source, target, 0);
if ((illegalMask & POLICY_ILLEGAL_DOWNGRADE) != 0) {
status_t error = _AddSolutionElement(solution,
Element::B_ALLOW_DOWNGRADE, sourceId, targetId, NULL);
if (error != B_OK)
return error;
}
if ((illegalMask & POLICY_ILLEGAL_NAMECHANGE) != 0) {
status_t error = _AddSolutionElement(solution,
Element::B_ALLOW_NAME_CHANGE, sourceId, targetId, NULL);
if (error != B_OK)
return error;
}
if ((illegalMask & POLICY_ILLEGAL_ARCHCHANGE) != 0) {
status_t error = _AddSolutionElement(solution,
Element::B_ALLOW_ARCHITECTURE_CHANGE, sourceId, targetId, NULL);
if (error != B_OK)
return error;
}
if ((illegalMask & POLICY_ILLEGAL_VENDORCHANGE) != 0) {
status_t error = _AddSolutionElement(solution,
Element::B_ALLOW_VENDOR_CHANGE, sourceId, targetId, NULL);
if (error != B_OK)
return error;
}
if (illegalMask == 0) {
return _AddSolutionElement(solution, Element::B_ALLOW_REPLACEMENT,
sourceId, targetId, NULL);
}
return B_OK;
}
status_t
LibsolvSolver::_AddSolutionElement(Solution* solution,
BSolverProblemSolutionElement::BType type, Id sourceSolvableId,
Id targetSolvableId, const char* selectionString)
{
BSolverPackage* sourcePackage = NULL;
if (sourceSolvableId != 0) {
sourcePackage = _GetPackage(sourceSolvableId);
if (sourcePackage == NULL)
return B_ERROR;
}
BSolverPackage* targetPackage = NULL;
if (targetSolvableId != 0) {
targetPackage = _GetPackage(targetSolvableId);
if (targetPackage == NULL)
return B_ERROR;
}
BString selection;
if (selectionString != NULL && selectionString[0] != '\0') {
selection = selectionString;
if (selection.IsEmpty())
return B_NO_MEMORY;
}
if (!solution->AppendElement(BSolverProblemSolutionElement(
type, sourcePackage, targetPackage, selection))) {
return B_NO_MEMORY;
}
return B_OK;
}
status_t
LibsolvSolver::_GetResolvableExpression(Id id,
BPackageResolvableExpression& _expression) const
{
if (!ISRELDEP(id)) {
_expression.SetTo(pool_id2str(fPool, id));
return B_OK;
}
Reldep* reldep = GETRELDEP(fPool, id);
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;
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;
}
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;
}
status_t
LibsolvSolver::_GetFoundPackages(SolvQueue& selection, uint32 flags,
BObjectList<BSolverPackage>& _packages)
{
SolvQueue solvables;
selection_solvables(fPool, &selection, &solvables);
for (int i = 0; i < solvables.count; i++) {
BSolverPackage* package = _GetPackage(solvables.elements[i]);
if (package == NULL)
return B_ERROR;
if ((flags & B_FIND_INSTALLED_ONLY) != 0
&& package->Repository() != fInstalledRepository->Repository()) {
continue;
}
if (!_packages.AddItem(package))
return B_NO_MEMORY;
}
return B_OK;
}
status_t
LibsolvSolver::_Solve()
{
if (fJobs == NULL || fSolver == NULL)
return B_BAD_VALUE;
int problemCount = solver_solve(fSolver, fJobs);
fProblems.MakeEmpty();
for (Id problemId = 1; problemId <= problemCount; problemId++) {
status_t error = _AddProblem(problemId);
if (error != B_OK)
return error;
}
return B_OK;
}
void
LibsolvSolver::_SetJobsSolverMode(int solverMode)
{
for (int i = 0; i < fJobs->count; i += 2)
fJobs->elements[i] |= solverMode;
}