There are at least two issues with || ( ) dependencies in *DEPEND. Firstly, developers commonly think it just means "or", and forget the "prefer leftmost" issue. Secondly, it's ambiguous in the same way that specs matching multiple slots are ambiguous: the package mangler doesn't know whether it's safe to swap || deps at runtime. We could improve the situation by replacing || in *DEPEND with a choice of <<* for "prefer the leftmost, and you can switch it at runtime" and <<= for "prefer the leftmost, and lock it when it's installed". An added bonus of this notation is that it's horribly ugly, just like || dependencies.
Maybe we should start with documenting "prefer leftmost" in PMS. Not sure what changing || to << would buy us. That || is evaluated from left to right (and processing stops after the first match) is quite common elsewhere, e.g. in bash or C. Also << would look strange in LICENSE or REQUIRED_USE. How about just allowing an optional * or = after the || operator?
Well, || ( <a-2 >a-3 ) comes up pretty regularly...
Heh, things could be much worse. <any-of prefer="leftmost" runtime="will-break-if-replaced"> <package match-type="greater-than">a-3</package> <package match-type="less-than">a-2</package> </any-of>
No Zynot jokes!
This issue just came up again in bug 493652. So, how about this syntax: ||* ( ) ||= ( ) Questions: - Should we allow || ( ) in addition, and (if yes) what should it default to? - How should ||* ( cat/foo:= cat/bar:= ) be interpreted (mgorny brought this up on IRC)? What about nested ||= and ||* of different type?
Another question: - ||* and ||= are meaningless for build-time dependencies. Should they be just synonyms for || there? Or should we disallow these operators in DEPEND? In the latter case, DEPEND="${RDEPEND}" would not be possible any more.
(In reply to Ulrich Müller from comment #5) > Questions: > - Should we allow || ( ) in addition, and (if yes) what should it default to? > - How should ||* ( cat/foo:= cat/bar:= ) be interpreted (mgorny brought this > up on IRC)? What about nested ||= and ||* of different type? Any ideas? If this shall make it into the next EAPI, these questions need to be answered.
Nested || should be banned everywhere, at least...
(In reply to Ulrich Müller from comment #5) > This issue just came up again in bug 493652. So, how about this syntax: > ||* ( ) > ||= ( ) > > Questions: > - Should we allow || ( ) in addition, and (if yes) what should it default to? Not allowing it would at least force people to make a decision (unlike in the slot operator case). > - How should ||* ( cat/foo:= cat/bar:= ) be interpreted (mgorny brought this > up on IRC)? This problem already exists and is orthogonal to the matters discussed here. > What about nested ||= and ||* of different type? How exactly behave nested ||s of the same type when written to the vdb? It's clear for ||* - just leave it as is. But what about this: "||= ( ||= ( A B ) ||= ( C D ) )" If D is installed but A, B, C aren't then I'd assume this gets rewritten to "D". But what if A and D are installed? Does it get rewritten to "A D" or does the PM choose either "A" or "D"? (In reply to Ulrich Müller from comment #6) > Another question: > - ||* and ||= are meaningless for build-time dependencies. Should they be > just > synonyms for || there? Or should we disallow these operators in DEPEND? > In the latter case, DEPEND="${RDEPEND}" would not be possible any more. Slot operators are allowed in DEPEND too. I think it would be too annoying to not allow them for the reason you gave.
One more question: || ( A B C ) does the 'prefer leftmost' rule apply to the behavior of package itself? That is, if multiple dependencies are installed, is the package required to use the left-most of them? This is important for the package manager to properly handle upgrades. Right now, || sounds pretty much like undefined behavior to me. That is, package manager seeing all of ( A, B, C ) installed would have to upgrade all of them, in case the package used another one than package manager expected. Using a new operator could clarify that as well.
Right now, anything using || has to work with any of its choices (even if some of the choices are broken, due to missing deps, so long as at least one of its choices is correct), and this has to be switchable at runtime.
Let me ask a different question: would there be use for ||= ( A B ) without := on A & B? Otherwise, we could achieve a similar effect via extrapolating the current rules for := and || (). Plain: || ( A B ) would mean runtime-switchable, while: || ( A:= B:= ) would require rebuild via subslots. This is already true if A & B are mutually exclusive. Since PMS says that 'the package will break unless a matching package with slot and sub-slot equal to the slot and sub-slot of the best installed version at the time the package was installed is available', removing the relevant package will need to trigger a rebuild. Not sure how to properly handle non-mutually exclusive variants. Not sure if we need any.
(In reply to Michał Górny from comment #12) > Let me ask a different question: would there be use for ||= ( A B ) without > := on A & B? Why not? There could be a case similar to mit-krb5 and heimdal where providers have different APIs, but runtime ABI switching within one provider could still be possible. > Otherwise, we could achieve a similar effect via extrapolating the current > rules for := and || (). > > Plain: > > || ( A B ) > > would mean runtime-switchable, How would you distinguish between ||= ( A B ) and ||* ( A B ) without separate operators? > while: > > || ( A:= B:= ) > > would require rebuild via subslots. I believe that ||= ( A:= B:= ) expresses the intention much more clearly. And ||* ( A:= B:= ) should be banned. Otherwise, you could switch the provider from A:1 to B and then from B to A:2 which would render the := meaningless.
So let's keep this simple. For PMS: - we add ||= () with 'prefer leftmost' and build-time switching. For policy/common sense: 1. || () still works like usual, that is run-time switching. Preferences do not really matter here since package can use any of the installed deps anyway. 1a. Corner case: || ( A[foo] B ) -- what if A[-foo] and B is installed? Some packages may actually try to use A and fail due to unsatisfied USE. But it's no new issue and I doubt it's worth special handling. Extra '!A[-foo]' could be used to solve it. 2. Alike :=, ||= can be used in DEPEND (works as '||' there). 2a. ||= in RDEPEND makes sense only if there's corresponding || or ||= in DEPEND -- the package needs to select provider build-time. I'd dare say requiring people to write RDEP='||= ( A B )' DEP='|| ( A B )' is... bad :). 2b. ||= (and :=) in PDEPEND doesn't make sense -- we use that when we can't install something build-time. 3. := atoms make sense only in ||=. We likely want to ban them in || in the EAPIs with ||=. In earlier EAPIs, those combinations will be left undefined. 4. I don't think we need to set any special rules for nesting. 'It should work', I'd dare say, but since it's hard to implement the real use will follow implementation: 4a. || ( || ( A B ) C ) Direct nesting doesn't really make sense :). 4b. ||= ( ( libX11 ||= ( ffmpeg:= libav:= ) ) ( emul-linux-x86-medialibs ) ) This one may actually be meaningful. Not sure how hard to implement it would be. 5. I don't think we need a specific ||*. || is already well-defined in PMS, and :* already proved being rarely used.
The more I think about it, the less I like the idea of ||= with packages that do not mutually block one another. For example: ||= ( A B ) means that you can't properly switch to B without unmerging A first, and therefore breaking the package.
(In reply to Michał Górny from comment #15) > The more I think about it, the less I like the idea of ||= with packages > that do not mutually block one another. > > For example: > > ||= ( A B ) > > means that you can't properly switch to B without unmerging A first, and > therefore breaking the package. How is this different from := in SLOT dependencies?
Well, I think we simply don't support downgrades :). In case of ||=, it'd rather more inconvenient not to support switching to a 'less preferred' provider.
(In reply to Michał Górny from comment #15) > The more I think about it, the less I like the idea of ||= with packages > that do not mutually block one another. > > For example: > > ||= ( A B ) > > means that you can't properly switch to B without unmerging A first, and > therefore breaking the package. Let me clarify that since it seems that I wasn't clear on what the problem is. The problem is that if you install both A and B, and then want to switch to B, you'd have to do: emerge -C A but so far the --unmerge/--depclean actions in emerge did not build anything, just remove packages. This means that either we would have to strongly change behavior, or the rebuild won't happen until next regular emerge call.
(In reply to Michał Górny from comment #18) Conceptionally that doesn't seem to be so much different from a slot operator dependency. For example, the package depends on foo:= and there is foo:2 installed when it is emerged. What happens if the user does "emerge foo:1" followed by "emerge -C foo:2"? Does portage rebuild the reverse dependency with the second command?
(Related ML post: https://archives.gentoo.org/gentoo-dev/message/6977c4c839e3ff3356d159cbe0c173cd).
Dropped from EAPI 8 in today's Council meeting.
The bug has been referenced in the following commit(s): https://gitweb.gentoo.org/proj/portage.git/commit/?id=ee127db438307c133fcf650c148ed594ceb68591 commit ee127db438307c133fcf650c148ed594ceb68591 Author: Sam James <sam@gentoo.org> AuthorDate: 2023-09-04 17:40:06 +0000 Commit: Sam James <sam@gentoo.org> CommitDate: 2023-12-10 22:01:48 +0000 dep: add comment to _eval_deps wrt binding slot deps in any-of || ( ... ) Bug: https://bugs.gentoo.org/455904 Bug: https://bugs.gentoo.org/489458 Bug: https://bugs.gentoo.org/586238 Signed-off-by: Sam James <sam@gentoo.org> lib/portage/dep/_slot_operator.py | 1 + 1 file changed, 1 insertion(+)