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.
cute-signatures
Timo Teräs 2013-06-18 16:24:48 +03:00
parent f91b01fea8
commit d315c9019c
8 changed files with 137 additions and 68 deletions

View File

@ -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,

View File

@ -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 {

View File

@ -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);

View File

@ -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;
/* 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 installed on self-upgrade */
/* Latest version required? */
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);
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 allowed pinning */
r = (int)pkgA->ss.tag_ok - (int)pkgB->ss.tag_ok;
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 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 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);
/* 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 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 allowed pinning */
r = (int)pkgA->ss.tag_ok - (int)pkgB->ss.tag_ok;
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 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;

View File

@ -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" },
};

15
test/basic12.test Normal file
View File

@ -0,0 +1,15 @@
@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]

10
test/basic13.test Normal file
View File

@ -0,0 +1,10 @@
@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]

View File

@ -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]