2011-07-26 13:56:55 +00:00
|
|
|
/* solver.c - Alpine Package Keeper (APK)
|
|
|
|
* A backtracking, forward checking dependency graph solver.
|
|
|
|
*
|
2012-02-18 09:54:17 +00:00
|
|
|
* Copyright (C) 2008-2012 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>
|
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
|
2012-02-24 16:27:55 +00:00
|
|
|
#define DEBUG_CHECKS
|
2012-02-18 09:54:17 +00:00
|
|
|
|
|
|
|
#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
|
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
#if defined(DEBUG_PRINT) || defined(DEBUG_CHECKS)
|
|
|
|
#define ASSERT(cond, fmt...) \
|
|
|
|
if (!(cond)) { fprintf(stderr, fmt); *(char*)NULL = 0; }
|
|
|
|
#else
|
|
|
|
#define ASSERT(cond, fmt...)
|
|
|
|
#endif
|
2011-10-14 17:48:20 +00:00
|
|
|
|
|
|
|
struct apk_score {
|
2012-02-21 09:01:21 +00:00
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
|
|
unsigned short preference;
|
2012-02-24 09:13:31 +00:00
|
|
|
unsigned short non_preferred_pinnings;
|
2012-02-21 09:01:21 +00:00
|
|
|
unsigned short non_preferred_actions;
|
2012-02-24 09:13:31 +00:00
|
|
|
unsigned short conflicts;
|
2012-02-21 09:01:21 +00:00
|
|
|
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
2012-02-24 09:13:31 +00:00
|
|
|
unsigned short conflicts;
|
2012-02-21 09:01:21 +00:00
|
|
|
unsigned short non_preferred_actions;
|
2012-02-24 09:13:31 +00:00
|
|
|
unsigned short non_preferred_pinnings;
|
2012-02-21 09:01:21 +00:00
|
|
|
unsigned short preference;
|
|
|
|
#else
|
|
|
|
#error Unknown endianess.
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
uint64_t score;
|
|
|
|
};
|
2011-10-14 17:48:20 +00:00
|
|
|
};
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-24 09:13:31 +00:00
|
|
|
#define SCORE_FMT "{%d/%d/%d,%d}"
|
|
|
|
#define SCORE_PRINTF(s) (s)->conflicts, (s)->non_preferred_actions, (s)->non_preferred_pinnings, (s)->preference
|
2012-02-18 09:54:17 +00:00
|
|
|
|
|
|
|
enum {
|
|
|
|
DECISION_ASSIGN = 0,
|
|
|
|
DECISION_EXCLUDE
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
BRANCH_NO,
|
|
|
|
BRANCH_YES,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct apk_decision {
|
|
|
|
union {
|
|
|
|
struct apk_name *name;
|
|
|
|
struct apk_package *pkg;
|
|
|
|
};
|
|
|
|
#ifdef DEBUG_CHECKS
|
|
|
|
struct apk_score saved_score;
|
2012-02-22 07:14:46 +00:00
|
|
|
unsigned short saved_requirers;
|
2012-02-18 09:54:17 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
unsigned no_package : 1;
|
|
|
|
unsigned type : 1;
|
|
|
|
unsigned branching_point : 1;
|
|
|
|
unsigned topology_position : 1;
|
2012-02-20 18:54:03 +00:00
|
|
|
unsigned found_solution : 1;
|
2012-02-18 09:54:17 +00:00
|
|
|
};
|
|
|
|
|
2011-07-26 13:56:55 +00:00
|
|
|
struct apk_package_state {
|
2011-09-14 13:48:28 +00:00
|
|
|
unsigned int topology_soft;
|
2011-07-26 13:56:55 +00:00
|
|
|
unsigned short conflicts;
|
2012-02-18 09:54:17 +00:00
|
|
|
unsigned char preference;
|
2012-02-16 19:46:09 +00:00
|
|
|
unsigned handle_install_if : 1;
|
|
|
|
unsigned locked : 1;
|
2011-07-26 13:56:55 +00:00
|
|
|
};
|
|
|
|
|
2012-02-24 13:50:39 +00:00
|
|
|
#define CHOSEN_NONE (struct apk_provider) { .pkg = NULL, .version = NULL }
|
|
|
|
|
2011-07-26 13:56:55 +00:00
|
|
|
struct apk_name_state {
|
|
|
|
struct list_head unsolved_list;
|
2011-09-22 10:09:23 +00:00
|
|
|
struct apk_name *name;
|
2012-02-24 13:50:39 +00:00
|
|
|
struct apk_provider chosen;
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2012-01-17 11:54:33 +00:00
|
|
|
struct apk_score minimum_penalty;
|
2011-07-26 13:56:55 +00:00
|
|
|
unsigned short requirers;
|
2011-08-17 13:55:35 +00:00
|
|
|
unsigned short install_ifs;
|
2011-09-16 14:10:50 +00:00
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
/* set on startup */
|
2012-02-15 11:57:36 +00:00
|
|
|
unsigned short preferred_pinning;
|
2012-02-20 18:54:03 +00:00
|
|
|
unsigned short maybe_pinning;
|
|
|
|
|
|
|
|
/* dynamic */
|
|
|
|
unsigned int last_touched_decision;
|
2012-02-15 11:57:36 +00:00
|
|
|
unsigned short allowed_pinning;
|
2012-02-20 18:54:03 +00:00
|
|
|
unsigned short inherited_pinning[APK_MAX_TAGS];
|
|
|
|
unsigned short inherited_upgrade;
|
|
|
|
unsigned short inherited_reinstall;
|
2012-02-15 11:57:36 +00:00
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
/* one time prepare/finish flags */
|
2012-02-18 09:54:17 +00:00
|
|
|
unsigned solver_flags_local : 4;
|
|
|
|
unsigned solver_flags_local_mask : 4;
|
2012-02-20 18:54:03 +00:00
|
|
|
unsigned solver_flags_maybe : 4;
|
2011-09-16 14:10:50 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
unsigned decision_counted : 1;
|
|
|
|
unsigned originally_installed : 1;
|
2012-02-20 18:54:03 +00:00
|
|
|
unsigned has_available_pkgs : 1;
|
2012-02-18 09:54:17 +00:00
|
|
|
unsigned in_changeset : 1;
|
2012-02-22 07:14:46 +00:00
|
|
|
unsigned in_world_dependency : 1;
|
2012-02-18 09:54:17 +00:00
|
|
|
|
|
|
|
/* dynamic state flags */
|
|
|
|
unsigned none_excluded : 1;
|
|
|
|
unsigned locked : 1;
|
2012-02-20 18:54:03 +00:00
|
|
|
unsigned name_touched : 1;
|
2011-07-26 13:56:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct apk_solver_state {
|
|
|
|
struct apk_database *db;
|
2012-02-18 09:54:17 +00:00
|
|
|
struct apk_decision *decisions;
|
2011-08-05 08:53:26 +00:00
|
|
|
|
2011-07-26 13:56:55 +00:00
|
|
|
struct list_head unsolved_list_head;
|
2012-02-18 09:54:17 +00:00
|
|
|
|
|
|
|
unsigned int num_topology_positions;
|
|
|
|
unsigned int num_decisions, max_decisions;
|
2011-07-26 13:56:55 +00:00
|
|
|
unsigned int topology_position;
|
|
|
|
unsigned int assigned_names;
|
2011-09-16 14:10:50 +00:00
|
|
|
|
2012-02-16 08:50:52 +00:00
|
|
|
struct apk_solution_array *best_solution;
|
2012-02-18 09:54:17 +00:00
|
|
|
|
|
|
|
struct apk_score score;
|
|
|
|
struct apk_score minimum_penalty;
|
2011-10-14 17:48:20 +00:00
|
|
|
struct apk_score best_score;
|
2012-02-18 09:54:17 +00:00
|
|
|
|
|
|
|
unsigned solver_flags : 4;
|
2011-07-26 13:56:55 +00:00
|
|
|
};
|
|
|
|
|
2012-02-16 18:37:03 +00:00
|
|
|
typedef enum {
|
|
|
|
SOLVERR_SOLUTION = 0,
|
|
|
|
SOLVERR_PRUNED,
|
|
|
|
SOLVERR_EXPAND,
|
|
|
|
SOLVERR_STOP,
|
|
|
|
} solver_result_t;
|
|
|
|
|
2011-07-30 17:59:47 +00:00
|
|
|
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);
|
2012-02-18 09:54:17 +00:00
|
|
|
static solver_result_t push_decision(struct apk_solver_state *ss,
|
|
|
|
struct apk_name *name,
|
|
|
|
struct apk_package *pkg,
|
|
|
|
int primary_decision,
|
|
|
|
int branching_point,
|
|
|
|
int topology_position);
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-22 10:59:46 +00:00
|
|
|
#ifdef DEBUG_CHECKS
|
2012-01-17 11:54:33 +00:00
|
|
|
static void addscore(struct apk_score *a, struct apk_score *b)
|
|
|
|
{
|
2012-02-22 10:59:46 +00:00
|
|
|
struct apk_score orig = *a;
|
|
|
|
a->score += b->score;
|
|
|
|
ASSERT(a->conflicts >= orig.conflicts, "Conflict overflow");
|
|
|
|
ASSERT(a->non_preferred_actions >= orig.non_preferred_actions, "Preferred action overflow");
|
|
|
|
ASSERT(a->preference >= orig.preference, "Preference overflow");
|
2012-01-17 11:54:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void subscore(struct apk_score *a, struct apk_score *b)
|
|
|
|
{
|
2012-02-22 10:59:46 +00:00
|
|
|
struct apk_score orig = *a;
|
|
|
|
a->score -= b->score;
|
|
|
|
ASSERT(a->conflicts <= orig.conflicts, "Conflict underflow");
|
|
|
|
ASSERT(a->non_preferred_actions <= orig.non_preferred_actions, "Preferred action underflow");
|
|
|
|
ASSERT(a->preference <= orig.preference, "Preference underflow");
|
2012-02-18 09:54:17 +00:00
|
|
|
}
|
2012-02-21 09:01:21 +00:00
|
|
|
#else
|
|
|
|
static void addscore(struct apk_score *a, struct apk_score *b)
|
|
|
|
{
|
|
|
|
a->score += b->score;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void subscore(struct apk_score *a, struct apk_score *b)
|
|
|
|
{
|
|
|
|
a->score -= b->score;
|
|
|
|
}
|
2012-02-22 10:59:46 +00:00
|
|
|
#endif
|
2012-02-21 09:01:21 +00:00
|
|
|
|
|
|
|
static inline int cmpscore(struct apk_score *a, struct apk_score *b)
|
|
|
|
{
|
|
|
|
if (a->score < b->score) return -1;
|
|
|
|
if (a->score > b->score) return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int cmpscore2(struct apk_score *a1, struct apk_score *a2, struct apk_score *b)
|
|
|
|
{
|
|
|
|
struct apk_score a;
|
|
|
|
a.score = a1->score + a2->score;
|
|
|
|
if (a.score < b->score) return -1;
|
|
|
|
if (a.score > b->score) return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
2012-02-18 09:54:17 +00:00
|
|
|
|
|
|
|
static struct apk_name *decision_to_name(struct apk_decision *d)
|
|
|
|
{
|
|
|
|
if (d->no_package)
|
|
|
|
return d->name;
|
|
|
|
return d->pkg->name;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct apk_package *decision_to_pkg(struct apk_decision *d)
|
|
|
|
{
|
|
|
|
if (d->no_package)
|
|
|
|
return NULL;
|
|
|
|
return d->pkg;
|
2012-01-17 11:54:33 +00:00
|
|
|
}
|
|
|
|
|
2011-09-09 13:31:11 +00:00
|
|
|
static struct apk_package_state *pkg_to_ps(struct apk_package *pkg)
|
2011-08-05 08:53:26 +00:00
|
|
|
{
|
|
|
|
return (struct apk_package_state*) pkg->state_ptr;
|
|
|
|
}
|
|
|
|
|
2011-09-09 13:31:11 +00:00
|
|
|
static struct apk_name_state *name_to_ns(struct apk_name *name)
|
2012-02-20 18:54:03 +00:00
|
|
|
{
|
|
|
|
return (struct apk_name_state*) name->state_ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct apk_name_state *name_to_ns_alloc(struct apk_name *name)
|
2011-09-09 13:31:11 +00:00
|
|
|
{
|
2011-09-22 10:09:23 +00:00
|
|
|
struct apk_name_state *ns;
|
2012-02-20 18:54:03 +00:00
|
|
|
int i;
|
2011-09-22 10:09:23 +00:00
|
|
|
|
|
|
|
if (name->state_ptr == NULL) {
|
|
|
|
ns = calloc(1, sizeof(struct apk_name_state));
|
|
|
|
ns->name = name;
|
2012-02-24 13:50:39 +00:00
|
|
|
for (i = 0; i < name->providers->num; i++) {
|
|
|
|
if (name->providers->item[i].pkg->repos != 0) {
|
2012-02-20 18:54:03 +00:00
|
|
|
ns->has_available_pkgs = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-09-22 10:09:23 +00:00
|
|
|
name->state_ptr = ns;
|
|
|
|
} else {
|
|
|
|
ns = (struct apk_name_state*) name->state_ptr;
|
|
|
|
}
|
|
|
|
return ns;
|
2011-09-09 13:31:11 +00:00
|
|
|
}
|
|
|
|
|
2012-02-22 08:28:02 +00:00
|
|
|
static inline int pkg_available(struct apk_database *db, struct apk_package *pkg)
|
2011-07-26 13:56:55 +00:00
|
|
|
{
|
2012-02-22 06:45:40 +00:00
|
|
|
/* virtual packages - only deps used; no real .apk */
|
2011-07-26 13:56:55 +00:00
|
|
|
if (pkg->installed_size == 0)
|
|
|
|
return TRUE;
|
2012-02-22 06:45:40 +00:00
|
|
|
/* obviously present */
|
2012-02-22 08:28:02 +00:00
|
|
|
if (pkg->in_cache || pkg->filename != NULL || (pkg->repos & db->local_repos))
|
2012-02-22 06:45:40 +00:00
|
|
|
return TRUE;
|
|
|
|
/* can download */
|
|
|
|
if ((pkg->repos & ~db->bad_repos) && !(apk_flags & APK_NO_NETWORK))
|
2011-07-26 13:56:55 +00:00
|
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2011-08-17 13:55:35 +00:00
|
|
|
static void foreach_dependency_pkg(
|
|
|
|
struct apk_solver_state *ss, struct apk_dependency_array *depends,
|
|
|
|
void (*cb)(struct apk_solver_state *ss, struct apk_package *dependency))
|
2011-08-05 08:53:26 +00:00
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
|
2011-08-17 13:55:35 +00:00
|
|
|
/* And sort the main dependencies */
|
|
|
|
for (i = 0; i < depends->num; i++) {
|
|
|
|
struct apk_dependency *dep = &depends->item[i];
|
2011-08-05 08:53:26 +00:00
|
|
|
struct apk_name *name0 = dep->name;
|
|
|
|
|
2012-02-24 13:50:39 +00:00
|
|
|
for (j = 0; j < name0->providers->num; j++) {
|
|
|
|
struct apk_provider *p0 = &name0->providers->item[j];
|
2011-08-17 13:55:35 +00:00
|
|
|
|
2011-08-05 08:53:26 +00:00
|
|
|
/* conflict depends on all to be not installed */
|
2012-02-24 13:50:39 +00:00
|
|
|
if (!apk_dep_is_provided(dep, p0))
|
2011-08-05 08:53:26 +00:00
|
|
|
continue;
|
2011-08-17 13:55:35 +00:00
|
|
|
|
2012-02-24 13:50:39 +00:00
|
|
|
cb(ss, p0->pkg);
|
2011-08-05 08:53:26 +00:00
|
|
|
}
|
|
|
|
}
|
2011-08-17 13:55:35 +00:00
|
|
|
}
|
2011-08-05 08:53:26 +00:00
|
|
|
|
2011-08-17 13:55:35 +00:00
|
|
|
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_name *name = pkg->name;
|
|
|
|
int i, j, k;
|
|
|
|
|
|
|
|
for (i = 0; i < pkg->name->rinstall_if->num; i++) {
|
|
|
|
struct apk_name *name0 = pkg->name->rinstall_if->item[i];
|
|
|
|
|
|
|
|
dbg_printf(PKG_VER_FMT ": rinstall_if %s\n",
|
|
|
|
PKG_VER_PRINTF(pkg), name0->name);
|
2011-08-05 08:53:26 +00:00
|
|
|
|
2012-02-24 13:50:39 +00:00
|
|
|
for (j = 0; j < name0->providers->num; j++) {
|
|
|
|
struct apk_provider *p0 = &name0->providers->item[j];
|
|
|
|
|
|
|
|
for (k = 0; k < p0->pkg->install_if->num; k++) {
|
|
|
|
struct apk_dependency *dep = &p0->pkg->install_if->item[k];
|
2011-08-17 13:55:35 +00:00
|
|
|
|
|
|
|
if (dep->name == name &&
|
2012-02-24 13:50:39 +00:00
|
|
|
apk_dep_is_provided(dep, p0))
|
2011-08-17 13:55:35 +00:00
|
|
|
break;
|
|
|
|
}
|
2012-02-24 13:50:39 +00:00
|
|
|
if (k >= p0->pkg->install_if->num)
|
2011-08-17 13:55:35 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* pkg depends (via install_if) on pkg0 */
|
2012-02-24 13:50:39 +00:00
|
|
|
cb(ss, p0->pkg);
|
2011-08-17 13:55:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
static unsigned int get_pinning_mask_repos(struct apk_database *db, unsigned short pinning_mask)
|
|
|
|
{
|
|
|
|
unsigned int repository_mask = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < db->num_repo_tags && pinning_mask; i++) {
|
|
|
|
if (!(BIT(i) & pinning_mask))
|
|
|
|
continue;
|
|
|
|
pinning_mask &= ~BIT(i);
|
|
|
|
repository_mask |= db->repo_tags[i].allowed_repos;
|
|
|
|
}
|
|
|
|
return repository_mask;
|
|
|
|
}
|
|
|
|
|
2012-02-22 11:09:51 +00:00
|
|
|
static int get_topology_score(
|
2012-02-18 09:54:17 +00:00
|
|
|
struct apk_solver_state *ss,
|
|
|
|
struct apk_name_state *ns,
|
|
|
|
struct apk_package *pkg,
|
|
|
|
struct apk_score *_score)
|
|
|
|
{
|
|
|
|
struct apk_package_state *ps = pkg_to_ps(pkg);
|
|
|
|
struct apk_score score;
|
|
|
|
unsigned int repos;
|
|
|
|
unsigned short preferred_pinning, allowed_pinning;
|
|
|
|
unsigned int preferred_repos, allowed_repos;
|
2012-02-24 14:27:51 +00:00
|
|
|
int score_locked = TRUE, sticky_installed = FALSE;
|
2012-02-18 09:54:17 +00:00
|
|
|
|
|
|
|
score = (struct apk_score) {
|
|
|
|
.conflicts = ps->conflicts,
|
|
|
|
.preference = ps->preference,
|
|
|
|
};
|
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
if (ss->solver_flags & APK_SOLVERF_AVAILABLE) {
|
2012-02-24 06:42:40 +00:00
|
|
|
/* available preferred */
|
2012-02-20 18:54:03 +00:00
|
|
|
if ((pkg->repos == 0) && ns->has_available_pkgs)
|
|
|
|
score.non_preferred_actions++;
|
2012-02-22 08:28:02 +00:00
|
|
|
} else if (ns->inherited_reinstall ||
|
|
|
|
(((ns->solver_flags_local|ss->solver_flags) & APK_SOLVERF_REINSTALL))) {
|
|
|
|
/* reinstall requested, but not available */
|
|
|
|
if (!pkg_available(ss->db, pkg))
|
|
|
|
score.non_preferred_actions++;
|
2012-02-24 06:42:40 +00:00
|
|
|
} else if (ns->inherited_upgrade ||
|
|
|
|
((ns->solver_flags_local|ss->solver_flags) & APK_SOLVERF_UPGRADE)) {
|
|
|
|
/* upgrading - score is just locked here */
|
2012-02-22 08:28:02 +00:00
|
|
|
} else if ((ns->inherited_upgrade == 0) &&
|
2012-02-20 18:54:03 +00:00
|
|
|
((ns->solver_flags_local|ss->solver_flags) & APK_SOLVERF_UPGRADE) == 0 &&
|
|
|
|
((ns->solver_flags_maybe & APK_SOLVERF_UPGRADE) == 0 || (ps->locked))) {
|
2012-02-18 09:54:17 +00:00
|
|
|
/* not upgrading: it is not preferred to change package */
|
|
|
|
if (pkg->ipkg == NULL && ns->originally_installed)
|
|
|
|
score.non_preferred_actions++;
|
2012-02-24 14:27:51 +00:00
|
|
|
else
|
|
|
|
sticky_installed = TRUE;
|
2012-02-22 11:09:51 +00:00
|
|
|
} else {
|
|
|
|
score_locked = FALSE;
|
2012-02-18 09:54:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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_repos = get_pinning_mask_repos(ss->db, preferred_pinning);
|
|
|
|
|
|
|
|
if (!(repos & preferred_repos))
|
2012-02-24 09:13:31 +00:00
|
|
|
score.non_preferred_pinnings++;
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2012-02-24 16:27:55 +00:00
|
|
|
if (ps->locked || (ns->allowed_pinning | ns->maybe_pinning) == ns->allowed_pinning) {
|
2012-02-22 06:45:40 +00:00
|
|
|
allowed_pinning = ns->allowed_pinning | preferred_pinning | APK_DEFAULT_PINNING_MASK;
|
2012-02-18 09:54:17 +00:00
|
|
|
allowed_repos = get_pinning_mask_repos(ss->db, allowed_pinning);
|
2012-02-24 14:27:51 +00:00
|
|
|
if (!(repos & allowed_repos)) {
|
|
|
|
if (sticky_installed)
|
|
|
|
score.non_preferred_actions++;
|
2012-02-24 09:13:31 +00:00
|
|
|
score.non_preferred_pinnings += 16;
|
2012-02-24 14:27:51 +00:00
|
|
|
}
|
2012-02-22 11:09:51 +00:00
|
|
|
} else {
|
|
|
|
score_locked = FALSE;
|
2012-02-18 09:54:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
*_score = score;
|
2012-02-22 11:09:51 +00:00
|
|
|
return score_locked;
|
2012-02-18 09:54:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int is_topology_optimum(struct apk_solver_state *ss,
|
|
|
|
struct apk_package *pkg)
|
|
|
|
{
|
|
|
|
struct apk_name *name = pkg->name;
|
|
|
|
struct apk_name_state *ns = name_to_ns(name);
|
|
|
|
struct apk_score score;
|
|
|
|
int i;
|
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
get_topology_score(ss, ns, pkg, &score);
|
2012-02-24 13:50:39 +00:00
|
|
|
for (i = 0; i < name->providers->num; i++) {
|
|
|
|
struct apk_package *pkg0 = name->providers->item[i].pkg;
|
2012-02-18 09:54:17 +00:00
|
|
|
struct apk_package_state *ps0 = pkg_to_ps(pkg0);
|
|
|
|
struct apk_score score0;
|
|
|
|
|
|
|
|
if (pkg0 == pkg)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (ps0 == NULL || ps0->locked ||
|
|
|
|
ss->topology_position < pkg->topology_hard)
|
|
|
|
continue;
|
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
get_topology_score(ss, ns, pkg0, &score0);
|
2012-02-18 09:54:17 +00:00
|
|
|
if (cmpscore(&score0, &score) < 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int compare_absolute_package_preference(
|
2012-02-24 13:50:39 +00:00
|
|
|
struct apk_provider *pA,
|
|
|
|
struct apk_provider *pB)
|
2012-02-18 09:54:17 +00:00
|
|
|
{
|
2012-02-24 13:50:39 +00:00
|
|
|
struct apk_package *pkgA = pA->pkg;
|
|
|
|
struct apk_package *pkgB = pB->pkg;
|
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
/* specified on command line directly */
|
|
|
|
if (pkgA->filename && !pkgB->filename)
|
|
|
|
return 1;
|
|
|
|
if (pkgB->filename && !pkgA->filename)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* upgrading, or neither of the package is installed, so
|
|
|
|
* we just fall back comparing to versions */
|
2012-02-24 13:50:39 +00:00
|
|
|
switch (apk_version_compare_blob(*pA->version, *pB->version)) {
|
2012-02-18 09:54:17 +00:00
|
|
|
case APK_VERSION_GREATER:
|
|
|
|
return 1;
|
|
|
|
case APK_VERSION_LESS:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* prefer the installed package */
|
|
|
|
if (pkgA->ipkg != NULL && pkgB->ipkg == NULL)
|
|
|
|
return 1;
|
|
|
|
if (pkgB->ipkg != NULL && pkgA->ipkg == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* prefer the one with lowest available repository */
|
|
|
|
return ffsl(pkgB->repos) - ffsl(pkgA->repos);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void calculate_pkg_preference(struct apk_package *pkg)
|
|
|
|
{
|
|
|
|
struct apk_name *name = pkg->name;
|
|
|
|
struct apk_package_state *ps = pkg_to_ps(pkg);
|
2012-02-24 13:50:39 +00:00
|
|
|
struct apk_provider p = APK_PROVIDER_FROM_PACKAGE(pkg);
|
2012-02-18 09:54:17 +00:00
|
|
|
int i;
|
|
|
|
|
2012-02-24 13:50:39 +00:00
|
|
|
for (i = 0; i < name->providers->num; i++) {
|
|
|
|
struct apk_provider *p0 = &name->providers->item[i];
|
|
|
|
if (pkg == p0->pkg)
|
2012-02-18 09:54:17 +00:00
|
|
|
continue;
|
2012-02-24 13:50:39 +00:00
|
|
|
if (compare_absolute_package_preference(&p, p0) < 0)
|
2012-02-18 09:54:17 +00:00
|
|
|
ps->preference++;
|
|
|
|
}
|
2012-02-24 13:50:39 +00:00
|
|
|
/* FIXME: consider all provided names too */
|
2012-02-18 09:54:17 +00:00
|
|
|
}
|
|
|
|
|
2012-02-24 16:27:55 +00:00
|
|
|
static void count_name(struct apk_solver_state *ss, struct apk_name *name)
|
2012-02-22 06:45:40 +00:00
|
|
|
{
|
2012-02-24 16:27:55 +00:00
|
|
|
struct apk_name_state *ns = name_to_ns_alloc(name);
|
|
|
|
|
2012-02-22 06:45:40 +00:00
|
|
|
if (!ns->decision_counted) {
|
|
|
|
ss->max_decisions++;
|
|
|
|
ns->decision_counted = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-17 13:55:35 +00:00
|
|
|
static void sort_hard_dependencies(struct apk_solver_state *ss, struct apk_package *pkg)
|
|
|
|
{
|
|
|
|
struct apk_package_state *ps;
|
2012-02-24 16:27:55 +00:00
|
|
|
int i;
|
2011-08-17 13:55:35 +00:00
|
|
|
|
|
|
|
if (pkg->state_ptr == NULL)
|
|
|
|
pkg->state_ptr = calloc(1, sizeof(struct apk_package_state));
|
|
|
|
|
|
|
|
ps = pkg_to_ps(pkg);
|
2011-09-15 06:48:51 +00:00
|
|
|
if (ps->topology_soft)
|
2011-08-17 13:55:35 +00:00
|
|
|
return;
|
2011-09-14 13:48:28 +00:00
|
|
|
pkg->topology_hard = -1;
|
2011-09-15 06:48:51 +00:00
|
|
|
ps->topology_soft = -1;
|
2011-08-17 13:55:35 +00:00
|
|
|
|
2012-02-22 06:45:40 +00:00
|
|
|
calculate_pkg_preference(pkg);
|
2011-08-17 13:55:35 +00:00
|
|
|
/* Consider hard dependencies only */
|
|
|
|
foreach_dependency_pkg(ss, pkg->depends, sort_hard_dependencies);
|
|
|
|
foreach_dependency_pkg(ss, pkg->install_if, sort_hard_dependencies);
|
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
ss->max_decisions++;
|
2012-02-24 16:27:55 +00:00
|
|
|
count_name(ss, pkg->name);
|
|
|
|
for (i = 0; i < pkg->provides->num; i++)
|
|
|
|
count_name(ss, pkg->provides->item[i].name);
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2011-09-14 13:48:28 +00:00
|
|
|
ps->topology_soft = pkg->topology_hard = ++ss->num_topology_positions;
|
2011-08-17 13:55:35 +00:00
|
|
|
dbg_printf(PKG_VER_FMT ": topology_hard=%d\n",
|
2011-09-14 13:48:28 +00:00
|
|
|
PKG_VER_PRINTF(pkg), pkg->topology_hard);
|
2011-08-17 13:55:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void sort_soft_dependencies(struct apk_solver_state *ss, struct apk_package *pkg)
|
|
|
|
{
|
|
|
|
struct apk_package_state *ps;
|
|
|
|
|
|
|
|
sort_hard_dependencies(ss, pkg);
|
|
|
|
|
|
|
|
ps = pkg_to_ps(pkg);
|
2011-09-14 13:48:28 +00:00
|
|
|
if (ps->topology_soft != pkg->topology_hard)
|
2011-08-17 13:55:35 +00:00
|
|
|
return;
|
2011-09-09 13:31:11 +00:00
|
|
|
ps->topology_soft = -1;
|
2011-08-17 13:55:35 +00:00
|
|
|
|
|
|
|
/* Soft reverse dependencies aka. install_if */
|
|
|
|
foreach_rinstall_if_pkg(ss, pkg, sort_hard_dependencies);
|
|
|
|
foreach_dependency_pkg(ss, pkg->depends, sort_soft_dependencies);
|
|
|
|
|
|
|
|
/* Assign a topology sorting order */
|
|
|
|
ps->topology_soft = ++ss->num_topology_positions;
|
|
|
|
dbg_printf(PKG_VER_FMT ": topology_soft=%d\n",
|
|
|
|
PKG_VER_PRINTF(pkg), ps->topology_soft);
|
2011-08-05 08:53:26 +00:00
|
|
|
}
|
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
static void recalculate_maybe(struct apk_solver_state *ss, struct apk_name *name,
|
|
|
|
unsigned short flags, unsigned short pinning)
|
2011-09-16 14:10:50 +00:00
|
|
|
{
|
2012-02-20 18:54:03 +00:00
|
|
|
struct apk_name_state *ns = name_to_ns_alloc(name);
|
|
|
|
int propagate = FALSE;
|
2011-09-16 14:10:50 +00:00
|
|
|
int i, j;
|
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
if ((ns->maybe_pinning & pinning) != pinning) {
|
|
|
|
ns->maybe_pinning |= pinning;
|
|
|
|
propagate = TRUE;
|
|
|
|
}
|
|
|
|
if ((ns->solver_flags_maybe & flags) != flags) {
|
|
|
|
ns->solver_flags_maybe |= flags;
|
|
|
|
propagate = TRUE;
|
|
|
|
}
|
|
|
|
if (!propagate)
|
2011-09-16 14:10:50 +00:00
|
|
|
return;
|
|
|
|
|
2012-02-24 13:50:39 +00:00
|
|
|
for (i = 0; i < name->providers->num; i++) {
|
|
|
|
struct apk_package *pkg = name->providers->item[i].pkg;
|
2011-09-16 14:10:50 +00:00
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
for (j = 0; j < pkg->depends->num; j++) {
|
|
|
|
struct apk_dependency *dep = &pkg->depends->item[j];
|
|
|
|
struct apk_name *name0 = dep->name;
|
|
|
|
recalculate_maybe(ss, name0, flags, pinning);
|
2011-09-16 14:10:50 +00:00
|
|
|
}
|
2012-02-20 18:54:03 +00:00
|
|
|
}
|
2011-09-16 14:10:50 +00:00
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
for (i = 0; i < name->rinstall_if->num; i++) {
|
|
|
|
struct apk_name *name0 = name->rinstall_if->item[i];
|
|
|
|
recalculate_maybe(ss, name0, flags, pinning);
|
2011-09-16 14:10:50 +00:00
|
|
|
}
|
2011-07-27 19:10:44 +00:00
|
|
|
}
|
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
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;
|
|
|
|
|
2012-02-24 13:50:39 +00:00
|
|
|
for (i = 0; i < name->providers->num; i++)
|
|
|
|
sort_soft_dependencies(ss, name->providers->item[i].pkg);
|
2012-02-20 18:54:03 +00:00
|
|
|
|
2012-02-24 16:27:55 +00:00
|
|
|
count_name(ss, name);
|
2012-02-20 18:54:03 +00:00
|
|
|
recalculate_maybe(ss, name,
|
|
|
|
ns->solver_flags_local & ns->solver_flags_local_mask,
|
|
|
|
ns->maybe_pinning);
|
|
|
|
}
|
|
|
|
|
2011-07-30 17:59:47 +00:00
|
|
|
static void foreach_dependency(struct apk_solver_state *ss, struct apk_dependency_array *deps,
|
|
|
|
void (*func)(struct apk_solver_state *ss, struct apk_dependency *dep))
|
2011-07-26 13:56:55 +00:00
|
|
|
{
|
2011-07-30 17:59:47 +00:00
|
|
|
int i;
|
2011-07-26 13:56:55 +00:00
|
|
|
for (i = 0; i < deps->num; i++)
|
2011-07-30 17:59:47 +00:00
|
|
|
func(ss, &deps->item[i]);
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
|
|
|
|
2011-08-17 13:55:35 +00:00
|
|
|
static int install_if_missing(struct apk_solver_state *ss, struct apk_package *pkg)
|
|
|
|
{
|
2011-09-09 13:31:11 +00:00
|
|
|
struct apk_name_state *ns;
|
2011-08-17 13:55:35 +00:00
|
|
|
int i, missing = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < pkg->install_if->num; i++) {
|
|
|
|
struct apk_dependency *dep = &pkg->install_if->item[i];
|
|
|
|
|
2011-09-09 13:31:11 +00:00
|
|
|
ns = name_to_ns(dep->name);
|
2012-02-22 06:45:40 +00:00
|
|
|
|
|
|
|
/* ns can be NULL, if the install_if has a name with
|
|
|
|
* no packages */
|
2012-02-24 13:50:39 +00:00
|
|
|
if (ns == NULL || !ns->locked ||
|
|
|
|
!apk_dep_is_provided(dep, &ns->chosen))
|
2011-08-17 13:55:35 +00:00
|
|
|
missing++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return missing;
|
|
|
|
}
|
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
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){
|
2012-02-22 07:08:14 +00:00
|
|
|
.conflicts = ns->requirers,
|
2012-02-24 13:50:39 +00:00
|
|
|
.preference = name->providers->num,
|
2012-02-18 09:54:17 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2012-02-22 10:59:46 +00:00
|
|
|
static void promote_name(struct apk_solver_state *ss, struct apk_name *name)
|
2011-07-26 13:56:55 +00:00
|
|
|
{
|
2011-09-22 10:09:23 +00:00
|
|
|
struct apk_name_state *ns = name_to_ns(name);
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-16 18:37:03 +00:00
|
|
|
if (ns->locked)
|
2012-02-22 10:59:46 +00:00
|
|
|
return;
|
2012-02-16 18:37:03 +00:00
|
|
|
|
2012-02-22 10:59:46 +00:00
|
|
|
/* queue for handling if needed */
|
|
|
|
if (!list_hashed(&ns->unsolved_list))
|
|
|
|
list_add_tail(&ns->unsolved_list, &ss->unsolved_list_head);
|
2012-01-17 11:54:33 +00:00
|
|
|
|
2012-02-22 10:59:46 +00:00
|
|
|
/* update info, but no cached information flush is required, as
|
|
|
|
* minimum_penalty can only go up */
|
2012-02-20 18:54:03 +00:00
|
|
|
ns->name_touched = 1;
|
2012-02-22 10:59:46 +00:00
|
|
|
}
|
2012-02-20 18:54:03 +00:00
|
|
|
|
2012-02-22 10:59:46 +00:00
|
|
|
static void demote_name(struct apk_solver_state *ss, struct apk_name *name)
|
|
|
|
{
|
|
|
|
struct apk_name_state *ns = name_to_ns(name);
|
2012-01-17 11:54:33 +00:00
|
|
|
|
2012-02-22 10:59:46 +00:00
|
|
|
if (ns->locked)
|
|
|
|
return;
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2012-02-22 10:59:46 +00:00
|
|
|
/* remove cached information */
|
|
|
|
subscore(&ss->minimum_penalty, &ns->minimum_penalty);
|
|
|
|
ns->minimum_penalty = (struct apk_score) { .score = 0 };
|
2012-02-24 13:50:39 +00:00
|
|
|
ns->chosen = CHOSEN_NONE;
|
2011-07-30 17:59:47 +00:00
|
|
|
|
2012-02-22 10:59:46 +00:00
|
|
|
/* and remove list, or request refresh */
|
2012-02-22 07:08:14 +00:00
|
|
|
if (ns->requirers == 0 && ns->install_ifs == 0) {
|
2011-07-30 17:59:47 +00:00
|
|
|
if (list_hashed(&ns->unsolved_list)) {
|
|
|
|
list_del(&ns->unsolved_list);
|
|
|
|
list_init(&ns->unsolved_list);
|
2012-02-22 10:59:46 +00:00
|
|
|
dbg_printf("%s: not required\n", name->name);
|
2012-01-17 11:54:33 +00:00
|
|
|
}
|
2012-02-22 10:59:46 +00:00
|
|
|
} else {
|
2012-02-24 16:27:55 +00:00
|
|
|
/* still needed, put back to list */
|
|
|
|
promote_name(ss, name);
|
2012-02-18 09:54:17 +00:00
|
|
|
}
|
2012-02-20 18:54:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void inherit_name_state(struct apk_database *db, struct apk_name *to, struct apk_name *from)
|
|
|
|
{
|
|
|
|
struct apk_name_state *tns = name_to_ns(to);
|
|
|
|
struct apk_name_state *fns = name_to_ns(from);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if ((fns->solver_flags_local & fns->solver_flags_local_mask & APK_SOLVERF_REINSTALL) ||
|
|
|
|
fns->inherited_reinstall)
|
|
|
|
tns->inherited_reinstall++;
|
|
|
|
|
|
|
|
if ((fns->solver_flags_local & fns->solver_flags_local_mask & APK_SOLVERF_UPGRADE) ||
|
|
|
|
fns->inherited_upgrade)
|
|
|
|
tns->inherited_upgrade++;
|
|
|
|
|
|
|
|
if (fns->allowed_pinning) {
|
|
|
|
for (i = 0; i < db->num_repo_tags; i++) {
|
|
|
|
if (!(fns->allowed_pinning & BIT(i)))
|
|
|
|
continue;
|
|
|
|
if (tns->inherited_pinning[i]++ == 0)
|
|
|
|
tns->allowed_pinning |= BIT(i);
|
|
|
|
}
|
2011-07-30 17:59:47 +00:00
|
|
|
}
|
2012-02-20 18:54:03 +00:00
|
|
|
}
|
2011-07-30 17:59:47 +00:00
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
static void uninherit_name_state(struct apk_database *db, struct apk_name *to, struct apk_name *from)
|
|
|
|
{
|
|
|
|
struct apk_name_state *tns = name_to_ns(to);
|
|
|
|
struct apk_name_state *fns = name_to_ns(from);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if ((fns->solver_flags_local & fns->solver_flags_local_mask & APK_SOLVERF_REINSTALL) ||
|
|
|
|
fns->inherited_reinstall)
|
|
|
|
tns->inherited_reinstall--;
|
|
|
|
|
|
|
|
if ((fns->solver_flags_local & fns->solver_flags_local_mask & APK_SOLVERF_UPGRADE) ||
|
|
|
|
fns->inherited_upgrade)
|
|
|
|
tns->inherited_upgrade--;
|
|
|
|
|
|
|
|
if (fns->allowed_pinning) {
|
|
|
|
for (i = 0; i < db->num_repo_tags; i++) {
|
|
|
|
if (!(fns->allowed_pinning & BIT(i)))
|
|
|
|
continue;
|
|
|
|
if (--tns->inherited_pinning[i] == 0)
|
|
|
|
tns->allowed_pinning &= ~BIT(i);
|
|
|
|
}
|
|
|
|
}
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
|
|
|
|
2011-08-17 13:55:35 +00:00
|
|
|
static void trigger_install_if(struct apk_solver_state *ss,
|
|
|
|
struct apk_package *pkg)
|
|
|
|
{
|
|
|
|
if (install_if_missing(ss, pkg) == 0) {
|
2012-02-20 18:54:03 +00:00
|
|
|
struct apk_name *name0 = decision_to_name(&ss->decisions[ss->num_decisions]);
|
2012-02-22 06:45:40 +00:00
|
|
|
struct apk_name_state *ns = name_to_ns(pkg->name);
|
2011-08-17 13:55:35 +00:00
|
|
|
|
|
|
|
dbg_printf("trigger_install_if: " PKG_VER_FMT " triggered\n",
|
|
|
|
PKG_VER_PRINTF(pkg));
|
|
|
|
ns->install_ifs++;
|
2012-02-20 18:54:03 +00:00
|
|
|
inherit_name_state(ss->db, pkg->name, name0);
|
2012-02-22 10:59:46 +00:00
|
|
|
promote_name(ss, pkg->name);
|
2011-08-17 13:55:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void untrigger_install_if(struct apk_solver_state *ss,
|
|
|
|
struct apk_package *pkg)
|
|
|
|
{
|
|
|
|
if (install_if_missing(ss, pkg) != 1) {
|
2012-02-20 18:54:03 +00:00
|
|
|
struct apk_name *name0 = decision_to_name(&ss->decisions[ss->num_decisions]);
|
2011-09-09 13:31:11 +00:00
|
|
|
struct apk_name_state *ns = name_to_ns(pkg->name);
|
2011-08-17 13:55:35 +00:00
|
|
|
|
|
|
|
dbg_printf("untrigger_install_if: " PKG_VER_FMT " no longer triggered\n",
|
|
|
|
PKG_VER_PRINTF(pkg));
|
|
|
|
ns->install_ifs--;
|
2012-02-20 18:54:03 +00:00
|
|
|
uninherit_name_state(ss->db, pkg->name, name0);
|
2012-02-22 10:59:46 +00:00
|
|
|
demote_name(ss, pkg->name);
|
2011-08-17 13:55:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-24 16:27:55 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
ASSERT(!ns->locked, "Assigning locked name");
|
|
|
|
ns->locked = 1;
|
|
|
|
ns->chosen = p;
|
|
|
|
if (list_hashed(&ns->unsolved_list)) {
|
|
|
|
list_del(&ns->unsolved_list);
|
|
|
|
list_init(&ns->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 = 0;
|
|
|
|
ns->chosen = CHOSEN_NONE;
|
|
|
|
ns->name_touched = 1;
|
|
|
|
|
|
|
|
demote_name(ss, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-16 18:37:03 +00:00
|
|
|
static solver_result_t apply_decision(struct apk_solver_state *ss,
|
2012-02-18 09:54:17 +00:00
|
|
|
struct apk_decision *d)
|
2011-07-26 13:56:55 +00:00
|
|
|
{
|
2012-02-18 09:54:17 +00:00
|
|
|
struct apk_name *name = decision_to_name(d);
|
2012-02-16 18:37:03 +00:00
|
|
|
struct apk_name_state *ns = name_to_ns(name);
|
2012-02-18 09:54:17 +00:00
|
|
|
struct apk_package *pkg = decision_to_pkg(d);
|
|
|
|
struct apk_score score;
|
2012-02-24 16:27:55 +00:00
|
|
|
int i;
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
ns->name_touched = 1;
|
2012-02-18 09:54:17 +00:00
|
|
|
if (pkg != NULL) {
|
|
|
|
struct apk_package_state *ps = pkg_to_ps(pkg);
|
|
|
|
|
|
|
|
dbg_printf("-->apply_decision: " PKG_VER_FMT " %s\n",
|
|
|
|
PKG_VER_PRINTF(pkg),
|
|
|
|
(d->type == DECISION_ASSIGN) ? "ASSIGN" : "EXCLUDE");
|
|
|
|
|
|
|
|
ps->locked = 1;
|
|
|
|
ps->handle_install_if = 0;
|
|
|
|
|
|
|
|
if (d->topology_position) {
|
|
|
|
if (ps->topology_soft < ss->topology_position) {
|
|
|
|
if (d->type == DECISION_ASSIGN) {
|
|
|
|
ps->handle_install_if = 1;
|
|
|
|
dbg_printf("triggers due to " PKG_VER_FMT "\n",
|
|
|
|
PKG_VER_PRINTF(pkg));
|
|
|
|
}
|
|
|
|
ss->topology_position = ps->topology_soft;
|
|
|
|
} else {
|
|
|
|
ss->topology_position = pkg->topology_hard;
|
|
|
|
}
|
2012-02-16 18:37:03 +00:00
|
|
|
}
|
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
if (d->type == DECISION_ASSIGN) {
|
|
|
|
subscore(&ss->minimum_penalty, &ns->minimum_penalty);
|
2012-02-21 09:01:21 +00:00
|
|
|
ns->minimum_penalty = (struct apk_score) { .score = 0 };
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
get_topology_score(ss, ns, pkg, &score);
|
2012-02-18 09:54:17 +00:00
|
|
|
addscore(&ss->score, &score);
|
|
|
|
|
2012-02-22 06:45:40 +00:00
|
|
|
if (cmpscore2(&ss->score, &ss->minimum_penalty, &ss->best_score) >= 0) {
|
2012-02-18 09:54:17 +00:00
|
|
|
dbg_printf("install causing "SCORE_FMT", penalty too big: "SCORE_FMT"+"SCORE_FMT">="SCORE_FMT"\n",
|
|
|
|
SCORE_PRINTF(&score),
|
|
|
|
SCORE_PRINTF(&ss->score),
|
|
|
|
SCORE_PRINTF(&ss->minimum_penalty),
|
|
|
|
SCORE_PRINTF(&ss->best_score));
|
|
|
|
|
|
|
|
subscore(&ss->score, &score);
|
2012-02-20 18:54:03 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
return SOLVERR_PRUNED;
|
|
|
|
}
|
2011-07-30 17:59:47 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
ss->assigned_names++;
|
2012-02-24 16:27:55 +00:00
|
|
|
assign_name(ss, name, APK_PROVIDER_FROM_PACKAGE(pkg));
|
|
|
|
for (i = 0; i < pkg->provides->num; i++) {
|
|
|
|
struct apk_dependency *p = &pkg->provides->item[i];
|
|
|
|
assign_name(ss, p->name, APK_PROVIDER_FROM_PROVIDES(pkg, p));
|
|
|
|
}
|
2012-02-18 09:54:17 +00:00
|
|
|
|
|
|
|
foreach_dependency(ss, pkg->depends, apply_constraint);
|
|
|
|
foreach_rinstall_if_pkg(ss, pkg, trigger_install_if);
|
|
|
|
}
|
2011-07-26 13:56:55 +00:00
|
|
|
} else {
|
2012-02-18 09:54:17 +00:00
|
|
|
dbg_printf("-->apply_decision: %s %s NOTHING\n",
|
|
|
|
name->name,
|
|
|
|
(d->type == DECISION_ASSIGN) ? "ASSIGN" : "EXCLUDE");
|
|
|
|
|
|
|
|
if (d->type == DECISION_ASSIGN) {
|
2012-02-16 18:37:03 +00:00
|
|
|
subscore(&ss->minimum_penalty, &ns->minimum_penalty);
|
2012-02-21 09:01:21 +00:00
|
|
|
ns->minimum_penalty = (struct apk_score) { .score = 0 };
|
2012-02-16 18:37:03 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
get_unassigned_score(name, &score);
|
|
|
|
addscore(&ss->score, &score);
|
2012-02-16 18:37:03 +00:00
|
|
|
|
2012-02-24 13:50:39 +00:00
|
|
|
ns->chosen = CHOSEN_NONE;
|
2012-02-16 18:37:03 +00:00
|
|
|
ns->locked = 1;
|
2012-02-18 09:54:17 +00:00
|
|
|
list_del(&ns->unsolved_list);
|
|
|
|
list_init(&ns->unsolved_list);
|
|
|
|
} else {
|
|
|
|
ns->none_excluded = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-16 18:37:03 +00:00
|
|
|
if (cmpscore2(&ss->score, &ss->minimum_penalty, &ss->best_score) >= 0) {
|
2012-02-18 09:54:17 +00:00
|
|
|
dbg_printf("%s: %s penalty too big: "SCORE_FMT"+"SCORE_FMT">="SCORE_FMT"\n",
|
|
|
|
name->name,
|
|
|
|
(d->type == DECISION_ASSIGN) ? "ASSIGN" : "EXCLUDE",
|
|
|
|
SCORE_PRINTF(&ss->score),
|
|
|
|
SCORE_PRINTF(&ss->minimum_penalty),
|
|
|
|
SCORE_PRINTF(&ss->best_score));
|
2012-02-16 18:37:03 +00:00
|
|
|
return SOLVERR_PRUNED;
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
2012-02-16 18:37:03 +00:00
|
|
|
|
|
|
|
return SOLVERR_EXPAND;
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void undo_decision(struct apk_solver_state *ss,
|
2012-02-18 09:54:17 +00:00
|
|
|
struct apk_decision *d)
|
2011-07-26 13:56:55 +00:00
|
|
|
{
|
2012-02-18 09:54:17 +00:00
|
|
|
struct apk_name *name = decision_to_name(d);
|
2012-02-17 07:43:14 +00:00
|
|
|
struct apk_name_state *ns = name_to_ns(name);
|
2012-02-18 09:54:17 +00:00
|
|
|
struct apk_package *pkg = decision_to_pkg(d);
|
|
|
|
struct apk_score score;
|
2012-02-24 16:27:55 +00:00
|
|
|
int i;
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
ns->name_touched = 1;
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
if (pkg != NULL) {
|
|
|
|
struct apk_package_state *ps = pkg_to_ps(pkg);
|
2011-07-30 17:59:47 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
dbg_printf("-->undo_decision: " PKG_VER_FMT " %s\n",
|
|
|
|
PKG_VER_PRINTF(pkg),
|
|
|
|
(d->type == DECISION_ASSIGN) ? "ASSIGN" : "EXCLUDE");
|
|
|
|
|
|
|
|
if (d->topology_position) {
|
|
|
|
if (ps->handle_install_if)
|
|
|
|
ss->topology_position = ps->topology_soft;
|
|
|
|
else
|
|
|
|
ss->topology_position = pkg->topology_hard;
|
|
|
|
}
|
2012-02-17 07:43:14 +00:00
|
|
|
|
2012-02-21 08:36:26 +00:00
|
|
|
if (ns->locked) {
|
2012-02-16 18:37:03 +00:00
|
|
|
foreach_rinstall_if_pkg(ss, pkg, untrigger_install_if);
|
|
|
|
foreach_dependency(ss, pkg->depends, undo_constraint);
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
get_topology_score(ss, ns, pkg, &score);
|
2012-02-18 09:54:17 +00:00
|
|
|
subscore(&ss->score, &score);
|
2012-02-24 16:27:55 +00:00
|
|
|
|
|
|
|
unassign_name(ss, name);
|
|
|
|
for (i = 0; i < pkg->provides->num; i++) {
|
|
|
|
struct apk_dependency *p = &pkg->provides->item[i];
|
|
|
|
unassign_name(ss, p->name);
|
|
|
|
}
|
|
|
|
ss->assigned_names--;
|
2012-02-17 07:43:14 +00:00
|
|
|
}
|
2012-02-18 09:54:17 +00:00
|
|
|
ps->locked = 0;
|
2012-02-16 18:37:03 +00:00
|
|
|
} else {
|
2012-02-18 09:54:17 +00:00
|
|
|
dbg_printf("-->undo_decision: %s %s NOTHING\n",
|
|
|
|
name->name,
|
|
|
|
(d->type == DECISION_ASSIGN) ? "ASSIGN" : "EXCLUDE");
|
|
|
|
|
|
|
|
if (d->type == DECISION_ASSIGN) {
|
|
|
|
get_unassigned_score(name, &score);
|
|
|
|
subscore(&ss->score, &score);
|
|
|
|
} else {
|
|
|
|
ns->none_excluded = 0;
|
2012-02-17 07:43:14 +00:00
|
|
|
}
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2012-02-24 16:27:55 +00:00
|
|
|
/* Put back the name to unsolved list */
|
|
|
|
ns->locked = 0;
|
|
|
|
promote_name(ss, name);
|
|
|
|
}
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
static solver_result_t push_decision(struct apk_solver_state *ss,
|
|
|
|
struct apk_name *name,
|
|
|
|
struct apk_package *pkg,
|
|
|
|
int primary_decision,
|
|
|
|
int branching_point,
|
|
|
|
int topology_position)
|
2011-07-26 13:56:55 +00:00
|
|
|
{
|
2012-02-18 09:54:17 +00:00
|
|
|
struct apk_decision *d;
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
ASSERT(ss->num_decisions <= ss->max_decisions,
|
|
|
|
"Decision tree overflow.\n");
|
2011-08-17 13:55:35 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
ss->num_decisions++;
|
|
|
|
d = &ss->decisions[ss->num_decisions];
|
|
|
|
|
|
|
|
#ifdef DEBUG_CHECKS
|
|
|
|
d->saved_score = ss->score;
|
2012-02-22 07:14:46 +00:00
|
|
|
d->saved_requirers = name_to_ns(name)->requirers;
|
2012-02-18 09:54:17 +00:00
|
|
|
#endif
|
|
|
|
d->type = primary_decision;
|
|
|
|
d->branching_point = branching_point;
|
|
|
|
d->topology_position = topology_position;
|
2012-02-20 18:54:03 +00:00
|
|
|
d->found_solution = 0;
|
2012-02-18 09:54:17 +00:00
|
|
|
if (pkg == NULL) {
|
|
|
|
d->name = name;
|
|
|
|
d->no_package = 1;
|
2011-08-17 13:55:35 +00:00
|
|
|
} else {
|
2012-02-18 09:54:17 +00:00
|
|
|
d->pkg = pkg;
|
|
|
|
d->no_package = 0;
|
2011-08-17 13:55:35 +00:00
|
|
|
}
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
return apply_decision(ss, d);
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int next_branch(struct apk_solver_state *ss)
|
|
|
|
{
|
2012-02-20 18:54:03 +00:00
|
|
|
unsigned int backup_until = ss->num_decisions;
|
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
while (ss->num_decisions > 0) {
|
|
|
|
struct apk_decision *d = &ss->decisions[ss->num_decisions];
|
2012-02-22 07:14:46 +00:00
|
|
|
struct apk_name *name = decision_to_name(d);
|
|
|
|
struct apk_name_state *ns = name_to_ns(name);
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
undo_decision(ss, d);
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
#ifdef DEBUG_CHECKS
|
|
|
|
ASSERT(cmpscore(&d->saved_score, &ss->score) == 0,
|
|
|
|
"ERROR! saved_score "SCORE_FMT" != score "SCORE_FMT"\n",
|
|
|
|
SCORE_PRINTF(&d->saved_score),
|
|
|
|
SCORE_PRINTF(&ss->score));
|
2012-02-22 07:14:46 +00:00
|
|
|
ASSERT(d->saved_requirers == ns->requirers,
|
|
|
|
"ERROR! requirers not restored between decisions\n");
|
2012-02-18 09:54:17 +00:00
|
|
|
#endif
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
if (backup_until >= ss->num_decisions &&
|
|
|
|
d->branching_point == BRANCH_YES) {
|
2012-02-18 09:54:17 +00:00
|
|
|
d->branching_point = BRANCH_NO;
|
|
|
|
d->type = (d->type == DECISION_ASSIGN) ? DECISION_EXCLUDE : DECISION_ASSIGN;
|
|
|
|
return apply_decision(ss, d);
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
2012-02-18 09:54:17 +00:00
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
if (d->no_package && !d->found_solution) {
|
|
|
|
if (ns->last_touched_decision < backup_until)
|
|
|
|
backup_until = ns->last_touched_decision;
|
|
|
|
}
|
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
ss->num_decisions--;
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
2011-08-05 08:53:26 +00:00
|
|
|
|
2012-02-16 18:37:03 +00:00
|
|
|
dbg_printf("-->next_branch: no more branches\n");
|
|
|
|
return SOLVERR_STOP;
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
|
|
|
|
2011-07-30 17:59:47 +00:00
|
|
|
static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency *dep)
|
2011-07-26 13:56:55 +00:00
|
|
|
{
|
|
|
|
struct apk_name *name = dep->name;
|
2011-09-09 13:31:11 +00:00
|
|
|
struct apk_name_state *ns = name_to_ns(name);
|
2012-02-22 07:14:46 +00:00
|
|
|
int i, strength;
|
|
|
|
|
|
|
|
if (ss->num_decisions > 0) {
|
|
|
|
struct apk_name *name0 = decision_to_name(&ss->decisions[ss->num_decisions]);
|
|
|
|
struct apk_name_state *ns0 = name_to_ns(name0);
|
|
|
|
strength = ns0->requirers ?: 1;
|
|
|
|
} else {
|
|
|
|
strength = 1;
|
|
|
|
}
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2011-09-16 14:10:50 +00:00
|
|
|
if (ns->locked) {
|
2012-02-24 13:50:39 +00:00
|
|
|
if (ns->chosen.pkg)
|
2011-10-31 14:51:53 +00:00
|
|
|
dbg_printf("%s: locked to " PKG_VER_FMT " already\n",
|
2012-02-24 13:50:39 +00:00
|
|
|
name->name, PKG_VER_PRINTF(ns->chosen.pkg));
|
2011-10-31 14:51:53 +00:00
|
|
|
else
|
|
|
|
dbg_printf("%s: locked to empty\n",
|
2012-02-17 07:43:14 +00:00
|
|
|
name->name);
|
2012-02-24 13:50:39 +00:00
|
|
|
if (!apk_dep_is_provided(dep, &ns->chosen))
|
2012-02-22 07:14:46 +00:00
|
|
|
ss->score.conflicts += strength;
|
2011-07-30 17:59:47 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-02-24 13:50:39 +00:00
|
|
|
if (name->providers->num == 0) {
|
2012-02-17 07:43:14 +00:00
|
|
|
if (!dep->optional)
|
2012-02-22 07:14:46 +00:00
|
|
|
ss->score.conflicts += strength;
|
2012-02-17 07:43:14 +00:00
|
|
|
return;
|
|
|
|
}
|
2011-07-30 17:59:47 +00:00
|
|
|
|
2011-10-29 02:18:21 +00:00
|
|
|
if (dep->repository_tag) {
|
2012-02-15 11:57:36 +00:00
|
|
|
dbg_printf("%s: adding pinnings %d\n",
|
2011-10-29 02:18:21 +00:00
|
|
|
dep->name->name, dep->repository_tag);
|
2012-02-15 11:57:36 +00:00
|
|
|
ns->preferred_pinning = BIT(dep->repository_tag);
|
|
|
|
ns->allowed_pinning |= BIT(dep->repository_tag);
|
2012-02-20 18:54:03 +00:00
|
|
|
ns->inherited_pinning[dep->repository_tag]++;
|
|
|
|
recalculate_maybe(ss, name, 0, ns->allowed_pinning);
|
2011-10-29 02:18:21 +00:00
|
|
|
}
|
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
if (ss->num_decisions > 0) {
|
|
|
|
struct apk_name *name0 = decision_to_name(&ss->decisions[ss->num_decisions]);
|
2012-02-17 07:43:14 +00:00
|
|
|
dbg_printf("%s: inheriting flags and pinning from %s\n",
|
2012-02-18 09:54:17 +00:00
|
|
|
name->name, name0->name);
|
2012-02-20 18:54:03 +00:00
|
|
|
inherit_name_state(ss->db, name, name0);
|
|
|
|
ns->last_touched_decision = ss->num_decisions;
|
2012-02-17 07:43:14 +00:00
|
|
|
}
|
|
|
|
|
2012-02-24 13:50:39 +00:00
|
|
|
for (i = 0; i < name->providers->num; i++) {
|
|
|
|
struct apk_provider *p0 = &name->providers->item[i];
|
|
|
|
struct apk_package *pkg0 = p0->pkg;
|
2011-08-05 08:53:26 +00:00
|
|
|
struct apk_package_state *ps0 = pkg_to_ps(pkg0);
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
if (ps0 == NULL || ps0->locked ||
|
|
|
|
ss->topology_position < pkg0->topology_hard)
|
2011-07-26 13:56:55 +00:00
|
|
|
continue;
|
|
|
|
|
2012-02-24 13:50:39 +00:00
|
|
|
if (!apk_dep_is_provided(dep, p0)) {
|
2011-07-26 13:56:55 +00:00
|
|
|
ps0->conflicts++;
|
|
|
|
dbg_printf(PKG_VER_FMT ": conflicts++ -> %d\n",
|
|
|
|
PKG_VER_PRINTF(pkg0),
|
|
|
|
ps0->conflicts);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-21 19:24:24 +00:00
|
|
|
if (!dep->optional)
|
2012-02-22 07:14:46 +00:00
|
|
|
ns->requirers += strength;
|
2011-11-23 12:35:54 +00:00
|
|
|
|
2012-02-22 10:59:46 +00:00
|
|
|
promote_name(ss, name);
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
|
|
|
|
2011-07-30 17:59:47 +00:00
|
|
|
static void undo_constraint(struct apk_solver_state *ss, struct apk_dependency *dep)
|
2011-07-26 13:56:55 +00:00
|
|
|
{
|
|
|
|
struct apk_name *name = dep->name;
|
2011-09-09 13:31:11 +00:00
|
|
|
struct apk_name_state *ns = name_to_ns(name);
|
2012-02-22 07:14:46 +00:00
|
|
|
int i, strength;
|
|
|
|
|
|
|
|
if (ss->num_decisions > 0) {
|
|
|
|
struct apk_name *name0 = decision_to_name(&ss->decisions[ss->num_decisions]);
|
|
|
|
struct apk_name_state *ns0 = name_to_ns(name0);
|
|
|
|
strength = ns0->requirers ?: 1;
|
|
|
|
} else {
|
|
|
|
strength = 1;
|
|
|
|
}
|
2011-07-30 17:59:47 +00:00
|
|
|
|
2011-09-16 14:10:50 +00:00
|
|
|
if (ns->locked) {
|
2012-02-24 13:50:39 +00:00
|
|
|
if (ns->chosen.pkg != NULL) {
|
2011-09-28 05:39:52 +00:00
|
|
|
dbg_printf(PKG_VER_FMT " selected already for %s\n",
|
2012-02-24 13:50:39 +00:00
|
|
|
PKG_VER_PRINTF(ns->chosen.pkg), name->name);
|
2011-09-28 05:39:52 +00:00
|
|
|
} else {
|
|
|
|
dbg_printf("%s selected to not be satisfied\n",
|
2012-02-17 07:43:14 +00:00
|
|
|
name->name);
|
2011-09-28 05:39:52 +00:00
|
|
|
}
|
2012-02-24 13:50:39 +00:00
|
|
|
if (!apk_dep_is_provided(dep, &ns->chosen))
|
2012-02-22 07:14:46 +00:00
|
|
|
ss->score.conflicts -= strength;
|
2012-02-17 07:43:14 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-02-24 13:50:39 +00:00
|
|
|
if (name->providers->num == 0) {
|
2012-02-17 07:43:14 +00:00
|
|
|
if (!dep->optional)
|
2012-02-22 07:14:46 +00:00
|
|
|
ss->score.conflicts -= strength;
|
2011-07-30 17:59:47 +00:00
|
|
|
return;
|
|
|
|
}
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
if (ss->num_decisions > 0) {
|
|
|
|
struct apk_name *name0 = decision_to_name(&ss->decisions[ss->num_decisions]);
|
|
|
|
dbg_printf("%s: uninheriting flags and pinning from %s\n",
|
|
|
|
name->name, name0->name);
|
|
|
|
uninherit_name_state(ss->db, name, name0);
|
|
|
|
/* note: for perfection, we should revert here to the
|
|
|
|
* *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;
|
|
|
|
}
|
|
|
|
|
2012-02-24 13:50:39 +00:00
|
|
|
for (i = 0; i < name->providers->num; i++) {
|
|
|
|
struct apk_provider *p0 = &name->providers->item[i];
|
|
|
|
struct apk_package *pkg0 = p0->pkg;
|
2011-08-05 08:53:26 +00:00
|
|
|
struct apk_package_state *ps0 = pkg_to_ps(pkg0);
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
if (ps0 == NULL || ps0->locked ||
|
|
|
|
ss->topology_position < pkg0->topology_hard)
|
2011-07-26 13:56:55 +00:00
|
|
|
continue;
|
|
|
|
|
2012-02-24 13:50:39 +00:00
|
|
|
if (!apk_dep_is_provided(dep, p0)) {
|
2011-07-26 13:56:55 +00:00
|
|
|
ps0->conflicts--;
|
|
|
|
dbg_printf(PKG_VER_FMT ": conflicts-- -> %d\n",
|
|
|
|
PKG_VER_PRINTF(pkg0),
|
|
|
|
ps0->conflicts);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-21 19:24:24 +00:00
|
|
|
if (!dep->optional)
|
2012-02-22 07:14:46 +00:00
|
|
|
ns->requirers -= strength;
|
2011-11-23 12:35:54 +00:00
|
|
|
|
2012-02-22 10:59:46 +00:00
|
|
|
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_score minscore, score;
|
2012-02-24 13:50:39 +00:00
|
|
|
struct apk_provider *next_p = NULL;
|
2012-02-22 10:59:46 +00:00
|
|
|
unsigned int next_topology = 0, options = 0;
|
2012-02-22 11:09:51 +00:00
|
|
|
int i, score_locked = FALSE;
|
2012-02-22 10:59:46 +00:00
|
|
|
|
|
|
|
subscore(&ss->minimum_penalty, &ns->minimum_penalty);
|
|
|
|
ns->minimum_penalty = (struct apk_score) { .score = 0 };
|
|
|
|
|
|
|
|
score = ss->score;
|
|
|
|
addscore(&score, &ss->minimum_penalty);
|
|
|
|
|
|
|
|
if (!ns->none_excluded) {
|
|
|
|
get_unassigned_score(name, &minscore);
|
|
|
|
if (cmpscore2(&score, &minscore, &ss->best_score) >= 0) {
|
|
|
|
dbg_printf("%s: pruning none, score too high "SCORE_FMT"+"SCORE_FMT">="SCORE_FMT"\n",
|
|
|
|
name->name,
|
|
|
|
SCORE_PRINTF(&score),
|
|
|
|
SCORE_PRINTF(&minscore),
|
|
|
|
SCORE_PRINTF(&ss->best_score));
|
|
|
|
return push_decision(ss, name, NULL, DECISION_EXCLUDE, BRANCH_NO, FALSE);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
minscore.score = -1;
|
|
|
|
}
|
|
|
|
|
2012-02-24 13:50:39 +00:00
|
|
|
for (i = 0; i < name->providers->num; i++) {
|
|
|
|
struct apk_provider *p0 = &name->providers->item[i];
|
|
|
|
struct apk_package *pkg0 = p0->pkg;
|
2012-02-22 10:59:46 +00:00
|
|
|
struct apk_package_state *ps0 = pkg_to_ps(pkg0);
|
|
|
|
struct apk_score pkg0_score;
|
|
|
|
|
|
|
|
if (ps0 == NULL || ps0->locked ||
|
|
|
|
ss->topology_position < pkg0->topology_hard ||
|
|
|
|
((pkg0->ipkg == NULL && !pkg_available(ss->db, pkg0))))
|
|
|
|
continue;
|
|
|
|
|
2012-02-22 11:09:51 +00:00
|
|
|
score_locked = get_topology_score(ss, ns, pkg0, &pkg0_score);
|
2012-02-22 10:59:46 +00:00
|
|
|
|
|
|
|
/* viable alternative? */
|
|
|
|
if (cmpscore2(&score, &pkg0_score, &ss->best_score) >= 0)
|
|
|
|
return push_decision(ss, name, pkg0, DECISION_EXCLUDE, BRANCH_NO, FALSE);
|
|
|
|
|
|
|
|
/* find the minimum penalty */
|
|
|
|
if (cmpscore(&pkg0_score, &minscore) < 0)
|
|
|
|
minscore = pkg0_score;
|
|
|
|
|
|
|
|
/* next in topology order - next to get locked in */
|
|
|
|
if (ps0->topology_soft < ss->topology_position &&
|
|
|
|
ps0->topology_soft > next_topology)
|
2012-02-24 13:50:39 +00:00
|
|
|
next_p = p0, next_topology = ps0->topology_soft;
|
2012-02-22 10:59:46 +00:00
|
|
|
else if (pkg0->topology_hard > next_topology)
|
2012-02-24 13:50:39 +00:00
|
|
|
next_p = p0, next_topology = pkg0->topology_hard;
|
2012-02-22 10:59:46 +00:00
|
|
|
|
|
|
|
options++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no options left */
|
|
|
|
if (options == 0) {
|
|
|
|
dbg_printf("reconsider_name: %s: no options locking none\n",
|
|
|
|
name->name);
|
|
|
|
if (ns->none_excluded)
|
|
|
|
return SOLVERR_PRUNED;
|
|
|
|
return push_decision(ss, name, NULL, DECISION_ASSIGN, BRANCH_NO, FALSE);
|
2012-02-22 11:09:51 +00:00
|
|
|
} else if (options == 1 && score_locked && ns->none_excluded) {
|
|
|
|
dbg_printf("reconsider_name: %s: only one choice left with known score, locking.\n",
|
|
|
|
name->name);
|
2012-02-24 13:50:39 +00:00
|
|
|
return push_decision(ss, name, next_p->pkg, DECISION_ASSIGN, BRANCH_NO, FALSE);
|
2012-02-22 10:59:46 +00:00
|
|
|
}
|
|
|
|
|
2012-02-24 13:50:39 +00:00
|
|
|
ns->chosen = *next_p;
|
2012-02-22 10:59:46 +00:00
|
|
|
ns->minimum_penalty = minscore;
|
|
|
|
addscore(&ss->minimum_penalty, &ns->minimum_penalty);
|
|
|
|
|
|
|
|
dbg_printf("reconsider_name: %s: min penalty " SCORE_FMT ", next_pkg=%p\n",
|
2012-02-24 13:50:39 +00:00
|
|
|
name->name, SCORE_PRINTF(&minscore), next_p->pkg);
|
2012-02-22 10:59:46 +00:00
|
|
|
|
|
|
|
return SOLVERR_SOLUTION;
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int expand_branch(struct apk_solver_state *ss)
|
|
|
|
{
|
2012-02-18 09:54:17 +00:00
|
|
|
struct apk_name *name;
|
2011-07-30 17:59:47 +00:00
|
|
|
struct apk_name_state *ns;
|
|
|
|
struct apk_package *pkg0 = NULL;
|
2012-02-22 10:59:46 +00:00
|
|
|
unsigned int r, topology0 = 0;
|
2012-02-16 19:35:05 +00:00
|
|
|
unsigned short allowed_pinning, preferred_pinning;
|
|
|
|
unsigned int allowed_repos;
|
2012-02-18 09:54:17 +00:00
|
|
|
int primary_decision, branching_point;
|
2011-07-30 17:59:47 +00:00
|
|
|
|
|
|
|
list_for_each_entry(ns, &ss->unsolved_list_head, unsolved_list) {
|
2012-02-18 09:54:17 +00:00
|
|
|
name = ns->name;
|
2012-02-22 10:59:46 +00:00
|
|
|
if (ns->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;
|
2012-02-18 09:54:17 +00:00
|
|
|
}
|
2012-02-24 13:50:39 +00:00
|
|
|
if (pkg_to_ps(ns->chosen.pkg)->topology_soft < ss->topology_position &&
|
|
|
|
pkg_to_ps(ns->chosen.pkg)->topology_soft > topology0)
|
|
|
|
pkg0 = ns->chosen.pkg, topology0 = pkg_to_ps(pkg0)->topology_soft;
|
|
|
|
else if (ns->chosen.pkg->topology_hard > topology0)
|
|
|
|
pkg0 = ns->chosen.pkg, topology0 = pkg0->topology_hard;
|
2011-07-30 17:59:47 +00:00
|
|
|
}
|
|
|
|
if (pkg0 == NULL) {
|
2012-02-18 09:54:17 +00:00
|
|
|
dbg_printf("expand_branch: solution with score "SCORE_FMT"\n",
|
|
|
|
SCORE_PRINTF(&ss->score));
|
2012-02-16 18:37:03 +00:00
|
|
|
return SOLVERR_SOLUTION;
|
2011-07-30 17:59:47 +00:00
|
|
|
}
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2011-07-30 17:59:47 +00:00
|
|
|
/* someone needs to provide this name -- find next eligible
|
|
|
|
* provider candidate */
|
2012-02-18 09:54:17 +00:00
|
|
|
name = pkg0->name;
|
|
|
|
ns = name_to_ns(name);
|
|
|
|
|
|
|
|
if (!ns->none_excluded) {
|
|
|
|
struct apk_package_state *ps0 = pkg_to_ps(pkg0);
|
|
|
|
if (ps0->conflicts > ns->requirers)
|
|
|
|
primary_decision = DECISION_ASSIGN;
|
|
|
|
else
|
|
|
|
primary_decision = DECISION_EXCLUDE;
|
|
|
|
return push_decision(ss, name, NULL, primary_decision, BRANCH_YES, FALSE);
|
|
|
|
}
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
dbg_printf("expand_branch: "PKG_VER_FMT" score: "SCORE_FMT"\tminpenalty: "SCORE_FMT"\tbest: "SCORE_FMT"\n",
|
|
|
|
PKG_VER_PRINTF(pkg0),
|
|
|
|
SCORE_PRINTF(&ss->score),
|
|
|
|
SCORE_PRINTF(&ss->minimum_penalty),
|
|
|
|
SCORE_PRINTF(&ss->best_score));
|
|
|
|
|
2012-02-16 19:35:05 +00:00
|
|
|
preferred_pinning = ns->preferred_pinning ?: APK_DEFAULT_PINNING_MASK;
|
2012-02-22 06:45:40 +00:00
|
|
|
allowed_pinning = ns->allowed_pinning | preferred_pinning | APK_DEFAULT_PINNING_MASK;
|
2012-02-16 19:35:05 +00:00
|
|
|
allowed_repos = get_pinning_mask_repos(ss->db, allowed_pinning);
|
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
if ((pkg0->repos != 0) && !(pkg0->repos & allowed_repos)) {
|
2012-02-16 19:35:05 +00:00
|
|
|
/* pinning has not enabled the package */
|
2012-02-18 09:54:17 +00:00
|
|
|
primary_decision = DECISION_EXCLUDE;
|
|
|
|
/* but if it is installed, we might consider it */
|
|
|
|
if ((pkg0->ipkg == NULL) && (pkg0->filename == NULL))
|
|
|
|
branching_point = BRANCH_NO;
|
|
|
|
else
|
|
|
|
branching_point = BRANCH_YES;
|
2012-02-16 19:35:05 +00:00
|
|
|
} else if (ns->requirers == 0 && ns->install_ifs != 0 &&
|
|
|
|
install_if_missing(ss, pkg0)) {
|
|
|
|
/* not directly required, and package specific
|
|
|
|
* install_if never triggered */
|
2012-02-18 09:54:17 +00:00
|
|
|
primary_decision = DECISION_EXCLUDE;
|
|
|
|
branching_point = BRANCH_NO;
|
|
|
|
} else if (is_topology_optimum(ss, pkg0)) {
|
|
|
|
primary_decision = DECISION_ASSIGN;
|
|
|
|
branching_point = BRANCH_YES;
|
2012-02-16 19:35:05 +00:00
|
|
|
} else {
|
2012-02-18 09:54:17 +00:00
|
|
|
primary_decision = DECISION_EXCLUDE;
|
|
|
|
branching_point = BRANCH_YES;
|
2012-02-16 19:35:05 +00:00
|
|
|
}
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
return push_decision(ss, pkg0->name, pkg0,
|
|
|
|
primary_decision, branching_point, TRUE);
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
|
|
|
|
2012-02-16 08:50:52 +00:00
|
|
|
static int get_tag(struct apk_database *db, unsigned short 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;
|
|
|
|
}
|
|
|
|
|
2011-07-26 13:56:55 +00:00
|
|
|
static void record_solution(struct apk_solver_state *ss)
|
|
|
|
{
|
2012-02-16 08:50:52 +00:00
|
|
|
struct apk_database *db = ss->db;
|
|
|
|
struct apk_name_state *ns;
|
2012-02-18 09:54:17 +00:00
|
|
|
int i, n;
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-16 08:50:52 +00:00
|
|
|
apk_solution_array_resize(&ss->best_solution, ss->assigned_names);
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
n = 0;
|
|
|
|
for (i = ss->num_decisions; i > 0; i--) {
|
|
|
|
struct apk_decision *d = &ss->decisions[i];
|
|
|
|
struct apk_package *pkg = decision_to_pkg(d);
|
|
|
|
unsigned short pinning;
|
|
|
|
unsigned int repos;
|
|
|
|
|
2012-02-20 18:54:03 +00:00
|
|
|
d->found_solution = 1;
|
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
if (pkg == NULL) {
|
|
|
|
dbg_printf("record_solution: %s: NOTHING\n",
|
|
|
|
decision_to_name(d)->name);
|
|
|
|
continue;
|
2011-09-05 07:51:11 +00:00
|
|
|
}
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
dbg_printf("record_solution: " PKG_VER_FMT ": %s\n",
|
2011-07-26 13:56:55 +00:00
|
|
|
PKG_VER_PRINTF(pkg),
|
2012-02-18 09:54:17 +00:00
|
|
|
d->type == DECISION_ASSIGN ? "INSTALL" : "no install");
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
if (d->type != DECISION_ASSIGN)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ns = name_to_ns(pkg->name);
|
|
|
|
pinning = ns->allowed_pinning | ns->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\n");
|
|
|
|
ss->best_solution->item[n++] = (struct apk_solution_entry){
|
|
|
|
.pkg = pkg,
|
2012-02-20 18:54:03 +00:00
|
|
|
.reinstall = ns->inherited_reinstall ||
|
|
|
|
((ns->solver_flags_local | ss->solver_flags) & APK_SOLVERF_REINSTALL),
|
2012-02-18 09:54:17 +00:00
|
|
|
.repository_tag = get_tag(db, pinning, repos),
|
|
|
|
};
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
2012-02-18 09:54:17 +00:00
|
|
|
apk_solution_array_resize(&ss->best_solution, n);
|
2011-07-30 17:59:47 +00:00
|
|
|
}
|
|
|
|
|
2012-02-16 08:50:52 +00:00
|
|
|
static int compare_solution_entry(const void *p1, const void *p2)
|
2011-07-30 17:59:47 +00:00
|
|
|
{
|
2012-02-16 08:50:52 +00:00
|
|
|
const struct apk_solution_entry *c1 = (const struct apk_solution_entry *) p1;
|
|
|
|
const struct apk_solution_entry *c2 = (const struct apk_solution_entry *) p2;
|
2011-07-30 17:59:47 +00:00
|
|
|
|
2012-02-16 08:50:52 +00:00
|
|
|
return strcmp(c1->pkg->name->name, c2->pkg->name->name);
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
|
|
|
|
2011-09-09 13:31:11 +00:00
|
|
|
static int compare_change(const void *p1, const void *p2)
|
|
|
|
{
|
|
|
|
const struct apk_change *c1 = (const struct apk_change *) p1;
|
|
|
|
const struct apk_change *c2 = (const struct apk_change *) p2;
|
|
|
|
|
|
|
|
if (c1->newpkg == NULL) {
|
2012-01-17 12:18:10 +00:00
|
|
|
if (c2->newpkg == NULL) {
|
2011-09-09 13:31:11 +00:00
|
|
|
/* both deleted - reverse topology order */
|
2012-01-17 12:18:10 +00:00
|
|
|
return c2->oldpkg->topology_hard -
|
2011-09-14 13:48:28 +00:00
|
|
|
c1->oldpkg->topology_hard;
|
2012-01-17 12:18:10 +00:00
|
|
|
}
|
2011-09-09 13:31:11 +00:00
|
|
|
|
|
|
|
/* c1 deleted, c2 installed -> c2 first*/
|
2012-01-17 12:18:10 +00:00
|
|
|
return 1;
|
2011-09-09 13:31:11 +00:00
|
|
|
}
|
|
|
|
if (c2->newpkg == NULL)
|
|
|
|
/* c1 installed, c2 deleted -> c1 first*/
|
2012-01-17 12:18:10 +00:00
|
|
|
return -1;
|
2011-09-09 13:31:11 +00:00
|
|
|
|
2012-01-17 12:18:10 +00:00
|
|
|
return c1->newpkg->topology_hard -
|
2011-09-14 13:48:28 +00:00
|
|
|
c2->newpkg->topology_hard;
|
2011-09-09 13:31:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int generate_changeset(struct apk_database *db,
|
2012-02-16 08:50:52 +00:00
|
|
|
struct apk_solution_array *solution,
|
2011-09-13 08:47:34 +00:00
|
|
|
struct apk_changeset *changeset,
|
|
|
|
unsigned short solver_flags)
|
2011-09-09 13:31:11 +00:00
|
|
|
{
|
|
|
|
struct apk_name *name;
|
|
|
|
struct apk_name_state *ns;
|
2012-02-24 13:50:39 +00:00
|
|
|
struct apk_package *pkg;
|
2011-09-09 13:31:11 +00:00
|
|
|
struct apk_installed_package *ipkg;
|
2012-02-24 13:50:39 +00:00
|
|
|
int i, num_installs = 0, num_removed = 0, ci = 0;
|
2011-09-09 13:31:11 +00:00
|
|
|
|
|
|
|
/* calculate change set size */
|
|
|
|
for (i = 0; i < solution->num; i++) {
|
2012-02-16 08:50:52 +00:00
|
|
|
pkg = solution->item[i].pkg;
|
2011-09-16 14:10:50 +00:00
|
|
|
ns = name_to_ns(pkg->name);
|
2012-02-24 13:50:39 +00:00
|
|
|
ns->chosen = APK_PROVIDER_FROM_PACKAGE(pkg);
|
2011-10-14 17:48:20 +00:00
|
|
|
ns->in_changeset = 1;
|
2011-09-09 13:31:11 +00:00
|
|
|
if ((pkg->ipkg == NULL) ||
|
2012-02-16 08:50:52 +00:00
|
|
|
solution->item[i].reinstall ||
|
|
|
|
solution->item[i].repository_tag != pkg->ipkg->repository_tag)
|
2011-09-09 13:31:11 +00:00
|
|
|
num_installs++;
|
|
|
|
}
|
|
|
|
list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
|
|
|
|
name = ipkg->pkg->name;
|
|
|
|
ns = name_to_ns(name);
|
2012-02-24 13:50:39 +00:00
|
|
|
if ((ns->chosen.pkg == NULL) || !ns->in_changeset)
|
2011-09-09 13:31:11 +00:00
|
|
|
num_removed++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* construct changeset */
|
|
|
|
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);
|
2012-02-24 13:50:39 +00:00
|
|
|
if ((ns->chosen.pkg == NULL) || !ns->in_changeset) {
|
2011-09-09 13:31:11 +00:00
|
|
|
changeset->changes->item[ci].oldpkg = ipkg->pkg;
|
|
|
|
ci++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i < solution->num; i++) {
|
2012-02-16 08:50:52 +00:00
|
|
|
pkg = solution->item[i].pkg;
|
2011-09-09 13:31:11 +00:00
|
|
|
name = pkg->name;
|
2011-09-16 14:10:50 +00:00
|
|
|
ns = name_to_ns(name);
|
2011-09-09 13:31:11 +00:00
|
|
|
|
|
|
|
if ((pkg->ipkg == NULL) ||
|
2012-02-16 08:50:52 +00:00
|
|
|
solution->item[i].reinstall ||
|
|
|
|
solution->item[i].repository_tag != pkg->ipkg->repository_tag){
|
2012-02-24 13:50:39 +00:00
|
|
|
changeset->changes->item[ci].oldpkg = apk_pkg_get_installed(name);
|
2011-09-09 13:31:11 +00:00
|
|
|
changeset->changes->item[ci].newpkg = pkg;
|
2012-02-16 08:50:52 +00:00
|
|
|
changeset->changes->item[ci].repository_tag = solution->item[i].repository_tag;
|
|
|
|
changeset->changes->item[ci].reinstall = solution->item[i].reinstall;
|
2011-09-09 13:31:11 +00:00
|
|
|
ci++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sort changeset to topology order */
|
|
|
|
qsort(changeset->changes->item, changeset->changes->num,
|
|
|
|
sizeof(struct apk_change), compare_change);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int free_state(apk_hash_item item, void *ctx)
|
|
|
|
{
|
|
|
|
struct apk_name *name = (struct apk_name *) item;
|
2012-02-18 09:54:17 +00:00
|
|
|
struct apk_name_state *ns = (struct apk_name_state *) name->state_ptr;
|
2011-09-09 13:31:11 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
if (ns != NULL) {
|
2012-02-22 07:14:46 +00:00
|
|
|
#ifdef DEBUG_CHECKS
|
|
|
|
ASSERT(ns->requirers == 0, "Requirers is not zero after cleanup\n");
|
|
|
|
#endif
|
2012-02-18 09:54:17 +00:00
|
|
|
free(ns);
|
2011-09-09 13:31:11 +00:00
|
|
|
name->state_ptr = NULL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-09-14 07:38:26 +00:00
|
|
|
static int free_package(apk_hash_item item, void *ctx)
|
|
|
|
{
|
|
|
|
struct apk_package *pkg = (struct apk_package *) item;
|
|
|
|
|
|
|
|
if (pkg->state_ptr != NULL) {
|
|
|
|
free(pkg->state_ptr);
|
|
|
|
pkg->state_ptr = NULL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-09-13 08:47:34 +00:00
|
|
|
void apk_solver_set_name_flags(struct apk_name *name,
|
2011-09-16 14:10:50 +00:00
|
|
|
unsigned short solver_flags,
|
|
|
|
unsigned short solver_flags_inheritable)
|
2011-09-13 08:47:34 +00:00
|
|
|
{
|
2012-02-20 18:54:03 +00:00
|
|
|
struct apk_name_state *ns = name_to_ns_alloc(name);
|
2011-09-16 14:10:50 +00:00
|
|
|
ns->solver_flags_local = solver_flags;
|
|
|
|
ns->solver_flags_local_mask = solver_flags_inheritable;
|
2011-09-13 08:47:34 +00:00
|
|
|
}
|
|
|
|
|
2011-09-14 13:48:28 +00:00
|
|
|
static void apk_solver_free(struct apk_database *db)
|
|
|
|
{
|
|
|
|
apk_hash_foreach(&db->available.names, free_state, NULL);
|
|
|
|
apk_hash_foreach(&db->available.packages, free_package, NULL);
|
|
|
|
}
|
|
|
|
|
2011-09-09 13:31:11 +00:00
|
|
|
int apk_solver_solve(struct apk_database *db,
|
|
|
|
unsigned short solver_flags,
|
|
|
|
struct apk_dependency_array *world,
|
2012-02-16 08:50:52 +00:00
|
|
|
struct apk_solution_array **solution,
|
2011-09-09 13:31:11 +00:00
|
|
|
struct apk_changeset *changeset)
|
2011-07-26 13:56:55 +00:00
|
|
|
{
|
|
|
|
struct apk_solver_state *ss;
|
2011-09-09 13:31:11 +00:00
|
|
|
struct apk_installed_package *ipkg;
|
2012-02-16 18:37:03 +00:00
|
|
|
solver_result_t r = SOLVERR_STOP;
|
|
|
|
int i;
|
2011-07-26 13:56:55 +00:00
|
|
|
|
|
|
|
ss = calloc(1, sizeof(struct apk_solver_state));
|
|
|
|
ss->db = db;
|
2011-09-09 13:31:11 +00:00
|
|
|
ss->solver_flags = solver_flags;
|
2011-07-26 13:56:55 +00:00
|
|
|
ss->topology_position = -1;
|
2012-02-21 09:01:21 +00:00
|
|
|
ss->best_score = (struct apk_score){ .conflicts = -1 };
|
2011-07-26 13:56:55 +00:00
|
|
|
list_init(&ss->unsolved_list_head);
|
2011-08-05 08:53:26 +00:00
|
|
|
|
2012-02-22 07:14:46 +00:00
|
|
|
for (i = 0; i < world->num; i++) {
|
2011-08-05 08:53:26 +00:00
|
|
|
sort_name(ss, world->item[i].name);
|
2012-02-22 07:14:46 +00:00
|
|
|
name_to_ns(world->item[i].name)->in_world_dependency = 1;
|
|
|
|
}
|
2012-02-18 09:54:17 +00:00
|
|
|
list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
|
2011-09-09 13:31:11 +00:00
|
|
|
sort_name(ss, ipkg->pkg->name);
|
2012-02-18 09:54:17 +00:00
|
|
|
name_to_ns(ipkg->pkg->name)->originally_installed = 1;
|
|
|
|
}
|
|
|
|
#if 0
|
|
|
|
for (i = 0; i < world->num; i++)
|
|
|
|
prepare_name(ss, world->item[i].name);
|
|
|
|
list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list)
|
|
|
|
prepare_name(ss, ipkg->pkg->name);
|
|
|
|
#endif
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
ss->max_decisions ++;
|
|
|
|
ss->decisions = calloc(1, sizeof(struct apk_decision[ss->max_decisions]));
|
2011-11-01 06:29:24 +00:00
|
|
|
|
2012-02-18 09:54:17 +00:00
|
|
|
foreach_dependency(ss, world, apply_constraint);
|
2011-11-23 12:35:54 +00:00
|
|
|
|
2012-01-17 11:54:33 +00:00
|
|
|
do {
|
2012-02-16 18:37:03 +00:00
|
|
|
/* need EXPAND if here, can return SOLUTION|PRUNED|EXPAND */
|
|
|
|
r = expand_branch(ss);
|
|
|
|
if (r == SOLVERR_SOLUTION) {
|
2012-02-17 07:43:14 +00:00
|
|
|
struct apk_score score;
|
|
|
|
|
|
|
|
score = ss->score;
|
|
|
|
addscore(&score, &ss->minimum_penalty);
|
|
|
|
|
|
|
|
if (cmpscore(&score, &ss->best_score) < 0) {
|
2012-02-18 09:54:17 +00:00
|
|
|
dbg_printf("updating best score "SCORE_FMT" (was: "SCORE_FMT")\n",
|
|
|
|
SCORE_PRINTF(&score),
|
|
|
|
SCORE_PRINTF(&ss->best_score));
|
|
|
|
|
2012-02-16 18:37:03 +00:00
|
|
|
record_solution(ss);
|
2012-02-17 07:43:14 +00:00
|
|
|
ss->best_score = score;
|
|
|
|
}
|
2012-02-16 18:37:03 +00:00
|
|
|
|
|
|
|
r = SOLVERR_PRUNED;
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
2012-02-16 18:37:03 +00:00
|
|
|
/* next_branch() returns PRUNED, STOP or EXPAND */
|
|
|
|
while (r == SOLVERR_PRUNED)
|
|
|
|
r = next_branch(ss);
|
|
|
|
/* STOP or EXPAND */
|
|
|
|
} while (r != SOLVERR_STOP);
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-22 07:14:46 +00:00
|
|
|
#ifdef DEBUG_CHECKS
|
|
|
|
foreach_dependency(ss, world, undo_constraint);
|
|
|
|
#endif
|
|
|
|
|
2011-07-26 13:56:55 +00:00
|
|
|
/* collect packages */
|
2012-02-18 09:54:17 +00:00
|
|
|
dbg_printf("finished. best score "SCORE_FMT". solution has %d packages.\n",
|
|
|
|
SCORE_PRINTF(&ss->best_score),
|
|
|
|
ss->best_solution->num);
|
|
|
|
|
2011-11-01 06:40:50 +00:00
|
|
|
if (changeset != NULL) {
|
|
|
|
generate_changeset(db, ss->best_solution, changeset,
|
|
|
|
ss->solver_flags);
|
|
|
|
}
|
|
|
|
if (solution != NULL) {
|
2011-07-30 17:59:47 +00:00
|
|
|
qsort(ss->best_solution->item, ss->best_solution->num,
|
2012-02-16 08:50:52 +00:00
|
|
|
sizeof(struct apk_solution_entry), compare_solution_entry);
|
2011-09-09 13:31:11 +00:00
|
|
|
*solution = ss->best_solution;
|
2011-11-01 06:40:50 +00:00
|
|
|
} else {
|
2012-02-16 08:50:52 +00:00
|
|
|
apk_solution_array_free(&ss->best_solution);
|
2011-11-01 06:40:50 +00:00
|
|
|
}
|
2012-02-18 09:54:17 +00:00
|
|
|
i = ss->best_score.conflicts;
|
2011-09-14 13:48:28 +00:00
|
|
|
apk_solver_free(db);
|
2012-02-18 09:54:17 +00:00
|
|
|
free(ss->decisions);
|
2011-07-26 13:56:55 +00:00
|
|
|
free(ss);
|
|
|
|
|
2012-02-16 18:37:03 +00:00
|
|
|
return i;
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
|
|
|
|
2011-09-09 13:31:11 +00:00
|
|
|
static void print_change(struct apk_database *db,
|
|
|
|
struct apk_change *change,
|
|
|
|
int cur, int total)
|
2011-07-26 13:56:55 +00:00
|
|
|
{
|
2011-09-09 13:31:11 +00:00
|
|
|
struct apk_name *name;
|
|
|
|
struct apk_package *oldpkg = change->oldpkg;
|
|
|
|
struct apk_package *newpkg = change->newpkg;
|
|
|
|
const char *msg = NULL;
|
2012-01-12 09:33:04 +00:00
|
|
|
char status[32], n[512], *nameptr;
|
2012-02-22 08:28:02 +00:00
|
|
|
apk_blob_t *oneversion = NULL;
|
2012-02-15 11:57:36 +00:00
|
|
|
int r;
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2011-09-09 13:31:11 +00:00
|
|
|
snprintf(status, sizeof(status), "(%i/%i)", cur+1, total);
|
2012-01-12 09:33:04 +00:00
|
|
|
status[sizeof(status) - 1] = 0;
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-02-15 11:57:36 +00:00
|
|
|
name = newpkg ? newpkg->name : oldpkg->name;
|
|
|
|
if (change->repository_tag > 0) {
|
2012-01-12 09:33:04 +00:00
|
|
|
snprintf(n, sizeof(n), "%s@" BLOB_FMT,
|
|
|
|
name->name,
|
2012-02-15 11:57:36 +00:00
|
|
|
BLOB_PRINTF(*db->repo_tags[change->repository_tag].name));
|
2012-01-12 09:33:04 +00:00
|
|
|
n[sizeof(n) - 1] = 0;
|
|
|
|
nameptr = n;
|
|
|
|
} else {
|
|
|
|
nameptr = name->name;
|
|
|
|
}
|
2011-09-09 13:31:11 +00:00
|
|
|
|
|
|
|
if (oldpkg == NULL) {
|
2012-02-22 08:28:02 +00:00
|
|
|
msg = "Installing";
|
|
|
|
oneversion = newpkg->version;
|
2011-09-09 13:31:11 +00:00
|
|
|
} else if (newpkg == NULL) {
|
2012-02-22 08:28:02 +00:00
|
|
|
msg = "Purging";
|
|
|
|
oneversion = oldpkg->version;
|
|
|
|
} else if (newpkg == oldpkg) {
|
|
|
|
if (change->reinstall) {
|
|
|
|
if (pkg_available(db, change->newpkg))
|
|
|
|
msg = "Re-installing";
|
|
|
|
else
|
|
|
|
msg = "[APK unavailable, skipped] Re-installing";
|
|
|
|
} else {
|
|
|
|
msg = "Updating pinning";
|
|
|
|
}
|
|
|
|
oneversion = newpkg->version;
|
2011-09-09 13:31:11 +00:00
|
|
|
} else {
|
|
|
|
r = apk_pkg_version_compare(newpkg, oldpkg);
|
|
|
|
switch (r) {
|
|
|
|
case APK_VERSION_LESS:
|
|
|
|
msg = "Downgrading";
|
|
|
|
break;
|
|
|
|
case APK_VERSION_EQUAL:
|
2012-02-22 08:28:02 +00:00
|
|
|
msg = "Replacing";
|
2011-09-09 13:31:11 +00:00
|
|
|
break;
|
|
|
|
case APK_VERSION_GREATER:
|
|
|
|
msg = "Upgrading";
|
|
|
|
break;
|
|
|
|
}
|
2012-02-22 08:28:02 +00:00
|
|
|
}
|
|
|
|
if (oneversion) {
|
|
|
|
apk_message("%s %s %s (" BLOB_FMT ")",
|
|
|
|
status, msg, nameptr,
|
|
|
|
BLOB_PRINTF(*oneversion));
|
|
|
|
} else {
|
2011-09-09 13:31:11 +00:00
|
|
|
apk_message("%s %s %s (" BLOB_FMT " -> " BLOB_FMT ")",
|
2012-01-12 09:33:04 +00:00
|
|
|
status, msg, nameptr,
|
2011-09-09 13:31:11 +00:00
|
|
|
BLOB_PRINTF(*oldpkg->version),
|
|
|
|
BLOB_PRINTF(*newpkg->version));
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
2011-09-09 13:31:11 +00:00
|
|
|
}
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2011-09-09 13:31:11 +00:00
|
|
|
struct apk_stats {
|
|
|
|
unsigned int bytes;
|
|
|
|
unsigned int packages;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void count_change(struct apk_change *change, struct apk_stats *stats)
|
|
|
|
{
|
|
|
|
if (change->newpkg != NULL) {
|
|
|
|
stats->bytes += change->newpkg->installed_size;
|
|
|
|
stats->packages ++;
|
|
|
|
}
|
|
|
|
if (change->oldpkg != NULL)
|
|
|
|
stats->packages ++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void draw_progress(int percent)
|
|
|
|
{
|
|
|
|
const int bar_width = apk_get_screen_width() - 7;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
fprintf(stderr, "\e7%3i%% [", percent);
|
|
|
|
for (i = 0; i < bar_width * percent / 100; i++)
|
|
|
|
fputc('#', stderr);
|
|
|
|
for (; i < bar_width; i++)
|
|
|
|
fputc(' ', stderr);
|
|
|
|
fputc(']', stderr);
|
|
|
|
fflush(stderr);
|
|
|
|
fputs("\e8\e[0K", stderr);
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
|
|
|
|
2011-09-09 13:31:11 +00:00
|
|
|
struct progress {
|
|
|
|
struct apk_stats done;
|
|
|
|
struct apk_stats total;
|
|
|
|
struct apk_package *pkg;
|
|
|
|
size_t count;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void progress_cb(void *ctx, size_t progress)
|
2011-07-26 13:56:55 +00:00
|
|
|
{
|
2011-09-09 13:31:11 +00:00
|
|
|
struct progress *prog = (struct progress *) ctx;
|
|
|
|
size_t partial = 0, count;
|
|
|
|
|
|
|
|
if (prog->pkg != NULL)
|
|
|
|
partial = muldiv(progress, prog->pkg->installed_size, APK_PROGRESS_SCALE);
|
|
|
|
|
|
|
|
count = muldiv(100, prog->done.bytes + prog->done.packages + partial,
|
|
|
|
prog->total.bytes + prog->total.packages);
|
|
|
|
|
|
|
|
if (prog->count != count)
|
|
|
|
draw_progress(count);
|
|
|
|
prog->count = count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dump_packages(struct apk_changeset *changeset,
|
|
|
|
int (*cmp)(struct apk_change *change),
|
|
|
|
const char *msg)
|
|
|
|
{
|
|
|
|
struct apk_change *change;
|
2011-07-26 13:56:55 +00:00
|
|
|
struct apk_name *name;
|
2011-09-09 17:06:10 +00:00
|
|
|
struct apk_indent indent = { .indent = 2 };
|
2011-09-09 13:31:11 +00:00
|
|
|
int match = 0, i;
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2011-09-09 13:31:11 +00:00
|
|
|
for (i = 0; i < changeset->changes->num; i++) {
|
|
|
|
change = &changeset->changes->item[i];
|
|
|
|
if (!cmp(change))
|
|
|
|
continue;
|
|
|
|
if (match == 0)
|
2011-10-29 20:43:50 +00:00
|
|
|
printf("%s:\n", msg);
|
2011-09-09 13:31:11 +00:00
|
|
|
if (change->newpkg != NULL)
|
|
|
|
name = change->newpkg->name;
|
|
|
|
else
|
|
|
|
name = change->oldpkg->name;
|
|
|
|
|
|
|
|
apk_print_indented(&indent, APK_BLOB_STR(name->name));
|
|
|
|
match++;
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
2011-09-09 13:31:11 +00:00
|
|
|
if (match)
|
|
|
|
printf("\n");
|
|
|
|
return match;
|
|
|
|
}
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2011-09-09 13:31:11 +00:00
|
|
|
static int cmp_remove(struct apk_change *change)
|
|
|
|
{
|
|
|
|
return change->newpkg == NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cmp_new(struct apk_change *change)
|
|
|
|
{
|
|
|
|
return change->oldpkg == NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cmp_downgrade(struct apk_change *change)
|
|
|
|
{
|
|
|
|
if (change->newpkg == NULL || change->oldpkg == NULL)
|
|
|
|
return 0;
|
|
|
|
if (apk_pkg_version_compare(change->newpkg, change->oldpkg)
|
|
|
|
& APK_VERSION_LESS)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cmp_upgrade(struct apk_change *change)
|
|
|
|
{
|
|
|
|
if (change->newpkg == NULL || change->oldpkg == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Count swapping package as upgrade too - this can happen if
|
|
|
|
* same package version is used after it was rebuilt against
|
|
|
|
* newer libraries. Basically, different (and probably newer)
|
|
|
|
* package, but equal version number. */
|
|
|
|
if ((apk_pkg_version_compare(change->newpkg, change->oldpkg) &
|
|
|
|
(APK_VERSION_GREATER | APK_VERSION_EQUAL)) &&
|
|
|
|
(change->newpkg != change->oldpkg))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-14 17:48:20 +00:00
|
|
|
static int compare_package_topology(const void *p1, const void *p2)
|
2011-09-14 08:30:02 +00:00
|
|
|
{
|
|
|
|
struct apk_package *pkg1 = *(struct apk_package **) p1;
|
|
|
|
struct apk_package *pkg2 = *(struct apk_package **) p2;
|
|
|
|
|
2011-09-14 13:48:28 +00:00
|
|
|
return pkg1->topology_hard - pkg2->topology_hard;
|
2011-09-14 08:30:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void run_triggers(struct apk_database *db)
|
|
|
|
{
|
|
|
|
struct apk_package_array *pkgs;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
pkgs = apk_db_get_pending_triggers(db);
|
|
|
|
if (pkgs == NULL || pkgs->num == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
qsort(pkgs->item, pkgs->num, sizeof(struct apk_package *),
|
2011-10-14 17:48:20 +00:00
|
|
|
compare_package_topology);
|
2011-09-14 08:30:02 +00:00
|
|
|
|
|
|
|
for (i = 0; i < pkgs->num; i++) {
|
|
|
|
struct apk_package *pkg = pkgs->item[i];
|
|
|
|
struct apk_installed_package *ipkg = pkg->ipkg;
|
|
|
|
|
|
|
|
*apk_string_array_add(&ipkg->pending_triggers) = NULL;
|
|
|
|
apk_ipkg_run_script(ipkg, db, APK_SCRIPT_TRIGGER,
|
|
|
|
ipkg->pending_triggers->item);
|
|
|
|
apk_string_array_free(&ipkg->pending_triggers);
|
|
|
|
}
|
|
|
|
apk_package_array_free(&pkgs);
|
|
|
|
}
|
|
|
|
|
2011-09-09 16:41:19 +00:00
|
|
|
int apk_solver_commit_changeset(struct apk_database *db,
|
|
|
|
struct apk_changeset *changeset,
|
|
|
|
struct apk_dependency_array *world)
|
2011-09-09 13:31:11 +00:00
|
|
|
{
|
|
|
|
struct progress prog;
|
|
|
|
struct apk_change *change;
|
2012-02-23 14:02:03 +00:00
|
|
|
int i, r = 0, size_diff = 0, size_unit;
|
2011-09-09 13:31:11 +00:00
|
|
|
|
2012-01-17 12:46:39 +00:00
|
|
|
if (apk_db_check_world(db, world) != 0) {
|
2012-01-12 08:42:27 +00:00
|
|
|
apk_error("Not committing changes due to missing repository tags. Use --force to override.");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-09-09 16:41:19 +00:00
|
|
|
if (changeset->changes == NULL)
|
|
|
|
goto all_done;
|
|
|
|
|
2011-09-09 13:31:11 +00:00
|
|
|
/* Count what needs to be done */
|
|
|
|
memset(&prog, 0, sizeof(prog));
|
|
|
|
for (i = 0; i < changeset->changes->num; i++) {
|
|
|
|
change = &changeset->changes->item[i];
|
|
|
|
count_change(change, &prog.total);
|
|
|
|
if (change->newpkg)
|
|
|
|
size_diff += change->newpkg->installed_size;
|
|
|
|
if (change->oldpkg)
|
|
|
|
size_diff -= change->oldpkg->installed_size;
|
|
|
|
}
|
|
|
|
size_diff /= 1024;
|
2012-02-23 14:02:03 +00:00
|
|
|
size_unit = 'K';
|
|
|
|
if (abs(size_diff) > 10000) {
|
|
|
|
size_diff /= 1024;
|
|
|
|
size_unit = 'M';
|
|
|
|
}
|
2011-09-09 13:31:11 +00:00
|
|
|
|
|
|
|
if (apk_verbosity > 1 || (apk_flags & APK_INTERACTIVE)) {
|
|
|
|
r = dump_packages(changeset, cmp_remove,
|
|
|
|
"The following packages will be REMOVED");
|
|
|
|
r += dump_packages(changeset, cmp_downgrade,
|
|
|
|
"The following packages will be DOWNGRADED");
|
|
|
|
if (r || (apk_flags & APK_INTERACTIVE) || apk_verbosity > 2) {
|
|
|
|
dump_packages(changeset, cmp_new,
|
|
|
|
"The following NEW packages will be installed");
|
|
|
|
dump_packages(changeset, cmp_upgrade,
|
|
|
|
"The following packages will be upgraded");
|
2012-02-23 14:02:03 +00:00
|
|
|
printf("After this operation, %d %ciB of %s\n",
|
|
|
|
abs(size_diff), size_unit,
|
|
|
|
(size_diff < 0) ?
|
|
|
|
"disk space will be freed." :
|
|
|
|
"additional disk space will be used.");
|
2011-09-09 13:31:11 +00:00
|
|
|
}
|
|
|
|
if (apk_flags & APK_INTERACTIVE) {
|
|
|
|
printf("Do you want to continue [Y/n]? ");
|
|
|
|
fflush(stdout);
|
|
|
|
r = fgetc(stdin);
|
|
|
|
if (r != 'y' && r != 'Y' && r != '\n')
|
|
|
|
return -1;
|
|
|
|
}
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
|
|
|
|
2011-09-09 13:31:11 +00:00
|
|
|
/* Go through changes */
|
|
|
|
r = 0;
|
|
|
|
for (i = 0; i < changeset->changes->num; i++) {
|
|
|
|
change = &changeset->changes->item[i];
|
|
|
|
|
|
|
|
print_change(db, change, i, changeset->changes->num);
|
|
|
|
if (apk_flags & APK_PROGRESS)
|
|
|
|
draw_progress(prog.count);
|
|
|
|
prog.pkg = change->newpkg;
|
|
|
|
|
|
|
|
if (!(apk_flags & APK_SIMULATE)) {
|
2012-02-22 08:28:02 +00:00
|
|
|
if (change->oldpkg != change->newpkg ||
|
|
|
|
(change->reinstall && pkg_available(db, change->newpkg)))
|
2012-02-16 08:50:52 +00:00
|
|
|
r = apk_db_install_pkg(db,
|
|
|
|
change->oldpkg, change->newpkg,
|
|
|
|
(apk_flags & APK_PROGRESS) ? progress_cb : NULL,
|
|
|
|
&prog);
|
2011-09-09 13:31:11 +00:00
|
|
|
if (r != 0)
|
|
|
|
break;
|
2012-02-15 11:57:36 +00:00
|
|
|
if (change->newpkg)
|
|
|
|
change->newpkg->ipkg->repository_tag = change->repository_tag;
|
2011-09-09 13:31:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
count_change(change, &prog.done);
|
|
|
|
}
|
|
|
|
if (apk_flags & APK_PROGRESS)
|
|
|
|
draw_progress(100);
|
|
|
|
|
2011-09-14 08:30:02 +00:00
|
|
|
run_triggers(db);
|
2011-09-09 13:31:11 +00:00
|
|
|
|
2011-09-09 16:41:19 +00:00
|
|
|
all_done:
|
2011-09-09 13:31:11 +00:00
|
|
|
apk_dependency_array_copy(&db->world, world);
|
|
|
|
apk_db_write_config(db);
|
|
|
|
|
2011-09-14 07:38:26 +00:00
|
|
|
if (r == 0 && !db->performing_self_update) {
|
2011-12-27 12:06:03 +00:00
|
|
|
if (apk_verbosity > 1) {
|
|
|
|
apk_message("OK: %d packages, %d dirs, %d files, %zu MiB",
|
|
|
|
db->installed.stats.packages,
|
|
|
|
db->installed.stats.dirs,
|
|
|
|
db->installed.stats.files,
|
|
|
|
db->installed.stats.bytes / (1024 * 1024));
|
|
|
|
} else {
|
|
|
|
apk_message("OK: %zu MiB in %d packages",
|
|
|
|
db->installed.stats.bytes / (1024 * 1024),
|
|
|
|
db->installed.stats.packages);
|
|
|
|
}
|
2011-09-09 13:31:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2011-10-29 02:18:21 +00:00
|
|
|
static void print_dep_errors(struct apk_database *db, char *label, struct apk_dependency_array *deps)
|
2011-09-09 13:31:11 +00:00
|
|
|
{
|
|
|
|
int i, print_label = 1;
|
|
|
|
char buf[256];
|
|
|
|
apk_blob_t p;
|
|
|
|
struct apk_indent indent;
|
|
|
|
|
|
|
|
for (i = 0; i < deps->num; i++) {
|
|
|
|
struct apk_dependency *dep = &deps->item[i];
|
|
|
|
struct apk_package *pkg = (struct apk_package*) dep->name->state_ptr;
|
|
|
|
|
2012-02-24 13:50:39 +00:00
|
|
|
if (apk_dep_is_materialized_or_provided(dep, pkg))
|
2011-07-26 13:56:55 +00:00
|
|
|
continue;
|
2011-09-09 13:31:11 +00:00
|
|
|
|
|
|
|
if (print_label) {
|
|
|
|
print_label = 0;
|
|
|
|
indent.x = printf(" %s:", label);
|
|
|
|
indent.indent = indent.x + 1;
|
|
|
|
}
|
|
|
|
p = APK_BLOB_BUF(buf);
|
2011-10-29 02:18:21 +00:00
|
|
|
apk_blob_push_dep(&p, db, dep);
|
2011-09-09 13:31:11 +00:00
|
|
|
p = apk_blob_pushed(APK_BLOB_BUF(buf), p);
|
|
|
|
apk_print_indented(&indent, p);
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
2011-09-09 13:31:11 +00:00
|
|
|
if (!print_label)
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
2011-09-09 16:41:19 +00:00
|
|
|
void apk_solver_print_errors(struct apk_database *db,
|
2012-02-16 08:50:52 +00:00
|
|
|
struct apk_solution_array *solution,
|
2011-09-09 16:41:19 +00:00
|
|
|
struct apk_dependency_array *world,
|
|
|
|
int unsatisfiable)
|
2011-09-09 13:31:11 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
apk_error("%d unsatisfiable dependencies:", unsatisfiable);
|
|
|
|
|
2011-07-26 13:56:55 +00:00
|
|
|
for (i = 0; i < solution->num; i++) {
|
2012-02-16 08:50:52 +00:00
|
|
|
struct apk_package *pkg = solution->item[i].pkg;
|
2011-09-09 13:31:11 +00:00
|
|
|
pkg->name->state_ptr = pkg;
|
|
|
|
}
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2011-10-29 02:18:21 +00:00
|
|
|
print_dep_errors(db, "world", world);
|
2011-09-09 13:31:11 +00:00
|
|
|
for (i = 0; i < solution->num; i++) {
|
2012-02-16 08:50:52 +00:00
|
|
|
struct apk_package *pkg = solution->item[i].pkg;
|
2011-09-09 13:31:11 +00:00
|
|
|
char pkgtext[256];
|
2012-02-16 08:50:52 +00:00
|
|
|
snprintf(pkgtext, sizeof(pkgtext), PKG_VER_FMT, PKG_VER_PRINTF(pkg));
|
2011-10-29 02:18:21 +00:00
|
|
|
print_dep_errors(db, pkgtext, pkg->depends);
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|
2011-09-09 13:31:11 +00:00
|
|
|
}
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2011-09-09 13:31:11 +00:00
|
|
|
int apk_solver_commit(struct apk_database *db,
|
|
|
|
unsigned short solver_flags,
|
|
|
|
struct apk_dependency_array *world)
|
|
|
|
{
|
|
|
|
struct apk_changeset changeset = {};
|
2012-02-16 08:50:52 +00:00
|
|
|
struct apk_solution_array *solution = NULL;
|
2011-09-09 13:31:11 +00:00
|
|
|
int r;
|
2011-07-26 13:56:55 +00:00
|
|
|
|
2012-01-17 12:46:39 +00:00
|
|
|
if (apk_db_check_world(db, world) != 0) {
|
2012-01-12 08:42:27 +00:00
|
|
|
apk_error("Not committing changes due to missing repository tags. Use --force to override.");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-09-14 13:48:28 +00:00
|
|
|
r = apk_solver_solve(db, solver_flags,
|
2011-09-14 08:30:02 +00:00
|
|
|
world, &solution, &changeset);
|
2011-09-09 13:31:11 +00:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (r == 0 || (apk_flags & APK_FORCE)) {
|
|
|
|
/* Success -- or forced installation of bad graph */
|
2011-09-28 11:04:02 +00:00
|
|
|
r = apk_solver_commit_changeset(db, &changeset, world);
|
2011-09-09 13:31:11 +00:00
|
|
|
} else {
|
|
|
|
/* Failure -- print errors */
|
2011-09-09 16:41:19 +00:00
|
|
|
apk_solver_print_errors(db, solution, world, r);
|
2011-09-09 13:31:11 +00:00
|
|
|
}
|
2012-02-16 08:50:52 +00:00
|
|
|
apk_solution_array_free(&solution);
|
2012-02-17 08:02:44 +00:00
|
|
|
apk_change_array_free(&changeset.changes);
|
2011-09-09 13:31:11 +00:00
|
|
|
|
|
|
|
return r;
|
2011-07-26 13:56:55 +00:00
|
|
|
}
|