solver: rewrite as deductive solver -- core features
Implementing basic dependency handling, install_if and awareness of pinning.cute-signatures
parent
f292a85867
commit
b8c44536ca
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* 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 int preferred_pinning;
|
||||
|
||||
unsigned short requirers;
|
||||
unsigned short install_ifs;
|
||||
unsigned short preferred_pinning;
|
||||
unsigned short locked;
|
||||
|
||||
/* one time prepare/finish flags */
|
||||
unsigned solver_flags_local : 4;
|
||||
unsigned solver_flags_inheritable : 4;
|
||||
|
||||
unsigned decision_counted : 1;
|
||||
unsigned originally_installed : 1;
|
||||
unsigned has_available_pkgs : 1;
|
||||
unsigned in_changeset : 1;
|
||||
unsigned 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);
|
||||
|
|
|
@ -0,0 +1,547 @@
|
|||
/* commit.c - Alpine Package Keeper (APK)
|
||||
* Apply solver calculated changes to database.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include "apk_defines.h"
|
||||
#include "apk_database.h"
|
||||
#include "apk_package.h"
|
||||
#include "apk_solver.h"
|
||||
|
||||
#include "apk_print.h"
|
||||
|
||||
static inline int pkg_available(struct apk_database *db, struct apk_package *pkg)
|
||||
{
|
||||
if (pkg->repos & db->available_repos)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int print_change(struct apk_database *db, struct apk_change *change,
|
||||
int cur, int total)
|
||||
{
|
||||
struct apk_name *name;
|
||||
struct apk_package *oldpkg = change->old_pkg;
|
||||
struct apk_package *newpkg = change->new_pkg;
|
||||
const char *msg = NULL;
|
||||
char status[32], n[512], *nameptr;
|
||||
apk_blob_t *oneversion = NULL;
|
||||
int r;
|
||||
|
||||
snprintf(status, sizeof(status), "(%i/%i)", cur+1, total);
|
||||
status[sizeof(status) - 1] = 0;
|
||||
|
||||
name = newpkg ? newpkg->name : oldpkg->name;
|
||||
if (change->new_repository_tag > 0) {
|
||||
snprintf(n, sizeof(n), "%s@" BLOB_FMT,
|
||||
name->name,
|
||||
BLOB_PRINTF(*db->repo_tags[change->new_repository_tag].name));
|
||||
n[sizeof(n) - 1] = 0;
|
||||
nameptr = n;
|
||||
} else {
|
||||
nameptr = name->name;
|
||||
}
|
||||
|
||||
if (oldpkg == NULL) {
|
||||
msg = "Installing";
|
||||
oneversion = newpkg->version;
|
||||
} else if (newpkg == NULL) {
|
||||
msg = "Purging";
|
||||
oneversion = oldpkg->version;
|
||||
} else if (newpkg == oldpkg) {
|
||||
if (change->reinstall) {
|
||||
if (pkg_available(db, newpkg))
|
||||
msg = "Re-installing";
|
||||
else
|
||||
msg = "[APK unavailable, skipped] Re-installing";
|
||||
} else if (change->old_repository_tag != change->new_repository_tag) {
|
||||
msg = "Updating pinning";
|
||||
}
|
||||
oneversion = newpkg->version;
|
||||
} else {
|
||||
r = apk_pkg_version_compare(newpkg, oldpkg);
|
||||
switch (r) {
|
||||
case APK_VERSION_LESS:
|
||||
msg = "Downgrading";
|
||||
break;
|
||||
case APK_VERSION_EQUAL:
|
||||
msg = "Replacing";
|
||||
break;
|
||||
case APK_VERSION_GREATER:
|
||||
msg = "Upgrading";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (msg == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (oneversion) {
|
||||
apk_message("%s %s %s (" BLOB_FMT ")",
|
||||
status, msg, nameptr,
|
||||
BLOB_PRINTF(*oneversion));
|
||||
} else {
|
||||
apk_message("%s %s %s (" BLOB_FMT " -> " BLOB_FMT ")",
|
||||
status, msg, nameptr,
|
||||
BLOB_PRINTF(*oldpkg->version),
|
||||
BLOB_PRINTF(*newpkg->version));
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
struct apk_stats {
|
||||
unsigned int changes;
|
||||
unsigned int bytes;
|
||||
unsigned int packages;
|
||||
};
|
||||
|
||||
static void count_change(struct apk_change *change, struct apk_stats *stats)
|
||||
{
|
||||
if (change->new_pkg != change->old_pkg) {
|
||||
if (change->new_pkg != NULL) {
|
||||
stats->bytes += change->new_pkg->installed_size;
|
||||
stats->packages ++;
|
||||
}
|
||||
if (change->old_pkg != NULL)
|
||||
stats->packages ++;
|
||||
stats->changes++;
|
||||
} else if (change->reinstall || change->new_repository_tag != change->old_repository_tag) {
|
||||
stats->changes++;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
struct progress {
|
||||
struct apk_stats done;
|
||||
struct apk_stats total;
|
||||
struct apk_package *pkg;
|
||||
size_t percent;
|
||||
int progress_fd;
|
||||
};
|
||||
|
||||
static void update_progress(struct progress *prog, size_t percent, int force)
|
||||
{
|
||||
if (prog->percent == percent && !force)
|
||||
return;
|
||||
prog->percent = percent;
|
||||
if (apk_flags & APK_PROGRESS)
|
||||
draw_progress(percent);
|
||||
if (prog->progress_fd != 0) {
|
||||
char buf[8];
|
||||
size_t n = snprintf(buf, sizeof(buf), "%zu\n", percent);
|
||||
write(prog->progress_fd, buf, n);
|
||||
}
|
||||
}
|
||||
|
||||
static void progress_cb(void *ctx, size_t pkg_percent)
|
||||
{
|
||||
struct progress *prog = (struct progress *) ctx;
|
||||
size_t partial = 0, percent;
|
||||
|
||||
if (prog->pkg != NULL)
|
||||
partial = muldiv(pkg_percent, prog->pkg->installed_size, APK_PROGRESS_SCALE);
|
||||
percent = muldiv(100, prog->done.bytes + prog->done.packages + partial,
|
||||
prog->total.bytes + prog->total.packages);
|
||||
update_progress(prog, percent, pkg_percent == 0);
|
||||
}
|
||||
|
||||
static int dump_packages(struct apk_changeset *changeset,
|
||||
int (*cmp)(struct apk_change *change),
|
||||
const char *msg)
|
||||
{
|
||||
struct apk_change *change;
|
||||
struct apk_name *name;
|
||||
struct apk_indent indent = { .indent = 2 };
|
||||
int match = 0, i;
|
||||
|
||||
for (i = 0; i < changeset->changes->num; i++) {
|
||||
change = &changeset->changes->item[i];
|
||||
if (!cmp(change))
|
||||
continue;
|
||||
if (match == 0)
|
||||
printf("%s:\n", msg);
|
||||
if (change->new_pkg != NULL)
|
||||
name = change->new_pkg->name;
|
||||
else
|
||||
name = change->old_pkg->name;
|
||||
|
||||
apk_print_indented(&indent, APK_BLOB_STR(name->name));
|
||||
match++;
|
||||
}
|
||||
if (match)
|
||||
printf("\n");
|
||||
return match;
|
||||
}
|
||||
|
||||
static int cmp_remove(struct apk_change *change)
|
||||
{
|
||||
return change->new_pkg == NULL;
|
||||
}
|
||||
|
||||
static int cmp_new(struct apk_change *change)
|
||||
{
|
||||
return change->old_pkg == NULL;
|
||||
}
|
||||
|
||||
static int cmp_downgrade(struct apk_change *change)
|
||||
{
|
||||
if (change->new_pkg == NULL || change->old_pkg == NULL)
|
||||
return 0;
|
||||
if (apk_pkg_version_compare(change->new_pkg, change->old_pkg)
|
||||
& APK_VERSION_LESS)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmp_upgrade(struct apk_change *change)
|
||||
{
|
||||
if (change->new_pkg == NULL || change->old_pkg == 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->new_pkg, change->old_pkg) &
|
||||
(APK_VERSION_GREATER | APK_VERSION_EQUAL)) &&
|
||||
(change->new_pkg != change->old_pkg))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void run_triggers(struct apk_database *db, struct apk_changeset *changeset)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (apk_db_fire_triggers(db) == 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < changeset->changes->num; i++) {
|
||||
struct apk_package *pkg = changeset->changes->item[i].new_pkg;
|
||||
struct apk_installed_package *ipkg;
|
||||
|
||||
if (pkg == NULL)
|
||||
continue;
|
||||
|
||||
ipkg = pkg->ipkg;
|
||||
if (ipkg->pending_triggers->num == 0)
|
||||
continue;
|
||||
|
||||
*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);
|
||||
}
|
||||
}
|
||||
|
||||
int apk_solver_commit_changeset(struct apk_database *db,
|
||||
struct apk_changeset *changeset,
|
||||
struct apk_dependency_array *world)
|
||||
{
|
||||
struct progress prog;
|
||||
struct apk_change *change;
|
||||
int i, r = 0, size_diff = 0, size_unit;
|
||||
|
||||
if (apk_db_check_world(db, world) != 0) {
|
||||
apk_error("Not committing changes due to missing repository tags. Use --force to override.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (changeset->changes == NULL)
|
||||
goto all_done;
|
||||
|
||||
/* Count what needs to be done */
|
||||
memset(&prog, 0, sizeof(prog));
|
||||
prog.progress_fd = db->progress_fd;
|
||||
for (i = 0; i < changeset->changes->num; i++) {
|
||||
change = &changeset->changes->item[i];
|
||||
count_change(change, &prog.total);
|
||||
if (change->new_pkg)
|
||||
size_diff += change->new_pkg->installed_size;
|
||||
if (change->old_pkg)
|
||||
size_diff -= change->old_pkg->installed_size;
|
||||
}
|
||||
size_diff /= 1024;
|
||||
size_unit = 'K';
|
||||
if (abs(size_diff) > 10000) {
|
||||
size_diff /= 1024;
|
||||
size_unit = 'M';
|
||||
}
|
||||
|
||||
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");
|
||||
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.");
|
||||
}
|
||||
if (changeset->changes->num > 0 &&
|
||||
(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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Go through changes */
|
||||
r = 0;
|
||||
for (i = 0; i < changeset->changes->num; i++) {
|
||||
change = &changeset->changes->item[i];
|
||||
|
||||
if (print_change(db, change, prog.done.changes, prog.total.changes)) {
|
||||
prog.pkg = change->new_pkg;
|
||||
progress_cb(&prog, 0);
|
||||
|
||||
if (!(apk_flags & APK_SIMULATE)) {
|
||||
if (change->old_pkg != change->new_pkg ||
|
||||
(change->reinstall && pkg_available(db, change->new_pkg)))
|
||||
r = apk_db_install_pkg(db,
|
||||
change->old_pkg, change->new_pkg,
|
||||
progress_cb, &prog);
|
||||
if (r != 0)
|
||||
break;
|
||||
if (change->new_pkg)
|
||||
change->new_pkg->ipkg->repository_tag = change->new_repository_tag;
|
||||
}
|
||||
}
|
||||
|
||||
count_change(change, &prog.done);
|
||||
}
|
||||
update_progress(&prog, 100, 1);
|
||||
|
||||
run_triggers(db, changeset);
|
||||
|
||||
all_done:
|
||||
apk_dependency_array_copy(&db->world, world);
|
||||
apk_db_write_config(db);
|
||||
|
||||
if (r == 0 && !db->performing_self_update) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void add_name_if_virtual(struct apk_name_array **names, struct apk_name *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (name->providers->num == 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < name->providers->num; i++)
|
||||
if (name->providers->item[i].pkg->name == name)
|
||||
return;
|
||||
|
||||
for (i = 0; i < (*names)->num; i++)
|
||||
if ((*names)->item[i] == name)
|
||||
return;
|
||||
|
||||
*apk_name_array_add(names) = name;
|
||||
}
|
||||
|
||||
static void print_pinning_errors(struct apk_database *db, char *label,
|
||||
struct apk_package *pkg, unsigned int tag)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (pkg->ipkg != NULL)
|
||||
return;
|
||||
if (pkg->repos & apk_db_get_pinning_mask_repos(db, APK_DEFAULT_PINNING_MASK | BIT(tag)))
|
||||
return;
|
||||
|
||||
printf(" %s: but needs pinning:", label);
|
||||
for (i = 0; i < db->num_repo_tags; i++) {
|
||||
if (pkg->repos & db->repo_tags[i].allowed_repos)
|
||||
printf(" @" BLOB_FMT, BLOB_PRINTF(*db->repo_tags[i].name));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void print_dep_errors(struct apk_database *db, char *label,
|
||||
struct apk_dependency_array *deps,
|
||||
struct apk_name_array **names)
|
||||
{
|
||||
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;
|
||||
|
||||
pkg = (struct apk_package*) (dep->name->state_int & ~1);
|
||||
if (apk_dep_is_materialized_or_provided(dep, pkg))
|
||||
continue;
|
||||
|
||||
if (pkg == NULL)
|
||||
add_name_if_virtual(names, dep->name);
|
||||
|
||||
if (print_label) {
|
||||
print_label = 0;
|
||||
indent.x = printf(" %s:", label);
|
||||
indent.indent = indent.x + 1;
|
||||
}
|
||||
p = APK_BLOB_BUF(buf);
|
||||
apk_blob_push_dep(&p, db, dep);
|
||||
p = apk_blob_pushed(APK_BLOB_BUF(buf), p);
|
||||
apk_print_indented(&indent, p);
|
||||
}
|
||||
if (!print_label)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void print_provides_errors(struct apk_database *db, char *label,
|
||||
struct apk_package *pkg)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pkg->provides->num; i++) {
|
||||
struct apk_name *pn = pkg->provides->item[i].name;
|
||||
struct apk_provider p = APK_PROVIDER_FROM_PROVIDES(pkg, &pkg->provides->item[i]);
|
||||
struct apk_package *pkgC = (struct apk_package *) (pn->state_int & ~1);
|
||||
|
||||
if (pkgC == NULL && p.version == &apk_null_blob)
|
||||
continue;
|
||||
|
||||
if ((pkgC == pkg) && (pn->state_int & 1) == 0) {
|
||||
pn->state_int |= 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
printf(" %s provides " PROVIDER_FMT " conflicting with " PKG_VER_FMT "\n",
|
||||
label, PROVIDER_PRINTF(pn, &p), PKG_VER_PRINTF(pkgC));
|
||||
}
|
||||
}
|
||||
|
||||
void apk_solver_print_errors(struct apk_database *db,
|
||||
struct apk_changeset *changeset,
|
||||
struct apk_dependency_array *world)
|
||||
{
|
||||
struct apk_name_array *names;
|
||||
char pkgtext[256];
|
||||
int i, j;
|
||||
|
||||
apk_name_array_init(&names);
|
||||
/* FIXME: Print more rational error messages. Group failed
|
||||
* dependencies by package name. As in,
|
||||
* pkg-1.2 selected, but:
|
||||
* world: pkg>2
|
||||
*/
|
||||
apk_error("unsatisfiable dependencies:");
|
||||
|
||||
for (i = 0; i < changeset->changes->num; i++) {
|
||||
struct apk_package *pkg = changeset->changes->item[i].new_pkg;
|
||||
if (pkg == NULL)
|
||||
continue;
|
||||
pkg->state_int = 1;
|
||||
pkg->name->state_ptr = pkg;
|
||||
for (j = 0; j < pkg->provides->num; j++) {
|
||||
if (pkg->provides->item[j].version == &apk_null_blob)
|
||||
continue;
|
||||
if (pkg->provides->item[j].name->state_ptr == NULL)
|
||||
pkg->provides->item[j].name->state_ptr = pkg;
|
||||
}
|
||||
}
|
||||
|
||||
print_dep_errors(db, "world", world, &names);
|
||||
for (i = 0; i < changeset->changes->num; i++) {
|
||||
struct apk_change *change = &changeset->changes->item[i];
|
||||
struct apk_package *pkg = change->new_pkg;
|
||||
if (pkg == NULL)
|
||||
continue;
|
||||
snprintf(pkgtext, sizeof(pkgtext), PKG_VER_FMT, PKG_VER_PRINTF(pkg));
|
||||
print_pinning_errors(db, pkgtext, pkg, change->new_repository_tag);
|
||||
print_dep_errors(db, pkgtext, pkg->depends, &names);
|
||||
print_provides_errors(db, pkgtext, pkg);
|
||||
}
|
||||
|
||||
for (i = 0; i < names->num; i++) {
|
||||
struct apk_indent indent;
|
||||
struct apk_name *name = names->item[i];
|
||||
|
||||
printf("\n %s is a virtual package provided by:\n ", name->name);
|
||||
indent.x = 4;
|
||||
indent.indent = 4;
|
||||
|
||||
for (j = 0; j < name->providers->num; j++) {
|
||||
struct apk_package *pkg = name->providers->item[j].pkg;
|
||||
snprintf(pkgtext, sizeof(pkgtext), PKG_VER_FMT, PKG_VER_PRINTF(pkg));
|
||||
apk_print_indented(&indent, APK_BLOB_STR(pkgtext));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
apk_name_array_free(&names);
|
||||
}
|
||||
|
||||
int apk_solver_commit(struct apk_database *db,
|
||||
unsigned short solver_flags,
|
||||
struct apk_dependency_array *world)
|
||||
{
|
||||
struct apk_changeset changeset = {};
|
||||
int r;
|
||||
|
||||
if (apk_db_check_world(db, world) != 0) {
|
||||
apk_error("Not committing changes due to missing repository tags. Use --force to override.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = apk_solver_solve(db, solver_flags, world, &changeset);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (r == 0 || (apk_flags & APK_FORCE)) {
|
||||
/* Success -- or forced installation of bad graph */
|
||||
r = apk_solver_commit_changeset(db, &changeset, world);
|
||||
} else {
|
||||
/* Failure -- print errors */
|
||||
apk_solver_print_errors(db, &changeset, world);
|
||||
}
|
||||
apk_change_array_free(&changeset.changes);
|
||||
|
||||
return r;
|
||||
}
|
|
@ -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,
|
||||
|
|
12
src/del.c
12
src/del.c
|
@ -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;
|
||||
|
|
2372
src/solver.c
2372
src/solver.c
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue