xbps: add patch to properly prune alternatives on updates
Required for https://github.com/void-linux/void-packages/pull/16458. The next OpenJDK8 update will involve renaming to `openjdk8`. With current xbps that will result in failures as we need to create a transitional package that, unlike the real one, has no alternatives, and xbps currently does not account for that.
This commit is contained in:
parent
11a1f7f4f4
commit
d188a1b967
2 changed files with 356 additions and 1 deletions
355
srcpkgs/xbps/patches/85b8b3b.patch
Normal file
355
srcpkgs/xbps/patches/85b8b3b.patch
Normal file
|
@ -0,0 +1,355 @@
|
|||
From 85b8b3bbb72ab6de6f4d72c9a2ca3f2be053eeb1 Mon Sep 17 00:00:00 2001
|
||||
From: q66 <daniel@octaforge.org>
|
||||
Date: Thu, 14 Nov 2019 17:40:54 +0100
|
||||
Subject: [PATCH] lib/package_alternatives.c: prune obsolete alternatives
|
||||
groups
|
||||
|
||||
In the edge case when an updated package has different (or no)
|
||||
alternatives groups, make sure to prune those that are in pkgdb
|
||||
but not in the newly installed package.
|
||||
|
||||
A potentially common case of this is when a package that formerly
|
||||
had alternatives gets removed and a transitional metapackage
|
||||
takes its place (which has no alternatives).
|
||||
|
||||
When the new package has no dependencies, oldest next possible
|
||||
alternatives group will be used. This is because that indicates
|
||||
a removed package. When there are dependencies, the newest one
|
||||
will be used; as this indicates a transitional package.
|
||||
---
|
||||
lib/package_alternatives.c | 155 +++++++++++++++++-----
|
||||
tests/xbps/xbps-alternatives/main_test.sh | 93 +++++++++++++
|
||||
2 files changed, 218 insertions(+), 30 deletions(-)
|
||||
|
||||
diff --git a/lib/package_alternatives.c b/lib/package_alternatives.c
|
||||
index 5e4f2b36..ecdc40ae 100644
|
||||
--- lib/package_alternatives.c
|
||||
+++ lib/package_alternatives.c
|
||||
@@ -309,6 +309,22 @@ xbps_alternatives_set(struct xbps_handle *xhp, const char *pkgname,
|
||||
return rv;
|
||||
}
|
||||
|
||||
+static int
|
||||
+switch_alt_group(struct xbps_handle *xhp, const char *grpn, const char *pkgn,
|
||||
+ xbps_dictionary_t *pkg_alternatives)
|
||||
+{
|
||||
+ xbps_dictionary_t curpkgd, pkgalts;
|
||||
+
|
||||
+ curpkgd = xbps_pkgdb_get_pkg(xhp, pkgn);
|
||||
+ assert(curpkgd);
|
||||
+
|
||||
+ xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_SWITCHED, 0, NULL,
|
||||
+ "Switched '%s' alternatives group to '%s'", grpn, pkgn);
|
||||
+ pkgalts = xbps_dictionary_get(curpkgd, "alternatives");
|
||||
+ if (pkg_alternatives) *pkg_alternatives = pkgalts;
|
||||
+ return create_symlinks(xhp, xbps_dictionary_get(pkgalts, grpn), grpn);
|
||||
+}
|
||||
+
|
||||
int
|
||||
xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
|
||||
{
|
||||
@@ -339,7 +355,6 @@ xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
|
||||
for (unsigned int i = 0; i < xbps_array_count(allkeys); i++) {
|
||||
xbps_array_t array;
|
||||
xbps_object_t keysym;
|
||||
- xbps_dictionary_t curpkgd = pkgd;
|
||||
bool current = false;
|
||||
const char *first = NULL, *keyname;
|
||||
|
||||
@@ -377,15 +392,7 @@ xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
|
||||
continue;
|
||||
|
||||
/* get the new alternative group package */
|
||||
- curpkgd = xbps_pkgdb_get_pkg(xhp, first);
|
||||
- assert(curpkgd);
|
||||
- xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_SWITCHED, 0, NULL,
|
||||
- "Switched '%s' alternatives group to '%s'", keyname, first);
|
||||
- pkg_alternatives = xbps_dictionary_get(curpkgd, "alternatives");
|
||||
- rv = create_symlinks(xhp,
|
||||
- xbps_dictionary_get(pkg_alternatives, keyname),
|
||||
- keyname);
|
||||
- if (rv != 0)
|
||||
+ if (switch_alt_group(xhp, keyname, first, &pkg_alternatives) != 0)
|
||||
break;
|
||||
}
|
||||
xbps_object_release(allkeys);
|
||||
@@ -394,25 +401,119 @@ xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
|
||||
return rv;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Prune the alternatives group from the db. This will first unregister
|
||||
+ * it for the package and if there's no other package left providing the
|
||||
+ * same, also ditch the whole group. When this is called, it is guranteed
|
||||
+ * that what is happening is an upgrade, because it's only invoked when
|
||||
+ * the repo and installed alternatives sets differ for a specific package.
|
||||
+ */
|
||||
+static void
|
||||
+prune_altgroup(struct xbps_handle *xhp, xbps_dictionary_t repod,
|
||||
+ char *pkgname, const char *pkgver, const char *keyname) {
|
||||
+ const char *newpkg, *curpkg;
|
||||
+ xbps_array_t array;
|
||||
+ xbps_dictionary_t alternatives;
|
||||
+ xbps_string_t kstr;
|
||||
+ unsigned int grp_count;
|
||||
+ bool current = false;
|
||||
+
|
||||
+ xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_REMOVED, 0, NULL,
|
||||
+ "%s: unregistered '%s' alternatives group", pkgver, keyname);
|
||||
+
|
||||
+ alternatives = xbps_dictionary_get(xhp->pkgdb, "_XBPS_ALTERNATIVES_");
|
||||
+ assert(alternatives);
|
||||
+ array = xbps_dictionary_get(alternatives, keyname);
|
||||
+
|
||||
+ /* if using alt group from another package, we won't switch anything */
|
||||
+ xbps_array_get_cstring_nocopy(array, 0, &curpkg);
|
||||
+ current = (strcmp(pkgname, curpkg) == 0);
|
||||
+
|
||||
+ /* actually prune the alt group for the current package */
|
||||
+ xbps_remove_string_from_array(array, pkgname);
|
||||
+ grp_count = xbps_array_count(array);
|
||||
+ if (grp_count == 0) {
|
||||
+ /* it was the last one, ditch the whole thing */
|
||||
+ xbps_dictionary_remove(alternatives, keyname);
|
||||
+ return;
|
||||
+ }
|
||||
+ if (!current) {
|
||||
+ /* not the last one, and ours wasn't the one being used */
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (xbps_array_count(xbps_dictionary_get(repod, "run_depends")) == 0 &&
|
||||
+ xbps_array_count(xbps_dictionary_get(repod, "shlib-requires")) == 0) {
|
||||
+ /*
|
||||
+ * Empty dependencies indicate a removed package (pure meta),
|
||||
+ * use the first available group after ours has been pruned
|
||||
+ */
|
||||
+ xbps_array_get_cstring_nocopy(array, 0, &newpkg);
|
||||
+ switch_alt_group(xhp, keyname, newpkg, NULL);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * 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);
|
||||
+
|
||||
+ /* 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);
|
||||
+}
|
||||
+
|
||||
+
|
||||
static void
|
||||
-remove_obsoletes(struct xbps_handle *xhp, xbps_dictionary_t pkgd, xbps_dictionary_t repod)
|
||||
+remove_obsoletes(struct xbps_handle *xhp, char *pkgname, const char *pkgver,
|
||||
+ xbps_dictionary_t repod)
|
||||
{
|
||||
xbps_array_t allkeys;
|
||||
+ xbps_dictionary_t pkgd, pkgd_alts, repod_alts;
|
||||
|
||||
- allkeys = xbps_dictionary_all_keys(pkgd);
|
||||
+ pkgd = xbps_pkgdb_get_pkg(xhp, pkgname);
|
||||
+ if (xbps_object_type(pkgd) != XBPS_TYPE_DICTIONARY) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ pkgd_alts = xbps_dictionary_get(pkgd, "alternatives");
|
||||
+ repod_alts = xbps_dictionary_get(repod, "alternatives");
|
||||
+
|
||||
+ if (xbps_object_type(pkgd_alts) != XBPS_TYPE_DICTIONARY) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ allkeys = xbps_dictionary_all_keys(pkgd_alts);
|
||||
for (unsigned int i = 0; i < xbps_array_count(allkeys); i++) {
|
||||
xbps_array_t array, array_repo;
|
||||
xbps_object_t keysym;
|
||||
const char *keyname;
|
||||
|
||||
keysym = xbps_array_get(allkeys, i);
|
||||
- array = xbps_dictionary_get_keysym(pkgd, keysym);
|
||||
+ array = xbps_dictionary_get_keysym(pkgd_alts, keysym);
|
||||
keyname = xbps_dictionary_keysym_cstring_nocopy(keysym);
|
||||
|
||||
- array_repo = xbps_dictionary_get(repod, keyname);
|
||||
+ array_repo = xbps_dictionary_get(repod_alts, keyname);
|
||||
if (!xbps_array_equals(array, array_repo)) {
|
||||
remove_symlinks(xhp, array, keyname);
|
||||
}
|
||||
+
|
||||
+ /*
|
||||
+ * There is nothing left in the alternatives group, which means
|
||||
+ * the package is being upgraded and is removing it; if we don't
|
||||
+ * prune it, the system will keep it set after removal of its
|
||||
+ * parent package, but it will be empty and invalid...
|
||||
+ */
|
||||
+ if (xbps_array_count(array_repo) == 0) {
|
||||
+ prune_altgroup(xhp, repod, pkgname, pkgver, keyname);
|
||||
+ }
|
||||
}
|
||||
xbps_object_release(allkeys);
|
||||
}
|
||||
@@ -421,7 +522,7 @@ int
|
||||
xbps_alternatives_register(struct xbps_handle *xhp, xbps_dictionary_t pkg_repod)
|
||||
{
|
||||
xbps_array_t allkeys;
|
||||
- xbps_dictionary_t alternatives, pkg_alternatives, pkgd, pkgd_alts;
|
||||
+ xbps_dictionary_t alternatives, pkg_alternatives;
|
||||
const char *pkgver;
|
||||
char *pkgname;
|
||||
int rv = 0;
|
||||
@@ -431,10 +532,6 @@ xbps_alternatives_register(struct xbps_handle *xhp, xbps_dictionary_t pkg_repod)
|
||||
if (xhp->pkgdb == NULL)
|
||||
return EINVAL;
|
||||
|
||||
- pkg_alternatives = xbps_dictionary_get(pkg_repod, "alternatives");
|
||||
- if (!xbps_dictionary_count(pkg_alternatives))
|
||||
- return 0;
|
||||
-
|
||||
alternatives = xbps_dictionary_get(xhp->pkgdb, "_XBPS_ALTERNATIVES_");
|
||||
if (alternatives == NULL) {
|
||||
alternatives = xbps_dictionary_create();
|
||||
@@ -449,17 +546,15 @@ xbps_alternatives_register(struct xbps_handle *xhp, xbps_dictionary_t pkg_repod)
|
||||
if (pkgname == NULL)
|
||||
return EINVAL;
|
||||
|
||||
- pkgd = xbps_pkgdb_get_pkg(xhp, pkgname);
|
||||
- if (xbps_object_type(pkgd) == XBPS_TYPE_DICTIONARY) {
|
||||
- /*
|
||||
- * Compare alternatives from pkgdb and repo and
|
||||
- * then remove obsolete symlinks.
|
||||
- */
|
||||
- pkgd_alts = xbps_dictionary_get(pkgd, "alternatives");
|
||||
- if (xbps_object_type(pkgd_alts) == XBPS_TYPE_DICTIONARY) {
|
||||
- remove_obsoletes(xhp, pkgd_alts, pkg_alternatives);
|
||||
- }
|
||||
- }
|
||||
+ /*
|
||||
+ * Compare alternatives from pkgdb and repo and then remove obsolete
|
||||
+ * symlinks, also remove obsolete (empty) alternatives groups.
|
||||
+ */
|
||||
+ remove_obsoletes(xhp, pkgname, pkgver, pkg_repod);
|
||||
+
|
||||
+ pkg_alternatives = xbps_dictionary_get(pkg_repod, "alternatives");
|
||||
+ if (!xbps_dictionary_count(pkg_alternatives))
|
||||
+ return 0;
|
||||
|
||||
allkeys = xbps_dictionary_all_keys(pkg_alternatives);
|
||||
for (unsigned int i = 0; i < xbps_array_count(allkeys); i++) {
|
||||
diff --git a/tests/xbps/xbps-alternatives/main_test.sh b/tests/xbps/xbps-alternatives/main_test.sh
|
||||
index fd66bcbf..4eddc411 100644
|
||||
--- tests/xbps/xbps-alternatives/main_test.sh
|
||||
+++ tests/xbps/xbps-alternatives/main_test.sh
|
||||
@@ -674,6 +674,98 @@ respect_current_provider_body() {
|
||||
atf_check_equal $rv 0
|
||||
}
|
||||
|
||||
+atf_test_case prune_leftover_groups
|
||||
+
|
||||
+prune_leftover_groups_head() {
|
||||
+ atf_set "descr" "xbps-alternatives: prune leftover groups on upgrades"
|
||||
+}
|
||||
+prune_leftover_groups_body() {
|
||||
+ mkdir -p repo pkg_A/usr/bin pkg_B/usr/bin
|
||||
+ touch pkg_A/usr/bin/fileA pkg_B/usr/bin/fileB
|
||||
+ cd repo
|
||||
+ xbps-create -A noarch -n A-1.1_1 -s "A pkg" --alternatives "file:/usr/bin/file:/usr/bin/fileA" ../pkg_A
|
||||
+ atf_check_equal $? 0
|
||||
+ xbps-create -A noarch -n B-1.1_1 -s "B pkg" --alternatives "file:/usr/bin/file:/usr/bin/fileB" ../pkg_B
|
||||
+ atf_check_equal $? 0
|
||||
+ xbps-rindex -d -a $PWD/*.xbps
|
||||
+ atf_check_equal $? 0
|
||||
+ cd ..
|
||||
+
|
||||
+ # A is the current provider now
|
||||
+ xbps-install -r root --repository=repo -ydv A
|
||||
+ atf_check_equal $? 0
|
||||
+
|
||||
+ out=$(xbps-query -r root -p pkgver A)
|
||||
+ atf_check_equal $out A-1.1_1
|
||||
+
|
||||
+ # C will replace it via a transitional package
|
||||
+ mkdir -p pkg_C/usr/bin
|
||||
+ touch pkg_C/usr/bin/fileC
|
||||
+ rm pkg_A/usr/bin/fileA
|
||||
+ cd repo
|
||||
+ xbps-create -A noarch -n C-1.2_1 -s "C pkg" --alternatives "file:/usr/bin/file:/usr/bin/fileC" ../pkg_C
|
||||
+ atf_check_equal $? 0
|
||||
+ xbps-create -A noarch -n A-1.2_1 -s "A pkg" --dependencies "C>=1.2_1" ../pkg_A
|
||||
+ atf_check_equal $? 0
|
||||
+ xbps-rindex -d -a $PWD/*.xbps
|
||||
+ atf_check_equal $? 0
|
||||
+ cd ..
|
||||
+
|
||||
+ # C is now the current provider, via upgraded A
|
||||
+ # also install B, to make sure it doesn't get that first
|
||||
+ xbps-install -r root --repository=repo -ydv B A
|
||||
+
|
||||
+ out=$(xbps-query -r root -p pkgver A)
|
||||
+ atf_check_equal $out A-1.2_1
|
||||
+ out=$(xbps-query -r root -p pkgver B)
|
||||
+ atf_check_equal $out B-1.1_1
|
||||
+ out=$(xbps-query -r root -p pkgver C)
|
||||
+ atf_check_equal $out C-1.2_1
|
||||
+
|
||||
+ lnk=$(readlink -f root/usr/bin/file)
|
||||
+ rv=1
|
||||
+ if [ "$lnk" = "$PWD/root/usr/bin/fileC" ]; then
|
||||
+ rv=0
|
||||
+ fi
|
||||
+ echo "lnk: $lnk"
|
||||
+ atf_check_equal $rv 0
|
||||
+
|
||||
+ # Create a new provider, D; then make C a removed package
|
||||
+ mkdir -p pkg_D/usr/bin
|
||||
+ touch pkg_D/usr/bin/fileD
|
||||
+ rm pkg_C/usr/bin/fileC
|
||||
+ cd repo
|
||||
+ xbps-create -A noarch -n D-1.4_1 -s "D pkg" --alternatives "file:/usr/bin/file:/usr/bin/fileD" ../pkg_D
|
||||
+ atf_check_equal $? 0
|
||||
+ xbps-create -A noarch -n C-1.3_1 -s "C pkg" ../pkg_C
|
||||
+ atf_check_equal $? 0
|
||||
+ xbps-rindex -d -a $PWD/*.xbps
|
||||
+ atf_check_equal $? 0
|
||||
+ cd ..
|
||||
+
|
||||
+ # B is now the current provider, as it's the first group available after
|
||||
+ # pruning C; the system special cases packages without dependencies as
|
||||
+ # removed packages, so it will not assume a renamed replacement
|
||||
+ xbps-install -r root --repository=repo -ydv C D
|
||||
+
|
||||
+ out=$(xbps-query -r root -p pkgver A)
|
||||
+ atf_check_equal $out A-1.2_1
|
||||
+ out=$(xbps-query -r root -p pkgver B)
|
||||
+ atf_check_equal $out B-1.1_1
|
||||
+ out=$(xbps-query -r root -p pkgver C)
|
||||
+ atf_check_equal $out C-1.3_1
|
||||
+ out=$(xbps-query -r root -p pkgver D)
|
||||
+ atf_check_equal $out D-1.4_1
|
||||
+
|
||||
+ lnk=$(readlink -f root/usr/bin/file)
|
||||
+ rv=1
|
||||
+ if [ "$lnk" = "$PWD/root/usr/bin/fileB" ]; then
|
||||
+ rv=0
|
||||
+ fi
|
||||
+ echo "lnk: $lnk"
|
||||
+ atf_check_equal $rv 0
|
||||
+}
|
||||
+
|
||||
atf_init_test_cases() {
|
||||
atf_add_test_case register_one
|
||||
atf_add_test_case register_one_dangling
|
||||
@@ -692,4 +784,5 @@ atf_init_test_cases() {
|
||||
atf_add_test_case useless_switch
|
||||
atf_add_test_case remove_current_provider
|
||||
atf_add_test_case respect_current_provider
|
||||
+ atf_add_test_case prune_leftover_groups
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
# Template file for 'xbps'
|
||||
pkgname=xbps
|
||||
version=0.57.1
|
||||
revision=2
|
||||
revision=3
|
||||
bootstrap=yes
|
||||
build_style=configure
|
||||
short_desc="XBPS package system utilities"
|
||||
|
|
Loading…
Reference in a new issue