pkgman: Refactoring -> PackageManager
Move common and reusable functionality from "search" and "install" to new PackageManager class.
This commit is contained in:
@@ -14,6 +14,7 @@ BinCommand pkgman :
|
||||
DecisionProvider.cpp
|
||||
JobStateListener.cpp
|
||||
PackageInfoErrorListener.cpp
|
||||
PackageManager.cpp
|
||||
pkgman.cpp
|
||||
RepositoryBuilder.cpp
|
||||
:
|
||||
|
||||
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* Copyright 2013, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Ingo Weinhold <[email protected]>
|
||||
*/
|
||||
|
||||
|
||||
#include "PackageManager.h"
|
||||
|
||||
#include <Directory.h>
|
||||
#include <package/DownloadFileRequest.h>
|
||||
#include <package/PackageRoster.h>
|
||||
#include <package/RefreshRepositoryRequest.h>
|
||||
#include <package/solver/SolverPackage.h>
|
||||
#include <package/solver/SolverPackageSpecifier.h>
|
||||
#include <package/solver/SolverPackageSpecifierList.h>
|
||||
#include <package/solver/SolverProblem.h>
|
||||
#include <package/solver/SolverProblemSolution.h>
|
||||
#include <package/solver/SolverResult.h>
|
||||
|
||||
#include <package/ActivationTransaction.h>
|
||||
#include <package/DaemonClient.h>
|
||||
|
||||
#include "pkgman.h"
|
||||
#include "RepositoryBuilder.h"
|
||||
|
||||
|
||||
using namespace BPackageKit::BPrivate;
|
||||
|
||||
|
||||
// #pragma mark - Repository
|
||||
|
||||
|
||||
PackageManager::Repository::Repository()
|
||||
:
|
||||
BSolverRepository()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
PackageManager::Repository::Init(BPackageRoster& roster, BContext& context,
|
||||
const char* name)
|
||||
{
|
||||
// get the repository config
|
||||
status_t error = roster.GetRepositoryConfig(name, &fConfig);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// refresh
|
||||
BRefreshRepositoryRequest refreshRequest(context, fConfig);
|
||||
error = refreshRequest.Process();
|
||||
if (error != B_OK) {
|
||||
WARN(error, "refreshing repository \"%s\" failed", name);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// re-get the config
|
||||
return roster.GetRepositoryConfig(name, &fConfig);
|
||||
}
|
||||
|
||||
|
||||
const BRepositoryConfig&
|
||||
PackageManager::Repository::Config() const
|
||||
{
|
||||
return fConfig;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Solver
|
||||
|
||||
|
||||
PackageManager::PackageManager(BPackageInstallationLocation location,
|
||||
bool addInstalledRepositories, bool addOtherRepositories)
|
||||
:
|
||||
fLocation(location),
|
||||
fSolver(NULL),
|
||||
fSystemRepository(),
|
||||
fCommonRepository(),
|
||||
fHomeRepository(),
|
||||
fInstalledRepositories(10),
|
||||
fOtherRepositories(10, true),
|
||||
fDecisionProvider(),
|
||||
fJobStateListener(),
|
||||
fContext(fDecisionProvider, fJobStateListener)
|
||||
{
|
||||
// create the solver
|
||||
status_t error = BSolver::Create(fSolver);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to create solver");
|
||||
|
||||
// add installation location repositories
|
||||
if (addInstalledRepositories) {
|
||||
// We add only the repository of our actual installation location as the
|
||||
// "installed" repository. The repositories for the more general
|
||||
// installation locations are added as regular repositories, but with
|
||||
// better priorities than the actual (remote) repositories. This
|
||||
// prevents the solver from showing conflicts when a package in a more
|
||||
// specific installation location overrides a package in a more general
|
||||
// one. Instead any requirement that is already installed in a more
|
||||
// general installation location will turn up as to be installed as
|
||||
// well. But we can easily filter those out.
|
||||
RepositoryBuilder(fSystemRepository, "system")
|
||||
.AddPackages(B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, "system")
|
||||
.AddToSolver(fSolver, false);
|
||||
fSystemRepository.SetPriority(-1);
|
||||
|
||||
bool installInHome = location == B_PACKAGE_INSTALLATION_LOCATION_HOME;
|
||||
RepositoryBuilder(fCommonRepository, "common")
|
||||
.AddPackages(B_PACKAGE_INSTALLATION_LOCATION_COMMON, "common")
|
||||
.AddToSolver(fSolver, !installInHome);
|
||||
|
||||
if (!fInstalledRepositories.AddItem(&fSystemRepository)
|
||||
|| !fInstalledRepositories.AddItem(&fCommonRepository)) {
|
||||
DIE(B_NO_MEMORY, "failed to add installed repositories to list");
|
||||
}
|
||||
|
||||
if (installInHome) {
|
||||
fCommonRepository.SetPriority(-2);
|
||||
RepositoryBuilder(fHomeRepository, "home")
|
||||
.AddPackages(B_PACKAGE_INSTALLATION_LOCATION_HOME, "home")
|
||||
.AddToSolver(fSolver, true);
|
||||
|
||||
if (!fInstalledRepositories.AddItem(&fHomeRepository))
|
||||
DIE(B_NO_MEMORY, "failed to add home repository to list");
|
||||
}
|
||||
}
|
||||
|
||||
// add other repositories
|
||||
if (addOtherRepositories) {
|
||||
BPackageRoster roster;
|
||||
BStringList repositoryNames;
|
||||
error = roster.GetRepositoryNames(repositoryNames);
|
||||
if (error != B_OK)
|
||||
WARN(error, "failed to get repository names");
|
||||
|
||||
int32 repositoryNameCount = repositoryNames.CountStrings();
|
||||
for (int32 i = 0; i < repositoryNameCount; i++) {
|
||||
Repository* repository = new(std::nothrow) Repository;
|
||||
if (repository == NULL || !fOtherRepositories.AddItem(repository))
|
||||
DIE(B_NO_MEMORY, "failed to create/add repository object");
|
||||
|
||||
const BString& name = repositoryNames.StringAt(i);
|
||||
error = repository->Init(roster, fContext, name);
|
||||
if (error != B_OK) {
|
||||
WARN(error,
|
||||
"failed to get config for repository \"%s\". Skipping.",
|
||||
name.String());
|
||||
fOtherRepositories.RemoveItem(repository, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
RepositoryBuilder(*repository, repository->Config())
|
||||
.AddToSolver(fSolver, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PackageManager::~PackageManager()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PackageManager::Install(const char* const* packages, int packageCount)
|
||||
{
|
||||
// solve
|
||||
BSolverPackageSpecifierList packagesToInstall;
|
||||
for (int i = 0; i < packageCount; i++) {
|
||||
if (!packagesToInstall.AppendSpecifier(packages[i]))
|
||||
DIE(B_NO_MEMORY, "failed to add specified package");
|
||||
}
|
||||
|
||||
const BSolverPackageSpecifier* unmatchedSpecifier;
|
||||
status_t error = fSolver->Install(packagesToInstall, &unmatchedSpecifier);
|
||||
if (error != B_OK) {
|
||||
if (unmatchedSpecifier != NULL) {
|
||||
DIE(error, "failed to find a match for \"%s\"",
|
||||
unmatchedSpecifier->SelectString().String());
|
||||
} else
|
||||
DIE(error, "failed to compute packages to install");
|
||||
}
|
||||
|
||||
_HandleProblems();
|
||||
|
||||
// install/uninstall packages
|
||||
_AnalyzeResult();
|
||||
_PrintResult();
|
||||
_ApplyPackageChanges();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PackageManager::_HandleProblems()
|
||||
{
|
||||
while (fSolver->HasProblems()) {
|
||||
printf("Encountered problems:\n");
|
||||
|
||||
int32 problemCount = fSolver->CountProblems();
|
||||
for (int32 i = 0; i < problemCount; i++) {
|
||||
// print problem and possible solutions
|
||||
BSolverProblem* problem = fSolver->ProblemAt(i);
|
||||
printf("problem %" B_PRId32 ": %s\n", i + 1,
|
||||
problem->ToString().String());
|
||||
|
||||
int32 solutionCount = problem->CountSolutions();
|
||||
for (int32 k = 0; k < solutionCount; k++) {
|
||||
const BSolverProblemSolution* solution = problem->SolutionAt(k);
|
||||
printf(" solution %" B_PRId32 ":\n", k + 1);
|
||||
int32 elementCount = solution->CountElements();
|
||||
for (int32 l = 0; l < elementCount; l++) {
|
||||
const BSolverProblemSolutionElement* element
|
||||
= solution->ElementAt(l);
|
||||
printf(" - %s\n", element->ToString().String());
|
||||
}
|
||||
}
|
||||
|
||||
// let the user choose a solution
|
||||
printf("Please select a solution, skip the problem for now or "
|
||||
"quit.\n");
|
||||
for (;;) {
|
||||
if (solutionCount > 1)
|
||||
printf("select [1...%" B_PRId32 "/s/q]: ", solutionCount);
|
||||
else
|
||||
printf("select [1/s/q]: ");
|
||||
|
||||
char buffer[32];
|
||||
if (fgets(buffer, sizeof(buffer), stdin) == NULL
|
||||
|| strcmp(buffer, "q\n") == 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (strcmp(buffer, "s\n") == 0)
|
||||
break;
|
||||
|
||||
char* end;
|
||||
long selected = strtol(buffer, &end, 0);
|
||||
if (end == buffer || *end != '\n' || selected < 1
|
||||
|| selected > solutionCount) {
|
||||
printf("*** invalid input\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
status_t error = fSolver->SelectProblemSolution(problem,
|
||||
problem->SolutionAt(selected - 1));
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to set solution");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
status_t error = fSolver->SolveAgain();
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to compute packages to install");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PackageManager::_AnalyzeResult()
|
||||
{
|
||||
BSolverResult result;
|
||||
status_t error = fSolver->GetResult(result);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to compute packages to install");
|
||||
|
||||
for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i);
|
||||
i++) {
|
||||
BSolverPackage* package = element->Package();
|
||||
|
||||
switch (element->Type()) {
|
||||
case BSolverResultElement::B_TYPE_INSTALL:
|
||||
if (!fInstalledRepositories.HasItem(package->Repository())) {
|
||||
if (!fPackagesToActivate.AddItem(package))
|
||||
DIE(B_NO_MEMORY, "failed to add package to activate");
|
||||
}
|
||||
break;
|
||||
|
||||
case BSolverResultElement::B_TYPE_UNINSTALL:
|
||||
if (!fPackagesToDeactivate.AddItem(package))
|
||||
DIE(B_NO_MEMORY, "failed to add package to deactivate");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fPackagesToActivate.IsEmpty() && fPackagesToDeactivate.IsEmpty()) {
|
||||
printf("Nothing to do.\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PackageManager::_PrintResult()
|
||||
{
|
||||
printf("The following changes will be made:\n");
|
||||
for (int32 i = 0; BSolverPackage* package = fPackagesToActivate.ItemAt(i);
|
||||
i++) {
|
||||
printf(" install package %s from repository %s\n",
|
||||
package->Info().CanonicalFileName().String(),
|
||||
package->Repository()->Name().String());
|
||||
}
|
||||
|
||||
for (int32 i = 0; BSolverPackage* package = fPackagesToDeactivate.ItemAt(i);
|
||||
i++) {
|
||||
printf(" uninstall package %s\n", package->VersionedName().String());
|
||||
}
|
||||
// TODO: Print file/download sizes. Unfortunately our package infos don't
|
||||
// contain the file size. Which is probably correct. The file size (and possibly
|
||||
// other information) should, however, be provided by the repository cache in
|
||||
// some way. Extend BPackageInfo? Create a BPackageFileInfo?
|
||||
|
||||
if (!fDecisionProvider.YesNoDecisionNeeded(BString(), "Continue?", "y", "n",
|
||||
"y")) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PackageManager::_ApplyPackageChanges()
|
||||
{
|
||||
// create an activation transaction
|
||||
BDaemonClient daemonClient;
|
||||
BActivationTransaction transaction;
|
||||
BDirectory transactionDirectory;
|
||||
status_t error = daemonClient.CreateTransaction(fLocation, transaction,
|
||||
transactionDirectory);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to create transaction");
|
||||
|
||||
// download the new packages and prepare the transaction
|
||||
for (int32 i = 0; BSolverPackage* package = fPackagesToActivate.ItemAt(i);
|
||||
i++) {
|
||||
// get package URL and target entry
|
||||
Repository* repository
|
||||
= static_cast<Repository*>(package->Repository());
|
||||
BString url = repository->Config().BaseURL();
|
||||
BString fileName(package->Info().CanonicalFileName());
|
||||
if (fileName.IsEmpty())
|
||||
DIE(B_NO_MEMORY, "failed to allocate file name");
|
||||
url << '/' << fileName;
|
||||
|
||||
BEntry entry;
|
||||
error = entry.SetTo(&transactionDirectory, fileName);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to create package entry");
|
||||
|
||||
// download the package
|
||||
DownloadFileRequest downloadRequest(fContext, url, entry,
|
||||
package->Info().Checksum());
|
||||
error = downloadRequest.Process();
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to download package");
|
||||
|
||||
// add package to transaction
|
||||
if (!transaction.AddPackageToActivate(
|
||||
package->Info().CanonicalFileName())) {
|
||||
DIE(B_NO_MEMORY,
|
||||
"failed to add package to activate to transaction");
|
||||
}
|
||||
}
|
||||
|
||||
for (int32 i = 0; BSolverPackage* package = fPackagesToDeactivate.ItemAt(i);
|
||||
i++) {
|
||||
// add package to transaction
|
||||
if (!transaction.AddPackageToDeactivate(
|
||||
package->Info().CanonicalFileName())) {
|
||||
DIE(B_NO_MEMORY,
|
||||
"failed to add package to deactivate to transaction");
|
||||
}
|
||||
}
|
||||
|
||||
// commit the transaction
|
||||
BDaemonClient::BCommitTransactionResult transactionResult;
|
||||
error = daemonClient.CommitTransaction(transaction, transactionResult);
|
||||
if (error != B_OK) {
|
||||
fprintf(stderr, "*** failed to commit transaction: %s\n",
|
||||
transactionResult.FullErrorMessage().String());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("Installation done. Old activation state backed up in \"%s\"\n",
|
||||
transactionResult.OldStateDirectory().String());
|
||||
|
||||
printf("Cleaning up ...\n");
|
||||
BEntry transactionDirectoryEntry;
|
||||
if ((error = transactionDirectory.GetEntry(&transactionDirectoryEntry))
|
||||
!= B_OK
|
||||
|| (error = transactionDirectoryEntry.Remove()) != B_OK) {
|
||||
WARN(error, "failed to remove transaction directory");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2013, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Ingo Weinhold <[email protected]>
|
||||
*/
|
||||
#ifndef PACKAGE_MANAGER_H
|
||||
#define PACKAGE_MANAGER_H
|
||||
|
||||
|
||||
#include <ObjectList.h>
|
||||
#include <package/Context.h>
|
||||
#include <package/PackageDefs.h>
|
||||
#include <package/PackageRoster.h>
|
||||
#include <package/RepositoryConfig.h>
|
||||
#include <package/solver/Solver.h>
|
||||
#include <package/solver/SolverRepository.h>
|
||||
|
||||
#include "DecisionProvider.h"
|
||||
#include "JobStateListener.h"
|
||||
|
||||
|
||||
using namespace BPackageKit;
|
||||
|
||||
|
||||
class PackageManager {
|
||||
public:
|
||||
struct Repository;
|
||||
typedef BObjectList<Repository> RepositoryList;
|
||||
|
||||
public:
|
||||
PackageManager(
|
||||
BPackageInstallationLocation location,
|
||||
bool addInstalledRepositories,
|
||||
bool addOtherRepositories);
|
||||
~PackageManager();
|
||||
|
||||
BSolver* Solver() const
|
||||
{ return fSolver; }
|
||||
|
||||
const BSolverRepository* SystemRepository() const
|
||||
{ return &fSystemRepository; }
|
||||
const BSolverRepository* CommonRepository() const
|
||||
{ return &fCommonRepository; }
|
||||
const BSolverRepository* HomeRepository() const
|
||||
{ return &fHomeRepository; }
|
||||
const BObjectList<BSolverRepository>& InstalledRepositories() const
|
||||
{ return fInstalledRepositories; }
|
||||
const RepositoryList& OtherRepositories() const
|
||||
{ return fOtherRepositories; }
|
||||
|
||||
void Install(const char* const* packages,
|
||||
int packageCount);
|
||||
|
||||
private:
|
||||
typedef BObjectList<BSolverPackage> PackageList;
|
||||
|
||||
private:
|
||||
void _HandleProblems();
|
||||
void _AnalyzeResult();
|
||||
void _PrintResult();
|
||||
void _ApplyPackageChanges();
|
||||
|
||||
private:
|
||||
BPackageInstallationLocation fLocation;
|
||||
BSolver* fSolver;
|
||||
BSolverRepository fSystemRepository;
|
||||
BSolverRepository fCommonRepository;
|
||||
BSolverRepository fHomeRepository;
|
||||
BObjectList<BSolverRepository> fInstalledRepositories;
|
||||
RepositoryList fOtherRepositories;
|
||||
DecisionProvider fDecisionProvider;
|
||||
JobStateListener fJobStateListener;
|
||||
BContext fContext;
|
||||
PackageList fPackagesToActivate;
|
||||
PackageList fPackagesToDeactivate;
|
||||
};
|
||||
|
||||
|
||||
struct PackageManager::Repository : public BSolverRepository {
|
||||
Repository();
|
||||
|
||||
status_t Init(BPackageRoster& roster, BContext& context,
|
||||
const char* name);
|
||||
|
||||
const BRepositoryConfig& Config() const;
|
||||
|
||||
private:
|
||||
BRepositoryConfig fConfig;
|
||||
};
|
||||
|
||||
|
||||
#endif // PACKAGE_MANAGER_H
|
||||
@@ -7,31 +7,13 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Directory.h>
|
||||
#include <package/DownloadFileRequest.h>
|
||||
#include <package/PackageRoster.h>
|
||||
#include <package/RefreshRepositoryRequest.h>
|
||||
#include <package/RepositoryConfig.h>
|
||||
#include <package/solver/SolverPackageSpecifier.h>
|
||||
#include <package/solver/SolverPackageSpecifierList.h>
|
||||
#include <package/solver/SolverProblem.h>
|
||||
#include <package/solver/SolverProblemSolution.h>
|
||||
#include <package/solver/SolverResult.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <package/ActivationTransaction.h>
|
||||
#include <package/DaemonClient.h>
|
||||
|
||||
#include "Command.h"
|
||||
#include "DecisionProvider.h"
|
||||
#include "JobStateListener.h"
|
||||
#include "pkgman.h"
|
||||
#include "RepositoryBuilder.h"
|
||||
#include "PackageManager.h"
|
||||
|
||||
|
||||
// TODO: internationalization!
|
||||
@@ -59,42 +41,6 @@ static const char* const kLongUsage =
|
||||
DEFINE_COMMAND(InstallCommand, "install", kShortUsage, kLongUsage)
|
||||
|
||||
|
||||
struct Repository : public BSolverRepository {
|
||||
Repository()
|
||||
:
|
||||
BSolverRepository()
|
||||
{
|
||||
}
|
||||
|
||||
status_t Init(BPackageRoster& roster, BContext& context, const char* name)
|
||||
{
|
||||
// get the repository config
|
||||
status_t error = roster.GetRepositoryConfig(name, &fConfig);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// refresh
|
||||
BRefreshRepositoryRequest refreshRequest(context, fConfig);
|
||||
error = refreshRequest.Process();
|
||||
if (error != B_OK) {
|
||||
WARN(error, "refreshing repository \"%s\" failed", name);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// re-get the config
|
||||
return roster.GetRepositoryConfig(name, &fConfig);
|
||||
}
|
||||
|
||||
const BRepositoryConfig& Config() const
|
||||
{
|
||||
return fConfig;
|
||||
}
|
||||
|
||||
private:
|
||||
BRepositoryConfig fConfig;
|
||||
};
|
||||
|
||||
|
||||
int
|
||||
InstallCommand::Execute(int argc, const char* const* argv)
|
||||
{
|
||||
@@ -134,289 +80,12 @@ InstallCommand::Execute(int argc, const char* const* argv)
|
||||
int packageCount = argc - optind;
|
||||
const char* const* packages = argv + optind;
|
||||
|
||||
// create the solver
|
||||
BSolver* solver;
|
||||
status_t error = BSolver::Create(solver);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to create solver");
|
||||
|
||||
// add repositories
|
||||
|
||||
// We add only the repository of our actual installation location as the
|
||||
// "installed" repository. The repositories for the more general
|
||||
// installation locations are added as regular repositories, but with better
|
||||
// priorities than the actual (remote) repositories. This prevents the solver
|
||||
// from showing conflicts when a package in a more specific installation
|
||||
// location overrides a package in a more general one. Instead any
|
||||
// requirement that is already installed in a more general installation
|
||||
// location will turn up as to be installed as well. But we can easily
|
||||
// filter those out.
|
||||
BSolverRepository systemRepository;
|
||||
RepositoryBuilder(systemRepository, "system")
|
||||
.AddPackages(B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, "system")
|
||||
.AddToSolver(solver, false);
|
||||
systemRepository.SetPriority(-1);
|
||||
|
||||
BSolverRepository commonRepository;
|
||||
RepositoryBuilder(commonRepository, "common")
|
||||
.AddPackages(B_PACKAGE_INSTALLATION_LOCATION_COMMON, "common")
|
||||
.AddToSolver(solver, !installInHome);
|
||||
|
||||
BObjectList<BSolverRepository> installedRepositories(10);
|
||||
if (!installedRepositories.AddItem(&systemRepository)
|
||||
|| !installedRepositories.AddItem(&commonRepository)) {
|
||||
DIE(B_NO_MEMORY, "failed to add installed repositories to list");
|
||||
}
|
||||
|
||||
BSolverRepository homeRepository;
|
||||
if (installInHome) {
|
||||
commonRepository.SetPriority(-2);
|
||||
RepositoryBuilder(homeRepository, "home")
|
||||
.AddPackages(B_PACKAGE_INSTALLATION_LOCATION_HOME, "home")
|
||||
.AddToSolver(solver, true);
|
||||
|
||||
if (!installedRepositories.AddItem(&homeRepository))
|
||||
DIE(B_NO_MEMORY, "failed to add home repository to list");
|
||||
}
|
||||
|
||||
// other repositories
|
||||
DecisionProvider decisionProvider;
|
||||
JobStateListener listener;
|
||||
BContext context(decisionProvider, listener);
|
||||
|
||||
BObjectList<Repository> otherRepositories(10, true);
|
||||
BPackageRoster roster;
|
||||
BStringList repositoryNames;
|
||||
error = roster.GetRepositoryNames(repositoryNames);
|
||||
if (error != B_OK)
|
||||
WARN(error, "failed to get repository names");
|
||||
|
||||
int32 repositoryNameCount = repositoryNames.CountStrings();
|
||||
for (int32 i = 0; i < repositoryNameCount; i++) {
|
||||
Repository* repository = new(std::nothrow) Repository;
|
||||
if (repository == NULL || !otherRepositories.AddItem(repository))
|
||||
DIE(B_NO_MEMORY, "failed to create/add repository object");
|
||||
|
||||
const BString& name = repositoryNames.StringAt(i);
|
||||
error = repository->Init(roster, context, name);
|
||||
if (error != B_OK) {
|
||||
WARN(error, "failed to get config for repository \"%s\". Skipping.",
|
||||
name.String());
|
||||
otherRepositories.RemoveItem(repository, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
RepositoryBuilder(*repository, repository->Config())
|
||||
.AddToSolver(solver, false);
|
||||
}
|
||||
|
||||
// solve
|
||||
BSolverPackageSpecifierList packagesToInstall;
|
||||
for (int i = 0; i < packageCount; i++) {
|
||||
if (!packagesToInstall.AppendSpecifier(packages[i]))
|
||||
DIE(B_NO_MEMORY, "failed to add specified package");
|
||||
}
|
||||
|
||||
const BSolverPackageSpecifier* unmatchedSpecifier;
|
||||
error = solver->Install(packagesToInstall, &unmatchedSpecifier);
|
||||
if (error != B_OK) {
|
||||
if (unmatchedSpecifier != NULL) {
|
||||
DIE(error, "failed to find a match for \"%s\"",
|
||||
unmatchedSpecifier->SelectString().String());
|
||||
} else
|
||||
DIE(error, "failed to compute packages to install");
|
||||
}
|
||||
|
||||
// deal with problems
|
||||
while (solver->HasProblems()) {
|
||||
printf("Encountered problems:\n");
|
||||
|
||||
int32 problemCount = solver->CountProblems();
|
||||
for (int32 i = 0; i < problemCount; i++) {
|
||||
// print problem and possible solutions
|
||||
BSolverProblem* problem = solver->ProblemAt(i);
|
||||
printf("problem %" B_PRId32 ": %s\n", i + 1,
|
||||
problem->ToString().String());
|
||||
|
||||
int32 solutionCount = problem->CountSolutions();
|
||||
for (int32 k = 0; k < solutionCount; k++) {
|
||||
const BSolverProblemSolution* solution = problem->SolutionAt(k);
|
||||
printf(" solution %" B_PRId32 ":\n", k + 1);
|
||||
int32 elementCount = solution->CountElements();
|
||||
for (int32 l = 0; l < elementCount; l++) {
|
||||
const BSolverProblemSolutionElement* element
|
||||
= solution->ElementAt(l);
|
||||
printf(" - %s\n", element->ToString().String());
|
||||
}
|
||||
}
|
||||
|
||||
// let the user choose a solution
|
||||
printf("Please select a solution, skip the problem for now or "
|
||||
"quit.\n");
|
||||
for (;;) {
|
||||
if (solutionCount > 1)
|
||||
printf("select [1...%" B_PRId32 "/s/q]: ", solutionCount);
|
||||
else
|
||||
printf("select [1/s/q]: ");
|
||||
|
||||
char buffer[32];
|
||||
if (fgets(buffer, sizeof(buffer), stdin) == NULL
|
||||
|| strcmp(buffer, "q\n") == 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (strcmp(buffer, "s\n") == 0)
|
||||
break;
|
||||
|
||||
char* end;
|
||||
long selected = strtol(buffer, &end, 0);
|
||||
if (end == buffer || *end != '\n' || selected < 1
|
||||
|| selected > solutionCount) {
|
||||
printf("*** invalid input\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
error = solver->SelectProblemSolution(problem,
|
||||
problem->SolutionAt(selected - 1));
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to set solution");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
error = solver->SolveAgain();
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to compute packages to install");
|
||||
}
|
||||
|
||||
// print result
|
||||
BSolverResult result;
|
||||
error = solver->GetResult(result);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to compute packages to install");
|
||||
|
||||
BObjectList<BSolverPackage> packagesToActivate;
|
||||
BObjectList<BSolverPackage> packagesToDeactivate;
|
||||
|
||||
for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i);
|
||||
i++) {
|
||||
BSolverPackage* package = element->Package();
|
||||
|
||||
switch (element->Type()) {
|
||||
case BSolverResultElement::B_TYPE_INSTALL:
|
||||
if (!installedRepositories.HasItem(package->Repository())) {
|
||||
if (!packagesToActivate.AddItem(package))
|
||||
DIE(B_NO_MEMORY, "failed to add package to activate");
|
||||
}
|
||||
break;
|
||||
|
||||
case BSolverResultElement::B_TYPE_UNINSTALL:
|
||||
if (!packagesToDeactivate.AddItem(package))
|
||||
DIE(B_NO_MEMORY, "failed to add package to deactivate");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (packagesToActivate.IsEmpty() && packagesToDeactivate.IsEmpty()) {
|
||||
printf("Nothing to do.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
printf("The following changes will be made:\n");
|
||||
for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i);
|
||||
i++) {
|
||||
printf(" install package %s from repository %s\n",
|
||||
package->Info().CanonicalFileName().String(),
|
||||
package->Repository()->Name().String());
|
||||
}
|
||||
|
||||
for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i);
|
||||
i++) {
|
||||
printf(" uninstall package %s\n", package->VersionedName().String());
|
||||
}
|
||||
// TODO: Print file/download sizes. Unfortunately our package infos don't
|
||||
// contain the file size. Which is probably correct. The file size (and possibly
|
||||
// other information) should, however, be provided by the repository cache in
|
||||
// some way. Extend BPackageInfo? Create a BPackageFileInfo?
|
||||
|
||||
if (!decisionProvider.YesNoDecisionNeeded(BString(), "Continue?", "y", "n",
|
||||
"y")) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// create an activation transaction
|
||||
BDaemonClient daemonClient;
|
||||
// perform the installation
|
||||
BPackageInstallationLocation location = installInHome
|
||||
? B_PACKAGE_INSTALLATION_LOCATION_HOME
|
||||
: B_PACKAGE_INSTALLATION_LOCATION_COMMON;
|
||||
BActivationTransaction transaction;
|
||||
BDirectory transactionDirectory;
|
||||
error = daemonClient.CreateTransaction(location, transaction,
|
||||
transactionDirectory);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to create transaction");
|
||||
|
||||
// download the new packages and prepare the transaction
|
||||
for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i);
|
||||
i++) {
|
||||
// get package URL and target entry
|
||||
Repository* repository
|
||||
= static_cast<Repository*>(package->Repository());
|
||||
BString url = repository->Config().BaseURL();
|
||||
BString fileName(package->Info().CanonicalFileName());
|
||||
if (fileName.IsEmpty())
|
||||
DIE(B_NO_MEMORY, "failed to allocate file name");
|
||||
url << '/' << fileName;
|
||||
|
||||
BEntry entry;
|
||||
error = entry.SetTo(&transactionDirectory, fileName);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to create package entry");
|
||||
|
||||
// download the package
|
||||
DownloadFileRequest downloadRequest(context, url, entry,
|
||||
package->Info().Checksum());
|
||||
error = downloadRequest.Process();
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to download package");
|
||||
|
||||
// add package to transaction
|
||||
if (!transaction.AddPackageToActivate(
|
||||
package->Info().CanonicalFileName())) {
|
||||
DIE(B_NO_MEMORY,
|
||||
"failed to add package to activate to transaction");
|
||||
}
|
||||
}
|
||||
|
||||
for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i);
|
||||
i++) {
|
||||
// add package to transaction
|
||||
if (!transaction.AddPackageToDeactivate(
|
||||
package->Info().CanonicalFileName())) {
|
||||
DIE(B_NO_MEMORY,
|
||||
"failed to add package to deactivate to transaction");
|
||||
}
|
||||
}
|
||||
|
||||
// commit the transaction
|
||||
BDaemonClient::BCommitTransactionResult transactionResult;
|
||||
error = daemonClient.CommitTransaction(transaction, transactionResult);
|
||||
if (error != B_OK) {
|
||||
fprintf(stderr, "*** failed to commit transaction: %s\n",
|
||||
transactionResult.FullErrorMessage().String());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("Installation done. Old activation state backed up in \"%s\"\n",
|
||||
transactionResult.OldStateDirectory().String());
|
||||
|
||||
printf("Cleaning up ...\n");
|
||||
BEntry transactionDirectoryEntry;
|
||||
if ((error = transactionDirectory.GetEntry(&transactionDirectoryEntry))
|
||||
!= B_OK
|
||||
|| (error = transactionDirectoryEntry.Remove()) != B_OK) {
|
||||
WARN(error, "failed to remove transaction directory");
|
||||
}
|
||||
PackageManager packageManager(location, true, true);
|
||||
packageManager.Install(packages, packageCount);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -14,14 +14,13 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <package/PackageRoster.h>
|
||||
#include <package/RepositoryConfig.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <package/solver/SolverPackage.h>
|
||||
|
||||
#include "Command.h"
|
||||
#include "PackageManager.h"
|
||||
#include "pkgman.h"
|
||||
#include "RepositoryBuilder.h"
|
||||
|
||||
|
||||
// TODO: internationalization!
|
||||
@@ -32,9 +31,6 @@
|
||||
using namespace BPackageKit;
|
||||
|
||||
|
||||
typedef std::map<BSolverPackage*, BString> PackagePathMap;
|
||||
|
||||
|
||||
static const char* const kShortUsage =
|
||||
" %command% <search-string>\n"
|
||||
" Searches for packages matching <search-string>.\n";
|
||||
@@ -113,64 +109,13 @@ SearchCommand::Execute(int argc, const char* const* argv)
|
||||
const char* searchString = argv[optind++];
|
||||
|
||||
// create the solver
|
||||
BSolver* solver;
|
||||
status_t error = BSolver::Create(solver);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to create solver");
|
||||
|
||||
// add repositories
|
||||
|
||||
// installed
|
||||
BSolverRepository systemRepository;
|
||||
BSolverRepository commonRepository;
|
||||
BSolverRepository homeRepository;
|
||||
if (!uninstalledOnly) {
|
||||
RepositoryBuilder(systemRepository, "system")
|
||||
.AddPackages(B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, "system")
|
||||
.AddToSolver(solver, false);
|
||||
RepositoryBuilder(commonRepository, "common")
|
||||
.AddPackages(B_PACKAGE_INSTALLATION_LOCATION_COMMON, "common")
|
||||
.AddToSolver(solver, false);
|
||||
// RepositoryBuilder(homeRepository, "home")
|
||||
// .AddPackages(B_PACKAGE_INSTALLATION_LOCATION_HOME, "home")
|
||||
// .AddToSolver(solver, false);
|
||||
}
|
||||
|
||||
// not installed
|
||||
BObjectList<BSolverRepository> uninstalledRepositories(10, true);
|
||||
|
||||
if (!installedOnly) {
|
||||
BPackageRoster roster;
|
||||
BStringList repositoryNames;
|
||||
error = roster.GetRepositoryNames(repositoryNames);
|
||||
if (error != B_OK)
|
||||
WARN(error, "failed to get repository names");
|
||||
|
||||
int32 repositoryNameCount = repositoryNames.CountStrings();
|
||||
for (int32 i = 0; i < repositoryNameCount; i++) {
|
||||
const BString& name = repositoryNames.StringAt(i);
|
||||
BRepositoryConfig config;
|
||||
error = roster.GetRepositoryConfig(name, &config);
|
||||
if (error != B_OK) {
|
||||
WARN(error, "failed to get config for repository \"%s\". "
|
||||
"Skipping.", name.String());
|
||||
continue;
|
||||
}
|
||||
|
||||
BSolverRepository* repository = new(std::nothrow) BSolverRepository;
|
||||
if (repository == NULL
|
||||
|| !uninstalledRepositories.AddItem(repository)) {
|
||||
DIE(B_NO_MEMORY, "out of memory");
|
||||
}
|
||||
|
||||
RepositoryBuilder(*repository, config)
|
||||
.AddToSolver(solver, false);
|
||||
}
|
||||
}
|
||||
PackageManager packageManager(B_PACKAGE_INSTALLATION_LOCATION_COMMON,
|
||||
!uninstalledOnly, !installedOnly);
|
||||
// TODO: Use B_PACKAGE_INSTALLATION_LOCATION_HOME once we actually mount it.
|
||||
|
||||
// search
|
||||
BObjectList<BSolverPackage> packages;
|
||||
error = solver->FindPackages(searchString,
|
||||
status_t error = packageManager.Solver()->FindPackages(searchString,
|
||||
BSolver::B_FIND_CASE_INSENSITIVE | BSolver::B_FIND_IN_NAME
|
||||
| BSolver::B_FIND_IN_SUMMARY | BSolver::B_FIND_IN_DESCRIPTION
|
||||
| BSolver::B_FIND_IN_PROVIDES,
|
||||
@@ -230,11 +175,11 @@ SearchCommand::Execute(int argc, const char* const* argv)
|
||||
BSolverPackage* package = packages.ItemAt(i);
|
||||
|
||||
const char* installed = "";
|
||||
if (package->Repository() == &systemRepository)
|
||||
if (package->Repository() == packageManager.SystemRepository())
|
||||
installed = "system";
|
||||
else if (package->Repository() == &commonRepository)
|
||||
else if (package->Repository() == packageManager.CommonRepository())
|
||||
installed = "common";
|
||||
else if (package->Repository() == &homeRepository)
|
||||
else if (package->Repository() == packageManager.HomeRepository())
|
||||
installed = "home";
|
||||
|
||||
printf("%-*s %-*s %-*.*s\n",
|
||||
|
||||
Reference in New Issue
Block a user