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:
@@ -1,6 +1,6 @@
|
||||
SubDir HAIKU_TOP src bin pkgman ;
|
||||
|
||||
UsePrivateHeaders shared support ;
|
||||
UsePrivateHeaders shared storage support ;
|
||||
|
||||
BinCommand pkgman :
|
||||
Command.cpp
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user