Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!

Bug 947530

Summary: [Future EAPI] Add ver_replacing PMS helper
Product: Gentoo Hosted Projects Reporter: Florian Schmaus <flow>
Component: PMS/EAPIAssignee: Package Manager Specification <pms>
Status: IN_PROGRESS ---    
Severity: normal CC: esigra, flow, mgorny
Priority: Normal    
Version: unspecified   
Hardware: All   
OS: Linux   
See Also: https://bugs.gentoo.org/show_bug.cgi?id=948641
Whiteboard: in-eapi-9
Package list:
Runtime testing required: ---
Bug Depends on:    
Bug Blocks: 174380    
Attachments: Specification of ver_replacing

Description Florian Schmaus gentoo-dev 2025-01-05 10:34:30 UTC
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.
Comment 1 Ulrich Müller gentoo-dev 2025-01-05 12:57:59 UTC
(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.
Comment 2 Michał Górny archtester Gentoo Infrastructure gentoo-dev Security 2025-01-05 13:23:28 UTC
"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.
Comment 3 Ulrich Müller gentoo-dev 2025-01-05 15:10:04 UTC
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.
Comment 4 Florian Schmaus gentoo-dev 2025-01-19 11:41:18 UTC
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.
Comment 5 Ulrich Müller gentoo-dev 2025-01-20 13:28:50 UTC
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.)
Comment 6 Ulrich Müller gentoo-dev 2025-02-09 20:28:11 UTC
Accepted for EAPI 9: https://projects.gentoo.org/council/meeting-logs/20250209.txt
Comment 7 Larry the Git Cow gentoo-dev 2025-02-13 17:18:06 UTC
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(+)