xref: /haiku/src/bin/pkgman/command_resolve_dependencies.cpp (revision 56430ad8002b8fd1ac69b590e9cc130de6d9e852)
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