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

upgrade: new option: --latest (-l)

Select latest version of package (if it is not pinned), and print
error if it cannot be installed due to other dependencies.

Together with --available, it selects the latest package which is
present at least in some repository.

This also fixes few solver issues with ordering of package selection
that got quite apparent with this flag. Namely, we cannot "lock"
a package until it's reverse dependencies are locked or not all of
the solver flags are propagated properly.
parent f91b01fe
......@@ -33,6 +33,7 @@ struct apk_changeset {
#define APK_SOLVERF_UPGRADE 0x0001
#define APK_SOLVERF_AVAILABLE 0x0002
#define APK_SOLVERF_REINSTALL 0x0004
#define APK_SOLVERF_LATEST 0x0008
void apk_solver_set_name_flags(struct apk_name *name,
unsigned short solver_flags,
......
......@@ -32,6 +32,7 @@ struct apk_solver_name_state {
unsigned locked : 1;
unsigned has_iif : 1;
unsigned has_options : 1;
unsigned reverse_deps_done : 1;
};
struct apk_solver_package_state {
......
......@@ -370,13 +370,18 @@ static void print_pinning_errors(struct print_state *ps, struct apk_package *pkg
if (pkg->ipkg != NULL)
return;
if (pkg->repos & apk_db_get_pinning_mask_repos(db, APK_DEFAULT_PINNING_MASK | BIT(tag)))
return;
for (i = 0; i < db->num_repo_tags; i++) {
if (pkg->repos & db->repo_tags[i].allowed_repos) {
label_start(ps, "masked in:");
apk_print_indented(&ps->i, db->repo_tags[i].tag);
if (!(pkg->repos & db->available_repos)) {
label_start(ps, "masked in:");
apk_print_indented_fmt(&ps->i, "--no-network");
} else {
if (pkg->repos & apk_db_get_pinning_mask_repos(db, APK_DEFAULT_PINNING_MASK | BIT(tag)))
return;
for (i = 0; i < db->num_repo_tags; i++) {
if (pkg->repos & db->repo_tags[i].allowed_repos) {
label_start(ps, "masked in:");
apk_print_indented(&ps->i, db->repo_tags[i].tag);
}
}
}
label_end(ps);
......
......@@ -222,11 +222,12 @@ static void discover_name(struct apk_solver_state *ss, struct apk_name *name)
}
name->ss.max_dep_chain = max(name->ss.max_dep_chain, pkg->ss.max_dep_chain);
dbg_printf("discover " PKG_VER_FMT ": tag_ok=%d, tag_pref=%d max_dep_chain=%d\n",
dbg_printf("discover " PKG_VER_FMT ": tag_ok=%d, tag_pref=%d max_dep_chain=%d available=%d\n",
PKG_VER_PRINTF(pkg),
pkg->ss.tag_ok,
pkg->ss.tag_preferred,
pkg->ss.max_dep_chain);
pkg->ss.max_dep_chain,
pkg->ss.available);
}
foreach_array_item(pname0, name->rinstall_if)
discover_name(ss, *pname0);
......@@ -266,7 +267,8 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_package *pp
BLOB_PRINTF(*dep->version));
name->ss.requirers += !dep->conflict;
name_requirers_changed(ss, name);
if (name->ss.requirers == 1 && !dep->conflict)
name_requirers_changed(ss, name);
foreach_array_item(p0, name->providers) {
struct apk_package *pkg0 = p0->pkg;
......@@ -294,7 +296,7 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_package *pp
static void reconsider_name(struct apk_solver_state *ss, struct apk_name *name)
{
struct apk_name *name0;
struct apk_name *name0, **pname0;
struct apk_dependency *dep;
struct apk_package *first_candidate = NULL;
struct apk_provider *p;
......@@ -366,26 +368,41 @@ static void reconsider_name(struct apk_solver_state *ss, struct apk_name *name)
foreach_array_item(p, name->providers)
p->pkg->ss.dependencies_used = p->pkg->ss.dependencies_merged;
/* TODO: could merge versioning bits too */
/* propagate down common dependencies */
foreach_array_item(dep, first_candidate->depends) {
if (dep->conflict)
continue;
name0 = dep->name;
if (name0->ss.merge_index == num_options) {
/* common dependency name with all */
if (name0->ss.requirers == 0) {
dbg_printf("%s common dependency: %s\n",
name->name, name0->name);
name0->ss.requirers++;
name_requirers_changed(ss, name0);
if (num_options == 1) {
/* FIXME: keeps increasing counts, use bit fields instead? */
foreach_array_item(dep, first_candidate->depends)
apply_constraint(ss, first_candidate, dep);
} else {
/* FIXME: could merge versioning bits too */
foreach_array_item(dep, first_candidate->depends) {
if (dep->conflict)
continue;
name0 = dep->name;
if (name0->ss.merge_index == num_options) {
/* common dependency name with all */
if (name0->ss.requirers == 0) {
dbg_printf("%s common dependency: %s\n",
name->name, name0->name);
name0->ss.requirers++;
name_requirers_changed(ss, name0);
}
}
name0->ss.merge_index = 0;
}
name0->ss.merge_index = 0;
}
}
dbg_printf("reconsider_name: %s [finished], has_options=%d\n",
name->name, name->ss.has_options);
name->ss.reverse_deps_done = 1;
foreach_array_item(pname0, name->rdepends) {
name0 = *pname0;
if (name0->ss.seen && !name0->ss.locked) {
name->ss.reverse_deps_done = 0;
break;
}
}
dbg_printf("reconsider_name: %s [finished], has_options=%d, reverse_deps_done=%d\n",
name->name, name->ss.has_options, name->ss.reverse_deps_done);
}
static int compare_providers(struct apk_solver_state *ss,
......@@ -400,50 +417,66 @@ static int compare_providers(struct apk_solver_state *ss,
if (pkgA == NULL || pkgB == NULL)
return (pkgA != NULL) - (pkgB != NULL);
/* Prefer without errors */
r = (int)pkgA->ss.available - (int)pkgB->ss.available;
if (r)
return r;
/* Latest version required? */
solver_flags = pkgA->ss.solver_flags | pkgB->ss.solver_flags;
if ((solver_flags & APK_SOLVERF_LATEST) &&
(pkgA->ss.pinning_allowed == APK_DEFAULT_PINNING_MASK) &&
(pkgB->ss.pinning_allowed == APK_DEFAULT_PINNING_MASK)) {
/* Prefer allowed pinning */
r = (int)pkgA->ss.tag_ok - (int)pkgB->ss.tag_ok;
if (r)
return r;
/* Prefer those that were in last dependency merging group */
r = (int)pkgA->ss.dependencies_used - (int)pkgB->ss.dependencies_used;
if (r)
return r;
r = pkgB->ss.conflicts - pkgA->ss.conflicts;
if (r)
return r;
/* Prefer available */
if (solver_flags & (APK_SOLVERF_AVAILABLE | APK_SOLVERF_REINSTALL)) {
r = !!(pkgA->repos & db->available_repos) - !!(pkgB->repos & db->available_repos);
if (r)
return r;
}
} else {
/* Prefer without errors */
r = (int)pkgA->ss.available - (int)pkgB->ss.available;
if (r)
return r;
/* Prefer installed on self-upgrade */
solver_flags = pkgA->ss.solver_flags | pkgB->ss.solver_flags;
if (db->performing_self_update && !(solver_flags & APK_SOLVERF_UPGRADE)) {
r = (pkgA->ipkg != NULL) - (pkgB->ipkg != NULL);
/* Prefer those that were in last dependency merging group */
r = (int)pkgA->ss.dependencies_used - (int)pkgB->ss.dependencies_used;
if (r)
return r;
r = pkgB->ss.conflicts - pkgA->ss.conflicts;
if (r)
return r;
}
/* Prefer allowed pinning */
r = (int)pkgA->ss.tag_ok - (int)pkgB->ss.tag_ok;
if (r)
return r;
/* Prefer installed on self-upgrade */
if (db->performing_self_update && !(solver_flags & APK_SOLVERF_UPGRADE)) {
r = (pkgA->ipkg != NULL) - (pkgB->ipkg != NULL);
if (r)
return r;
}
/* Prefer available */
if (solver_flags & (APK_SOLVERF_AVAILABLE | APK_SOLVERF_REINSTALL)) {
r = !!(pkgA->repos & db->available_repos) -
!!(pkgB->repos & db->available_repos);
/* Prefer allowed pinning */
r = (int)pkgA->ss.tag_ok - (int)pkgB->ss.tag_ok;
if (r)
return r;
}
/* Prefer preferred pinning */
r = (int)pkgA->ss.tag_preferred - (int)pkgB->ss.tag_preferred;
if (r)
return r;
/* Prefer available */
if (solver_flags & (APK_SOLVERF_AVAILABLE | APK_SOLVERF_REINSTALL)) {
r = !!(pkgA->repos & db->available_repos) - !!(pkgB->repos & db->available_repos);
if (r)
return r;
}
/* Prefer installed */
if (!(solver_flags & APK_SOLVERF_UPGRADE)) {
r = (pkgA->ipkg != NULL) - (pkgB->ipkg != NULL);
/* Prefer preferred pinning */
r = (int)pkgA->ss.tag_preferred - (int)pkgB->ss.tag_preferred;
if (r)
return r;
/* Prefer installed */
if (!(solver_flags & APK_SOLVERF_UPGRADE)) {
r = (pkgA->ipkg != NULL) - (pkgB->ipkg != NULL);
if (r)
return r;
}
}
/* Select latest by requested name */
......@@ -747,17 +780,15 @@ restart:
name = NULL;
list_for_each_entry(name0, &ss->unresolved_head, ss.unresolved_list) {
if ((!name0->ss.has_options) && name0->ss.requirers > 0) {
if (name0->ss.reverse_deps_done && name0->ss.requirers && !name0->ss.has_options) {
name = name0;
break;
}
if (name == NULL)
goto prefer;
if (name0->ss.requirers == 0 && name->ss.requirers > 0)
if ((!!name0->ss.requirers) - (!!name->ss.requirers) < 0)
continue;
if (name0->ss.requirers > 0 && name->ss.requirers == 0)
goto prefer;
if (name0->ss.max_dep_chain < name->ss.max_dep_chain)
if (name0->ss.max_dep_chain - name->ss.max_dep_chain < 0)
continue;
prefer:
name = name0;
......
......@@ -35,6 +35,9 @@ static int upgrade_parse(void *ctx, struct apk_db_options *dbopts,
case 'a':
uctx->solver_flags |= APK_SOLVERF_AVAILABLE;
break;
case 'l':
uctx->solver_flags |= APK_SOLVERF_LATEST;
break;
default:
return -1;
}
......@@ -131,6 +134,9 @@ static struct apk_option upgrade_options[] = {
"replacing or downgrading packages (instead of holding them) "
"if the currently installed package is no longer available "
"from any repository" },
{ 'l', "latest",
"Select latest version of package (if it is not pinned), and "
"print error if it cannot be installed due to other dependencies" },
{ 0x10000, "no-self-upgrade",
"Do not do early upgrade of 'apk-tools' package" },
};
......
@ARGS
--no-network
--test-repo basic.repo
--test-instdb basic.installed
--test-world a
--latest
upgrade
@EXPECT
ERROR: unsatisfiable constraints:
b-2:
masked in: --no-network
satisfies: a-2[b]
a-2:
masked in: --no-network
satisfies: world[a]
@ARGS
--test-repo basic.repo
--test-instdb basic.installed
--test-world a<2
--latest
upgrade
@EXPECT
ERROR: unsatisfiable constraints:
a-2:
breaks: world[a<2]
......@@ -3,6 +3,6 @@
add a>2
@EXPECT
ERROR: unsatisfiable constraints:
d-1.5:
breaks: a-3[d>1.5]
satisfies: b-1[d<2.0] c-1[d>1.0]
d-2.0:
breaks: b-1[d<2.0]
satisfies: a-3[d>1.5] c-1[d>1.0]
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