diff --git a/src/apk_package.h b/src/apk_package.h index 55e6ceb..a5cda2f 100644 --- a/src/apk_package.h +++ b/src/apk_package.h @@ -61,6 +61,7 @@ struct apk_sign_ctx { struct apk_dependency { struct apk_name *name; apk_blob_t *version; + unsigned broken : 1; unsigned repository_tag : 6; unsigned conflict : 1; unsigned result_mask : 3; diff --git a/src/apk_solver_data.h b/src/apk_solver_data.h index fa59fd0..cc41f6d 100644 --- a/src/apk_solver_data.h +++ b/src/apk_solver_data.h @@ -49,6 +49,7 @@ struct apk_solver_package_state { unsigned dependencies_merged : 1; unsigned in_changeset : 1; unsigned iif_triggered : 1; + unsigned error : 1; }; #endif diff --git a/src/commit.c b/src/commit.c index f0e9e84..f426ba0 100644 --- a/src/commit.c +++ b/src/commit.c @@ -536,17 +536,11 @@ int apk_solver_commit(struct apk_database *db, } 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 */ + if (r == 0) r = apk_solver_commit_changeset(db, &changeset, world); - } else { - /* Failure -- print errors */ + else apk_solver_print_errors(db, &changeset, world); - } - apk_change_array_free(&changeset.changes); + apk_change_array_free(&changeset.changes); return r; } diff --git a/src/del.c b/src/del.c index ed5f63e..305843d 100644 --- a/src/del.c +++ b/src/del.c @@ -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); - if (r == 0 || (apk_flags & APK_FORCE)) { + if (r == 0) { /* check for non-deleted package names */ for (i = 0; i < changeset.changes->num; i++) { struct apk_package *pkg = changeset.changes->item[i].new_pkg; diff --git a/src/solver.c b/src/solver.c index 4823ece..71a4d41 100644 --- a/src/solver.c +++ b/src/solver.c @@ -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); } -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)) +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_package *ppkg, struct apk_dependency *dep)) { int 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, @@ -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) { 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_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); } @@ -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_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++) pkg->ss.max_dep_chain = max(pkg->ss.max_dep_chain, 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; 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 && name->ss.chosen.version == &apk_null_blob) return; - /* Othewise providing locked item is an error */ - ss->errors++; + /* Conflict: providing same name */ + mark_error(ss, p.pkg); + mark_error(ss, name->ss.chosen.pkg); return; } @@ -560,11 +570,13 @@ static void select_package(struct apk_solver_state *ss, struct apk_name *name) if (chosen.version == &apk_null_blob) { /* Pure virtual package */ assign_name(ss, name, provider_none); - ss->errors++; + ss->errors += (name->ss.requirers > 0); return; } - if (!pkg->ss.available || !pkg->ss.tag_ok) - ss->errors++; + if (!pkg->ss.available || !pkg->ss.tag_ok) { + /* 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); assign_name(ss, pkg->name, APK_PROVIDER_FROM_PACKAGE(pkg)); 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->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->pinning_inherit = 0; ss->num_selections++; } else { dbg_printf("selecting: %s [unassigned]\n", name->name); assign_name(ss, name, provider_none); - if (name->ss.requirers) - ss->errors++; + ss->errors += (name->ss.requirers > 0); } } -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(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->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++]; 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); } -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_package *pkg = name->ss.chosen.pkg; if (!apk_dep_is_provided(dep, &name->ss.chosen)) - ss->errors++; + mark_error(ss, ppkg); 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) @@ -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); - foreach_dependency(ss, world, generate_change_dep); + foreach_dependency(ss, NULL, world, generate_change_dep); /* FIXME: could order better the removals of unneeded packages */ 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; int i; +restart: memset(ss, 0, sizeof(*ss)); ss->db = db; ss->changeset = changeset; @@ -728,17 +744,19 @@ int apk_solver_solve(struct apk_database *db, list_init(&ss->dirty_head); list_init(&ss->unresolved_head); - foreach_dependency(ss, world, discover_names); + foreach_dependency(ss, NULL, world, discover_names); dbg_printf("applying world\n"); ss->prefer_pinning = 1; ss->solver_flags_inherit = solver_flags; for (i = 0; i < world->num; i++) { struct apk_dependency *dep = &world->item[i]; + if (dep->broken) + continue; name = dep->name; name->ss.in_world_dependency = 1; ss->pinning_inherit = BIT(dep->repository_tag); - apply_constraint(ss, dep); + apply_constraint(ss, NULL, dep); } ss->solver_flags_inherit = 0; ss->pinning_inherit = 0; @@ -776,9 +794,23 @@ int apk_solver_solve(struct apk_database *db, 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.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); return ss->errors; diff --git a/src/upgrade.c b/src/upgrade.c index 60b19ec..8c8284d 100644 --- a/src/upgrade.c +++ b/src/upgrade.c @@ -53,10 +53,7 @@ int apk_do_self_upgrade(struct apk_database *db, unsigned short solver_flags) 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, &changeset, db->world); + apk_solver_print_errors(db, &changeset, db->world); goto ret; } diff --git a/test/basic.repo b/test/basic.repo index 4aace18..3ca23b3 100644 --- a/test/basic.repo +++ b/test/basic.repo @@ -24,3 +24,10 @@ V:2 S:1 I:1 +C:Q1hdUpqRv5mYgJEqW52UmVsvmeedd= +P:broken +V:1 +S:1 +I:1 +D:missing-dependency + diff --git a/test/basic10.test b/test/basic10.test new file mode 100644 index 0000000..f463e8c --- /dev/null +++ b/test/basic10.test @@ -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 diff --git a/test/basic11.test b/test/basic11.test new file mode 100644 index 0000000..24f7e1e --- /dev/null +++ b/test/basic11.test @@ -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