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