/* * Copyright 2013, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Ingo Weinhold */ #include "LibsolvSolver.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // 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::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), fDebugLevel(0) { } LibsolvSolver::~LibsolvSolver() { _Cleanup(); } status_t LibsolvSolver::Init() { _Cleanup(); // We do all initialization lazily. 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 the repository represents installed packages, check, if we already // have such a repository. if (repository->IsInstalled() && _InstalledRepository() != 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; } return B_OK; } status_t LibsolvSolver::FindPackages(const char* searchString, uint32 flags, BObjectList& _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); } // search package requires 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& _packages, const BSolverPackageSpecifier** _unmatched) { if (_unmatched != NULL) *_unmatched = NULL; if ((flags & B_FIND_INSTALLED_ONLY) != 0 && _InstalledRepository() == NULL) return B_BAD_VALUE; // add repositories to pool status_t error = _AddRepositories(); if (error != B_OK) return error; error = _InitJobQueue(); if (error != B_OK) return error; // add the package specifies to the job queue 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; // 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 (_InstalledRepository() == 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::FullSync() { // add repositories to pool status_t error = _AddRepositories(); if (error != B_OK) return error; // Init the job queue and specify that all packages shall be updated. error = _InitJobQueue(); if (error != B_OK) return error; queue_push2(fJobs, SOLVER_SOLVABLE_ALL, 0); // set jobs' solver mode and solve _SetJobsSolverMode(SOLVER_DISTUPGRADE); _InitSolver(); return _Solve(); } status_t LibsolvSolver::VerifyInstallation(uint32 flags) { if (_InstalledRepository() == 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(); 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); if (_solution == NULL) { problem->SetSelectedSolution(NULL); return B_OK; } const Solution* solution = static_cast(_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 transactionDeleter(transaction, &transaction_free); 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); // 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; 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(); } // create "provides" lookup 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: { // 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::_GetFoundPackages(SolvQueue& selection, uint32 flags, BObjectList& _packages) { // 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; // TODO: Fix handling of SELECTION_INSTALLED_ONLY in libsolv. Despite // passing the flag, we get solvables that aren't installed. 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); // 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; }