added 4 changesets to branch 'refs/remotes/HaikuPM-github/package-management' old head: 7c092d4d865eb53029c28fb5b1ddfbbada85899f new head: 2f028e9a2dc4b33ecac66d8fd491244a02d0f677 overview: https://github.com/haiku/HaikuPM/compare/7c092d4...2f028e9 ---------------------------------------------------------------------------- adc49c4: pkgman install: better out-of-memory error messages ec41ff5: pkgman install: Refresh repositories first 8e6c363: BSolver/LibsolvSolver: Add problem solution selection support ... and re-solving. 2f028e9: pkgman install: Add problem solution selection support [ Ingo Weinhold <ingo_weinhold@xxxxxx> ] ---------------------------------------------------------------------------- 4 files changed, 220 insertions(+), 48 deletions(-) headers/os/package/solver/Solver.h | 6 ++ src/bin/pkgman/command_install.cpp | 115 ++++++++++++++++----- src/kits/package/solver/LibsolvSolver.cpp | 137 +++++++++++++++++++++----- src/kits/package/solver/LibsolvSolver.h | 10 +- ############################################################################ Commit: adc49c411a101e1372d77d95c3473cdbd5dace06 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sat Apr 20 19:47:35 2013 UTC pkgman install: better out-of-memory error messages ---------------------------------------------------------------------------- diff --git a/src/bin/pkgman/command_install.cpp b/src/bin/pkgman/command_install.cpp index 743f19e..0e1c3e0 100644 --- a/src/bin/pkgman/command_install.cpp +++ b/src/bin/pkgman/command_install.cpp @@ -178,7 +178,7 @@ InstallCommand::Execute(int argc, const char* const* argv) for (int32 i = 0; i < repositoryNameCount; i++) { Repository* repository = new(std::nothrow) Repository; if (repository == NULL || !otherRepositories.AddItem(repository)) - DIE(B_NO_MEMORY, "out of memory"); + DIE(B_NO_MEMORY, "failed to create/add repository object"); const BString& name = repositoryNames.StringAt(i); error = repository->Init(roster, name); @@ -306,7 +306,7 @@ exit(1); BString url = repository->Config().BaseURL(); BString fileName(package->Info().CanonicalFileName()); if (fileName.IsEmpty()) - DIE(B_NO_MEMORY, "out of memory"); + DIE(B_NO_MEMORY, "failed to allocate file name"); url << '/' << fileName; BEntry entry; ############################################################################ Commit: ec41ff587ff27f05dd852cd58a91bcb70043be41 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sat Apr 20 22:36:31 2013 UTC pkgman install: Refresh repositories first ---------------------------------------------------------------------------- diff --git a/src/bin/pkgman/command_install.cpp b/src/bin/pkgman/command_install.cpp index 0e1c3e0..b89dd0e 100644 --- a/src/bin/pkgman/command_install.cpp +++ b/src/bin/pkgman/command_install.cpp @@ -15,6 +15,7 @@ #include <Directory.h> #include <package/DownloadFileRequest.h> #include <package/PackageRoster.h> +#include <package/RefreshRepositoryRequest.h> #include <package/RepositoryConfig.h> #include <package/solver/SolverPackageSpecifier.h> #include <package/solver/SolverPackageSpecifierList.h> @@ -65,8 +66,22 @@ struct Repository : public BSolverRepository { { } - status_t Init(BPackageRoster& roster, const char* name) + status_t Init(BPackageRoster& roster, BContext& context, const char* name) { + // get the repository config + status_t error = roster.GetRepositoryConfig(name, &fConfig); + if (error != B_OK) + return error; + + // refresh + BRefreshRepositoryRequest refreshRequest(context, fConfig); + error = refreshRequest.Process(); + if (error != B_OK) { + WARN(error, "refreshing repository \"%s\" failed", name); + return B_OK; + } + + // re-get the config return roster.GetRepositoryConfig(name, &fConfig); } @@ -119,8 +134,6 @@ InstallCommand::Execute(int argc, const char* const* argv) int packageCount = argc - optind; const char* const* packages = argv + optind; -// TODO: Refresh repositories. - // create the solver BSolver* solver; status_t error = BSolver::Create(solver); @@ -167,6 +180,10 @@ InstallCommand::Execute(int argc, const char* const* argv) } // other repositories + DecisionProvider decisionProvider; + JobStateListener listener; + BContext context(decisionProvider, listener); + BObjectList<Repository> otherRepositories(10, true); BPackageRoster roster; BStringList repositoryNames; @@ -181,7 +198,7 @@ InstallCommand::Execute(int argc, const char* const* argv) DIE(B_NO_MEMORY, "failed to create/add repository object"); const BString& name = repositoryNames.StringAt(i); - error = repository->Init(roster, name); + error = repository->Init(roster, context, name); if (error != B_OK) { WARN(error, "failed to get config for repository \"%s\". Skipping.", name.String()); @@ -268,7 +285,6 @@ exit(1); // other information) should, however, be provided by the repository cache in // some way. Extend BPackageInfo? Create a BPackageFileInfo? - DecisionProvider decisionProvider; if (!decisionProvider.YesNoDecisionNeeded(BString(), "Continue?", "y", "n", "y")) { return 1; @@ -287,9 +303,6 @@ exit(1); DIE(error, "failed to create transaction"); // download the new packages and prepare the transaction - JobStateListener listener; - BContext context(decisionProvider, listener); - for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i); i++) { BSolverPackage* package = element->Package(); ############################################################################ Commit: 8e6c3631a3e6deca3ca008bbce20bf30ec616d63 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sun Apr 21 00:46:26 2013 UTC BSolver/LibsolvSolver: Add problem solution selection support ... and re-solving. ---------------------------------------------------------------------------- diff --git a/headers/os/package/solver/Solver.h b/headers/os/package/solver/Solver.h index 90c0045..f72afff 100644 --- a/headers/os/package/solver/Solver.h +++ b/headers/os/package/solver/Solver.h @@ -17,6 +17,7 @@ class BSolverPackage; class BSolverPackageSpecifier; class BSolverPackageSpecifierList; class BSolverProblem; +class BSolverProblemSolution; class BSolverRepository; class BSolverResult; @@ -58,6 +59,11 @@ public: virtual int32 CountProblems() const = 0; virtual BSolverProblem* ProblemAt(int32 index) const = 0; + virtual status_t SelectProblemSolution( + BSolverProblem* problem, + const BSolverProblemSolution* solution) = 0; + virtual status_t SolveAgain() = 0; + virtual status_t GetResult(BSolverResult& _result) = 0; protected: diff --git a/src/kits/package/solver/LibsolvSolver.cpp b/src/kits/package/solver/LibsolvSolver.cpp index 460befd..2f02a27 100644 --- a/src/kits/package/solver/LibsolvSolver.cpp +++ b/src/kits/package/solver/LibsolvSolver.cpp @@ -118,7 +118,8 @@ struct LibsolvSolver::Problem : public BSolverProblem { const BPackageResolvableExpression& dependency) : BSolverProblem(type, sourcePackage, targetPackage, dependency), - fId(id) + fId(id), + fSelectedSolution(NULL) { } @@ -127,13 +128,24 @@ struct LibsolvSolver::Problem : public BSolverProblem { return fId; } + const Solution* SelectedSolution() const + { + return fSelectedSolution; + } + + void SetSelectedSolution(const Solution* solution) + { + fSelectedSolution = solution; + } + private: - ::Id fId; + ::Id fId; + const Solution* fSelectedSolution; }; struct LibsolvSolver::Solution : public BSolverProblemSolution { - Solution(::Id id, Problem* problem) + Solution(::Id id, LibsolvSolver::Problem* problem) : BSolverProblemSolution(), fId(id), @@ -146,9 +158,14 @@ struct LibsolvSolver::Solution : public BSolverProblemSolution { return fId; } + LibsolvSolver::Problem* Problem() const + { + return fProblem; + } + private: - ::Id fId; - Problem* fProblem; + ::Id fId; + LibsolvSolver::Problem* fProblem; }; @@ -159,6 +176,7 @@ LibsolvSolver::LibsolvSolver() : fPool(NULL), fSolver(NULL), + fJobs(NULL), fRepositoryInfos(10, true), fInstalledRepository(NULL), fSolvablePackages(), @@ -299,7 +317,9 @@ LibsolvSolver::Install(const BSolverPackageSpecifierList& packages, return error; // add the packages to install to the job queue - SolvQueue jobs; + error = _InitJobQueue(); + if (error != B_OK) + return error; int32 packageCount = packages.CountSpecifiers(); for (int32 i = 0; i < packageCount; i++) { @@ -317,7 +337,7 @@ LibsolvSolver::Install(const BSolverPackageSpecifierList& packages, return B_BAD_VALUE; } - queue_push2(&jobs, SOLVER_SOLVABLE, + queue_push2(fJobs, SOLVER_SOLVABLE, solvable - fPool->solvables); break; } @@ -359,15 +379,15 @@ LibsolvSolver::Install(const BSolverPackageSpecifierList& packages, #endif for (int j = 0; j < matchingPackages.count; j++) - queue_push(&jobs, matchingPackages.elements[j]); + queue_push(fJobs, matchingPackages.elements[j]); } } } // set jobs' solver mode and solve - _SetJobsSolverMode(jobs, SOLVER_INSTALL); + _SetJobsSolverMode(*fJobs, SOLVER_INSTALL); - return _Solve(jobs); + return _Solve(false); } @@ -383,13 +403,56 @@ LibsolvSolver::VerifyInstallation() return error; // add the verify job to the job queue - SolvQueue jobs; - queue_push2(&jobs, SOLVER_SOLVABLE_ALL, 0); + error = _InitJobQueue(); + if (error != B_OK) + return error; + + queue_push2(fJobs, SOLVER_SOLVABLE_ALL, 0); // set jobs' solver mode and solve - _SetJobsSolverMode(jobs, SOLVER_VERIFY); + _SetJobsSolverMode(*fJobs, SOLVER_VERIFY); + + return _Solve(false); +} + + +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; + } - return _Solve(jobs); + 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(true); } @@ -518,6 +581,16 @@ LibsolvSolver::_InitPool() } +status_t +LibsolvSolver::_InitJobQueue() +{ + _CleanupJobQueue(); + + fJobs = new(std::nothrow) SolvQueue; + return fJobs != NULL ? B_OK : B_NO_MEMORY;; +} + + void LibsolvSolver::_Cleanup() { @@ -532,8 +605,8 @@ LibsolvSolver::_Cleanup() void LibsolvSolver::_CleanupPool() { - // clean up solver data - _CleanupSolver(); + // clean up jobs and solver data + _CleanupJobQueue(); // clean up our data structures that depend on/refer to libsolv pool data fSolvablePackages.clear(); @@ -552,6 +625,16 @@ LibsolvSolver::_CleanupPool() void +LibsolvSolver::_CleanupJobQueue() +{ + _CleanupSolver(); + + delete fJobs; + fJobs = NULL; +} + + +void LibsolvSolver::_CleanupSolver() { fProblems.MakeEmpty(); @@ -1055,16 +1138,24 @@ LibsolvSolver::_GetResolvableExpression(Id id, status_t -LibsolvSolver::_Solve(Queue& jobs) +LibsolvSolver::_Solve(bool solveAgain) { - _CleanupSolver(); + if (fJobs == NULL) + return B_BAD_VALUE; + + if (solveAgain) { + if (fSolver == NULL) + return B_BAD_VALUE; + } else { + _CleanupSolver(); - // create the solver and solve - fSolver = solver_create(fPool); - solver_set_flag(fSolver, SOLVER_FLAG_SPLITPROVIDES, 1); - solver_set_flag(fSolver, SOLVER_FLAG_BEST_OBEY_POLICY, 1); + // create the solver and solve + fSolver = solver_create(fPool); + solver_set_flag(fSolver, SOLVER_FLAG_SPLITPROVIDES, 1); + solver_set_flag(fSolver, SOLVER_FLAG_BEST_OBEY_POLICY, 1); + } - int problemCount = solver_solve(fSolver, &jobs); + int problemCount = solver_solve(fSolver, fJobs); // get the problems (if any) fProblems.MakeEmpty(); diff --git a/src/kits/package/solver/LibsolvSolver.h b/src/kits/package/solver/LibsolvSolver.h index 09c6dba..a4dcdd7 100644 --- a/src/kits/package/solver/LibsolvSolver.h +++ b/src/kits/package/solver/LibsolvSolver.h @@ -48,6 +48,11 @@ public: virtual int32 CountProblems() const; virtual BSolverProblem* ProblemAt(int32 index) const; + virtual status_t SelectProblemSolution( + BSolverProblem* problem, + const BSolverProblemSolution* solution); + virtual status_t SolveAgain(); + virtual status_t GetResult(BSolverResult& _result); private: @@ -64,8 +69,10 @@ private: private: status_t _InitPool(); + status_t _InitJobQueue(); void _Cleanup(); void _CleanupPool(); + void _CleanupJobQueue(); void _CleanupSolver(); bool _HaveRepositoriesChanged() const; @@ -89,12 +96,13 @@ private: BPackageResolvableExpression& _expression) const; - status_t _Solve(Queue& jobs); + status_t _Solve(bool solveAgain); void _SetJobsSolverMode(Queue& jobs, int solverMode); private: Pool* fPool; Solver* fSolver; + SolvQueue* fJobs; RepositoryInfoList fRepositoryInfos; RepositoryInfo* fInstalledRepository; SolvableMap fSolvablePackages; ############################################################################ Commit: 2f028e9a2dc4b33ecac66d8fd491244a02d0f677 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sun Apr 21 00:47:51 2013 UTC pkgman install: Add problem solution selection support ---------------------------------------------------------------------------- diff --git a/src/bin/pkgman/command_install.cpp b/src/bin/pkgman/command_install.cpp index b89dd0e..0554cea 100644 --- a/src/bin/pkgman/command_install.cpp +++ b/src/bin/pkgman/command_install.cpp @@ -233,24 +233,60 @@ InstallCommand::Execute(int argc, const char* const* argv) int32 problemCount = solver->CountProblems(); for (int32 i = 0; i < problemCount; i++) { + // print problem and possible solutions BSolverProblem* problem = solver->ProblemAt(i); - printf(" %" B_PRId32 ": %s\n", i + 1, + printf("problem %" B_PRId32 ": %s\n", i + 1, problem->ToString().String()); int32 solutionCount = problem->CountSolutions(); for (int32 k = 0; k < solutionCount; k++) { const BSolverProblemSolution* solution = problem->SolutionAt(k); - printf(" solution %" B_PRId32 ":\n", k + 1); + printf(" solution %" B_PRId32 ":\n", k + 1); int32 elementCount = solution->CountElements(); for (int32 l = 0; l < elementCount; l++) { const BSolverProblemSolutionElement* element = solution->ElementAt(l); - printf(" - %s\n", element->ToString().String()); + printf(" - %s\n", element->ToString().String()); } } + + // let the user choose a solution + printf("Please select a solution, skip the problem for now or " + "quit.\n"); + for (;;) { + if (solutionCount > 1) + printf("select [1...%" B_PRId32 "/s/q]: ", solutionCount); + else + printf("select [1/s/q]: "); + + char buffer[32]; + if (fgets(buffer, sizeof(buffer), stdin) == NULL + || strcmp(buffer, "q\n") == 0) { + exit(1); + } + + if (strcmp(buffer, "s\n") == 0) + break; + + char* end; + long selected = strtol(buffer, &end, 0); + if (end == buffer || *end != '\n' || selected < 1 + || selected > solutionCount) { + printf("*** invalid input\n"); + continue; + } + + error = solver->SelectProblemSolution(problem, + problem->SolutionAt(selected - 1)); + if (error != B_OK) + DIE(error, "failed to set solution"); + break; + } } -// TODO: Allow the user to select solutions! -exit(1); + + error = solver->SolveAgain(); + if (error != B_OK) + DIE(error, "failed to compute packages to install"); } // print result @@ -259,27 +295,45 @@ exit(1); if (error != B_OK) DIE(error, "failed to compute packages to install"); - printf("The following changes will be made:\n"); + BObjectList<BSolverPackage> packagesToActivate; + BObjectList<BSolverPackage> packagesToDeactivate; + for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i); i++) { BSolverPackage* package = element->Package(); switch (element->Type()) { case BSolverResultElement::B_TYPE_INSTALL: - if (installedRepositories.HasItem(package->Repository())) - continue; - - printf(" install package %s from repository %s\n", - package->Info().CanonicalFileName().String(), - package->Repository()->Name().String()); + if (!installedRepositories.HasItem(package->Repository())) { + if (!packagesToActivate.AddItem(package)) + DIE(B_NO_MEMORY, "failed to add package to activate"); + } break; case BSolverResultElement::B_TYPE_UNINSTALL: - printf(" uninstall package %s\n", - package->VersionedName().String()); + if (!packagesToDeactivate.AddItem(package)) + DIE(B_NO_MEMORY, "failed to add package to deactivate"); break; } } + + if (packagesToActivate.IsEmpty() && packagesToDeactivate.IsEmpty()) { + printf("Nothing to do.\n"); + exit(0); + } + + printf("The following changes will be made:\n"); + for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i); + i++) { + printf(" install package %s from repository %s\n", + package->Info().CanonicalFileName().String(), + package->Repository()->Name().String()); + } + + for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i); + i++) { + printf(" uninstall package %s\n", package->VersionedName().String()); + } // TODO: Print file/download sizes. Unfortunately our package infos don't // contain the file size. Which is probably correct. The file size (and possibly // other information) should, however, be provided by the repository cache in