EAPI 4 introduced REPLACING_VERSIONS. Since then, many packages made good use of it to detect if a package replacing a specific version of itself. At the time of writing this (2025-01-05), 694 ebuilds use REPLACING_VERSIONS. Often to emit a helpful message with information about behavior changes in the new version or upgrade-related considerations directed at the user. A typical idiom is pkg_postinst() { if [[ ${REPLACING_VERSIONS} ]] && ver_test ${REPLACING_VERSIONS} -lt 1.2; then elog "Starting with version 1.2, this package will no longer support foo" fi } This idiom works for unslotted packages, but it would break for slotted packages because their REPLACING_VERSIONS may contain multiple versions. For slotted packages, the correct idiom would be pkg_postinst() { local v for v in {REPLACING_VERSIONS}; do if ver_test ${v} -lt 1.2; then elog "Starting with version 1.2, this package will no longer support foo" break fi done } It could be argued that this idiom should be used everywhere, as it is, unlike the previous idiom, correct in any case (slotted and not slotted) and hence avoids potential copy-and-paste mistakes. However, this idiom is very verbose and noisy. Given that REPLACING_VERSIONS dependent behavior is very beneficial -- it is likely that even more packages could and should make use of it -- a dedicated PMS helper function is desirable for readability. The following proposes ver_replace(), in addition to ver_test and ver_cut introduced by EAPI 7, which would reduce the code above to pkg_postinst() { if ver_replace -lt 1.2; then elog "Starting with version 1.2, this package will no longer support foo" fi } A possible implementation of ver_replace() is the following: ver_replace() { debug-print-function ${FUNCNAME} "${@}" [[ $# -eq 2 ]] || die "${FUNCNAME}: bad number of arguments: need 2, got $#" case ${EBUILD_PHASE_FUNC} in pkg_preinst|pkg_postinst|pkg_pretend|pkg_setup) ;; *) die "${FUNCNAME}: can not be called in ${EBUILD_PHASE_FUNC}" esac local op="${1}" local ver="${2}" local r_ver for r_ver in ${REPLACING_VERSIONS}; do if ver_test ${r_ver} ${op} ${ver}; then return 0 fi done return 1 } The reader may wonder about a similar PMS helper function for REPLACED_BY_VERSIONS, also introduced in EAPI 4. However, since this variable is at the time of writing this not used in ::gentoo, the need for such a ver_replaced_by() function has not manifested yet. It could be added at any time later if the need arises.
(In reply to Florian Schmaus from comment #0) > [...] A typical idiom is > > pkg_postinst() { > if [[ ${REPLACING_VERSIONS} ]] && ver_test ${REPLACING_VERSIONS} -lt > 1.2; then > elog "Starting with version 1.2, this package will no longer support > foo" > fi > } > > This idiom works for unslotted packages, but it would break for slotted > packages because their REPLACING_VERSIONS may contain multiple versions. I hope this isn't "a typical idiom" because it is not correct and should never be used. Even for an unslotted package there can be situations where REPLACING_VERSIONS has multiple elements: https://public-inbox.gentoo.org/gentoo-dev/20160722145736.30fa087b@snowflex/ "Slots are not the only way in which you can end up with multiple installed versions of the same package. Another way is if there's a fatal error during certain parts of the upgrade process." We even have bug 589444 as a tracker bug for such incorrect use.
"Replace" is an active verb. So "ver_replace" sounds like it is meant to replace some version with something else. Also, I have serious worry that people will be confused over whether they intended conjunction or disjunction of matches.
Maybe "ver_replacing_any" would be a better name? I also had thought about the conjuction/disjunction problem, and whether we would need two flavours of the command, like "ver_replacing_any" and "ver_replacing_all". Then again, I believe the *_any variant would be used much more often, and AFAICS "ver_replacing_all -lt 1.2" would be exactly equivalent to "! ver_replacing_any -ge 1.2" by De Morgan's law.
Using ver_verplacing, instead of ver_replace is sensible. Thanks for the suggestion. That said, the vast majority of cases where an ebuild considers the content of REPLACING_VERSION queries if at least one version matches a given predicate (usually ver_test -lt). Therefore, I do not share the fear that people will mistakenly assume that ver_replacing requires all replacing versions to match the predicate versus at least one. However, if there is a strong opinion that the function needs to be named ver_replacing_any for clarity, we could go with that. But in my opinion, using the shorter name ver_replacing is preferable. Especially since I could not identify a single case where REPLACING_VERSION was used to require all replacement versions to match the predicate, it seems unlikely we will require a second function, like ver_replacing_all. Even if we find a need for ver_replacing_all, having one function named ver_replacing, which ends up being the dominant used one, and another one named ver_verplacig_all does not sound too bad from an ebuild API design perspective.
Created attachment 917131 [details, diff] Specification of ver_replacing I think the specification would look like this. This is in no way meant to imply that the feature should be part of EAPI 9. (I had to use some EAPI for the tables.)
Accepted for EAPI 9: https://projects.gentoo.org/council/meeting-logs/20250209.txt
The bug has been referenced in the following commit(s): https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=65cb33ec019e2ce8980f546a137ba27c55042c55 commit 65cb33ec019e2ce8980f546a137ba27c55042c55 Author: Ulrich Müller <ulm@gentoo.org> AuthorDate: 2025-02-10 19:05:59 +0000 Commit: Ulrich Müller <ulm@gentoo.org> CommitDate: 2025-02-13 17:17:58 +0000 eapi9-ver.eclass: New eclass This implements the ver_replacing command, as proposed for EAPI 9: | Takes an operator and a version string as arguments, which follow | the same specification as in ver_test. Iterates over the elements | of REPLACING_VERSIONS, using ver_test to compare each element with | the version string. Returns shell true (0) if the specified relation | is fulfilled for any element. Note that if REPLACING_VERSIONS is | empty, shell false (1) is returned. The command is only meaningful | in phases where \t{REPLACING_VERSIONS} is defined. Bug: https://bugs.gentoo.org/947530 Signed-off-by: Ulrich Müller <ulm@gentoo.org> eclass/eapi9-ver.eclass | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+)