solver: merge apk_name_state to apk_name

apk_name_state is now quite small; and we avoid overhead of two
pointers (+ malloc overhead) when we just make it part of apk_name.
It also fixes some problems (that got introduced) where apk_name_state
was not allocated.
cute-signatures
Timo Teräs 2012-02-29 11:26:12 +02:00
parent 15adb0475f
commit 2f66295fc7
5 changed files with 220 additions and 236 deletions

View File

@ -18,6 +18,9 @@
#include "apk_package.h"
#include "apk_io.h"
#include "apk_provider_data.h"
#include "apk_solver_data.h"
extern const char * const apk_index_gz;
extern const char * const apkindex_tar_gz;
@ -78,25 +81,18 @@ struct apk_db_dir_instance {
gid_t gid;
};
#define PROVIDER_FMT "%s%s"BLOB_FMT
#define PROVIDER_PRINTF(n,p) (n)->name, (p)->version->len ? "-" : "", BLOB_PRINTF(*(p)->version)
struct apk_provider {
struct apk_package *pkg;
apk_blob_t *version;
};
APK_ARRAY(apk_provider_array, struct apk_provider);
struct apk_name {
apk_hash_node hash_node;
union {
int state_int;
void *state_ptr;
};
char *name;
struct apk_provider_array *providers;
struct apk_name_array *rdepends;
struct apk_name_array *rinstall_if;
union {
struct apk_solver_name_state ss;
void *state_ptr;
int state_int;
};
};
struct apk_repository {

27
src/apk_provider_data.h Normal file
View File

@ -0,0 +1,27 @@
/* apk_provider_data.h - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008-2012 Timo Teräs <timo.teras@iki.fi>
* 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.
*/
#ifndef APK_PROVIDER_DATA_H
#define APK_PROVIDER_DATA_H
#include "apk_defines.h"
#include "apk_blob.h"
struct apk_provider {
struct apk_package *pkg;
apk_blob_t *version;
};
APK_ARRAY(apk_provider_array, struct apk_provider);
#define PROVIDER_FMT "%s%s"BLOB_FMT
#define PROVIDER_PRINTF(n,p) (n)->name, (p)->version->len ? "-" : "", BLOB_PRINTF(*(p)->version)
#endif

View File

@ -12,6 +12,9 @@
#ifndef APK_SOLVER_H
#define APK_SOLVER_H
struct apk_name;
struct apk_package;
struct apk_solution_entry {
struct apk_package *pkg;
unsigned short repository_tag : 15;

45
src/apk_solver_data.h Normal file
View File

@ -0,0 +1,45 @@
/* apk_solver_data.h - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008-2012 Timo Teräs <timo.teras@iki.fi>
* 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.
*/
#ifndef APK_SOLVER_DATA_H
#define APK_SOLVER_DATA_H
#include "apk_defines.h"
#include "apk_provider_data.h"
struct apk_solver_name_state {
/* dynamic */
struct list_head unsolved_list;
struct apk_provider chosen;
unsigned int last_touched_decision;
unsigned short requirers;
unsigned short install_ifs;
unsigned short preferred_pinning;
unsigned short locked;
/* one time prepare/finish flags */
unsigned solver_flags_local : 4;
unsigned solver_flags_inheritable : 4;
unsigned decision_counted : 1;
unsigned originally_installed : 1;
unsigned has_available_pkgs : 1;
unsigned in_changeset : 1;
unsigned in_world_dependency : 1;
/* dynamic state flags */
unsigned none_excluded : 1;
unsigned name_touched : 1;
unsigned preferred_chosen : 1;
};
#endif

View File

@ -109,34 +109,6 @@ struct apk_package_state {
#define CHOSEN_NONE (struct apk_provider) { .pkg = NULL, .version = NULL }
struct apk_name_state {
/* dynamic */
struct list_head unsolved_list;
struct apk_name *name;
struct apk_provider chosen;
unsigned int last_touched_decision;
unsigned short requirers;
unsigned short install_ifs;
unsigned short preferred_pinning;
unsigned short locked;
/* one time prepare/finish flags */
unsigned solver_flags_local : 4;
unsigned solver_flags_inheritable : 4;
unsigned decision_counted : 1;
unsigned originally_installed : 1;
unsigned has_available_pkgs : 1;
unsigned in_changeset : 1;
unsigned in_world_dependency : 1;
/* dynamic state flags */
unsigned none_excluded : 1;
unsigned name_touched : 1;
unsigned preferred_chosen : 1;
};
struct apk_solver_state {
struct apk_database *db;
struct apk_decision *decisions;
@ -247,32 +219,6 @@ static struct apk_package_state *pkg_to_ps_alloc(struct apk_package *pkg)
return pkg_to_ps(pkg);
}
static struct apk_name_state *name_to_ns(struct apk_name *name)
{
return (struct apk_name_state*) name->state_ptr;
}
static struct apk_name_state *name_to_ns_alloc(struct apk_name *name)
{
struct apk_name_state *ns;
int i;
if (name->state_ptr == NULL) {
ns = calloc(1, sizeof(struct apk_name_state));
ns->name = name;
for (i = 0; i < name->providers->num; i++) {
if (name->providers->item[i].pkg->repos != 0) {
ns->has_available_pkgs = 1;
break;
}
}
name->state_ptr = ns;
} else {
ns = (struct apk_name_state*) name->state_ptr;
}
return ns;
}
static inline int pkg_available(struct apk_database *db, struct apk_package *pkg)
{
/* virtual packages - only deps used; no real .apk */
@ -358,10 +304,10 @@ static unsigned int get_pinning_mask_repos(struct apk_database *db, unsigned sho
static int get_topology_score(
struct apk_solver_state *ss,
struct apk_name_state *ns,
struct apk_package *pkg,
struct apk_score *_score)
{
struct apk_name *name = pkg->name;
struct apk_package_state *ps = pkg_to_ps(pkg);
struct apk_score score;
unsigned int repos;
@ -376,21 +322,21 @@ static int get_topology_score(
if (ss->solver_flags & APK_SOLVERF_AVAILABLE) {
/* available preferred */
if ((pkg->repos == 0) && ns->has_available_pkgs)
if ((pkg->repos == 0) && name->ss.has_available_pkgs)
score.non_preferred_actions++;
} else if (ps->inherited_reinstall ||
(((ns->solver_flags_local|ss->solver_flags) & APK_SOLVERF_REINSTALL))) {
(((name->ss.solver_flags_local|ss->solver_flags) & APK_SOLVERF_REINSTALL))) {
/* reinstall requested, but not available */
if (!pkg_available(ss->db, pkg))
score.non_preferred_actions++;
} else if (ps->inherited_upgrade ||
((ns->solver_flags_local|ss->solver_flags) & APK_SOLVERF_UPGRADE)) {
((name->ss.solver_flags_local|ss->solver_flags) & APK_SOLVERF_UPGRADE)) {
/* upgrading - score is just locked here */
} else if ((ps->inherited_upgrade == 0) &&
((ns->solver_flags_local|ss->solver_flags) & APK_SOLVERF_UPGRADE) == 0 &&
((name->ss.solver_flags_local|ss->solver_flags) & APK_SOLVERF_UPGRADE) == 0 &&
((ps->solver_flags_maybe & APK_SOLVERF_UPGRADE) == 0 || (ps->locked))) {
/* not upgrading: it is not preferred to change package */
if (pkg->ipkg == NULL && ns->originally_installed)
if (pkg->ipkg == NULL && name->ss.originally_installed)
score.non_preferred_actions++;
else
sticky_installed = TRUE;
@ -399,7 +345,7 @@ static int get_topology_score(
}
repos = pkg->repos | (pkg->ipkg ? ss->db->repo_tags[pkg->ipkg->repository_tag].allowed_repos : 0);
preferred_pinning = ns->preferred_pinning ?: APK_DEFAULT_PINNING_MASK;
preferred_pinning = name->ss.preferred_pinning ?: APK_DEFAULT_PINNING_MASK;
preferred_repos = get_pinning_mask_repos(ss->db, preferred_pinning);
if (!(repos & preferred_repos))
@ -483,11 +429,9 @@ static void calculate_pkg_preference(struct apk_package *pkg)
static void count_name(struct apk_solver_state *ss, struct apk_name *name)
{
struct apk_name_state *ns = name_to_ns_alloc(name);
if (!ns->decision_counted) {
if (!name->ss.decision_counted) {
ss->max_decisions++;
ns->decision_counted = 1;
name->ss.decision_counted = 1;
}
}
@ -495,6 +439,7 @@ static void sort_hard_dependencies(struct apk_solver_state *ss,
struct apk_package *pkg,
struct apk_package *parent_pkg)
{
struct apk_name *name = pkg->name;
struct apk_package_state *ps;
int i;
@ -511,9 +456,12 @@ static void sort_hard_dependencies(struct apk_solver_state *ss,
foreach_dependency_pkg(ss, pkg, pkg->install_if, sort_hard_dependencies);
ss->max_decisions++;
count_name(ss, pkg->name);
for (i = 0; i < pkg->provides->num; i++)
name->ss.has_available_pkgs = 1;
count_name(ss, name);
for (i = 0; i < pkg->provides->num; i++) {
pkg->provides->item[i].name->ss.has_available_pkgs = 1;
count_name(ss, pkg->provides->item[i].name);
}
ps->topology_soft = pkg->topology_hard = ++ss->num_topology_positions;
dbg_printf(PKG_VER_FMT ": topology_hard=%d\n",
@ -560,7 +508,6 @@ static void update_allowed(struct apk_database *db, struct apk_package *pkg)
static void sort_name(struct apk_solver_state *ss, struct apk_name *name)
{
struct apk_name_state *ns = name_to_ns_alloc(name);
int i, j;
for (i = 0; i < name->providers->num; i++) {
@ -568,8 +515,8 @@ static void sort_name(struct apk_solver_state *ss, struct apk_name *name)
struct apk_package_state *ps = pkg_to_ps_alloc(pkg);
unsigned short allowed_pinning;
ps->allowed_pinning |= ns->preferred_pinning;
ps->allowed_pinning_maybe |= ns->preferred_pinning;
ps->allowed_pinning |= name->ss.preferred_pinning;
ps->allowed_pinning_maybe |= name->ss.preferred_pinning;
allowed_pinning = ps->allowed_pinning;
for (j = 0; allowed_pinning; j++) {
@ -593,18 +540,13 @@ static void foreach_dependency(struct apk_solver_state *ss, struct apk_dependenc
static int install_if_missing(struct apk_solver_state *ss, struct apk_package *pkg)
{
struct apk_name_state *ns;
int i, missing = 0;
for (i = 0; i < pkg->install_if->num; i++) {
struct apk_dependency *dep = &pkg->install_if->item[i];
struct apk_name *name = dep->name;
ns = name_to_ns(dep->name);
/* ns can be NULL, if the install_if has a name with
* no packages */
if (ns == NULL || !ns->locked ||
!apk_dep_is_provided(dep, &ns->chosen))
if (!name->ss.locked || !apk_dep_is_provided(dep, &name->ss.chosen))
missing++;
}
@ -613,45 +555,39 @@ static int install_if_missing(struct apk_solver_state *ss, struct apk_package *p
static void get_unassigned_score(struct apk_name *name, struct apk_score *score)
{
struct apk_name_state *ns = name_to_ns(name);
*score = (struct apk_score){
.conflicts = ns->requirers,
.conflicts = name->ss.requirers,
.preference = name->providers->num,
};
}
static void promote_name(struct apk_solver_state *ss, struct apk_name *name)
{
struct apk_name_state *ns = name_to_ns(name);
if (ns->locked)
if (name->ss.locked)
return;
/* queue for handling if needed */
if (!list_hashed(&ns->unsolved_list))
list_add_tail(&ns->unsolved_list, &ss->unsolved_list_head);
if (!list_hashed(&name->ss.unsolved_list))
list_add_tail(&name->ss.unsolved_list, &ss->unsolved_list_head);
/* update info, but no cached information flush is required, as
* minimum_penalty can only go up */
ns->name_touched = 1;
name->ss.name_touched = 1;
}
static void demote_name(struct apk_solver_state *ss, struct apk_name *name)
{
struct apk_name_state *ns = name_to_ns(name);
if (ns->locked)
if (name->ss.locked)
return;
/* remove cached information */
ns->chosen = CHOSEN_NONE;
name->ss.chosen = CHOSEN_NONE;
/* and remove list, or request refresh */
if (ns->requirers == 0 && ns->install_ifs == 0) {
if (list_hashed(&ns->unsolved_list)) {
list_del(&ns->unsolved_list);
list_init(&ns->unsolved_list);
if (name->ss.requirers == 0 && name->ss.install_ifs == 0) {
if (list_hashed(&name->ss.unsolved_list)) {
list_del(&name->ss.unsolved_list);
list_init(&name->ss.unsolved_list);
dbg_printf("%s: not required\n", name->name);
}
} else {
@ -662,18 +598,18 @@ static void demote_name(struct apk_solver_state *ss, struct apk_name *name)
static int inherit_package_state(struct apk_database *db, struct apk_package *to, struct apk_package *from)
{
struct apk_name *fname = from->name;
struct apk_package_state *tps = pkg_to_ps(to);
struct apk_name_state *fns = name_to_ns(from->name);
struct apk_package_state *fps = pkg_to_ps(from);
int i, changed = 0;
if ((fns->solver_flags_inheritable & APK_SOLVERF_REINSTALL) ||
if ((fname->ss.solver_flags_inheritable & APK_SOLVERF_REINSTALL) ||
fps->inherited_reinstall) {
tps->inherited_reinstall++;
changed = 1;
}
if ((fns->solver_flags_inheritable & APK_SOLVERF_UPGRADE) ||
if ((fname->ss.solver_flags_inheritable & APK_SOLVERF_UPGRADE) ||
fps->inherited_upgrade) {
tps->inherited_upgrade++;
changed = 1;
@ -699,16 +635,16 @@ static int inherit_package_state(struct apk_database *db, struct apk_package *to
static void uninherit_package_state(struct apk_database *db, struct apk_package *to, struct apk_package *from)
{
struct apk_name *fname = from->name;
struct apk_package_state *tps = pkg_to_ps(to);
struct apk_name_state *fns = name_to_ns(from->name);
struct apk_package_state *fps = pkg_to_ps(from);
int i, changed = 0;
if ((fns->solver_flags_inheritable & APK_SOLVERF_REINSTALL) ||
if ((fname->ss.solver_flags_inheritable & APK_SOLVERF_REINSTALL) ||
fps->inherited_reinstall)
tps->inherited_reinstall--;
if ((fns->solver_flags_inheritable & APK_SOLVERF_UPGRADE) ||
if ((fname->ss.solver_flags_inheritable & APK_SOLVERF_UPGRADE) ||
fps->inherited_upgrade)
tps->inherited_upgrade--;
@ -732,14 +668,14 @@ static void trigger_install_if(struct apk_solver_state *ss,
struct apk_package *pkg,
struct apk_package *parent_pkg)
{
if (install_if_missing(ss, pkg) == 0) {
struct apk_name_state *ns = name_to_ns(pkg->name);
struct apk_name *name = pkg->name;
if (install_if_missing(ss, pkg) == 0) {
dbg_printf("trigger_install_if: " PKG_VER_FMT " triggered\n",
PKG_VER_PRINTF(pkg));
ns->install_ifs++;
name->ss.install_ifs++;
inherit_package_state(ss->db, pkg, parent_pkg);
promote_name(ss, pkg->name);
promote_name(ss, name);
}
}
@ -747,45 +683,41 @@ static void untrigger_install_if(struct apk_solver_state *ss,
struct apk_package *pkg,
struct apk_package *parent_pkg)
{
if (install_if_missing(ss, pkg) != 1) {
struct apk_name_state *ns = name_to_ns(pkg->name);
struct apk_name *name = pkg->name;
if (install_if_missing(ss, pkg) != 1) {
dbg_printf("untrigger_install_if: " PKG_VER_FMT " no longer triggered\n",
PKG_VER_PRINTF(pkg));
ns->install_ifs--;
name->ss.install_ifs--;
uninherit_package_state(ss->db, pkg, parent_pkg);
demote_name(ss, pkg->name);
demote_name(ss, name);
}
}
static inline void assign_name(
struct apk_solver_state *ss, struct apk_name *name, struct apk_provider p)
{
struct apk_name_state *ns = name_to_ns(name);
if (p.version == &apk_null_blob) {
ASSERT(!ns->locked || ns->chosen.version == &apk_null_blob,
ASSERT(!name->ss.locked || name->ss.chosen.version == &apk_null_blob,
"Assigning locked name with version");
} else {
ASSERT(!ns->locked, "Assigning locked name");
ASSERT(!name->ss.locked, "Assigning locked name");
}
ns->chosen = p;
ns->locked++;
if (list_hashed(&ns->unsolved_list)) {
list_del(&ns->unsolved_list);
list_init(&ns->unsolved_list);
name->ss.chosen = p;
name->ss.locked++;
if (list_hashed(&name->ss.unsolved_list)) {
list_del(&name->ss.unsolved_list);
list_init(&name->ss.unsolved_list);
}
}
static inline void unassign_name(struct apk_solver_state *ss, struct apk_name *name)
{
struct apk_name_state *ns = name_to_ns(name);
ASSERT(ns->locked, "Unassigning unlocked name");
ns->locked--;
if (ns->locked == 0) {
ns->chosen = CHOSEN_NONE;
ns->name_touched = 1;
ASSERT(name->ss.locked, "Unassigning unlocked name");
name->ss.locked--;
if (name->ss.locked == 0) {
name->ss.chosen = CHOSEN_NONE;
name->ss.name_touched = 1;
demote_name(ss, name);
}
}
@ -794,13 +726,12 @@ static solver_result_t apply_decision(struct apk_solver_state *ss,
struct apk_decision *d)
{
struct apk_name *name = decision_to_name(d);
struct apk_name_state *ns = name_to_ns(name);
struct apk_package *pkg = decision_to_pkg(d);
struct apk_score score;
int i;
ss->impossible_state = 0;
ns->name_touched = 1;
name->ss.name_touched = 1;
if (pkg != NULL) {
struct apk_package_state *ps = pkg_to_ps(pkg);
@ -809,7 +740,7 @@ static solver_result_t apply_decision(struct apk_solver_state *ss,
(d->type == DECISION_ASSIGN) ? "ASSIGN" : "EXCLUDE");
for (i = 0; i < pkg->provides->num; i++)
name_to_ns(pkg->provides->item[i].name)->name_touched = 1;
pkg->provides->item[i].name->ss.name_touched = 1;
ps->locked = 1;
ps->handle_install_if = 0;
@ -828,7 +759,7 @@ static solver_result_t apply_decision(struct apk_solver_state *ss,
}
if (d->type == DECISION_ASSIGN) {
get_topology_score(ss, ns, pkg, &score);
get_topology_score(ss, pkg, &score);
addscore(&ss->score, &score);
ss->assigned_names++;
@ -850,12 +781,12 @@ static solver_result_t apply_decision(struct apk_solver_state *ss,
get_unassigned_score(name, &score);
addscore(&ss->score, &score);
ns->chosen = CHOSEN_NONE;
ns->locked = 1;
list_del(&ns->unsolved_list);
list_init(&ns->unsolved_list);
name->ss.chosen = CHOSEN_NONE;
name->ss.locked = 1;
list_del(&name->ss.unsolved_list);
list_init(&name->ss.unsolved_list);
} else {
ns->none_excluded = 1;
name->ss.none_excluded = 1;
}
}
@ -882,12 +813,11 @@ static void undo_decision(struct apk_solver_state *ss,
struct apk_decision *d)
{
struct apk_name *name = decision_to_name(d);
struct apk_name_state *ns = name_to_ns(name);
struct apk_package *pkg = decision_to_pkg(d);
struct apk_score score;
int i;
ns->name_touched = 1;
name->ss.name_touched = 1;
if (pkg != NULL) {
struct apk_package_state *ps = pkg_to_ps(pkg);
@ -904,13 +834,13 @@ static void undo_decision(struct apk_solver_state *ss,
}
for (i = 0; i < pkg->provides->num; i++)
name_to_ns(pkg->provides->item[i].name)->name_touched = 1;
pkg->provides->item[i].name->ss.name_touched = 1;
if (ns->locked) {
if (name->ss.locked) {
foreach_rinstall_if_pkg(ss, pkg, untrigger_install_if);
foreach_dependency(ss, pkg->depends, undo_constraint);
get_topology_score(ss, ns, pkg, &score);
get_topology_score(ss, pkg, &score);
subscore(&ss->score, &score);
unassign_name(ss, name);
@ -930,11 +860,11 @@ static void undo_decision(struct apk_solver_state *ss,
get_unassigned_score(name, &score);
subscore(&ss->score, &score);
} else {
ns->none_excluded = 0;
name->ss.none_excluded = 0;
}
/* Put back the name to unsolved list */
ns->locked = 0;
name->ss.locked = 0;
promote_name(ss, name);
}
}
@ -956,7 +886,7 @@ static solver_result_t push_decision(struct apk_solver_state *ss,
#ifdef DEBUG_CHECKS
d->saved_score = ss->score;
d->saved_requirers = name_to_ns(name)->requirers;
d->saved_requirers = name->ss.requirers;
#endif
d->type = primary_decision;
d->branching_point = branching_point;
@ -980,7 +910,6 @@ static int next_branch(struct apk_solver_state *ss)
while (ss->num_decisions > 0) {
struct apk_decision *d = &ss->decisions[ss->num_decisions];
struct apk_name *name = decision_to_name(d);
struct apk_name_state *ns = name_to_ns(name);
undo_decision(ss, d);
@ -989,7 +918,7 @@ static int next_branch(struct apk_solver_state *ss)
"ERROR! saved_score "SCORE_FMT" != score "SCORE_FMT,
SCORE_PRINTF(&d->saved_score),
SCORE_PRINTF(&ss->score));
ASSERT(d->saved_requirers == ns->requirers,
ASSERT(d->saved_requirers == name->ss.requirers,
"ERROR! requirers not restored between decisions");
#endif
@ -1001,8 +930,8 @@ static int next_branch(struct apk_solver_state *ss)
}
if (d->no_package && !d->found_solution) {
if (ns->last_touched_decision < backup_until)
backup_until = ns->last_touched_decision;
if (name->ss.last_touched_decision < backup_until)
backup_until = name->ss.last_touched_decision;
}
ss->num_decisions--;
@ -1016,26 +945,24 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency
{
struct apk_package *requirer_pkg = NULL;
struct apk_name *name = dep->name, *requirer_name = NULL;
struct apk_name_state *ns = name_to_ns(name), *requirer_ns = NULL;
int i, strength, changed = 0;
if (ss->num_decisions > 0) {
requirer_name = decision_to_name(&ss->decisions[ss->num_decisions]);
requirer_pkg = decision_to_pkg(&ss->decisions[ss->num_decisions]);
requirer_ns = name_to_ns(requirer_name);
strength = requirer_ns->requirers ?: 1;
strength = requirer_name->ss.requirers ?: 1;
} else {
strength = 1;
}
if (ns->locked) {
if (ns->chosen.pkg)
if (name->ss.locked) {
if (name->ss.chosen.pkg)
dbg_printf("%s: locked to " PKG_VER_FMT " already\n",
name->name, PKG_VER_PRINTF(ns->chosen.pkg));
name->name, PKG_VER_PRINTF(name->ss.chosen.pkg));
else
dbg_printf("%s: locked to empty\n",
name->name);
if (!apk_dep_is_provided(dep, &ns->chosen)) {
if (!apk_dep_is_provided(dep, &name->ss.chosen)) {
dbg_printf("%s: constraint violation %d\n",
name->name, strength);
ss->score.conflicts += strength;
@ -1079,10 +1006,10 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency
}
if (changed)
ns->last_touched_decision = ss->num_decisions;
name->ss.last_touched_decision = ss->num_decisions;
if (!dep->conflict)
ns->requirers += strength;
name->ss.requirers += strength;
promote_name(ss, name);
}
@ -1090,28 +1017,26 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency
static void undo_constraint(struct apk_solver_state *ss, struct apk_dependency *dep)
{
struct apk_name *name = dep->name, *requirer_name = NULL;
struct apk_name_state *ns = name_to_ns(name), *requirer_ns = NULL;
struct apk_package *requirer_pkg = NULL;
int i, strength;
if (ss->num_decisions > 0) {
requirer_name = decision_to_name(&ss->decisions[ss->num_decisions]);
requirer_pkg = decision_to_pkg(&ss->decisions[ss->num_decisions]);
requirer_ns = name_to_ns(requirer_name);
strength = requirer_ns->requirers ?: 1;
strength = requirer_name->ss.requirers ?: 1;
} else {
strength = 1;
}
if (ns->locked) {
if (ns->chosen.pkg != NULL) {
if (name->ss.locked) {
if (name->ss.chosen.pkg != NULL) {
dbg_printf(PKG_VER_FMT " selected already for %s\n",
PKG_VER_PRINTF(ns->chosen.pkg), name->name);
PKG_VER_PRINTF(name->ss.chosen.pkg), name->name);
} else {
dbg_printf("%s selected to not be satisfied\n",
name->name);
}
if (!apk_dep_is_provided(dep, &ns->chosen))
if (!apk_dep_is_provided(dep, &name->ss.chosen))
ss->score.conflicts -= strength;
return;
}
@ -1150,24 +1075,23 @@ static void undo_constraint(struct apk_solver_state *ss, struct apk_dependency *
* *previous* value, but that'd require keeping track
* of it which would require dynamic memory allocations.
* in practice this is good enough. */
if (ns->last_touched_decision > ss->num_decisions)
ns->last_touched_decision = ss->num_decisions;
if (name->ss.last_touched_decision > ss->num_decisions)
name->ss.last_touched_decision = ss->num_decisions;
if (!dep->conflict)
ns->requirers -= strength;
name->ss.requirers -= strength;
demote_name(ss, name);
}
static int reconsider_name(struct apk_solver_state *ss, struct apk_name *name)
{
struct apk_name_state *ns = name_to_ns(name);
struct apk_provider *next_p = NULL, *best_p = NULL;
unsigned int next_topology = 0, options = 0;
int i, j, score_locked = FALSE;
struct apk_score best_score = (struct apk_score) { .conflicts = -1 };
if (!ns->none_excluded) {
if (!name->ss.none_excluded) {
struct apk_score minscore;
get_unassigned_score(name, &minscore);
if (cmpscore2(&ss->score, &minscore, &ss->best_score) >= 0) {
@ -1193,16 +1117,16 @@ static int reconsider_name(struct apk_solver_state *ss, struct apk_name *name)
for (j = 0; j < pkg0->provides->num; j++) {
struct apk_dependency *p00 = &pkg0->provides->item[j];
if (!name_to_ns(p00->name)->locked)
if (!p00->name->ss.locked)
continue;
if (name_to_ns(p00->name)->chosen.version != &apk_null_blob ||
if (p00->name->ss.chosen.version != &apk_null_blob ||
p00->version != &apk_null_blob)
break;
}
if (j < pkg0->provides->num)
continue;
score_locked = get_topology_score(ss, ns, pkg0, &pkg0_score);
score_locked = get_topology_score(ss, pkg0, &pkg0_score);
/* viable alternative? */
if (cmpscore2(&ss->score, &pkg0_score, &ss->best_score) >= 0)
@ -1227,63 +1151,66 @@ static int reconsider_name(struct apk_solver_state *ss, struct apk_name *name)
if (options == 0) {
dbg_printf("reconsider_name: %s: no options locking none\n",
name->name);
if (ns->none_excluded)
if (name->ss.none_excluded)
return SOLVERR_PRUNED;
return push_decision(ss, name, NULL, DECISION_ASSIGN, BRANCH_NO, FALSE);
} else if (options == 1 && score_locked && ns->none_excluded && name == next_p->pkg->name) {
} else if (options == 1 && score_locked && name->ss.none_excluded && name == next_p->pkg->name) {
dbg_printf("reconsider_name: %s: only one choice left with known score, locking.\n",
name->name);
return push_decision(ss, name, next_p->pkg, DECISION_ASSIGN, BRANCH_NO, FALSE);
}
ns->chosen = *next_p;
ns->preferred_chosen = (best_p == next_p);
name->ss.chosen = *next_p;
name->ss.preferred_chosen = (best_p == next_p);
dbg_printf("reconsider_name: %s: next_pkg=%p [ version="BLOB_FMT" ]\n",
name->name, next_p->pkg, BLOB_PRINTF(*ns->chosen.version));
name->name, next_p->pkg, BLOB_PRINTF(*name->ss.chosen.version));
return SOLVERR_SOLUTION;
}
static int expand_branch(struct apk_solver_state *ss)
{
struct apk_name *name0, *name;
struct apk_name_state *ns;
struct apk_name *name0 = NULL, *name;
struct apk_package *pkg0 = NULL;
struct apk_package_state *ps0;
unsigned int r, topology0 = 0;
int primary_decision, branching_point;
int can_install = FALSE;
list_for_each_entry(ns, &ss->unsolved_list_head, unsolved_list) {
name = ns->name;
if (ns->name_touched) {
list_for_each_entry(name, &ss->unsolved_list_head, ss.unsolved_list) {
struct apk_package *cpkg;
struct apk_package_state *cps;
if (name->ss.name_touched) {
dbg_printf("%s: reconsidering things\n",
name->name);
r = reconsider_name(ss, name);
if (r != SOLVERR_SOLUTION)
return r;
ns->name_touched = 0;
name->ss.name_touched = 0;
}
if (pkg_to_ps(ns->chosen.pkg)->topology_soft < ss->topology_position &&
pkg_to_ps(ns->chosen.pkg)->topology_soft >= topology0) {
topology0 = pkg_to_ps(ns->chosen.pkg)->topology_soft;
} else if (ns->chosen.pkg->topology_hard >= topology0) {
topology0 = ns->chosen.pkg->topology_hard;
cpkg = name->ss.chosen.pkg;
cps = pkg_to_ps(cpkg);
if (cps->topology_soft < ss->topology_position &&
cps->topology_soft >= topology0) {
topology0 = cps->topology_soft;
} else if (cpkg->topology_hard >= topology0) {
topology0 = cpkg->topology_hard;
} else {
continue;
}
if (pkg0 != ns->chosen.pkg) {
if (pkg0 != cpkg) {
can_install = FALSE;
name0 = name;
}
pkg0 = ns->chosen.pkg;
if (ns->chosen.version != &apk_null_blob) {
pkg0 = cpkg;
if (name->ss.chosen.version != &apk_null_blob) {
can_install |= TRUE;
name0 = name;
}
}
if (pkg0 == NULL) {
if (name0 == NULL || pkg0 == NULL) {
dbg_printf("expand_branch: solution with score "SCORE_FMT"\n",
SCORE_PRINTF(&ss->score));
return SOLVERR_SOLUTION;
@ -1293,11 +1220,10 @@ static int expand_branch(struct apk_solver_state *ss)
* provider candidate */
ps0 = pkg_to_ps(pkg0);
name = name0;
ns = name_to_ns(name);
if (!ns->none_excluded) {
if (!name->ss.none_excluded) {
struct apk_package_state *ps0 = pkg_to_ps(pkg0);
if (ps0->incompat_dep > ns->requirers)
if (ps0->incompat_dep > name->ss.requirers)
primary_decision = DECISION_ASSIGN;
else
primary_decision = DECISION_EXCLUDE;
@ -1322,13 +1248,13 @@ static int expand_branch(struct apk_solver_state *ss)
branching_point = BRANCH_NO;
else
branching_point = BRANCH_YES;
} else if (ns->requirers == 0 && ns->install_ifs != 0 &&
} else if (name->ss.requirers == 0 && name->ss.install_ifs != 0 &&
install_if_missing(ss, pkg0)) {
/* not directly required, and package specific
* install_if never triggered */
primary_decision = DECISION_EXCLUDE;
branching_point = BRANCH_NO;
} else if (ns->preferred_chosen) {
} else if (name->ss.preferred_chosen) {
primary_decision = DECISION_ASSIGN;
branching_point = BRANCH_YES;
} else {
@ -1356,7 +1282,6 @@ static int get_tag(struct apk_database *db, unsigned short pinning_mask, unsigne
static void record_solution(struct apk_solver_state *ss)
{
struct apk_database *db = ss->db;
struct apk_name_state *ns;
int i, n;
apk_solution_array_resize(&ss->best_solution, ss->assigned_names);
@ -1364,6 +1289,7 @@ static void record_solution(struct apk_solver_state *ss)
n = 0;
for (i = ss->num_decisions; i > 0; i--) {
struct apk_decision *d = &ss->decisions[i];
struct apk_name *name = decision_to_name(d);
struct apk_package *pkg = decision_to_pkg(d);
struct apk_package_state *ps;
unsigned short pinning;
@ -1384,16 +1310,15 @@ static void record_solution(struct apk_solver_state *ss)
if (d->type != DECISION_ASSIGN)
continue;
ns = name_to_ns(pkg->name);
ps = pkg_to_ps(pkg);
pinning = ps->allowed_pinning | ns->preferred_pinning | APK_DEFAULT_PINNING_MASK;
pinning = ps->allowed_pinning | name->ss.preferred_pinning | APK_DEFAULT_PINNING_MASK;
repos = pkg->repos | (pkg->ipkg ? db->repo_tags[pkg->ipkg->repository_tag].allowed_repos : 0);
ASSERT(n < ss->assigned_names, "Name assignment overflow");
ss->best_solution->item[n++] = (struct apk_solution_entry){
.pkg = pkg,
.reinstall = ps->inherited_reinstall ||
((ns->solver_flags_local | ss->solver_flags) & APK_SOLVERF_REINSTALL),
((name->ss.solver_flags_local | ss->solver_flags) & APK_SOLVERF_REINSTALL),
.repository_tag = get_tag(db, pinning, repos),
};
}
@ -1437,7 +1362,6 @@ static int generate_changeset(struct apk_database *db,
unsigned short solver_flags)
{
struct apk_name *name;
struct apk_name_state *ns;
struct apk_package *pkg;
struct apk_installed_package *ipkg;
int i, num_installs = 0, num_removed = 0, ci = 0;
@ -1445,9 +1369,9 @@ static int generate_changeset(struct apk_database *db,
/* calculate change set size */
for (i = 0; i < solution->num; i++) {
pkg = solution->item[i].pkg;
ns = name_to_ns(pkg->name);
ns->chosen = APK_PROVIDER_FROM_PACKAGE(pkg);
ns->in_changeset = 1;
name = pkg->name;
name->ss.chosen = APK_PROVIDER_FROM_PACKAGE(pkg);
name->ss.in_changeset = 1;
if ((pkg->ipkg == NULL) ||
solution->item[i].reinstall ||
solution->item[i].repository_tag != pkg->ipkg->repository_tag)
@ -1455,8 +1379,7 @@ static int generate_changeset(struct apk_database *db,
}
list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
name = ipkg->pkg->name;
ns = name_to_ns(name);
if ((ns->chosen.pkg == NULL) || !ns->in_changeset)
if ((name->ss.chosen.pkg == NULL) || !name->ss.in_changeset)
num_removed++;
}
@ -1464,8 +1387,7 @@ static int generate_changeset(struct apk_database *db,
apk_change_array_resize(&changeset->changes, num_installs + num_removed);
list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
name = ipkg->pkg->name;
ns = name_to_ns(name);
if ((ns->chosen.pkg == NULL) || !ns->in_changeset) {
if ((name->ss.chosen.pkg == NULL) || !name->ss.in_changeset) {
changeset->changes->item[ci].oldpkg = ipkg->pkg;
ci++;
}
@ -1473,7 +1395,6 @@ static int generate_changeset(struct apk_database *db,
for (i = 0; i < solution->num; i++) {
pkg = solution->item[i].pkg;
name = pkg->name;
ns = name_to_ns(name);
if ((pkg->ipkg == NULL) ||
solution->item[i].reinstall ||
@ -1496,15 +1417,10 @@ static int generate_changeset(struct apk_database *db,
static int free_state(apk_hash_item item, void *ctx)
{
struct apk_name *name = (struct apk_name *) item;
struct apk_name_state *ns = (struct apk_name_state *) name->state_ptr;
if (ns != NULL) {
#ifdef DEBUG_CHECKS
ASSERT(ns->requirers == 0, "Requirers is not zero after cleanup");
#endif
free(ns);
name->state_ptr = NULL;
}
ASSERT(name->ss.requirers == 0, "Requirers is not zero after cleanup");
memset(&name->ss, 0, sizeof(name->ss));
return 0;
}
@ -1523,9 +1439,8 @@ void apk_solver_set_name_flags(struct apk_name *name,
unsigned short solver_flags,
unsigned short solver_flags_inheritable)
{
struct apk_name_state *ns = name_to_ns_alloc(name);
ns->solver_flags_local = solver_flags;
ns->solver_flags_inheritable = solver_flags & solver_flags_inheritable;
name->ss.solver_flags_local = solver_flags;
name->ss.solver_flags_inheritable = solver_flags & solver_flags_inheritable;
}
static void apk_solver_free(struct apk_database *db)
@ -1555,18 +1470,16 @@ int apk_solver_solve(struct apk_database *db,
for (i = 0; i < world->num; i++) {
struct apk_dependency *dep = &world->item[i];
struct apk_name *name = dep->name;
struct apk_name_state *ns = name_to_ns_alloc(name);
ns->in_world_dependency = 1;
dbg_printf("%s: adding pinnings %d\n", name->name, dep->repository_tag);
ns->preferred_pinning = BIT(dep->repository_tag);
name->ss.in_world_dependency = 1;
name->ss.preferred_pinning = BIT(dep->repository_tag);
sort_name(ss, name);
}
list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
sort_name(ss, ipkg->pkg->name);
name_to_ns(ipkg->pkg->name)->originally_installed = 1;
ipkg->pkg->name->ss.originally_installed = 1;
}
ss->max_decisions ++;