1 /* 2 * Copyright 2013-2020, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ingo Weinhold <ingo_weinhold@gmx.de> 7 * Andrew Lindesay <apl@lindesay.co.nz> 8 */ 9 10 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 15 #include <map> 16 17 #include <package/RepositoryCache.h> 18 #include <package/manager/Exceptions.h> 19 #include <package/manager/RepositoryBuilder.h> 20 #include <package/solver/Solver.h> 21 #include <package/solver/SolverPackageSpecifier.h> 22 #include <package/solver/SolverPackageSpecifierList.h> 23 #include <package/solver/SolverProblem.h> 24 #include <package/solver/SolverProblemSolution.h> 25 #include <package/solver/SolverRepository.h> 26 #include <package/solver/SolverResult.h> 27 28 29 using namespace BPackageKit; 30 using namespace BPackageKit::BManager::BPrivate; 31 32 33 static const char* sProgramName = "get_package_dependencies"; 34 35 36 #define DIE(result, msg...) \ 37 do { \ 38 fprintf(stderr, "*** " msg); \ 39 fprintf(stderr, " : %s\n", strerror(result)); \ 40 exit(5); \ 41 } while(0) 42 43 44 void 45 print_usage_and_exit(bool error) 46 { 47 fprintf(error ? stderr : stdout, 48 "Usage: %s <repository> ... -- <package> ...\n" 49 "Resolves the dependencies of the given packages using the given\n" 50 "repositories and prints the URLs of the packages that are also\n" 51 "needed to satisfy all requirements. Fails, if there are conflicts\n" 52 "or some requirements cannot be satisfied.\n", 53 sProgramName); 54 exit(error ? 1 : 0); 55 } 56 57 58 int 59 main(int argc, const char* const* argv) 60 { 61 if (argc < 2) 62 print_usage_and_exit(true); 63 64 if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) 65 print_usage_and_exit(false); 66 67 // get lists of repositories and packages 68 int argIndex = 1; 69 const char* const* repositories = argv + argIndex; 70 while (argIndex < argc && strcmp(argv[argIndex], "--") != 0) 71 argIndex++; 72 int repositoryCount = argv + argIndex - repositories; 73 74 if (repositoryCount == 0 || argIndex == argc) 75 print_usage_and_exit(true); 76 77 const char* const* packages = argv + argIndex + 1; 78 int packageCount = argv + argc - packages; 79 80 // create the solver 81 BSolver* solver; 82 status_t error = BSolver::Create(solver); 83 if (error != B_OK) 84 DIE(error, "failed to create solver"); 85 86 // add the "installed" repository with the given packages 87 BSolverRepository installedRepository; 88 try { 89 BRepositoryBuilder installedRepositoryBuilder(installedRepository, "installed"); 90 for (int i = 0; i < packageCount; i++) 91 installedRepositoryBuilder.AddPackage(packages[i]); 92 installedRepositoryBuilder.AddToSolver(solver, true); 93 } catch (BFatalErrorException& e) { 94 DIE(e.Error(), "%s %s", e.Message().String(), e.Details().String()); 95 } 96 97 // add external repositories 98 std::map<BSolverRepository*, BRepositoryInfo> repositoryInfos; 99 for (int i = 0; i < repositoryCount; i++) { 100 BSolverRepository* repository = new BSolverRepository; 101 BRepositoryCache cache; 102 error = cache.SetTo(repositories[i]); 103 if (error != B_OK) 104 DIE(error, "failed to read repository file '%s'", repositories[i]); 105 BRepositoryBuilder(*repository, cache) 106 .AddToSolver(solver, false); 107 if (cache.Info().BaseURL().IsEmpty()) { 108 DIE(B_ERROR, "missing base url in repository file '%s'", 109 repositories[i]); 110 } 111 repositoryInfos[repository] = cache.Info(); 112 } 113 114 // solve 115 error = solver->VerifyInstallation(); 116 if (error != B_OK) 117 DIE(error, "failed to compute packages to install"); 118 119 // print problems (and fail), if any 120 if (solver->HasProblems()) { 121 fprintf(stderr, "Encountered problems:\n"); 122 123 int32 problemCount = solver->CountProblems(); 124 for (int32 i = 0; i < problemCount; i++) { 125 // print problem and possible solutions 126 BSolverProblem* problem = solver->ProblemAt(i); 127 fprintf(stderr, "problem %" B_PRId32 ": %s\n", i + 1, 128 problem->ToString().String()); 129 130 int32 solutionCount = problem->CountSolutions(); 131 for (int32 k = 0; k < solutionCount; k++) { 132 const BSolverProblemSolution* solution = problem->SolutionAt(k); 133 fprintf(stderr, " solution %" B_PRId32 ":\n", k + 1); 134 int32 elementCount = solution->CountElements(); 135 for (int32 l = 0; l < elementCount; l++) { 136 const BSolverProblemSolutionElement* element 137 = solution->ElementAt(l); 138 fprintf(stderr, " - %s\n", element->ToString().String()); 139 } 140 } 141 } 142 143 exit(1); 144 } 145 146 // print URL of packages that additionally need to be installed 147 BSolverResult result; 148 error = solver->GetResult(result); 149 if (error != B_OK) 150 DIE(error, "failed to compute packages to install"); 151 152 bool duplicatePackage = false; 153 for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i); 154 i++) { 155 BSolverPackage* package = element->Package(); 156 157 switch (element->Type()) { 158 case BSolverResultElement::B_TYPE_INSTALL: 159 if (package->Repository() != &installedRepository) { 160 const BRepositoryInfo& info 161 = repositoryInfos[package->Repository()]; 162 BString url = info.BaseURL(); 163 url << "/packages/" << package->Info().CanonicalFileName(); 164 printf("%s\n", url.String()); 165 } 166 break; 167 168 case BSolverResultElement::B_TYPE_UNINSTALL: 169 fprintf(stderr, "Error: would need to uninstall package %s\n", 170 package->VersionedName().String()); 171 duplicatePackage = true; 172 break; 173 } 174 } 175 176 return duplicatePackage ? 1 : 0; 177 } 178