diff --git a/bin/xbps-alternatives/main.c b/bin/xbps-alternatives/main.c index e3aec59e3..e76f85b6f 100644 --- a/bin/xbps-alternatives/main.c +++ b/bin/xbps-alternatives/main.c @@ -149,8 +149,10 @@ list_alternatives(struct xbps_handle *xhp, const char *pkgname, const char *grp) xbps_array_get_cstring_nocopy(array, x, &str); printf(" - %s%s\n", str, x == 0 ? " (current)" : ""); pkgd = xbps_pkgdb_get_pkg(xhp, str); - assert(pkgd); - list_pkg_alternatives(pkgd, keyname, false); + if (pkgd) + list_pkg_alternatives(pkgd, keyname, false); + else + xbps_dbg_printf(xhp, "Not installed package '%s' registered as alternative for '%s'\n", str, grp); } } xbps_object_release(allkeys); diff --git a/lib/package_alternatives.c b/lib/package_alternatives.c index f23b440d0..8015347d1 100644 --- a/lib/package_alternatives.c +++ b/lib/package_alternatives.c @@ -327,6 +327,31 @@ switch_alt_group(struct xbps_handle *xhp, const char *grpn, const char *pkgn, return create_symlinks(xhp, xbps_dictionary_get(pkgalts, grpn), grpn); } +/* + * Removes packages that do not provide alternatives for group. + * Old xbps versions didn't clean up list on time. + */ +static void +remove_outdated_packages(struct xbps_handle *xhp, const char *groupname, xbps_array_t packages) +{ + for (unsigned int i = 0; i < xbps_array_count(packages);) { + const char *pkgname = NULL; + xbps_dictionary_t pkgdict; + xbps_dictionary_t alts; + xbps_array_t alts_group; + xbps_array_get_cstring_nocopy(packages, i, &pkgname); + if (!(pkgdict = xbps_pkgdb_get_pkg(xhp, pkgname)) || + !(alts = xbps_dictionary_get(pkgdict, "alternatives")) || + !(alts_group = xbps_dictionary_get(alts, groupname)) || + xbps_array_count(alts_group) == 0 + ) { + xbps_array_remove(packages, i); + continue; + } + i++; + } +} + int xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd) { @@ -380,18 +405,20 @@ xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd) xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_REMOVED, 0, NULL, "%s: unregistered '%s' alternatives group", pkgver, keyname); xbps_remove_string_from_array(array, pkgname); - xbps_array_get_cstring_nocopy(array, 0, &first); } + remove_outdated_packages(xhp, keyname, array); if (xbps_array_count(array) == 0) { xbps_dictionary_remove(alternatives, keyname); continue; } + /* XXX: ... && remove_outdated_packages didn't removed current package) */ if (update || !current) continue; /* get the new alternative group package */ + xbps_array_get_cstring_nocopy(array, 0, &first); if (switch_alt_group(xhp, keyname, first, &pkg_alternatives) != 0) break; } @@ -415,7 +442,8 @@ prune_altgroup(struct xbps_handle *xhp, xbps_dictionary_t repod, xbps_array_t array; xbps_dictionary_t alternatives; xbps_string_t kstr; - unsigned int grp_count; + unsigned int grp_count, depends_count; + uint64_t size = 0; bool current = false; xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_REMOVED, 0, NULL, @@ -442,31 +470,35 @@ prune_altgroup(struct xbps_handle *xhp, xbps_dictionary_t repod, return; } - if (xbps_array_count(xbps_dictionary_get(repod, "run_depends")) == 0 && - xbps_array_count(xbps_dictionary_get(repod, "shlib-requires")) == 0) { + xbps_dictionary_get_uint64(repod, "installed_size", &size); + depends_count = xbps_array_count(xbps_dictionary_get(repod, "run_depends")); + + /* + * Non-empty package is an ordinary package dropping alternatives. + * Empty dependencies indicate a removed package (pure meta). + */ + if (size == 0 && 0 < depends_count) { /* - * Empty dependencies indicate a removed package (pure meta), - * use the first available group after ours has been pruned + * Use the last group, as this indicates that a transitional metapackage + * is replacing the original and therefore a new package has registered + * a replacement group, which should be last in the array (most recent). */ - xbps_array_get_cstring_nocopy(array, 0, &newpkg); - switch_alt_group(xhp, keyname, newpkg, NULL); - return; + xbps_array_get_cstring_nocopy(array, grp_count - 1, &newpkg); + /* put the new package as head */ + kstr = xbps_string_create_cstring(newpkg); + xbps_remove_string_from_array(array, newpkg); + xbps_array_add_first(array, kstr); + xbps_object_release(kstr); } - /* - * Use the last group, as this indicates that a transitional metapackage - * is replacing the original and therefore a new package has registered - * a replacement group, which should be last in the array (most recent). - */ - xbps_array_get_cstring_nocopy(array, grp_count - 1, &newpkg); + remove_outdated_packages(xhp, keyname, array); + if (xbps_array_count(array) == 0) { + /* it was the last one, ditch the whole thing */ + xbps_dictionary_remove(alternatives, keyname); + return; + } - /* put the new package as head */ - kstr = xbps_string_create_cstring(newpkg); - xbps_remove_string_from_array(array, newpkg); - xbps_array_add_first(array, kstr); xbps_array_get_cstring_nocopy(array, 0, &newpkg); - xbps_object_release(kstr); - switch_alt_group(xhp, keyname, newpkg, NULL); } diff --git a/tests/xbps/xbps-alternatives/main_test.sh b/tests/xbps/xbps-alternatives/main_test.sh index 084069f05..b0976b5f9 100644 --- a/tests/xbps/xbps-alternatives/main_test.sh +++ b/tests/xbps/xbps-alternatives/main_test.sh @@ -263,6 +263,84 @@ unregister_multi_body() { atf_check_equal $rv 0 } +atf_test_case alternative_unregister + +alternative_unregister_head() { + atf_set "descr" "xbps-alternatives: removal of the alternative group from pkgdb" +} +alternative_unregister_body() { + mkdir -p repo pkg_A/usr/bin + mkdir -p repo pkg_B/usr/bin + touch pkg_A/usr/bin/gcc + touch pkg_B/usr/bin/clang + + cd repo + xbps-create -A noarch -n pkgA-1.1_1 -s "A pkg" --alternatives "cc:cc:/usr/bin/A" ../pkg_A + atf_check_equal $? 0 + xbps-create -A noarch -n pkgB-1.1_1 -s "B pkg" --alternatives "cc:cc:/usr/bin/B" ../pkg_B + atf_check_equal $? 0 + xbps-rindex -d -a $PWD/*.xbps + atf_check_equal $? 0 + cd .. + + xbps-install -r root --repository=repo -ydv pkgA pkgB + atf_check_equal $? 0 + atf_check_equal "$(grep -c ' pkgA' root/var/db/xbps/pkgdb*.plist)"A 1A + atf_check_equal "$(grep -c ' pkgB' root/var/db/xbps/pkgdb*.plist)"B 1B + + cd repo + xbps-create -A noarch -n pkgA-1.1_2 -s "A pkg" ../pkg_A + atf_check_equal $? 0 + xbps-create -A noarch -n pkgB-1.1_2 -s "B pkg" --alternatives "cc:cc:/usr/bin/B" ../pkg_B + atf_check_equal $? 0 + xbps-rindex -d -a $PWD/*.xbps + atf_check_equal $? 0 + cd .. + + xbps-install -r root --repository=repo -dvyu + atf_check_equal $? 0 + xbps-remove -r root -dvy pkgA + atf_check_equal $? 0 + atf_check_equal "$(grep -c ' pkgA' root/var/db/xbps/pkgdb*.plist)"A 0A + atf_check_equal "$(grep -c ' pkgB' root/var/db/xbps/pkgdb*.plist)"B 1B +} + + +atf_test_case handle_0_57_1_pkgdb + +handle_0_57_1_pkgdb_head() { + atf_set "descr" "xbps-alternatives: processing old pkgdb containing removed packages in _XBPS_ALTERNATIVES_" +} +handle_0_57_1_pkgdb_body() { + mkdir -p repo pkg_A/usr/bin + mkdir -p repo pkg_B/usr/bin + touch pkg_A/usr/bin/gcc + touch pkg_B/usr/bin/clang + + cd repo + xbps-create -A noarch -n pkgA-1.1_1 -s "A pkg" ../pkg_A + atf_check_equal $? 0 + xbps-create -A noarch -n pkgB-1.1_1 -s "B pkg" --alternatives "cc:cc:/usr/bin/B" ../pkg_B + atf_check_equal $? 0 + xbps-rindex -d -a $PWD/*.xbps + atf_check_equal $? 0 + cd .. + + xbps-install -r root --repository=repo -ydv pkgB + atf_check_equal $? 0 + + atf_check_equal "$(grep -c ' pkgA' root/var/db/xbps/pkgdb*.plist)"A 0A + atf_check_equal "$(grep -c ' pkgB' root/var/db/xbps/pkgdb*.plist)"B 1B + sed -e 's:pkgB:& pkgA:' -i root/var/db/xbps/pkgdb*.plist + atf_check_equal "$(grep -c ' pkgA' root/var/db/xbps/pkgdb*.plist)"A 1A + + xbps-alternatives -r root -l + atf_check_equal $? 0 + + xbps-remove -r root -dvy pkgB + atf_check_equal $? 0 +} + atf_test_case set_pkg set_pkg_head() { @@ -951,6 +1029,8 @@ atf_init_test_cases() { atf_add_test_case unregister_one atf_add_test_case unregister_one_relative atf_add_test_case unregister_multi + atf_add_test_case alternative_unregister + atf_add_test_case handle_0_57_1_pkgdb atf_add_test_case set_pkg atf_add_test_case set_pkg_group atf_add_test_case update_pkgs