diff --git a/src/bin/pkgman/Jamfile b/src/bin/pkgman/Jamfile index b752c4a6b6..f6dfa06b37 100644 --- a/src/bin/pkgman/Jamfile +++ b/src/bin/pkgman/Jamfile @@ -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 diff --git a/src/bin/pkgman/command_resolve_build_dependencies.cpp b/src/bin/pkgman/command_resolve_build_dependencies.cpp deleted file mode 100644 index 2ebbb59494..0000000000 --- a/src/bin/pkgman/command_resolve_build_dependencies.cpp +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright 2013, Haiku, Inc. All Rights Reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Ingo Weinhold - */ - - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -//#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 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 \n" - " [ ... ]\n" - "Resolves and lists all packages needed for building a package. Fails, if\n" - "not all dependencies could be resolved.\n" - "\n" - "\n" - " The package info for the package to be built, requiring all build\n" - " requisites.\n" - "\n" - " A text file listing the package's build prerequisites, i.e. the\n" - " requisites that must be installed from the host environment.\n" - "\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" - "\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; -} diff --git a/src/bin/pkgman/command_resolve_dependencies.cpp b/src/bin/pkgman/command_resolve_dependencies.cpp new file mode 100644 index 0000000000..42f9a801c9 --- /dev/null +++ b/src/bin/pkgman/command_resolve_dependencies.cpp @@ -0,0 +1,311 @@ +/* + * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pkgman.h" + + +using namespace BPackageKit; + + +// TODO: internationalization! + + +static const char* kCommandUsage = + "Usage: %s resolve-dependencies [ ] ...\n" + "Resolves and lists all packages a given package depends on. Fails, if\n" + "not all dependencies could be resolved.\n" + "\n" + "\n" + " The HPKG or package info file of the package for which the\n" + " dependencies shall be resolved.\n" + "\n" + " Path to a directory containing packages from which the package's\n" + " dependencies shall be resolved. Multiple directories can be specified.\n" + "\n" + " Can follow a 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 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 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; +} diff --git a/src/bin/pkgman/pkgman.cpp b/src/bin/pkgman/pkgman.cpp index bfa50d11a1..6942008f4b 100644 --- a/src/bin/pkgman/pkgman.cpp +++ b/src/bin/pkgman/pkgman.cpp @@ -33,9 +33,8 @@ static const char* kUsage = " refresh [ ...]\n" " Refreshes all or just the given repositories.\n" "\n" - " resolve-build-dependencies \n" - " [ ... ]\n" - " Resolves all packages needed for building a package.\n" + " resolve-dependencies [ ] ...\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); diff --git a/src/bin/pkgman/pkgman.h b/src/bin/pkgman/pkgman.h index 088cd23271..9808903fd3 100644 --- a/src/bin/pkgman/pkgman.h +++ b/src/bin/pkgman/pkgman.h @@ -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);