Commit b8c44536 authored by Timo Teräs's avatar Timo Teräs

solver: rewrite as deductive solver -- core features

Implementing basic dependency handling, install_if and awareness
of pinning.
parent f292a858
......@@ -25,7 +25,7 @@ apk-objs := apk.o add.o del.o fix.o update.o info.o \
libapk.so-objs := common.o database.o package.o archive.o \
version.o io.o url.o gunzip.o blob.o hash.o print.o \
solver.o
commit.o solver.o
ifeq ($(TEST),y)
progs-y += apk-test
......
......@@ -138,6 +138,7 @@ struct apk_database {
char *cache_remount_dir;
apk_blob_t *arch;
unsigned int local_repos, available_repos;
unsigned int pending_triggers;
int performing_self_update : 1;
int permanent : 1;
int compat_newfeatures : 1;
......@@ -206,7 +207,7 @@ void apk_db_close(struct apk_database *db);
int apk_db_write_config(struct apk_database *db);
int apk_db_permanent(struct apk_database *db);
int apk_db_check_world(struct apk_database *db, struct apk_dependency_array *world);
struct apk_package_array *apk_db_get_pending_triggers(struct apk_database *db);
int apk_db_fire_triggers(struct apk_database *db);
struct apk_package *apk_db_pkg_add(struct apk_database *db, struct apk_package *pkg);
struct apk_package *apk_db_get_pkg(struct apk_database *db, struct apk_checksum *csum);
......@@ -222,6 +223,7 @@ struct apk_repository *apk_db_select_repo(struct apk_database *db,
int apk_repo_format_filename(char *buf, size_t len,
const char *repourl, apk_blob_t *arch,
const char *pkgfile);
unsigned int apk_db_get_pinning_mask_repos(struct apk_database *db, unsigned short pinning_mask);
int apk_db_cache_active(struct apk_database *db);
void apk_cache_format_index(apk_blob_t to, struct apk_repository *repo);
......
......@@ -14,8 +14,9 @@
#include <string.h>
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define BIT(x) (1 << (x))
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define BIT(x) (1 << (x))
#define max(a, b) ((a) > (b) ? (a) : (b))
#ifndef TRUE
#define TRUE 1
......@@ -259,7 +260,7 @@ static inline void list_add_tail(struct list_head *new, struct list_head *head)
__list_add(new, head->prev, head);
}
static inline void __list_del(struct list_head * prev, struct list_head * next)
static inline void __list_del(struct list_head *prev, struct list_head *next)
{
next->prev = prev;
prev->next = next;
......@@ -272,6 +273,13 @@ static inline void list_del(struct list_head *entry)
entry->prev = LIST_POISON2;
}
static inline void list_del_init(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = NULL;
entry->prev = NULL;
}
static inline int list_hashed(const struct list_head *n)
{
return n->next != n && n->next != NULL;
......@@ -282,8 +290,17 @@ static inline int list_empty(const struct list_head *n)
return n->next == n;
}
static inline struct list_head *__list_pop(struct list_head *head)
{
struct list_head *n = head->next;
list_del_init(n);
return n;
}
#define list_entry(ptr, type, member) container_of(ptr,type,member)
#define list_pop(head, type, member) container_of(__list_pop(head),type,member)
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
......
......@@ -15,6 +15,7 @@
#include "apk_version.h"
#include "apk_hash.h"
#include "apk_io.h"
#include "apk_solver_data.h"
struct apk_database;
struct apk_name;
......@@ -60,7 +61,6 @@ struct apk_sign_ctx {
struct apk_dependency {
struct apk_name *name;
apk_blob_t *version;
unsigned solver_state : 22;
unsigned repository_tag : 6;
unsigned conflict : 1;
unsigned result_mask : 3;
......@@ -86,6 +86,7 @@ struct apk_installed_package {
struct apk_package {
apk_hash_node hash_node;
union {
struct apk_solver_package_state ss;
int state_int;
void *state_ptr;
};
......@@ -98,7 +99,6 @@ struct apk_package {
struct apk_dependency_array *depends, *install_if, *provides;
size_t installed_size, size;
time_t build_time;
unsigned int topology_hard;
unsigned repos : APK_MAX_REPOS;
struct apk_checksum csum;
};
......
/* apk_solver.h - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
* Copyright (C) 2008-2013 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
......@@ -15,22 +15,18 @@
struct apk_name;
struct apk_package;
struct apk_solution_entry {
struct apk_package *pkg;
unsigned short repository_tag : 15;
unsigned reinstall : 1;
};
APK_ARRAY(apk_solution_array, struct apk_solution_entry);
struct apk_change {
struct apk_package *oldpkg;
struct apk_package *newpkg;
unsigned short repository_tag : 15;
struct apk_package *old_pkg;
struct apk_package *new_pkg;
unsigned old_repository_tag : 15;
unsigned new_repository_tag : 15;
unsigned reinstall : 1;
};
APK_ARRAY(apk_change_array, struct apk_change);
struct apk_changeset {
int num_install, num_remove, num_adjust;
int num_total_changes;
struct apk_change_array *changes;
};
......@@ -44,17 +40,16 @@ void apk_solver_set_name_flags(struct apk_name *name,
int apk_solver_solve(struct apk_database *db,
unsigned short solver_flags,
struct apk_dependency_array *world,
struct apk_solution_array **solution,
struct apk_changeset *changeset);
int apk_solver_commit_changeset(struct apk_database *db,
struct apk_changeset *changeset,
struct apk_dependency_array *world);
void apk_solver_print_errors(struct apk_database *db,
struct apk_solution_array *solution,
struct apk_dependency_array *world,
int unsatisfiable);
int apk_solver_commit(struct apk_database *db,
unsigned short solver_flags,
struct apk_changeset *changeset,
struct apk_dependency_array *world);
int apk_solver_commit(struct apk_database *db, unsigned short solver_flags,
struct apk_dependency_array *world);
#endif
......
......@@ -16,39 +16,33 @@
#include "apk_defines.h"
#include "apk_provider_data.h"
struct apk_score {
uint32_t unsatisfied;
uint32_t non_preferred_actions;
uint32_t non_preferred_pinnings;
uint32_t preference;
};
struct apk_solver_name_state {
/* dynamic */
struct list_head unsolved_list;
struct apk_score minimum_penalty;
struct list_head dirty_list;
struct list_head unresolved_list;
struct apk_provider chosen;
unsigned int last_touched_decision;
unsigned short requirers;
unsigned short install_ifs;
unsigned short preferred_pinning;
unsigned short locked;
unsigned int preferred_pinning;
/* 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 short requirers;
unsigned short merge_index;
unsigned short max_dep_chain;
unsigned seen : 1;
unsigned in_world_dependency : 1;
unsigned in_changeset : 1;
unsigned reevaluate_deps : 1;
unsigned reevaluate_iif : 1;
unsigned locked : 1;
unsigned has_iif : 1;
unsigned has_options : 1;
};
/* dynamic state flags */
unsigned none_excluded : 1;
unsigned name_touched : 1;
unsigned preferred_chosen : 1;
struct apk_solver_package_state {
unsigned short conflicts;
unsigned seen : 1;
unsigned available : 1;
unsigned in_changeset : 1;
unsigned iif_triggered : 1;
unsigned max_dep_chain : 10;
};
#endif
......@@ -34,7 +34,7 @@ static int cache_download(struct apk_database *db)
char item[PATH_MAX], cacheitem[PATH_MAX];
int i, r, ret = 0;
r = apk_solver_solve(db, 0, db->world, NULL, &changeset);
r = apk_solver_solve(db, 0, db->world, &changeset);
if (r < 0) {
apk_error("Unable to select packages. Run apk fix.");
return r;
......@@ -42,9 +42,8 @@ static int cache_download(struct apk_database *db)
for (i = 0; i < changeset.changes->num; i++) {
change = &changeset.changes->item[i];
pkg = change->newpkg;
if (pkg->repos & db->local_repos)
pkg = change->new_pkg;
if ((pkg == NULL) || (pkg->repos & db->local_repos))
continue;
repo = apk_db_select_repo(db, pkg);
......
This diff is collapsed.
......@@ -1751,9 +1751,11 @@ static int fire_triggers(apk_hash_item item, void *ctx)
continue;
/* And place holder for script name */
if (ipkg->pending_triggers->num == 0)
if (ipkg->pending_triggers->num == 0) {
*apk_string_array_add(&ipkg->pending_triggers) =
NULL;
db->pending_triggers++;
}
*apk_string_array_add(&ipkg->pending_triggers) =
dbd->rooted_name;
break;
......@@ -1763,20 +1765,10 @@ static int fire_triggers(apk_hash_item item, void *ctx)
return 0;
}
struct apk_package_array *apk_db_get_pending_triggers(struct apk_database *db)
int apk_db_fire_triggers(struct apk_database *db)
{
struct apk_installed_package *ipkg;
struct apk_package_array *pkgs = NULL;
apk_package_array_init(&pkgs);
apk_hash_foreach(&db->installed.dirs, fire_triggers, db);
list_for_each_entry(ipkg, &db->installed.triggers, trigger_pkgs_list) {
if (ipkg->pending_triggers->num == 0)
continue;
*apk_package_array_add(&pkgs) = ipkg->pkg;
}
return pkgs;
return db->pending_triggers;
}
int apk_db_cache_active(struct apk_database *db)
......@@ -1904,6 +1896,20 @@ int apk_repo_format_filename(char *buf, size_t len,
return n;
}
unsigned int apk_db_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;
}
static struct apk_bstream *apk_repo_file_open(struct apk_repository *repo,
apk_blob_t *arch,
const char *file,
......
......@@ -143,7 +143,6 @@ static int del_main(void *pctx, struct apk_database *db, int argc, char **argv)
struct del_ctx *ctx = (struct del_ctx *) pctx;
struct apk_name **name;
struct apk_changeset changeset = {};
struct apk_solution_array *solution = NULL;
struct not_deleted_ctx ndctx = {};
int i, j, r = 0;
......@@ -159,11 +158,13 @@ static int del_main(void *pctx, struct apk_database *db, int argc, char **argv)
delete_from_world, ctx);
}
r = apk_solver_solve(db, 0, ctx->world, &solution, &changeset);
r = apk_solver_solve(db, 0, ctx->world, &changeset);
if (r == 0 || (apk_flags & APK_FORCE)) {
/* check for non-deleted package names */
for (i = 0; i < solution->num; i++) {
struct apk_package *pkg = solution->item[i].pkg;
for (i = 0; i < changeset.changes->num; i++) {
struct apk_package *pkg = changeset.changes->item[i].new_pkg;
if (pkg == NULL)
continue;
pkg->name->state_ptr = pkg;
for (j = 0; j < pkg->provides->num; j++)
pkg->provides->item[j].name->state_ptr = pkg;
......@@ -183,9 +184,8 @@ static int del_main(void *pctx, struct apk_database *db, int argc, char **argv)
apk_solver_commit_changeset(db, &changeset, ctx->world);
r = 0;
} else {
apk_solver_print_errors(db, solution, ctx->world, r);
apk_solver_print_errors(db, &changeset, ctx->world);
}
apk_solution_array_free(&solution);
apk_dependency_array_free(&ctx->world);
return r;
......
......@@ -186,7 +186,7 @@ static int fetch_main(void *ctx, struct apk_database *db, int argc, char **argv)
apk_dependency_array_init(&world);
*apk_dependency_array_add(&world) = dep;
r = apk_solver_solve(db, 0, world, NULL, &changeset);
r = apk_solver_solve(db, 0, world, &changeset);
apk_dependency_array_free(&world);
if (r != 0) {
apk_error("Unable to install '%s'", argv[i]);
......@@ -195,7 +195,7 @@ static int fetch_main(void *ctx, struct apk_database *db, int argc, char **argv)
for (j = 0; j < changeset.changes->num; j++) {
struct apk_change *change = &changeset.changes->item[j];
r = fetch_package(fctx, db, change->newpkg);
r = fetch_package(fctx, db, change->new_pkg);
if (r != 0)
goto err;
}
......
......@@ -16,8 +16,6 @@
#include "apk_print.h"
#include "apk_solver.h"
/* FIXME: reimplement fix applet */
struct fix_ctx {
unsigned short solver_flags;
int fix_depends : 1;
......
This diff is collapsed.
......@@ -45,23 +45,22 @@ int apk_do_self_upgrade(struct apk_database *db, unsigned short solver_flags)
{
struct apk_name *name;
struct apk_changeset changeset = {};
struct apk_solution_array *solution = NULL;
int r;
name = apk_db_get_name(db, APK_BLOB_STR("apk-tools"));
apk_solver_set_name_flags(name, solver_flags, solver_flags);
db->performing_self_update = 1;
r = apk_solver_solve(db, 0, db->world, &solution, &changeset);
r = apk_solver_solve(db, 0, db->world, &changeset);
if (r != 0) {
if ((r > 0) && (apk_flags & APK_FORCE))
r = 0;
else
apk_solver_print_errors(db, solution, db->world, r);
apk_solver_print_errors(db, &changeset, db->world);
goto ret;
}
if (changeset.changes->num == 0)
if (changeset.num_install + changeset.num_remove + changeset.num_adjust == 0)
goto ret;
if (apk_flags & APK_SIMULATE) {
......@@ -83,7 +82,6 @@ int apk_do_self_upgrade(struct apk_database *db, unsigned short solver_flags)
exit(1);
ret:
apk_solution_array_free(&solution);
apk_change_array_free(&changeset.changes);
db->performing_self_update = 0;
......
......@@ -2,5 +2,5 @@
--test-repo conflict.repo
add a b>1
@EXPECT
ERROR: 1 unsatisfiable dependencies:
ERROR: unsatisfiable dependencies:
world: b>1
......@@ -2,5 +2,5 @@
--test-repo complicated1.repo
add a d>1.5
@EXPECT
ERROR: 1 unsatisfiable dependencies:
ERROR: unsatisfiable dependencies:
b-1: d<2.0
......@@ -2,5 +2,5 @@
--test-repo complicated1.repo
add a d<1.5
@EXPECT
ERROR: 1 unsatisfiable dependencies:
ERROR: unsatisfiable dependencies:
world: d<1.5
......@@ -2,5 +2,5 @@
--test-repo complicated1.repo
add a !b
@EXPECT
ERROR: 1 unsatisfiable dependencies:
ERROR: unsatisfiable dependencies:
a-3: b
......@@ -2,5 +2,5 @@
--test-repo complicated1.repo
add a nonexistant
@EXPECT
ERROR: 1 unsatisfiable dependencies:
ERROR: unsatisfiable dependencies:
world: nonexistant
......@@ -2,5 +2,5 @@
--test-repo complicated1.repo
add a>2
@EXPECT
ERROR: 1 unsatisfiable dependencies:
ERROR: unsatisfiable dependencies:
b-1: d<2.0
......@@ -2,7 +2,7 @@
--test-repo provides.repo
add mail-reader
@EXPECT
ERROR: 1 unsatisfiable dependencies:
ERROR: unsatisfiable dependencies:
world: mail-reader
mail-reader is a virtual package provided by:
......
......@@ -2,5 +2,5 @@
--test-repo provides.repo
add server-a server-b
@EXPECT
ERROR: 1 unsatisfiable dependencies:
ERROR: unsatisfiable dependencies:
world: server-a
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment