added 6 changesets to branch 'refs/remotes/HaikuPM-github/package-management' old head: 2f028e9a2dc4b33ecac66d8fd491244a02d0f677 new head: df2685d885f3b940bc04b1cf5f39511ce6750ddf overview: https://github.com/haiku/HaikuPM/compare/2f028e9...df2685d ---------------------------------------------------------------------------- 7466fa2: pkgman install: Simplify transaction creation c128275: <Archivable.h>: Fix conflict with BPackageKit::BPrivate ... when "using BPackageKit::BPrivate". 3ac0de3: pkgman: Refactoring -> PackageManager Move common and reusable functionality from "search" and "install" to new PackageManager class. de62d76: BSolver/LibsolvSolver: Add Uninstall() Also fix incorrect check in LibsolvSolver::GetResult(). 8ba41d6: pkgman install: Fix typo in usage text df2685d: pkgman: Add "uninstall" command [ Ingo Weinhold <ingo_weinhold@xxxxxx> ] ---------------------------------------------------------------------------- 10 files changed, 741 insertions(+), 440 deletions(-) headers/os/package/solver/Solver.h | 7 +- headers/os/support/Archivable.h | 4 +- src/bin/pkgman/Jamfile | 2 + src/bin/pkgman/PackageManager.cpp | 426 ++++++++++++++++++++++++++ src/bin/pkgman/PackageManager.h | 96 ++++++ src/bin/pkgman/command_install.cpp | 351 +-------------------- src/bin/pkgman/command_search.cpp | 75 +---- src/bin/pkgman/command_uninstall.cpp | 91 ++++++ src/kits/package/solver/LibsolvSolver.cpp | 119 +++++-- src/kits/package/solver/LibsolvSolver.h | 10 +- ############################################################################ Commit: 7466fa2d4901461dea3fc89a9229e0e04707124a Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sun Apr 21 08:36:19 2013 UTC pkgman install: Simplify transaction creation ---------------------------------------------------------------------------- diff --git a/src/bin/pkgman/command_install.cpp b/src/bin/pkgman/command_install.cpp index 0554cea..1bcbeff 100644 --- a/src/bin/pkgman/command_install.cpp +++ b/src/bin/pkgman/command_install.cpp @@ -357,54 +357,44 @@ InstallCommand::Execute(int argc, const char* const* argv) DIE(error, "failed to create transaction"); // download the new packages and prepare the transaction - 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; - - // get package URL and target entry - Repository* repository - = static_cast<Repository*>(package->Repository()); - BString url = repository->Config().BaseURL(); - BString fileName(package->Info().CanonicalFileName()); - if (fileName.IsEmpty()) - DIE(B_NO_MEMORY, "failed to allocate file name"); - url << '/' << fileName; - - BEntry entry; - error = entry.SetTo(&transactionDirectory, fileName); - if (error != B_OK) - DIE(error, "failed to create package entry"); + for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i); + i++) { + // get package URL and target entry + Repository* repository + = static_cast<Repository*>(package->Repository()); + BString url = repository->Config().BaseURL(); + BString fileName(package->Info().CanonicalFileName()); + if (fileName.IsEmpty()) + DIE(B_NO_MEMORY, "failed to allocate file name"); + url << '/' << fileName; + + BEntry entry; + error = entry.SetTo(&transactionDirectory, fileName); + if (error != B_OK) + DIE(error, "failed to create package entry"); - // download the package - DownloadFileRequest downloadRequest(context, url, entry, - package->Info().Checksum()); - error = downloadRequest.Process(); - if (error != B_OK) - DIE(error, "failed to download package"); + // download the package + DownloadFileRequest downloadRequest(context, url, entry, + package->Info().Checksum()); + error = downloadRequest.Process(); + if (error != B_OK) + DIE(error, "failed to download package"); - // add package to transaction - if (!transaction.AddPackageToActivate( - package->Info().CanonicalFileName())) { - DIE(B_NO_MEMORY, - "failed to add package to activate to transaction"); - } - break; - } + // add package to transaction + if (!transaction.AddPackageToActivate( + package->Info().CanonicalFileName())) { + DIE(B_NO_MEMORY, + "failed to add package to activate to transaction"); + } + } - case BSolverResultElement::B_TYPE_UNINSTALL: - // add package to transaction - if (!transaction.AddPackageToDeactivate( - package->Info().CanonicalFileName())) { - DIE(B_NO_MEMORY, - "failed to add package to deactivate to transaction"); - } - break; + for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i); + i++) { + // add package to transaction + if (!transaction.AddPackageToDeactivate( + package->Info().CanonicalFileName())) { + DIE(B_NO_MEMORY, + "failed to add package to deactivate to transaction"); } } ############################################################################ Commit: c128275ede0f0441de54a0f0c8bab9af7ea7460d Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sun Apr 21 10:29:41 2013 UTC <Archivable.h>: Fix conflict with BPackageKit::BPrivate ... when "using BPackageKit::BPrivate". ---------------------------------------------------------------------------- diff --git a/headers/os/support/Archivable.h b/headers/os/support/Archivable.h index 076a2a8..88fe3b8 100644 --- a/headers/os/support/Archivable.h +++ b/headers/os/support/Archivable.h @@ -21,8 +21,8 @@ namespace Archiving { } } -using BPrivate::Archiving::BArchiveManager; -using BPrivate::Archiving::BUnarchiveManager; +using ::BPrivate::Archiving::BArchiveManager; +using ::BPrivate::Archiving::BUnarchiveManager; class BArchivable { ############################################################################ Commit: 3ac0de3b1fac18912d6cb7758b502468e3802ee9 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sun Apr 21 10:31:29 2013 UTC pkgman: Refactoring -> PackageManager Move common and reusable functionality from "search" and "install" to new PackageManager class. ---------------------------------------------------------------------------- diff --git a/src/bin/pkgman/Jamfile b/src/bin/pkgman/Jamfile index cb8328a..4669d21 100644 --- a/src/bin/pkgman/Jamfile +++ b/src/bin/pkgman/Jamfile @@ -14,6 +14,7 @@ BinCommand pkgman : DecisionProvider.cpp JobStateListener.cpp PackageInfoErrorListener.cpp + PackageManager.cpp pkgman.cpp RepositoryBuilder.cpp : diff --git a/src/bin/pkgman/PackageManager.cpp b/src/bin/pkgman/PackageManager.cpp new file mode 100644 index 0000000..e099995 --- /dev/null +++ b/src/bin/pkgman/PackageManager.cpp @@ -0,0 +1,396 @@ +/* + * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold <ingo_weinhold@xxxxxx> + */ + + +#include "PackageManager.h" + +#include <Directory.h> +#include <package/DownloadFileRequest.h> +#include <package/PackageRoster.h> +#include <package/RefreshRepositoryRequest.h> +#include <package/solver/SolverPackage.h> +#include <package/solver/SolverPackageSpecifier.h> +#include <package/solver/SolverPackageSpecifierList.h> +#include <package/solver/SolverProblem.h> +#include <package/solver/SolverProblemSolution.h> +#include <package/solver/SolverResult.h> + +#include <package/ActivationTransaction.h> +#include <package/DaemonClient.h> + +#include "pkgman.h" +#include "RepositoryBuilder.h" + + +using namespace BPackageKit::BPrivate; + + +// #pragma mark - Repository + + +PackageManager::Repository::Repository() + : + BSolverRepository() +{ +} + + +status_t +PackageManager::Repository::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); +} + + +const BRepositoryConfig& +PackageManager::Repository::Config() const +{ + return fConfig; +} + + +// #pragma mark - Solver + + +PackageManager::PackageManager(BPackageInstallationLocation location, + bool addInstalledRepositories, bool addOtherRepositories) + : + fLocation(location), + fSolver(NULL), + fSystemRepository(), + fCommonRepository(), + fHomeRepository(), + fInstalledRepositories(10), + fOtherRepositories(10, true), + fDecisionProvider(), + fJobStateListener(), + fContext(fDecisionProvider, fJobStateListener) +{ + // create the solver + status_t error = BSolver::Create(fSolver); + if (error != B_OK) + DIE(error, "failed to create solver"); + + // add installation location repositories + if (addInstalledRepositories) { + // We add only the repository of our actual installation location as the + // "installed" repository. The repositories for the more general + // installation locations are added as regular repositories, but with + // better priorities than the actual (remote) repositories. This + // prevents the solver from showing conflicts when a package in a more + // specific installation location overrides a package in a more general + // one. Instead any requirement that is already installed in a more + // general installation location will turn up as to be installed as + // well. But we can easily filter those out. + RepositoryBuilder(fSystemRepository, "system") + .AddPackages(B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, "system") + .AddToSolver(fSolver, false); + fSystemRepository.SetPriority(-1); + + bool installInHome = location == B_PACKAGE_INSTALLATION_LOCATION_HOME; + RepositoryBuilder(fCommonRepository, "common") + .AddPackages(B_PACKAGE_INSTALLATION_LOCATION_COMMON, "common") + .AddToSolver(fSolver, !installInHome); + + if (!fInstalledRepositories.AddItem(&fSystemRepository) + || !fInstalledRepositories.AddItem(&fCommonRepository)) { + DIE(B_NO_MEMORY, "failed to add installed repositories to list"); + } + + if (installInHome) { + fCommonRepository.SetPriority(-2); + RepositoryBuilder(fHomeRepository, "home") + .AddPackages(B_PACKAGE_INSTALLATION_LOCATION_HOME, "home") + .AddToSolver(fSolver, true); + + if (!fInstalledRepositories.AddItem(&fHomeRepository)) + DIE(B_NO_MEMORY, "failed to add home repository to list"); + } + } + + // add other repositories + if (addOtherRepositories) { + 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++) { + Repository* repository = new(std::nothrow) Repository; + if (repository == NULL || !fOtherRepositories.AddItem(repository)) + DIE(B_NO_MEMORY, "failed to create/add repository object"); + + const BString& name = repositoryNames.StringAt(i); + error = repository->Init(roster, fContext, name); + if (error != B_OK) { + WARN(error, + "failed to get config for repository \"%s\". Skipping.", + name.String()); + fOtherRepositories.RemoveItem(repository, true); + continue; + } + + RepositoryBuilder(*repository, repository->Config()) + .AddToSolver(fSolver, false); + } + } +} + + +PackageManager::~PackageManager() +{ +} + + +void +PackageManager::Install(const char* const* packages, int packageCount) +{ + // solve + BSolverPackageSpecifierList packagesToInstall; + for (int i = 0; i < packageCount; i++) { + if (!packagesToInstall.AppendSpecifier(packages[i])) + DIE(B_NO_MEMORY, "failed to add specified package"); + } + + const BSolverPackageSpecifier* unmatchedSpecifier; + status_t error = fSolver->Install(packagesToInstall, &unmatchedSpecifier); + if (error != B_OK) { + if (unmatchedSpecifier != NULL) { + DIE(error, "failed to find a match for \"%s\"", + unmatchedSpecifier->SelectString().String()); + } else + DIE(error, "failed to compute packages to install"); + } + + _HandleProblems(); + + // install/uninstall packages + _AnalyzeResult(); + _PrintResult(); + _ApplyPackageChanges(); +} + + +void +PackageManager::_HandleProblems() +{ + while (fSolver->HasProblems()) { + printf("Encountered problems:\n"); + + int32 problemCount = fSolver->CountProblems(); + for (int32 i = 0; i < problemCount; i++) { + // print problem and possible solutions + BSolverProblem* problem = fSolver->ProblemAt(i); + 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); + int32 elementCount = solution->CountElements(); + for (int32 l = 0; l < elementCount; l++) { + const BSolverProblemSolutionElement* element + = solution->ElementAt(l); + 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; + } + + status_t error = fSolver->SelectProblemSolution(problem, + problem->SolutionAt(selected - 1)); + if (error != B_OK) + DIE(error, "failed to set solution"); + break; + } + } + + status_t error = fSolver->SolveAgain(); + if (error != B_OK) + DIE(error, "failed to compute packages to install"); + } +} + + +void +PackageManager::_AnalyzeResult() +{ + BSolverResult result; + status_t error = fSolver->GetResult(result); + if (error != B_OK) + DIE(error, "failed to compute packages to install"); + + for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i); + i++) { + BSolverPackage* package = element->Package(); + + switch (element->Type()) { + case BSolverResultElement::B_TYPE_INSTALL: + if (!fInstalledRepositories.HasItem(package->Repository())) { + if (!fPackagesToActivate.AddItem(package)) + DIE(B_NO_MEMORY, "failed to add package to activate"); + } + break; + + case BSolverResultElement::B_TYPE_UNINSTALL: + if (!fPackagesToDeactivate.AddItem(package)) + DIE(B_NO_MEMORY, "failed to add package to deactivate"); + break; + } + } + + if (fPackagesToActivate.IsEmpty() && fPackagesToDeactivate.IsEmpty()) { + printf("Nothing to do.\n"); + exit(0); + } +} + + +void +PackageManager::_PrintResult() +{ + printf("The following changes will be made:\n"); + for (int32 i = 0; BSolverPackage* package = fPackagesToActivate.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 = fPackagesToDeactivate.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 +// some way. Extend BPackageInfo? Create a BPackageFileInfo? + + if (!fDecisionProvider.YesNoDecisionNeeded(BString(), "Continue?", "y", "n", + "y")) { + exit(1); + } +} + + +void +PackageManager::_ApplyPackageChanges() +{ + // create an activation transaction + BDaemonClient daemonClient; + BActivationTransaction transaction; + BDirectory transactionDirectory; + status_t error = daemonClient.CreateTransaction(fLocation, transaction, + transactionDirectory); + if (error != B_OK) + DIE(error, "failed to create transaction"); + + // download the new packages and prepare the transaction + for (int32 i = 0; BSolverPackage* package = fPackagesToActivate.ItemAt(i); + i++) { + // get package URL and target entry + Repository* repository + = static_cast<Repository*>(package->Repository()); + BString url = repository->Config().BaseURL(); + BString fileName(package->Info().CanonicalFileName()); + if (fileName.IsEmpty()) + DIE(B_NO_MEMORY, "failed to allocate file name"); + url << '/' << fileName; + + BEntry entry; + error = entry.SetTo(&transactionDirectory, fileName); + if (error != B_OK) + DIE(error, "failed to create package entry"); + + // download the package + DownloadFileRequest downloadRequest(fContext, url, entry, + package->Info().Checksum()); + error = downloadRequest.Process(); + if (error != B_OK) + DIE(error, "failed to download package"); + + // add package to transaction + if (!transaction.AddPackageToActivate( + package->Info().CanonicalFileName())) { + DIE(B_NO_MEMORY, + "failed to add package to activate to transaction"); + } + } + + for (int32 i = 0; BSolverPackage* package = fPackagesToDeactivate.ItemAt(i); + i++) { + // add package to transaction + if (!transaction.AddPackageToDeactivate( + package->Info().CanonicalFileName())) { + DIE(B_NO_MEMORY, + "failed to add package to deactivate to transaction"); + } + } + + // commit the transaction + BDaemonClient::BCommitTransactionResult transactionResult; + error = daemonClient.CommitTransaction(transaction, transactionResult); + if (error != B_OK) { + fprintf(stderr, "*** failed to commit transaction: %s\n", + transactionResult.FullErrorMessage().String()); + exit(1); + } + + printf("Installation done. Old activation state backed up in \"%s\"\n", + transactionResult.OldStateDirectory().String()); + + printf("Cleaning up ...\n"); + BEntry transactionDirectoryEntry; + if ((error = transactionDirectory.GetEntry(&transactionDirectoryEntry)) + != B_OK + || (error = transactionDirectoryEntry.Remove()) != B_OK) { + WARN(error, "failed to remove transaction directory"); + } +} diff --git a/src/bin/pkgman/PackageManager.h b/src/bin/pkgman/PackageManager.h new file mode 100644 index 0000000..7a8ebd0 --- /dev/null +++ b/src/bin/pkgman/PackageManager.h @@ -0,0 +1,94 @@ +/* + * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold <ingo_weinhold@xxxxxx> + */ +#ifndef PACKAGE_MANAGER_H +#define PACKAGE_MANAGER_H + + +#include <ObjectList.h> +#include <package/Context.h> +#include <package/PackageDefs.h> +#include <package/PackageRoster.h> +#include <package/RepositoryConfig.h> +#include <package/solver/Solver.h> +#include <package/solver/SolverRepository.h> + +#include "DecisionProvider.h" +#include "JobStateListener.h" + + +using namespace BPackageKit; + + +class PackageManager { +public: + struct Repository; + typedef BObjectList<Repository> RepositoryList; + +public: + PackageManager( + BPackageInstallationLocation location, + bool addInstalledRepositories, + bool addOtherRepositories); + ~PackageManager(); + + BSolver* Solver() const + { return fSolver; } + + const BSolverRepository* SystemRepository() const + { return &fSystemRepository; } + const BSolverRepository* CommonRepository() const + { return &fCommonRepository; } + const BSolverRepository* HomeRepository() const + { return &fHomeRepository; } + const BObjectList<BSolverRepository>& InstalledRepositories() const + { return fInstalledRepositories; } + const RepositoryList& OtherRepositories() const + { return fOtherRepositories; } + + void Install(const char* const* packages, + int packageCount); + +private: + typedef BObjectList<BSolverPackage> PackageList; + +private: + void _HandleProblems(); + void _AnalyzeResult(); + void _PrintResult(); + void _ApplyPackageChanges(); + +private: + BPackageInstallationLocation fLocation; + BSolver* fSolver; + BSolverRepository fSystemRepository; + BSolverRepository fCommonRepository; + BSolverRepository fHomeRepository; + BObjectList<BSolverRepository> fInstalledRepositories; + RepositoryList fOtherRepositories; + DecisionProvider fDecisionProvider; + JobStateListener fJobStateListener; + BContext fContext; + PackageList fPackagesToActivate; + PackageList fPackagesToDeactivate; +}; + + +struct PackageManager::Repository : public BSolverRepository { + Repository(); + + status_t Init(BPackageRoster& roster, BContext& context, + const char* name); + + const BRepositoryConfig& Config() const; + +private: + BRepositoryConfig fConfig; +}; + + +#endif // PACKAGE_MANAGER_H diff --git a/src/bin/pkgman/command_install.cpp b/src/bin/pkgman/command_install.cpp index 1bcbeff..da9c77f 100644 --- a/src/bin/pkgman/command_install.cpp +++ b/src/bin/pkgman/command_install.cpp @@ -7,31 +7,13 @@ */ -#include <errno.h> #include <getopt.h> #include <stdio.h> #include <stdlib.h> -#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> -#include <package/solver/SolverProblem.h> -#include <package/solver/SolverProblemSolution.h> -#include <package/solver/SolverResult.h> - -#include <AutoDeleter.h> -#include <package/ActivationTransaction.h> -#include <package/DaemonClient.h> - #include "Command.h" -#include "DecisionProvider.h" -#include "JobStateListener.h" #include "pkgman.h" -#include "RepositoryBuilder.h" +#include "PackageManager.h" // TODO: internationalization! @@ -59,42 +41,6 @@ static const char* const kLongUsage = DEFINE_COMMAND(InstallCommand, "install", kShortUsage, kLongUsage) -struct Repository : public BSolverRepository { - Repository() - : - BSolverRepository() - { - } - - 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); - } - - const BRepositoryConfig& Config() const - { - return fConfig; - } - -private: - BRepositoryConfig fConfig; -}; - - int InstallCommand::Execute(int argc, const char* const* argv) { @@ -134,289 +80,12 @@ InstallCommand::Execute(int argc, const char* const* argv) int packageCount = argc - optind; const char* const* packages = 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 - - // We add only the repository of our actual installation location as the - // "installed" repository. The repositories for the more general - // installation locations are added as regular repositories, but with better - // priorities than the actual (remote) repositories. This prevents the solver - // from showing conflicts when a package in a more specific installation - // location overrides a package in a more general one. Instead any - // requirement that is already installed in a more general installation - // location will turn up as to be installed as well. But we can easily - // filter those out. - BSolverRepository systemRepository; - RepositoryBuilder(systemRepository, "system") - .AddPackages(B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, "system") - .AddToSolver(solver, false); - systemRepository.SetPriority(-1); - - BSolverRepository commonRepository; - RepositoryBuilder(commonRepository, "common") - .AddPackages(B_PACKAGE_INSTALLATION_LOCATION_COMMON, "common") - .AddToSolver(solver, !installInHome); - - BObjectList<BSolverRepository> installedRepositories(10); - if (!installedRepositories.AddItem(&systemRepository) - || !installedRepositories.AddItem(&commonRepository)) { - DIE(B_NO_MEMORY, "failed to add installed repositories to list"); - } - - BSolverRepository homeRepository; - if (installInHome) { - commonRepository.SetPriority(-2); - RepositoryBuilder(homeRepository, "home") - .AddPackages(B_PACKAGE_INSTALLATION_LOCATION_HOME, "home") - .AddToSolver(solver, true); - - if (!installedRepositories.AddItem(&homeRepository)) - DIE(B_NO_MEMORY, "failed to add home repository to list"); - } - - // other repositories - DecisionProvider decisionProvider; - JobStateListener listener; - BContext context(decisionProvider, listener); - - BObjectList<Repository> otherRepositories(10, true); - 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++) { - Repository* repository = new(std::nothrow) Repository; - if (repository == NULL || !otherRepositories.AddItem(repository)) - DIE(B_NO_MEMORY, "failed to create/add repository object"); - - const BString& name = repositoryNames.StringAt(i); - error = repository->Init(roster, context, name); - if (error != B_OK) { - WARN(error, "failed to get config for repository \"%s\". Skipping.", - name.String()); - otherRepositories.RemoveItem(repository, true); - continue; - } - - RepositoryBuilder(*repository, repository->Config()) - .AddToSolver(solver, false); - } - - // solve - BSolverPackageSpecifierList packagesToInstall; - for (int i = 0; i < packageCount; i++) { - if (!packagesToInstall.AppendSpecifier(packages[i])) - DIE(B_NO_MEMORY, "failed to add specified package"); - } - - const BSolverPackageSpecifier* unmatchedSpecifier; - error = solver->Install(packagesToInstall, &unmatchedSpecifier); - if (error != B_OK) { - if (unmatchedSpecifier != NULL) { - DIE(error, "failed to find a match for \"%s\"", - unmatchedSpecifier->SelectString().String()); - } else - DIE(error, "failed to compute packages to install"); - } - - // deal with problems - while (solver->HasProblems()) { - printf("Encountered problems:\n"); - - int32 problemCount = solver->CountProblems(); - for (int32 i = 0; i < problemCount; i++) { - // print problem and possible solutions - BSolverProblem* problem = solver->ProblemAt(i); - 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); - int32 elementCount = solution->CountElements(); - for (int32 l = 0; l < elementCount; l++) { - const BSolverProblemSolutionElement* element - = solution->ElementAt(l); - 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; - } - } - - error = solver->SolveAgain(); - if (error != B_OK) - DIE(error, "failed to compute packages to install"); - } - - // print result - BSolverResult result; - error = solver->GetResult(result); - if (error != B_OK) - DIE(error, "failed to compute packages to install"); - - 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())) { - if (!packagesToActivate.AddItem(package)) - DIE(B_NO_MEMORY, "failed to add package to activate"); - } - break; - - case BSolverResultElement::B_TYPE_UNINSTALL: - 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 -// some way. Extend BPackageInfo? Create a BPackageFileInfo? - - if (!decisionProvider.YesNoDecisionNeeded(BString(), "Continue?", "y", "n", - "y")) { - return 1; - } - - // create an activation transaction - BDaemonClient daemonClient; + // perform the installation BPackageInstallationLocation location = installInHome ? B_PACKAGE_INSTALLATION_LOCATION_HOME : B_PACKAGE_INSTALLATION_LOCATION_COMMON; - BActivationTransaction transaction; - BDirectory transactionDirectory; - error = daemonClient.CreateTransaction(location, transaction, - transactionDirectory); - if (error != B_OK) - DIE(error, "failed to create transaction"); - - // download the new packages and prepare the transaction - for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i); - i++) { - // get package URL and target entry - Repository* repository - = static_cast<Repository*>(package->Repository()); - BString url = repository->Config().BaseURL(); - BString fileName(package->Info().CanonicalFileName()); - if (fileName.IsEmpty()) - DIE(B_NO_MEMORY, "failed to allocate file name"); - url << '/' << fileName; - - BEntry entry; - error = entry.SetTo(&transactionDirectory, fileName); - if (error != B_OK) - DIE(error, "failed to create package entry"); - - // download the package - DownloadFileRequest downloadRequest(context, url, entry, - package->Info().Checksum()); - error = downloadRequest.Process(); - if (error != B_OK) - DIE(error, "failed to download package"); - - // add package to transaction - if (!transaction.AddPackageToActivate( - package->Info().CanonicalFileName())) { - DIE(B_NO_MEMORY, - "failed to add package to activate to transaction"); - } - } - - for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i); - i++) { - // add package to transaction - if (!transaction.AddPackageToDeactivate( - package->Info().CanonicalFileName())) { - DIE(B_NO_MEMORY, - "failed to add package to deactivate to transaction"); - } - } - - // commit the transaction - BDaemonClient::BCommitTransactionResult transactionResult; - error = daemonClient.CommitTransaction(transaction, transactionResult); - if (error != B_OK) { - fprintf(stderr, "*** failed to commit transaction: %s\n", - transactionResult.FullErrorMessage().String()); - exit(1); - } - - printf("Installation done. Old activation state backed up in \"%s\"\n", - transactionResult.OldStateDirectory().String()); - - printf("Cleaning up ...\n"); - BEntry transactionDirectoryEntry; - if ((error = transactionDirectory.GetEntry(&transactionDirectoryEntry)) - != B_OK - || (error = transactionDirectoryEntry.Remove()) != B_OK) { - WARN(error, "failed to remove transaction directory"); - } + PackageManager packageManager(location, true, true); + packageManager.Install(packages, packageCount); return 0; } diff --git a/src/bin/pkgman/command_search.cpp b/src/bin/pkgman/command_search.cpp index f65db3e..9d2a72b 100644 --- a/src/bin/pkgman/command_search.cpp +++ b/src/bin/pkgman/command_search.cpp @@ -14,14 +14,13 @@ #include <sys/ioctl.h> #include <unistd.h> -#include <package/PackageRoster.h> -#include <package/RepositoryConfig.h> +#include <algorithm> -#include <AutoDeleter.h> +#include <package/solver/SolverPackage.h> #include "Command.h" +#include "PackageManager.h" #include "pkgman.h" -#include "RepositoryBuilder.h" // TODO: internationalization! @@ -32,9 +31,6 @@ using namespace BPackageKit; -typedef std::map<BSolverPackage*, BString> PackagePathMap; - - static const char* const kShortUsage = " %command% <search-string>\n" " Searches for packages matching <search-string>.\n"; @@ -113,64 +109,13 @@ SearchCommand::Execute(int argc, const char* const* argv) 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); - } - } + PackageManager packageManager(B_PACKAGE_INSTALLATION_LOCATION_COMMON, + !uninstalledOnly, !installedOnly); +// TODO: Use B_PACKAGE_INSTALLATION_LOCATION_HOME once we actually mount it. // search BObjectList<BSolverPackage> packages; - error = solver->FindPackages(searchString, + status_t error = packageManager.Solver()->FindPackages(searchString, BSolver::B_FIND_CASE_INSENSITIVE | BSolver::B_FIND_IN_NAME | BSolver::B_FIND_IN_SUMMARY | BSolver::B_FIND_IN_DESCRIPTION | BSolver::B_FIND_IN_PROVIDES, @@ -230,11 +175,11 @@ SearchCommand::Execute(int argc, const char* const* argv) BSolverPackage* package = packages.ItemAt(i); const char* installed = ""; - if (package->Repository() == &systemRepository) + if (package->Repository() == packageManager.SystemRepository()) installed = "system"; - else if (package->Repository() == &commonRepository) + else if (package->Repository() == packageManager.CommonRepository()) installed = "common"; - else if (package->Repository() == &homeRepository) + else if (package->Repository() == packageManager.HomeRepository()) installed = "home"; printf("%-*s %-*s %-*.*s\n", ############################################################################ Commit: de62d76176c323928d2b97bc667f39850727aa1f Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sun Apr 21 10:48:39 2013 UTC BSolver/LibsolvSolver: Add Uninstall() Also fix incorrect check in LibsolvSolver::GetResult(). ---------------------------------------------------------------------------- diff --git a/headers/os/package/solver/Solver.h b/headers/os/package/solver/Solver.h index f72afff..975b008 100644 --- a/headers/os/package/solver/Solver.h +++ b/headers/os/package/solver/Solver.h @@ -48,8 +48,11 @@ public: BObjectList<BSolverPackage>& _packages) = 0; virtual status_t Install( - const BSolverPackageSpecifierList& - packages, + const BSolverPackageSpecifierList& packages, + const BSolverPackageSpecifier** _unmatched + = NULL) = 0; + virtual status_t Uninstall( + const BSolverPackageSpecifierList& packages, const BSolverPackageSpecifier** _unmatched = NULL) = 0; virtual status_t VerifyInstallation() = 0; diff --git a/src/kits/package/solver/LibsolvSolver.cpp b/src/kits/package/solver/LibsolvSolver.cpp index 2f02a27..92c98e0 100644 --- a/src/kits/package/solver/LibsolvSolver.cpp +++ b/src/kits/package/solver/LibsolvSolver.cpp @@ -387,7 +387,82 @@ LibsolvSolver::Install(const BSolverPackageSpecifierList& packages, // set jobs' solver mode and solve _SetJobsSolverMode(*fJobs, SOLVER_INSTALL); - return _Solve(false); + _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; + + 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(); + Solvable* solvable; + if (package == NULL + || (solvable = _GetSolvable(package)) == NULL + || package->Repository() + != fInstalledRepository->Repository()) { + return B_BAD_VALUE; + } + + queue_push2(fJobs, SOLVER_SOLVABLE, + solvable - fPool->solvables); + 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 + | SELECTION_INSTALLED_ONLY; + /*int matchFlags =*/ selection_make(fPool, &matchingPackages, + specifier.SelectString().String(), flags); + if (matchingPackages.count == 0) { + if (_unmatched != NULL) + *_unmatched = &specifier; + return B_NAME_NOT_FOUND; + } + + for (int j = 0; j < matchingPackages.count; j++) + queue_push(fJobs, matchingPackages.elements[j]); + } + } + } + + // set jobs' solver mode and solve + _SetJobsSolverMode(*fJobs, SOLVER_ERASE); + + _InitSolver(); + solver_set_flag(fSolver, SOLVER_FLAG_ALLOW_UNINSTALL, 1); + return _Solve(); } @@ -412,7 +487,8 @@ LibsolvSolver::VerifyInstallation() // set jobs' solver mode and solve _SetJobsSolverMode(*fJobs, SOLVER_VERIFY); - return _Solve(false); + _InitSolver(); + return _Solve(); } @@ -452,7 +528,7 @@ LibsolvSolver::SolveAgain() solver_take_solution(fSolver, problem->Id(), solution->Id(), fJobs); } - return _Solve(true); + return _Solve(); } @@ -505,11 +581,11 @@ LibsolvSolver::GetResult(BSolverResult& _result) if (package == NULL) return B_ERROR; - status_t error = _result.AppendElement( - BSolverResultElement(BSolverResultElement::B_TYPE_UNINSTALL, - package)); - if (error != B_OK) - return error; + if (!_result.AppendElement( + BSolverResultElement( + BSolverResultElement::B_TYPE_UNINSTALL, package))) { + return B_NO_MEMORY; + } break; } @@ -592,6 +668,17 @@ LibsolvSolver::_InitJobQueue() 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(); @@ -1138,23 +1225,11 @@ LibsolvSolver::_GetResolvableExpression(Id id, status_t -LibsolvSolver::_Solve(bool solveAgain) +LibsolvSolver::_Solve() { - if (fJobs == NULL) + if (fJobs == NULL || fSolver == 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); - } - int problemCount = solver_solve(fSolver, fJobs); // get the problems (if any) diff --git a/src/kits/package/solver/LibsolvSolver.h b/src/kits/package/solver/LibsolvSolver.h index a4dcdd7..348be9e 100644 --- a/src/kits/package/solver/LibsolvSolver.h +++ b/src/kits/package/solver/LibsolvSolver.h @@ -39,8 +39,11 @@ public: BObjectList<BSolverPackage>& _packages); virtual status_t Install( - const BSolverPackageSpecifierList& - packages, + const BSolverPackageSpecifierList& packages, + const BSolverPackageSpecifier** _unmatched + = NULL); + virtual status_t Uninstall( + const BSolverPackageSpecifierList& packages, const BSolverPackageSpecifier** _unmatched = NULL); virtual status_t VerifyInstallation(); @@ -70,6 +73,7 @@ private: private: status_t _InitPool(); status_t _InitJobQueue(); + void _InitSolver(); void _Cleanup(); void _CleanupPool(); void _CleanupJobQueue(); @@ -96,7 +100,7 @@ private: BPackageResolvableExpression& _expression) const; - status_t _Solve(bool solveAgain); + status_t _Solve(); void _SetJobsSolverMode(Queue& jobs, int solverMode); private: ############################################################################ Commit: 8ba41d628c0ba3fd9f694562dc8b494fbfc220f7 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sun Apr 21 10:50:31 2013 UTC pkgman install: Fix typo in usage text ---------------------------------------------------------------------------- diff --git a/src/bin/pkgman/command_install.cpp b/src/bin/pkgman/command_install.cpp index da9c77f..a1f49c7 100644 --- a/src/bin/pkgman/command_install.cpp +++ b/src/bin/pkgman/command_install.cpp @@ -33,7 +33,7 @@ static const char* const kLongUsage = "\n" "Options:\n" " -H, --home\n" - " Install the packages in the user's home directory. Default is to.\n" + " Install the packages in the user's home directory. Default is to\n" " install in the common directory.\n" "\n"; ############################################################################ Commit: df2685d885f3b940bc04b1cf5f39511ce6750ddf Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sun Apr 21 10:52:06 2013 UTC pkgman: Add "uninstall" command ---------------------------------------------------------------------------- diff --git a/src/bin/pkgman/Jamfile b/src/bin/pkgman/Jamfile index 4669d21..de3d08c 100644 --- a/src/bin/pkgman/Jamfile +++ b/src/bin/pkgman/Jamfile @@ -11,6 +11,7 @@ BinCommand pkgman : command_refresh.cpp command_resolve_dependencies.cpp command_search.cpp + command_uninstall.cpp DecisionProvider.cpp JobStateListener.cpp PackageInfoErrorListener.cpp diff --git a/src/bin/pkgman/PackageManager.cpp b/src/bin/pkgman/PackageManager.cpp index e099995..f587451 100644 --- a/src/bin/pkgman/PackageManager.cpp +++ b/src/bin/pkgman/PackageManager.cpp @@ -194,6 +194,36 @@ PackageManager::Install(const char* const* packages, int packageCount) void +PackageManager::Uninstall(const char* const* packages, int packageCount) +{ + // solve + BSolverPackageSpecifierList packagesToUninstall; + for (int i = 0; i < packageCount; i++) { + if (!packagesToUninstall.AppendSpecifier(packages[i])) + DIE(B_NO_MEMORY, "failed to add specified package"); + } + + const BSolverPackageSpecifier* unmatchedSpecifier; + status_t error = fSolver->Uninstall(packagesToUninstall, + &unmatchedSpecifier); + if (error != B_OK) { + if (unmatchedSpecifier != NULL) { + DIE(error, "failed to find a match for \"%s\"", + unmatchedSpecifier->SelectString().String()); + } else + DIE(error, "failed to compute packages to uninstall"); + } + + _HandleProblems(); + + // install/uninstall packages + _AnalyzeResult(); + _PrintResult(); + _ApplyPackageChanges(); +} + + +void PackageManager::_HandleProblems() { while (fSolver->HasProblems()) { @@ -254,7 +284,7 @@ PackageManager::_HandleProblems() status_t error = fSolver->SolveAgain(); if (error != B_OK) - DIE(error, "failed to compute packages to install"); + DIE(error, "failed to recompute packages to un/-install"); } } @@ -265,7 +295,7 @@ PackageManager::_AnalyzeResult() BSolverResult result; status_t error = fSolver->GetResult(result); if (error != B_OK) - DIE(error, "failed to compute packages to install"); + DIE(error, "failed to compute packages to un/-install"); for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i); i++) { diff --git a/src/bin/pkgman/PackageManager.h b/src/bin/pkgman/PackageManager.h index 7a8ebd0..ca9b3a0 100644 --- a/src/bin/pkgman/PackageManager.h +++ b/src/bin/pkgman/PackageManager.h @@ -52,6 +52,8 @@ public: void Install(const char* const* packages, int packageCount); + void Uninstall(const char* const* packages, + int packageCount); private: typedef BObjectList<BSolverPackage> PackageList; diff --git a/src/bin/pkgman/command_uninstall.cpp b/src/bin/pkgman/command_uninstall.cpp new file mode 100644 index 0000000..5743603 --- /dev/null +++ b/src/bin/pkgman/command_uninstall.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold <ingo_weinhold@xxxxxx> + */ + + +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> + +#include "Command.h" +#include "pkgman.h" +#include "PackageManager.h" + + +// TODO: internationalization! + + +using namespace BPackageKit; +using namespace BPackageKit::BPrivate; + + +static const char* const kShortUsage = + " %command% <package> ...\n" + " Uninstalls one or more packages.\n"; + +static const char* const kLongUsage = + "Usage: %program% %command% <package> ...\n" + "Uninstalls the specified packages.\n" + "\n" + "Options:\n" + " -H, --home\n" + " Uninstall the packages from the user's home directory. Default is to\n" + " uninstall from the common directory.\n" + "\n"; + + +DEFINE_COMMAND(UninstallCommand, "uninstall", kShortUsage, kLongUsage) + + +int +UninstallCommand::Execute(int argc, const char* const* argv) +{ + bool uninstallFromHome = false; + + while (true) { + static struct option sLongOptions[] = { + { "help", no_argument, 0, 'h' }, + { "home", no_argument, 0, 'H' }, + { 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': + PrintUsageAndExit(false); + break; + + case 'H': + uninstallFromHome = true; + break; + + default: + PrintUsageAndExit(true); + break; + } + } + + // The remaining arguments are the packages to be uninstalled. + if (argc < optind + 1) + PrintUsageAndExit(true); + + int packageCount = argc - optind; + const char* const* packages = argv + optind; + + // perform the installation + BPackageInstallationLocation location = uninstallFromHome + ? B_PACKAGE_INSTALLATION_LOCATION_HOME + : B_PACKAGE_INSTALLATION_LOCATION_COMMON; + PackageManager packageManager(location, true, true); + packageManager.Uninstall(packages, packageCount); + + return 0; +}