Files
haiku-beta6/src/kits/package/solver/LibsolvSolver.cpp
T
Ingo Weinhold 98c6dfa41e Switch build system from optional package to repositories
* Build libsolv and the dependency solver part of the package kit for
  the build platform.
* Add build tool get_package_dependencies. Given a list of package files
  and a list of repository files it determines the additional packages
  that need to be retrieved from the repositories and prints their URLs.
* Add rules to work with external repositories in the build system
  (build/jam/RepositoryRules):
  - PackageRepository declares an external repository with all its
    packages. The URL of the repository file isn't specified. It is
    computed from a given base URL and the SHA256 hash of the list of
    package files.
  - GeneratedRepositoryPackageList generates a file containing the file
    names of all packages in a repository.
  - IsPackageAvailable returns whether a package is available in any
    repository.
  - PackageURL returns the URL for a package.
* Declare the HaikuPorts repository for x86_gcc2
  (build/jam/repositories/HaikuPorts/x86_gcc2).
* Add rule AddHaikuImagePackages to add a package to the image and rule
  IsHaikuImagePackageAdded to determine whether a package has been
  added.
* OptionalPackages: Remove all entries that just downloaded and
  installed an external package. AddHaikuImagePackages can be used
  instead and is used in the remaining entries. Also move the remaining
  optional package dependency declarations from
  OptionalPackageDependencies here.
* ExtractBuildFeatureArchives: Instead of the URL parameter a package
  name must be specified now. This allows to simplify BuildFeatures
  significantly, since there's no dealing with URLs anymore. "if" out
  the entries that aren't supported yet.
* build_haiku_image: For the packages installed in system and common
  resolve their dependencies and download and install them as well.
2013-07-05 10:51:42 +02:00

1282 lines
29 KiB
C++

/*
* 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 <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>
// 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.
#ifdef HAIKU_TARGET_PLATFORM_HAIKU
BSolver*
BPackageKit::create_solver()
{
return new(std::nothrow) LibsolvSolver;
}
#else
extern "C" BSolver*
__create_libsolv_solver()
{
return new(std::nothrow) LibsolvSolver;
}
#endif
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;
};
// #pragma mark - LibsolvSolver
LibsolvSolver::LibsolvSolver()
:
fPool(NULL),
fSolver(NULL),
fJobs(NULL),
fRepositoryInfos(10, true),
fInstalledRepository(NULL),
fSolvablePackages(),
fPackageSolvables(),
fProblems(10, true)
{
}
LibsolvSolver::~LibsolvSolver()
{
_Cleanup();
}
status_t
LibsolvSolver::Init()
{
_Cleanup();
// We do all initialization lazily.
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::FindPackages(const char* searchString, uint32 flags,
BObjectList<BSolverPackage>& _packages)
{
// add repositories to pool
status_t error = _AddRepositories();
if (error != B_OK)
return error;
// create data iterator
int iteratorFlags = SEARCH_SUBSTRING;
if ((flags & B_FIND_CASE_INSENSITIVE) != 0)
iteratorFlags |= SEARCH_NOCASE;
SolvDataIterator iterator(fPool, 0, 0, 0, searchString, iteratorFlags);
SolvQueue selection;
// search package names
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);
}
// search package summaries
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);
}
// search package description
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);
}
// search package provides
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);
}
// get solvables
SolvQueue solvables;
selection_solvables(fPool, &selection, &solvables);
// get packages
for (int i = 0; i < solvables.count; i++) {
BSolverPackage* package = _GetPackage(solvables.elements[i]);
if (package == NULL)
return B_ERROR;
if (!_packages.AddItem(package))
return B_NO_MEMORY;
}
return B_OK;
}
status_t
LibsolvSolver::Install(const BSolverPackageSpecifierList& packages,
const BSolverPackageSpecifier** _unmatched)
{
if (_unmatched != NULL)
*_unmatched = NULL;
if (packages.IsEmpty())
return B_BAD_VALUE;
// add repositories to pool
status_t error = _AddRepositories();
if (error != B_OK)
return error;
// add the packages to install to the job queue
error = _InitJobQueue();
if (error != B_OK)
return error;
error = _AddSpecifiedPackages(packages, _unmatched, 0);
if (error != B_OK)
return error;
// set jobs' solver mode and solve
_SetJobsSolverMode(SOLVER_INSTALL);
_InitSolver();
return _Solve();
}
status_t
LibsolvSolver::Uninstall(const BSolverPackageSpecifierList& packages,
const BSolverPackageSpecifier** _unmatched)
{
if (_unmatched != NULL)
*_unmatched = NULL;
if (fInstalledRepository == NULL || packages.IsEmpty())
return B_BAD_VALUE;
// add repositories to pool
status_t error = _AddRepositories();
if (error != B_OK)
return error;
// add the packages to uninstall to the job queue
error = _InitJobQueue();
if (error != B_OK)
return error;
error = _AddSpecifiedPackages(packages, _unmatched,
SELECTION_INSTALLED_ONLY);
if (error != B_OK)
return error;
// set jobs' solver mode and solve
_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;
// add repositories to pool
status_t error = _AddRepositories();
if (error != B_OK)
return error;
// add the packages to update to the job queue -- if none are specified,
// update all
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;
}
// set jobs' solver mode and solve
_SetJobsSolverMode(SOLVER_UPDATE);
if (installNotYetInstalled) {
for (int i = 0; i < fJobs->count; i += 2) {
// change solver mode to SOLVER_INSTALL for empty update jobs
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::VerifyInstallation()
{
if (fInstalledRepository == NULL)
return B_BAD_VALUE;
// add repositories to pool
status_t error = _AddRepositories();
if (error != B_OK)
return error;
// add the verify job to the job queue
error = _InitJobQueue();
if (error != B_OK)
return error;
queue_push2(fJobs, SOLVER_SOLVABLE_ALL, 0);
// set jobs' solver mode and solve
_SetJobsSolverMode(SOLVER_VERIFY);
_InitSolver();
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;
// iterate through all problems and propagate the selected solutions
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> 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];
switch (transaction_type(transaction, solvableId,
SOLVER_TRANSACTION_RPM_ONLY)) {
case SOLVER_TRANSACTION_ERASE:
{
BSolverPackage* package = _GetPackage(solvableId);
if (package == NULL)
return B_ERROR;
if (!_result.AppendElement(
BSolverResultElement(
BSolverResultElement::B_TYPE_UNINSTALL, package))) {
return B_NO_MEMORY;
}
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(solvableId);
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;
}
status_t
LibsolvSolver::_InitPool()
{
_CleanupPool();
fPool = pool_create();
// Set the system architecture. We use what uname() returns unless we're on
// x86 gcc2.
{
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()
{
// clean up jobs and solver data
_CleanupJobQueue();
// clean up our data structures that depend on/refer to libsolv pool data
fSolvablePackages.clear();
fPackageSolvables.clear();
int32 repositoryCount = fRepositoryInfos.CountItems();
for (int32 i = 0; i < repositoryCount; i++)
fRepositoryInfos.ItemAt(i)->SetSolvRepo(NULL);
// delete the pool
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;
// something has changed -- re-create the pool
status_t error = _InitPool();
if (error != B_OK)
return error;
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())
pool_set_installed(fPool, repo);
repositoryInfo->SetUnchanged();
}
// create "provides" lookup
pool_createwhatprovides(fPool);
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(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:
{
// find matching packages
SolvQueue matchingPackages;
int flags = SELECTION_NAME | SELECTION_PROVIDES | SELECTION_GLOB
| SELECTION_CANON | SELECTION_DOTARCH | SELECTION_REL
| additionalFlags;
/*int matchFlags =*/ selection_make(fPool, &matchingPackages,
specifier.SelectString().String(), flags);
if (matchingPackages.count == 0) {
if (_unmatched != NULL)
*_unmatched = &specifier;
return B_NAME_NOT_FOUND;
}
// TODO: We might want to add support for restricting to certain repositories.
#if 0
// 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;
}
#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) {
// targetId is an index into the job queue
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);
}
// replace source with target
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
{
// 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;
}
status_t
LibsolvSolver::_Solve()
{
if (fJobs == NULL || fSolver == NULL)
return B_BAD_VALUE;
int problemCount = solver_solve(fSolver, fJobs);
// get the problems (if any)
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;
}