* 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.
1282 lines
29 KiB
C++
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;
|
|
}
|