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 ;
|
SubDir HAIKU_TOP src bin pkgman ;
|
||||||
|
|
||||||
UsePrivateHeaders shared support ;
|
UsePrivateHeaders shared storage support ;
|
||||||
|
|
||||||
BinCommand pkgman :
|
BinCommand pkgman :
|
||||||
Command.cpp
|
Command.cpp
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <package/solver/SolverProblemSolution.h>
|
#include <package/solver/SolverProblemSolution.h>
|
||||||
#include <package/solver/SolverResult.h>
|
#include <package/solver/SolverResult.h>
|
||||||
|
|
||||||
|
#include <CopyEngine.h>
|
||||||
#include <package/ActivationTransaction.h>
|
#include <package/ActivationTransaction.h>
|
||||||
#include <package/DaemonClient.h>
|
#include <package/DaemonClient.h>
|
||||||
|
|
||||||
@@ -331,17 +332,23 @@ PackageManager::_AnalyzeResult()
|
|||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
DIE(error, "failed to compute packages to un/-install");
|
DIE(error, "failed to compute packages to un/-install");
|
||||||
|
|
||||||
|
PackageList potentialBasePackages;
|
||||||
|
|
||||||
for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i);
|
for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i);
|
||||||
i++) {
|
i++) {
|
||||||
BSolverPackage* package = element->Package();
|
BSolverPackage* package = element->Package();
|
||||||
|
|
||||||
switch (element->Type()) {
|
switch (element->Type()) {
|
||||||
case BSolverResultElement::B_TYPE_INSTALL:
|
case BSolverResultElement::B_TYPE_INSTALL:
|
||||||
if (!fInstalledRepositories.HasItem(package->Repository())) {
|
{
|
||||||
if (!fPackagesToActivate.AddItem(package))
|
PackageList& packageList
|
||||||
DIE(B_NO_MEMORY, "failed to add package to activate");
|
= fInstalledRepositories.HasItem(package->Repository())
|
||||||
}
|
? potentialBasePackages
|
||||||
|
: fPackagesToActivate;
|
||||||
|
if (!packageList.AddItem(package))
|
||||||
|
DIE(B_NO_MEMORY, "failed to add package to activate");
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case BSolverResultElement::B_TYPE_UNINSTALL:
|
case BSolverResultElement::B_TYPE_UNINSTALL:
|
||||||
if (!fPackagesToDeactivate.AddItem(package))
|
if (!fPackagesToDeactivate.AddItem(package))
|
||||||
@@ -354,6 +361,18 @@ PackageManager::_AnalyzeResult()
|
|||||||
printf("Nothing to do.\n");
|
printf("Nothing to do.\n");
|
||||||
exit(0);
|
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
|
// get package URL and target entry
|
||||||
Repository* repository
|
Repository* repository
|
||||||
= static_cast<Repository*>(package->Repository());
|
= static_cast<Repository*>(package->Repository());
|
||||||
BString url = repository->Config().PackagesURL();
|
|
||||||
BString fileName(package->Info().CanonicalFileName());
|
BString fileName(package->Info().CanonicalFileName());
|
||||||
if (fileName.IsEmpty())
|
if (fileName.IsEmpty())
|
||||||
DIE(B_NO_MEMORY, "failed to allocate file name");
|
DIE(B_NO_MEMORY, "failed to allocate file name");
|
||||||
url << '/' << fileName;
|
|
||||||
|
|
||||||
BEntry entry;
|
BEntry entry;
|
||||||
error = entry.SetTo(&transactionDirectory, fileName);
|
error = entry.SetTo(&transactionDirectory, fileName);
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
DIE(error, "failed to create package entry");
|
DIE(error, "failed to create package entry");
|
||||||
|
|
||||||
// download the package
|
if (fInstalledRepositories.HasItem(repository)) {
|
||||||
DownloadFileRequest downloadRequest(fContext, url, entry,
|
// clone the existing package
|
||||||
package->Info().Checksum());
|
_ClonePackageFile(repository, fileName, entry);
|
||||||
error = downloadRequest.Process();
|
} else {
|
||||||
if (error != B_OK)
|
// download the package
|
||||||
DIE(error, "failed to download 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
|
// add package to transaction
|
||||||
if (!transaction.AddPackageToActivate(
|
if (!transaction.AddPackageToActivate(fileName)) {
|
||||||
package->Info().CanonicalFileName())) {
|
|
||||||
DIE(B_NO_MEMORY,
|
DIE(B_NO_MEMORY,
|
||||||
"failed to add package to activate to transaction");
|
"failed to add package to activate to transaction");
|
||||||
}
|
}
|
||||||
@@ -458,3 +483,80 @@ PackageManager::_ApplyPackageChanges()
|
|||||||
WARN(error, "failed to remove transaction directory");
|
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 _PrintResult();
|
||||||
void _ApplyPackageChanges();
|
void _ApplyPackageChanges();
|
||||||
|
|
||||||
|
void _ClonePackageFile(Repository* repository,
|
||||||
|
const BString& fileName,
|
||||||
|
const BEntry& entry) const;
|
||||||
|
int32 _FindBasePackage(const PackageList& packages,
|
||||||
|
const BPackageInfo& info) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BPackageInstallationLocation fLocation;
|
BPackageInstallationLocation fLocation;
|
||||||
BSolver* fSolver;
|
BSolver* fSolver;
|
||||||
|
|||||||
Reference in New Issue
Block a user