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