pkgman install: Add base package support

If any of the packages to install has requires a base package, that is
installed in a more general installation location, we copy the package
to the target installation location. Ideally we'd hard-link, but BFS
doesn't support that. Symlinks would be a small nightmare to maintain.

We might want to choose the installation location intelligently
depending on the base package requirement -- e.g. install curl_devel in
system rather than common, since curl is installed in system -- so we
can avoid duplicating packages.
This commit is contained in:
Ingo Weinhold
2013-08-28 00:36:28 +02:00
parent 617be97d8e
commit 90f9f1392b
3 changed files with 123 additions and 15 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
SubDir HAIKU_TOP src bin pkgman ;
UsePrivateHeaders shared support ;
UsePrivateHeaders shared storage support ;
BinCommand pkgman :
Command.cpp
+116 -14
View File
@@ -20,6 +20,7 @@
#include <package/solver/SolverProblemSolution.h>
#include <package/solver/SolverResult.h>
#include <CopyEngine.h>
#include <package/ActivationTransaction.h>
#include <package/DaemonClient.h>
@@ -331,17 +332,23 @@ PackageManager::_AnalyzeResult()
if (error != B_OK)
DIE(error, "failed to compute packages to un/-install");
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:
if (!fInstalledRepositories.HasItem(package->Repository())) {
if (!fPackagesToActivate.AddItem(package))
DIE(B_NO_MEMORY, "failed to add package to activate");
}
{
PackageList& packageList
= fInstalledRepositories.HasItem(package->Repository())
? potentialBasePackages
: fPackagesToActivate;
if (!packageList.AddItem(package))
DIE(B_NO_MEMORY, "failed to add package to activate");
break;
}
case BSolverResultElement::B_TYPE_UNINSTALL:
if (!fPackagesToDeactivate.AddItem(package))
@@ -354,6 +361,18 @@ PackageManager::_AnalyzeResult()
printf("Nothing to do.\n");
exit(0);
}
// Make sure base packages are installed in the same location.
for (int32 i = 0; i < fPackagesToActivate.CountItems(); i++) {
BSolverPackage* package = fPackagesToActivate.ItemAt(i);
int32 index = _FindBasePackage(potentialBasePackages, package->Info());
if (index < 0)
continue;
BSolverPackage* basePackage = potentialBasePackages.RemoveItemAt(index);
if (!fPackagesToActivate.AddItem(basePackage))
DIE(B_NO_MEMORY, "failed to add package to activate");
}
}
@@ -402,27 +421,33 @@ PackageManager::_ApplyPackageChanges()
// get package URL and target entry
Repository* repository
= static_cast<Repository*>(package->Repository());
BString url = repository->Config().PackagesURL();
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");
if (fInstalledRepositories.HasItem(repository)) {
// clone the existing package
_ClonePackageFile(repository, fileName, entry);
} else {
// download the package
BString url = repository->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(
package->Info().CanonicalFileName())) {
if (!transaction.AddPackageToActivate(fileName)) {
DIE(B_NO_MEMORY,
"failed to add package to activate to transaction");
}
@@ -458,3 +483,80 @@ PackageManager::_ApplyPackageChanges()
WARN(error, "failed to remove transaction directory");
}
}
void
PackageManager::_ClonePackageFile(Repository* 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;
}
+6
View File
@@ -71,6 +71,12 @@ private:
void _PrintResult();
void _ApplyPackageChanges();
void _ClonePackageFile(Repository* repository,
const BString& fileName,
const BEntry& entry) const;
int32 _FindBasePackage(const PackageList& packages,
const BPackageInfo& info) const;
private:
BPackageInstallationLocation fLocation;
BSolver* fSolver;