From 426a12686e6e6dcce11616c774176c01ad0985f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Wed, 12 Jun 2013 08:45:29 +0300 Subject: [PATCH] solver: rewrite as deductive solver -- per name flags Handle properly per-name preference flags, and add test cases for testing those via fix applet. --- src/apk_solver_data.h | 6 ++++-- src/commit.c | 19 ++++++++++++------- src/solver.c | 32 ++++++++++++++++++++++++-------- test/fix1.test | 9 +++++++++ test/fix2.test | 9 +++++++++ test/fix3.test | 11 +++++++++++ test/fix4.test | 8 ++++++++ test/fix5.test | 8 ++++++++ test/fix6.test | 10 ++++++++++ 9 files changed, 95 insertions(+), 17 deletions(-) create mode 100644 test/fix1.test create mode 100644 test/fix2.test create mode 100644 test/fix3.test create mode 100644 test/fix4.test create mode 100644 test/fix5.test create mode 100644 test/fix6.test diff --git a/src/apk_solver_data.h b/src/apk_solver_data.h index 913829f..47496ac 100644 --- a/src/apk_solver_data.h +++ b/src/apk_solver_data.h @@ -37,12 +37,14 @@ struct apk_solver_name_state { }; struct apk_solver_package_state { - unsigned short conflicts; + unsigned int conflicts; + unsigned short max_dep_chain; + unsigned solver_flags : 4; + unsigned solver_flags_inheritable : 4; unsigned seen : 1; unsigned available : 1; unsigned in_changeset : 1; unsigned iif_triggered : 1; - unsigned max_dep_chain : 10; }; #endif diff --git a/src/commit.c b/src/commit.c index 660aab9..2c1d4b5 100644 --- a/src/commit.c +++ b/src/commit.c @@ -104,15 +104,16 @@ struct apk_stats { static void count_change(struct apk_change *change, struct apk_stats *stats) { - if (change->new_pkg != change->old_pkg) { + if (change->new_pkg != change->old_pkg || change->reinstall) { if (change->new_pkg != NULL) { stats->bytes += change->new_pkg->installed_size; - stats->packages ++; + stats->packages++; } if (change->old_pkg != NULL) - stats->packages ++; + stats->packages++; stats->changes++; - } else if (change->reinstall || change->new_repository_tag != change->old_repository_tag) { + } else if (change->new_repository_tag != change->old_repository_tag) { + stats->packages++; stats->changes++; } } @@ -157,12 +158,16 @@ static void update_progress(struct progress *prog, size_t percent, int force) static void progress_cb(void *ctx, size_t pkg_percent) { struct progress *prog = (struct progress *) ctx; - size_t partial = 0, percent; + size_t partial = 0, percent, total; 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); + total = prog->total.bytes + prog->total.packages; + if (total > 0) + percent = muldiv(100, prog->done.bytes + prog->done.packages + partial, + prog->total.bytes + prog->total.packages); + else + percent = 0; update_progress(prog, percent, pkg_percent == 0); } diff --git a/src/solver.c b/src/solver.c index 1e301e7..bc370f6 100644 --- a/src/solver.c +++ b/src/solver.c @@ -34,9 +34,9 @@ struct apk_solver_state { struct apk_changeset *changeset; struct list_head dirty_head; struct list_head unresolved_head; - unsigned int solver_flags; unsigned int errors; unsigned int num_selections, num_solution_entries; + unsigned int solver_flags_inherit; }; static struct apk_provider provider_none = { @@ -48,6 +48,13 @@ void apk_solver_set_name_flags(struct apk_name *name, unsigned short solver_flags, unsigned short solver_flags_inheritable) { + int i; + + for (i = 0; i < name->providers->num; i++) { + struct apk_package *pkg = name->providers->item[i].pkg; + pkg->ss.solver_flags |= solver_flags; + pkg->ss.solver_flags_inheritable |= solver_flags_inheritable; + } } static void foreach_dependency(struct apk_solver_state *ss, struct apk_dependency_array *deps, @@ -181,6 +188,7 @@ static void name_requirers_changed(struct apk_solver_state *ss, struct apk_name static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency *dep) { + unsigned int solver_flags_inherit = ss->solver_flags_inherit; struct apk_name *name = dep->name; int i; @@ -205,6 +213,10 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency pkg0->ss.conflicts += !is_provided; if (unlikely(pkg0->ss.available && pkg0->ss.conflicts)) disqualify_package(ss, pkg0, "conflicting dependency"); + if (is_provided) { + pkg0->ss.solver_flags |= solver_flags_inherit; + pkg0->ss.solver_flags_inheritable |= solver_flags_inherit; + } } } @@ -307,6 +319,7 @@ static int compare_providers(struct apk_solver_state *ss, { struct apk_database *db = ss->db; struct apk_package *pkgA = pA->pkg, *pkgB = pB->pkg; + unsigned int solver_flags; int r; /* Prefer existing package */ @@ -319,7 +332,8 @@ static int compare_providers(struct apk_solver_state *ss, return r; /* Prefer available */ - if (ss->solver_flags & APK_SOLVERF_AVAILABLE) { + solver_flags = pkgA->ss.solver_flags | pkgB->ss.solver_flags; + if (solver_flags & APK_SOLVERF_AVAILABLE) { r = !!(pkgA->repos & db->available_repos) - !!(pkgB->repos & db->available_repos); if (r) @@ -328,7 +342,7 @@ static int compare_providers(struct apk_solver_state *ss, /* Prefer installed */ /* FIXME: check-per-name flags here too */ - if (!(ss->solver_flags & APK_SOLVERF_UPGRADE)) { + if (!(solver_flags & APK_SOLVERF_UPGRADE)) { r = (pkgA->ipkg != NULL) - (pkgB->ipkg != NULL); if (r) return r; @@ -431,7 +445,9 @@ static void select_package(struct apk_solver_state *ss, struct apk_name *name) struct apk_dependency *p = &pkg->provides->item[i]; assign_name(ss, p->name, APK_PROVIDER_FROM_PROVIDES(pkg, p)); } + ss->solver_flags_inherit = pkg->ss.solver_flags_inheritable; foreach_dependency(ss, pkg->depends, apply_constraint); + ss->solver_flags_inherit = 0; ss->num_selections++; } else { dbg_printf("selecting: %s [unassigned]\n", name->name); @@ -468,11 +484,10 @@ static void generate_change(struct apk_solver_state *ss, struct apk_name *name) .old_repository_tag = opkg ? opkg->ipkg->repository_tag : 0, .new_pkg = pkg, .new_repository_tag = pkg->ipkg ? pkg->ipkg->repository_tag : 0, + .reinstall = !!(pkg->ss.solver_flags & APK_SOLVERF_REINSTALL), #if 0 - /* FIXME: setup reinstall and repository_tag for solution */ - .reinstall = ps->inherited_reinstall || - ((name->ss.solver_flags_local | ss->solver_flags) & APK_SOLVERF_REINSTALL), - .repository_tag = get_tag(db, pinning, repos), + /* FIXME: repository_tag from pinning */ + .new_repository_tag = get_tag(db, pinning, repos), #endif }; if (change->new_pkg == NULL) @@ -585,7 +600,6 @@ int apk_solver_solve(struct apk_database *db, memset(ss, 0, sizeof(*ss)); ss->db = db; ss->changeset = changeset; - ss->solver_flags = solver_flags; list_init(&ss->dirty_head); list_init(&ss->unresolved_head); @@ -594,6 +608,7 @@ int apk_solver_solve(struct apk_database *db, /* FIXME: If filename specified, force to use it */ dbg_printf("applying world\n"); + ss->solver_flags_inherit = solver_flags; for (i = 0; i < world->num; i++) { struct apk_dependency *dep = &world->item[i]; name = dep->name; @@ -601,6 +616,7 @@ int apk_solver_solve(struct apk_database *db, name->ss.preferred_pinning = BIT(dep->repository_tag); apply_constraint(ss, dep); } + ss->solver_flags_inherit = 0; dbg_printf("applying world [finished]\n"); do { diff --git a/test/fix1.test b/test/fix1.test new file mode 100644 index 0000000..fcc0a98 --- /dev/null +++ b/test/fix1.test @@ -0,0 +1,9 @@ +@ARGS +--test-repo basic.repo +--test-instdb basic.installed +--test-world a +--upgrade +fix b +@EXPECT +(1/1) Upgrading b (1 -> 2) +OK: 0 MiB in 2 packages diff --git a/test/fix2.test b/test/fix2.test new file mode 100644 index 0000000..25f2198 --- /dev/null +++ b/test/fix2.test @@ -0,0 +1,9 @@ +@ARGS +--test-repo basic.repo +--test-instdb basic.installed +--test-world a +--upgrade +fix a +@EXPECT +(1/1) Upgrading a (1 -> 2) +OK: 0 MiB in 2 packages diff --git a/test/fix3.test b/test/fix3.test new file mode 100644 index 0000000..364f710 --- /dev/null +++ b/test/fix3.test @@ -0,0 +1,11 @@ +@ARGS +--test-repo basic.repo +--test-instdb basic.installed +--test-world a +--upgrade +--depends +fix a +@EXPECT +(1/2) Upgrading b (1 -> 2) +(2/2) Upgrading a (1 -> 2) +OK: 0 MiB in 2 packages diff --git a/test/fix4.test b/test/fix4.test new file mode 100644 index 0000000..431266b --- /dev/null +++ b/test/fix4.test @@ -0,0 +1,8 @@ +@ARGS +--test-repo basic.repo +--test-instdb basic.installed +--test-world a +fix b +@EXPECT +(1/1) Re-installing b (1) +OK: 0 MiB in 2 packages diff --git a/test/fix5.test b/test/fix5.test new file mode 100644 index 0000000..33029fc --- /dev/null +++ b/test/fix5.test @@ -0,0 +1,8 @@ +@ARGS +--test-repo basic.repo +--test-instdb basic.installed +--test-world a +fix a +@EXPECT +(1/1) Re-installing a (1) +OK: 0 MiB in 2 packages diff --git a/test/fix6.test b/test/fix6.test new file mode 100644 index 0000000..c6dc6bd --- /dev/null +++ b/test/fix6.test @@ -0,0 +1,10 @@ +@ARGS +--test-repo basic.repo +--test-instdb basic.installed +--test-world a +--depends +fix a +@EXPECT +(1/2) Re-installing b (1) +(2/2) Re-installing a (1) +OK: 0 MiB in 2 packages