xref: /haiku/src/tools/get_package_dependencies/get_package_dependencies.cpp (revision cbe0a0c436162d78cc3f92a305b64918c839d079)
1 /*
2  * Copyright 2013-2020, 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  *		Andrew Lindesay <apl@lindesay.co.nz>
8  */
9 
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include <map>
16 
17 #include <package/RepositoryCache.h>
18 #include <package/manager/Exceptions.h>
19 #include <package/manager/RepositoryBuilder.h>
20 #include <package/solver/Solver.h>
21 #include <package/solver/SolverPackageSpecifier.h>
22 #include <package/solver/SolverPackageSpecifierList.h>
23 #include <package/solver/SolverProblem.h>
24 #include <package/solver/SolverProblemSolution.h>
25 #include <package/solver/SolverRepository.h>
26 #include <package/solver/SolverResult.h>
27 
28 
29 using namespace BPackageKit;
30 using namespace BPackageKit::BManager::BPrivate;
31 
32 
33 static const char* sProgramName = "get_package_dependencies";
34 
35 
36 #define DIE(result, msg...)											\
37 do {																\
38 	fprintf(stderr, "*** " msg);									\
39 	fprintf(stderr, " : %s\n", strerror(result));					\
40 	exit(5);														\
41 } while(0)
42 
43 
44 void
45 print_usage_and_exit(bool error)
46 {
47 	fprintf(error ? stderr : stdout,
48 		"Usage: %s <repository> ... -- <package> ...\n"
49 		"Resolves the dependencies of the given packages using the given\n"
50 		"repositories and prints the URLs of the packages that are also\n"
51 		"needed to satisfy all requirements. Fails, if there are conflicts\n"
52 		"or some requirements cannot be satisfied.\n",
53 		sProgramName);
54 	exit(error ? 1 : 0);
55 }
56 
57 
58 int
59 main(int argc, const char* const* argv)
60 {
61 	if (argc < 2)
62 		print_usage_and_exit(true);
63 
64 	if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)
65 		print_usage_and_exit(false);
66 
67 	// get lists of repositories and packages
68 	int argIndex = 1;
69 	const char* const* repositories = argv + argIndex;
70 	while (argIndex < argc && strcmp(argv[argIndex], "--") != 0)
71 		argIndex++;
72 	int repositoryCount = argv + argIndex - repositories;
73 
74 	if (repositoryCount == 0 || argIndex == argc)
75 		print_usage_and_exit(true);
76 
77 	const char* const* packages = argv + argIndex + 1;
78 	int packageCount = argv + argc - packages;
79 
80 	// create the solver
81 	BSolver* solver;
82 	status_t error = BSolver::Create(solver);
83 	if (error != B_OK)
84 		DIE(error, "failed to create solver");
85 
86 	// add the "installed" repository with the given packages
87 	BSolverRepository installedRepository;
88 	try {
89 		BRepositoryBuilder installedRepositoryBuilder(installedRepository, "installed");
90 		for (int i = 0; i < packageCount; i++)
91 			installedRepositoryBuilder.AddPackage(packages[i]);
92 		installedRepositoryBuilder.AddToSolver(solver, true);
93 	} catch (BFatalErrorException& e) {
94 		DIE(e.Error(), "%s %s", e.Message().String(), e.Details().String());
95 	}
96 
97 	// add external repositories
98 	std::map<BSolverRepository*, BRepositoryInfo> repositoryInfos;
99 	for (int i = 0; i < repositoryCount; i++) {
100 		BSolverRepository* repository = new BSolverRepository;
101 		BRepositoryCache cache;
102 		error = cache.SetTo(repositories[i]);
103 		if (error != B_OK)
104 			DIE(error, "failed to read repository file '%s'", repositories[i]);
105 		BRepositoryBuilder(*repository, cache)
106 			.AddToSolver(solver, false);
107 		if (cache.Info().BaseURL().IsEmpty()) {
108 			DIE(B_ERROR, "missing base url in repository file '%s'",
109 				repositories[i]);
110 		}
111 		repositoryInfos[repository] = cache.Info();
112 	}
113 
114 	// solve
115 	error = solver->VerifyInstallation();
116 	if (error != B_OK)
117 		DIE(error, "failed to compute packages to install");
118 
119 	// print problems (and fail), if any
120 	if (solver->HasProblems()) {
121 		fprintf(stderr, "Encountered problems:\n");
122 
123 		int32 problemCount = solver->CountProblems();
124 		for (int32 i = 0; i < problemCount; i++) {
125 			// print problem and possible solutions
126 			BSolverProblem* problem = solver->ProblemAt(i);
127 			fprintf(stderr, "problem %" B_PRId32 ": %s\n", i + 1,
128 				problem->ToString().String());
129 
130 			int32 solutionCount = problem->CountSolutions();
131 			for (int32 k = 0; k < solutionCount; k++) {
132 				const BSolverProblemSolution* solution = problem->SolutionAt(k);
133 				fprintf(stderr, "  solution %" B_PRId32 ":\n", k + 1);
134 				int32 elementCount = solution->CountElements();
135 				for (int32 l = 0; l < elementCount; l++) {
136 					const BSolverProblemSolutionElement* element
137 						= solution->ElementAt(l);
138 					fprintf(stderr, "    - %s\n", element->ToString().String());
139 				}
140 			}
141 		}
142 
143 		exit(1);
144 	}
145 
146 	// print URL of packages that additionally need to be installed
147 	BSolverResult result;
148 	error = solver->GetResult(result);
149 	if (error != B_OK)
150 		DIE(error, "failed to compute packages to install");
151 
152 	bool duplicatePackage = false;
153 	for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i);
154 			i++) {
155 		BSolverPackage* package = element->Package();
156 
157 		switch (element->Type()) {
158 			case BSolverResultElement::B_TYPE_INSTALL:
159 				if (package->Repository() != &installedRepository) {
160 					const BRepositoryInfo& info
161 						= repositoryInfos[package->Repository()];
162 					BString url = info.BaseURL();
163 					url << "/packages/" << package->Info().CanonicalFileName();
164 					printf("%s\n", url.String());
165 				}
166 				break;
167 
168 			case BSolverResultElement::B_TYPE_UNINSTALL:
169 				fprintf(stderr, "Error: would need to uninstall package %s\n",
170 					package->VersionedName().String());
171 				duplicatePackage = true;
172 				break;
173 		}
174 	}
175 
176 	return duplicatePackage ? 1 : 0;
177 }
178