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