Refactor reusable pkgman code into libpackage

* Move RepositoryBuilder class to libpackage and add B* prefix to name.
* Pull BPackageManager class out of PackageManager and move to
  libpackage. The base class is customizable via three handler objects
  responsible for transaction handling, request execution, respectively
  user interaction.
* Reorganize _ApplyPackageChanges(): Now we first prepare the
  transactions for all affected installation locations (downloading
  files etc.) and then commit them.
This commit is contained in:
Ingo Weinhold 2013-08-31 21:18:28 +02:00
parent a0cb235c0e
commit 83462cc28d
25 changed files with 1646 additions and 998 deletions

View File

@ -0,0 +1 @@
#include <../private/package/manager/Exceptions.h>

View File

@ -0,0 +1 @@
#include <../private/package/manager/RepositoryBuilder.h>

View File

@ -0,0 +1,74 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#ifndef _PACKAGE__MANAGER__PRIVATE__EXCEPTIONS_H_
#define _PACKAGE__MANAGER__PRIVATE__EXCEPTIONS_H_
#include <package/Context.h>
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_

View File

@ -0,0 +1,264 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#ifndef _PACKAGE__MANAGER__PRIVATE__PACKAGE_MANAGER_H_
#define _PACKAGE__MANAGER__PRIVATE__PACKAGE_MANAGER_H_
#include <Directory.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 <package/ActivationTransaction.h>
#include <package/DaemonClient.h>
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<RemoteRepository> RemoteRepositoryList;
typedef BObjectList<InstalledRepository> InstalledRepositoryList;
typedef BObjectList<BSolverPackage> PackageList;
typedef BObjectList<Transaction> 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<BSolverPackage> 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_

View File

@ -0,0 +1,74 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#ifndef _PACKAGE__MANAGER__PRIVATE__REPOSITORY_BUILDER_H_
#define _PACKAGE__MANAGER__PRIVATE__REPOSITORY_BUILDER_H_
#include <map>
#include <package/PackageInfo.h>
#include <package/solver/Solver.h>
#include <package/solver/SolverPackage.h>
#include <package/solver/SolverRepository.h>
namespace BPackageKit {
namespace BManager {
namespace BPrivate {
typedef std::map<BSolverPackage*, BString> 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_

View File

@ -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++)

View File

@ -1,27 +0,0 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#include "PackageInfoErrorListener.h"
#include <stdio.h>
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());
}

View File

@ -1,31 +0,0 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#ifndef PACKAGE_INFO_ERROR_LISTENER_H
#define PACKAGE_INFO_ERROR_LISTENER_H
#include <package/PackageInfo.h>
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

View File

@ -9,516 +9,119 @@
#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 <CopyEngine.h>
#include <package/ActivationTransaction.h>
#include <package/DaemonClient.h>
#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<InstalledRepository*>(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<RemoteRepository*>(package->Repository());
if (remoteRepository == NULL) {
// clone the existing package
_ClonePackageFile(
dynamic_cast<InstalledRepository*>(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;
}

View File

@ -9,151 +9,58 @@
#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 <package/DaemonClient.h>
#include <package/manager/PackageManager.h>
#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<RemoteRepository> RemoteRepositoryList;
typedef BObjectList<InstalledRepository> InstalledRepositoryList;
typedef BObjectList<BSolverPackage> 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<BSolverPackage> 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;
};

View File

@ -1,62 +0,0 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#ifndef REPOSITORY_BUILDER_H
#define REPOSITORY_BUILDER_H
#include <map>
#include <package/PackageInfo.h>
#include <package/solver/Solver.h>
#include <package/solver/SolverPackage.h>
#include <package/solver/SolverRepository.h>
using namespace BPackageKit;
typedef std::map<BSolverPackage*, BString> 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

View File

@ -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;

View File

@ -13,6 +13,7 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <package/manager/RepositoryBuilder.h>
#include <package/solver/SolverPackageSpecifier.h>
#include <package/solver/SolverPackageSpecifierList.h>
#include <package/solver/SolverProblem.h>
@ -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<BSolverRepository> 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(),

View File

@ -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<BSolverPackage> packages;

View File

@ -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;

View File

@ -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;

View File

@ -11,9 +11,14 @@
#include <stdlib.h>
#include <string.h>
#include <package/manager/Exceptions.h>
#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;
}
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,107 @@
/*
* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <package/manager/Exceptions.h>
#include <stdarg.h>
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

View File

@ -0,0 +1,824 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#include <package/manager/PackageManager.h>
#include <Directory.h>
#include <package/PackageRoster.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 <CopyEngine.h>
#include <package/ActivationTransaction.h>
#include <package/DaemonClient.h>
#include <package/manager/RepositoryBuilder.h>
#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<InstalledRepository*>(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<RemoteRepository*>(package->Repository());
if (remoteRepository == NULL) {
// clone the existing package
_ClonePackageFile(
dynamic_cast<InstalledRepository*>(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

View File

@ -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 <package/manager/Exceptions.h>
#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

View File

@ -7,7 +7,7 @@
*/
#include "RepositoryBuilder.h"
#include <package/manager/RepositoryBuilder.h>
#include <errno.h>
#include <dirent.h>
@ -18,11 +18,46 @@
#include <AutoDeleter.h>
#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

View File

@ -8,10 +8,6 @@ USES_BE_API on <build>get_package_dependencies = true ;
BuildPlatformMain <build>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

View File

@ -14,6 +14,7 @@
#include <map>
#include <package/RepositoryCache.h>
#include <package/manager/RepositoryBuilder.h>
#include <package/solver/Solver.h>
#include <package/solver/SolverPackageSpecifier.h>
#include <package/solver/SolverPackageSpecifierList.h>
@ -22,13 +23,22 @@
#include <package/solver/SolverRepository.h>
#include <package/solver/SolverResult.h>
#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();
}