From 038b672061919296b68b83a1ccead9c31b650c6c Mon Sep 17 00:00:00 2001 From: Timo Teräs Date: Tue, 1 Jun 2010 11:49:32 +0300 Subject: state: improve error messages from dependency failures Print more information why installation changeset calculation failed. Fixes #187. --- src/add.c | 26 +++++----- src/apk_blob.h | 1 + src/apk_package.h | 2 + src/apk_state.h | 2 + src/blob.c | 8 +++ src/del.c | 11 ++-- src/fix.c | 15 +++--- src/package.c | 59 +++++++++++---------- src/state.c | 149 +++++++++++++++++++++++++++++++++++++++++++++--------- src/upgrade.c | 12 ++--- 10 files changed, 197 insertions(+), 88 deletions(-) (limited to 'src') diff --git a/src/add.c b/src/add.c index 17935c8..91570fe 100644 --- a/src/add.c +++ b/src/add.c @@ -67,12 +67,12 @@ static int add_main(void *ctx, struct apk_database *db, int argc, char **argv) if (actx->virtpkg) { if (non_repository_check(db)) - goto err; + return -1; virtpkg = apk_pkg_new(); if (virtpkg == NULL) { apk_error("Failed to allocate virtual meta package"); - goto err; + return -1; } virtpkg->name = apk_db_get_name(db, APK_BLOB_STR(actx->virtpkg)); apk_blob_checksum(APK_BLOB_STR(virtpkg->name->name), @@ -96,7 +96,7 @@ static int add_main(void *ctx, struct apk_database *db, int argc, char **argv) struct apk_sign_ctx sctx; if (non_repository_check(db)) - goto err; + return -1; apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY_AND_GENERATE, NULL, db->keys_fd); @@ -104,14 +104,13 @@ static int add_main(void *ctx, struct apk_database *db, int argc, char **argv) apk_sign_ctx_free(&sctx); if (r != 0) { apk_error("%s: %s", argv[i], apk_error_str(r)); - goto err; - + return -1; } apk_dep_from_pkg(&dep, db, pkg); } else { r = apk_dep_from_blob(&dep, db, APK_BLOB_STR(argv[i])); if (r != 0) - goto err; + return -1; } if (virtpkg) @@ -126,7 +125,7 @@ static int add_main(void *ctx, struct apk_database *db, int argc, char **argv) state = apk_state_new(db); if (state == NULL) - goto err; + return -1; for (i = 0; i < num_deps; i++) { r = apk_state_lock_dependency(state, &deps[i]); @@ -134,16 +133,15 @@ static int add_main(void *ctx, struct apk_database *db, int argc, char **argv) apk_deps_add(&db->world, &deps[i]); deps[i].name->flags |= APK_NAME_TOPLEVEL; } else { - apk_error("Unable to install '%s': %d", - deps[i].name->name, r); errors++; } } - if (errors && !(apk_flags & APK_FORCE)) - goto err; - - r = apk_state_commit(state, db); -err: + if (errors && !(apk_flags & APK_FORCE)) { + apk_state_print_errors(state); + r = -1; + } else { + r = apk_state_commit(state, db); + } if (state != NULL) apk_state_unref(state); return r; diff --git a/src/apk_blob.h b/src/apk_blob.h index 5a5e381..8ef95c6 100644 --- a/src/apk_blob.h +++ b/src/apk_blob.h @@ -74,6 +74,7 @@ int apk_blob_spn(apk_blob_t blob, const char *accept, apk_blob_t *l, apk_blob_t int apk_blob_cspn(apk_blob_t blob, const char *reject, apk_blob_t *l, apk_blob_t *r); int apk_blob_split(apk_blob_t blob, apk_blob_t split, apk_blob_t *l, apk_blob_t *r); int apk_blob_rsplit(apk_blob_t blob, char split, apk_blob_t *l, apk_blob_t *r); +apk_blob_t apk_blob_pushed(apk_blob_t buffer, apk_blob_t left); unsigned long apk_blob_hash_seed(apk_blob_t, unsigned long seed); unsigned long apk_blob_hash(apk_blob_t str); int apk_blob_compare(apk_blob_t a, apk_blob_t b); diff --git a/src/apk_package.h b/src/apk_package.h index dbd469c..7901ddb 100644 --- a/src/apk_package.h +++ b/src/apk_package.h @@ -112,6 +112,8 @@ int apk_dep_from_blob(struct apk_dependency *dep, struct apk_database *db, apk_blob_t blob); void apk_dep_from_pkg(struct apk_dependency *dep, struct apk_database *db, struct apk_package *pkg); +void apk_blob_push_dep(apk_blob_t *to, struct apk_dependency *dep); + int apk_deps_add(struct apk_dependency_array **depends, struct apk_dependency *dep); void apk_deps_del(struct apk_dependency_array **deps, diff --git a/src/apk_state.h b/src/apk_state.h index 72c02b9..a862cdf 100644 --- a/src/apk_state.h +++ b/src/apk_state.h @@ -26,6 +26,7 @@ struct apk_state { unsigned int refs, num_names; struct apk_database *db; struct list_head change_list_head; + struct apk_package_array *conflicts; apk_name_state_t name[]; }; @@ -33,6 +34,7 @@ struct apk_state *apk_state_new(struct apk_database *db); struct apk_state *apk_state_dup(struct apk_state *state); void apk_state_unref(struct apk_state *state); +void apk_state_print_errors(struct apk_state *state); int apk_state_commit(struct apk_state *state, struct apk_database *db); int apk_state_lock_dependency(struct apk_state *state, struct apk_dependency *dep); diff --git a/src/blob.c b/src/blob.c index 0b6efbf..83bcf80 100644 --- a/src/blob.c +++ b/src/blob.c @@ -98,6 +98,14 @@ int apk_blob_split(apk_blob_t blob, apk_blob_t split, apk_blob_t *l, apk_blob_t } } +apk_blob_t apk_blob_pushed(apk_blob_t buffer, apk_blob_t left) +{ + if (buffer.ptr + buffer.len != left.ptr + left.len) + return APK_BLOB_NULL; + + return APK_BLOB_PTR_LEN(buffer.ptr, left.ptr - buffer.ptr); +} + unsigned long apk_blob_hash_seed(apk_blob_t blob, unsigned long seed) { unsigned long hash = seed; diff --git a/src/del.c b/src/del.c index 03b720e..c88bc81 100644 --- a/src/del.c +++ b/src/del.c @@ -56,13 +56,12 @@ static int del_main(void *ctx, struct apk_database *db, int argc, char **argv) .result_mask = APK_DEPMASK_CONFLICT, }; - r = apk_state_lock_dependency(state, &dep); - if (r != 0) { - apk_error("Unable to remove '%s'", name->name); - goto err; - } + r |= apk_state_lock_dependency(state, &dep); } - r = apk_state_commit(state, db); + if (r == 0) + r = apk_state_commit(state, db); + else + apk_state_print_errors(state); err: if (state != NULL) apk_state_unref(state); diff --git a/src/fix.c b/src/fix.c index 5c2ff54..f84fbc8 100644 --- a/src/fix.c +++ b/src/fix.c @@ -84,14 +84,13 @@ static int fix_main(void *pctx, struct apk_database *db, int argc, char **argv) name->flags |= APK_NAME_REINSTALL; } - for (i = 0; i < argc; i++) { - r = apk_state_lock_dependency(state, &deps[i]); - if (r != 0) { - if (!(apk_flags & APK_FORCE)) - goto err; - } - } - r = apk_state_commit(state, db); + for (i = 0; i < argc; i++) + r |= apk_state_lock_dependency(state, &deps[i]); + + if (r == 0 || (apk_flags & APK_FORCE)) + r = apk_state_commit(state, db); + else + apk_state_print_errors(state); err: if (r != 0 && i < argc) apk_error("Error while processing '%s'", argv[i]); diff --git a/src/package.c b/src/package.c index b97c412..b608fb8 100644 --- a/src/package.c +++ b/src/package.c @@ -286,43 +286,41 @@ void apk_deps_parse(struct apk_database *db, apk_blob_for_each_segment(blob, " ", parse_depend, &ctx); } +void apk_blob_push_dep(apk_blob_t *to, struct apk_dependency *dep) +{ + if (dep->result_mask == APK_DEPMASK_CONFLICT) + apk_blob_push_blob(to, APK_BLOB_PTR_LEN("!", 1)); + + apk_blob_push_blob(to, APK_BLOB_STR(dep->name->name)); + + if (dep->result_mask != APK_DEPMASK_CONFLICT && + dep->result_mask != APK_DEPMASK_REQUIRE) { + apk_blob_push_blob(to, APK_BLOB_STR(apk_version_op_string(dep->result_mask))); + apk_blob_push_blob(to, APK_BLOB_STR(dep->version)); + } +} + int apk_deps_write(struct apk_dependency_array *deps, struct apk_ostream *os) { - int i, r, n = 0; + apk_blob_t blob; + char tmp[256]; + int i, n = 0; if (deps == NULL) return 0; for (i = 0; i < deps->num; i++) { - if (i) { - if (os->write(os, " ", 1) != 1) - return -1; - n += 1; - } - - if (deps->item[i].result_mask == APK_DEPMASK_CONFLICT) { - if (os->write(os, "!", 1) != 1) - return -1; - n += 1; - } + blob = APK_BLOB_BUF(tmp); + if (i) + apk_blob_push_blob(&blob, APK_BLOB_PTR_LEN(" ", 1)); + apk_blob_push_dep(&blob, &deps->item[i]); + + blob = apk_blob_pushed(APK_BLOB_BUF(tmp), blob); + if (APK_BLOB_IS_NULL(blob) || + os->write(os, blob.ptr, blob.len) != blob.len) + return -1; - r = apk_ostream_write_string(os, deps->item[i].name->name); - if (r < 0) - return r; - n += r; - - if (deps->item[i].result_mask != APK_DEPMASK_CONFLICT && - deps->item[i].result_mask != APK_DEPMASK_REQUIRE) { - r = apk_ostream_write_string(os, apk_version_op_string(deps->item[i].result_mask)); - if (r < 0) - return r; - n += r; - - r = apk_ostream_write_string(os, deps->item[i].version); - if (r < 0) - return r; - n += r; - } + n += blob.len; } return n; @@ -967,7 +965,8 @@ int apk_pkg_write_index_entry(struct apk_package *info, if (APK_BLOB_IS_NULL(bbuf)) return -1; - if (os->write(os, buf, bbuf.ptr - buf) != bbuf.ptr - buf) + bbuf = apk_blob_pushed(APK_BLOB_BUF(buf), bbuf); + if (os->write(os, bbuf.ptr, bbuf.len) != bbuf.len) return -1; if (info->depends != NULL) { diff --git a/src/state.c b/src/state.c index f0561b6..33cabb8 100644 --- a/src/state.c +++ b/src/state.c @@ -34,6 +34,16 @@ int apk_state_prune_dependency(struct apk_state *state, #define APK_NS_LOCKED 0x00000001 #define APK_NS_PENDING 0x00000002 +#define APK_NS_ERROR 0x00000004 + +static void apk_state_record_conflict(struct apk_state *state, + struct apk_package *pkg) +{ + struct apk_name *name = pkg->name; + + state->name[name->id] = (void*) (((intptr_t)state->name[name->id]) | APK_NS_ERROR); + *apk_package_array_add(&state->conflicts) = pkg; +} static int inline ns_locked(apk_name_state_t name) { @@ -49,6 +59,13 @@ static int inline ns_pending(apk_name_state_t name) return FALSE; } +static int inline ns_error(apk_name_state_t name) +{ + if (((intptr_t) name) & APK_NS_ERROR) + return TRUE; + return FALSE; +} + static int ns_empty(apk_name_state_t name) { return name == NULL; @@ -67,7 +84,7 @@ static apk_name_state_t ns_from_pkg_non_pending(struct apk_package *pkg) static struct apk_package *ns_to_pkg(apk_name_state_t name) { return (struct apk_package *) - (((intptr_t) name) & ~(APK_NS_LOCKED | APK_NS_PENDING)); + (((intptr_t) name) & ~(APK_NS_LOCKED | APK_NS_PENDING | APK_NS_ERROR)); } static apk_name_state_t ns_from_choices(struct apk_name_choices *nc) @@ -247,7 +264,9 @@ int apk_state_prune_dependency(struct apk_state *state, struct apk_package *pkg = ns_to_pkg(state->name[name->id]); /* Locked to not-installed / remove? */ - if (pkg == NULL) { + if (ns_error(state->name[name->id])) { + return -1; + } else if (pkg == NULL) { if (dep->result_mask != APK_DEPMASK_CONFLICT) return -1; } else { @@ -258,6 +277,7 @@ int apk_state_prune_dependency(struct apk_state *state, if (ns_pending(state->name[name->id])) return 1; + return 0; } @@ -370,22 +390,29 @@ int apk_state_lock_dependency(struct apk_state *state, static int apk_state_fix_package(struct apk_state *state, struct apk_package *pkg) { - int i, r; + int i, r, ret = 0; + + if (pkg == NULL || pkg->depends == NULL) + return 0; for (i = 0; i < pkg->depends->num; i++) { r = apk_state_lock_dependency(state, &pkg->depends->item[i]); if (r != 0) - return -1; + ret = -1; } - return 0; + + return ret; } static int call_if_dependency_broke(struct apk_state *state, struct apk_package *pkg, struct apk_name *dep_name, int (*cb)(struct apk_state *state, - struct apk_package *pkg)) + struct apk_package *pkg, + struct apk_dependency *dep, + void *ctx), + void *ctx) { struct apk_package *dep_pkg; int k; @@ -405,7 +432,7 @@ static int call_if_dependency_broke(struct apk_state *state, (apk_version_compare(dep_pkg->version, dep->version) & dep->result_mask)) continue; - return cb(state, pkg); + return cb(state, pkg, dep, ctx); } return 0; @@ -414,7 +441,10 @@ static int call_if_dependency_broke(struct apk_state *state, static int for_each_broken_reverse_depency(struct apk_state *state, struct apk_name *name, int (*cb)(struct apk_state *state, - struct apk_package *pkg)) + struct apk_package *pkg, + struct apk_dependency *dep, + void *ctx), + void *ctx) { struct apk_package *pkg0; int i, j, r; @@ -429,7 +459,8 @@ static int for_each_broken_reverse_depency(struct apk_state *state, pkg0 = ns_to_pkg(state->name[name0->id]); if (pkg0 == NULL) continue; - r = call_if_dependency_broke(state, pkg0, name, cb); + r = call_if_dependency_broke(state, pkg0, name, + cb, ctx); if (r != 0) return r; } else if (!ns_empty(state->name[name0->id])) { @@ -441,7 +472,7 @@ static int for_each_broken_reverse_depency(struct apk_state *state, continue; r = call_if_dependency_broke(state, ns->pkgs[j], - name, cb); + name, cb, ctx); if (r != 0) return r; break; @@ -455,7 +486,7 @@ static int for_each_broken_reverse_depency(struct apk_state *state, r = call_if_dependency_broke(state, name0->pkgs->item[j], - name, cb); + name, cb, ctx); if (r != 0) return r; break; @@ -467,20 +498,24 @@ static int for_each_broken_reverse_depency(struct apk_state *state, } static int delete_broken_package(struct apk_state *state, - struct apk_package *pkg) + struct apk_package *pkg, + struct apk_dependency *dep, + void *ctx) { return apk_state_lock_name(state, pkg->name, NULL); } static int reinstall_broken_package(struct apk_state *state, - struct apk_package *pkg) + struct apk_package *pkg, + struct apk_dependency *dep, + void *ctx) { - struct apk_dependency dep = { + struct apk_dependency dep0 = { .name = pkg->name, .result_mask = APK_DEPMASK_REQUIRE, }; - return apk_state_lock_dependency(state, &dep); + return apk_state_lock_dependency(state, &dep0); } int apk_state_lock_name(struct apk_state *state, @@ -512,16 +547,19 @@ int apk_state_lock_name(struct apk_state *state, r = for_each_broken_reverse_depency(state, name, newpkg == NULL ? delete_broken_package : - reinstall_broken_package); - if (r != 0) + reinstall_broken_package, + NULL); + if (r != 0) { + apk_state_record_conflict(state, newpkg); return r; + } } /* Check that all other dependencies hold for the new package. */ - if (newpkg != NULL && newpkg->depends != NULL) { - r = apk_state_fix_package(state, newpkg); - if (r != 0) - return r; + r = apk_state_fix_package(state, newpkg); + if (r != 0) { + apk_state_record_conflict(state, newpkg); + return r; } /* If the chosen package is installed, all is done here */ @@ -709,7 +747,9 @@ static int cmp_upgrade(struct apk_change *change) } static int fail_if_something_broke(struct apk_state *state, - struct apk_package *pkg) + struct apk_package *pkg, + struct apk_dependency *dep, + void *ctx) { return 1; @@ -735,7 +775,8 @@ static int apk_state_autoclean(struct apk_state *state, oldns = state->name[n->id]; state->name[n->id] = ns_from_pkg(NULL); r = for_each_broken_reverse_depency(state, n, - fail_if_something_broke); + fail_if_something_broke, + NULL); state->name[n->id] = oldns; if (r == 0) { @@ -747,6 +788,68 @@ static int apk_state_autoclean(struct apk_state *state, return 0; } +struct error_state { + struct apk_indent indent; + struct apk_package *prevpkg; +}; + +static int print_dep(struct apk_state *state, + struct apk_package *pkg, + struct apk_dependency *dep, + void *ctx) +{ + struct error_state *es = (struct error_state *) ctx; + apk_blob_t blob; + char buf[256]; + int len; + + if (pkg != es->prevpkg) { + printf("\n"); + es->indent.x = 0; + len = snprintf(buf, sizeof(buf), "%s-%s:", + pkg->name->name, pkg->version); + apk_print_indented(&es->indent, APK_BLOB_PTR_LEN(buf, len)); + es->prevpkg = pkg; + } + + blob = APK_BLOB_BUF(buf); + apk_blob_push_dep(&blob, dep); + blob = apk_blob_pushed(APK_BLOB_BUF(buf), blob); + apk_print_indented(&es->indent, blob); + + return 0; +} + +void apk_state_print_errors(struct apk_state *state) +{ + struct apk_package *pkg; + struct error_state es; + int i, j, r; + + if (state->conflicts == NULL) + return; + + apk_error("Unable to satisfy all dependencies:"); + for (i = 0; i < state->conflicts->num; i++) { + es.prevpkg = pkg = state->conflicts->item[i]; + es.indent.x = es.indent.indent = + printf(" %s-%s:", + pkg->name->name, pkg->version); + + for (j = 0; j < pkg->depends->num; j++) { + r = apk_state_lock_dependency(state, + &pkg->depends->item[j]); + if (r != 0) + print_dep(state, pkg, &pkg->depends->item[j], &es); + } + + /* Print conflicting reverse deps */ + for_each_broken_reverse_depency(state, pkg->name, + print_dep, &es); + printf("\n"); + } +} + int apk_state_commit(struct apk_state *state, struct apk_database *db) { diff --git a/src/upgrade.c b/src/upgrade.c index 879e14c..611f677 100644 --- a/src/upgrade.c +++ b/src/upgrade.c @@ -46,14 +46,12 @@ static int upgrade_main(void *ctx, struct apk_database *db, int argc, char **arg dep->result_mask = APK_VERSION_EQUAL | APK_VERSION_LESS | APK_VERSION_GREATER; dep->version = NULL; } - r = apk_state_lock_dependency(state, dep); - if (r != 0) { - apk_error("Unable to upgrade '%s'", - dep->name->name); - goto err; - } + r |= apk_state_lock_dependency(state, dep); } - r = apk_state_commit(state, db); + if (r == 0) + r = apk_state_commit(state, db); + else + apk_state_print_errors(state); err: if (state != NULL) apk_state_unref(state); -- cgit v1.2.3-60-g2f50