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