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:
parent
a0cb235c0e
commit
83462cc28d
1
headers/build/private/package/manager/Exceptions.h
Normal file
1
headers/build/private/package/manager/Exceptions.h
Normal file
@ -0,0 +1 @@
|
||||
#include <../private/package/manager/Exceptions.h>
|
@ -0,0 +1 @@
|
||||
#include <../private/package/manager/RepositoryBuilder.h>
|
74
headers/private/package/manager/Exceptions.h
Normal file
74
headers/private/package/manager/Exceptions.h
Normal 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_
|
264
headers/private/package/manager/PackageManager.h
Normal file
264
headers/private/package/manager/PackageManager.h
Normal 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_
|
74
headers/private/package/manager/RepositoryBuilder.h
Normal file
74
headers/private/package/manager/RepositoryBuilder.h
Normal 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_
|
@ -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++)
|
||||
|
@ -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());
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
@ -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;
|
||||
|
@ -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(),
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
107
src/kits/package/manager/Exceptions.cpp
Normal file
107
src/kits/package/manager/Exceptions.cpp
Normal 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
|
824
src/kits/package/manager/PackageManager.cpp
Normal file
824
src/kits/package/manager/PackageManager.cpp
Normal 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
|
24
src/kits/package/manager/PackageManagerUtils.h
Normal file
24
src/kits/package/manager/PackageManagerUtils.h
Normal 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
|
@ -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
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user