pkgman: Simplify and rename resolve-build-dependencies
* Now it only gets a package (info) file and a single list of repository directories, optionally with priority, and resolves the package's dependencies. The more complex two resolving steps it did before can just as well be done by haikuporter, and this way the command is more flexible. * Rename to resolve-dependencies. * Some TODOs still remain.
This commit is contained in:
parent
07a4d4e370
commit
19f3eaaee6
@ -5,7 +5,7 @@ UsePrivateHeaders shared support ;
|
||||
BinCommand pkgman :
|
||||
command_add_repo.cpp
|
||||
command_drop_repo.cpp
|
||||
command_resolve_build_dependencies.cpp
|
||||
command_resolve_dependencies.cpp
|
||||
command_list_repos.cpp
|
||||
command_refresh.cpp
|
||||
DecisionProvider.cpp
|
||||
|
@ -1,413 +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 <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <Entry.h>
|
||||
#include <File.h>
|
||||
#include <Path.h>
|
||||
|
||||
#include <package/PackageInfo.h>
|
||||
#include <package/solver/Solver.h>
|
||||
#include <package/solver/SolverPackage.h>
|
||||
#include <package/solver/SolverPackageSpecifier.h>
|
||||
#include <package/solver/SolverPackageSpecifierList.h>
|
||||
#include <package/solver/SolverProblem.h>
|
||||
#include <package/solver/SolverRepository.h>
|
||||
#include <package/solver/SolverResult.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
|
||||
//#include "DecisionProvider.h"
|
||||
//#include "JobStateListener.h"
|
||||
#include "pkgman.h"
|
||||
|
||||
|
||||
using namespace BPackageKit;
|
||||
|
||||
|
||||
// TODO: internationalization!
|
||||
|
||||
|
||||
struct PackageInfoErrorListener : public BPackageInfo::ParseErrorListener {
|
||||
public:
|
||||
PackageInfoErrorListener(const BString& errorContext)
|
||||
:
|
||||
fErrorContext(errorContext)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void 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());
|
||||
}
|
||||
|
||||
private:
|
||||
BString fErrorContext;
|
||||
};
|
||||
|
||||
|
||||
struct RepositoryBuilder {
|
||||
RepositoryBuilder(BSolverRepository& repository, const BString& name,
|
||||
const BString& errorName = BString())
|
||||
:
|
||||
fRepository(repository),
|
||||
fErrorName(errorName.IsEmpty() ? name : errorName)
|
||||
{
|
||||
status_t error = fRepository.SetTo(name);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to init %s repository", fErrorName.String());
|
||||
}
|
||||
|
||||
RepositoryBuilder& AddPackage(const BPackageInfo& info,
|
||||
const char* packageErrorName = NULL)
|
||||
{
|
||||
status_t error = fRepository.AddPackage(info);
|
||||
if (error != B_OK) {
|
||||
DIE(error, "failed to add %s to %s repository",
|
||||
packageErrorName != NULL
|
||||
? packageErrorName
|
||||
: (BString("package ") << info.Name()).String(),
|
||||
fErrorName.String());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
RepositoryBuilder& AddPackages(BPackageInstallationLocation location,
|
||||
const char* locationErrorName)
|
||||
{
|
||||
status_t error = fRepository.AddPackages(location);
|
||||
if (error != B_OK) {
|
||||
DIE(error, "failed to add %s packages to %s repository",
|
||||
locationErrorName, fErrorName.String());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
RepositoryBuilder& AddPackagesDirectory(const char* path)
|
||||
{
|
||||
// open directory
|
||||
DIR* dir = opendir(path);
|
||||
if (dir == NULL)
|
||||
DIE(errno, "failed to open package directory \"%s\"", path);
|
||||
CObjectDeleter<DIR, int> dirCloser(dir, &closedir);
|
||||
|
||||
// iterate through directory entries
|
||||
while (dirent* entry = readdir(dir)) {
|
||||
// skip "." and ".."
|
||||
const char* name = entry->d_name;
|
||||
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
|
||||
continue;
|
||||
|
||||
// stat() the entry and skip any non-file
|
||||
BPath entryPath;
|
||||
status_t error = entryPath.SetTo(path, name);
|
||||
if (error != B_OK)
|
||||
DIE(errno, "failed to construct path");
|
||||
|
||||
struct stat st;
|
||||
if (lstat(entryPath.Path(), &st) != 0)
|
||||
DIE(errno, "failed to stat() %s", entryPath.Path());
|
||||
|
||||
if (!S_ISREG(st.st_mode))
|
||||
continue;
|
||||
|
||||
// read a package info from the (HPKG or package info) file
|
||||
BPackageInfo packageInfo;
|
||||
|
||||
size_t nameLength = strlen(name);
|
||||
if (nameLength > 5 && strcmp(name + nameLength - 5, ".hpkg") == 0) {
|
||||
// a package file
|
||||
error = packageInfo.ReadFromPackageFile(entryPath.Path());
|
||||
} else {
|
||||
// a package info file (supposedly)
|
||||
PackageInfoErrorListener errorListener("reading package info");
|
||||
error = packageInfo.ReadFromConfigFile(BEntry(entryPath.Path()),
|
||||
&errorListener);
|
||||
}
|
||||
|
||||
if (error != B_OK) {
|
||||
DIE(errno, "failed to read package info from \"%s\"",
|
||||
entryPath.Path());
|
||||
}
|
||||
|
||||
AddPackage(packageInfo, entryPath.Path());
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
RepositoryBuilder& AddToSolver(BSolver* solver, bool isInstalled = false)
|
||||
{
|
||||
fRepository.SetInstalled(isInstalled);
|
||||
|
||||
status_t error = solver->AddRepository(&fRepository);
|
||||
if (error != B_OK) {
|
||||
DIE(error, "failed to add %s repository to solver",
|
||||
fErrorName.String());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
BSolverRepository& fRepository;
|
||||
BString fErrorName;
|
||||
};
|
||||
|
||||
|
||||
static const char* kCommandUsage =
|
||||
"Usage: %s resolve-build-dependencies <package> <prerequisites>\n"
|
||||
" <build-repository> [ <prerequisite-repository> ... ]\n"
|
||||
"Resolves and lists all packages needed for building a package. Fails, if\n"
|
||||
"not all dependencies could be resolved.\n"
|
||||
"\n"
|
||||
"<package>\n"
|
||||
" The package info for the package to be built, requiring all build\n"
|
||||
" requisites.\n"
|
||||
"<prerequisites>\n"
|
||||
" A text file listing the package's build prerequisites, i.e. the\n"
|
||||
" requisites that must be installed from the host environment.\n"
|
||||
"<build-repository>\n"
|
||||
" Path to a directory containing package infos for all the packages that\n"
|
||||
" can be built. This repository is used to resolve the build requisites\n"
|
||||
" the package to build.\n"
|
||||
"<prerequisite-repository>\n"
|
||||
" Path to a directory containing packages from which the package's\n"
|
||||
" prerequisites shall be resolved. Multiple directories can be\n"
|
||||
" specified. If none is given, the system's installed packages are used.\n"
|
||||
"\n"
|
||||
;
|
||||
|
||||
static const char* kPrerequisitesPackageInfoTemplate =
|
||||
"name _build_prerequisites_\n"
|
||||
"version 1.0.0-1\n"
|
||||
"architecture %s\n"
|
||||
"summary none\n"
|
||||
"description none\n"
|
||||
"packager none\n"
|
||||
"vendor \"%s\"\n"
|
||||
"licenses {\n"
|
||||
" MIT\n"
|
||||
"}\n"
|
||||
"copyrights {\n"
|
||||
" none\n"
|
||||
"}\n"
|
||||
"provides {\n"
|
||||
" _build_prerequisites_ = 1.0.0\n"
|
||||
"}\n"
|
||||
"requires {\n %s }\n";
|
||||
|
||||
|
||||
static void
|
||||
print_command_usage_and_exit(bool error)
|
||||
{
|
||||
fprintf(error ? stderr : stdout, kCommandUsage, kProgramName);
|
||||
exit(error ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
resolve_packages(BSolver* solver, const BPackageInfo& packageInfo,
|
||||
bool isPrerequisitePhase, BSolverResult& _result)
|
||||
{
|
||||
const char* requisitesString = isPrerequisitePhase
|
||||
? "prerequisites" :"requisites";
|
||||
|
||||
// add a repository with the build package resolvable
|
||||
BSolverRepository dummyRepository;
|
||||
RepositoryBuilder(dummyRepository, "dummy", "build package")
|
||||
.AddPackage(packageInfo, "package to install")
|
||||
.AddToSolver(solver);
|
||||
|
||||
// resolve
|
||||
BSolverPackageSpecifierList packagesToInstall;
|
||||
if (!packagesToInstall.AppendSpecifier(
|
||||
BSolverPackageSpecifier(&dummyRepository,
|
||||
BPackageResolvableExpression(packageInfo.Name())))) {
|
||||
DIE(B_NO_MEMORY, "failed to add package to install");
|
||||
}
|
||||
|
||||
status_t error = solver->Install(packagesToInstall);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to resolve %s", requisitesString);
|
||||
|
||||
if (solver->HasProblems()) {
|
||||
fprintf(stderr, "Encountered problems resolving %s:\n",
|
||||
requisitesString);
|
||||
|
||||
int32 problemCount = solver->CountProblems();
|
||||
for (int32 i = 0; i < problemCount; i++) {
|
||||
printf(" %" B_PRId32 ": %s\n", i + 1,
|
||||
solver->ProblemAt(i)->ToString().String());
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
BSolverResult& result = _result;
|
||||
error = solver->GetResult(result);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to resolve %s", requisitesString);
|
||||
|
||||
// print packages
|
||||
printf("[%s]\n", requisitesString);
|
||||
for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i);
|
||||
i++) {
|
||||
// TODO: Print the path to the package/package info!
|
||||
printf("%s-%s\n",
|
||||
element->Package()->Info().Name().String(),
|
||||
element->Package()->Info().Version().ToString().String());
|
||||
// TODO: Filter out the given package!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
command_resolve_build_dependencies(int argc, const char* const* argv)
|
||||
{
|
||||
while (true) {
|
||||
static struct option sLongOptions[] = {
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
opterr = 0; // don't print errors
|
||||
int c = getopt_long(argc, (char**)argv, "hu", sLongOptions, NULL);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_command_usage_and_exit(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
print_command_usage_and_exit(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The remaining arguments are the build package info, the prerequisites file,
|
||||
// the build requisites directory, and zero or more prerequisites
|
||||
// directories.
|
||||
if (argc < optind + 3)
|
||||
print_command_usage_and_exit(true);
|
||||
|
||||
const char* buildPackagePath = argv[optind++];
|
||||
const char* prerequisitesPath = argv[optind++];
|
||||
const char* requisitesDirectoryPath = argv[optind++];
|
||||
int prerequisitesDirectoryPathCount = argc - optind;
|
||||
const char* const* prerequisitesDirectoryPaths
|
||||
= prerequisitesDirectoryPathCount > 0 ? argv + optind : NULL;
|
||||
|
||||
if (prerequisitesDirectoryPaths != NULL) {
|
||||
DIE(B_NOT_SUPPORTED,
|
||||
"sorry, prerequisite directory repositories not supported yet");
|
||||
// TODO: Support prerequisite directory repositories!
|
||||
}
|
||||
|
||||
// read build package info
|
||||
BEntry buildPackageEntry(buildPackagePath);
|
||||
BPackageInfo buildPackageInfo;
|
||||
PackageInfoErrorListener buildPackageInfoErrorListener(
|
||||
"Error: Failed to parse package info");
|
||||
status_t error = buildPackageInfo.ReadFromConfigFile(buildPackageEntry,
|
||||
&buildPackageInfoErrorListener);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to read build package info file");
|
||||
|
||||
// read the prerequisites file into a string
|
||||
BString prerequisitesString;
|
||||
BFile prerequisitesFile;
|
||||
error = prerequisitesFile.SetTo(prerequisitesPath, B_READ_ONLY);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to open prerequisites file");
|
||||
|
||||
off_t fileSize;
|
||||
error = prerequisitesFile.GetSize(&fileSize);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to get prerequisites file size");
|
||||
|
||||
if (char* buffer = prerequisitesString.LockBuffer(fileSize)) {
|
||||
ssize_t bytesRead = prerequisitesFile.Read(buffer, fileSize);
|
||||
if (bytesRead != fileSize) {
|
||||
DIE(bytesRead < 0 ? (status_t)bytesRead :B_ERROR,
|
||||
"failed to read prerequisites");
|
||||
}
|
||||
} else
|
||||
DIE(B_NO_MEMORY, "failed to read prerequisites");
|
||||
|
||||
prerequisitesString.UnlockBuffer();
|
||||
prerequisitesFile.Unset();
|
||||
|
||||
// create a package info that contains the build prerequisites
|
||||
BPackageInfo prerequisitesPackageInfo;
|
||||
PackageInfoErrorListener buildPrerequisitesPackageInfoErrorListener(
|
||||
"Error: Failed to parse prerequisites package info");
|
||||
error = prerequisitesPackageInfo.ReadFromConfigString(
|
||||
BString().SetToFormat(kPrerequisitesPackageInfoTemplate,
|
||||
BPackageInfo::kArchitectureNames[buildPackageInfo.Architecture()],
|
||||
buildPackageInfo.Vendor().String(),
|
||||
prerequisitesString.String()),
|
||||
&buildPrerequisitesPackageInfoErrorListener);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to create prerequisites package info");
|
||||
|
||||
// resolve the prerequisites
|
||||
|
||||
// create the solver
|
||||
BSolver* solver;
|
||||
error = BSolver::Create(solver);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to create solver");
|
||||
|
||||
// add prerequisite repository
|
||||
BSolverRepository prerequisiteRepository;
|
||||
RepositoryBuilder(prerequisiteRepository, "prerequisites")
|
||||
.AddPackages(B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, "system")
|
||||
.AddPackages(B_PACKAGE_INSTALLATION_LOCATION_COMMON, "common")
|
||||
.AddToSolver(solver);
|
||||
|
||||
BSolverResult result;
|
||||
resolve_packages(solver, prerequisitesPackageInfo, true, result);
|
||||
|
||||
// resolve the requisites
|
||||
|
||||
// reset the solver
|
||||
error = solver->Init();
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to re-init solver");
|
||||
|
||||
// add an installation repository with the resolved prerequisites
|
||||
BSolverRepository installationRepository;
|
||||
RepositoryBuilder installationRepositoryBuilder(installationRepository,
|
||||
"installation");
|
||||
for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i);
|
||||
i++) {
|
||||
installationRepositoryBuilder.AddPackage(element->Package()->Info());
|
||||
}
|
||||
installationRepositoryBuilder.AddToSolver(solver, true);
|
||||
|
||||
// add requisite repository
|
||||
BSolverRepository requisiteRepository;
|
||||
RepositoryBuilder(requisiteRepository, "requisites")
|
||||
.AddPackagesDirectory(requisitesDirectoryPath)
|
||||
.AddToSolver(solver);
|
||||
|
||||
resolve_packages(solver, buildPackageInfo, false, result);
|
||||
// TODO: We need to make sure that the package isn't part of a cyclic
|
||||
// dependency.
|
||||
|
||||
return 0;
|
||||
}
|
311
src/bin/pkgman/command_resolve_dependencies.cpp
Normal file
311
src/bin/pkgman/command_resolve_dependencies.cpp
Normal file
@ -0,0 +1,311 @@
|
||||
/*
|
||||
* Copyright 2013, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Ingo Weinhold <ingo_weinhold@gmx.de>
|
||||
*/
|
||||
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <Entry.h>
|
||||
#include <File.h>
|
||||
#include <Path.h>
|
||||
|
||||
#include <package/PackageInfo.h>
|
||||
#include <package/solver/Solver.h>
|
||||
#include <package/solver/SolverPackage.h>
|
||||
#include <package/solver/SolverPackageSpecifier.h>
|
||||
#include <package/solver/SolverPackageSpecifierList.h>
|
||||
#include <package/solver/SolverProblem.h>
|
||||
#include <package/solver/SolverRepository.h>
|
||||
#include <package/solver/SolverResult.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
|
||||
#include "pkgman.h"
|
||||
|
||||
|
||||
using namespace BPackageKit;
|
||||
|
||||
|
||||
// TODO: internationalization!
|
||||
|
||||
|
||||
static const char* kCommandUsage =
|
||||
"Usage: %s resolve-dependencies <package> <repository> [ <priority> ] ...\n"
|
||||
"Resolves and lists all packages a given package depends on. Fails, if\n"
|
||||
"not all dependencies could be resolved.\n"
|
||||
"\n"
|
||||
"<package>\n"
|
||||
" The HPKG or package info file of the package for which the\n"
|
||||
" dependencies shall be resolved.\n"
|
||||
"<repository>\n"
|
||||
" Path to a directory containing packages from which the package's\n"
|
||||
" dependencies shall be resolved. Multiple directories can be specified.\n"
|
||||
"<priority>\n"
|
||||
" Can follow a <repository> to specify the priority of that repository.\n"
|
||||
" The default priority is 0.\n"
|
||||
"\n"
|
||||
;
|
||||
|
||||
|
||||
static void
|
||||
print_command_usage_and_exit(bool error)
|
||||
{
|
||||
fprintf(error ? stderr : stdout, kCommandUsage, kProgramName);
|
||||
exit(error ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
struct PackageInfoErrorListener : public BPackageInfo::ParseErrorListener {
|
||||
public:
|
||||
PackageInfoErrorListener(const BString& errorContext)
|
||||
:
|
||||
fErrorContext(errorContext)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void 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());
|
||||
}
|
||||
|
||||
private:
|
||||
BString fErrorContext;
|
||||
};
|
||||
|
||||
|
||||
struct RepositoryBuilder {
|
||||
RepositoryBuilder(BSolverRepository& repository, const BString& name,
|
||||
const BString& errorName = BString())
|
||||
:
|
||||
fRepository(repository),
|
||||
fErrorName(errorName.IsEmpty() ? name : errorName)
|
||||
{
|
||||
status_t error = fRepository.SetTo(name);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to init %s repository", fErrorName.String());
|
||||
}
|
||||
|
||||
RepositoryBuilder& AddPackage(const BPackageInfo& info,
|
||||
const char* packageErrorName = NULL)
|
||||
{
|
||||
status_t error = fRepository.AddPackage(info);
|
||||
if (error != B_OK) {
|
||||
DIE(error, "failed to add %s to %s repository",
|
||||
packageErrorName != NULL
|
||||
? packageErrorName
|
||||
: (BString("package ") << info.Name()).String(),
|
||||
fErrorName.String());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
RepositoryBuilder& AddPackage(const char* path)
|
||||
{
|
||||
// read a package info from the (HPKG or package info) file
|
||||
BPackageInfo packageInfo;
|
||||
|
||||
size_t pathLength = strlen(path);
|
||||
status_t error;
|
||||
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),
|
||||
&errorListener);
|
||||
}
|
||||
|
||||
if (error != B_OK)
|
||||
DIE(errno, "failed to read package info from \"%s\"", path);
|
||||
|
||||
return AddPackage(packageInfo, path);
|
||||
}
|
||||
|
||||
RepositoryBuilder& AddPackages(BPackageInstallationLocation location,
|
||||
const char* locationErrorName)
|
||||
{
|
||||
status_t error = fRepository.AddPackages(location);
|
||||
if (error != B_OK) {
|
||||
DIE(error, "failed to add %s packages to %s repository",
|
||||
locationErrorName, fErrorName.String());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
RepositoryBuilder& AddPackagesDirectory(const char* path)
|
||||
{
|
||||
// open directory
|
||||
DIR* dir = opendir(path);
|
||||
if (dir == NULL)
|
||||
DIE(errno, "failed to open package directory \"%s\"", path);
|
||||
CObjectDeleter<DIR, int> dirCloser(dir, &closedir);
|
||||
|
||||
// iterate through directory entries
|
||||
while (dirent* entry = readdir(dir)) {
|
||||
// skip "." and ".."
|
||||
const char* name = entry->d_name;
|
||||
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
|
||||
continue;
|
||||
|
||||
// stat() the entry and skip any non-file
|
||||
BPath entryPath;
|
||||
status_t error = entryPath.SetTo(path, name);
|
||||
if (error != B_OK)
|
||||
DIE(errno, "failed to construct path");
|
||||
|
||||
struct stat st;
|
||||
if (lstat(entryPath.Path(), &st) != 0)
|
||||
DIE(errno, "failed to stat() %s", entryPath.Path());
|
||||
|
||||
if (!S_ISREG(st.st_mode))
|
||||
continue;
|
||||
|
||||
AddPackage(entryPath.Path());
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
RepositoryBuilder& AddToSolver(BSolver* solver, bool isInstalled = false)
|
||||
{
|
||||
fRepository.SetInstalled(isInstalled);
|
||||
|
||||
status_t error = solver->AddRepository(&fRepository);
|
||||
if (error != B_OK) {
|
||||
DIE(error, "failed to add %s repository to solver",
|
||||
fErrorName.String());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
BSolverRepository& fRepository;
|
||||
BString fErrorName;
|
||||
};
|
||||
|
||||
|
||||
int
|
||||
command_resolve_dependencies(int argc, const char* const* argv)
|
||||
{
|
||||
while (true) {
|
||||
static struct option sLongOptions[] = {
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
opterr = 0; // don't print errors
|
||||
int c = getopt_long(argc, (char**)argv, "hu", sLongOptions, NULL);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_command_usage_and_exit(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
print_command_usage_and_exit(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The remaining arguments are the package (info) file and the repository
|
||||
// directories (at least one), optionally with priorities.
|
||||
if (argc < optind + 2)
|
||||
print_command_usage_and_exit(true);
|
||||
|
||||
const char* packagePath = argv[optind++];
|
||||
int repositoryDirectoryCount = argc - optind;
|
||||
const char* const* repositoryDirectories = 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
|
||||
BObjectList<BSolverRepository> repositories(10, true);
|
||||
int32 repositoryIndex = 0;
|
||||
for (int i = 0; i < repositoryDirectoryCount; i++, repositoryIndex++) {
|
||||
const char* directoryPath = repositoryDirectories[i];
|
||||
|
||||
BSolverRepository* repository = new(std::nothrow) BSolverRepository;
|
||||
if (repository == NULL || !repositories.AddItem(repository))
|
||||
DIE(B_NO_MEMORY, "failed to create repository");
|
||||
|
||||
|
||||
if (i + 1 < repositoryDirectoryCount) {
|
||||
char* end;
|
||||
long priority = strtol(repositoryDirectories[i + 1], &end, 0);
|
||||
if (*end == '\0') {
|
||||
repository->SetPriority((uint8)priority);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
RepositoryBuilder(*repository, BString("repository") << repositoryIndex)
|
||||
.AddPackagesDirectory(directoryPath)
|
||||
.AddToSolver(solver);
|
||||
}
|
||||
|
||||
// add a repository with only the specified package
|
||||
BSolverRepository dummyRepository;
|
||||
RepositoryBuilder(dummyRepository, "dummy", "specified package")
|
||||
.AddPackage(packagePath)
|
||||
.AddToSolver(solver);
|
||||
BSolverPackage* package = dummyRepository.PackageAt(0);
|
||||
|
||||
// resolve
|
||||
BSolverPackageSpecifierList packagesToInstall;
|
||||
if (!packagesToInstall.AppendSpecifier(
|
||||
BSolverPackageSpecifier(&dummyRepository,
|
||||
BPackageResolvableExpression(package->Info().Name())))) {
|
||||
DIE(B_NO_MEMORY, "failed to add specified package");
|
||||
}
|
||||
|
||||
error = solver->Install(packagesToInstall);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to resolve package dependencies");
|
||||
|
||||
if (solver->HasProblems()) {
|
||||
fprintf(stderr,
|
||||
"Encountered problems resolving package dependencies:\n");
|
||||
|
||||
int32 problemCount = solver->CountProblems();
|
||||
for (int32 i = 0; i < problemCount; i++) {
|
||||
printf(" %" B_PRId32 ": %s\n", i + 1,
|
||||
solver->ProblemAt(i)->ToString().String());
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
BSolverResult result;
|
||||
error = solver->GetResult(result);
|
||||
if (error != B_OK)
|
||||
DIE(error, "failed to resolve package dependencies");
|
||||
|
||||
// print packages
|
||||
for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i);
|
||||
i++) {
|
||||
// TODO: Print the path to the package/package info!
|
||||
printf("%s-%s\n",
|
||||
element->Package()->Info().Name().String(),
|
||||
element->Package()->Info().Version().ToString().String());
|
||||
// TODO: Filter out the given package!
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -33,9 +33,8 @@ static const char* kUsage =
|
||||
" refresh [<repo-name> ...]\n"
|
||||
" Refreshes all or just the given repositories.\n"
|
||||
"\n"
|
||||
" resolve-build-dependencies <package> <prerequisites>\n"
|
||||
" <build-repository> [ <prerequisite-repository> ... ]\n"
|
||||
" Resolves all packages needed for building a package.\n"
|
||||
" resolve-dependencies <package> <repository> [ <priority> ] ...\n"
|
||||
" Resolves all packages a given package depends on.\n"
|
||||
"\n"
|
||||
"Common Options:\n"
|
||||
" -h, --help - Print this usage info.\n"
|
||||
@ -60,9 +59,6 @@ main(int argc, const char* const* argv)
|
||||
if (strncmp(command, "add-r", 5) == 0)
|
||||
return command_add_repo(argc - 1, argv + 1);
|
||||
|
||||
if (strcmp(command, "list-build-dependency-packages") == 0)
|
||||
return command_resolve_build_dependencies(argc - 1, argv + 1);
|
||||
|
||||
if (strncmp(command, "drop-r", 6) == 0)
|
||||
return command_drop_repo(argc - 1, argv + 1);
|
||||
|
||||
@ -72,6 +68,9 @@ main(int argc, const char* const* argv)
|
||||
if (strncmp(command, "refr", 4) == 0)
|
||||
return command_refresh(argc - 1, argv + 1);
|
||||
|
||||
if (strcmp(command, "resolve-dependencies") == 0)
|
||||
return command_resolve_dependencies(argc - 1, argv + 1);
|
||||
|
||||
// if (strcmp(command, "search") == 0)
|
||||
// return command_search(argc - 1, argv + 1);
|
||||
|
||||
|
@ -35,7 +35,7 @@ do { \
|
||||
|
||||
void print_usage_and_exit(bool error);
|
||||
int command_add_repo(int argc, const char* const* argv);
|
||||
int command_resolve_build_dependencies(int argc, const char* const* argv);
|
||||
int command_resolve_dependencies(int argc, const char* const* argv);
|
||||
int command_drop_repo(int argc, const char* const* argv);
|
||||
int command_list_repos(int argc, const char* const* argv);
|
||||
int command_refresh(int argc, const char* const* argv);
|
||||
|
Loading…
Reference in New Issue
Block a user