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;
|
||||
|
||||
apk_blob_pull_dep(&b, db, &virtdep);
|
||||
if (APK_BLOB_IS_NULL(b) ||
|
||||
virtdep.result_mask != APK_DEPMASK_REQUIRE ||
|
||||
if (APK_BLOB_IS_NULL(b) || virtdep.conflict ||
|
||||
virtdep.result_mask != APK_DEPMASK_ANY ||
|
||||
virtdep.version != &apk_null_blob) {
|
||||
apk_error("%s: bad package specifier");
|
||||
return -1;
|
||||
|
|
|
@ -61,7 +61,7 @@ struct apk_dependency {
|
|||
struct apk_name *name;
|
||||
apk_blob_t *version;
|
||||
unsigned short repository_tag;
|
||||
unsigned optional : 1;
|
||||
unsigned conflict : 1;
|
||||
unsigned result_mask : 3;
|
||||
};
|
||||
APK_ARRAY(apk_dependency_array, struct apk_dependency);
|
||||
|
|
|
@ -18,8 +18,7 @@
|
|||
#define APK_VERSION_LESS 2
|
||||
#define APK_VERSION_GREATER 4
|
||||
|
||||
#define APK_DEPMASK_CONFLICT (0)
|
||||
#define APK_DEPMASK_REQUIRE (APK_VERSION_EQUAL|APK_VERSION_LESS|\
|
||||
#define APK_DEPMASK_ANY (APK_VERSION_EQUAL|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) {
|
||||
.name = apk_db_get_name(db, APK_BLOB_STR(argv[i])),
|
||||
.version = apk_blob_atomize(APK_BLOB_NULL),
|
||||
.result_mask = APK_DEPMASK_REQUIRE,
|
||||
.result_mask = APK_DEPMASK_ANY,
|
||||
};
|
||||
|
||||
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) {
|
||||
.name = pkg->name,
|
||||
.version = apk_blob_atomize(APK_BLOB_NULL),
|
||||
.result_mask = APK_DEPMASK_REQUIRE,
|
||||
.result_mask = APK_DEPMASK_ANY,
|
||||
};
|
||||
apk_deps_add(&deps, &dep);
|
||||
} 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;
|
||||
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 */
|
||||
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] == '!') {
|
||||
bdep.ptr++;
|
||||
bdep.len--;
|
||||
optional = 1;
|
||||
conflict = 1;
|
||||
}
|
||||
|
||||
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)
|
||||
goto fail;
|
||||
|
||||
if (optional)
|
||||
mask ^= APK_DEPMASK_REQUIRE;
|
||||
|
||||
*dep = (struct apk_dependency){
|
||||
.name = name,
|
||||
.version = apk_blob_atomize_dup(bver),
|
||||
.repository_tag = tag,
|
||||
.result_mask = mask,
|
||||
.optional = optional,
|
||||
.conflict = conflict,
|
||||
};
|
||||
return;
|
||||
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)
|
||||
{
|
||||
if (p == NULL)
|
||||
return dep->optional;
|
||||
if (p == NULL || p->pkg == NULL)
|
||||
return dep->conflict;
|
||||
|
||||
switch (dep->result_mask) {
|
||||
case APK_DEPMASK_CHECKSUM:
|
||||
return apk_dep_match_checksum(dep, p->pkg);
|
||||
case APK_DEPMASK_CONFLICT:
|
||||
return 0;
|
||||
case APK_DEPMASK_REQUIRE:
|
||||
return 1;
|
||||
case APK_DEPMASK_ANY:
|
||||
return !dep->conflict;
|
||||
default:
|
||||
if (p->version == &apk_null_blob)
|
||||
return dep->conflict;
|
||||
if (apk_version_compare_blob(*p->version, *dep->version)
|
||||
& dep->result_mask)
|
||||
return 1;
|
||||
return 0;
|
||||
return !dep->conflict;
|
||||
return dep->conflict;
|
||||
}
|
||||
return 0;
|
||||
return dep->conflict;
|
||||
}
|
||||
|
||||
int apk_dep_is_materialized(struct apk_dependency *dep, struct apk_package *pkg)
|
||||
{
|
||||
if (pkg == NULL)
|
||||
return dep->optional;
|
||||
return dep->conflict;
|
||||
if (dep->name != pkg->name)
|
||||
return 0;
|
||||
return dep->conflict;
|
||||
|
||||
switch (dep->result_mask) {
|
||||
case APK_DEPMASK_CHECKSUM:
|
||||
return apk_dep_match_checksum(dep, pkg);
|
||||
case APK_DEPMASK_CONFLICT:
|
||||
return 0;
|
||||
case APK_DEPMASK_REQUIRE:
|
||||
return 1;
|
||||
case APK_DEPMASK_ANY:
|
||||
return !dep->conflict;
|
||||
default:
|
||||
if (apk_version_compare_blob(*pkg->version, *dep->version)
|
||||
& dep->result_mask)
|
||||
return 1;
|
||||
return 0;
|
||||
return !dep->conflict;
|
||||
return dep->conflict;
|
||||
}
|
||||
return 0;
|
||||
return dep->conflict;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (pkg == NULL)
|
||||
return dep->optional;
|
||||
return dep->conflict;
|
||||
|
||||
if (dep->name == pkg->name)
|
||||
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 dep->optional;
|
||||
return dep->conflict;
|
||||
}
|
||||
|
||||
void apk_blob_push_dep(apk_blob_t *to, struct apk_database *db, struct apk_dependency *dep)
|
||||
{
|
||||
int result_mask = dep->result_mask;
|
||||
|
||||
if (dep->optional) {
|
||||
if (dep->conflict)
|
||||
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));
|
||||
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_reinstall;
|
||||
|
||||
unsigned short conflicts;
|
||||
unsigned short must_not;
|
||||
unsigned short incompat_dep;
|
||||
|
||||
unsigned char preference;
|
||||
unsigned handle_install_if : 1;
|
||||
unsigned allowed : 1;
|
||||
|
@ -152,6 +154,7 @@ struct apk_solver_state {
|
|||
struct apk_score best_score;
|
||||
|
||||
unsigned solver_flags : 4;
|
||||
unsigned impossible_state : 1;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
|
@ -367,7 +370,7 @@ static int get_topology_score(
|
|||
int score_locked = TRUE, sticky_installed = FALSE;
|
||||
|
||||
score = (struct apk_score) {
|
||||
.conflicts = ps->conflicts,
|
||||
.conflicts = ps->incompat_dep,
|
||||
.preference = ps->preference,
|
||||
};
|
||||
|
||||
|
@ -796,6 +799,7 @@ static solver_result_t apply_decision(struct apk_solver_state *ss,
|
|||
struct apk_score score;
|
||||
int i;
|
||||
|
||||
ss->impossible_state = 0;
|
||||
ns->name_touched = 1;
|
||||
if (pkg != NULL) {
|
||||
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) {
|
||||
dbg_printf("%s: %s penalty too big: "SCORE_FMT">="SCORE_FMT"\n",
|
||||
name->name,
|
||||
|
@ -1024,13 +1035,18 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency
|
|||
else
|
||||
dbg_printf("%s: locked to empty\n",
|
||||
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;
|
||||
if (dep->conflict)
|
||||
ss->impossible_state = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (name->providers->num == 0) {
|
||||
if (!dep->optional)
|
||||
if (!dep->conflict)
|
||||
ss->score.conflicts += strength;
|
||||
return;
|
||||
}
|
||||
|
@ -1045,10 +1061,14 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency
|
|||
continue;
|
||||
|
||||
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",
|
||||
PKG_VER_PRINTF(pkg0),
|
||||
ps0->conflicts);
|
||||
ps0->must_not);
|
||||
changed |= 1;
|
||||
} else if (requirer_pkg != NULL) {
|
||||
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)
|
||||
ns->last_touched_decision = ss->num_decisions;
|
||||
|
||||
if (!dep->optional)
|
||||
if (!dep->conflict)
|
||||
ns->requirers += strength;
|
||||
|
||||
promote_name(ss, name);
|
||||
|
@ -1096,7 +1116,7 @@ static void undo_constraint(struct apk_solver_state *ss, struct apk_dependency *
|
|||
return;
|
||||
}
|
||||
if (name->providers->num == 0) {
|
||||
if (!dep->optional)
|
||||
if (!dep->conflict)
|
||||
ss->score.conflicts -= strength;
|
||||
return;
|
||||
}
|
||||
|
@ -1111,10 +1131,13 @@ static void undo_constraint(struct apk_solver_state *ss, struct apk_dependency *
|
|||
continue;
|
||||
|
||||
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",
|
||||
PKG_VER_PRINTF(pkg0),
|
||||
ps0->conflicts);
|
||||
ps0->must_not);
|
||||
} else if (requirer_pkg != NULL) {
|
||||
dbg_printf(PKG_VER_FMT ": uninheriting flags and pinning from "PKG_VER_FMT"\n",
|
||||
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)
|
||||
ns->last_touched_decision = ss->num_decisions;
|
||||
|
||||
if (!dep->optional)
|
||||
if (!dep->conflict)
|
||||
ns->requirers -= strength;
|
||||
|
||||
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_score pkg0_score;
|
||||
|
||||
if (ps0 == NULL || ps0->locked ||
|
||||
if (ps0 == NULL || ps0->locked || ps0->must_not ||
|
||||
ss->topology_position < pkg0->topology_hard ||
|
||||
(pkg0->ipkg == NULL && (!ps0->allowed || !pkg_available(ss->db, pkg0))))
|
||||
continue;
|
||||
|
@ -1274,7 +1297,7 @@ static int expand_branch(struct apk_solver_state *ss)
|
|||
|
||||
if (!ns->none_excluded) {
|
||||
struct apk_package_state *ps0 = pkg_to_ps(pkg0);
|
||||
if (ps0->conflicts > ns->requirers)
|
||||
if (ps0->incompat_dep > ns->requirers)
|
||||
primary_decision = DECISION_ASSIGN;
|
||||
else
|
||||
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++) {
|
||||
struct apk_dependency *dep = &world->item[i];
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
add a b>1
|
||||
@EXPECT
|
||||
ERROR: 1 unsatisfiable dependencies:
|
||||
a-1: !b>1
|
||||
world: a
|
||||
|
|
Loading…
Reference in New Issue