solver: rework internals a bit
* cleaned up little bit on the internal state machine * the decision applying mechanism now aborts early to avoid work if we are approaching bad solution candidate * package availability checking is now done on-demand; which could still be improvedcute-signatures
parent
53f8a36c1f
commit
b0c0b900db
250
src/solver.c
250
src/solver.c
|
@ -38,8 +38,11 @@ struct apk_package_state {
|
|||
struct apk_package *backtrack;
|
||||
unsigned int topology_soft;
|
||||
struct apk_score saved_score;
|
||||
unsigned short flags;
|
||||
unsigned short conflicts;
|
||||
unsigned availability_checked : 1;
|
||||
unsigned unavailable : 1;
|
||||
unsigned install_applied : 1;
|
||||
unsigned flags : 4;
|
||||
};
|
||||
|
||||
struct apk_name_state {
|
||||
|
@ -57,8 +60,8 @@ struct apk_name_state {
|
|||
unsigned int solver_flags_local_mask : 4;
|
||||
unsigned int solver_flags_inherited : 4;
|
||||
|
||||
unsigned int availability_checked : 1;
|
||||
unsigned int locked : 1;
|
||||
unsigned int no_choices_left : 1;
|
||||
unsigned int in_changeset : 1;
|
||||
};
|
||||
|
||||
|
@ -74,15 +77,21 @@ struct apk_solver_state {
|
|||
struct apk_score minimum_penalty;
|
||||
|
||||
unsigned int solver_flags : 4;
|
||||
unsigned int refresh_name_states : 1;
|
||||
|
||||
struct apk_solution_array *best_solution;
|
||||
struct apk_score best_score;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
SOLVERR_SOLUTION = 0,
|
||||
SOLVERR_PRUNED,
|
||||
SOLVERR_EXPAND,
|
||||
SOLVERR_STOP,
|
||||
} solver_result_t;
|
||||
|
||||
static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency *dep);
|
||||
static void undo_constraint(struct apk_solver_state *ss, struct apk_dependency *dep);
|
||||
static void push_decision(struct apk_solver_state *ss, struct apk_package *pkg,
|
||||
static solver_result_t push_decision(struct apk_solver_state *ss, struct apk_package *pkg,
|
||||
int flags);
|
||||
|
||||
static void addscore(struct apk_score *a, struct apk_score *b)
|
||||
|
@ -258,32 +267,6 @@ static void sort_name(struct apk_solver_state *ss, struct apk_name *name)
|
|||
sort_soft_dependencies(ss, name->pkgs->item[i]);
|
||||
}
|
||||
|
||||
static void prepare_name(struct apk_solver_state *ss, struct apk_name *name,
|
||||
struct apk_name_state *ns)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (ns->availability_checked)
|
||||
return;
|
||||
|
||||
for (i = 0; i < name->pkgs->num; i++) {
|
||||
struct apk_package *pkg = name->pkgs->item[i];
|
||||
struct apk_package_state *ps = pkg_to_ps(pkg);
|
||||
struct apk_name_state *ns = name_to_ns(pkg->name);
|
||||
|
||||
/* if package is needed for (re-)install */
|
||||
if ((pkg->ipkg == NULL) ||
|
||||
((ns->solver_flags_local | ns->solver_flags_inherited |
|
||||
ss->solver_flags) & APK_SOLVERF_REINSTALL)) {
|
||||
/* and it's not available, we can't use it */
|
||||
if (!pkg_available(ss->db, pkg))
|
||||
ps->conflicts = 1024;
|
||||
}
|
||||
}
|
||||
|
||||
ns->availability_checked = 1;
|
||||
}
|
||||
|
||||
static void foreach_locked_reverse_dependency(
|
||||
struct apk_name *name,
|
||||
void (*cb)(struct apk_package *rdepend, void *ctx), void *ctx)
|
||||
|
@ -428,7 +411,7 @@ static int get_preference(struct apk_solver_state *ss,
|
|||
continue;
|
||||
|
||||
if (installable_only &&
|
||||
(ss->topology_position <= pkg0->topology_hard ||
|
||||
(ss->topology_position < pkg0->topology_hard ||
|
||||
(ps0->flags & APK_PKGSTF_DECIDED)))
|
||||
continue;
|
||||
|
||||
|
@ -459,6 +442,30 @@ static int install_if_missing(struct apk_solver_state *ss, struct apk_package *p
|
|||
return missing;
|
||||
}
|
||||
|
||||
static int check_if_package_unavailable(struct apk_solver_state *ss, struct apk_package *pkg)
|
||||
{
|
||||
struct apk_name *name = pkg->name;
|
||||
struct apk_package_state *ps = pkg_to_ps(pkg);
|
||||
struct apk_name_state *ns = name_to_ns(name);
|
||||
|
||||
/* installed and no-reinstall required? no check needed. */
|
||||
if ((pkg->ipkg != NULL) &&
|
||||
((ns->solver_flags_local | ns->solver_flags_inherited |
|
||||
ss->solver_flags) & APK_SOLVERF_REINSTALL) == 0)
|
||||
return 0;
|
||||
|
||||
/* done already? */
|
||||
if (ps->availability_checked)
|
||||
return ps->unavailable;
|
||||
|
||||
/* and it's not available, we can't use it */
|
||||
if (!pkg_available(ss->db, pkg))
|
||||
ps->unavailable = 1;
|
||||
|
||||
ps->availability_checked = 1;
|
||||
return ps->unavailable;
|
||||
}
|
||||
|
||||
static int update_name_state(struct apk_solver_state *ss, struct apk_name *name)
|
||||
{
|
||||
struct apk_name_state *ns = name_to_ns(name);
|
||||
|
@ -472,6 +479,9 @@ static int update_name_state(struct apk_solver_state *ss, struct apk_name *name)
|
|||
unsigned int preferred_repos, allowed_repos;
|
||||
int i, options = 0, skipped_options = 0;
|
||||
|
||||
if (ns->locked)
|
||||
return ns->chosen != NULL;
|
||||
|
||||
preferred_pinning = ns->preferred_pinning ?: APK_DEFAULT_PINNING_MASK;
|
||||
preferred_repos = get_pinning_mask_repos(ss->db, preferred_pinning);
|
||||
allowed_pinning = ns->allowed_pinning | preferred_pinning;
|
||||
|
@ -488,16 +498,11 @@ static int update_name_state(struct apk_solver_state *ss, struct apk_name *name)
|
|||
struct apk_package_state *ps0 = pkg_to_ps(pkg0);
|
||||
|
||||
if (ps0 == NULL ||
|
||||
pkg0->topology_hard >= ss->topology_position ||
|
||||
(ps0->flags & APK_PKGSTF_DECIDED))
|
||||
ss->topology_position < pkg0->topology_hard ||
|
||||
(ps0->flags & APK_PKGSTF_DECIDED) ||
|
||||
check_if_package_unavailable(ss, pkg0))
|
||||
continue;
|
||||
|
||||
if ((pkg0->repos != 0) && (pkg0->ipkg == NULL) && (pkg0->filename == NULL) &&
|
||||
!(pkg0->repos & allowed_repos)) {
|
||||
skipped_options++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((preferred_pkg == NULL) ||
|
||||
(ps0->conflicts < preferred_ps->conflicts) ||
|
||||
(ps0->conflicts == preferred_ps->conflicts &&
|
||||
|
@ -509,6 +514,16 @@ static int update_name_state(struct apk_solver_state *ss, struct apk_name *name)
|
|||
preferred_ps = ps0;
|
||||
}
|
||||
|
||||
/* pinning has not enabled the package */
|
||||
if ((pkg0->repos != 0) && (pkg0->ipkg == NULL) &&
|
||||
(pkg0->filename == NULL) && !(pkg0->repos & allowed_repos)) {
|
||||
skipped_options++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* not directly required, there's at least one
|
||||
* valid install_if, but for not this specific pkg;
|
||||
* this might get enabled later */
|
||||
if (ns->requirers == 0 && ns->install_ifs != 0 &&
|
||||
install_if_missing(ss, pkg0)) {
|
||||
skipped_options++;
|
||||
|
@ -523,17 +538,6 @@ static int update_name_state(struct apk_solver_state *ss, struct apk_name *name)
|
|||
best_pkg = pkg0, best_topology = pkg0->topology_hard;
|
||||
}
|
||||
|
||||
if (skipped_options == 0 && options == 0) {
|
||||
if (!ns->locked) {
|
||||
ss->score.unsatisfiable += ns->requirers;
|
||||
ss->score.preference += name->pkgs->num;
|
||||
}
|
||||
ns->locked = 1;
|
||||
ns->chosen = NULL;
|
||||
} else {
|
||||
ns->locked = 0;
|
||||
}
|
||||
|
||||
if (ns->requirers == 0 && ns->install_ifs == 0) {
|
||||
if (list_hashed(&ns->unsolved_list)) {
|
||||
list_del(&ns->unsolved_list);
|
||||
|
@ -545,7 +549,7 @@ static int update_name_state(struct apk_solver_state *ss, struct apk_name *name)
|
|||
} else {
|
||||
if (!list_hashed(&ns->unsolved_list))
|
||||
list_add(&ns->unsolved_list, &ss->unsolved_list_head);
|
||||
if (!ns->locked) {
|
||||
|
||||
ns->chosen = best_pkg;
|
||||
if (preferred_pkg != NULL &&
|
||||
preferred_ps->conflicts < ns->requirers) {
|
||||
|
@ -563,14 +567,21 @@ static int update_name_state(struct apk_solver_state *ss, struct apk_name *name)
|
|||
.unsatisfiable = ns->requirers,
|
||||
.preference = name->pkgs->num,
|
||||
};
|
||||
dbg_printf("%s: min.penalty for name {%d, %d} from no pkg\n",
|
||||
name->name,
|
||||
ns->minimum_penalty.unsatisfiable,
|
||||
ns->minimum_penalty.preference);
|
||||
}
|
||||
addscore(&ss->minimum_penalty, &ns->minimum_penalty);
|
||||
}
|
||||
dbg_printf("%s: added to unsolved: %d requirers, %d install_ifs, %d options (next topology %d)\n",
|
||||
name->name, ns->requirers, ns->install_ifs, options,
|
||||
|
||||
dbg_printf("%s: added to unsolved: %d requirers, %d install_ifs, %d options, %d skipped options (next topology %d)\n",
|
||||
name->name, ns->requirers, ns->install_ifs,
|
||||
options, skipped_options,
|
||||
best_topology);
|
||||
}
|
||||
|
||||
ns->no_choices_left = (options == 0 && skipped_options == 0);
|
||||
|
||||
return options + skipped_options;
|
||||
}
|
||||
|
||||
|
@ -600,22 +611,40 @@ static void untrigger_install_if(struct apk_solver_state *ss,
|
|||
}
|
||||
}
|
||||
|
||||
static void apply_decision(struct apk_solver_state *ss,
|
||||
static solver_result_t apply_decision(struct apk_solver_state *ss,
|
||||
struct apk_package *pkg,
|
||||
struct apk_package_state *ps)
|
||||
{
|
||||
struct apk_name_state *ns = name_to_ns(pkg->name);
|
||||
struct apk_name *name = pkg->name;
|
||||
struct apk_name_state *ns = name_to_ns(name);
|
||||
|
||||
dbg_printf("apply_decision: " PKG_VER_FMT " %s\n", PKG_VER_PRINTF(pkg),
|
||||
dbg_printf("-->apply_decision: " PKG_VER_FMT " %s\n", PKG_VER_PRINTF(pkg),
|
||||
(ps->flags & APK_PKGSTF_INSTALL) ? "INSTALL" : "NO_INSTALL");
|
||||
|
||||
if (ps->flags & APK_PKGSTF_INSTALL) {
|
||||
unsigned short preference;
|
||||
|
||||
subscore(&ss->minimum_penalty, &ns->minimum_penalty);
|
||||
ns->minimum_penalty = (struct apk_score) { 0, 0 };
|
||||
|
||||
ss->assigned_names++;
|
||||
preference = get_preference(ss, pkg, FALSE);
|
||||
ss->score.unsatisfiable += ps->conflicts;
|
||||
ss->score.preference += get_preference(ss, pkg, FALSE);
|
||||
ss->score.preference += preference;
|
||||
if (cmpscore2(&ss->score, &ss->minimum_penalty, &ss->best_score) >= 0) {
|
||||
dbg_printf("install causing {%d,%d}, penalty too big: {%d,%d}+{%d,%d}>={%d,%d}\n",
|
||||
ps->conflicts, preference,
|
||||
ss->score.unsatisfiable, ss->score.preference,
|
||||
ss->minimum_penalty.unsatisfiable, ss->minimum_penalty.preference,
|
||||
ss->best_score.unsatisfiable, ss->best_score.preference
|
||||
);
|
||||
|
||||
ss->score.unsatisfiable -= ps->conflicts;
|
||||
ss->score.preference -= preference;
|
||||
return SOLVERR_PRUNED;
|
||||
}
|
||||
|
||||
ps->install_applied = 1;
|
||||
ss->assigned_names++;
|
||||
ns->chosen = pkg;
|
||||
ns->locked = 1;
|
||||
|
||||
|
@ -627,17 +656,38 @@ static void apply_decision(struct apk_solver_state *ss,
|
|||
foreach_dependency(ss, pkg->depends, apply_constraint);
|
||||
foreach_rinstall_if_pkg(ss, pkg, trigger_install_if);
|
||||
} else {
|
||||
update_name_state(ss, pkg->name);
|
||||
if (ns->locked == 0 && update_name_state(ss, pkg->name) == 0) {
|
||||
subscore(&ss->minimum_penalty, &ns->minimum_penalty);
|
||||
ns->minimum_penalty = (struct apk_score) { 0, 0 };
|
||||
|
||||
dbg_printf("%s: out of candidates, locking to none\n",
|
||||
pkg->name->name);
|
||||
|
||||
ss->score.unsatisfiable += ns->requirers;
|
||||
ss->score.preference += name->pkgs->num;
|
||||
ns->locked = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmpscore2(&ss->score, &ss->minimum_penalty, &ss->best_score) >= 0) {
|
||||
dbg_printf("install/uninstall finished penalty too big: {%d,%d}+{%d,%d}>={%d,%d}\n",
|
||||
ss->score.unsatisfiable, ss->score.preference,
|
||||
ss->minimum_penalty.unsatisfiable, ss->minimum_penalty.preference,
|
||||
ss->best_score.unsatisfiable, ss->best_score.preference
|
||||
);
|
||||
return SOLVERR_PRUNED;
|
||||
}
|
||||
|
||||
return SOLVERR_EXPAND;
|
||||
}
|
||||
|
||||
static void undo_decision(struct apk_solver_state *ss,
|
||||
struct apk_package *pkg,
|
||||
struct apk_package_state *ps)
|
||||
{
|
||||
struct apk_name_state *ns = name_to_ns(pkg->name);
|
||||
|
||||
dbg_printf("undo_decision: " PKG_VER_FMT " %s\n", PKG_VER_PRINTF(pkg),
|
||||
dbg_printf("-->undo_decision: " PKG_VER_FMT " %s\n", PKG_VER_PRINTF(pkg),
|
||||
(ps->flags & APK_PKGSTF_INSTALL) ? "INSTALL" : "NO_INSTALL");
|
||||
|
||||
if (ps->flags & APK_PKGSTF_INSTALLIF)
|
||||
|
@ -646,20 +696,27 @@ static void undo_decision(struct apk_solver_state *ss,
|
|||
ss->topology_position = pkg->topology_hard;
|
||||
|
||||
if (ps->flags & APK_PKGSTF_INSTALL) {
|
||||
if (ps->install_applied) {
|
||||
ps->install_applied = 0;
|
||||
ss->assigned_names--;
|
||||
|
||||
foreach_rinstall_if_pkg(ss, pkg, untrigger_install_if);
|
||||
foreach_dependency(ss, pkg->depends, undo_constraint);
|
||||
}
|
||||
|
||||
ns->locked = 0;
|
||||
ns->chosen = NULL;
|
||||
} else {
|
||||
/* UNINSTALL decision removed - either name is unlocked
|
||||
* or locked to none */
|
||||
ns->locked = 0;
|
||||
ns->chosen = NULL;
|
||||
}
|
||||
|
||||
ss->score = ps->saved_score;
|
||||
update_name_state(ss, pkg->name);
|
||||
}
|
||||
|
||||
static void push_decision(struct apk_solver_state *ss, struct apk_package *pkg,
|
||||
static solver_result_t push_decision(struct apk_solver_state *ss, struct apk_package *pkg,
|
||||
int flags)
|
||||
{
|
||||
struct apk_package_state *ps = pkg_to_ps(pkg);
|
||||
|
@ -678,7 +735,7 @@ static void push_decision(struct apk_solver_state *ss, struct apk_package *pkg,
|
|||
}
|
||||
ss->latest_decision = pkg;
|
||||
|
||||
dbg_printf("push_decision: adding new BRANCH at topology_position %d (score: unsatisfied %d, preference %d)\n",
|
||||
dbg_printf("-->push_decision: adding new BRANCH at topology_position %d (score: unsatisfied %d, preference %d)\n",
|
||||
ss->topology_position,
|
||||
ss->score.unsatisfiable,
|
||||
ss->score.preference);
|
||||
|
@ -687,7 +744,7 @@ static void push_decision(struct apk_solver_state *ss, struct apk_package *pkg,
|
|||
dbg_printf("triggers due to " PKG_VER_FMT "\n",
|
||||
PKG_VER_PRINTF(pkg));
|
||||
|
||||
apply_decision(ss, pkg, ps);
|
||||
return apply_decision(ss, pkg, ps);
|
||||
}
|
||||
|
||||
static int next_branch(struct apk_solver_state *ss)
|
||||
|
@ -700,29 +757,27 @@ static int next_branch(struct apk_solver_state *ss)
|
|||
ps = pkg_to_ps(pkg);
|
||||
|
||||
if (ps->flags & APK_PKGSTF_ALT_BRANCH) {
|
||||
dbg_printf("next_branch: undo decision at topology_position %d\n",
|
||||
dbg_printf("-->next_branch: undo decision at topology_position %d\n",
|
||||
ss->topology_position);
|
||||
ps->flags &= ~(APK_PKGSTF_ALT_BRANCH | APK_PKGSTF_DECIDED);
|
||||
undo_decision(ss, pkg, ps);
|
||||
|
||||
ss->latest_decision = ps->backtrack;
|
||||
ss->refresh_name_states = 1;
|
||||
} else {
|
||||
undo_decision(ss, pkg, ps);
|
||||
|
||||
dbg_printf("next_branch: swapping BRANCH at topology_position %d\n",
|
||||
dbg_printf("-->next_branch: swapping BRANCH at topology_position %d\n",
|
||||
ss->topology_position);
|
||||
|
||||
undo_decision(ss, pkg, ps);
|
||||
|
||||
ps->flags |= APK_PKGSTF_ALT_BRANCH;
|
||||
ps->flags ^= APK_PKGSTF_INSTALL;
|
||||
|
||||
apply_decision(ss, pkg, ps);
|
||||
return 0;
|
||||
return apply_decision(ss, pkg, ps);
|
||||
}
|
||||
}
|
||||
|
||||
dbg_printf("next_branch: no more branches\n");
|
||||
return 1;
|
||||
dbg_printf("-->next_branch: no more branches\n");
|
||||
return SOLVERR_STOP;
|
||||
}
|
||||
|
||||
static void inherit_name_state(struct apk_name *to, struct apk_name *from)
|
||||
|
@ -771,8 +826,6 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency
|
|||
struct apk_name_state *ns = name_to_ns(name);
|
||||
int i;
|
||||
|
||||
prepare_name(ss, name, ns);
|
||||
|
||||
if (ns->locked) {
|
||||
if (ns->chosen)
|
||||
dbg_printf("%s: locked to " PKG_VER_FMT " already\n",
|
||||
|
@ -808,8 +861,11 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency
|
|||
}
|
||||
}
|
||||
|
||||
if (ss->latest_decision != NULL)
|
||||
if (ss->latest_decision != NULL) {
|
||||
dbg_printf("%s: inheriting flags and pinning from %s\n",
|
||||
name->name, ss->latest_decision->name->name);
|
||||
inherit_name_state(name, ss->latest_decision->name);
|
||||
}
|
||||
|
||||
if (!dep->optional)
|
||||
ns->requirers++;
|
||||
|
@ -867,13 +923,12 @@ static int expand_branch(struct apk_solver_state *ss)
|
|||
|
||||
/* FIXME: change unsolved_list to a priority queue */
|
||||
list_for_each_entry(ns, &ss->unsolved_list_head, unsolved_list) {
|
||||
if (ss->refresh_name_states)
|
||||
if (ns->chosen == NULL && !ns->no_choices_left) {
|
||||
/* we have not been able to determine candidate
|
||||
* for the package, but is not yet completely
|
||||
* excluded either. try updating it. */
|
||||
update_name_state(ss, ns->name);
|
||||
|
||||
/* ns->chosen can be NULL if the name has only install_if
|
||||
* requirers that got later conflicted, but it still has
|
||||
* other options that can get activated later due to more
|
||||
* complicated install_if rules in some other package. */
|
||||
}
|
||||
if (ns->chosen == NULL)
|
||||
continue;
|
||||
if (pkg_to_ps(ns->chosen)->topology_soft < ss->topology_position &&
|
||||
|
@ -882,20 +937,17 @@ static int expand_branch(struct apk_solver_state *ss)
|
|||
else if (ns->chosen->topology_hard > topology0)
|
||||
pkg0 = ns->chosen, topology0 = pkg0->topology_hard;
|
||||
}
|
||||
ss->refresh_name_states = 0;
|
||||
if (pkg0 == NULL) {
|
||||
list_for_each_entry(ns, &ss->unsolved_list_head, unsolved_list) {
|
||||
if (ns->locked)
|
||||
continue;
|
||||
|
||||
ss->score.unsatisfiable += ns->requirers;
|
||||
ss->score.preference += ns->name->pkgs->num;
|
||||
}
|
||||
|
||||
dbg_printf("expand_branch: list is empty (%d unsatisfied)\n",
|
||||
ss->score.unsatisfiable);
|
||||
|
||||
return 1;
|
||||
return SOLVERR_SOLUTION;
|
||||
}
|
||||
|
||||
/* someone needs to provide this name -- find next eligible
|
||||
|
@ -907,9 +959,8 @@ static int expand_branch(struct apk_solver_state *ss)
|
|||
flags = APK_PKGSTF_INSTALL;
|
||||
else
|
||||
flags = APK_PKGSTF_NOINSTALL;
|
||||
push_decision(ss, pkg0, flags);
|
||||
|
||||
return 0;
|
||||
return push_decision(ss, pkg0, flags);
|
||||
}
|
||||
|
||||
static int get_tag(struct apk_database *db, unsigned short pinning_mask, unsigned int repos)
|
||||
|
@ -1113,7 +1164,8 @@ int apk_solver_solve(struct apk_database *db,
|
|||
struct apk_solver_state *ss;
|
||||
struct apk_installed_package *ipkg;
|
||||
struct apk_score zero_score;
|
||||
int i, r;
|
||||
solver_result_t r = SOLVERR_STOP;
|
||||
int i;
|
||||
|
||||
ss = calloc(1, sizeof(struct apk_solver_state));
|
||||
ss->db = db;
|
||||
|
@ -1135,9 +1187,9 @@ int apk_solver_solve(struct apk_database *db,
|
|||
zero_score.preference);
|
||||
|
||||
do {
|
||||
if (cmpscore2(&ss->score, &ss->minimum_penalty, &ss->best_score) < 0) {
|
||||
/* need EXPAND if here, can return SOLUTION|PRUNED|EXPAND */
|
||||
r = expand_branch(ss);
|
||||
if (r) {
|
||||
if (r == SOLVERR_SOLUTION) {
|
||||
dbg_printf("solution with: %d unsatisfiable, %d preference\n",
|
||||
ss->score.unsatisfiable,
|
||||
ss->score.preference);
|
||||
|
@ -1149,15 +1201,15 @@ int apk_solver_solve(struct apk_database *db,
|
|||
/* found solution - it is optimal because we permutate
|
||||
* each preferred local option first, and permutations
|
||||
* happen in topologally sorted order. */
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
r = next_branch(ss);
|
||||
r = SOLVERR_PRUNED;
|
||||
}
|
||||
} else {
|
||||
/* next_branch() returns PRUNED, STOP or EXPAND */
|
||||
while (r == SOLVERR_PRUNED)
|
||||
r = next_branch(ss);
|
||||
}
|
||||
} while (r == 0);
|
||||
/* STOP or EXPAND */
|
||||
} while (r != SOLVERR_STOP);
|
||||
|
||||
/* collect packages */
|
||||
if (changeset != NULL) {
|
||||
|
@ -1171,11 +1223,11 @@ int apk_solver_solve(struct apk_database *db,
|
|||
} else {
|
||||
apk_solution_array_free(&ss->best_solution);
|
||||
}
|
||||
r = ss->best_score.unsatisfiable;
|
||||
i = ss->best_score.unsatisfiable;
|
||||
apk_solver_free(db);
|
||||
free(ss);
|
||||
|
||||
return r;
|
||||
return i;
|
||||
}
|
||||
|
||||
static void print_change(struct apk_database *db,
|
||||
|
|
Loading…
Reference in New Issue