xref: /haiku/src/tools/get_package_dependencies/get_package_dependencies.cpp (revision 692fe5550319c0342c9525e674b7f10105d977ee)
1 /*
2  * Copyright 2013-2016, 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  *		Alexander von Gluck IV <kallisti5@unixzen.com>
8  */
9 
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include <map>
16 
17 #include <FindDirectory.h>
18 #include <package/RepositoryConfig.h>
19 #include <package/RepositoryCache.h>
20 #include <package/manager/Exceptions.h>
21 #include <package/manager/RepositoryBuilder.h>
22 #include <package/solver/Solver.h>
23 #include <package/solver/SolverPackageSpecifier.h>
24 #include <package/solver/SolverPackageSpecifierList.h>
25 #include <package/solver/SolverProblem.h>
26 #include <package/solver/SolverProblemSolution.h>
27 #include <package/solver/SolverRepository.h>
28 #include <package/solver/SolverResult.h>
29 #include <Path.h>
30 
31 #include "HTTPClient.h"
32 
33 
34 using namespace BPackageKit;
35 using namespace BPackageKit::BManager::BPrivate;
36 
37 
38 static const char* sProgramName = "get_package_dependencies";
39 
40 
41 #define DIE(result, msg...)											\
42 do {																\
43 	fprintf(stderr, "*** " msg);									\
44 	fprintf(stderr, " : %s\n", strerror(result));					\
45 	exit(5);														\
46 } while(0)
47 
48 
49 void
50 print_usage_and_exit(bool error)
51 {
52 	fprintf(error ? stderr : stdout,
53 		"Usage: %s <repository-config> ... -- <package> ...\n"
54 		"Resolves the dependencies of the given packages using the given\n"
55 		"repositories and prints the URLs of the packages that are also\n"
56 		"needed to satisfy all requirements. Fails, if there are conflicts\n"
57 		"or some requirements cannot be satisfied.\n",
58 		sProgramName);
59 	exit(error ? 1 : 0);
60 }
61 
62 
63 static status_t
64 get_repocache(BRepositoryConfig config, BPath* outFile)
65 {
66 	BString finalURL;
67 	finalURL.SetTo(config.BaseURL());
68 	finalURL += "/repo";
69 
70 	if (find_directory(B_SYSTEM_TEMP_DIRECTORY, outFile) != B_OK)
71 		outFile->SetTo("/tmp");
72 
73 	BString tempFile = config.Name();
74 	tempFile += ".repocache";
75 
76 	outFile->Append(tempFile);
77 
78 	//printf("Downloading: %s\n", finalURL.String());
79 	//printf("Final output will be %s\n", outFile->Path());
80 
81 	BEntry entry(outFile->Path());
82 	HTTPClient* http = new HTTPClient;
83 	status_t result = http->Get(&finalURL, &entry);
84 	delete http;
85 
86 	return result;
87 }
88 
89 
90 int
91 main(int argc, const char* const* argv)
92 {
93 	if (argc < 2)
94 		print_usage_and_exit(true);
95 
96 	if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)
97 		print_usage_and_exit(false);
98 
99 	// get lists of repositories and packages
100 	int argIndex = 1;
101 	const char* const* repositories = argv + argIndex;
102 	while (argIndex < argc && strcmp(argv[argIndex], "--") != 0)
103 		argIndex++;
104 	int repositoryCount = argv + argIndex - repositories;
105 
106 	if (repositoryCount == 0 || argIndex == argc)
107 		print_usage_and_exit(true);
108 
109 	const char* const* packages = argv + argIndex + 1;
110 	int packageCount = argv + argc - packages;
111 
112 	// create the solver
113 	BSolver* solver;
114 	status_t error = BSolver::Create(solver);
115 	if (error != B_OK)
116 		DIE(error, "failed to create solver");
117 
118 	// add the "installed" repository with the given packages
119 	BSolverRepository installedRepository;
120 	try {
121 		BRepositoryBuilder installedRepositoryBuilder(installedRepository, "installed");
122 		for (int i = 0; i < packageCount; i++)
123 			installedRepositoryBuilder.AddPackage(packages[i]);
124 		installedRepositoryBuilder.AddToSolver(solver, true);
125 	} catch (BFatalErrorException e) {
126 		DIE(B_OK, "%s", e.Details().String());
127 	}
128 
129 	// add specified remote repositories
130 	std::map<BSolverRepository*, BString> repositoryURLs;
131 
132 	for (int i = 0; i < repositoryCount; i++) {
133 		BSolverRepository* repository = new BSolverRepository;
134 		BRepositoryConfig config;
135 		error = config.SetTo(repositories[i]);
136 		if (error != B_OK)
137 			DIE(error, "failed to read repository config '%s'", repositories[i]);
138 
139 		// TODO: It would make sense if BRepositoryBuilder could accept a
140 		// BRepositoryConfig directly and "get" the remote BRepositoryCache
141 		// to properly solve dependencies. For now we curl here.
142 
143 		BPath output;
144 		get_repocache(config, &output);
145 
146 		BRepositoryCache cache;
147 		error = cache.SetTo(output.Path());
148 		if (error != B_OK)
149 			DIE(error, "failed to read repository cache '%s'", repositories[i]);
150 
151 		BRepositoryBuilder(*repository, cache)
152 			.AddToSolver(solver, false);
153 		repositoryURLs[repository] = config.BaseURL();
154 	}
155 
156 	// solve
157 	error = solver->VerifyInstallation();
158 	if (error != B_OK)
159 		DIE(error, "failed to compute packages to install");
160 
161 	// print problems (and fail), if any
162 	if (solver->HasProblems()) {
163 		fprintf(stderr, "Encountered problems:\n");
164 
165 		int32 problemCount = solver->CountProblems();
166 		for (int32 i = 0; i < problemCount; i++) {
167 			// print problem and possible solutions
168 			BSolverProblem* problem = solver->ProblemAt(i);
169 			fprintf(stderr, "problem %" B_PRId32 ": %s\n", i + 1,
170 				problem->ToString().String());
171 
172 			int32 solutionCount = problem->CountSolutions();
173 			for (int32 k = 0; k < solutionCount; k++) {
174 				const BSolverProblemSolution* solution = problem->SolutionAt(k);
175 				fprintf(stderr, "  solution %" B_PRId32 ":\n", k + 1);
176 				int32 elementCount = solution->CountElements();
177 				for (int32 l = 0; l < elementCount; l++) {
178 					const BSolverProblemSolutionElement* element
179 						= solution->ElementAt(l);
180 					fprintf(stderr, "    - %s\n", element->ToString().String());
181 				}
182 			}
183 		}
184 
185 		exit(1);
186 	}
187 
188 	// print URL of packages that additionally need to be installed
189 	BSolverResult result;
190 	error = solver->GetResult(result);
191 	if (error != B_OK)
192 		DIE(error, "failed to compute packages to install");
193 
194 	bool duplicatePackage = false;
195 	for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i);
196 			i++) {
197 		BSolverPackage* package = element->Package();
198 
199 		switch (element->Type()) {
200 			case BSolverResultElement::B_TYPE_INSTALL:
201 				if (package->Repository() != &installedRepository) {
202 					BString url = repositoryURLs[package->Repository()];
203 					url << "/packages/" << package->Info().CanonicalFileName();
204 					printf("%s\n", url.String());
205 				}
206 				break;
207 
208 			case BSolverResultElement::B_TYPE_UNINSTALL:
209 				fprintf(stderr, "Error: would need to uninstall package %s\n",
210 					package->VersionedName().String());
211 				duplicatePackage = true;
212 				break;
213 		}
214 	}
215 
216 	return duplicatePackage ? 1 : 0;
217 }
218