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
29using namespace BPackageKit;
30using namespace BPackageKit::BManager::BPrivate;
31
32
33static const char* sProgramName = "get_package_dependencies";
34
35
36#define DIE(result, msg...)											\
37do {																\
38	fprintf(stderr, "*** " msg);									\
39	fprintf(stderr, " : %s\n", strerror(result));					\
40	exit(5);														\
41} while(0)
42
43
44void
45print_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
58int
59main(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