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