2011-07-26 13:56:55 +00:00
|
|
|
/* solver.c - Alpine Package Keeper (APK)
|
2013-06-11 11:06:06 +00:00
|
|
|
* Up- and down-propagating, forwarding checking, deductive dependency solver.
|
2011-07-26 13:56:55 +00:00
|
|
|
*
|
2013-06-11 11:06:06 +00:00
|
|
|
* Copyright (C) 2008-2013 Timo Teräs <timo.teras@iki.fi>
|
2011-07-26 13:56:55 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License version 2 as published
|
|
|
|
* by the Free Software Foundation. See http://www.gnu.org/ for details.
|
|
|
|
*/
|
|
|
|
|
2012-02-21 09:01:21 +00:00
|
|
|
#include <stdint.h>
|
2012-09-20 12:12:15 +00:00
|
|
|
#include <unistd.h>
|
2011-07-26 13:56:55 +00:00
|
|
|
#include "apk_defines.h"
|
|
|
|
#include "apk_database.h"
|
|
|
|
#include "apk_package.h"
|
|
|
|
#include "apk_solver.h"
|
|
|
|
|
2011-09-09 13:31:11 +00:00
|
|
|
#include "apk_print.h"
|
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
//#define DEBUG_PRINT
|
|
|
|
|
|
|
|
#ifdef DEBUG_PRINT
|
2011-07-26 13:56:55 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#define dbg_printf(args...) fprintf(stderr, args)
|
|
|
|
#else
|
|
|
|
#define dbg_printf(args...)
|
|
|
|
#endif
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
#define ASSERT(cond, fmt...) if (!(cond)) { apk_error(fmt); *(char*)NULL = 0; }
|
2012-02-24 13:50:39 +00:00
|
|
|
|
2011-07-26 13:56:55 +00:00
|
|
|
struct apk_solver_state {
|
|
|
|
struct apk_database *db;
|
2013-06-11 11:06:06 +00:00
|
|
|
struct apk_changeset *changeset;
|
|
|
|
struct list_head dirty_head;
|
|
|
|
struct list_head unresolved_head;
|
|
|
|
unsigned int errors;
|
2013-06-12 05:45:29 +00:00
|
|
|
unsigned int solver_flags_inherit;
|
2013-06-12 10:24:07 +00:00
|
|
|
unsigned int pinning_inherit;
|
|
|
|
unsigned int default_repos;
|
|
|
|
unsigned prefer_pinning : 1;
|
2013-06-11 11:06:06 +00:00
|
|
|
};
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
static struct apk_provider provider_none = {
|
|
|
|
.pkg = NULL,
|
|
|
|
.version = &apk_null_blob
|
2011-07-26 13:56:55 +00:00
|
|
|
};
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
void apk_solver_set_name_flags(struct apk_name *name,
|
|
|
|
unsigned short solver_flags,
|
|
|
|
unsigned short solver_flags_inheritable)
|
2012-01-17 11:54:33 +00:00
|
|
|
{
|
2013-06-14 18:20:19 +00:00
|
|
|
struct apk_provider *p;
|
2013-06-12 05:45:29 +00:00
|
|
|
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(p, name->providers) {
|
|
|
|
struct apk_package *pkg = p->pkg;
|
2013-06-12 05:45:29 +00:00
|
|
|
pkg->ss.solver_flags |= solver_flags;
|
|
|
|
pkg->ss.solver_flags_inheritable |= solver_flags_inheritable;
|
|
|
|
}
|
2012-01-17 11:54:33 +00:00
|
|
|
}
|
|
|
|
|
2013-06-12 10:24:07 +00:00
|
|
|
static int get_tag(struct apk_database *db, unsigned int pinning_mask, unsigned int repos)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < db->num_repo_tags; i++) {
|
|
|
|
if (!(BIT(i) & pinning_mask))
|
|
|
|
continue;
|
|
|
|
if (db->repo_tags[i].allowed_repos & repos)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return APK_DEFAULT_REPOSITORY_TAG;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int get_pkg_repos(struct apk_database *db, struct apk_package *pkg)
|
|
|
|
{
|
|
|
|
return pkg->repos | (pkg->ipkg ? db->repo_tags[pkg->ipkg->repository_tag].allowed_repos : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void foreach_rinstall_if_pkg(
|
|
|
|
struct apk_solver_state *ss, struct apk_package *pkg,
|
|
|
|
void (*cb)(struct apk_solver_state *ss, struct apk_package *rinstall_if, struct apk_package *parent_pkg))
|
|
|
|
{
|
2013-06-14 18:20:19 +00:00
|
|
|
struct apk_name *name = pkg->name, *name0, **pname0;
|
|
|
|
struct apk_dependency *dep;
|
|
|
|
struct apk_provider *p0;
|
2013-06-12 10:24:07 +00:00
|
|
|
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(pname0, pkg->name->rinstall_if) {
|
|
|
|
name0 = *pname0;
|
2013-06-12 10:24:07 +00:00
|
|
|
dbg_printf(PKG_VER_FMT ": rinstall_if %s\n", PKG_VER_PRINTF(pkg), name0->name);
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(p0, name0->providers) {
|
|
|
|
foreach_array_item(dep, p0->pkg->install_if) {
|
|
|
|
if (dep->name == name && apk_dep_is_provided(dep, p0)) {
|
|
|
|
/* pkg depends (via install_if) on pkg0 */
|
|
|
|
cb(ss, p0->pkg, pkg);
|
2013-06-12 10:24:07 +00:00
|
|
|
break;
|
2013-06-14 18:20:19 +00:00
|
|
|
}
|
2013-06-12 10:24:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-12 14:00:43 +00:00
|
|
|
static void mark_error(struct apk_solver_state *ss, struct apk_package *pkg)
|
|
|
|
{
|
|
|
|
if (pkg == NULL || pkg->ss.error)
|
|
|
|
return;
|
|
|
|
pkg->ss.error = 1;
|
|
|
|
ss->errors++;
|
|
|
|
}
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
static void queue_dirty(struct apk_solver_state *ss, struct apk_name *name)
|
2012-02-21 09:01:21 +00:00
|
|
|
{
|
2013-06-11 11:06:06 +00:00
|
|
|
if (list_hashed(&name->ss.dirty_list) || name->ss.locked ||
|
|
|
|
(name->ss.requirers == 0 && !name->ss.reevaluate_iif))
|
|
|
|
return;
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
dbg_printf("queue_dirty: %s\n", name->name);
|
|
|
|
list_add_tail(&name->ss.dirty_list, &ss->dirty_head);
|
2011-08-05 08:53:26 +00:00
|
|
|
}
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
static void queue_unresolved(struct apk_solver_state *ss, struct apk_name *name)
|
2012-02-27 08:26:30 +00:00
|
|
|
{
|
2013-06-11 11:06:06 +00:00
|
|
|
int want;
|
2012-02-27 08:26:30 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
if (name->ss.locked)
|
|
|
|
return;
|
2012-10-05 14:26:53 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
want = (name->ss.requirers > 0) || (name->ss.has_iif);
|
|
|
|
dbg_printf("queue_unresolved: %s, want=%d\n", name->name, want);
|
|
|
|
if (want && !list_hashed(&name->ss.unresolved_list))
|
|
|
|
list_add(&name->ss.unresolved_list, &ss->unresolved_head);
|
|
|
|
else if (!want && list_hashed(&name->ss.unresolved_list))
|
|
|
|
list_del_init(&name->ss.unresolved_list);
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
|
|
|
|
2013-06-14 18:20:19 +00:00
|
|
|
static void reevaluate_reverse_deps(struct apk_solver_state *ss, struct apk_name *name)
|
2011-08-05 08:53:26 +00:00
|
|
|
{
|
2013-06-14 18:20:19 +00:00
|
|
|
struct apk_name **pname0, *name0;
|
|
|
|
|
|
|
|
foreach_array_item(pname0, name->rdepends) {
|
|
|
|
name0 = *pname0;
|
|
|
|
if (!name0->ss.seen)
|
|
|
|
continue;
|
|
|
|
name0->ss.reevaluate_deps = 1;
|
|
|
|
queue_dirty(ss, name0);
|
|
|
|
}
|
2011-08-17 13:55:35 +00:00
|
|
|
}
|
2011-08-05 08:53:26 +00:00
|
|
|
|
2013-06-14 18:20:19 +00:00
|
|
|
static void reevaluate_reverse_installif(struct apk_solver_state *ss, struct apk_name *name)
|
2011-08-17 13:55:35 +00:00
|
|
|
{
|
2013-06-14 18:20:19 +00:00
|
|
|
struct apk_name **pname0, *name0;
|
|
|
|
|
|
|
|
foreach_array_item(pname0, name->rinstall_if) {
|
|
|
|
name0 = *pname0;
|
|
|
|
if (!name0->ss.seen)
|
|
|
|
continue;
|
|
|
|
name0->ss.reevaluate_iif = 1;
|
|
|
|
queue_dirty(ss, name0);
|
|
|
|
}
|
2011-08-17 13:55:35 +00:00
|
|
|
}
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
static void disqualify_package(struct apk_solver_state *ss, struct apk_package *pkg, const char *reason)
|
2012-02-18 09:54:17 +00:00
|
|
|
{
|
2013-06-14 18:20:19 +00:00
|
|
|
struct apk_dependency *p;
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
dbg_printf("disqualify_package: " PKG_VER_FMT " (%s)\n", PKG_VER_PRINTF(pkg), reason);
|
2013-06-20 11:08:16 +00:00
|
|
|
pkg->ss.pkg_selectable = 0;
|
2013-06-14 18:20:19 +00:00
|
|
|
reevaluate_reverse_deps(ss, pkg->name);
|
|
|
|
foreach_array_item(p, pkg->provides)
|
|
|
|
reevaluate_reverse_deps(ss, p->name);
|
|
|
|
reevaluate_reverse_installif(ss, pkg->name);
|
2012-02-18 09:54:17 +00:00
|
|
|
}
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
static int dependency_satisfiable(struct apk_solver_state *ss, struct apk_dependency *dep)
|
2012-02-18 09:54:17 +00:00
|
|
|
{
|
2013-06-11 11:06:06 +00:00
|
|
|
struct apk_name *name = dep->name;
|
2013-06-14 18:20:19 +00:00
|
|
|
struct apk_provider *p;
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
if (name->ss.locked)
|
|
|
|
return apk_dep_is_provided(dep, &name->ss.chosen);
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
if (name->ss.requirers == 0 && apk_dep_is_provided(dep, &provider_none))
|
|
|
|
return TRUE;
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(p, name->providers)
|
2013-06-20 11:08:16 +00:00
|
|
|
if (p->pkg->ss.pkg_selectable && apk_dep_is_provided(dep, p))
|
2013-06-11 11:06:06 +00:00
|
|
|
return TRUE;
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
return FALSE;
|
2012-02-18 09:54:17 +00:00
|
|
|
}
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
static void discover_name(struct apk_solver_state *ss, struct apk_name *name)
|
2012-02-18 09:54:17 +00:00
|
|
|
{
|
2013-06-11 11:06:06 +00:00
|
|
|
struct apk_database *db = ss->db;
|
2013-06-14 18:20:19 +00:00
|
|
|
struct apk_name **pname0;
|
|
|
|
struct apk_provider *p;
|
|
|
|
struct apk_dependency *dep;
|
2013-06-12 10:24:07 +00:00
|
|
|
unsigned int repos;
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
if (name->ss.seen)
|
|
|
|
return;
|
2013-06-12 10:24:07 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
name->ss.seen = 1;
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(p, name->providers) {
|
|
|
|
struct apk_package *pkg = p->pkg;
|
2013-06-11 11:06:06 +00:00
|
|
|
if (pkg->ss.seen)
|
2012-02-28 08:34:35 +00:00
|
|
|
continue;
|
2013-06-20 11:08:16 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
pkg->ss.seen = 1;
|
2013-06-12 10:24:07 +00:00
|
|
|
pkg->ss.pinning_allowed = APK_DEFAULT_PINNING_MASK;
|
|
|
|
pkg->ss.pinning_preferred = APK_DEFAULT_PINNING_MASK;
|
2013-06-20 11:08:16 +00:00
|
|
|
pkg->ss.pkg_available =
|
|
|
|
(pkg->filename != NULL) ||
|
|
|
|
(pkg->installed_size == 0) ||
|
|
|
|
(pkg->repos & db->available_repos);
|
|
|
|
pkg->ss.pkg_selectable = pkg->ss.pkg_available || pkg->ipkg;
|
2013-06-12 10:24:07 +00:00
|
|
|
|
|
|
|
repos = get_pkg_repos(db, pkg);
|
2013-06-20 11:08:16 +00:00
|
|
|
pkg->ss.tag_preferred =
|
|
|
|
(pkg->filename != NULL) ||
|
|
|
|
(pkg->installed_size == 0) ||
|
|
|
|
!!(repos & ss->default_repos);
|
|
|
|
pkg->ss.tag_ok = pkg->ss.tag_preferred || pkg->ipkg;
|
2013-06-12 10:24:07 +00:00
|
|
|
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(dep, pkg->depends) {
|
|
|
|
discover_name(ss, dep->name);
|
2013-06-12 10:24:07 +00:00
|
|
|
pkg->ss.max_dep_chain = max(pkg->ss.max_dep_chain,
|
2013-06-14 18:20:19 +00:00
|
|
|
dep->name->ss.max_dep_chain+1);
|
|
|
|
}
|
2013-06-11 11:06:06 +00:00
|
|
|
name->ss.max_dep_chain = max(name->ss.max_dep_chain, pkg->ss.max_dep_chain);
|
2013-06-12 10:24:07 +00:00
|
|
|
|
2013-06-20 11:08:16 +00:00
|
|
|
dbg_printf("discover " PKG_VER_FMT ": tag_ok=%d, tag_pref=%d max_dep_chain=%d selectable=%d\n",
|
2013-06-12 10:24:07 +00:00
|
|
|
PKG_VER_PRINTF(pkg),
|
|
|
|
pkg->ss.tag_ok,
|
|
|
|
pkg->ss.tag_preferred,
|
2013-06-18 13:24:48 +00:00
|
|
|
pkg->ss.max_dep_chain,
|
2013-06-20 11:08:16 +00:00
|
|
|
pkg->ss.pkg_selectable);
|
2012-02-28 08:34:35 +00:00
|
|
|
}
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(pname0, name->rinstall_if)
|
|
|
|
discover_name(ss, *pname0);
|
2012-02-18 09:54:17 +00:00
|
|
|
}
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
static void name_requirers_changed(struct apk_solver_state *ss, struct apk_name *name)
|
2012-02-22 06:45:40 +00:00
|
|
|
{
|
2013-06-11 11:06:06 +00:00
|
|
|
queue_unresolved(ss, name);
|
2013-06-14 18:20:19 +00:00
|
|
|
reevaluate_reverse_installif(ss, name);
|
2013-06-11 11:06:06 +00:00
|
|
|
queue_dirty(ss, name);
|
2012-02-22 06:45:40 +00:00
|
|
|
}
|
|
|
|
|
2013-06-12 10:24:07 +00:00
|
|
|
static void inherit_pinning(struct apk_solver_state *ss, struct apk_package *pkg, unsigned int pinning, int prefer)
|
|
|
|
{
|
|
|
|
unsigned int repo_mask = apk_db_get_pinning_mask_repos(ss->db, pinning);
|
|
|
|
unsigned int repos = get_pkg_repos(ss->db, pkg);
|
|
|
|
|
|
|
|
pkg->ss.pinning_allowed |= pinning;
|
|
|
|
pkg->ss.tag_ok |= !!(repos & repo_mask);
|
|
|
|
if (prefer) {
|
|
|
|
pkg->ss.pinning_preferred |= pinning;
|
|
|
|
pkg->ss.tag_preferred = !!(repos & apk_db_get_pinning_mask_repos(ss->db, pkg->ss.pinning_preferred));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-12 14:00:43 +00:00
|
|
|
static void apply_constraint(struct apk_solver_state *ss, struct apk_package *ppkg, struct apk_dependency *dep)
|
2011-08-17 13:55:35 +00:00
|
|
|
{
|
2013-06-11 11:06:06 +00:00
|
|
|
struct apk_name *name = dep->name;
|
2013-06-14 18:20:19 +00:00
|
|
|
struct apk_provider *p0;
|
2013-06-12 10:24:07 +00:00
|
|
|
unsigned int solver_flags_inherit = ss->solver_flags_inherit;
|
2013-06-14 18:20:19 +00:00
|
|
|
int is_provided;
|
2011-08-17 13:55:35 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
dbg_printf("apply_constraint: %s%s%s" BLOB_FMT "\n",
|
|
|
|
dep->conflict ? "!" : "",
|
|
|
|
name->name,
|
|
|
|
apk_version_op_string(dep->result_mask),
|
|
|
|
BLOB_PRINTF(*dep->version));
|
|
|
|
|
|
|
|
name->ss.requirers += !dep->conflict;
|
2013-06-18 13:24:48 +00:00
|
|
|
if (name->ss.requirers == 1 && !dep->conflict)
|
|
|
|
name_requirers_changed(ss, name);
|
2011-08-17 13:55:35 +00:00
|
|
|
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(p0, name->providers) {
|
2013-06-11 11:06:06 +00:00
|
|
|
struct apk_package *pkg0 = p0->pkg;
|
2012-02-27 08:26:30 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
is_provided = apk_dep_is_provided(dep, p0);
|
|
|
|
dbg_printf("apply_constraint: provider: %s-" BLOB_FMT ": %d\n",
|
|
|
|
pkg0->name->name, BLOB_PRINTF(*p0->version), is_provided);
|
2011-08-17 13:55:35 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
pkg0->ss.conflicts += !is_provided;
|
2013-06-20 11:08:16 +00:00
|
|
|
if (unlikely(pkg0->ss.pkg_selectable && pkg0->ss.conflicts))
|
2013-06-11 11:06:06 +00:00
|
|
|
disqualify_package(ss, pkg0, "conflicting dependency");
|
2013-06-12 10:24:07 +00:00
|
|
|
|
2013-06-12 05:45:29 +00:00
|
|
|
if (is_provided) {
|
|
|
|
pkg0->ss.solver_flags |= solver_flags_inherit;
|
|
|
|
pkg0->ss.solver_flags_inheritable |= solver_flags_inherit;
|
2013-06-12 10:24:07 +00:00
|
|
|
inherit_pinning(ss, pkg0, ss->pinning_inherit, ss->prefer_pinning);
|
|
|
|
|
|
|
|
dbg_printf(PKG_VER_FMT ": tag_ok=%d, tag_pref=%d\n",
|
|
|
|
PKG_VER_PRINTF(pkg0),
|
|
|
|
pkg0->ss.tag_ok,
|
|
|
|
pkg0->ss.tag_preferred);
|
2013-06-12 05:45:29 +00:00
|
|
|
}
|
2012-02-29 09:26:12 +00:00
|
|
|
}
|
2011-08-17 13:55:35 +00:00
|
|
|
}
|
|
|
|
|
2013-06-19 10:13:51 +00:00
|
|
|
static void exclude_non_providers(struct apk_solver_state *ss, struct apk_name *name, struct apk_name *must_provide)
|
|
|
|
{
|
|
|
|
struct apk_provider *p;
|
|
|
|
struct apk_dependency *d;
|
|
|
|
|
2013-06-20 11:08:16 +00:00
|
|
|
if (name == must_provide)
|
|
|
|
return;
|
|
|
|
|
2013-06-19 10:13:51 +00:00
|
|
|
dbg_printf("%s must provide %s\n", name->name, must_provide->name);
|
|
|
|
|
|
|
|
foreach_array_item(p, name->providers) {
|
|
|
|
if (p->pkg->name == must_provide)
|
|
|
|
goto next;
|
|
|
|
foreach_array_item(d, p->pkg->provides)
|
|
|
|
if (d->name == must_provide)
|
|
|
|
goto next;
|
|
|
|
disqualify_package(ss, p->pkg, "provides transitivity");
|
|
|
|
next: ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void merge_index(unsigned short *index, int num_options)
|
|
|
|
{
|
|
|
|
if (*index == num_options)
|
|
|
|
*index = num_options + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int merge_index_complete(unsigned short *index, int num_options)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = (*index == num_options);
|
|
|
|
*index = 0;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
static void reconsider_name(struct apk_solver_state *ss, struct apk_name *name)
|
2011-08-17 13:55:35 +00:00
|
|
|
{
|
2013-06-18 13:24:48 +00:00
|
|
|
struct apk_name *name0, **pname0;
|
2013-06-11 11:06:06 +00:00
|
|
|
struct apk_dependency *dep;
|
2013-06-19 10:13:51 +00:00
|
|
|
struct apk_package *first_candidate = NULL, *pkg;
|
2013-06-14 18:20:19 +00:00
|
|
|
struct apk_provider *p;
|
|
|
|
int reevaluate_deps, reevaluate_iif;
|
2013-06-12 10:24:07 +00:00
|
|
|
int num_options = 0, num_tag_not_ok = 0, has_iif = 0;
|
2011-08-17 13:55:35 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
dbg_printf("reconsider_name: %s\n", name->name);
|
2011-08-17 13:55:35 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
reevaluate_deps = name->ss.reevaluate_deps;
|
|
|
|
reevaluate_iif = name->ss.reevaluate_iif;
|
2013-06-14 18:20:19 +00:00
|
|
|
name->ss.reevaluate_deps = 0;
|
2013-06-11 11:06:06 +00:00
|
|
|
name->ss.reevaluate_iif = 0;
|
2011-08-17 13:55:35 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
/* propagate down by merging common dependencies and
|
|
|
|
* applying new constraints */
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(p, name->providers) {
|
2013-06-11 11:06:06 +00:00
|
|
|
/* check if this pkg's dependencies have become unsatisfiable */
|
2013-06-19 10:13:51 +00:00
|
|
|
pkg = p->pkg;
|
2013-06-12 12:25:16 +00:00
|
|
|
pkg->ss.dependencies_merged = 0;
|
2013-06-11 11:06:06 +00:00
|
|
|
if (reevaluate_deps) {
|
2013-06-20 11:08:16 +00:00
|
|
|
if (!pkg->ss.pkg_selectable)
|
2013-06-11 11:06:06 +00:00
|
|
|
continue;
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(dep, pkg->depends) {
|
2013-06-11 11:06:06 +00:00
|
|
|
if (!dependency_satisfiable(ss, dep)) {
|
|
|
|
disqualify_package(ss, pkg, "dependency no longer satisfiable");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-06-20 11:08:16 +00:00
|
|
|
if (!pkg->ss.pkg_selectable)
|
2013-06-11 11:06:06 +00:00
|
|
|
continue;
|
2012-02-28 09:27:56 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
if (reevaluate_iif) {
|
2013-06-14 18:20:19 +00:00
|
|
|
pkg->ss.iif_triggered = 1;
|
|
|
|
foreach_array_item(dep, pkg->install_if) {
|
|
|
|
if (!dependency_satisfiable(ss, dep)) {
|
|
|
|
pkg->ss.iif_triggered = 0;
|
2013-06-11 11:06:06 +00:00
|
|
|
break;
|
2013-06-14 18:20:19 +00:00
|
|
|
}
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
|
|
|
has_iif |= pkg->ss.iif_triggered;
|
|
|
|
}
|
2011-09-16 14:10:50 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
if (name->ss.requirers == 0 && !pkg->ss.iif_triggered)
|
|
|
|
continue;
|
2012-02-27 08:26:30 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
/* merge common dependencies */
|
2013-06-12 12:25:16 +00:00
|
|
|
pkg->ss.dependencies_merged = 1;
|
2013-06-14 18:20:19 +00:00
|
|
|
if (first_candidate == NULL)
|
|
|
|
first_candidate = pkg;
|
2013-06-19 10:13:51 +00:00
|
|
|
|
|
|
|
/* FIXME: can merge also conflicts */
|
|
|
|
foreach_array_item(dep, pkg->depends)
|
|
|
|
if (!dep->conflict)
|
|
|
|
merge_index(&dep->name->ss.merge_depends, num_options);
|
|
|
|
|
|
|
|
merge_index(&pkg->name->ss.merge_provides, num_options);
|
|
|
|
foreach_array_item(dep, pkg->provides)
|
|
|
|
if (dep->version != &apk_null_blob)
|
|
|
|
merge_index(&dep->name->ss.merge_provides, num_options);
|
2013-06-14 18:20:19 +00:00
|
|
|
|
|
|
|
num_tag_not_ok += !pkg->ss.tag_ok;
|
|
|
|
num_options++;
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
2013-06-12 10:24:07 +00:00
|
|
|
name->ss.has_options = (num_options > 1 || num_tag_not_ok > 0);
|
2013-06-11 11:06:06 +00:00
|
|
|
name->ss.has_iif = has_iif;
|
|
|
|
queue_unresolved(ss, name);
|
|
|
|
|
2013-06-14 18:20:19 +00:00
|
|
|
if (first_candidate != NULL) {
|
2013-06-19 10:13:51 +00:00
|
|
|
pkg = first_candidate;
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(p, name->providers)
|
|
|
|
p->pkg->ss.dependencies_used = p->pkg->ss.dependencies_merged;
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
/* propagate down common dependencies */
|
2013-06-18 13:24:48 +00:00
|
|
|
if (num_options == 1) {
|
|
|
|
/* FIXME: keeps increasing counts, use bit fields instead? */
|
2013-06-19 10:13:51 +00:00
|
|
|
foreach_array_item(dep, pkg->depends)
|
|
|
|
if (merge_index_complete(&dep->name->ss.merge_depends, num_options))
|
|
|
|
apply_constraint(ss, pkg, dep);
|
2013-06-18 13:24:48 +00:00
|
|
|
} else {
|
|
|
|
/* FIXME: could merge versioning bits too */
|
2013-06-19 10:13:51 +00:00
|
|
|
foreach_array_item(dep, pkg->depends) {
|
2013-06-18 13:24:48 +00:00
|
|
|
name0 = dep->name;
|
2013-06-19 10:13:51 +00:00
|
|
|
if (merge_index_complete(&name0->ss.merge_depends, num_options) &&
|
|
|
|
name0->ss.requirers == 0) {
|
2013-06-18 13:24:48 +00:00
|
|
|
/* common dependency name with all */
|
2013-06-19 10:13:51 +00:00
|
|
|
dbg_printf("%s common dependency: %s\n",
|
|
|
|
name->name, name0->name);
|
|
|
|
name0->ss.requirers++;
|
|
|
|
name_requirers_changed(ss, name0);
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
|
|
|
}
|
2011-09-16 14:10:50 +00:00
|
|
|
}
|
2013-06-19 10:13:51 +00:00
|
|
|
|
|
|
|
/* provides transitivity */
|
|
|
|
if (merge_index_complete(&pkg->name->ss.merge_provides, num_options))
|
|
|
|
exclude_non_providers(ss, pkg->name, name);
|
|
|
|
foreach_array_item(dep, pkg->provides)
|
|
|
|
if (merge_index_complete(&dep->name->ss.merge_provides, num_options))
|
|
|
|
exclude_non_providers(ss, dep->name, name);
|
2011-09-16 14:10:50 +00:00
|
|
|
}
|
2013-06-19 10:13:51 +00:00
|
|
|
|
2013-06-18 13:24:48 +00:00
|
|
|
name->ss.reverse_deps_done = 1;
|
|
|
|
foreach_array_item(pname0, name->rdepends) {
|
|
|
|
name0 = *pname0;
|
|
|
|
if (name0->ss.seen && !name0->ss.locked) {
|
|
|
|
name->ss.reverse_deps_done = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dbg_printf("reconsider_name: %s [finished], has_options=%d, reverse_deps_done=%d\n",
|
|
|
|
name->name, name->ss.has_options, name->ss.reverse_deps_done);
|
2011-07-27 19:10:44 +00:00
|
|
|
}
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
static int compare_providers(struct apk_solver_state *ss,
|
|
|
|
struct apk_provider *pA, struct apk_provider *pB)
|
2011-07-26 13:56:55 +00:00
|
|
|
{
|
2013-06-11 11:06:06 +00:00
|
|
|
struct apk_database *db = ss->db;
|
|
|
|
struct apk_package *pkgA = pA->pkg, *pkgB = pB->pkg;
|
2013-06-12 05:45:29 +00:00
|
|
|
unsigned int solver_flags;
|
2013-06-11 11:06:06 +00:00
|
|
|
int r;
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
/* Prefer existing package */
|
|
|
|
if (pkgA == NULL || pkgB == NULL)
|
|
|
|
return (pkgA != NULL) - (pkgB != NULL);
|
2011-08-17 13:55:35 +00:00
|
|
|
|
2013-06-18 13:24:48 +00:00
|
|
|
/* Latest version required? */
|
|
|
|
solver_flags = pkgA->ss.solver_flags | pkgB->ss.solver_flags;
|
|
|
|
if ((solver_flags & APK_SOLVERF_LATEST) &&
|
|
|
|
(pkgA->ss.pinning_allowed == APK_DEFAULT_PINNING_MASK) &&
|
|
|
|
(pkgB->ss.pinning_allowed == APK_DEFAULT_PINNING_MASK)) {
|
|
|
|
/* Prefer allowed pinning */
|
|
|
|
r = (int)pkgA->ss.tag_ok - (int)pkgB->ss.tag_ok;
|
|
|
|
if (r)
|
|
|
|
return r;
|
2011-08-17 13:55:35 +00:00
|
|
|
|
2013-06-18 13:24:48 +00:00
|
|
|
/* Prefer available */
|
|
|
|
if (solver_flags & (APK_SOLVERF_AVAILABLE | APK_SOLVERF_REINSTALL)) {
|
2013-06-20 11:08:16 +00:00
|
|
|
r = (int)pkgA->ss.pkg_available - (int)pkgB->ss.pkg_available;
|
2013-06-18 13:24:48 +00:00
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Prefer without errors */
|
2013-06-20 11:08:16 +00:00
|
|
|
r = (int)pkgA->ss.pkg_selectable - (int)pkgB->ss.pkg_selectable;
|
2013-06-18 13:24:48 +00:00
|
|
|
if (r)
|
|
|
|
return r;
|
2013-06-12 12:25:16 +00:00
|
|
|
|
2013-06-18 13:24:48 +00:00
|
|
|
/* Prefer those that were in last dependency merging group */
|
|
|
|
r = (int)pkgA->ss.dependencies_used - (int)pkgB->ss.dependencies_used;
|
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
r = pkgB->ss.conflicts - pkgA->ss.conflicts;
|
2013-06-12 12:25:16 +00:00
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
2013-06-18 13:24:48 +00:00
|
|
|
/* Prefer installed on self-upgrade */
|
|
|
|
if (db->performing_self_update && !(solver_flags & APK_SOLVERF_UPGRADE)) {
|
|
|
|
r = (pkgA->ipkg != NULL) - (pkgB->ipkg != NULL);
|
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
}
|
2013-06-12 10:24:07 +00:00
|
|
|
|
2013-06-18 13:24:48 +00:00
|
|
|
/* Prefer allowed pinning */
|
|
|
|
r = (int)pkgA->ss.tag_ok - (int)pkgB->ss.tag_ok;
|
2013-06-11 11:06:06 +00:00
|
|
|
if (r)
|
|
|
|
return r;
|
2011-08-17 13:55:35 +00:00
|
|
|
|
2013-06-18 13:24:48 +00:00
|
|
|
/* Prefer available */
|
|
|
|
if (solver_flags & (APK_SOLVERF_AVAILABLE | APK_SOLVERF_REINSTALL)) {
|
2013-06-20 11:08:16 +00:00
|
|
|
r = (int)pkgA->ss.pkg_available - (int)pkgB->ss.pkg_available;
|
2013-06-18 13:24:48 +00:00
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
}
|
2013-06-12 10:24:07 +00:00
|
|
|
|
2013-06-18 13:24:48 +00:00
|
|
|
/* Prefer preferred pinning */
|
|
|
|
r = (int)pkgA->ss.tag_preferred - (int)pkgB->ss.tag_preferred;
|
2013-06-11 11:06:06 +00:00
|
|
|
if (r)
|
|
|
|
return r;
|
2013-06-18 13:24:48 +00:00
|
|
|
|
|
|
|
/* Prefer installed */
|
|
|
|
if (!(solver_flags & APK_SOLVERF_UPGRADE)) {
|
|
|
|
r = (pkgA->ipkg != NULL) - (pkgB->ipkg != NULL);
|
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
}
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
2011-08-17 13:55:35 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
/* Select latest by requested name */
|
|
|
|
switch (apk_version_compare_blob(*pA->version, *pB->version)) {
|
|
|
|
case APK_VERSION_LESS:
|
|
|
|
return -1;
|
|
|
|
case APK_VERSION_GREATER:
|
|
|
|
return 1;
|
|
|
|
}
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
/* Select latest by principal name */
|
|
|
|
if (pkgA->name == pkgB->name) {
|
|
|
|
switch (apk_version_compare_blob(*pkgA->version, *pkgB->version)) {
|
|
|
|
case APK_VERSION_LESS:
|
|
|
|
return -1;
|
|
|
|
case APK_VERSION_GREATER:
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2012-02-16 18:37:03 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
/* Prefer installed (matches here if upgrading) */
|
|
|
|
r = (pkgA->ipkg != NULL) - (pkgB->ipkg != NULL);
|
|
|
|
if (r)
|
|
|
|
return r;
|
2012-01-17 11:54:33 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
/* Prefer lowest available repository */
|
|
|
|
return ffsl(pkgB->repos) - ffsl(pkgA->repos);
|
2012-02-22 10:59:46 +00:00
|
|
|
}
|
2012-02-20 18:54:03 +00:00
|
|
|
|
2013-06-12 10:24:07 +00:00
|
|
|
static void inherit_pinning_from_pkg(struct apk_solver_state *ss, struct apk_package *rinstall_if, struct apk_package *parent_pkg)
|
|
|
|
{
|
|
|
|
inherit_pinning(ss, rinstall_if, parent_pkg->ss.pinning_allowed, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void assign_name(struct apk_solver_state *ss, struct apk_name *name, struct apk_provider p)
|
2012-02-22 10:59:46 +00:00
|
|
|
{
|
2013-06-14 18:20:19 +00:00
|
|
|
struct apk_provider *p0;
|
2013-06-11 11:06:06 +00:00
|
|
|
|
|
|
|
if (name->ss.locked) {
|
|
|
|
/* If both are providing this name without version, it's ok */
|
|
|
|
if (p.version == &apk_null_blob &&
|
|
|
|
name->ss.chosen.version == &apk_null_blob)
|
|
|
|
return;
|
2013-06-12 14:00:43 +00:00
|
|
|
/* Conflict: providing same name */
|
|
|
|
mark_error(ss, p.pkg);
|
|
|
|
mark_error(ss, name->ss.chosen.pkg);
|
2012-02-22 10:59:46 +00:00
|
|
|
return;
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2013-06-12 10:24:07 +00:00
|
|
|
if (p.pkg)
|
|
|
|
dbg_printf("assign %s to "PKG_VER_FMT"\n", name->name, PKG_VER_PRINTF(p.pkg));
|
2011-07-30 17:59:47 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
name->ss.locked = 1;
|
|
|
|
name->ss.chosen = p;
|
|
|
|
if (list_hashed(&name->ss.unresolved_list))
|
|
|
|
list_del(&name->ss.unresolved_list);
|
|
|
|
if (list_hashed(&name->ss.dirty_list))
|
|
|
|
list_del(&name->ss.dirty_list);
|
|
|
|
|
2013-06-12 10:24:07 +00:00
|
|
|
/* propagate pinning to install_if candidates */
|
|
|
|
if (p.pkg)
|
|
|
|
foreach_rinstall_if_pkg(ss, p.pkg, inherit_pinning_from_pkg);
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
/* disqualify all conflicting packages */
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(p0, name->providers) {
|
|
|
|
if (p0->pkg == p.pkg)
|
2013-06-11 11:06:06 +00:00
|
|
|
continue;
|
|
|
|
if (p.version == &apk_null_blob &&
|
2013-06-14 18:20:19 +00:00
|
|
|
p0->version == &apk_null_blob)
|
2013-06-11 11:06:06 +00:00
|
|
|
continue;
|
2013-06-14 18:20:19 +00:00
|
|
|
disqualify_package(ss, p0->pkg, "conflicting provides");
|
2012-02-18 09:54:17 +00:00
|
|
|
}
|
2013-06-14 18:20:19 +00:00
|
|
|
reevaluate_reverse_deps(ss, name);
|
2012-02-20 18:54:03 +00:00
|
|
|
}
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
static void select_package(struct apk_solver_state *ss, struct apk_name *name)
|
2012-02-20 18:54:03 +00:00
|
|
|
{
|
2013-06-14 18:20:19 +00:00
|
|
|
struct apk_provider chosen = { NULL, &apk_null_blob }, *p;
|
2013-06-11 11:06:06 +00:00
|
|
|
struct apk_package *pkg = NULL;
|
2013-06-14 18:20:19 +00:00
|
|
|
struct apk_dependency *d;
|
2012-02-20 18:54:03 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
dbg_printf("select_package: %s\n", name->name);
|
2012-02-20 18:54:03 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
if (name->ss.requirers || name->ss.has_iif) {
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(p, name->providers) {
|
2013-06-18 05:03:40 +00:00
|
|
|
/* Ensure valid pinning and install-if trigger */
|
2013-06-12 10:24:07 +00:00
|
|
|
if (name->ss.requirers == 0 &&
|
2013-06-14 18:20:19 +00:00
|
|
|
(!p->pkg->ss.iif_triggered ||
|
|
|
|
!p->pkg->ss.tag_ok))
|
2012-02-20 18:54:03 +00:00
|
|
|
continue;
|
2013-06-18 05:03:40 +00:00
|
|
|
/* Virtual packages cannot be autoselected */
|
|
|
|
if (p->version == &apk_null_blob && p->pkg->name->ss.requirers == 0)
|
|
|
|
continue;
|
2013-06-14 18:20:19 +00:00
|
|
|
if (compare_providers(ss, p, &chosen) > 0)
|
|
|
|
chosen = *p;
|
2012-02-20 18:54:03 +00:00
|
|
|
}
|
2011-07-30 17:59:47 +00:00
|
|
|
}
|
2012-02-27 08:26:30 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
pkg = chosen.pkg;
|
|
|
|
if (pkg) {
|
2013-06-20 11:08:16 +00:00
|
|
|
if (!pkg->ss.pkg_selectable || !pkg->ss.tag_ok) {
|
2013-06-12 14:00:43 +00:00
|
|
|
/* Selecting broken or unallowed package */
|
|
|
|
mark_error(ss, pkg);
|
|
|
|
}
|
2013-06-20 11:08:16 +00:00
|
|
|
dbg_printf("selecting: " PKG_VER_FMT ", available: %d\n", PKG_VER_PRINTF(pkg), pkg->ss.pkg_selectable);
|
2013-06-14 18:20:19 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
assign_name(ss, pkg->name, APK_PROVIDER_FROM_PACKAGE(pkg));
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(d, pkg->provides)
|
|
|
|
assign_name(ss, d->name, APK_PROVIDER_FROM_PROVIDES(pkg, d));
|
|
|
|
|
2013-06-12 05:45:29 +00:00
|
|
|
ss->solver_flags_inherit = pkg->ss.solver_flags_inheritable;
|
2013-06-12 10:24:07 +00:00
|
|
|
ss->pinning_inherit = pkg->ss.pinning_allowed;
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(d, pkg->depends)
|
|
|
|
apply_constraint(ss, pkg, d);
|
2013-06-12 05:45:29 +00:00
|
|
|
ss->solver_flags_inherit = 0;
|
2013-06-12 10:24:07 +00:00
|
|
|
ss->pinning_inherit = 0;
|
2013-06-11 11:06:06 +00:00
|
|
|
} else {
|
|
|
|
dbg_printf("selecting: %s [unassigned]\n", name->name);
|
|
|
|
assign_name(ss, name, provider_none);
|
2013-06-12 14:00:43 +00:00
|
|
|
ss->errors += (name->ss.requirers > 0);
|
2012-02-20 18:54:03 +00:00
|
|
|
}
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
|
|
|
|
2013-06-19 17:46:53 +00:00
|
|
|
static void record_change(struct apk_solver_state *ss, struct apk_package *opkg, struct apk_package *npkg)
|
2011-08-17 13:55:35 +00:00
|
|
|
{
|
2013-06-11 11:06:06 +00:00
|
|
|
struct apk_changeset *changeset = ss->changeset;
|
|
|
|
struct apk_change *change;
|
|
|
|
|
2013-06-19 17:46:53 +00:00
|
|
|
change = apk_change_array_add(&changeset->changes);
|
2013-06-11 11:06:06 +00:00
|
|
|
*change = (struct apk_change) {
|
|
|
|
.old_pkg = opkg,
|
|
|
|
.old_repository_tag = opkg ? opkg->ipkg->repository_tag : 0,
|
2013-06-19 17:46:53 +00:00
|
|
|
.new_pkg = npkg,
|
|
|
|
.new_repository_tag = npkg ? get_tag(ss->db, npkg->ss.pinning_allowed, get_pkg_repos(ss->db, npkg)) : 0,
|
|
|
|
.reinstall = npkg ? !!(npkg->ss.solver_flags & APK_SOLVERF_REINSTALL) : 0,
|
2013-06-11 11:06:06 +00:00
|
|
|
};
|
2013-06-19 17:46:53 +00:00
|
|
|
if (npkg == NULL)
|
2013-06-11 11:06:06 +00:00
|
|
|
changeset->num_remove++;
|
2013-06-19 17:46:53 +00:00
|
|
|
else if (opkg == NULL)
|
2013-06-11 11:06:06 +00:00
|
|
|
changeset->num_install++;
|
2013-06-19 17:46:53 +00:00
|
|
|
else if (npkg != opkg || change->reinstall || change->new_repository_tag != change->old_repository_tag)
|
2013-06-11 11:06:06 +00:00
|
|
|
changeset->num_adjust++;
|
2013-06-19 17:46:53 +00:00
|
|
|
}
|
2012-04-27 05:47:20 +00:00
|
|
|
|
2013-06-19 17:46:53 +00:00
|
|
|
static void cset_gen_name_change(struct apk_solver_state *ss, struct apk_name *name);
|
|
|
|
static void cset_gen_name_remove(struct apk_solver_state *ss, struct apk_package *pkg);
|
|
|
|
static void cset_gen_dep(struct apk_solver_state *ss, struct apk_package *ppkg, struct apk_dependency *dep);
|
|
|
|
|
2013-06-19 18:39:01 +00:00
|
|
|
static void cset_track_deps_added(struct apk_package *pkg)
|
2013-06-19 17:46:53 +00:00
|
|
|
{
|
|
|
|
struct apk_dependency *d;
|
|
|
|
|
2013-06-19 18:39:01 +00:00
|
|
|
foreach_array_item(d, pkg->depends) {
|
|
|
|
if (d->conflict || !d->name->ss.installed_name)
|
|
|
|
continue;
|
|
|
|
d->name->ss.installed_name->ss.requirers++;
|
|
|
|
}
|
2013-06-19 17:46:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void cset_track_deps_removed(struct apk_solver_state *ss, struct apk_package *pkg)
|
|
|
|
{
|
|
|
|
struct apk_dependency *d;
|
|
|
|
struct apk_package *pkg0;
|
|
|
|
|
|
|
|
foreach_array_item(d, pkg->depends) {
|
2013-06-19 18:39:01 +00:00
|
|
|
if (d->conflict || !d->name->ss.installed_name)
|
2013-06-19 17:46:53 +00:00
|
|
|
continue;
|
2013-06-19 18:39:01 +00:00
|
|
|
if (--d->name->ss.installed_name->ss.requirers > 0)
|
2013-06-19 17:46:53 +00:00
|
|
|
continue;
|
2013-06-19 18:39:01 +00:00
|
|
|
pkg0 = d->name->ss.installed_pkg;
|
2013-06-19 17:46:53 +00:00
|
|
|
if (pkg0 != NULL)
|
|
|
|
cset_gen_name_remove(ss, pkg0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cset_check_removal_by_deps(struct apk_solver_state *ss, struct apk_package *pkg)
|
|
|
|
{
|
|
|
|
if (pkg->name->ss.requirers == 0)
|
|
|
|
cset_gen_name_remove(ss, pkg);
|
2011-08-17 13:55:35 +00:00
|
|
|
}
|
|
|
|
|
2013-06-19 17:46:53 +00:00
|
|
|
static void cset_check_install_by_iif(struct apk_solver_state *ss, struct apk_name *name)
|
2011-08-17 13:55:35 +00:00
|
|
|
{
|
2013-06-11 11:06:06 +00:00
|
|
|
struct apk_package *pkg = name->ss.chosen.pkg;
|
2013-06-14 18:20:19 +00:00
|
|
|
struct apk_dependency *dep0;
|
2011-08-17 13:55:35 +00:00
|
|
|
|
2013-06-19 17:46:53 +00:00
|
|
|
if (pkg == NULL || !name->ss.seen || name->ss.in_changeset)
|
2012-04-27 05:47:20 +00:00
|
|
|
return;
|
|
|
|
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(dep0, pkg->install_if) {
|
2013-06-11 11:06:06 +00:00
|
|
|
struct apk_name *name0 = dep0->name;
|
|
|
|
if (!name0->ss.in_changeset)
|
|
|
|
return;
|
|
|
|
if (!apk_dep_is_provided(dep0, &name0->ss.chosen))
|
|
|
|
return;
|
2012-02-24 16:27:55 +00:00
|
|
|
}
|
2013-06-19 17:46:53 +00:00
|
|
|
cset_gen_name_change(ss, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cset_check_removal_by_iif(struct apk_solver_state *ss, struct apk_name *name)
|
|
|
|
{
|
2013-06-19 18:39:01 +00:00
|
|
|
struct apk_package *pkg = name->ss.installed_pkg;
|
2013-06-19 17:46:53 +00:00
|
|
|
struct apk_dependency *dep0;
|
2012-02-24 16:27:55 +00:00
|
|
|
|
2013-06-19 18:39:01 +00:00
|
|
|
if (pkg == NULL || name->ss.in_changeset || name->ss.chosen.pkg != NULL)
|
2013-06-19 17:46:53 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
foreach_array_item(dep0, pkg->install_if) {
|
|
|
|
if (dep0->name->ss.in_changeset &&
|
|
|
|
dep0->name->ss.chosen.pkg == NULL) {
|
|
|
|
cset_check_removal_by_deps(ss, pkg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cset_gen_name_change(struct apk_solver_state *ss, struct apk_name *name)
|
|
|
|
{
|
|
|
|
struct apk_name **pname;
|
|
|
|
struct apk_package *pkg = name->ss.chosen.pkg, *opkg;
|
|
|
|
struct apk_dependency *d;
|
|
|
|
|
|
|
|
if (pkg == NULL || pkg->ss.in_changeset)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pkg->ss.in_changeset = 1;
|
|
|
|
pkg->name->ss.in_changeset = 1;
|
|
|
|
|
2013-06-19 18:39:01 +00:00
|
|
|
opkg = pkg->name->ss.installed_pkg;
|
2013-06-19 17:46:53 +00:00
|
|
|
if (opkg) {
|
|
|
|
foreach_array_item(pname, opkg->name->rinstall_if)
|
|
|
|
cset_check_removal_by_iif(ss, *pname);
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach_array_item(d, pkg->depends)
|
|
|
|
cset_gen_dep(ss, pkg, d);
|
|
|
|
|
2013-06-20 11:08:16 +00:00
|
|
|
dbg_printf("Selecting: "PKG_VER_FMT"%s\n", PKG_VER_PRINTF(pkg), pkg->ss.pkg_selectable ? "" : " [NOT SELECTABLE]");
|
2013-06-19 17:46:53 +00:00
|
|
|
record_change(ss, opkg, pkg);
|
|
|
|
|
|
|
|
foreach_array_item(pname, pkg->name->rinstall_if)
|
|
|
|
cset_check_install_by_iif(ss, *pname);
|
|
|
|
|
2013-06-19 18:39:01 +00:00
|
|
|
cset_track_deps_added(pkg);
|
2013-06-19 17:46:53 +00:00
|
|
|
if (opkg)
|
|
|
|
cset_track_deps_removed(ss, opkg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cset_gen_name_remove(struct apk_solver_state *ss, struct apk_package *pkg)
|
|
|
|
{
|
|
|
|
struct apk_name *name = pkg->name, **pname;
|
|
|
|
|
|
|
|
if (name->ss.chosen.pkg != NULL || name->ss.in_changeset)
|
|
|
|
return;
|
|
|
|
|
|
|
|
name->ss.in_changeset = 1;
|
|
|
|
foreach_array_item(pname, pkg->name->rinstall_if)
|
|
|
|
cset_check_removal_by_iif(ss, *pname);
|
|
|
|
record_change(ss, pkg, NULL);
|
|
|
|
cset_track_deps_removed(ss, pkg);
|
2012-02-24 16:27:55 +00:00
|
|
|
}
|
|
|
|
|
2013-06-19 17:46:53 +00:00
|
|
|
static void cset_gen_dep(struct apk_solver_state *ss, struct apk_package *ppkg, struct apk_dependency *dep)
|
2011-07-26 13:56:55 +00:00
|
|
|
{
|
2013-06-11 11:06:06 +00:00
|
|
|
struct apk_name *name = dep->name;
|
2013-06-12 14:00:43 +00:00
|
|
|
struct apk_package *pkg = name->ss.chosen.pkg;
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
if (!apk_dep_is_provided(dep, &name->ss.chosen))
|
2013-06-12 14:00:43 +00:00
|
|
|
mark_error(ss, ppkg);
|
2012-02-27 14:35:04 +00:00
|
|
|
|
2013-06-19 17:46:53 +00:00
|
|
|
cset_gen_name_change(ss, name);
|
2013-06-12 14:00:43 +00:00
|
|
|
|
|
|
|
if (pkg && pkg->ss.error)
|
|
|
|
mark_error(ss, ppkg);
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
2012-04-27 06:13:11 +00:00
|
|
|
|
2013-06-19 18:39:01 +00:00
|
|
|
static int cset_reset_name(apk_hash_item item, void *ctx)
|
|
|
|
{
|
|
|
|
struct apk_name *name = (struct apk_name *) item;
|
|
|
|
name->ss.installed_pkg = NULL;
|
|
|
|
name->ss.installed_name = NULL;
|
|
|
|
name->ss.requirers = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
static void generate_changeset(struct apk_solver_state *ss, struct apk_dependency_array *world)
|
|
|
|
{
|
|
|
|
struct apk_changeset *changeset = ss->changeset;
|
2013-06-19 18:39:01 +00:00
|
|
|
struct apk_package *pkg;
|
2013-06-11 11:06:06 +00:00
|
|
|
struct apk_installed_package *ipkg;
|
2013-06-14 18:20:19 +00:00
|
|
|
struct apk_dependency *d;
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2013-06-19 17:46:53 +00:00
|
|
|
apk_change_array_init(&changeset->changes);
|
|
|
|
|
2013-06-19 18:39:01 +00:00
|
|
|
apk_hash_foreach(&ss->db->available.names, cset_reset_name, NULL);
|
|
|
|
list_for_each_entry(ipkg, &ss->db->installed.packages, installed_pkgs_list) {
|
|
|
|
pkg = ipkg->pkg;
|
|
|
|
pkg->name->ss.installed_pkg = pkg;
|
|
|
|
pkg->name->ss.installed_name = pkg->name;
|
|
|
|
foreach_array_item(d, pkg->provides)
|
|
|
|
if (d->version != &apk_null_blob)
|
|
|
|
d->name->ss.installed_name = pkg->name;
|
|
|
|
}
|
2013-06-19 17:46:53 +00:00
|
|
|
list_for_each_entry(ipkg, &ss->db->installed.packages, installed_pkgs_list)
|
2013-06-19 18:39:01 +00:00
|
|
|
cset_track_deps_added(ipkg->pkg);
|
2013-06-19 17:46:53 +00:00
|
|
|
list_for_each_entry(ipkg, &ss->db->installed.packages, installed_pkgs_list)
|
|
|
|
cset_check_removal_by_deps(ss, ipkg->pkg);
|
2012-02-16 18:37:03 +00:00
|
|
|
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(d, world)
|
2013-06-19 17:46:53 +00:00
|
|
|
cset_gen_dep(ss, NULL, d);
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2013-06-19 17:46:53 +00:00
|
|
|
list_for_each_entry(ipkg, &ss->db->installed.packages, installed_pkgs_list)
|
|
|
|
cset_gen_name_remove(ss, ipkg->pkg);
|
2012-02-29 06:53:43 +00:00
|
|
|
|
2013-06-19 17:46:53 +00:00
|
|
|
changeset->num_total_changes =
|
|
|
|
changeset->num_install +
|
|
|
|
changeset->num_remove +
|
|
|
|
changeset->num_adjust;
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
|
|
|
|
2013-06-19 18:39:01 +00:00
|
|
|
static int free_name(apk_hash_item item, void *ctx)
|
2013-06-11 11:06:06 +00:00
|
|
|
{
|
|
|
|
struct apk_name *name = (struct apk_name *) item;
|
|
|
|
memset(&name->ss, 0, sizeof(name->ss));
|
|
|
|
return 0;
|
|
|
|
}
|
2012-02-16 18:37:03 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
static int free_package(apk_hash_item item, void *ctx)
|
|
|
|
{
|
|
|
|
struct apk_package *pkg = (struct apk_package *) item;
|
|
|
|
memset(&pkg->ss, 0, sizeof(pkg->ss));
|
|
|
|
return 0;
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
int apk_solver_solve(struct apk_database *db,
|
|
|
|
unsigned short solver_flags,
|
|
|
|
struct apk_dependency_array *world,
|
|
|
|
struct apk_changeset *changeset)
|
2011-07-26 13:56:55 +00:00
|
|
|
{
|
2013-06-11 11:06:06 +00:00
|
|
|
struct apk_name *name, *name0;
|
2013-06-14 18:20:19 +00:00
|
|
|
struct apk_package *pkg;
|
2013-06-11 11:06:06 +00:00
|
|
|
struct apk_solver_state ss_data, *ss = &ss_data;
|
2013-06-14 18:20:19 +00:00
|
|
|
struct apk_dependency *d;
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2013-06-12 14:00:43 +00:00
|
|
|
restart:
|
2013-06-11 11:06:06 +00:00
|
|
|
memset(ss, 0, sizeof(*ss));
|
|
|
|
ss->db = db;
|
|
|
|
ss->changeset = changeset;
|
2013-06-12 10:24:07 +00:00
|
|
|
ss->default_repos = apk_db_get_pinning_mask_repos(db, APK_DEFAULT_PINNING_MASK);
|
2013-06-11 11:06:06 +00:00
|
|
|
list_init(&ss->dirty_head);
|
|
|
|
list_init(&ss->unresolved_head);
|
2011-07-30 17:59:47 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
dbg_printf("applying world\n");
|
2013-06-12 10:24:07 +00:00
|
|
|
ss->prefer_pinning = 1;
|
2013-06-12 05:45:29 +00:00
|
|
|
ss->solver_flags_inherit = solver_flags;
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(d, world) {
|
|
|
|
if (d->broken)
|
2013-06-12 14:00:43 +00:00
|
|
|
continue;
|
2013-06-14 18:20:19 +00:00
|
|
|
name = d->name;
|
|
|
|
discover_name(ss, d->name);
|
|
|
|
ss->pinning_inherit = BIT(d->repository_tag);
|
|
|
|
apply_constraint(ss, NULL, d);
|
2012-02-18 09:54:17 +00:00
|
|
|
}
|
2013-06-12 05:45:29 +00:00
|
|
|
ss->solver_flags_inherit = 0;
|
2013-06-12 10:24:07 +00:00
|
|
|
ss->pinning_inherit = 0;
|
|
|
|
ss->prefer_pinning = 0;
|
2013-06-11 11:06:06 +00:00
|
|
|
dbg_printf("applying world [finished]\n");
|
2011-11-23 12:35:54 +00:00
|
|
|
|
2012-01-17 11:54:33 +00:00
|
|
|
do {
|
2013-06-11 11:06:06 +00:00
|
|
|
while (!list_empty(&ss->dirty_head)) {
|
|
|
|
name = list_pop(&ss->dirty_head, struct apk_name, ss.dirty_list);
|
|
|
|
reconsider_name(ss, name);
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
2011-09-09 13:31:11 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
name = NULL;
|
|
|
|
list_for_each_entry(name0, &ss->unresolved_head, ss.unresolved_list) {
|
2013-06-18 13:24:48 +00:00
|
|
|
if (name0->ss.reverse_deps_done && name0->ss.requirers && !name0->ss.has_options) {
|
2013-06-11 11:06:06 +00:00
|
|
|
name = name0;
|
2011-09-09 13:31:11 +00:00
|
|
|
break;
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
|
|
|
if (name == NULL)
|
|
|
|
goto prefer;
|
2013-06-18 13:24:48 +00:00
|
|
|
if ((!!name0->ss.requirers) - (!!name->ss.requirers) < 0)
|
2012-10-03 11:59:48 +00:00
|
|
|
continue;
|
2013-06-18 13:24:48 +00:00
|
|
|
if (name0->ss.max_dep_chain - name->ss.max_dep_chain < 0)
|
2013-06-12 10:24:07 +00:00
|
|
|
continue;
|
2013-06-11 11:06:06 +00:00
|
|
|
prefer:
|
|
|
|
name = name0;
|
2012-02-27 14:35:04 +00:00
|
|
|
}
|
2013-06-11 11:06:06 +00:00
|
|
|
if (name == NULL)
|
|
|
|
break;
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
select_package(ss, name);
|
|
|
|
} while (1);
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
generate_changeset(ss, world);
|
2012-01-12 08:42:27 +00:00
|
|
|
|
2013-06-12 14:00:43 +00:00
|
|
|
if (ss->errors && (apk_flags & APK_FORCE)) {
|
2013-06-14 18:20:19 +00:00
|
|
|
foreach_array_item(d, world) {
|
|
|
|
name = d->name;
|
|
|
|
pkg = name->ss.chosen.pkg;
|
2013-06-12 14:00:43 +00:00
|
|
|
if (pkg == NULL || pkg->ss.error) {
|
2013-06-14 18:20:19 +00:00
|
|
|
d->broken = 1;
|
2013-06-12 14:00:43 +00:00
|
|
|
dbg_printf("disabling broken world dep: %s", name->name);
|
|
|
|
}
|
|
|
|
}
|
2013-06-19 18:39:01 +00:00
|
|
|
apk_hash_foreach(&db->available.names, free_name, NULL);
|
2013-06-12 14:00:43 +00:00
|
|
|
apk_hash_foreach(&db->available.packages, free_package, NULL);
|
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
|
2013-06-19 18:39:01 +00:00
|
|
|
apk_hash_foreach(&db->available.names, free_name, NULL);
|
2013-06-11 11:06:06 +00:00
|
|
|
apk_hash_foreach(&db->available.packages, free_package, NULL);
|
|
|
|
dbg_printf("solver done, errors=%d\n", ss->errors);
|
2011-09-09 13:31:11 +00:00
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
return ss->errors;
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|