1 /* 2 * Copyright 2013-2016, 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 * Alexander von Gluck IV <kallisti5@unixzen.com> 8 */ 9 10 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 15 #include <map> 16 17 #include <FindDirectory.h> 18 #include <package/RepositoryConfig.h> 19 #include <package/RepositoryCache.h> 20 #include <package/manager/Exceptions.h> 21 #include <package/manager/RepositoryBuilder.h> 22 #include <package/solver/Solver.h> 23 #include <package/solver/SolverPackageSpecifier.h> 24 #include <package/solver/SolverPackageSpecifierList.h> 25 #include <package/solver/SolverProblem.h> 26 #include <package/solver/SolverProblemSolution.h> 27 #include <package/solver/SolverRepository.h> 28 #include <package/solver/SolverResult.h> 29 #include <Path.h> 30 31 #include "HTTPClient.h" 32 33 34 using namespace BPackageKit; 35 using namespace BPackageKit::BManager::BPrivate; 36 37 38 static const char* sProgramName = "get_package_dependencies"; 39 40 41 #define DIE(result, msg...) \ 42 do { \ 43 fprintf(stderr, "*** " msg); \ 44 fprintf(stderr, " : %s\n", strerror(result)); \ 45 exit(5); \ 46 } while(0) 47 48 49 void 50 print_usage_and_exit(bool error) 51 { 52 fprintf(error ? stderr : stdout, 53 "Usage: %s <repository-config> ... -- <package> ...\n" 54 "Resolves the dependencies of the given packages using the given\n" 55 "repositories and prints the URLs of the packages that are also\n" 56 "needed to satisfy all requirements. Fails, if there are conflicts\n" 57 "or some requirements cannot be satisfied.\n", 58 sProgramName); 59 exit(error ? 1 : 0); 60 } 61 62 63 static status_t 64 get_repocache(BRepositoryConfig config, BPath* outFile) 65 { 66 BString finalURL; 67 finalURL.SetTo(config.BaseURL()); 68 finalURL += "/repo"; 69 70 if (find_directory(B_SYSTEM_TEMP_DIRECTORY, outFile) != B_OK) 71 outFile->SetTo("/tmp"); 72 73 BString tempFile = config.Name(); 74 tempFile += ".repocache"; 75 76 outFile->Append(tempFile); 77 78 //printf("Downloading: %s\n", finalURL.String()); 79 //printf("Final output will be %s\n", outFile->Path()); 80 81 BEntry entry(outFile->Path()); 82 HTTPClient* http = new HTTPClient; 83 status_t result = http->Get(&finalURL, &entry); 84 delete http; 85 86 return result; 87 } 88 89 90 int 91 main(int argc, const char* const* argv) 92 { 93 if (argc < 2) 94 print_usage_and_exit(true); 95 96 if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) 97 print_usage_and_exit(false); 98 99 // get lists of repositories and packages 100 int argIndex = 1; 101 const char* const* repositories = argv + argIndex; 102 while (argIndex < argc && strcmp(argv[argIndex], "--") != 0) 103 argIndex++; 104 int repositoryCount = argv + argIndex - repositories; 105 106 if (repositoryCount == 0 || argIndex == argc) 107 print_usage_and_exit(true); 108 109 const char* const* packages = argv + argIndex + 1; 110 int packageCount = argv + argc - packages; 111 112 // create the solver 113 BSolver* solver; 114 status_t error = BSolver::Create(solver); 115 if (error != B_OK) 116 DIE(error, "failed to create solver"); 117 118 // add the "installed" repository with the given packages 119 BSolverRepository installedRepository; 120 try { 121 BRepositoryBuilder installedRepositoryBuilder(installedRepository, "installed"); 122 for (int i = 0; i < packageCount; i++) 123 installedRepositoryBuilder.AddPackage(packages[i]); 124 installedRepositoryBuilder.AddToSolver(solver, true); 125 } catch (BFatalErrorException e) { 126 DIE(B_OK, "%s", e.Details().String()); 127 } 128 129 // add specified remote repositories 130 std::map<BSolverRepository*, BString> repositoryURLs; 131 132 for (int i = 0; i < repositoryCount; i++) { 133 BSolverRepository* repository = new BSolverRepository; 134 BRepositoryConfig config; 135 error = config.SetTo(repositories[i]); 136 if (error != B_OK) 137 DIE(error, "failed to read repository config '%s'", repositories[i]); 138 139 // TODO: It would make sense if BRepositoryBuilder could accept a 140 // BRepositoryConfig directly and "get" the remote BRepositoryCache 141 // to properly solve dependencies. For now we curl here. 142 143 BPath output; 144 get_repocache(config, &output); 145 146 BRepositoryCache cache; 147 error = cache.SetTo(output.Path()); 148 if (error != B_OK) 149 DIE(error, "failed to read repository cache '%s'", repositories[i]); 150 151 BRepositoryBuilder(*repository, cache) 152 .AddToSolver(solver, false); 153 repositoryURLs[repository] = config.BaseURL(); 154 } 155 156 // solve 157 error = solver->VerifyInstallation(); 158 if (error != B_OK) 159 DIE(error, "failed to compute packages to install"); 160 161 // print problems (and fail), if any 162 if (solver->HasProblems()) { 163 fprintf(stderr, "Encountered problems:\n"); 164 165 int32 problemCount = solver->CountProblems(); 166 for (int32 i = 0; i < problemCount; i++) { 167 // print problem and possible solutions 168 BSolverProblem* problem = solver->ProblemAt(i); 169 fprintf(stderr, "problem %" B_PRId32 ": %s\n", i + 1, 170 problem->ToString().String()); 171 172 int32 solutionCount = problem->CountSolutions(); 173 for (int32 k = 0; k < solutionCount; k++) { 174 const BSolverProblemSolution* solution = problem->SolutionAt(k); 175 fprintf(stderr, " solution %" B_PRId32 ":\n", k + 1); 176 int32 elementCount = solution->CountElements(); 177 for (int32 l = 0; l < elementCount; l++) { 178 const BSolverProblemSolutionElement* element 179 = solution->ElementAt(l); 180 fprintf(stderr, " - %s\n", element->ToString().String()); 181 } 182 } 183 } 184 185 exit(1); 186 } 187 188 // print URL of packages that additionally need to be installed 189 BSolverResult result; 190 error = solver->GetResult(result); 191 if (error != B_OK) 192 DIE(error, "failed to compute packages to install"); 193 194 bool duplicatePackage = false; 195 for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i); 196 i++) { 197 BSolverPackage* package = element->Package(); 198 199 switch (element->Type()) { 200 case BSolverResultElement::B_TYPE_INSTALL: 201 if (package->Repository() != &installedRepository) { 202 BString url = repositoryURLs[package->Repository()]; 203 url << "/packages/" << package->Info().CanonicalFileName(); 204 printf("%s\n", url.String()); 205 } 206 break; 207 208 case BSolverResultElement::B_TYPE_UNINSTALL: 209 fprintf(stderr, "Error: would need to uninstall package %s\n", 210 package->VersionedName().String()); 211 duplicatePackage = true; 212 break; 213 } 214 } 215 216 return duplicatePackage ? 1 : 0; 217 } 218