solver, test: make conflicts unconditional
Solver will now never report partial solution where a conflict constraint is not satisfied. The is because with --force we might install the partial solution; and if conflicted packages were to be installed we might have extra trouble.cute-signatures
parent
2e8fe783a1
commit
f27f194d92
|
@ -75,8 +75,8 @@ static int add_main(void *ctx, struct apk_database *db, int argc, char **argv)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
apk_blob_pull_dep(&b, db, &virtdep);
|
apk_blob_pull_dep(&b, db, &virtdep);
|
||||||
if (APK_BLOB_IS_NULL(b) ||
|
if (APK_BLOB_IS_NULL(b) || virtdep.conflict ||
|
||||||
virtdep.result_mask != APK_DEPMASK_REQUIRE ||
|
virtdep.result_mask != APK_DEPMASK_ANY ||
|
||||||
virtdep.version != &apk_null_blob) {
|
virtdep.version != &apk_null_blob) {
|
||||||
apk_error("%s: bad package specifier");
|
apk_error("%s: bad package specifier");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -61,7 +61,7 @@ struct apk_dependency {
|
||||||
struct apk_name *name;
|
struct apk_name *name;
|
||||||
apk_blob_t *version;
|
apk_blob_t *version;
|
||||||
unsigned short repository_tag;
|
unsigned short repository_tag;
|
||||||
unsigned optional : 1;
|
unsigned conflict : 1;
|
||||||
unsigned result_mask : 3;
|
unsigned result_mask : 3;
|
||||||
};
|
};
|
||||||
APK_ARRAY(apk_dependency_array, struct apk_dependency);
|
APK_ARRAY(apk_dependency_array, struct apk_dependency);
|
||||||
|
|
|
@ -18,8 +18,7 @@
|
||||||
#define APK_VERSION_LESS 2
|
#define APK_VERSION_LESS 2
|
||||||
#define APK_VERSION_GREATER 4
|
#define APK_VERSION_GREATER 4
|
||||||
|
|
||||||
#define APK_DEPMASK_CONFLICT (0)
|
#define APK_DEPMASK_ANY (APK_VERSION_EQUAL|APK_VERSION_LESS|\
|
||||||
#define APK_DEPMASK_REQUIRE (APK_VERSION_EQUAL|APK_VERSION_LESS|\
|
|
||||||
APK_VERSION_GREATER)
|
APK_VERSION_GREATER)
|
||||||
#define APK_DEPMASK_CHECKSUM (APK_VERSION_LESS|APK_VERSION_GREATER)
|
#define APK_DEPMASK_CHECKSUM (APK_VERSION_LESS|APK_VERSION_GREATER)
|
||||||
|
|
||||||
|
|
|
@ -177,7 +177,7 @@ static int fetch_main(void *ctx, struct apk_database *db, int argc, char **argv)
|
||||||
struct apk_dependency dep = (struct apk_dependency) {
|
struct apk_dependency dep = (struct apk_dependency) {
|
||||||
.name = apk_db_get_name(db, APK_BLOB_STR(argv[i])),
|
.name = apk_db_get_name(db, APK_BLOB_STR(argv[i])),
|
||||||
.version = apk_blob_atomize(APK_BLOB_NULL),
|
.version = apk_blob_atomize(APK_BLOB_NULL),
|
||||||
.result_mask = APK_DEPMASK_REQUIRE,
|
.result_mask = APK_DEPMASK_ANY,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (fctx->flags & FETCH_RECURSIVE) {
|
if (fctx->flags & FETCH_RECURSIVE) {
|
||||||
|
|
|
@ -118,7 +118,7 @@ static int info_who_owns(struct info_ctx *ctx, struct apk_database *db,
|
||||||
dep = (struct apk_dependency) {
|
dep = (struct apk_dependency) {
|
||||||
.name = pkg->name,
|
.name = pkg->name,
|
||||||
.version = apk_blob_atomize(APK_BLOB_NULL),
|
.version = apk_blob_atomize(APK_BLOB_NULL),
|
||||||
.result_mask = APK_DEPMASK_REQUIRE,
|
.result_mask = APK_DEPMASK_ANY,
|
||||||
};
|
};
|
||||||
apk_deps_add(&deps, &dep);
|
apk_deps_add(&deps, &dep);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -218,7 +218,7 @@ void apk_blob_pull_dep(apk_blob_t *b, struct apk_database *db, struct apk_depend
|
||||||
{
|
{
|
||||||
struct apk_name *name;
|
struct apk_name *name;
|
||||||
apk_blob_t bdep, bname, bop, bver = APK_BLOB_NULL, btag;
|
apk_blob_t bdep, bname, bop, bver = APK_BLOB_NULL, btag;
|
||||||
int mask = APK_DEPMASK_REQUIRE, optional = 0, tag = 0;
|
int mask = APK_DEPMASK_ANY, conflict = 0, tag = 0;
|
||||||
|
|
||||||
/* [!]name[<,<=,=,>=,>,><]ver */
|
/* [!]name[<,<=,=,>=,>,><]ver */
|
||||||
if (APK_BLOB_IS_NULL(*b))
|
if (APK_BLOB_IS_NULL(*b))
|
||||||
|
@ -240,7 +240,7 @@ void apk_blob_pull_dep(apk_blob_t *b, struct apk_database *db, struct apk_depend
|
||||||
if (bdep.ptr[0] == '!') {
|
if (bdep.ptr[0] == '!') {
|
||||||
bdep.ptr++;
|
bdep.ptr++;
|
||||||
bdep.len--;
|
bdep.len--;
|
||||||
optional = 1;
|
conflict = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (apk_blob_cspn(bdep, apk_spn_dependency_comparer, &bname, &bop)) {
|
if (apk_blob_cspn(bdep, apk_spn_dependency_comparer, &bname, &bop)) {
|
||||||
|
@ -281,15 +281,12 @@ void apk_blob_pull_dep(apk_blob_t *b, struct apk_database *db, struct apk_depend
|
||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (optional)
|
|
||||||
mask ^= APK_DEPMASK_REQUIRE;
|
|
||||||
|
|
||||||
*dep = (struct apk_dependency){
|
*dep = (struct apk_dependency){
|
||||||
.name = name,
|
.name = name,
|
||||||
.version = apk_blob_atomize_dup(bver),
|
.version = apk_blob_atomize_dup(bver),
|
||||||
.repository_tag = tag,
|
.repository_tag = tag,
|
||||||
.result_mask = mask,
|
.result_mask = mask,
|
||||||
.optional = optional,
|
.conflict = conflict,
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
fail:
|
fail:
|
||||||
|
@ -340,46 +337,44 @@ static int apk_dep_match_checksum(struct apk_dependency *dep, struct apk_package
|
||||||
|
|
||||||
int apk_dep_is_provided(struct apk_dependency *dep, struct apk_provider *p)
|
int apk_dep_is_provided(struct apk_dependency *dep, struct apk_provider *p)
|
||||||
{
|
{
|
||||||
if (p == NULL)
|
if (p == NULL || p->pkg == NULL)
|
||||||
return dep->optional;
|
return dep->conflict;
|
||||||
|
|
||||||
switch (dep->result_mask) {
|
switch (dep->result_mask) {
|
||||||
case APK_DEPMASK_CHECKSUM:
|
case APK_DEPMASK_CHECKSUM:
|
||||||
return apk_dep_match_checksum(dep, p->pkg);
|
return apk_dep_match_checksum(dep, p->pkg);
|
||||||
case APK_DEPMASK_CONFLICT:
|
case APK_DEPMASK_ANY:
|
||||||
return 0;
|
return !dep->conflict;
|
||||||
case APK_DEPMASK_REQUIRE:
|
|
||||||
return 1;
|
|
||||||
default:
|
default:
|
||||||
|
if (p->version == &apk_null_blob)
|
||||||
|
return dep->conflict;
|
||||||
if (apk_version_compare_blob(*p->version, *dep->version)
|
if (apk_version_compare_blob(*p->version, *dep->version)
|
||||||
& dep->result_mask)
|
& dep->result_mask)
|
||||||
return 1;
|
return !dep->conflict;
|
||||||
return 0;
|
return dep->conflict;
|
||||||
}
|
}
|
||||||
return 0;
|
return dep->conflict;
|
||||||
}
|
}
|
||||||
|
|
||||||
int apk_dep_is_materialized(struct apk_dependency *dep, struct apk_package *pkg)
|
int apk_dep_is_materialized(struct apk_dependency *dep, struct apk_package *pkg)
|
||||||
{
|
{
|
||||||
if (pkg == NULL)
|
if (pkg == NULL)
|
||||||
return dep->optional;
|
return dep->conflict;
|
||||||
if (dep->name != pkg->name)
|
if (dep->name != pkg->name)
|
||||||
return 0;
|
return dep->conflict;
|
||||||
|
|
||||||
switch (dep->result_mask) {
|
switch (dep->result_mask) {
|
||||||
case APK_DEPMASK_CHECKSUM:
|
case APK_DEPMASK_CHECKSUM:
|
||||||
return apk_dep_match_checksum(dep, pkg);
|
return apk_dep_match_checksum(dep, pkg);
|
||||||
case APK_DEPMASK_CONFLICT:
|
case APK_DEPMASK_ANY:
|
||||||
return 0;
|
return !dep->conflict;
|
||||||
case APK_DEPMASK_REQUIRE:
|
|
||||||
return 1;
|
|
||||||
default:
|
default:
|
||||||
if (apk_version_compare_blob(*pkg->version, *dep->version)
|
if (apk_version_compare_blob(*pkg->version, *dep->version)
|
||||||
& dep->result_mask)
|
& dep->result_mask)
|
||||||
return 1;
|
return !dep->conflict;
|
||||||
return 0;
|
return dep->conflict;
|
||||||
}
|
}
|
||||||
return 0;
|
return dep->conflict;
|
||||||
}
|
}
|
||||||
|
|
||||||
int apk_dep_is_materialized_or_provided(struct apk_dependency *dep, struct apk_package *pkg)
|
int apk_dep_is_materialized_or_provided(struct apk_dependency *dep, struct apk_package *pkg)
|
||||||
|
@ -387,7 +382,7 @@ int apk_dep_is_materialized_or_provided(struct apk_dependency *dep, struct apk_p
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (pkg == NULL)
|
if (pkg == NULL)
|
||||||
return dep->optional;
|
return dep->conflict;
|
||||||
|
|
||||||
if (dep->name == pkg->name)
|
if (dep->name == pkg->name)
|
||||||
return apk_dep_is_materialized(dep, pkg);
|
return apk_dep_is_materialized(dep, pkg);
|
||||||
|
@ -401,17 +396,15 @@ int apk_dep_is_materialized_or_provided(struct apk_dependency *dep, struct apk_p
|
||||||
return apk_dep_is_provided(dep, &p);
|
return apk_dep_is_provided(dep, &p);
|
||||||
}
|
}
|
||||||
|
|
||||||
return dep->optional;
|
return dep->conflict;
|
||||||
}
|
}
|
||||||
|
|
||||||
void apk_blob_push_dep(apk_blob_t *to, struct apk_database *db, struct apk_dependency *dep)
|
void apk_blob_push_dep(apk_blob_t *to, struct apk_database *db, struct apk_dependency *dep)
|
||||||
{
|
{
|
||||||
int result_mask = dep->result_mask;
|
int result_mask = dep->result_mask;
|
||||||
|
|
||||||
if (dep->optional) {
|
if (dep->conflict)
|
||||||
apk_blob_push_blob(to, APK_BLOB_PTR_LEN("!", 1));
|
apk_blob_push_blob(to, APK_BLOB_PTR_LEN("!", 1));
|
||||||
result_mask ^= APK_DEPMASK_REQUIRE;
|
|
||||||
}
|
|
||||||
|
|
||||||
apk_blob_push_blob(to, APK_BLOB_STR(dep->name->name));
|
apk_blob_push_blob(to, APK_BLOB_STR(dep->name->name));
|
||||||
if (dep->repository_tag && db != NULL) {
|
if (dep->repository_tag && db != NULL) {
|
||||||
|
|
49
src/solver.c
49
src/solver.c
|
@ -96,7 +96,9 @@ struct apk_package_state {
|
||||||
unsigned short inherited_upgrade;
|
unsigned short inherited_upgrade;
|
||||||
unsigned short inherited_reinstall;
|
unsigned short inherited_reinstall;
|
||||||
|
|
||||||
unsigned short conflicts;
|
unsigned short must_not;
|
||||||
|
unsigned short incompat_dep;
|
||||||
|
|
||||||
unsigned char preference;
|
unsigned char preference;
|
||||||
unsigned handle_install_if : 1;
|
unsigned handle_install_if : 1;
|
||||||
unsigned allowed : 1;
|
unsigned allowed : 1;
|
||||||
|
@ -152,6 +154,7 @@ struct apk_solver_state {
|
||||||
struct apk_score best_score;
|
struct apk_score best_score;
|
||||||
|
|
||||||
unsigned solver_flags : 4;
|
unsigned solver_flags : 4;
|
||||||
|
unsigned impossible_state : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -367,7 +370,7 @@ static int get_topology_score(
|
||||||
int score_locked = TRUE, sticky_installed = FALSE;
|
int score_locked = TRUE, sticky_installed = FALSE;
|
||||||
|
|
||||||
score = (struct apk_score) {
|
score = (struct apk_score) {
|
||||||
.conflicts = ps->conflicts,
|
.conflicts = ps->incompat_dep,
|
||||||
.preference = ps->preference,
|
.preference = ps->preference,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -796,6 +799,7 @@ static solver_result_t apply_decision(struct apk_solver_state *ss,
|
||||||
struct apk_score score;
|
struct apk_score score;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
ss->impossible_state = 0;
|
||||||
ns->name_touched = 1;
|
ns->name_touched = 1;
|
||||||
if (pkg != NULL) {
|
if (pkg != NULL) {
|
||||||
struct apk_package_state *ps = pkg_to_ps(pkg);
|
struct apk_package_state *ps = pkg_to_ps(pkg);
|
||||||
|
@ -855,6 +859,13 @@ static solver_result_t apply_decision(struct apk_solver_state *ss,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ss->impossible_state) {
|
||||||
|
dbg_printf("%s: %s impossible constraints\n",
|
||||||
|
name->name,
|
||||||
|
(d->type == DECISION_ASSIGN) ? "ASSIGN" : "EXCLUDE");
|
||||||
|
return SOLVERR_PRUNED;
|
||||||
|
}
|
||||||
|
|
||||||
if (cmpscore(&ss->score, &ss->best_score) >= 0) {
|
if (cmpscore(&ss->score, &ss->best_score) >= 0) {
|
||||||
dbg_printf("%s: %s penalty too big: "SCORE_FMT">="SCORE_FMT"\n",
|
dbg_printf("%s: %s penalty too big: "SCORE_FMT">="SCORE_FMT"\n",
|
||||||
name->name,
|
name->name,
|
||||||
|
@ -1024,13 +1035,18 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency
|
||||||
else
|
else
|
||||||
dbg_printf("%s: locked to empty\n",
|
dbg_printf("%s: locked to empty\n",
|
||||||
name->name);
|
name->name);
|
||||||
if (!apk_dep_is_provided(dep, &ns->chosen))
|
if (!apk_dep_is_provided(dep, &ns->chosen)) {
|
||||||
|
dbg_printf("%s: constraint violation %d\n",
|
||||||
|
name->name, strength);
|
||||||
ss->score.conflicts += strength;
|
ss->score.conflicts += strength;
|
||||||
|
if (dep->conflict)
|
||||||
|
ss->impossible_state = 1;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name->providers->num == 0) {
|
if (name->providers->num == 0) {
|
||||||
if (!dep->optional)
|
if (!dep->conflict)
|
||||||
ss->score.conflicts += strength;
|
ss->score.conflicts += strength;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1045,10 +1061,14 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!apk_dep_is_provided(dep, p0)) {
|
if (!apk_dep_is_provided(dep, p0)) {
|
||||||
ps0->conflicts++;
|
if (dep->conflict)
|
||||||
|
ps0->must_not++;
|
||||||
|
else
|
||||||
|
ps0->incompat_dep++;
|
||||||
|
|
||||||
dbg_printf(PKG_VER_FMT ": conflicts++ -> %d\n",
|
dbg_printf(PKG_VER_FMT ": conflicts++ -> %d\n",
|
||||||
PKG_VER_PRINTF(pkg0),
|
PKG_VER_PRINTF(pkg0),
|
||||||
ps0->conflicts);
|
ps0->must_not);
|
||||||
changed |= 1;
|
changed |= 1;
|
||||||
} else if (requirer_pkg != NULL) {
|
} else if (requirer_pkg != NULL) {
|
||||||
dbg_printf(PKG_VER_FMT ": inheriting flags and pinning from"PKG_VER_FMT"\n",
|
dbg_printf(PKG_VER_FMT ": inheriting flags and pinning from"PKG_VER_FMT"\n",
|
||||||
|
@ -1061,7 +1081,7 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency
|
||||||
if (changed)
|
if (changed)
|
||||||
ns->last_touched_decision = ss->num_decisions;
|
ns->last_touched_decision = ss->num_decisions;
|
||||||
|
|
||||||
if (!dep->optional)
|
if (!dep->conflict)
|
||||||
ns->requirers += strength;
|
ns->requirers += strength;
|
||||||
|
|
||||||
promote_name(ss, name);
|
promote_name(ss, name);
|
||||||
|
@ -1096,7 +1116,7 @@ static void undo_constraint(struct apk_solver_state *ss, struct apk_dependency *
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (name->providers->num == 0) {
|
if (name->providers->num == 0) {
|
||||||
if (!dep->optional)
|
if (!dep->conflict)
|
||||||
ss->score.conflicts -= strength;
|
ss->score.conflicts -= strength;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1111,10 +1131,13 @@ static void undo_constraint(struct apk_solver_state *ss, struct apk_dependency *
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!apk_dep_is_provided(dep, p0)) {
|
if (!apk_dep_is_provided(dep, p0)) {
|
||||||
ps0->conflicts--;
|
if (dep->conflict)
|
||||||
|
ps0->must_not--;
|
||||||
|
else
|
||||||
|
ps0->incompat_dep--;
|
||||||
dbg_printf(PKG_VER_FMT ": conflicts-- -> %d\n",
|
dbg_printf(PKG_VER_FMT ": conflicts-- -> %d\n",
|
||||||
PKG_VER_PRINTF(pkg0),
|
PKG_VER_PRINTF(pkg0),
|
||||||
ps0->conflicts);
|
ps0->must_not);
|
||||||
} else if (requirer_pkg != NULL) {
|
} else if (requirer_pkg != NULL) {
|
||||||
dbg_printf(PKG_VER_FMT ": uninheriting flags and pinning from "PKG_VER_FMT"\n",
|
dbg_printf(PKG_VER_FMT ": uninheriting flags and pinning from "PKG_VER_FMT"\n",
|
||||||
PKG_VER_PRINTF(pkg0),
|
PKG_VER_PRINTF(pkg0),
|
||||||
|
@ -1130,7 +1153,7 @@ static void undo_constraint(struct apk_solver_state *ss, struct apk_dependency *
|
||||||
if (ns->last_touched_decision > ss->num_decisions)
|
if (ns->last_touched_decision > ss->num_decisions)
|
||||||
ns->last_touched_decision = ss->num_decisions;
|
ns->last_touched_decision = ss->num_decisions;
|
||||||
|
|
||||||
if (!dep->optional)
|
if (!dep->conflict)
|
||||||
ns->requirers -= strength;
|
ns->requirers -= strength;
|
||||||
|
|
||||||
demote_name(ss, name);
|
demote_name(ss, name);
|
||||||
|
@ -1163,7 +1186,7 @@ static int reconsider_name(struct apk_solver_state *ss, struct apk_name *name)
|
||||||
struct apk_package_state *ps0 = pkg_to_ps(pkg0);
|
struct apk_package_state *ps0 = pkg_to_ps(pkg0);
|
||||||
struct apk_score pkg0_score;
|
struct apk_score pkg0_score;
|
||||||
|
|
||||||
if (ps0 == NULL || ps0->locked ||
|
if (ps0 == NULL || ps0->locked || ps0->must_not ||
|
||||||
ss->topology_position < pkg0->topology_hard ||
|
ss->topology_position < pkg0->topology_hard ||
|
||||||
(pkg0->ipkg == NULL && (!ps0->allowed || !pkg_available(ss->db, pkg0))))
|
(pkg0->ipkg == NULL && (!ps0->allowed || !pkg_available(ss->db, pkg0))))
|
||||||
continue;
|
continue;
|
||||||
|
@ -1274,7 +1297,7 @@ static int expand_branch(struct apk_solver_state *ss)
|
||||||
|
|
||||||
if (!ns->none_excluded) {
|
if (!ns->none_excluded) {
|
||||||
struct apk_package_state *ps0 = pkg_to_ps(pkg0);
|
struct apk_package_state *ps0 = pkg_to_ps(pkg0);
|
||||||
if (ps0->conflicts > ns->requirers)
|
if (ps0->incompat_dep > ns->requirers)
|
||||||
primary_decision = DECISION_ASSIGN;
|
primary_decision = DECISION_ASSIGN;
|
||||||
else
|
else
|
||||||
primary_decision = DECISION_EXCLUDE;
|
primary_decision = DECISION_EXCLUDE;
|
||||||
|
|
|
@ -114,7 +114,7 @@ static int upgrade_main(void *ctx, struct apk_database *db, int argc, char **arg
|
||||||
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->result_mask == APK_DEPMASK_CHECKSUM) {
|
if (dep->result_mask == APK_DEPMASK_CHECKSUM) {
|
||||||
dep->result_mask = APK_DEPMASK_REQUIRE;
|
dep->result_mask = APK_DEPMASK_ANY;
|
||||||
dep->version = apk_blob_atomize(APK_BLOB_NULL);
|
dep->version = apk_blob_atomize(APK_BLOB_NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
add a b>1
|
add a b>1
|
||||||
@EXPECT
|
@EXPECT
|
||||||
ERROR: 1 unsatisfiable dependencies:
|
ERROR: 1 unsatisfiable dependencies:
|
||||||
a-1: !b>1
|
world: a
|
||||||
|
|
Loading…
Reference in New Issue