added 4 changesets to branch 'refs/remotes/HaikuPM-github/package-management' old head: 62f7022a8294bbd5407826c0bb8b071975ed90d5 new head: dd46d9816332a8936534aeeb26613c5269b75a6a overview: https://github.com/haiku/HaikuPM/compare/62f7022...dd46d98 ---------------------------------------------------------------------------- a78a254: LibsolvSolver: Fix the lazy re-/initialization * _Init() was a bit too enthusiastic, throwing really everything away. So, after calling it at the beginning of _AddRepositories() there wouldn't be any repositories anymore. * Rename _Init() to _InitPool() to make its purpose clearer. * Pull a _CleanupPool() out of _Cleanup() that only deletes the pool and anything depending on it. * RepositoryInfo::HasChanged(): Always consider changed when there's no libsolv repo yet. fc57db4: BSolver/LibsolvSolver: Add FindPackages() Given a search string it finds all matching packages. 7898675: pkgman RepositoryBuilder: Add BRepositoryConfig constructor dd46d98: pkgman: Add "search" command [ Ingo Weinhold <ingo_weinhold@xxxxxx> ] ---------------------------------------------------------------------------- 9 files changed, 390 insertions(+), 11 deletions(-) headers/os/package/solver/Solver.h | 14 ++ src/bin/pkgman/Jamfile | 1 + src/bin/pkgman/RepositoryBuilder.cpp | 13 ++ src/bin/pkgman/RepositoryBuilder.h | 2 + src/bin/pkgman/command_search.cpp | 247 ++++++++++++++++++++++++++ src/bin/pkgman/pkgman.cpp | 7 +- src/bin/pkgman/pkgman.h | 3 +- src/kits/package/solver/LibsolvSolver.cpp | 106 ++++++++++- src/kits/package/solver/LibsolvSolver.h | 8 +- ############################################################################ Commit: a78a2540a820ca541177fce1d454d3aa7073715c Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Thu Apr 11 15:21:27 2013 UTC LibsolvSolver: Fix the lazy re-/initialization * _Init() was a bit too enthusiastic, throwing really everything away. So, after calling it at the beginning of _AddRepositories() there wouldn't be any repositories anymore. * Rename _Init() to _InitPool() to make its purpose clearer. * Pull a _CleanupPool() out of _Cleanup() that only deletes the pool and anything depending on it. * RepositoryInfo::HasChanged(): Always consider changed when there's no libsolv repo yet. ---------------------------------------------------------------------------- diff --git a/src/kits/package/solver/LibsolvSolver.cpp b/src/kits/package/solver/LibsolvSolver.cpp index af536af..2a2926f 100644 --- a/src/kits/package/solver/LibsolvSolver.cpp +++ b/src/kits/package/solver/LibsolvSolver.cpp @@ -62,7 +62,7 @@ struct LibsolvSolver::RepositoryInfo { : fRepository(repository), fSolvRepo(NULL), - fChangeCount(repository->ChangeCount() - 1) + fChangeCount(repository->ChangeCount()) { } @@ -83,7 +83,7 @@ struct LibsolvSolver::RepositoryInfo { bool HasChanged() const { - return fChangeCount != fRepository->ChangeCount(); + return fChangeCount != fRepository->ChangeCount() || fSolvRepo == NULL; } void SetUnchanged() @@ -162,6 +162,8 @@ LibsolvSolver::~LibsolvSolver() status_t LibsolvSolver::Init() { + _Cleanup(); + // We do all initialization lazily. return B_OK; } @@ -370,9 +372,9 @@ LibsolvSolver::GetResult(BSolverResult& _result) status_t -LibsolvSolver::_Init() +LibsolvSolver::_InitPool() { - _Cleanup(); + _CleanupPool(); fPool = pool_create(); @@ -403,12 +405,28 @@ LibsolvSolver::_Init() void LibsolvSolver::_Cleanup() { - _CleanupSolver(); + _CleanupPool(); - fSolvablePackages.clear(); fInstalledRepository = NULL; fRepositoryInfos.MakeEmpty(); +} + + +void +LibsolvSolver::_CleanupPool() +{ + // clean up solver data + _CleanupSolver(); + + // clean up our data structures that depend on/refer to libsolv pool data + fSolvablePackages.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; @@ -449,7 +467,7 @@ LibsolvSolver::_AddRepositories() return B_OK; // something has changed -- re-create the pool - status_t error = _Init(); + status_t error = _InitPool(); if (error != B_OK) return error; diff --git a/src/kits/package/solver/LibsolvSolver.h b/src/kits/package/solver/LibsolvSolver.h index 81f99eb..5ae1602 100644 --- a/src/kits/package/solver/LibsolvSolver.h +++ b/src/kits/package/solver/LibsolvSolver.h @@ -55,8 +55,9 @@ private: typedef std::map<Solvable*, BSolverPackage*> SolvableMap; private: - status_t _Init(); + status_t _InitPool(); void _Cleanup(); + void _CleanupPool(); void _CleanupSolver(); bool _HaveRepositoriesChanged() const; ############################################################################ Commit: fc57db481fa36695457bc355ca9710c91d53ab1d Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Thu Apr 11 15:30:08 2013 UTC BSolver/LibsolvSolver: Add FindPackages() Given a search string it finds all matching packages. ---------------------------------------------------------------------------- diff --git a/headers/os/package/solver/Solver.h b/headers/os/package/solver/Solver.h index 2f1160b..5403baa 100644 --- a/headers/os/package/solver/Solver.h +++ b/headers/os/package/solver/Solver.h @@ -6,12 +6,14 @@ #define _PACKAGE__SOLVER_H_ +#include <ObjectList.h> #include <SupportDefs.h> namespace BPackageKit { +class BSolverPackage; class BSolverPackageSpecifierList; class BSolverProblem; class BSolverRepository; @@ -20,6 +22,14 @@ class BSolverResult; class BSolver { public: + // FindPackages() flags + enum { + B_FIND_CASE_INSENSITIVE = 0x01, + B_FIND_IN_SUMMARY = 0x02, + B_FIND_IN_DESCRIPTION = 0x04 + }; + +public: virtual ~BSolver(); static status_t Create(BSolver*& _solver); @@ -29,6 +39,10 @@ public: virtual status_t AddRepository( BSolverRepository* repository) = 0; + virtual status_t FindPackages(const char* searchString, + uint32 flags, + BObjectList<BSolverPackage>& _packages) = 0; + virtual status_t Install( const BSolverPackageSpecifierList& packages) = 0; diff --git a/src/kits/package/solver/LibsolvSolver.cpp b/src/kits/package/solver/LibsolvSolver.cpp index 2a2926f..a1ab43c 100644 --- a/src/kits/package/solver/LibsolvSolver.cpp +++ b/src/kits/package/solver/LibsolvSolver.cpp @@ -57,6 +57,20 @@ struct LibsolvSolver::SolvQueue : Queue { }; +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) : @@ -198,6 +212,66 @@ LibsolvSolver::AddRepository(BSolverRepository* repository) 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); + + // search package names + dataiterator_set_keyname(&iterator, SOLVABLE_NAME); + dataiterator_set_search(&iterator, 0, 0); + + SolvQueue selection; + 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); + } + + // 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) { if (packages.IsEmpty()) diff --git a/src/kits/package/solver/LibsolvSolver.h b/src/kits/package/solver/LibsolvSolver.h index 5ae1602..eaecd8f 100644 --- a/src/kits/package/solver/LibsolvSolver.h +++ b/src/kits/package/solver/LibsolvSolver.h @@ -34,6 +34,10 @@ public: virtual status_t AddRepository(BSolverRepository* repository); + virtual status_t FindPackages(const char* searchString, + uint32 flags, + BObjectList<BSolverPackage>& _packages); + virtual status_t Install( const BSolverPackageSpecifierList& packages); @@ -46,6 +50,7 @@ public: private: struct SolvQueue; + struct SolvDataIterator; struct RepositoryInfo; struct Problem; struct Solution; ############################################################################ Commit: 789867563ad6e4730b470bfe2b8b7314aa671756 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Thu Apr 11 15:32:17 2013 UTC pkgman RepositoryBuilder: Add BRepositoryConfig constructor ---------------------------------------------------------------------------- diff --git a/src/bin/pkgman/RepositoryBuilder.cpp b/src/bin/pkgman/RepositoryBuilder.cpp index a307533..1b7e0e9 100644 --- a/src/bin/pkgman/RepositoryBuilder.cpp +++ b/src/bin/pkgman/RepositoryBuilder.cpp @@ -34,6 +34,19 @@ RepositoryBuilder::RepositoryBuilder(BSolverRepository& repository, } +RepositoryBuilder::RepositoryBuilder(BSolverRepository& repository, + const BRepositoryConfig& config) + : + fRepository(repository), + fErrorName(fRepository.Name()), + fPackagePaths(NULL) +{ + status_t error = fRepository.SetTo(config); + if (error != B_OK) + DIE(error, "failed to init %s repository", fErrorName.String()); +} + + RepositoryBuilder& RepositoryBuilder::SetPackagePathMap(PackagePathMap* packagePaths) { diff --git a/src/bin/pkgman/RepositoryBuilder.h b/src/bin/pkgman/RepositoryBuilder.h index 0b9e72a..d3c72ad 100644 --- a/src/bin/pkgman/RepositoryBuilder.h +++ b/src/bin/pkgman/RepositoryBuilder.h @@ -31,6 +31,8 @@ public: RepositoryBuilder(BSolverRepository& repository, const BString& name, const BString& errorName = BString()); + RepositoryBuilder(BSolverRepository& repository, + const BRepositoryConfig& config); RepositoryBuilder& SetPackagePathMap(PackagePathMap* packagePaths); ############################################################################ Commit: dd46d9816332a8936534aeeb26613c5269b75a6a Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Thu Apr 11 15:33:23 2013 UTC pkgman: Add "search" command ---------------------------------------------------------------------------- diff --git a/src/bin/pkgman/Jamfile b/src/bin/pkgman/Jamfile index db1206a..de9b873 100644 --- a/src/bin/pkgman/Jamfile +++ b/src/bin/pkgman/Jamfile @@ -8,6 +8,7 @@ BinCommand pkgman : command_list_repos.cpp command_refresh.cpp command_resolve_dependencies.cpp + command_search.cpp DecisionProvider.cpp JobStateListener.cpp PackageInfoErrorListener.cpp diff --git a/src/bin/pkgman/command_search.cpp b/src/bin/pkgman/command_search.cpp new file mode 100644 index 0000000..069489e --- /dev/null +++ b/src/bin/pkgman/command_search.cpp @@ -0,0 +1,247 @@ +/* + * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold <ingo_weinhold@xxxxxx> + */ + + +#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <unistd.h> + +#include <package/PackageRoster.h> +#include <package/RepositoryConfig.h> + +#include <AutoDeleter.h> + +#include "pkgman.h" +#include "RepositoryBuilder.h" + + +// TODO: internationalization! +// The printing code will need serious attention wrt. dealing with UTF-8 and, +// even worse, full-width characters. + + +using namespace BPackageKit; + + +typedef std::map<BSolverPackage*, BString> PackagePathMap; + + +static const char* kCommandUsage = + "Usage: %s search <search-string>\n" + "Searches for packages matching <search-string>.\n" + "\n" + "Options:\n" + " -i, --installed-only\n" + " Only find installed packages.\n" + " -u, --uninstalled-only\n" + " Only find not installed packages.\n" + "\n" +; + + +static void +print_command_usage_and_exit(bool error) +{ + fprintf(error ? stderr : stdout, kCommandUsage, kProgramName); + exit(error ? 1 : 0); +} + + +static int +get_terminal_width() +{ + int fd = fileno(stdout); + struct winsize windowSize; + if (isatty(fd) == 1 && ioctl(fd, TIOCGWINSZ, &windowSize) == 0) + return windowSize.ws_col; + + return INT_MAX; +} + + +int +command_search(int argc, const char* const* argv) +{ + bool installedOnly = false; + bool uninstalledOnly = false; + + while (true) { + static struct option sLongOptions[] = { + { "help", no_argument, 0, 'h' }, + { "installed-only", no_argument, 0, 'i' }, + { "uninstalled-only", no_argument, 0, 'u' }, + { 0, 0, 0, 0 } + }; + + opterr = 0; // don't print errors + int c = getopt_long(argc, (char**)argv, "hu", sLongOptions, NULL); + if (c == -1) + break; + + switch (c) { + case 'h': + print_command_usage_and_exit(false); + break; + + case 'i': + installedOnly = true; + uninstalledOnly = false; + break; + + case 'u': + uninstalledOnly = true; + installedOnly = false; + break; + + default: + print_command_usage_and_exit(true); + break; + } + } + + // The remaining argument is the search string. + if (argc != optind + 1) + print_command_usage_and_exit(true); + + const char* searchString = argv[optind++]; + + // create the solver + BSolver* solver; + status_t error = BSolver::Create(solver); + if (error != B_OK) + DIE(error, "failed to create solver"); + + // add repositories + + // installed + BSolverRepository systemRepository; + BSolverRepository commonRepository; + BSolverRepository homeRepository; + if (!uninstalledOnly) { + RepositoryBuilder(systemRepository, "system") + .AddPackages(B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, "system") + .AddToSolver(solver, false); + RepositoryBuilder(commonRepository, "common") + .AddPackages(B_PACKAGE_INSTALLATION_LOCATION_COMMON, "common") + .AddToSolver(solver, false); +// RepositoryBuilder(homeRepository, "home") +// .AddPackages(B_PACKAGE_INSTALLATION_LOCATION_HOME, "home") +// .AddToSolver(solver, false); + } + + // not installed + BObjectList<BSolverRepository> uninstalledRepositories(10, true); + + if (!installedOnly) { + BPackageRoster roster; + BStringList repositoryNames; + error = roster.GetRepositoryNames(repositoryNames); + if (error != B_OK) + WARN(error, "failed to get repository names"); + + int32 repositoryNameCount = repositoryNames.CountStrings(); + for (int32 i = 0; i < repositoryNameCount; i++) { + const BString& name = repositoryNames.StringAt(i); + BRepositoryConfig config; + error = roster.GetRepositoryConfig(name, &config); + if (error != B_OK) { + WARN(error, "failed to get config for repository \"%s\". " + "Skipping.", name.String()); + continue; + } + + BSolverRepository* repository = new(std::nothrow) BSolverRepository; + if (repository == NULL + || !uninstalledRepositories.AddItem(repository)) { + DIE(B_NO_MEMORY, "out of memory"); + } + + RepositoryBuilder(*repository, config) + .AddToSolver(solver, false); + } + } + + // search + BObjectList<BSolverPackage> packages; + error = solver->FindPackages(searchString, + BSolver::B_FIND_CASE_INSENSITIVE | BSolver::B_FIND_IN_SUMMARY + | BSolver::B_FIND_IN_DESCRIPTION, packages); + if (error != B_OK) + DIE(error, "searching packages failed"); + + if (packages.IsEmpty()) { + printf("No matching packages found.\n"); + return 0; + } + + // print table + + // determine column widths + BString installedColumnTitle("Installed"); + BString nameColumnTitle("Name"); + BString descriptionColumnTitle("Description"); + + int installedColumnWidth = installedColumnTitle.Length(); + int nameColumnWidth = nameColumnTitle.Length(); + int descriptionColumnWidth = descriptionColumnTitle.Length(); + + int32 packageCount = packages.CountItems(); + for (int32 i = 0; i < packageCount; i++) { + BSolverPackage* package = packages.ItemAt(i); + nameColumnWidth = std::max(nameColumnWidth, + (int)package->Name().Length()); + descriptionColumnWidth = std::max(descriptionColumnWidth, + (int)package->Info().Summary().Length()); + } + + // print header + BString header; + header.SetToFormat("%-*s %-*s %s", + installedColumnWidth, installedColumnTitle.String(), + nameColumnWidth, nameColumnTitle.String(), + descriptionColumnTitle.String()); + printf("%s\n", header.String()); + + int minLineWidth = header.Length(); + int lineWidth = minLineWidth + descriptionColumnWidth + - descriptionColumnTitle.Length(); + int terminalWidth = get_terminal_width(); + if (lineWidth > terminalWidth) { + // truncate description + int actualLineWidth = std::max(minLineWidth, terminalWidth); + descriptionColumnWidth -= lineWidth - actualLineWidth; + lineWidth = actualLineWidth; + } + + header.SetTo('-', lineWidth); + printf("%s\n", header.String()); + + // print packages + for (int32 i = 0; i < packageCount; i++) { + BSolverPackage* package = packages.ItemAt(i); + + const char* installed = ""; + if (package->Repository() == &systemRepository) + installed = "system"; + else if (package->Repository() == &commonRepository) + installed = "common"; + else if (package->Repository() == &homeRepository) + installed = "home"; + + printf("%-*s %-*s %-*.*s\n", + installedColumnWidth, installed, + nameColumnWidth, package->Name().String(), + descriptionColumnWidth, descriptionColumnWidth, + package->Info().Summary().String()); + } + + return 0; +} diff --git a/src/bin/pkgman/pkgman.cpp b/src/bin/pkgman/pkgman.cpp index 6942008..6611700 100644 --- a/src/bin/pkgman/pkgman.cpp +++ b/src/bin/pkgman/pkgman.cpp @@ -36,6 +36,9 @@ static const char* kUsage = " resolve-dependencies <package> <repository> [ <priority> ] ...\n" " Resolves all packages a given package depends on.\n" "\n" + " search <search-string>\n" + " Searches for packages matching <search-string>.\n" + "\n" "Common Options:\n" " -h, --help - Print this usage info.\n" ; @@ -71,8 +74,8 @@ main(int argc, const char* const* argv) if (strcmp(command, "resolve-dependencies") == 0) return command_resolve_dependencies(argc - 1, argv + 1); -// if (strcmp(command, "search") == 0) -// return command_search(argc - 1, argv + 1); + if (strcmp(command, "search") == 0) + return command_search(argc - 1, argv + 1); if (strcmp(command, "help") == 0) print_usage_and_exit(false); diff --git a/src/bin/pkgman/pkgman.h b/src/bin/pkgman/pkgman.h index 9808903..c2a2fe9 100644 --- a/src/bin/pkgman/pkgman.h +++ b/src/bin/pkgman/pkgman.h @@ -35,10 +35,11 @@ do { \ void print_usage_and_exit(bool error); int command_add_repo(int argc, const char* const* argv); -int command_resolve_dependencies(int argc, const char* const* argv); int command_drop_repo(int argc, const char* const* argv); int command_list_repos(int argc, const char* const* argv); int command_refresh(int argc, const char* const* argv); +int command_resolve_dependencies(int argc, const char* const* argv); +int command_search(int argc, const char* const* argv); #endif // PKGMAN_H