LibsolvSolver.cpp revision 8e6c3631
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 "LibsolvSolver.h"
11
12#include <errno.h>
13
14#include <new>
15
16#include <solv/policy.h>
17#include <solv/poolarch.h>
18#include <solv/repo.h>
19#include <solv/repo_haiku.h>
20#include <solv/selection.h>
21#include <solv/solverdebug.h>
22
23#include <package/PackageResolvableExpression.h>
24#include <package/RepositoryCache.h>
25#include <package/solver/SolverPackage.h>
26#include <package/solver/SolverPackageSpecifier.h>
27#include <package/solver/SolverPackageSpecifierList.h>
28#include <package/solver/SolverProblem.h>
29#include <package/solver/SolverRepository.h>
30#include <package/solver/SolverResult.h>
31
32#include <AutoDeleter.h>
33#include <ObjectList.h>
34
35
36// TODO: libsolv doesn't have any helpful out-of-memory handling. It just just
37// abort()s. Obviously that isn't good behavior for a library.
38
39
40BSolver*
41BPackageKit::create_solver()
42{
43	return new(std::nothrow) LibsolvSolver;
44}
45
46
47struct LibsolvSolver::SolvQueue : Queue {
48	SolvQueue()
49	{
50		queue_init(this);
51	}
52
53	~SolvQueue()
54	{
55		queue_free(this);
56	}
57};
58
59
60struct LibsolvSolver::SolvDataIterator : Dataiterator {
61	SolvDataIterator(Pool* pool, Repo* repo, Id solvableId, Id keyname,
62		const char* match, int flags)
63	{
64		dataiterator_init(this, pool, repo, solvableId, keyname, match, flags);
65	}
66
67	~SolvDataIterator()
68	{
69		dataiterator_free(this);
70	}
71};
72
73
74struct LibsolvSolver::RepositoryInfo {
75	RepositoryInfo(BSolverRepository* repository)
76		:
77		fRepository(repository),
78		fSolvRepo(NULL),
79		fChangeCount(repository->ChangeCount())
80	{
81	}
82
83	BSolverRepository* Repository() const
84	{
85		return fRepository;
86	}
87
88	Repo* SolvRepo()
89	{
90		return fSolvRepo;
91	}
92
93	void SetSolvRepo(Repo* repo)
94	{
95		fSolvRepo = repo;
96	}
97
98	bool HasChanged() const
99	{
100		return fChangeCount != fRepository->ChangeCount() || fSolvRepo == NULL;
101	}
102
103	void SetUnchanged()
104	{
105		fChangeCount = fRepository->ChangeCount();
106	}
107
108private:
109	BSolverRepository*	fRepository;
110	Repo*				fSolvRepo;
111	uint64				fChangeCount;
112};
113
114
115struct LibsolvSolver::Problem : public BSolverProblem {
116	Problem(::Id id, BType type, BSolverPackage* sourcePackage,
117		BSolverPackage* targetPackage,
118		const BPackageResolvableExpression& dependency)
119		:
120		BSolverProblem(type, sourcePackage, targetPackage, dependency),
121		fId(id),
122		fSelectedSolution(NULL)
123	{
124	}
125
126	::Id Id() const
127	{
128		return fId;
129	}
130
131	const Solution* SelectedSolution() const
132	{
133		return fSelectedSolution;
134	}
135
136	void SetSelectedSolution(const Solution* solution)
137	{
138		fSelectedSolution = solution;
139	}
140
141private:
142	::Id			fId;
143	const Solution*	fSelectedSolution;
144};
145
146
147struct LibsolvSolver::Solution : public BSolverProblemSolution {
148	Solution(::Id id, LibsolvSolver::Problem* problem)
149		:
150		BSolverProblemSolution(),
151		fId(id),
152		fProblem(problem)
153	{
154	}
155
156	::Id Id() const
157	{
158		return fId;
159	}
160
161	LibsolvSolver::Problem* Problem() const
162	{
163		return fProblem;
164	}
165
166private:
167	::Id					fId;
168	LibsolvSolver::Problem*	fProblem;
169};
170
171
172// #pragma mark - LibsolvSolver
173
174
175LibsolvSolver::LibsolvSolver()
176	:
177	fPool(NULL),
178	fSolver(NULL),
179	fJobs(NULL),
180	fRepositoryInfos(10, true),
181	fInstalledRepository(NULL),
182	fSolvablePackages(),
183	fPackageSolvables(),
184	fProblems(10, true)
185{
186}
187
188
189LibsolvSolver::~LibsolvSolver()
190{
191	_Cleanup();
192}
193
194
195status_t
196LibsolvSolver::Init()
197{
198	_Cleanup();
199
200	// We do all initialization lazily.
201	return B_OK;
202}
203
204
205status_t
206LibsolvSolver::AddRepository(BSolverRepository* repository)
207{
208	if (repository == NULL || repository->InitCheck() != B_OK)
209		return B_BAD_VALUE;
210
211	// If the repository represents installed packages, check, if we already
212	// have such a repository.
213	if (repository->IsInstalled() && fInstalledRepository != NULL)
214		return B_BAD_VALUE;
215
216	// add the repository info
217	RepositoryInfo* info = new(std::nothrow) RepositoryInfo(repository);
218	if (info == NULL)
219		return B_NO_MEMORY;
220
221	if (!fRepositoryInfos.AddItem(info)) {
222		delete info;
223		return B_NO_MEMORY;
224	}
225
226	if (repository->IsInstalled())
227		fInstalledRepository = info;
228
229	return B_OK;
230}
231
232
233status_t
234LibsolvSolver::FindPackages(const char* searchString, uint32 flags,
235	BObjectList<BSolverPackage>& _packages)
236{
237	// add repositories to pool
238	status_t error = _AddRepositories();
239	if (error != B_OK)
240		return error;
241
242	// create data iterator
243	int iteratorFlags = SEARCH_SUBSTRING;
244	if ((flags & B_FIND_CASE_INSENSITIVE) != 0)
245		iteratorFlags |= SEARCH_NOCASE;
246
247	SolvDataIterator iterator(fPool, 0, 0, 0, searchString, iteratorFlags);
248	SolvQueue selection;
249
250	// search package names
251	if ((flags & B_FIND_IN_NAME) != 0) {
252		dataiterator_set_keyname(&iterator, SOLVABLE_NAME);
253		dataiterator_set_search(&iterator, 0, 0);
254
255		while (dataiterator_step(&iterator))
256			queue_push2(&selection, SOLVER_SOLVABLE, iterator.solvid);
257	}
258
259	// search package summaries
260	if ((flags & B_FIND_IN_SUMMARY) != 0) {
261		dataiterator_set_keyname(&iterator, SOLVABLE_SUMMARY);
262		dataiterator_set_search(&iterator, 0, 0);
263
264		while (dataiterator_step(&iterator))
265			queue_push2(&selection, SOLVER_SOLVABLE, iterator.solvid);
266	}
267
268	// search package description
269	if ((flags & B_FIND_IN_DESCRIPTION) != 0) {
270		dataiterator_set_keyname(&iterator, SOLVABLE_DESCRIPTION);
271		dataiterator_set_search(&iterator, 0, 0);
272
273		while (dataiterator_step(&iterator))
274			queue_push2(&selection, SOLVER_SOLVABLE, iterator.solvid);
275	}
276
277	// search package provides
278	if ((flags & B_FIND_IN_PROVIDES) != 0) {
279		dataiterator_set_keyname(&iterator, SOLVABLE_PROVIDES);
280		dataiterator_set_search(&iterator, 0, 0);
281
282		while (dataiterator_step(&iterator))
283			queue_push2(&selection, SOLVER_SOLVABLE, iterator.solvid);
284	}
285
286	// get solvables
287	SolvQueue solvables;
288	selection_solvables(fPool, &selection, &solvables);
289
290	// get packages
291	for (int i = 0; i < solvables.count; i++) {
292		BSolverPackage* package = _GetPackage(solvables.elements[i]);
293		if (package == NULL)
294			return B_ERROR;
295
296		if (!_packages.AddItem(package))
297			return B_NO_MEMORY;
298	}
299
300	return B_OK;
301}
302
303
304status_t
305LibsolvSolver::Install(const BSolverPackageSpecifierList& packages,
306	const BSolverPackageSpecifier** _unmatched)
307{
308	if (_unmatched != NULL)
309		*_unmatched = NULL;
310
311	if (packages.IsEmpty())
312		return B_BAD_VALUE;
313
314	// add repositories to pool
315	status_t error = _AddRepositories();
316	if (error != B_OK)
317		return error;
318
319	// add the packages to install to the job queue
320	error = _InitJobQueue();
321	if (error != B_OK)
322		return error;
323
324	int32 packageCount = packages.CountSpecifiers();
325	for (int32 i = 0; i < packageCount; i++) {
326		const BSolverPackageSpecifier& specifier = *packages.SpecifierAt(i);
327		switch (specifier.Type()) {
328			case BSolverPackageSpecifier::B_UNSPECIFIED:
329				return B_BAD_VALUE;
330
331			case BSolverPackageSpecifier::B_PACKAGE:
332			{
333				BSolverPackage* package = specifier.Package();
334				Solvable* solvable;
335				if (package == NULL
336					|| (solvable = _GetSolvable(package)) == NULL) {
337					return B_BAD_VALUE;
338				}
339
340				queue_push2(fJobs, SOLVER_SOLVABLE,
341					solvable - fPool->solvables);
342				break;
343			}
344
345			case BSolverPackageSpecifier::B_SELECT_STRING:
346			{
347				// find matching packages
348				SolvQueue matchingPackages;
349
350				int flags = SELECTION_NAME | SELECTION_PROVIDES | SELECTION_GLOB
351					| SELECTION_CANON | SELECTION_DOTARCH | SELECTION_REL;
352				/*int matchFlags =*/ selection_make(fPool, &matchingPackages,
353					specifier.SelectString().String(), flags);
354				if (matchingPackages.count == 0) {
355					if (_unmatched != NULL)
356						*_unmatched = &specifier;
357					return B_NAME_NOT_FOUND;
358				}
359// TODO: We might want to add support for restricting to certain repositories.
360#if 0
361				// restrict to the matching repository
362				if (BSolverRepository* repository = specifier.Repository()) {
363					RepositoryInfo* repositoryInfo
364						= _GetRepositoryInfo(repository);
365					if (repositoryInfo == NULL)
366						return B_BAD_VALUE;
367
368					SolvQueue repoFilter;
369					queue_push2(&repoFilter,
370						SOLVER_SOLVABLE_REPO
371							/* | SOLVER_SETREPO | SOLVER_SETVENDOR*/,
372						repositoryInfo->SolvRepo()->repoid);
373
374					selection_filter(fPool, &matchingPackages, &repoFilter);
375
376					if (matchingPackages.count == 0)
377						return B_NAME_NOT_FOUND;
378				}
379#endif
380
381				for (int j = 0; j < matchingPackages.count; j++)
382					queue_push(fJobs, matchingPackages.elements[j]);
383			}
384		}
385	}
386
387	// set jobs' solver mode and solve
388	_SetJobsSolverMode(*fJobs, SOLVER_INSTALL);
389
390	return _Solve(false);
391}
392
393
394status_t
395LibsolvSolver::VerifyInstallation()
396{
397	if (fInstalledRepository == NULL)
398		return B_BAD_VALUE;
399
400	// add repositories to pool
401	status_t error = _AddRepositories();
402	if (error != B_OK)
403		return error;
404
405	// add the verify job to the job queue
406	error = _InitJobQueue();
407	if (error != B_OK)
408		return error;
409
410	queue_push2(fJobs, SOLVER_SOLVABLE_ALL, 0);
411
412	// set jobs' solver mode and solve
413	_SetJobsSolverMode(*fJobs, SOLVER_VERIFY);
414
415	return _Solve(false);
416}
417
418
419status_t
420LibsolvSolver::SelectProblemSolution(BSolverProblem* _problem,
421	const BSolverProblemSolution* _solution)
422{
423	if (_problem == NULL)
424		return B_BAD_VALUE;
425
426	Problem* problem = static_cast<Problem*>(_problem);
427	if (_solution == NULL) {
428		problem->SetSelectedSolution(NULL);
429		return B_OK;
430	}
431
432	const Solution* solution = static_cast<const Solution*>(_solution);
433	if (solution->Problem() != problem)
434		return B_BAD_VALUE;
435
436	problem->SetSelectedSolution(solution);
437	return B_OK;
438}
439
440
441status_t
442LibsolvSolver::SolveAgain()
443{
444	if (fSolver == NULL || fJobs == NULL)
445		return B_BAD_VALUE;
446
447	// iterate through all problems and propagate the selected solutions
448	int32 problemCount = fProblems.CountItems();
449	for (int32 i = 0; i < problemCount; i++) {
450		Problem* problem = fProblems.ItemAt(i);
451		if (const Solution* solution = problem->SelectedSolution())
452			solver_take_solution(fSolver, problem->Id(), solution->Id(), fJobs);
453	}
454
455	return _Solve(true);
456}
457
458
459int32
460LibsolvSolver::CountProblems() const
461{
462	return fProblems.CountItems();
463}
464
465
466BSolverProblem*
467LibsolvSolver::ProblemAt(int32 index) const
468{
469	return fProblems.ItemAt(index);
470}
471
472
473status_t
474LibsolvSolver::GetResult(BSolverResult& _result)
475{
476	if (fSolver == NULL || HasProblems())
477		return B_BAD_VALUE;
478
479	_result.MakeEmpty();
480
481	Transaction* transaction = solver_create_transaction(fSolver);
482	CObjectDeleter<Transaction> transactionDeleter(transaction,
483		&transaction_free);
484
485	if (transaction->steps.count == 0)
486		return B_OK;
487
488	// Get the packages that end up in the installation. The result queue
489	// contains newPackageCount new packages to install first, followed by the
490	// kept packages.
491	SolvQueue installedPackages;
492	int newPackageCount = transaction_installedresult(transaction,
493		&installedPackages);
494
495	transaction_order(transaction, 0);
496
497	for (int i = 0; i < transaction->steps.count; i++) {
498		Id solvableId = transaction->steps.elements[i];
499		Solvable* solvable = pool_id2solvable(fPool, solvableId);
500		switch (transaction_type(transaction, solvableId,
501				SOLVER_TRANSACTION_RPM_ONLY)) {
502			case SOLVER_TRANSACTION_ERASE:
503			{
504				BSolverPackage* package = _GetPackage(solvable);
505				if (package == NULL)
506					return B_ERROR;
507
508				status_t error = _result.AppendElement(
509					BSolverResultElement(BSolverResultElement::B_TYPE_UNINSTALL,
510						package));
511				if (error != B_OK)
512					return error;
513				break;
514			}
515
516			case SOLVER_TRANSACTION_INSTALL:
517			case SOLVER_TRANSACTION_MULTIINSTALL:
518			{
519				// check, if it really is a new package
520// TODO: Is this check really necessary?
521				bool foundPackage = false;
522				for (int j = 0; j < newPackageCount; j++) {
523					if (installedPackages.elements[j] == solvableId) {
524						foundPackage = true;
525						break;
526					}
527				}
528
529				if (!foundPackage)
530					continue;
531
532				BSolverPackage* package = _GetPackage(solvable);
533				if (package == NULL)
534					return B_ERROR;
535
536				if (!_result.AppendElement(
537						BSolverResultElement(
538							BSolverResultElement::B_TYPE_INSTALL, package))) {
539					return B_NO_MEMORY;
540				}
541				break;
542			}
543
544			default:
545				break;
546		}
547	}
548
549	return B_OK;
550}
551
552
553status_t
554LibsolvSolver::_InitPool()
555{
556	_CleanupPool();
557
558	fPool = pool_create();
559
560	// Set the system architecture. We use what uname() returns unless we're on
561	// x86 gcc2.
562	{
563		const char* arch;
564		#ifdef __HAIKU_ARCH_X86
565			#if (B_HAIKU_ABI & B_HAIKU_ABI_MAJOR) == B_HAIKU_ABI_GCC_2
566				arch = "x86_gcc2";
567			#else
568				arch = "x86";
569			#endif
570		#else
571			struct utsname info;
572			if (uname(&info) != 0)
573				return errno;
574			arch = info.machine;
575		#endif
576
577		pool_setarchpolicy(fPool, arch);
578	}
579
580	return B_OK;
581}
582
583
584status_t
585LibsolvSolver::_InitJobQueue()
586{
587	_CleanupJobQueue();
588
589	fJobs = new(std::nothrow) SolvQueue;
590	return fJobs != NULL ? B_OK : B_NO_MEMORY;;
591}
592
593
594void
595LibsolvSolver::_Cleanup()
596{
597	_CleanupPool();
598
599	fInstalledRepository = NULL;
600	fRepositoryInfos.MakeEmpty();
601
602}
603
604
605void
606LibsolvSolver::_CleanupPool()
607{
608	// clean up jobs and solver data
609	_CleanupJobQueue();
610
611	// clean up our data structures that depend on/refer to libsolv pool data
612	fSolvablePackages.clear();
613	fPackageSolvables.clear();
614
615	int32 repositoryCount = fRepositoryInfos.CountItems();
616	for (int32 i = 0; i < repositoryCount; i++)
617		fRepositoryInfos.ItemAt(i)->SetSolvRepo(NULL);
618
619	// delete the pool
620	if (fPool != NULL) {
621		pool_free(fPool);
622		fPool = NULL;
623	}
624}
625
626
627void
628LibsolvSolver::_CleanupJobQueue()
629{
630	_CleanupSolver();
631
632	delete fJobs;
633	fJobs = NULL;
634}
635
636
637void
638LibsolvSolver::_CleanupSolver()
639{
640	fProblems.MakeEmpty();
641
642	if (fSolver != NULL) {
643		solver_free(fSolver);
644		fSolver = NULL;
645	}
646}
647
648
649bool
650LibsolvSolver::_HaveRepositoriesChanged() const
651{
652	int32 repositoryCount = fRepositoryInfos.CountItems();
653	for (int32 i = 0; i < repositoryCount; i++) {
654		RepositoryInfo* repositoryInfo = fRepositoryInfos.ItemAt(i);
655		if (repositoryInfo->HasChanged())
656			return true;
657	}
658
659	return false;
660}
661
662
663status_t
664LibsolvSolver::_AddRepositories()
665{
666	if (fPool != NULL && !_HaveRepositoriesChanged())
667		return B_OK;
668
669	// something has changed -- re-create the pool
670	status_t error = _InitPool();
671	if (error != B_OK)
672		return error;
673
674	int32 repositoryCount = fRepositoryInfos.CountItems();
675	for (int32 i = 0; i < repositoryCount; i++) {
676		RepositoryInfo* repositoryInfo = fRepositoryInfos.ItemAt(i);
677		BSolverRepository* repository = repositoryInfo->Repository();
678		Repo* repo = repo_create(fPool, repository->Name());
679		repositoryInfo->SetSolvRepo(repo);
680
681		repo->priority = -1 - repository->Priority();
682		repo->appdata = (void*)repositoryInfo;
683
684		int32 packageCount = repository->CountPackages();
685		for (int32 k = 0; k < packageCount; k++) {
686			BSolverPackage* package = repository->PackageAt(k);
687			Id solvableId = repo_add_haiku_package_info(repo, package->Info(),
688				REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE);
689			Solvable* solvable = pool_id2solvable(fPool, solvableId);
690
691			try {
692				fSolvablePackages[solvable] = package;
693				fPackageSolvables[package] = solvable;
694			} catch (std::bad_alloc&) {
695				return B_NO_MEMORY;
696			}
697		}
698
699		repo_internalize(repo);
700
701		if (repository->IsInstalled())
702			pool_set_installed(fPool, repo);
703
704		repositoryInfo->SetUnchanged();
705	}
706
707	// create "provides" lookup
708	pool_createwhatprovides(fPool);
709
710	return B_OK;
711}
712
713
714LibsolvSolver::RepositoryInfo*
715LibsolvSolver::_GetRepositoryInfo(BSolverRepository* repository) const
716{
717	int32 repositoryCount = fRepositoryInfos.CountItems();
718	for (int32 i = 0; i < repositoryCount; i++) {
719		RepositoryInfo* repositoryInfo = fRepositoryInfos.ItemAt(i);
720		if (repository == repositoryInfo->Repository())
721			return repositoryInfo;
722	}
723
724	return NULL;
725}
726
727
728BSolverPackage*
729LibsolvSolver::_GetPackage(Solvable* solvable) const
730{
731	SolvableMap::const_iterator it = fSolvablePackages.find(solvable);
732	return it != fSolvablePackages.end() ? it->second : NULL;
733}
734
735
736BSolverPackage*
737LibsolvSolver::_GetPackage(Id solvableId) const
738{
739	Solvable* solvable = pool_id2solvable(fPool, solvableId);
740	return solvable != NULL ? _GetPackage(solvable) : NULL;
741}
742
743
744Solvable*
745LibsolvSolver::_GetSolvable(BSolverPackage* package) const
746{
747	PackageMap::const_iterator it = fPackageSolvables.find(package);
748	return it != fPackageSolvables.end() ? it->second : NULL;
749}
750
751
752status_t
753LibsolvSolver::_AddProblem(Id problemId)
754{
755	enum {
756		NEED_SOURCE		= 0x1,
757		NEED_TARGET		= 0x2,
758		NEED_DEPENDENCY	= 0x4
759	};
760
761	Id ruleId = solver_findproblemrule(fSolver, problemId);
762	Id sourceId;
763	Id targetId;
764	Id dependencyId;
765	BSolverProblem::BType problemType = BSolverProblem::B_UNSPECIFIED;
766	uint32 needed = 0;
767
768	switch (solver_ruleinfo(fSolver, ruleId, &sourceId, &targetId,
769			&dependencyId)) {
770		case SOLVER_RULE_DISTUPGRADE:
771			problemType = BSolverProblem::B_NOT_IN_DISTUPGRADE_REPOSITORY;
772			needed = NEED_SOURCE;
773			break;
774		case SOLVER_RULE_INFARCH:
775			problemType = BSolverProblem::B_INFERIOR_ARCHITECTURE;
776			needed = NEED_SOURCE;
777			break;
778		case SOLVER_RULE_UPDATE:
779			problemType = BSolverProblem::B_INSTALLED_PACKAGE_PROBLEM;
780			needed = NEED_SOURCE;
781			break;
782		case SOLVER_RULE_JOB:
783			problemType = BSolverProblem::B_CONFLICTING_REQUESTS;
784			break;
785		case SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP:
786			problemType = BSolverProblem::B_REQUESTED_RESOLVABLE_NOT_PROVIDED;
787			needed = NEED_DEPENDENCY;
788			break;
789		case SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM:
790			problemType
791				= BSolverProblem::B_REQUESTED_RESOLVABLE_PROVIDED_BY_SYSTEM;
792			needed = NEED_DEPENDENCY;
793			break;
794		case SOLVER_RULE_RPM:
795			problemType = BSolverProblem::B_DEPENDENCY_PROBLEM;
796			break;
797		case SOLVER_RULE_RPM_NOT_INSTALLABLE:
798			problemType = BSolverProblem::B_PACKAGE_NOT_INSTALLABLE;
799			needed = NEED_SOURCE;
800			break;
801		case SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP:
802			problemType = BSolverProblem::B_DEPENDENCY_NOT_PROVIDED;
803			needed = NEED_SOURCE | NEED_DEPENDENCY;
804			break;
805		case SOLVER_RULE_RPM_SAME_NAME:
806			problemType = BSolverProblem::B_PACKAGE_NAME_CLASH;
807			needed = NEED_SOURCE | NEED_TARGET;
808			break;
809		case SOLVER_RULE_RPM_PACKAGE_CONFLICT:
810			problemType = BSolverProblem::B_PACKAGE_CONFLICT;
811			needed = NEED_SOURCE | NEED_TARGET | NEED_DEPENDENCY;
812			break;
813		case SOLVER_RULE_RPM_PACKAGE_OBSOLETES:
814			problemType = BSolverProblem::B_PACKAGE_OBSOLETES_RESOLVABLE;
815			needed = NEED_SOURCE | NEED_TARGET | NEED_DEPENDENCY;
816			break;
817		case SOLVER_RULE_RPM_INSTALLEDPKG_OBSOLETES:
818			problemType
819				= BSolverProblem::B_INSTALLED_PACKAGE_OBSOLETES_RESOLVABLE;
820			needed = NEED_SOURCE | NEED_TARGET | NEED_DEPENDENCY;
821			break;
822		case SOLVER_RULE_RPM_IMPLICIT_OBSOLETES:
823			problemType
824				= BSolverProblem::B_PACKAGE_IMPLICITLY_OBSOLETES_RESOLVABLE;
825			needed = NEED_SOURCE | NEED_TARGET | NEED_DEPENDENCY;
826			break;
827		case SOLVER_RULE_RPM_PACKAGE_REQUIRES:
828			problemType = BSolverProblem::B_DEPENDENCY_NOT_INSTALLABLE;
829			needed = NEED_SOURCE | NEED_DEPENDENCY;
830			break;
831		case SOLVER_RULE_RPM_SELF_CONFLICT:
832			problemType = BSolverProblem::B_SELF_CONFLICT;
833			needed = NEED_SOURCE | NEED_DEPENDENCY;
834			break;
835		case SOLVER_RULE_UNKNOWN:
836		case SOLVER_RULE_FEATURE:
837		case SOLVER_RULE_LEARNT:
838		case SOLVER_RULE_CHOICE:
839		case SOLVER_RULE_BEST:
840			problemType = BSolverProblem::B_UNSPECIFIED;
841			break;
842	}
843
844	BSolverPackage* sourcePackage = NULL;
845	if ((needed & NEED_SOURCE) != 0) {
846		sourcePackage = _GetPackage(sourceId);
847		if (sourcePackage == NULL)
848			return B_ERROR;
849	}
850
851	BSolverPackage* targetPackage = NULL;
852	if ((needed & NEED_TARGET) != 0) {
853		targetPackage = _GetPackage(targetId);
854		if (targetPackage == NULL)
855			return B_ERROR;
856	}
857
858	BPackageResolvableExpression dependency;
859	if ((needed & NEED_DEPENDENCY) != 0) {
860		status_t error = _GetResolvableExpression(dependencyId, dependency);
861		if (error != B_OK)
862			return error;
863	}
864
865	Problem* problem = new(std::nothrow) Problem(problemId, problemType,
866		sourcePackage, targetPackage, dependency);
867	if (problem == NULL || !fProblems.AddItem(problem)) {
868		delete problem;
869		return B_NO_MEMORY;
870	}
871
872	int solutionCount = solver_solution_count(fSolver, problemId);
873	for (Id solutionId = 1; solutionId <= solutionCount; solutionId++) {
874		status_t error = _AddSolution(problem, solutionId);
875		if (error != B_OK)
876			return error;
877	}
878
879	return B_OK;
880}
881
882
883status_t
884LibsolvSolver::_AddSolution(Problem* problem, Id solutionId)
885{
886	Solution* solution = new(std::nothrow) Solution(solutionId, problem);
887	if (solution == NULL || !problem->AppendSolution(solution)) {
888		delete solution;
889		return B_NO_MEMORY;
890	}
891
892	Id elementId = 0;
893	for (;;) {
894		Id sourceId;
895		Id targetId;
896		elementId = solver_next_solutionelement(fSolver, problem->Id(),
897			solutionId, elementId, &sourceId, &targetId);
898		if (elementId == 0)
899			break;
900
901		status_t error = _AddSolutionElement(solution, sourceId, targetId);
902		if (error != B_OK)
903			return error;
904	}
905
906	return B_OK;
907}
908
909
910status_t
911LibsolvSolver::_AddSolutionElement(Solution* solution, Id sourceId, Id targetId)
912{
913	typedef BSolverProblemSolutionElement Element;
914
915	if (sourceId == SOLVER_SOLUTION_JOB
916		|| sourceId == SOLVER_SOLUTION_POOLJOB) {
917		// targetId is an index into the job queue
918		if (sourceId == SOLVER_SOLUTION_JOB)
919			targetId += fSolver->pooljobcnt;
920
921		Id how = fSolver->job.elements[targetId - 1];
922		Id what = fSolver->job.elements[targetId];
923		Id select = how & SOLVER_SELECTMASK;
924
925		switch (how & SOLVER_JOBMASK) {
926			case SOLVER_INSTALL:
927				if (select == SOLVER_SOLVABLE && fInstalledRepository != NULL
928					&& fPool->solvables[what].repo
929						== fInstalledRepository->SolvRepo()) {
930					return _AddSolutionElement(solution, Element::B_DONT_KEEP,
931						fPool->solvables + what, NULL, NULL);
932				}
933
934				return _AddSolutionElement(solution,
935					Element::B_DONT_INSTALL, NULL, NULL,
936					solver_select2str(fPool, select, what));
937
938			case SOLVER_ERASE:
939			{
940				if (select == SOLVER_SOLVABLE
941					&& (fInstalledRepository == NULL
942						|| fPool->solvables[what].repo
943							!= fInstalledRepository->SolvRepo())) {
944					return _AddSolutionElement(solution,
945						Element::B_DONT_FORBID_INSTALLATION,
946						fPool->solvables + what, NULL, NULL);
947				}
948
949				Element::BType type = select == SOLVER_SOLVABLE_PROVIDES
950					? Element::B_DONT_DEINSTALL_ALL : Element::B_DONT_DEINSTALL;
951				return _AddSolutionElement(solution, type, NULL, NULL,
952					solver_select2str(fPool, select, what));
953			}
954
955			case SOLVER_UPDATE:
956				return _AddSolutionElement(solution,
957					Element::B_DONT_INSTALL_MOST_RECENT, NULL, NULL,
958					solver_select2str(fPool, select, what));
959
960			case SOLVER_LOCK:
961				return _AddSolutionElement(solution, Element::B_DONT_LOCK, NULL,
962					NULL, solver_select2str(fPool, select, what));
963
964			default:
965				return _AddSolutionElement(solution, Element::B_UNSPECIFIED,
966					NULL, NULL, NULL);
967		}
968	}
969
970	Solvable* target = targetId != 0 ? fPool->solvables + targetId : NULL;
971	bool targetInstalled = target && fInstalledRepository
972		&& target->repo == fInstalledRepository->SolvRepo();
973
974	if (sourceId == SOLVER_SOLUTION_INFARCH) {
975		return _AddSolutionElement(solution,
976			targetInstalled
977				? Element::B_KEEP_INFERIOR_ARCHITECTURE
978				: Element::B_INSTALL_INFERIOR_ARCHITECTURE,
979			target, NULL, NULL);
980	}
981
982	if (sourceId == SOLVER_SOLUTION_DISTUPGRADE) {
983		return _AddSolutionElement(solution,
984			targetInstalled
985				? Element::B_KEEP_EXCLUDED : Element::B_INSTALL_EXCLUDED,
986			target, NULL, NULL);
987	}
988
989	if (sourceId == SOLVER_SOLUTION_BEST) {
990		return _AddSolutionElement(solution,
991			targetInstalled ? Element::B_KEEP_OLD : Element::B_INSTALL_OLD,
992			target, NULL, NULL);
993	}
994
995	// replace source with target
996	Solvable* source = fPool->solvables + sourceId;
997	if (target == NULL) {
998		return _AddSolutionElement(solution, Element::B_ALLOW_DEINSTALLATION,
999			source, NULL, NULL);
1000	}
1001
1002	int illegalMask = policy_is_illegal(fSolver, source, target, 0);
1003	if ((illegalMask & POLICY_ILLEGAL_DOWNGRADE) != 0) {
1004		status_t error = _AddSolutionElement(solution,
1005			Element::B_ALLOW_DOWNGRADE, source, target, NULL);
1006		if (error != B_OK)
1007			return error;
1008	}
1009
1010	if ((illegalMask & POLICY_ILLEGAL_NAMECHANGE) != 0) {
1011		status_t error = _AddSolutionElement(solution,
1012			Element::B_ALLOW_NAME_CHANGE, source, target, NULL);
1013		if (error != B_OK)
1014			return error;
1015	}
1016
1017	if ((illegalMask & POLICY_ILLEGAL_ARCHCHANGE) != 0) {
1018		status_t error = _AddSolutionElement(solution,
1019			Element::B_ALLOW_ARCHITECTURE_CHANGE, source, target, NULL);
1020		if (error != B_OK)
1021			return error;
1022	}
1023
1024	if ((illegalMask & POLICY_ILLEGAL_VENDORCHANGE) != 0) {
1025		status_t error = _AddSolutionElement(solution,
1026			Element::B_ALLOW_VENDOR_CHANGE, source, target, NULL);
1027		if (error != B_OK)
1028			return error;
1029	}
1030
1031	if (illegalMask == 0) {
1032		return _AddSolutionElement(solution, Element::B_ALLOW_REPLACEMENT,
1033			source, target, NULL);
1034	}
1035
1036	return B_OK;
1037}
1038
1039
1040status_t
1041LibsolvSolver::_AddSolutionElement(Solution* solution,
1042	BSolverProblemSolutionElement::BType type, Solvable* sourceSolvable,
1043	Solvable* targetSolvable, const char* selectionString)
1044{
1045	BSolverPackage* sourcePackage = NULL;
1046	if (sourceSolvable != NULL) {
1047		sourcePackage = _GetPackage(sourceSolvable);
1048		if (sourcePackage == NULL)
1049			return B_ERROR;
1050	}
1051
1052	BSolverPackage* targetPackage = NULL;
1053	if (targetSolvable != NULL) {
1054		targetPackage = _GetPackage(targetSolvable);
1055		if (targetPackage == NULL)
1056			return B_ERROR;
1057	}
1058
1059	BString selection;
1060	if (selectionString != NULL && selectionString[0] != '\0') {
1061		selection = selectionString;
1062		if (selection.IsEmpty())
1063			return B_NO_MEMORY;
1064	}
1065
1066	if (!solution->AppendElement(BSolverProblemSolutionElement(
1067			type, sourcePackage, targetPackage, selection))) {
1068		return B_NO_MEMORY;
1069	}
1070
1071	return B_OK;
1072}
1073
1074
1075status_t
1076LibsolvSolver::_GetResolvableExpression(Id id,
1077	BPackageResolvableExpression& _expression) const
1078{
1079	// Try to translate the libsolv ID to a resolvable expression. Generally
1080	// that doesn't work, since libsolv is more expressive, but all the stuff
1081	// we feed libsolv we should be able to translate back.
1082
1083	if (!ISRELDEP(id)) {
1084		// just a string
1085		_expression.SetTo(pool_id2str(fPool, id));
1086		return B_OK;
1087	}
1088
1089	// a composite -- analyze it
1090	Reldep* reldep = GETRELDEP(fPool, id);
1091
1092	// No support for more than one level, so both name and evr must be strings.
1093	if (ISRELDEP(reldep->name) || ISRELDEP(reldep->evr))
1094		return B_NOT_SUPPORTED;
1095
1096	const char* name = pool_id2str(fPool, reldep->name);
1097	const char* versionString = pool_id2str(fPool, reldep->evr);
1098	if (name == NULL || versionString == NULL)
1099		return B_NOT_SUPPORTED;
1100
1101	// get the operator -- we don't support all libsolv supports
1102	BPackageResolvableOperator op;
1103	switch (reldep->flags) {
1104		case 1:
1105			op = B_PACKAGE_RESOLVABLE_OP_GREATER;
1106			break;
1107		case 2:
1108			op = B_PACKAGE_RESOLVABLE_OP_EQUAL;
1109			break;
1110		case 3:
1111			op = B_PACKAGE_RESOLVABLE_OP_GREATER_EQUAL;
1112			break;
1113		case 4:
1114			op = B_PACKAGE_RESOLVABLE_OP_LESS;
1115			break;
1116		case 5:
1117			op = B_PACKAGE_RESOLVABLE_OP_NOT_EQUAL;
1118			break;
1119		case 6:
1120			op = B_PACKAGE_RESOLVABLE_OP_LESS_EQUAL;
1121			break;
1122		default:
1123			return B_NOT_SUPPORTED;
1124	}
1125
1126	// get the version (cut off the empty epoch)
1127	if (versionString[0] == ':')
1128		versionString++;
1129
1130	BPackageVersion version;
1131	status_t error = version.SetTo(versionString, true);
1132	if (error != B_OK)
1133		return error == B_BAD_DATA ? B_NOT_SUPPORTED : error;
1134
1135	_expression.SetTo(name, op, version);
1136	return B_OK;
1137}
1138
1139
1140status_t
1141LibsolvSolver::_Solve(bool solveAgain)
1142{
1143	if (fJobs == NULL)
1144		return B_BAD_VALUE;
1145
1146	if (solveAgain) {
1147		if (fSolver == NULL)
1148			return B_BAD_VALUE;
1149	} else {
1150		_CleanupSolver();
1151
1152		// create the solver and solve
1153		fSolver = solver_create(fPool);
1154		solver_set_flag(fSolver, SOLVER_FLAG_SPLITPROVIDES, 1);
1155		solver_set_flag(fSolver, SOLVER_FLAG_BEST_OBEY_POLICY, 1);
1156	}
1157
1158	int problemCount = solver_solve(fSolver, fJobs);
1159
1160	// get the problems (if any)
1161	fProblems.MakeEmpty();
1162
1163	for (Id problemId = 1; problemId <= problemCount; problemId++) {
1164		status_t error = _AddProblem(problemId);
1165		if (error != B_OK)
1166			return error;
1167	}
1168
1169	return B_OK;
1170}
1171
1172
1173void
1174LibsolvSolver::_SetJobsSolverMode(Queue& jobs, int solverMode)
1175{
1176	for (int i = 0; i < jobs.count; i += 2) {
1177		jobs.elements[i] |= solverMode;
1178//		if (cleandeps)
1179//			jobs.elements[i] |= SOLVER_CLEANDEPS;
1180//		if (forcebest)
1181//			jobs.elements[i] |= SOLVER_FORCEBEST;
1182	}
1183}
1184