1 /* 2 * Copyright 2013, 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 */ 8 9 10 #include <errno.h> 11 #include <getopt.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <sys/stat.h> 15 16 #include <package/manager/RepositoryBuilder.h> 17 #include <package/solver/SolverPackageSpecifier.h> 18 #include <package/solver/SolverPackageSpecifierList.h> 19 #include <package/solver/SolverProblem.h> 20 #include <package/solver/SolverResult.h> 21 22 #include <AutoDeleter.h> 23 24 #include "Command.h" 25 #include "pkgman.h" 26 27 28 // TODO: internationalization! 29 30 31 using namespace BPackageKit; 32 using BManager::BPrivate::BPackagePathMap; 33 using BManager::BPrivate::BRepositoryBuilder; 34 35 36 static const char* const kShortUsage = 37 " %command% <package> ... <repository> [ <priority> ] ...\n" 38 " Resolves all packages the given packages depend on.\n"; 39 40 static const char* const kLongUsage = 41 "Usage: %program% %command% <package> ... <repository> [ <priority> ] ...\n" 42 "Resolves and lists all packages the given packages depend on. Fails, if\n" 43 "not all dependencies could be resolved.\n" 44 "\n" 45 "Arguments:\n" 46 " <package>\n" 47 " The HPKG or package info file of the package for which the\n" 48 " dependencies shall be resolved. Multiple files can be specified.\n" 49 " <repository>\n" 50 " Path to a directory containing packages from which the package's\n" 51 " dependencies shall be resolved. Multiple directories can be\n" 52 " specified.\n" 53 " <priority>\n" 54 " Can follow a <repository> to specify the priority of that\n" 55 " repository. The default priority is 0.\n" 56 "\n"; 57 58 59 DEFINE_COMMAND(ResolveDependenciesCommand, "resolve-dependencies", kShortUsage, 60 kLongUsage, kCommandCategoryOther) 61 62 63 static void 64 check_problems(BSolver* solver, const char* errorContext) 65 { 66 if (solver->HasProblems()) { 67 fprintf(stderr, 68 "Encountered problems %s:\n", errorContext); 69 70 int32 problemCount = solver->CountProblems(); 71 for (int32 i = 0; i < problemCount; i++) { 72 printf(" %" B_PRId32 ": %s\n", i + 1, 73 solver->ProblemAt(i)->ToString().String()); 74 } 75 exit(1); 76 } 77 } 78 79 80 static void 81 verify_result(const BSolverResult& result, 82 const BPackagePathMap& specifiedPackagePaths) 83 { 84 // create the solver 85 BSolver* solver; 86 status_t error = BSolver::Create(solver); 87 if (error != B_OK) 88 DIE(error, "failed to create solver"); 89 90 // Add an installation repository and add all of the result packages save 91 // the specified packages. 92 BSolverRepository installation; 93 BRepositoryBuilder installationBuilder(installation, "installation"); 94 95 for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i); 96 i++) { 97 BSolverPackage* package = element->Package(); 98 if (specifiedPackagePaths.find(package) == specifiedPackagePaths.end()) 99 installationBuilder.AddPackage(package->Info()); 100 } 101 installationBuilder.AddToSolver(solver, true); 102 103 // resolve 104 error = solver->VerifyInstallation(); 105 if (error != B_OK) 106 DIE(error, "failed to verify computed package dependencies"); 107 108 check_problems(solver, "verifying computed package dependencies"); 109 } 110 111 112 int 113 ResolveDependenciesCommand::Execute(int argc, const char* const* argv) 114 { 115 while (true) { 116 static struct option sLongOptions[] = { 117 { "help", no_argument, 0, 'h' }, 118 { 0, 0, 0, 0 } 119 }; 120 121 opterr = 0; // don't print errors 122 int c = getopt_long(argc, (char**)argv, "h", sLongOptions, NULL); 123 if (c == -1) 124 break; 125 126 switch (c) { 127 case 'h': 128 PrintUsageAndExit(false); 129 break; 130 131 default: 132 PrintUsageAndExit(true); 133 break; 134 } 135 } 136 137 // The remaining arguments are the package (info) files and the repository 138 // directories (at least one), optionally with priorities. 139 if (argc < optind + 2) 140 PrintUsageAndExit(true); 141 142 // Determine where the package list ends and the repository list starts. 143 const char* const* specifiedPackages = argv + optind; 144 for (; optind < argc; optind++) { 145 const char* path = argv[optind]; 146 struct stat st; 147 if (stat(path, &st) != 0) 148 DIE(errno, "failed to stat() \"%s\"", path); 149 150 if (S_ISDIR(st.st_mode)) 151 break; 152 } 153 154 const char* const* repositoryDirectories = argv + optind; 155 int repositoryDirectoryCount = argc - optind; 156 int specifiedPackageCount = repositoryDirectories - specifiedPackages; 157 158 // create the solver 159 BSolver* solver; 160 status_t error = BSolver::Create(solver); 161 if (error != B_OK) 162 DIE(error, "failed to create solver"); 163 164 // add repositories 165 BPackagePathMap packagePaths; 166 BObjectList<BSolverRepository> repositories(10, true); 167 int32 repositoryIndex = 0; 168 for (int i = 0; i < repositoryDirectoryCount; i++, repositoryIndex++) { 169 const char* directoryPath = repositoryDirectories[i]; 170 171 BSolverRepository* repository = new(std::nothrow) BSolverRepository; 172 if (repository == NULL || !repositories.AddItem(repository)) 173 DIE(B_NO_MEMORY, "failed to create repository"); 174 175 176 if (i + 1 < repositoryDirectoryCount) { 177 char* end; 178 long priority = strtol(repositoryDirectories[i + 1], &end, 0); 179 if (*end == '\0') { 180 repository->SetPriority((uint8)priority); 181 i++; 182 } 183 } 184 185 BRepositoryBuilder(*repository, 186 BString("repository") << repositoryIndex) 187 .SetPackagePathMap(&packagePaths) 188 .AddPackagesDirectory(directoryPath) 189 .AddToSolver(solver); 190 } 191 192 // add a repository with only the specified packages 193 BPackagePathMap specifiedPackagePaths; 194 BSolverRepository dummyRepository; 195 { 196 BRepositoryBuilder builder(dummyRepository, "dummy", 197 "specified packages"); 198 builder.SetPackagePathMap(&specifiedPackagePaths); 199 200 for (int i = 0; i < specifiedPackageCount; i++) 201 builder.AddPackage(specifiedPackages[i]); 202 203 builder.AddToSolver(solver); 204 } 205 206 // resolve 207 BSolverPackageSpecifierList packagesToInstall; 208 for (BPackagePathMap::const_iterator it = specifiedPackagePaths.begin(); 209 it != specifiedPackagePaths.end(); ++it) { 210 if (!packagesToInstall.AppendSpecifier(it->first)) 211 DIE(B_NO_MEMORY, "failed to add specified package"); 212 } 213 214 error = solver->Install(packagesToInstall); 215 if (error != B_OK) 216 DIE(error, "failed to resolve package dependencies"); 217 218 check_problems(solver, "resolving package dependencies"); 219 220 BSolverResult result; 221 error = solver->GetResult(result); 222 if (error != B_OK) 223 DIE(error, "failed to resolve package dependencies"); 224 225 // Verify that the resolved packages don't depend on the specified package. 226 verify_result(result, specifiedPackagePaths); 227 228 // print packages 229 for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i); 230 i++) { 231 // skip the specified package 232 BSolverPackage* package = element->Package(); 233 if (specifiedPackagePaths.find(package) != specifiedPackagePaths.end()) 234 continue; 235 236 // resolve and print the path 237 BPackagePathMap::const_iterator it = packagePaths.find(package); 238 if (it == packagePaths.end()) { 239 DIE(B_ERROR, "ugh, no package %p (%s-%s) not in package path map", 240 package, package->Info().Name().String(), 241 package->Info().Version().ToString().String()); 242 } 243 244 printf("%s\n", it->second.String()); 245 } 246 247 return 0; 248 } 249