diff --git a/headers/build/private/package/manager/Exceptions.h b/headers/build/private/package/manager/Exceptions.h new file mode 100644 index 0000000000..2d83e06be8 --- /dev/null +++ b/headers/build/private/package/manager/Exceptions.h @@ -0,0 +1 @@ +#include <../private/package/manager/Exceptions.h> diff --git a/headers/build/private/package/manager/RepositoryBuilder.h b/headers/build/private/package/manager/RepositoryBuilder.h new file mode 100644 index 0000000000..ec6dc69462 --- /dev/null +++ b/headers/build/private/package/manager/RepositoryBuilder.h @@ -0,0 +1 @@ +#include <../private/package/manager/RepositoryBuilder.h> diff --git a/headers/private/package/manager/Exceptions.h b/headers/private/package/manager/Exceptions.h new file mode 100644 index 0000000000..beb204fa37 --- /dev/null +++ b/headers/private/package/manager/Exceptions.h @@ -0,0 +1,74 @@ +/* + * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ +#ifndef _PACKAGE__MANAGER__PRIVATE__EXCEPTIONS_H_ +#define _PACKAGE__MANAGER__PRIVATE__EXCEPTIONS_H_ + + +#include + + +namespace BPackageKit { + +namespace BManager { + +namespace BPrivate { + + +class BException { +public: + BException(); + BException(const BString& message); + + const BString& Message() const + { return fMessage; } + +protected: + BString fMessage; +}; + + +class BFatalErrorException : public BException { +public: + BFatalErrorException(); + BFatalErrorException(const char* format, ...); + BFatalErrorException(status_t error, + const char* format, ...); + + const BString& Details() const + { return fDetails; } + BFatalErrorException& SetDetails(const BString& details); + + status_t Error() const + { return fError; } + +private: + BString fDetails; + status_t fError; +}; + + +class BAbortedByUserException : public BException { +public: + BAbortedByUserException(); +}; + + +class BNothingToDoException : public BException { +public: + BNothingToDoException(); +}; + + +} // namespace BPrivate + +} // namespace BManager + +} // namespace BPackageKit + + +#endif // _PACKAGE__MANAGER__PRIVATE__EXCEPTIONS_H_ diff --git a/headers/private/package/manager/PackageManager.h b/headers/private/package/manager/PackageManager.h new file mode 100644 index 0000000000..d4518b0cde --- /dev/null +++ b/headers/private/package/manager/PackageManager.h @@ -0,0 +1,264 @@ +/* + * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ +#ifndef _PACKAGE__MANAGER__PRIVATE__PACKAGE_MANAGER_H_ +#define _PACKAGE__MANAGER__PRIVATE__PACKAGE_MANAGER_H_ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +namespace BPackageKit { + +namespace BManager { + +namespace BPrivate { + + +using namespace BPackageKit::BPrivate; + + +class BPackageManager { +public: + class RemoteRepository; + class InstalledRepository; + class Transaction; + class TransactionHandler; + class DaemonClientTransactionHandler; + class UserInteractionHandler; + class RequestHandler; + + typedef BObjectList RemoteRepositoryList; + typedef BObjectList InstalledRepositoryList; + typedef BObjectList PackageList; + typedef BObjectList TransactionList; + + enum { + B_ADD_INSTALLED_REPOSITORIES = 0x01, + B_ADD_REMOTE_REPOSITORIES = 0x02, + B_REFRESH_REPOSITORIES = 0x04, + }; + +public: + BPackageManager( + BPackageInstallationLocation location); + ~BPackageManager(); + + void Init(uint32 flags); + + BSolver* Solver() const + { return fSolver; } + + const InstalledRepository* SystemRepository() const + { return fSystemRepository; } + const InstalledRepository* CommonRepository() const + { return fCommonRepository; } + const InstalledRepository* HomeRepository() const + { return fHomeRepository; } + const InstalledRepositoryList& InstalledRepositories() const + { return fInstalledRepositories; } + const RemoteRepositoryList& OtherRepositories() const + { return fOtherRepositories; } + + void Install(const char* const* packages, + int packageCount); + void Uninstall(const char* const* packages, + int packageCount); + void Update(const char* const* packages, + int packageCount); + +private: + void _HandleProblems(); + void _AnalyzeResult(); + void _ConfirmChanges(bool fromMostSpecific = false); + void _ApplyPackageChanges( + bool fromMostSpecific = false); + void _PreparePackageChanges( + InstalledRepository& + installationRepository); + void _CommitPackageChanges(Transaction& transaction); + + void _ClonePackageFile( + InstalledRepository* repository, + const BString& fileName, + const BEntry& entry); + int32 _FindBasePackage(const PackageList& packages, + const BPackageInfo& info); + + InstalledRepository& _InstallationRepository(); + + void _AddInstalledRepository( + InstalledRepository* repository); + void _AddRemoteRepository(BPackageRoster& roster, + const char* name, bool refresh); + status_t _GetRepositoryConfig(BPackageRoster& roster, + const char* name, bool refresh, + BRepositoryConfig& _config); + + bool _NextSpecificInstallationLocation(); + +protected: + BPackageInstallationLocation fLocation; + BSolver* fSolver; + InstalledRepository* fSystemRepository; + InstalledRepository* fCommonRepository; + InstalledRepository* fHomeRepository; + InstalledRepositoryList fInstalledRepositories; + RemoteRepositoryList fOtherRepositories; + TransactionList fTransactions; + + // must be set by the derived class + TransactionHandler* fTransactionHandler; + RequestHandler* fRequestHandler; + UserInteractionHandler* fUserInteractionHandler; +}; + + +class BPackageManager::RemoteRepository : public BSolverRepository { +public: + RemoteRepository( + const BRepositoryConfig& config); + + const BRepositoryConfig& Config() const; + +private: + BRepositoryConfig fConfig; +}; + + +class BPackageManager::InstalledRepository : public BSolverRepository { +public: + typedef BObjectList PackageList; + +public: + InstalledRepository(const char* name, + BPackageInstallationLocation location, + int32 priority); + + BPackageInstallationLocation Location() const + { return fLocation; } + const char* InitialName() const + { return fInitialName; } + int32 InitialPriority() const + { return fInitialPriority; } + + void DisablePackage(BSolverPackage* package); + + PackageList& PackagesToActivate() + { return fPackagesToActivate; } + PackageList& PackagesToDeactivate() + { return fPackagesToDeactivate; } + + bool HasChanges() const; + void ApplyChanges(); + +private: + PackageList fDisabledPackages; + PackageList fPackagesToActivate; + PackageList fPackagesToDeactivate; + const char* fInitialName; + BPackageInstallationLocation fLocation; + int32 fInitialPriority; +}; + + +class BPackageManager::Transaction { +public: + Transaction(InstalledRepository& repository); + ~Transaction(); + + InstalledRepository& Repository() + { return fRepository; } + BActivationTransaction& ActivationTransaction() + { return fTransaction; } + BDirectory& TransactionDirectory() + { return fTransactionDirectory; } + +private: + InstalledRepository& fRepository; + BActivationTransaction fTransaction; + BDirectory fTransactionDirectory; +}; + + +class BPackageManager::TransactionHandler { +public: + virtual ~TransactionHandler(); + + virtual status_t PrepareTransaction(Transaction& transaction) + = 0; + virtual status_t CommitTransaction(Transaction& transaction, + BDaemonClient::BCommitTransactionResult& + _result) = 0; +}; + + +class BPackageManager::DaemonClientTransactionHandler + : public TransactionHandler { +public: + DaemonClientTransactionHandler(); + virtual ~DaemonClientTransactionHandler(); + + virtual status_t PrepareTransaction(Transaction& transaction); + virtual status_t CommitTransaction(Transaction& transaction, + BDaemonClient::BCommitTransactionResult& + _result); + +private: + BDaemonClient fDaemonClient; +}; + + +class BPackageManager::RequestHandler { +public: + virtual ~RequestHandler(); + + virtual status_t RefreshRepository( + const BRepositoryConfig& repoConfig) = 0; + virtual status_t DownloadPackage(const BString& fileURL, + const BEntry& targetEntry, + const BString& checksum) = 0; +}; + + +class BPackageManager::UserInteractionHandler { +public: + virtual ~UserInteractionHandler(); + + virtual void HandleProblems() = 0; + virtual void ConfirmChanges(bool fromMostSpecific) = 0; + + virtual void Warn(status_t error, const char* format, ...) + = 0; + virtual void ProgressStartApplyingChanges( + InstalledRepository& repository) = 0; + virtual void ProgressTransactionCommitted( + InstalledRepository& repository, + const char* transactionDirectoryName) = 0; + virtual void ProgressApplyingChangesDone( + InstalledRepository& repository) = 0; +}; + + +} // namespace BPrivate + +} // namespace BManager + +} // namespace BPackageKit + + +#endif // _PACKAGE__MANAGER__PRIVATE__PACKAGE_MANAGER_H_ diff --git a/headers/private/package/manager/RepositoryBuilder.h b/headers/private/package/manager/RepositoryBuilder.h new file mode 100644 index 0000000000..0c25432b80 --- /dev/null +++ b/headers/private/package/manager/RepositoryBuilder.h @@ -0,0 +1,74 @@ +/* + * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ +#ifndef _PACKAGE__MANAGER__PRIVATE__REPOSITORY_BUILDER_H_ +#define _PACKAGE__MANAGER__PRIVATE__REPOSITORY_BUILDER_H_ + + +#include + +#include +#include +#include +#include + + +namespace BPackageKit { + +namespace BManager { + +namespace BPrivate { + + +typedef std::map BPackagePathMap; + + +class BRepositoryBuilder { +public: + BRepositoryBuilder( + BSolverRepository& repository, + const BString& name, + const BString& errorName = BString()); + BRepositoryBuilder( + BSolverRepository& repository, + const BRepositoryConfig& config); + BRepositoryBuilder( + BSolverRepository& repository, + const BRepositoryCache& cache, + const BString& errorName = BString()); + + BRepositoryBuilder& SetPackagePathMap( + BPackagePathMap* packagePaths); + + BRepositoryBuilder& AddPackage(const BPackageInfo& info, + const char* packageErrorName = NULL, + BSolverPackage** _package = NULL); + BRepositoryBuilder& AddPackage(const char* path, + BSolverPackage** _package = NULL); + BRepositoryBuilder& AddPackages( + BPackageInstallationLocation location, + const char* locationErrorName); + BRepositoryBuilder& AddPackagesDirectory(const char* path); + + BRepositoryBuilder& AddToSolver(BSolver* solver, + bool isInstalled = false); + +private: + BSolverRepository& fRepository; + BString fErrorName; + BPackagePathMap* fPackagePaths; +}; + + +} // namespace BPrivate + +} // namespace BManager + +} // namespace BPackageKit + + +#endif // _PACKAGE__MANAGER__PRIVATE__REPOSITORY_BUILDER_H_ diff --git a/src/bin/pkgman/Jamfile b/src/bin/pkgman/Jamfile index d95774350d..e37556419c 100644 --- a/src/bin/pkgman/Jamfile +++ b/src/bin/pkgman/Jamfile @@ -15,10 +15,8 @@ BinCommand pkgman : command_uninstall.cpp DecisionProvider.cpp JobStateListener.cpp - PackageInfoErrorListener.cpp PackageManager.cpp pkgman.cpp - RepositoryBuilder.cpp : package be $(TARGET_LIBSUPC++) $(TARGET_LIBSTDC++) diff --git a/src/bin/pkgman/PackageInfoErrorListener.cpp b/src/bin/pkgman/PackageInfoErrorListener.cpp deleted file mode 100644 index 89639c3d1b..0000000000 --- a/src/bin/pkgman/PackageInfoErrorListener.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2013, Haiku, Inc. All Rights Reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Ingo Weinhold - */ - - -#include "PackageInfoErrorListener.h" - -#include - - -PackageInfoErrorListener::PackageInfoErrorListener(const BString& errorContext) - : - fErrorContext(errorContext) -{ -} - - -void -PackageInfoErrorListener::OnError(const BString& message, int line, int column) -{ - fprintf(stderr, "%s: Parse error in line %d:%d: %s\n", - fErrorContext.String(), line, column, message.String()); -} diff --git a/src/bin/pkgman/PackageInfoErrorListener.h b/src/bin/pkgman/PackageInfoErrorListener.h deleted file mode 100644 index 7bd9b1a865..0000000000 --- a/src/bin/pkgman/PackageInfoErrorListener.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2013, Haiku, Inc. All Rights Reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Ingo Weinhold - */ -#ifndef PACKAGE_INFO_ERROR_LISTENER_H -#define PACKAGE_INFO_ERROR_LISTENER_H - - -#include - - -using namespace BPackageKit; - - -class PackageInfoErrorListener : public BPackageInfo::ParseErrorListener { -public: - PackageInfoErrorListener( - const BString& errorContext); - - virtual void OnError(const BString& message, int line, - int column); - -private: - BString fErrorContext; -}; - - -#endif // PACKAGE_INFO_ERROR_LISTENER_H diff --git a/src/bin/pkgman/PackageManager.cpp b/src/bin/pkgman/PackageManager.cpp index 5f7957640c..d0cabbfbab 100644 --- a/src/bin/pkgman/PackageManager.cpp +++ b/src/bin/pkgman/PackageManager.cpp @@ -9,516 +9,119 @@ #include "PackageManager.h" -#include #include -#include #include #include -#include -#include #include #include -#include - -#include -#include -#include #include "pkgman.h" -#include "RepositoryBuilder.h" using namespace BPackageKit::BPrivate; -// #pragma mark - RemoteRepository - - -PackageManager::RemoteRepository::RemoteRepository() +PackageManager::PackageManager(BPackageInstallationLocation location) : - BSolverRepository() -{ -} - - -status_t -PackageManager::RemoteRepository::Init(BPackageRoster& roster, - BContext& context, const char* name, bool refresh) -{ - // get the repository config - status_t error = roster.GetRepositoryConfig(name, &fConfig); - if (error != B_OK) - return error; - - // refresh - if (!refresh) - return B_OK; - - 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::RemoteRepository::Config() const -{ - return fConfig; -} - - -// #pragma mark - InstalledRepository - - -PackageManager::InstalledRepository::InstalledRepository(const char* name, - BPackageInstallationLocation location, int32 priority) - : - BSolverRepository(), - fDisabledPackages(10, true), - fPackagesToActivate(), - fPackagesToDeactivate(), - fInitialName(name), - fLocation(location), - fInitialPriority(priority) -{ -} - - -void -PackageManager::InstalledRepository::DisablePackage(BSolverPackage* package) -{ - if (fDisabledPackages.HasItem(package)) { - fprintf(stderr, "*** package %s already disabled\n", - package->VersionedName().String()); - exit(1); - } - - if (package->Repository() != this) { - fprintf(stderr, "*** package %s not in repository %s\n", - package->VersionedName().String(), Name().String()); - exit(1); - } - - // move to disabled list - if (!fDisabledPackages.AddItem(package)) - DIE(B_NO_MEMORY, "failed to add package to list"); - - RemovePackage(package); -} - - -bool -PackageManager::InstalledRepository::HasChanges() const -{ - return !fPackagesToActivate.IsEmpty() || !fPackagesToDeactivate.IsEmpty(); -} - - -void -PackageManager::InstalledRepository::ApplyChanges() -{ - // disable packages to deactivate - for (int32 i = 0; BSolverPackage* package = fPackagesToDeactivate.ItemAt(i); - i++) { - if (!fDisabledPackages.HasItem(package)) - DisablePackage(package); - } - - // add packages to activate - for (int32 i = 0; BSolverPackage* package = fPackagesToActivate.ItemAt(i); - i++) { - status_t error = AddPackage(package->Info()); - if (error != B_OK) { - DIE(error, "failed to add package %s to %s repository", - package->Name().String(), - Name().String()); - } - } -} - - -// #pragma mark - Solver - - -PackageManager::PackageManager(BPackageInstallationLocation location, - uint32 flags) - : - fLocation(location), - fSolver(NULL), - fSystemRepository(new (std::nothrow) InstalledRepository("system", - B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, -1)), - fCommonRepository(new (std::nothrow) InstalledRepository("common", - B_PACKAGE_INSTALLATION_LOCATION_COMMON, -2)), - fHomeRepository(new (std::nothrow) InstalledRepository("home", - B_PACKAGE_INSTALLATION_LOCATION_HOME, -3)), - fInstalledRepositories(10), - fOtherRepositories(10, true), + BPackageManager(location), + BPackageManager::UserInteractionHandler(), fDecisionProvider(), fJobStateListener(JobStateListener::EXIT_ON_ABORT), - fContext(fDecisionProvider, fJobStateListener) + fContext(fDecisionProvider, fJobStateListener), + fDaemonClientTransactionHandler() { - // create the solver - status_t error = BSolver::Create(fSolver); - if (error != B_OK) - DIE(error, "failed to create solver"); - - if (fSystemRepository == NULL || fCommonRepository == NULL - || fHomeRepository == NULL) { - DIE(B_NO_MEMORY, "failed to create repositories"); - } - - // add installation location repositories - if ((flags & ADD_INSTALLED_REPOSITORIES) != 0) { - // 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. - _AddInstalledRepository(fSystemRepository); - - if (!fSystemRepository->IsInstalled()) { - _AddInstalledRepository(fCommonRepository); - - if (!fCommonRepository->IsInstalled()) - _AddInstalledRepository(fHomeRepository); - } - } - - // add other repositories - if ((flags & ADD_REMOTE_REPOSITORIES) != 0) { - 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++) { - RemoteRepository* repository = new(std::nothrow) RemoteRepository; - 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, - (flags & REFRESH_REPOSITORIES) != 0); - 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); - } - } + fTransactionHandler = &fDaemonClientTransactionHandler; + fRequestHandler = this; + fUserInteractionHandler = this; } PackageManager::~PackageManager() { - delete fSystemRepository; - delete fCommonRepository; - delete fHomeRepository; +} + + +status_t +PackageManager::RefreshRepository(const BRepositoryConfig& repoConfig) +{ + return BRefreshRepositoryRequest(fContext, repoConfig).Process(); +} + + +status_t +PackageManager::DownloadPackage(const BString& fileURL, + const BEntry& targetEntry, const BString& checksum) +{ + return DownloadFileRequest(fContext, fileURL, targetEntry, checksum) + .Process(); } void -PackageManager::Install(const char* const* packages, int packageCount) +PackageManager::HandleProblems() { - // solve - BSolverPackageSpecifierList packagesToInstall; - for (int i = 0; i < packageCount; i++) { - if (!packagesToInstall.AppendSpecifier(packages[i])) - DIE(B_NO_MEMORY, "failed to add specified package"); - } + printf("Encountered problems:\n"); - 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"); - } + 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()); - _HandleProblems(); + 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()); + } + } - // install/uninstall packages - _AnalyzeResult(); - _PrintResult(); - _ApplyPackageChanges(); -} + // 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); + } -void -PackageManager::Uninstall(const char* const* packages, int packageCount) -{ - // find the packages that match the specification - BSolverPackageSpecifierList packagesToUninstall; - for (int i = 0; i < packageCount; i++) { - if (!packagesToUninstall.AppendSpecifier(packages[i])) - DIE(B_NO_MEMORY, "failed to add specified package"); - } + if (strcmp(buffer, "s\n") == 0) + break; - const BSolverPackageSpecifier* unmatchedSpecifier; - PackageList foundPackages; - status_t error = fSolver->FindPackages(packagesToUninstall, - BSolver::B_FIND_INSTALLED_ONLY, foundPackages, &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"); - } - - // determine the inverse base package closure for the found packages -// TODO: Optimize! - InstalledRepository& installationRepository = _InstallationRepository(); - bool foundAnotherPackage; - do { - foundAnotherPackage = false; - int32 count = installationRepository.CountPackages(); - for (int32 i = 0; i < count; i++) { - BSolverPackage* package = installationRepository.PackageAt(i); - if (foundPackages.HasItem(package)) + char* end; + long selected = strtol(buffer, &end, 0); + if (end == buffer || *end != '\n' || selected < 1 + || selected > solutionCount) { + printf("*** invalid input\n"); continue; - - if (_FindBasePackage(foundPackages, package->Info()) >= 0) { - foundPackages.AddItem(package); - foundAnotherPackage = true; - } - } - } while (foundAnotherPackage); - - // remove the packages from the repository - for (int32 i = 0; BSolverPackage* package = foundPackages.ItemAt(i); i++) - installationRepository.DisablePackage(package); - - for (;;) { - error = fSolver->VerifyInstallation(BSolver::B_VERIFY_ALLOW_UNINSTALL); - if (error != B_OK) - DIE(error, "failed to compute packages to uninstall"); - - _HandleProblems(); - - // (virtually) apply the result to this repository - _AnalyzeResult(); - - for (int32 i = foundPackages.CountItems() - 1; i >= 0; i--) { - if (!installationRepository.PackagesToDeactivate() - .AddItem(foundPackages.ItemAt(i))) { - DIE(B_NO_MEMORY, "failed to add package to uninstall"); - } - } - - installationRepository.ApplyChanges(); - - // verify the next specific respository - if (!_NextSpecificInstallationLocation()) - break; - - foundPackages.MakeEmpty(); - - // NOTE: In theory, after verifying a more specific location, it would - // be more correct to compute the inverse base package closure for the - // packages we need to uninstall and (if anything changed) verify again. - // In practice, however, base packages are always required with an exact - // version (ATM). If that base package still exist in a more general - // location (the only reason why the package requiring the base package - // wouldn't be marked to be uninstalled as well) there shouldn't have - // been any reason to remove it from the more specific location in the - // first place. - } - - _PrintResult(true); - _ApplyPackageChanges(true); -} - - -void -PackageManager::Update(const char* const* packages, int packageCount) -{ - // solve - BSolverPackageSpecifierList packagesToUpdate; - for (int i = 0; i < packageCount; i++) { - if (!packagesToUpdate.AppendSpecifier(packages[i])) - DIE(B_NO_MEMORY, "failed to add specified package"); - } - - const BSolverPackageSpecifier* unmatchedSpecifier; - status_t error = fSolver->Update(packagesToUpdate, true, - &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 update"); - } - - _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 recompute packages to un/-install"); - } -} - - -void -PackageManager::_AnalyzeResult() -{ - BSolverResult result; - status_t error = fSolver->GetResult(result); - if (error != B_OK) - DIE(error, "failed to compute packages to un/-install"); - - InstalledRepository& installationRepository = _InstallationRepository(); - PackageList& packagesToActivate - = installationRepository.PackagesToActivate(); - PackageList& packagesToDeactivate - = installationRepository.PackagesToDeactivate(); - - PackageList potentialBasePackages; - - for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i); - i++) { - BSolverPackage* package = element->Package(); - - switch (element->Type()) { - case BSolverResultElement::B_TYPE_INSTALL: - { - PackageList& packageList - = dynamic_cast(package->Repository()) - != NULL - ? potentialBasePackages - : packagesToActivate; - if (!packageList.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; - } - } - - // Make sure base packages are installed in the same location. - for (int32 i = 0; i < packagesToActivate.CountItems(); i++) { - BSolverPackage* package = packagesToActivate.ItemAt(i); - int32 index = _FindBasePackage(potentialBasePackages, package->Info()); - if (index < 0) - continue; - - BSolverPackage* basePackage = potentialBasePackages.RemoveItemAt(index); - if (!packagesToActivate.AddItem(basePackage)) - DIE(B_NO_MEMORY, "failed to add package to activate"); - } -} - - -void -PackageManager::_PrintResult(bool fromMostSpecific) -{ - // check, if there are any changes at all - int32 count = fInstalledRepositories.CountItems(); - bool hasChanges = false; - for (int32 i = 0; i < count; i++) { - if (fInstalledRepositories.ItemAt(i)->HasChanges()) { - hasChanges = true; + status_t error = fSolver->SelectProblemSolution(problem, + problem->SolutionAt(selected - 1)); + if (error != B_OK) + DIE(error, "failed to set solution"); break; } } +} - if (!hasChanges) { - printf("Nothing to do.\n"); - exit(0); - } +void +PackageManager::ConfirmChanges(bool fromMostSpecific) +{ printf("The following changes will be made:\n"); + int32 count = fInstalledRepositories.CountItems(); if (fromMostSpecific) { for (int32 i = count - 1; i >= 0; i--) _PrintResult(*fInstalledRepositories.ItemAt(i)); @@ -534,6 +137,45 @@ PackageManager::_PrintResult(bool fromMostSpecific) } +void +PackageManager::Warn(status_t error, const char* format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + + if (error == B_OK) + printf("\n"); + else + printf(": %s\n", strerror(error)); +} + + +void +PackageManager::ProgressStartApplyingChanges(InstalledRepository& repository) +{ + printf("[%s] Applying changes ...\n", repository.Name().String()); +} + + +void +PackageManager::ProgressTransactionCommitted(InstalledRepository& repository, + const char* transactionDirectoryName) +{ + printf("[%s] Changes applied. Old activation state backed up in \"%s\"\n", + repository.Name().String(), transactionDirectoryName); + printf("[%s] Cleaning up ...\n", repository.Name().String()); +} + + +void +PackageManager::ProgressApplyingChangesDone(InstalledRepository& repository) +{ + printf("[%s] Done.\n", repository.Name().String()); +} + + void PackageManager::_PrintResult(InstalledRepository& installationRepository) { @@ -563,233 +205,3 @@ PackageManager::_PrintResult(InstalledRepository& installationRepository) // other information) should, however, be provided by the repository cache in // some way. Extend BPackageInfo? Create a BPackageFileInfo? } - - -void -PackageManager::_ApplyPackageChanges(bool fromMostSpecific) -{ - int32 count = fInstalledRepositories.CountItems(); - if (fromMostSpecific) { - for (int32 i = count - 1; i >= 0; i--) - _ApplyPackageChanges(*fInstalledRepositories.ItemAt(i)); - } else { - for (int32 i = 0; i < count; i++) - _ApplyPackageChanges(*fInstalledRepositories.ItemAt(i)); - } -} - - -void -PackageManager::_ApplyPackageChanges( - InstalledRepository& installationRepository) -{ - - if (!installationRepository.HasChanges()) - return; - - PackageList& packagesToActivate - = installationRepository.PackagesToActivate(); - PackageList& packagesToDeactivate - = installationRepository.PackagesToDeactivate(); - - // create an activation transaction - BDaemonClient daemonClient; - BActivationTransaction transaction; - BDirectory transactionDirectory; - status_t error = daemonClient.CreateTransaction( - installationRepository.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 - - BString fileName(package->Info().FileName()); - if (fileName.IsEmpty()) - DIE(B_NO_MEMORY, "failed to allocate file name"); - - BEntry entry; - error = entry.SetTo(&transactionDirectory, fileName); - if (error != B_OK) - DIE(error, "failed to create package entry"); - - RemoteRepository* remoteRepository - = dynamic_cast(package->Repository()); - if (remoteRepository == NULL) { - // clone the existing package - _ClonePackageFile( - dynamic_cast(package->Repository()), - fileName, entry); - } else { - // download the package - BString url = remoteRepository->Config().PackagesURL(); - url << '/' << fileName; - - 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(fileName)) { - 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().FileName())) { - 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("Changes applied in \"%s\". Old activation state backed up in " - "\"%s\"\n", installationRepository.Name().String(), - 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"); - } -} - - -void -PackageManager::_ClonePackageFile(InstalledRepository* repository, - const BString& fileName, const BEntry& entry) const -{ - // get the source and destination file paths - directory_which packagesWhich; - if (repository == fSystemRepository) { - packagesWhich = B_SYSTEM_PACKAGES_DIRECTORY; - } else if (repository == fCommonRepository) { - packagesWhich = B_COMMON_PACKAGES_DIRECTORY; - } else { - fprintf(stderr, "*** don't know packages directory path for " - "installation location \"%s\"", repository->Name().String()); - exit(1); - } - - BPath sourcePath; - status_t error = find_directory(packagesWhich, &sourcePath); - if (error != B_OK || (error = sourcePath.Append(fileName)) != B_OK) { - DIE(error, "failed to get path of package file \"%s\" in installation " - "location \"%s\"", fileName.String(), repository->Name().String()); - } - - BPath destinationPath; - error = entry.GetPath(&destinationPath); - if (error != B_OK) { - DIE(error, "failed to entry path of package file to install \"%s\"", - fileName.String()); - } - - // Copy the package. Ideally we would just hard-link it, but BFS doesn't - // support that. - error = BCopyEngine().CopyEntry(sourcePath.Path(), destinationPath.Path()); - if (error != B_OK) - DIE(error, "failed to copy package file \"%s\"", sourcePath.Path()); -} - - -int32 -PackageManager::_FindBasePackage(const PackageList& packages, - const BPackageInfo& info) const -{ - if (info.BasePackage().IsEmpty()) - return -1; - - // find the requirement matching the base package - BPackageResolvableExpression* basePackage = NULL; - int32 count = info.RequiresList().CountItems(); - for (int32 i = 0; i < count; i++) { - BPackageResolvableExpression* requires = info.RequiresList().ItemAt(i); - if (requires->Name() == info.BasePackage()) { - basePackage = requires; - break; - } - } - - if (basePackage == NULL) { - printf("warning: package %s-%s doesn't have a matching requires for " - "its base package \"%s\"\n", info.Name().String(), - info.Version().ToString().String(), info.BasePackage().String()); - return -1; - } - - // find the first package matching the base package requires - count = packages.CountItems(); - for (int32 i = 0; i < count; i++) { - BSolverPackage* package = packages.ItemAt(i); - if (package->Name() == basePackage->Name() - && package->Info().Matches(*basePackage)) { - return i; - } - } - - return -1; -} - - -PackageManager::InstalledRepository& -PackageManager::_InstallationRepository() -{ - if (fInstalledRepositories.IsEmpty()) - DIE(B_ERROR, "no installation repository"); - - return *fInstalledRepositories.LastItem(); -} - - -void -PackageManager::_AddInstalledRepository(InstalledRepository* repository) -{ - const char* name = repository->InitialName(); - RepositoryBuilder(*repository, name) - .AddPackages(repository->Location(), name) - .AddToSolver(fSolver, repository->Location() == fLocation); - repository->SetPriority(repository->InitialPriority()); - - if (!fInstalledRepositories.AddItem(repository)) - DIE(B_NO_MEMORY, "failed to add %s repository to list", name); -} - - -bool -PackageManager::_NextSpecificInstallationLocation() -{ - if (fLocation == B_PACKAGE_INSTALLATION_LOCATION_SYSTEM) { - fLocation = B_PACKAGE_INSTALLATION_LOCATION_COMMON; - fSystemRepository->SetInstalled(false); - _AddInstalledRepository(fCommonRepository); - return true; - } - - if (fLocation == B_PACKAGE_INSTALLATION_LOCATION_COMMON) { - fLocation = B_PACKAGE_INSTALLATION_LOCATION_HOME; - fCommonRepository->SetInstalled(false); - _AddInstalledRepository(fHomeRepository); - return true; - } - - return false; -} diff --git a/src/bin/pkgman/PackageManager.h b/src/bin/pkgman/PackageManager.h index 4101072fba..ca7846264a 100644 --- a/src/bin/pkgman/PackageManager.h +++ b/src/bin/pkgman/PackageManager.h @@ -9,151 +9,58 @@ #define PACKAGE_MANAGER_H -#include -#include -#include -#include -#include -#include -#include +#include +#include #include "DecisionProvider.h" #include "JobStateListener.h" using namespace BPackageKit; +using BPackageKit::BPrivate::BDaemonClient; +using BManager::BPrivate::BPackageManager; -class PackageManager { -public: - struct RemoteRepository; - struct InstalledRepository; - typedef BObjectList RemoteRepositoryList; - typedef BObjectList InstalledRepositoryList; - typedef BObjectList PackageList; - - enum { - ADD_INSTALLED_REPOSITORIES = 0x01, - ADD_REMOTE_REPOSITORIES = 0x02, - REFRESH_REPOSITORIES = 0x04, - }; - +class PackageManager : public BPackageManager, + private BPackageManager::RequestHandler, + private BPackageManager::UserInteractionHandler { public: PackageManager( - BPackageInstallationLocation location, - uint32 flags); + BPackageInstallationLocation location); ~PackageManager(); - BSolver* Solver() const - { return fSolver; } - - const InstalledRepository* SystemRepository() const - { return fSystemRepository; } - const InstalledRepository* CommonRepository() const - { return fCommonRepository; } - const InstalledRepository* HomeRepository() const - { return fHomeRepository; } - const InstalledRepositoryList& InstalledRepositories() const - { return fInstalledRepositories; } - const RemoteRepositoryList& OtherRepositories() const - { return fOtherRepositories; } - - void Install(const char* const* packages, - int packageCount); - void Uninstall(const char* const* packages, - int packageCount); - void Update(const char* const* packages, - int packageCount); +private: + // RequestHandler + virtual status_t RefreshRepository( + const BRepositoryConfig& repoConfig); + virtual status_t DownloadPackage(const BString& fileURL, + const BEntry& targetEntry, + const BString& checksum); private: - void _HandleProblems(); - void _AnalyzeResult(); - void _PrintResult(bool fromMostSpecific = false); - void _PrintResult( - InstalledRepository& - installationRepository); - void _ApplyPackageChanges( - bool fromMostSpecific = false); - void _ApplyPackageChanges( - InstalledRepository& - installationRepository); + // UserInteractionHandler + virtual void HandleProblems(); + virtual void ConfirmChanges(bool fromMostSpecific); - void _ClonePackageFile( - InstalledRepository* repository, - const BString& fileName, - const BEntry& entry) const; - int32 _FindBasePackage(const PackageList& packages, - const BPackageInfo& info) const; - - InstalledRepository& _InstallationRepository(); - - void _AddInstalledRepository( - InstalledRepository* repository); - bool _NextSpecificInstallationLocation(); + virtual void Warn(status_t error, const char* format, ...); + virtual void ProgressStartApplyingChanges( + InstalledRepository& repository); + virtual void ProgressTransactionCommitted( + InstalledRepository& repository, + const char* transactionDirectoryName); + virtual void ProgressApplyingChangesDone( + InstalledRepository& repository); + +private: + void _PrintResult(InstalledRepository& + installationRepository); private: - BPackageInstallationLocation fLocation; - BSolver* fSolver; - InstalledRepository* fSystemRepository; - InstalledRepository* fCommonRepository; - InstalledRepository* fHomeRepository; - InstalledRepositoryList fInstalledRepositories; - RemoteRepositoryList fOtherRepositories; DecisionProvider fDecisionProvider; JobStateListener fJobStateListener; BContext fContext; - PackageList fPackagesToActivate; - PackageList fPackagesToDeactivate; -}; - - -class PackageManager::RemoteRepository : public BSolverRepository { -public: - RemoteRepository(); - - status_t Init(BPackageRoster& roster, BContext& context, - const char* name, bool refresh); - - const BRepositoryConfig& Config() const; - -private: - BRepositoryConfig fConfig; -}; - - -class PackageManager::InstalledRepository : public BSolverRepository { -public: - typedef BObjectList PackageList; - -public: - InstalledRepository(const char* name, - BPackageInstallationLocation location, - int32 priority); - - BPackageInstallationLocation Location() const - { return fLocation; } - const char* InitialName() const - { return fInitialName; } - int32 InitialPriority() const - { return fInitialPriority; } - - void DisablePackage(BSolverPackage* package); - - PackageList& PackagesToActivate() - { return fPackagesToActivate; } - PackageList& PackagesToDeactivate() - { return fPackagesToDeactivate; } - - bool HasChanges() const; - void ApplyChanges(); - -private: - PackageList fDisabledPackages; - PackageList fPackagesToActivate; - PackageList fPackagesToDeactivate; - const char* fInitialName; - BPackageInstallationLocation fLocation; - int32 fInitialPriority; + BPackageManager::DaemonClientTransactionHandler + fDaemonClientTransactionHandler; }; diff --git a/src/bin/pkgman/RepositoryBuilder.h b/src/bin/pkgman/RepositoryBuilder.h deleted file mode 100644 index b3d4166c2c..0000000000 --- a/src/bin/pkgman/RepositoryBuilder.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2013, Haiku, Inc. All Rights Reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Ingo Weinhold - */ -#ifndef REPOSITORY_BUILDER_H -#define REPOSITORY_BUILDER_H - - -#include - -#include -#include -#include -#include - - -using namespace BPackageKit; - - -typedef std::map PackagePathMap; - - -class RepositoryBuilder { -private: - typedef RepositoryBuilder Builder; - -public: - RepositoryBuilder(BSolverRepository& repository, - const BString& name, - const BString& errorName = BString()); - RepositoryBuilder(BSolverRepository& repository, - const BRepositoryConfig& config); - RepositoryBuilder(BSolverRepository& repository, - const BRepositoryCache& cache, - const BString& errorName = BString()); - - RepositoryBuilder& SetPackagePathMap(PackagePathMap* packagePaths); - - RepositoryBuilder& AddPackage(const BPackageInfo& info, - const char* packageErrorName = NULL, - BSolverPackage** _package = NULL); - RepositoryBuilder& AddPackage(const char* path, - BSolverPackage** _package = NULL); - RepositoryBuilder& AddPackages( - BPackageInstallationLocation location, - const char* locationErrorName); - RepositoryBuilder& AddPackagesDirectory(const char* path); - - RepositoryBuilder& AddToSolver(BSolver* solver, - bool isInstalled = false); - -private: - BSolverRepository& fRepository; - BString fErrorName; - PackagePathMap* fPackagePaths; -}; - - -#endif // REPOSITORY_BUILDER_H diff --git a/src/bin/pkgman/command_install.cpp b/src/bin/pkgman/command_install.cpp index 7fa380296e..2b5589ab29 100644 --- a/src/bin/pkgman/command_install.cpp +++ b/src/bin/pkgman/command_install.cpp @@ -91,10 +91,7 @@ InstallCommand::Execute(int argc, const char* const* argv) const char* const* packages = argv + optind; // perform the installation - PackageManager packageManager(location, - PackageManager::ADD_INSTALLED_REPOSITORIES - | PackageManager::ADD_REMOTE_REPOSITORIES - | PackageManager::REFRESH_REPOSITORIES); + PackageManager packageManager(location); packageManager.Install(packages, packageCount); return 0; diff --git a/src/bin/pkgman/command_resolve_dependencies.cpp b/src/bin/pkgman/command_resolve_dependencies.cpp index 272f623af6..eb10867514 100644 --- a/src/bin/pkgman/command_resolve_dependencies.cpp +++ b/src/bin/pkgman/command_resolve_dependencies.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -22,13 +23,14 @@ #include "Command.h" #include "pkgman.h" -#include "RepositoryBuilder.h" // TODO: internationalization! using namespace BPackageKit; +using BManager::BPrivate::BPackagePathMap; +using BManager::BPrivate::BRepositoryBuilder; static const char* const kShortUsage = @@ -77,7 +79,7 @@ check_problems(BSolver* solver, const char* errorContext) static void verify_result(const BSolverResult& result, - const PackagePathMap& specifiedPackagePaths) + const BPackagePathMap& specifiedPackagePaths) { // create the solver BSolver* solver; @@ -88,7 +90,7 @@ verify_result(const BSolverResult& result, // Add an installation repository and add all of the result packages save // the specified packages. BSolverRepository installation; - RepositoryBuilder installationBuilder(installation, "installation"); + BRepositoryBuilder installationBuilder(installation, "installation"); for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i); i++) { @@ -160,7 +162,7 @@ ResolveDependenciesCommand::Execute(int argc, const char* const* argv) DIE(error, "failed to create solver"); // add repositories - PackagePathMap packagePaths; + BPackagePathMap packagePaths; BObjectList repositories(10, true); int32 repositoryIndex = 0; for (int i = 0; i < repositoryDirectoryCount; i++, repositoryIndex++) { @@ -180,17 +182,18 @@ ResolveDependenciesCommand::Execute(int argc, const char* const* argv) } } - RepositoryBuilder(*repository, BString("repository") << repositoryIndex) + BRepositoryBuilder(*repository, + BString("repository") << repositoryIndex) .SetPackagePathMap(&packagePaths) .AddPackagesDirectory(directoryPath) .AddToSolver(solver); } // add a repository with only the specified packages - PackagePathMap specifiedPackagePaths; + BPackagePathMap specifiedPackagePaths; BSolverRepository dummyRepository; { - RepositoryBuilder builder(dummyRepository, "dummy", + BRepositoryBuilder builder(dummyRepository, "dummy", "specified packages"); builder.SetPackagePathMap(&specifiedPackagePaths); @@ -202,7 +205,7 @@ ResolveDependenciesCommand::Execute(int argc, const char* const* argv) // resolve BSolverPackageSpecifierList packagesToInstall; - for (PackagePathMap::const_iterator it = specifiedPackagePaths.begin(); + for (BPackagePathMap::const_iterator it = specifiedPackagePaths.begin(); it != specifiedPackagePaths.end(); ++it) { if (!packagesToInstall.AppendSpecifier(it->first)) DIE(B_NO_MEMORY, "failed to add specified package"); @@ -231,7 +234,7 @@ ResolveDependenciesCommand::Execute(int argc, const char* const* argv) continue; // resolve and print the path - PackagePathMap::const_iterator it = packagePaths.find(package); + BPackagePathMap::const_iterator it = packagePaths.find(package); if (it == packagePaths.end()) { DIE(B_ERROR, "ugh, no package %p (%s-%s) not in package path map", package, package->Info().Name().String(), diff --git a/src/bin/pkgman/command_search.cpp b/src/bin/pkgman/command_search.cpp index 35902c7865..37c8c4cd3b 100644 --- a/src/bin/pkgman/command_search.cpp +++ b/src/bin/pkgman/command_search.cpp @@ -119,9 +119,10 @@ SearchCommand::Execute(int argc, const char* const* argv) const char* searchString = listAll ? "" : argv[optind++]; // create the solver - PackageManager packageManager(B_PACKAGE_INSTALLATION_LOCATION_HOME, - (!uninstalledOnly ? PackageManager::ADD_INSTALLED_REPOSITORIES : 0) - | (!installedOnly ? PackageManager::ADD_REMOTE_REPOSITORIES : 0)); + PackageManager packageManager(B_PACKAGE_INSTALLATION_LOCATION_HOME); + packageManager.Init( + (!uninstalledOnly ? PackageManager::B_ADD_INSTALLED_REPOSITORIES : 0) + | (!installedOnly ? PackageManager::B_ADD_REMOTE_REPOSITORIES : 0)); // search BObjectList packages; diff --git a/src/bin/pkgman/command_uninstall.cpp b/src/bin/pkgman/command_uninstall.cpp index 8bbf4e4b1d..43d3df5445 100644 --- a/src/bin/pkgman/command_uninstall.cpp +++ b/src/bin/pkgman/command_uninstall.cpp @@ -91,8 +91,7 @@ UninstallCommand::Execute(int argc, const char* const* argv) const char* const* packages = argv + optind; // perform the installation - PackageManager packageManager(location, - PackageManager::ADD_INSTALLED_REPOSITORIES); + PackageManager packageManager(location); packageManager.Uninstall(packages, packageCount); return 0; diff --git a/src/bin/pkgman/command_update.cpp b/src/bin/pkgman/command_update.cpp index 7f0aacae8b..f8527f2c98 100644 --- a/src/bin/pkgman/command_update.cpp +++ b/src/bin/pkgman/command_update.cpp @@ -89,10 +89,7 @@ UpdateCommand::Execute(int argc, const char* const* argv) const char* const* packages = argv + optind; // perform the update - PackageManager packageManager(location, - PackageManager::ADD_INSTALLED_REPOSITORIES - | PackageManager::ADD_REMOTE_REPOSITORIES - | PackageManager::REFRESH_REPOSITORIES); + PackageManager packageManager(location); packageManager.Update(packages, packageCount); return 0; diff --git a/src/bin/pkgman/pkgman.cpp b/src/bin/pkgman/pkgman.cpp index d0843ae328..3d09f1d581 100644 --- a/src/bin/pkgman/pkgman.cpp +++ b/src/bin/pkgman/pkgman.cpp @@ -11,9 +11,14 @@ #include #include +#include + #include "Command.h" +using namespace BPackageKit::BManager::BPrivate; + + extern const char* __progname; const char* kProgramName = __progname; @@ -85,5 +90,25 @@ main(int argc, const char* const* argv) if (commands.CountItems() != 1) print_usage_and_exit(true); - return commands.ItemAt(0)->Execute(argc - 1, argv + 1); + try { + return commands.ItemAt(0)->Execute(argc - 1, argv + 1); + } catch (BNothingToDoException&) { + fprintf(stderr, "Nothing to do.\n"); + return 0; + } catch (std::bad_alloc&) { + fprintf(stderr, "Out of memory!\n"); + return 1; + } catch (BFatalErrorException& exception) { + if (!exception.Details().IsEmpty()) + fprintf(stderr, "%s", exception.Details().String()); + if (exception.Error() == B_OK) { + fprintf(stderr, "*** %s\n", exception.Message().String()); + } else { + fprintf(stderr, "*** %s: %s\n", exception.Message().String(), + strerror(exception.Error())); + } + return 1; + } catch (BAbortedByUserException&) { + return 0; + } } diff --git a/src/build/libpackage/Jamfile b/src/build/libpackage/Jamfile index a6004d46bf..6f9d80ea1e 100644 --- a/src/build/libpackage/Jamfile +++ b/src/build/libpackage/Jamfile @@ -5,6 +5,7 @@ UsePrivateBuildHeaders kernel package shared storage ; SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits package ] ; SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits package hpkg ] ; SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits package hpkg v1 ] ; +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits package manager ] ; SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits package solver ] ; USES_BE_API on libpackage_build.so = true ; @@ -116,6 +117,10 @@ BuildPlatformSharedLibrary libpackage_build.so NoErrorOutput.cpp StandardErrorOutput.cpp + # manager + Exceptions.cpp + RepositoryBuilder.cpp + # solver Solver.cpp SolverPackage.cpp diff --git a/src/kits/package/Jamfile b/src/kits/package/Jamfile index 7f84897fe9..80e2e10a14 100644 --- a/src/kits/package/Jamfile +++ b/src/kits/package/Jamfile @@ -67,6 +67,7 @@ for architectureObject in [ MultiArchSubDirSetup ] { SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits package hpkg ] ; SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits package hpkg v1 ] ; + SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits package manager ] ; SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits package solver ] ; Includes [ FGristFiles $(HPKG_SOURCES) ] @@ -112,6 +113,11 @@ for architectureObject in [ MultiArchSubDirSetup ] { NoErrorOutput.cpp StandardErrorOutput.cpp + # manager + Exceptions.cpp + PackageManager.cpp + RepositoryBuilder.cpp + # solver Solver.cpp SolverPackage.cpp diff --git a/src/kits/package/manager/Exceptions.cpp b/src/kits/package/manager/Exceptions.cpp new file mode 100644 index 0000000000..804691f457 --- /dev/null +++ b/src/kits/package/manager/Exceptions.cpp @@ -0,0 +1,107 @@ +/* + * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include + + +namespace BPackageKit { + +namespace BManager { + +namespace BPrivate { + + +// #pragma mark - BException + + +BException::BException() + : + fMessage() +{ +} + + +BException::BException(const BString& message) + : + fMessage(message) +{ +} + + +// #pragma mark - BFatalErrorException + + +BFatalErrorException::BFatalErrorException() + : + BException(), + fDetails(), + fError(B_OK) +{ +} + + +BFatalErrorException::BFatalErrorException(const char* format, ...) + : + BException(), + fDetails(), + fError(B_OK) +{ + va_list args; + va_start(args, format); + fMessage.SetToFormatVarArgs(format, args); + va_end(args); +} + + +BFatalErrorException::BFatalErrorException(status_t error, const char* format, + ...) + : + BException(), + fDetails(), + fError(error) +{ + va_list args; + va_start(args, format); + fMessage.SetToFormatVarArgs(format, args); + va_end(args); +} + + +BFatalErrorException& +BFatalErrorException::SetDetails(const BString& details) +{ + fDetails = details; + return *this; +} + + +// #pragma mark - BAbortedByUserException + + +BAbortedByUserException::BAbortedByUserException() + : + BException() +{ +} + + +// #pragma mark - BNothingToDoException + + +BNothingToDoException::BNothingToDoException() + : + BException() +{ +} + + +} // namespace BPrivate + +} // namespace BManager + +} // namespace BPackageKit diff --git a/src/kits/package/manager/PackageManager.cpp b/src/kits/package/manager/PackageManager.cpp new file mode 100644 index 0000000000..66bf9a4e9a --- /dev/null +++ b/src/kits/package/manager/PackageManager.cpp @@ -0,0 +1,824 @@ +/* + * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "PackageManagerUtils.h" + + +namespace BPackageKit { + +namespace BManager { + +namespace BPrivate { + + +// #pragma mark - BPackageManager + + +BPackageManager::BPackageManager(BPackageInstallationLocation location) + : + fLocation(location), + fSolver(NULL), + fSystemRepository(new (std::nothrow) InstalledRepository("system", + B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, -1)), + fCommonRepository(new (std::nothrow) InstalledRepository("common", + B_PACKAGE_INSTALLATION_LOCATION_COMMON, -2)), + fHomeRepository(new (std::nothrow) InstalledRepository("home", + B_PACKAGE_INSTALLATION_LOCATION_HOME, -3)), + fInstalledRepositories(10), + fOtherRepositories(10, true), + fTransactions(5, true), + fTransactionHandler(NULL), + fRequestHandler(NULL), + fUserInteractionHandler(NULL) +{ +} + + +BPackageManager::~BPackageManager() +{ + delete fSolver; + delete fSystemRepository; + delete fCommonRepository; + delete fHomeRepository; +} + + +void +BPackageManager::Init(uint32 flags) +{ + if (fSolver != NULL) + return; + + // create the solver + status_t error = BSolver::Create(fSolver); + if (error != B_OK) + DIE(error, "failed to create solver"); + + if (fSystemRepository == NULL || fCommonRepository == NULL + || fHomeRepository == NULL) { + throw std::bad_alloc(); + } + + // add installation location repositories + if ((flags & B_ADD_INSTALLED_REPOSITORIES) != 0) { + // 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. + _AddInstalledRepository(fSystemRepository); + + if (!fSystemRepository->IsInstalled()) { + _AddInstalledRepository(fCommonRepository); + + if (!fCommonRepository->IsInstalled()) + _AddInstalledRepository(fHomeRepository); + } + } + + // add other repositories + if ((flags & B_ADD_REMOTE_REPOSITORIES) != 0) { + BPackageRoster roster; + BStringList repositoryNames; + error = roster.GetRepositoryNames(repositoryNames); + if (error != B_OK) { + fUserInteractionHandler->Warn(error, + "failed to get repository names"); + } + + int32 repositoryNameCount = repositoryNames.CountStrings(); + for (int32 i = 0; i < repositoryNameCount; i++) { + _AddRemoteRepository(roster, repositoryNames.StringAt(i), + (flags & B_REFRESH_REPOSITORIES) != 0); + } + } +} + + +void +BPackageManager::Install(const char* const* packages, int packageCount) +{ + Init(B_ADD_INSTALLED_REPOSITORIES | B_ADD_REMOTE_REPOSITORIES + | B_REFRESH_REPOSITORIES); + + // solve + BSolverPackageSpecifierList packagesToInstall; + for (int i = 0; i < packageCount; i++) { + if (!packagesToInstall.AppendSpecifier(packages[i])) + throw std::bad_alloc(); + } + + 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(); + _ConfirmChanges(); + _ApplyPackageChanges(); +} + + +void +BPackageManager::Uninstall(const char* const* packages, int packageCount) +{ + Init(B_ADD_INSTALLED_REPOSITORIES); + + // find the packages that match the specification + BSolverPackageSpecifierList packagesToUninstall; + for (int i = 0; i < packageCount; i++) { + if (!packagesToUninstall.AppendSpecifier(packages[i])) + throw std::bad_alloc(); + } + + const BSolverPackageSpecifier* unmatchedSpecifier; + PackageList foundPackages; + status_t error = fSolver->FindPackages(packagesToUninstall, + BSolver::B_FIND_INSTALLED_ONLY, foundPackages, &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"); + } + + // determine the inverse base package closure for the found packages +// TODO: Optimize! + InstalledRepository& installationRepository = _InstallationRepository(); + bool foundAnotherPackage; + do { + foundAnotherPackage = false; + int32 count = installationRepository.CountPackages(); + for (int32 i = 0; i < count; i++) { + BSolverPackage* package = installationRepository.PackageAt(i); + if (foundPackages.HasItem(package)) + continue; + + if (_FindBasePackage(foundPackages, package->Info()) >= 0) { + foundPackages.AddItem(package); + foundAnotherPackage = true; + } + } + } while (foundAnotherPackage); + + // remove the packages from the repository + for (int32 i = 0; BSolverPackage* package = foundPackages.ItemAt(i); i++) + installationRepository.DisablePackage(package); + + for (;;) { + error = fSolver->VerifyInstallation(BSolver::B_VERIFY_ALLOW_UNINSTALL); + if (error != B_OK) + DIE(error, "failed to compute packages to uninstall"); + + _HandleProblems(); + + // (virtually) apply the result to this repository + _AnalyzeResult(); + + for (int32 i = foundPackages.CountItems() - 1; i >= 0; i--) { + if (!installationRepository.PackagesToDeactivate() + .AddItem(foundPackages.ItemAt(i))) { + throw std::bad_alloc(); + } + } + + installationRepository.ApplyChanges(); + + // verify the next specific respository + if (!_NextSpecificInstallationLocation()) + break; + + foundPackages.MakeEmpty(); + + // NOTE: In theory, after verifying a more specific location, it would + // be more correct to compute the inverse base package closure for the + // packages we need to uninstall and (if anything changed) verify again. + // In practice, however, base packages are always required with an exact + // version (ATM). If that base package still exist in a more general + // location (the only reason why the package requiring the base package + // wouldn't be marked to be uninstalled as well) there shouldn't have + // been any reason to remove it from the more specific location in the + // first place. + } + + _ConfirmChanges(true); + _ApplyPackageChanges(true); +} + + +void +BPackageManager::Update(const char* const* packages, int packageCount) +{ + Init(B_ADD_INSTALLED_REPOSITORIES | B_ADD_REMOTE_REPOSITORIES + | B_REFRESH_REPOSITORIES); + + // solve + BSolverPackageSpecifierList packagesToUpdate; + for (int i = 0; i < packageCount; i++) { + if (!packagesToUpdate.AppendSpecifier(packages[i])) + throw std::bad_alloc(); + } + + const BSolverPackageSpecifier* unmatchedSpecifier; + status_t error = fSolver->Update(packagesToUpdate, true, + &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 update"); + } + + _HandleProblems(); + + // install/uninstall packages + _AnalyzeResult(); + _ConfirmChanges(); + _ApplyPackageChanges(); +} + + +void +BPackageManager::_HandleProblems() +{ + while (fSolver->HasProblems()) { + fUserInteractionHandler->HandleProblems(); + + status_t error = fSolver->SolveAgain(); + if (error != B_OK) + DIE(error, "failed to recompute packages to un/-install"); + } +} + + +void +BPackageManager::_AnalyzeResult() +{ + BSolverResult result; + status_t error = fSolver->GetResult(result); + if (error != B_OK) + DIE(error, "failed to compute packages to un/-install"); + + InstalledRepository& installationRepository = _InstallationRepository(); + PackageList& packagesToActivate + = installationRepository.PackagesToActivate(); + PackageList& packagesToDeactivate + = installationRepository.PackagesToDeactivate(); + + PackageList potentialBasePackages; + + for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i); + i++) { + BSolverPackage* package = element->Package(); + + switch (element->Type()) { + case BSolverResultElement::B_TYPE_INSTALL: + { + PackageList& packageList + = dynamic_cast(package->Repository()) + != NULL + ? potentialBasePackages + : packagesToActivate; + if (!packageList.AddItem(package)) + throw std::bad_alloc(); + break; + } + + case BSolverResultElement::B_TYPE_UNINSTALL: + if (!packagesToDeactivate.AddItem(package)) + throw std::bad_alloc(); + break; + } + } + + // Make sure base packages are installed in the same location. + for (int32 i = 0; i < packagesToActivate.CountItems(); i++) { + BSolverPackage* package = packagesToActivate.ItemAt(i); + int32 index = _FindBasePackage(potentialBasePackages, package->Info()); + if (index < 0) + continue; + + BSolverPackage* basePackage = potentialBasePackages.RemoveItemAt(index); + if (!packagesToActivate.AddItem(basePackage)) + throw std::bad_alloc(); + } +} + + +void +BPackageManager::_ConfirmChanges(bool fromMostSpecific) +{ + // check, if there are any changes at all + int32 count = fInstalledRepositories.CountItems(); + bool hasChanges = false; + for (int32 i = 0; i < count; i++) { + if (fInstalledRepositories.ItemAt(i)->HasChanges()) { + hasChanges = true; + break; + } + } + + if (!hasChanges) + throw BNothingToDoException(); + + fUserInteractionHandler->ConfirmChanges(fromMostSpecific); +} + + +void +BPackageManager::_ApplyPackageChanges(bool fromMostSpecific) +{ + int32 count = fInstalledRepositories.CountItems(); + if (fromMostSpecific) { + for (int32 i = count - 1; i >= 0; i--) + _PreparePackageChanges(*fInstalledRepositories.ItemAt(i)); + } else { + for (int32 i = 0; i < count; i++) + _PreparePackageChanges(*fInstalledRepositories.ItemAt(i)); + } + + for (int32 i = 0; Transaction* transaction = fTransactions.ItemAt(i); i++) + _CommitPackageChanges(*transaction); + +// TODO: Clean up the transaction directories on error! +} + + +void +BPackageManager::_PreparePackageChanges( + InstalledRepository& installationRepository) +{ + if (!installationRepository.HasChanges()) + return; + + PackageList& packagesToActivate + = installationRepository.PackagesToActivate(); + PackageList& packagesToDeactivate + = installationRepository.PackagesToDeactivate(); + + // create the transaction + Transaction* transaction = new Transaction(installationRepository); + if (!fTransactions.AddItem(transaction)) { + delete transaction; + throw std::bad_alloc(); + } + + status_t error = fTransactionHandler->PrepareTransaction(*transaction); + 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 + + BString fileName(package->Info().FileName()); + if (fileName.IsEmpty()) + throw std::bad_alloc(); + + BEntry entry; + error = entry.SetTo(&transaction->TransactionDirectory(), fileName); + if (error != B_OK) + DIE(error, "failed to create package entry"); + + RemoteRepository* remoteRepository + = dynamic_cast(package->Repository()); + if (remoteRepository == NULL) { + // clone the existing package + _ClonePackageFile( + dynamic_cast(package->Repository()), + fileName, entry); + } else { + // download the package + BString url = remoteRepository->Config().PackagesURL(); + url << '/' << fileName; + + status_t error = fRequestHandler->DownloadPackage(url, entry, + package->Info().Checksum()); + if (error != B_OK) + DIE(error, "failed to download package"); + } + + // add package to transaction + if (!transaction->ActivationTransaction().AddPackageToActivate( + fileName)) { + throw std::bad_alloc(); + } + } + + for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i); + i++) { + // add package to transaction + if (!transaction->ActivationTransaction().AddPackageToDeactivate( + package->Info().FileName())) { + throw std::bad_alloc(); + } + } +} + + +void +BPackageManager::_CommitPackageChanges(Transaction& transaction) +{ + InstalledRepository& installationRepository = transaction.Repository(); + + fUserInteractionHandler->ProgressStartApplyingChanges( + installationRepository); + + // commit the transaction + BDaemonClient::BCommitTransactionResult transactionResult; + status_t error = fTransactionHandler->CommitTransaction(transaction, + transactionResult); + if (error != B_OK) { + DIE("failed to commit transaction: %s", + transactionResult.FullErrorMessage().String()); + } + + fUserInteractionHandler->ProgressTransactionCommitted( + installationRepository, transactionResult.OldStateDirectory()); + + BEntry transactionDirectoryEntry; + if ((error = transaction.TransactionDirectory() + .GetEntry(&transactionDirectoryEntry)) != B_OK + || (error = transactionDirectoryEntry.Remove()) != B_OK) { + fUserInteractionHandler->Warn(error, + "failed to remove transaction directory"); + } + + fUserInteractionHandler->ProgressApplyingChangesDone( + installationRepository); +} + + +void +BPackageManager::_ClonePackageFile(InstalledRepository* repository, + const BString& fileName, const BEntry& entry) +{ + // get the source and destination file paths + directory_which packagesWhich; + if (repository == fSystemRepository) { + packagesWhich = B_SYSTEM_PACKAGES_DIRECTORY; + } else if (repository == fCommonRepository) { + packagesWhich = B_COMMON_PACKAGES_DIRECTORY; + } else { + DIE("don't know packages directory path for installation location " + "\"%s\"", repository->Name().String()); + } + + BPath sourcePath; + status_t error = find_directory(packagesWhich, &sourcePath); + if (error != B_OK || (error = sourcePath.Append(fileName)) != B_OK) { + DIE(error, "failed to get path of package file \"%s\" in installation " + "location \"%s\"", fileName.String(), repository->Name().String()); + } + + BPath destinationPath; + error = entry.GetPath(&destinationPath); + if (error != B_OK) { + DIE(error, "failed to entry path of package file to install \"%s\"", + fileName.String()); + } + + // Copy the package. Ideally we would just hard-link it, but BFS doesn't + // support that. + error = BCopyEngine().CopyEntry(sourcePath.Path(), destinationPath.Path()); + if (error != B_OK) + DIE(error, "failed to copy package file \"%s\"", sourcePath.Path()); +} + + +int32 +BPackageManager::_FindBasePackage(const PackageList& packages, + const BPackageInfo& info) +{ + if (info.BasePackage().IsEmpty()) + return -1; + + // find the requirement matching the base package + BPackageResolvableExpression* basePackage = NULL; + int32 count = info.RequiresList().CountItems(); + for (int32 i = 0; i < count; i++) { + BPackageResolvableExpression* requires = info.RequiresList().ItemAt(i); + if (requires->Name() == info.BasePackage()) { + basePackage = requires; + break; + } + } + + if (basePackage == NULL) { + fUserInteractionHandler->Warn(B_OK, "package %s-%s doesn't have a " + "matching requires for its base package \"%s\"", + info.Name().String(), info.Version().ToString().String(), + info.BasePackage().String()); + return -1; + } + + // find the first package matching the base package requires + count = packages.CountItems(); + for (int32 i = 0; i < count; i++) { + BSolverPackage* package = packages.ItemAt(i); + if (package->Name() == basePackage->Name() + && package->Info().Matches(*basePackage)) { + return i; + } + } + + return -1; +} + + +BPackageManager::InstalledRepository& +BPackageManager::_InstallationRepository() +{ + if (fInstalledRepositories.IsEmpty()) + DIE("no installation repository"); + + return *fInstalledRepositories.LastItem(); +} + + +void +BPackageManager::_AddInstalledRepository(InstalledRepository* repository) +{ + const char* name = repository->InitialName(); + BRepositoryBuilder(*repository, name) + .AddPackages(repository->Location(), name) + .AddToSolver(fSolver, repository->Location() == fLocation); + repository->SetPriority(repository->InitialPriority()); + + if (!fInstalledRepositories.AddItem(repository)) + throw std::bad_alloc(); +} + + +void +BPackageManager::_AddRemoteRepository(BPackageRoster& roster, const char* name, + bool refresh) +{ + BRepositoryConfig config; + status_t error = _GetRepositoryConfig(roster, name, refresh, config); + if (error != B_OK) { + fUserInteractionHandler->Warn(error, + "failed to get config for repository \"%s\". Skipping.", name); + return; + } + + RemoteRepository* repository = new RemoteRepository(config); + if (!fOtherRepositories.AddItem(repository)) { + delete repository; + throw std::bad_alloc(); + } + + BRepositoryBuilder(*repository, repository->Config()) + .AddToSolver(fSolver, false); +} + + +status_t +BPackageManager::_GetRepositoryConfig(BPackageRoster& roster, const char* name, + bool refresh, BRepositoryConfig& _config) +{ + // get the repository config + status_t error = roster.GetRepositoryConfig(name, &_config); + if (error != B_OK) + return error; + + // refresh + if (!refresh) + return B_OK; + + error = fRequestHandler->RefreshRepository(_config); + if (error != B_OK) { + fUserInteractionHandler->Warn(error, + "refreshing repository \"%s\" failed", name); + return B_OK; + } + + // re-get the config + return roster.GetRepositoryConfig(name, &_config); +} + + +bool +BPackageManager::_NextSpecificInstallationLocation() +{ + if (fLocation == B_PACKAGE_INSTALLATION_LOCATION_SYSTEM) { + fLocation = B_PACKAGE_INSTALLATION_LOCATION_COMMON; + fSystemRepository->SetInstalled(false); + _AddInstalledRepository(fCommonRepository); + return true; + } + + if (fLocation == B_PACKAGE_INSTALLATION_LOCATION_COMMON) { + fLocation = B_PACKAGE_INSTALLATION_LOCATION_HOME; + fCommonRepository->SetInstalled(false); + _AddInstalledRepository(fHomeRepository); + return true; + } + + return false; +} + + +// #pragma mark - RemoteRepository + + +BPackageManager::RemoteRepository::RemoteRepository( + const BRepositoryConfig& config) + : + BSolverRepository(), + fConfig(config) +{ +} + + +const BRepositoryConfig& +BPackageManager::RemoteRepository::Config() const +{ + return fConfig; +} + + +// #pragma mark - InstalledRepository + + +BPackageManager::InstalledRepository::InstalledRepository(const char* name, + BPackageInstallationLocation location, int32 priority) + : + BSolverRepository(), + fDisabledPackages(10, true), + fPackagesToActivate(), + fPackagesToDeactivate(), + fInitialName(name), + fLocation(location), + fInitialPriority(priority) +{ +} + + +void +BPackageManager::InstalledRepository::DisablePackage(BSolverPackage* package) +{ + if (fDisabledPackages.HasItem(package)) + DIE("package %s already disabled", package->VersionedName().String()); + + if (package->Repository() != this) { + DIE("package %s not in repository %s", + package->VersionedName().String(), Name().String()); + } + + // move to disabled list + if (!fDisabledPackages.AddItem(package)) + throw std::bad_alloc(); + + RemovePackage(package); +} + + +bool +BPackageManager::InstalledRepository::HasChanges() const +{ + return !fPackagesToActivate.IsEmpty() || !fPackagesToDeactivate.IsEmpty(); +} + + +void +BPackageManager::InstalledRepository::ApplyChanges() +{ + // disable packages to deactivate + for (int32 i = 0; BSolverPackage* package = fPackagesToDeactivate.ItemAt(i); + i++) { + if (!fDisabledPackages.HasItem(package)) + DisablePackage(package); + } + + // add packages to activate + for (int32 i = 0; BSolverPackage* package = fPackagesToActivate.ItemAt(i); + i++) { + status_t error = AddPackage(package->Info()); + if (error != B_OK) { + DIE(error, "failed to add package %s to %s repository", + package->Name().String(), Name().String()); + } + } +} + + +// #pragma mark - Transaction + + +BPackageManager::Transaction::Transaction(InstalledRepository& repository) + : + fRepository(repository), + fTransaction(), + fTransactionDirectory() +{ +} + + +BPackageManager::Transaction::~Transaction() +{ +} + + +// #pragma mark - TransactionHandler + + +BPackageManager::TransactionHandler::~TransactionHandler() +{ +} + + +// #pragma mark - DaemonClientTransactionHandler + + +BPackageManager::DaemonClientTransactionHandler + ::DaemonClientTransactionHandler() + : + fDaemonClient() +{ +} + + +BPackageManager::DaemonClientTransactionHandler + ::~DaemonClientTransactionHandler() +{ +} + + +status_t +BPackageManager::DaemonClientTransactionHandler::PrepareTransaction( + Transaction& transaction) +{ + return fDaemonClient.CreateTransaction(transaction.Repository().Location(), + transaction.ActivationTransaction(), + transaction.TransactionDirectory()); +} + + +status_t +BPackageManager::DaemonClientTransactionHandler::CommitTransaction( + Transaction& transaction, BDaemonClient::BCommitTransactionResult& _result) +{ + return fDaemonClient.CommitTransaction(transaction.ActivationTransaction(), + _result); +} + + +// #pragma mark - RequestHandler + + +BPackageManager::RequestHandler::~RequestHandler() +{ +} + + +// #pragma mark - UserInteractionHandler + + +BPackageManager::UserInteractionHandler::~UserInteractionHandler() +{ +} + + +} // namespace BPrivate + +} // namespace BManager + +} // namespace BPackageKit diff --git a/src/kits/package/manager/PackageManagerUtils.h b/src/kits/package/manager/PackageManagerUtils.h new file mode 100644 index 0000000000..8de9c5d510 --- /dev/null +++ b/src/kits/package/manager/PackageManagerUtils.h @@ -0,0 +1,24 @@ +/* + * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_MANAGER_UTILS_H +#define PACKAGE_MANAGER_UTILS_H + + +#include + + +#define DIE(...) \ +do { \ + throw BFatalErrorException(__VA_ARGS__); \ +} while(0) + + +#define DIE_DETAILS(details, ...) \ +do { \ + throw BFatalErrorException(__VA_ARGS__).SetDetails(details); \ +} while(0) + + +#endif // PACKAGE_MANAGER_UTILS_H diff --git a/src/bin/pkgman/RepositoryBuilder.cpp b/src/kits/package/manager/RepositoryBuilder.cpp similarity index 68% rename from src/bin/pkgman/RepositoryBuilder.cpp rename to src/kits/package/manager/RepositoryBuilder.cpp index 251f11bbd3..de2a7978aa 100644 --- a/src/bin/pkgman/RepositoryBuilder.cpp +++ b/src/kits/package/manager/RepositoryBuilder.cpp @@ -7,7 +7,7 @@ */ -#include "RepositoryBuilder.h" +#include #include #include @@ -18,11 +18,46 @@ #include -#include "PackageInfoErrorListener.h" -#include "pkgman.h" +#include "PackageManagerUtils.h" -RepositoryBuilder::RepositoryBuilder(BSolverRepository& repository, +namespace BPackageKit { + +namespace BManager { + +namespace BPrivate { + + +namespace { + + +class PackageInfoErrorListener : public BPackageInfo::ParseErrorListener { +public: + PackageInfoErrorListener(const char* context) + { + } + + virtual void OnError(const BString& message, int line, int column) + { + fErrors << BString().SetToFormat("%s: parse error in line %d:%d: %s\n", + fContext, line, column, message.String()); + } + + const BString& Errors() const + { + return fErrors; + } + +private: + const char* fContext; + BString fErrors; +}; + + +} // unnamed namespace + + +BRepositoryBuilder::BRepositoryBuilder(BSolverRepository& repository, const BString& name, const BString& errorName) : fRepository(repository), @@ -35,7 +70,7 @@ RepositoryBuilder::RepositoryBuilder(BSolverRepository& repository, } -RepositoryBuilder::RepositoryBuilder(BSolverRepository& repository, +BRepositoryBuilder::BRepositoryBuilder(BSolverRepository& repository, const BRepositoryConfig& config) : fRepository(repository), @@ -48,7 +83,7 @@ RepositoryBuilder::RepositoryBuilder(BSolverRepository& repository, } -RepositoryBuilder::RepositoryBuilder(BSolverRepository& repository, +BRepositoryBuilder::BRepositoryBuilder(BSolverRepository& repository, const BRepositoryCache& cache, const BString& errorName) : fRepository(repository), @@ -62,16 +97,16 @@ RepositoryBuilder::RepositoryBuilder(BSolverRepository& repository, } -RepositoryBuilder& -RepositoryBuilder::SetPackagePathMap(PackagePathMap* packagePaths) +BRepositoryBuilder& +BRepositoryBuilder::SetPackagePathMap(BPackagePathMap* packagePaths) { fPackagePaths = packagePaths; return *this; } -RepositoryBuilder& -RepositoryBuilder::AddPackage(const BPackageInfo& info, +BRepositoryBuilder& +BRepositoryBuilder::AddPackage(const BPackageInfo& info, const char* packageErrorName, BSolverPackage** _package) { status_t error = fRepository.AddPackage(info, _package); @@ -86,27 +121,28 @@ RepositoryBuilder::AddPackage(const BPackageInfo& info, } -RepositoryBuilder& -RepositoryBuilder::AddPackage(const char* path, BSolverPackage** _package) +BRepositoryBuilder& +BRepositoryBuilder::AddPackage(const char* path, BSolverPackage** _package) { // read a package info from the (HPKG or package info) file BPackageInfo packageInfo; size_t pathLength = strlen(path); status_t error; + PackageInfoErrorListener errorListener(path); if (pathLength > 5 && strcmp(path + pathLength - 5, ".hpkg") == 0) { // a package file error = packageInfo.ReadFromPackageFile(path); } else { // a package info file (supposedly) - PackageInfoErrorListener errorListener( - "Error: failed to read package info"); error = packageInfo.ReadFromConfigFile(BEntry(path, true), &errorListener); } - if (error != B_OK) - DIE(error, "failed to read package info from \"%s\"", path); + if (error != B_OK) { + DIE_DETAILS(errorListener.Errors(), error, + "failed to read package info from \"%s\"", path); + } // add the package BSolverPackage* package; @@ -123,8 +159,8 @@ RepositoryBuilder::AddPackage(const char* path, BSolverPackage** _package) } -RepositoryBuilder& -RepositoryBuilder::AddPackages(BPackageInstallationLocation location, +BRepositoryBuilder& +BRepositoryBuilder::AddPackages(BPackageInstallationLocation location, const char* locationErrorName) { status_t error = fRepository.AddPackages(location); @@ -136,8 +172,8 @@ RepositoryBuilder::AddPackages(BPackageInstallationLocation location, } -RepositoryBuilder& -RepositoryBuilder::AddPackagesDirectory(const char* path) +BRepositoryBuilder& +BRepositoryBuilder::AddPackagesDirectory(const char* path) { // open directory DIR* dir = opendir(path); @@ -172,8 +208,8 @@ RepositoryBuilder::AddPackagesDirectory(const char* path) } -RepositoryBuilder& -RepositoryBuilder::AddToSolver(BSolver* solver, bool isInstalled) +BRepositoryBuilder& +BRepositoryBuilder::AddToSolver(BSolver* solver, bool isInstalled) { fRepository.SetInstalled(isInstalled); @@ -184,3 +220,10 @@ RepositoryBuilder::AddToSolver(BSolver* solver, bool isInstalled) } return *this; } + + +} // namespace BPrivate + +} // namespace BManager + +} // namespace BPackageKit diff --git a/src/tools/get_package_dependencies/Jamfile b/src/tools/get_package_dependencies/Jamfile index 9425c0c554..90e636db21 100644 --- a/src/tools/get_package_dependencies/Jamfile +++ b/src/tools/get_package_dependencies/Jamfile @@ -8,10 +8,6 @@ USES_BE_API on get_package_dependencies = true ; BuildPlatformMain get_package_dependencies : get_package_dependencies.cpp - - # pkgman sources - PackageInfoErrorListener.cpp - RepositoryBuilder.cpp : libpackage-add-on-libsolv_build.so libsolvext_build.so libsolv_build.so diff --git a/src/tools/get_package_dependencies/get_package_dependencies.cpp b/src/tools/get_package_dependencies/get_package_dependencies.cpp index 189a8c7afb..22bf9eb459 100644 --- a/src/tools/get_package_dependencies/get_package_dependencies.cpp +++ b/src/tools/get_package_dependencies/get_package_dependencies.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -22,13 +23,22 @@ #include #include -#include "pkgman.h" -#include "RepositoryBuilder.h" + +using namespace BPackageKit; +using namespace BPackageKit::BManager::BPrivate; static const char* sProgramName = "get_package_dependencies"; +#define DIE(result, msg...) \ +do { \ + fprintf(stderr, "*** " msg); \ + fprintf(stderr, " : %s\n", strerror(result)); \ + exit(5); \ +} while(0) + + void print_usage_and_exit(bool error) { @@ -74,7 +84,7 @@ main(int argc, const char* const* argv) // add the "installed" repository with the given packages BSolverRepository installedRepository; { - RepositoryBuilder installedRepositoryBuilder(installedRepository, + BRepositoryBuilder installedRepositoryBuilder(installedRepository, "installed"); for (int i = 0; i < packageCount; i++) installedRepositoryBuilder.AddPackage(packages[i]); @@ -89,7 +99,7 @@ main(int argc, const char* const* argv) error = cache.SetTo(repositories[i]); if (error != B_OK) DIE(error, "failed to read repository file '%s'", repositories[i]); - RepositoryBuilder(*repository, cache) + BRepositoryBuilder(*repository, cache) .AddToSolver(solver, false); repositoryInfos[repository] = cache.Info(); }