solver: prune broken world dependencies with --force

mostly useful for reboot, when all packages are not available.
cute-signatures
Timo Teräs 2013-06-12 17:00:43 +03:00
parent 59678309ea
commit 25ff68a87e
9 changed files with 84 additions and 36 deletions

View File

@ -61,6 +61,7 @@ struct apk_sign_ctx {
struct apk_dependency { struct apk_dependency {
struct apk_name *name; struct apk_name *name;
apk_blob_t *version; apk_blob_t *version;
unsigned broken : 1;
unsigned repository_tag : 6; unsigned repository_tag : 6;
unsigned conflict : 1; unsigned conflict : 1;
unsigned result_mask : 3; unsigned result_mask : 3;

View File

@ -49,6 +49,7 @@ struct apk_solver_package_state {
unsigned dependencies_merged : 1; unsigned dependencies_merged : 1;
unsigned in_changeset : 1; unsigned in_changeset : 1;
unsigned iif_triggered : 1; unsigned iif_triggered : 1;
unsigned error : 1;
}; };
#endif #endif

View File

@ -536,17 +536,11 @@ int apk_solver_commit(struct apk_database *db,
} }
r = apk_solver_solve(db, solver_flags, world, &changeset); r = apk_solver_solve(db, solver_flags, world, &changeset);
if (r < 0) 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); r = apk_solver_commit_changeset(db, &changeset, world);
} else { else
/* Failure -- print errors */
apk_solver_print_errors(db, &changeset, world); apk_solver_print_errors(db, &changeset, world);
}
apk_change_array_free(&changeset.changes);
apk_change_array_free(&changeset.changes);
return r; return r;
} }

View File

@ -159,7 +159,7 @@ static int del_main(void *pctx, struct apk_database *db, int argc, char **argv)
} }
r = apk_solver_solve(db, 0, ctx->world, &changeset); r = apk_solver_solve(db, 0, ctx->world, &changeset);
if (r == 0 || (apk_flags & APK_FORCE)) { if (r == 0) {
/* check for non-deleted package names */ /* check for non-deleted package names */
for (i = 0; i < changeset.changes->num; i++) { for (i = 0; i < changeset.changes->num; i++) {
struct apk_package *pkg = changeset.changes->item[i].new_pkg; struct apk_package *pkg = changeset.changes->item[i].new_pkg;

View File

@ -78,12 +78,12 @@ static unsigned int get_pkg_repos(struct apk_database *db, struct apk_package *p
return pkg->repos | (pkg->ipkg ? db->repo_tags[pkg->ipkg->repository_tag].allowed_repos : 0); return pkg->repos | (pkg->ipkg ? db->repo_tags[pkg->ipkg->repository_tag].allowed_repos : 0);
} }
static void foreach_dependency(struct apk_solver_state *ss, struct apk_dependency_array *deps, static void foreach_dependency(struct apk_solver_state *ss, struct apk_package *ppkg, struct apk_dependency_array *deps,
void (*func)(struct apk_solver_state *ss, struct apk_dependency *dep)) void (*func)(struct apk_solver_state *ss, struct apk_package *ppkg, struct apk_dependency *dep))
{ {
int i; int i;
for (i = 0; i < deps->num; i++) for (i = 0; i < deps->num; i++)
func(ss, &deps->item[i]); func(ss, ppkg, &deps->item[i]);
} }
static void foreach_name(struct apk_solver_state *ss, struct apk_name_array *names, static void foreach_name(struct apk_solver_state *ss, struct apk_name_array *names,
@ -126,6 +126,14 @@ static void foreach_rinstall_if_pkg(
} }
} }
static void mark_error(struct apk_solver_state *ss, struct apk_package *pkg)
{
if (pkg == NULL || pkg->ss.error)
return;
pkg->ss.error = 1;
ss->errors++;
}
static void queue_dirty(struct apk_solver_state *ss, struct apk_name *name) static void queue_dirty(struct apk_solver_state *ss, struct apk_name *name)
{ {
if (list_hashed(&name->ss.dirty_list) || name->ss.locked || if (list_hashed(&name->ss.dirty_list) || name->ss.locked ||
@ -199,7 +207,8 @@ static int dependency_satisfiable(struct apk_solver_state *ss, struct apk_depend
} }
static void discover_name(struct apk_solver_state *ss, struct apk_name *name); static void discover_name(struct apk_solver_state *ss, struct apk_name *name);
static void discover_names(struct apk_solver_state *ss, struct apk_dependency *dep)
static void discover_names(struct apk_solver_state *ss, struct apk_package *ppkg, struct apk_dependency *dep)
{ {
discover_name(ss, dep->name); discover_name(ss, dep->name);
} }
@ -227,7 +236,7 @@ static void discover_name(struct apk_solver_state *ss, struct apk_name *name)
pkg->ss.tag_ok = !!(repos & ss->default_repos); pkg->ss.tag_ok = !!(repos & ss->default_repos);
pkg->ss.tag_preferred = !!(repos & ss->default_repos); pkg->ss.tag_preferred = !!(repos & ss->default_repos);
foreach_dependency(ss, pkg->depends, discover_names); foreach_dependency(ss, pkg, pkg->depends, discover_names);
for (j = 0; j < pkg->depends->num; j++) for (j = 0; j < pkg->depends->num; j++)
pkg->ss.max_dep_chain = max(pkg->ss.max_dep_chain, pkg->ss.max_dep_chain = max(pkg->ss.max_dep_chain,
pkg->depends->item[j].name->ss.max_dep_chain+1); pkg->depends->item[j].name->ss.max_dep_chain+1);
@ -264,7 +273,7 @@ static void inherit_pinning(struct apk_solver_state *ss, struct apk_package *pkg
} }
} }
static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency *dep) static void apply_constraint(struct apk_solver_state *ss, struct apk_package *ppkg, struct apk_dependency *dep)
{ {
struct apk_name *name = dep->name; struct apk_name *name = dep->name;
unsigned int solver_flags_inherit = ss->solver_flags_inherit; unsigned int solver_flags_inherit = ss->solver_flags_inherit;
@ -503,8 +512,9 @@ static void assign_name(struct apk_solver_state *ss, struct apk_name *name, stru
if (p.version == &apk_null_blob && if (p.version == &apk_null_blob &&
name->ss.chosen.version == &apk_null_blob) name->ss.chosen.version == &apk_null_blob)
return; return;
/* Othewise providing locked item is an error */ /* Conflict: providing same name */
ss->errors++; mark_error(ss, p.pkg);
mark_error(ss, name->ss.chosen.pkg);
return; return;
} }
@ -560,11 +570,13 @@ static void select_package(struct apk_solver_state *ss, struct apk_name *name)
if (chosen.version == &apk_null_blob) { if (chosen.version == &apk_null_blob) {
/* Pure virtual package */ /* Pure virtual package */
assign_name(ss, name, provider_none); assign_name(ss, name, provider_none);
ss->errors++; ss->errors += (name->ss.requirers > 0);
return; return;
} }
if (!pkg->ss.available || !pkg->ss.tag_ok) if (!pkg->ss.available || !pkg->ss.tag_ok) {
ss->errors++; /* Selecting broken or unallowed package */
mark_error(ss, pkg);
}
dbg_printf("selecting: " PKG_VER_FMT ", available: %d\n", PKG_VER_PRINTF(pkg), pkg->ss.available); dbg_printf("selecting: " PKG_VER_FMT ", available: %d\n", PKG_VER_PRINTF(pkg), pkg->ss.available);
assign_name(ss, pkg->name, APK_PROVIDER_FROM_PACKAGE(pkg)); assign_name(ss, pkg->name, APK_PROVIDER_FROM_PACKAGE(pkg));
for (i = 0; i < pkg->provides->num; i++) { for (i = 0; i < pkg->provides->num; i++) {
@ -573,19 +585,18 @@ static void select_package(struct apk_solver_state *ss, struct apk_name *name)
} }
ss->solver_flags_inherit = pkg->ss.solver_flags_inheritable; ss->solver_flags_inherit = pkg->ss.solver_flags_inheritable;
ss->pinning_inherit = pkg->ss.pinning_allowed; ss->pinning_inherit = pkg->ss.pinning_allowed;
foreach_dependency(ss, pkg->depends, apply_constraint); foreach_dependency(ss, pkg, pkg->depends, apply_constraint);
ss->solver_flags_inherit = 0; ss->solver_flags_inherit = 0;
ss->pinning_inherit = 0; ss->pinning_inherit = 0;
ss->num_selections++; ss->num_selections++;
} else { } else {
dbg_printf("selecting: %s [unassigned]\n", name->name); dbg_printf("selecting: %s [unassigned]\n", name->name);
assign_name(ss, name, provider_none); assign_name(ss, name, provider_none);
if (name->ss.requirers) ss->errors += (name->ss.requirers > 0);
ss->errors++;
} }
} }
static void generate_change_dep(struct apk_solver_state *ss, struct apk_dependency *dep); static void generate_change_dep(struct apk_solver_state *ss, struct apk_package *ppkg, struct apk_dependency *dep);
static void generate_change_iif(struct apk_solver_state *ss, struct apk_name *name); static void generate_change_iif(struct apk_solver_state *ss, struct apk_name *name);
static void generate_change(struct apk_solver_state *ss, struct apk_name *name) static void generate_change(struct apk_solver_state *ss, struct apk_name *name)
@ -602,7 +613,7 @@ static void generate_change(struct apk_solver_state *ss, struct apk_name *name)
pkg->ss.in_changeset = 1; pkg->ss.in_changeset = 1;
pkg->name->ss.in_changeset = 1; pkg->name->ss.in_changeset = 1;
foreach_dependency(ss, pkg->depends, generate_change_dep); foreach_dependency(ss, pkg, pkg->depends, generate_change_dep);
change = &changeset->changes->item[ss->num_solution_entries++]; change = &changeset->changes->item[ss->num_solution_entries++];
dbg_printf("Selecting: "PKG_VER_FMT"%s\n", PKG_VER_PRINTF(pkg), pkg->ss.available ? "" : " [NOT AVAILABLE]"); dbg_printf("Selecting: "PKG_VER_FMT"%s\n", PKG_VER_PRINTF(pkg), pkg->ss.available ? "" : " [NOT AVAILABLE]");
@ -646,14 +657,18 @@ static void generate_change_iif(struct apk_solver_state *ss, struct apk_name *na
generate_change(ss, name); generate_change(ss, name);
} }
static void generate_change_dep(struct apk_solver_state *ss, struct apk_dependency *dep) static void generate_change_dep(struct apk_solver_state *ss, struct apk_package *ppkg, struct apk_dependency *dep)
{ {
struct apk_name *name = dep->name; struct apk_name *name = dep->name;
struct apk_package *pkg = name->ss.chosen.pkg;
if (!apk_dep_is_provided(dep, &name->ss.chosen)) if (!apk_dep_is_provided(dep, &name->ss.chosen))
ss->errors++; mark_error(ss, ppkg);
generate_change(ss, name); generate_change(ss, name);
if (pkg && pkg->ss.error)
mark_error(ss, ppkg);
} }
static void generate_changeset(struct apk_solver_state *ss, struct apk_dependency_array *world) static void generate_changeset(struct apk_solver_state *ss, struct apk_dependency_array *world)
@ -668,7 +683,7 @@ static void generate_changeset(struct apk_solver_state *ss, struct apk_dependenc
} }
apk_change_array_resize(&ss->changeset->changes, ss->num_selections); apk_change_array_resize(&ss->changeset->changes, ss->num_selections);
foreach_dependency(ss, world, generate_change_dep); foreach_dependency(ss, NULL, world, generate_change_dep);
/* FIXME: could order better the removals of unneeded packages */ /* FIXME: could order better the removals of unneeded packages */
list_for_each_entry(ipkg, &ss->db->installed.packages, installed_pkgs_list) { list_for_each_entry(ipkg, &ss->db->installed.packages, installed_pkgs_list) {
@ -721,6 +736,7 @@ int apk_solver_solve(struct apk_database *db,
struct apk_solver_state ss_data, *ss = &ss_data; struct apk_solver_state ss_data, *ss = &ss_data;
int i; int i;
restart:
memset(ss, 0, sizeof(*ss)); memset(ss, 0, sizeof(*ss));
ss->db = db; ss->db = db;
ss->changeset = changeset; ss->changeset = changeset;
@ -728,17 +744,19 @@ int apk_solver_solve(struct apk_database *db,
list_init(&ss->dirty_head); list_init(&ss->dirty_head);
list_init(&ss->unresolved_head); list_init(&ss->unresolved_head);
foreach_dependency(ss, world, discover_names); foreach_dependency(ss, NULL, world, discover_names);
dbg_printf("applying world\n"); dbg_printf("applying world\n");
ss->prefer_pinning = 1; ss->prefer_pinning = 1;
ss->solver_flags_inherit = solver_flags; ss->solver_flags_inherit = solver_flags;
for (i = 0; i < world->num; i++) { for (i = 0; i < world->num; i++) {
struct apk_dependency *dep = &world->item[i]; struct apk_dependency *dep = &world->item[i];
if (dep->broken)
continue;
name = dep->name; name = dep->name;
name->ss.in_world_dependency = 1; name->ss.in_world_dependency = 1;
ss->pinning_inherit = BIT(dep->repository_tag); ss->pinning_inherit = BIT(dep->repository_tag);
apply_constraint(ss, dep); apply_constraint(ss, NULL, dep);
} }
ss->solver_flags_inherit = 0; ss->solver_flags_inherit = 0;
ss->pinning_inherit = 0; ss->pinning_inherit = 0;
@ -776,9 +794,23 @@ int apk_solver_solve(struct apk_database *db,
generate_changeset(ss, world); generate_changeset(ss, world);
if (ss->errors && (apk_flags & APK_FORCE)) {
for (i = 0; i < world->num; i++) {
struct apk_dependency *dep = &world->item[i];
struct apk_name *name = dep->name;
struct apk_package *pkg = name->ss.chosen.pkg;
if (pkg == NULL || pkg->ss.error) {
dep->broken = 1;
dbg_printf("disabling broken world dep: %s", name->name);
}
}
apk_hash_foreach(&db->available.names, free_state, NULL); apk_hash_foreach(&db->available.names, free_state, NULL);
apk_hash_foreach(&db->available.packages, free_package, NULL); apk_hash_foreach(&db->available.packages, free_package, NULL);
goto restart;
}
apk_hash_foreach(&db->available.names, free_state, NULL);
apk_hash_foreach(&db->available.packages, free_package, NULL);
dbg_printf("solver done, errors=%d\n", ss->errors); dbg_printf("solver done, errors=%d\n", ss->errors);
return ss->errors; return ss->errors;

View File

@ -53,9 +53,6 @@ int apk_do_self_upgrade(struct apk_database *db, unsigned short solver_flags)
r = apk_solver_solve(db, 0, db->world, &changeset); r = apk_solver_solve(db, 0, db->world, &changeset);
if (r != 0) { if (r != 0) {
if ((r > 0) && (apk_flags & APK_FORCE))
r = 0;
else
apk_solver_print_errors(db, &changeset, db->world); apk_solver_print_errors(db, &changeset, db->world);
goto ret; goto ret;
} }

View File

@ -24,3 +24,10 @@ V:2
S:1 S:1
I:1 I:1
C:Q1hdUpqRv5mYgJEqW52UmVsvmeedd=
P:broken
V:1
S:1
I:1
D:missing-dependency

8
test/basic10.test Normal file
View File

@ -0,0 +1,8 @@
@ARGS
--test-repo basic.repo
--force
add a not-in-repo
@EXPECT
(1/2) Installing b (2)
(2/2) Installing a (2)
OK: 0 MiB in 0 packages

8
test/basic11.test Normal file
View File

@ -0,0 +1,8 @@
@ARGS
--test-repo basic.repo
--force
add a broken not-in-repo
@EXPECT
(1/2) Installing b (2)
(2/2) Installing a (2)
OK: 0 MiB in 0 packages