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