Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 916135 - emerge --depclean: wrong order of cleaning with circular dependencies and IDEPEND (which breaks pkg_* phases)
Summary: emerge --depclean: wrong order of cleaning with circular dependencies and IDE...
Status: RESOLVED FIXED
Alias: None
Product: Portage Development
Classification: Unclassified
Component: Core - Dependencies (show other bugs)
Hardware: All Linux
: Normal normal (vote)
Assignee: Portage team
URL:
Whiteboard:
Keywords: InVCS
Depends on: 919862
Blocks: 155723
  Show dependency tree
 
Reported: 2023-10-22 19:03 UTC by Andrei Horodniceanu
Modified: 2023-12-27 21:28 UTC (History)
2 users (show)

See Also:
Package list:
Runtime testing required: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Andrei Horodniceanu 2023-10-22 19:03:58 UTC
I have seen this bug happen in the dlang overlay with the dev-lang/ldc2 package. Since it is slotted, it IDEPENDs on app-eselect/eselect-dlang to provide symlinks. Since it is written in D, it requires a D compiler to both compile and provide a runtime so. This may lead to ldc2 RDEPENDing on ldc2 (possibly the same version) and IDEPEND on eselect-dlang.

The bug arises when trying to depclean ldc2 along with eselect-dlang. The order should be:
1. dev-lang/ldc2
2. app-eselect/eselect-dlang
yet emerge chooses the reverse:
1. app-eselect/eselect-dlang
2. dev-lang/ldc2

This leads to ldc2 not being able to call the eselect dlang module which leads to broken symlinks.

An example of such a situation:
-------------
$ emerge -c ldc2 eselect-dlang

Calculating dependencies... done!
>>> Calculating removal order...

 app-eselect/eselect-dlang
    selected: 20190608
   protected: none
     omitted: none

 dev-lang/ldc2
    selected: 1.35.0
   protected: none
     omitted: none

All selected packages: =app-eselect/eselect-dlang-20190608 =dev-lang/ldc2-1.35.0

>>> 'Selected' packages are slated for removal.
>>> 'Protected' and 'omitted' packages will not be removed.

>>> Waiting 5 seconds before starting...
>>> (Control-C to abort)...
>>> Unmerging in: 5 4 3 2 1
>>> Unmerging (1 of 2) app-eselect/eselect-dlang-20190608...
>>> Unmerging (2 of 2) dev-lang/ldc2-1.35.0...
!!! Error: Can't load module dlang
Packages installed:   386
Packages in world:    30
Packages in system:   50
Required packages:    386
Number removed:       2

 * GNU info directory index is up-to-date.
-------------

The dependencies of the 2 packages in the above case:
-------------
$ qdepends -v ldc2 eselect-dlang
dev-lang/ldc2-1.35.0:
DEPEND="
    || (
        sys-devel/llvm:16
        sys-devel/llvm:15
        sys-devel/llvm:14
    )
    <sys-devel/llvm-17:16/16=
    !!sys-devel/llvm:0
    dev-lang/ldc2:1.35/0=
"
RDEPEND="
    || (
        sys-devel/llvm:16
        sys-devel/llvm:15
        sys-devel/llvm:14
    )
    <sys-devel/llvm-17:16/16=
    >=app-eselect/eselect-dlang-20140709
    dev-lang/ldc2:1.35/0=
"
BDEPEND="
    dev-util/ninja
    >=dev-util/ninja-1.8.2
    >=dev-util/cmake-3.20.5
"
IDEPEND="
    >=app-eselect/eselect-dlang-20140709
"
app-eselect/eselect-dlang-20190608:
RDEPEND="
    app-admin/eselect
"
-------------

As a small note, versions of ldc2 before 1.35.0 are affected in the same way, though they specify app-eselect/eselect-dlang as a RDEPEND due to EAPI=7.

The above test can be simplified to (as a python portage unittest):
-------------
    def testIDEPENDDepclean(self):
        ebuilds = {
            "dev-util/A-1": {},
            "dev-libs/B-1": {
                "EAPI": "8",
                "IDEPEND": "dev-util/A",
                "RDEPEND": "dev-libs/B:=",
            },
            "dev-libs/C-1": {},
        }

        installed = {
            "dev-util/A-1": {},
            "dev-libs/B-1": {
                "EAPI": "8",
                "IDEPEND": "dev-util/A",
                "RDEPEND": "dev-libs/B:0/0=",
            },
            "dev-libs/C-1": {},
        }

        world = ('dev-libs/C',)

        test_cases = (
            # Remove dev-libs/B first because it IDEPENDs on dev-util/A
            ResolverPlaygroundTestCase(
                [],
                options={"--depclean": True},
                success=True,
                ordered=True,
                cleanlist=[
                    "dev-libs/B-1",
                    "dev-util/A-1",
                ],
            ),
        )

        playground = ResolverPlayground(
            ebuilds=ebuilds, installed=installed, world=world
        )
        try:
            for test_case in test_cases:
                playground.run_TestCase(test_case)
                self.assertEqual(test_case.test_success, True, test_case.fail_msg)
        finally:
            playground.cleanup()

-------------

Interestingly, removing the := operator leads to the test sometimes failing and sometimes passing, i.e. the remove order becomes inconsistent.
Comment 1 Zac Medico gentoo-dev 2023-12-24 16:26:32 UTC
In https://github.com/gentoo/portage/pull/1212 I've handled direct circular dependencies, but it is possible that indirect circular dependencies may still trigger problems with the unmerge order (for example if there was a virtual/ldc2 which made this an indirect circular dependency).
Comment 2 Larry the Git Cow gentoo-dev 2023-12-24 19:30:19 UTC
The bug has been referenced in the following commit(s):

https://gitweb.gentoo.org/proj/portage.git/commit/?id=bf3d091de8702f0c95e5530d03c6e925008ee80a

commit bf3d091de8702f0c95e5530d03c6e925008ee80a
Author:     Zac Medico <zmedico@gentoo.org>
AuthorDate: 2023-12-24 05:12:55 +0000
Commit:     Zac Medico <zmedico@gentoo.org>
CommitDate: 2023-12-24 19:29:25 +0000

    depclean: drop direct circular deps in merge order calculation
    
    Drop direct circular deps in the depclean merge order calculation,
    since it does not handle them well as shown by the test case
    for bug 916135.
    
    Bug: https://bugs.gentoo.org/916135
    Signed-off-by: Zac Medico <zmedico@gentoo.org>

 lib/_emerge/actions.py                            |  8 ++++++--
 lib/portage/tests/resolver/test_depclean_order.py | 10 ++++++----
 2 files changed, 12 insertions(+), 6 deletions(-)

https://gitweb.gentoo.org/proj/portage.git/commit/?id=eeb4c29a64927efbaa7028153230367651bcf3b7

commit eeb4c29a64927efbaa7028153230367651bcf3b7
Author:     Andrei Horodniceanu <a.horodniceanu@proton.me>
AuthorDate: 2023-10-21 14:38:36 +0000
Commit:     Zac Medico <zmedico@gentoo.org>
CommitDate: 2023-12-24 19:29:16 +0000

    tests/resolver: test depclean order with IDEPEND and circular deps
    
    Bug: https://bugs.gentoo.org/916135
    Signed-off-by: Andrei Horodniceanu <a.horodniceanu@proton.me>
    Closes: https://github.com/gentoo/portage/pull/1147
    Signed-off-by: Zac Medico <zmedico@gentoo.org>

 lib/portage/tests/resolver/test_depclean_order.py | 50 +++++++++++++++++++++++
 1 file changed, 50 insertions(+)
Comment 3 Andrei Horodniceanu 2023-12-24 21:11:48 UTC
(In reply to Zac Medico from comment #1)
> In https://github.com/gentoo/portage/pull/1212 I've handled direct circular
> dependencies, but it is possible that indirect circular dependencies may
> still trigger problems with the unmerge order (for example if there was a
> virtual/ldc2 which made this an indirect circular dependency).

You can get indirect circular dependencies by having 2 ldc2 versions build each other, for example ldc2:1.35 was built with ldc2:1.34 and ldc2:1.34 was built with ldc2:1.35. As you guessed, the unmerge order is the wrong one because of the 1-layer indirection.
Comment 4 Zac Medico gentoo-dev 2023-12-25 00:43:01 UTC
To handle indirect circular dependencies, we can increase the priority of IDEPEND relative to RDEPEND. Here's a test case that shows the incorrect order:

    def testCircularDepclean(self):
        """
        Test for bug 916135, where an indirect circular dependency caused
        the unmerge order to fail to account for IDEPEND.
        """

        ebuilds = {
            "dev-util/A-1": {},
            "dev-libs/B-1": {
                "EAPI": "8",
                "SLOT": "1",
                "IDEPEND": "dev-util/A",
                "RDEPEND": "dev-libs/B:=",
            },
            "dev-libs/B-2": {
                "EAPI": "8",
                "SLOT": "2",
                "IDEPEND": "dev-util/A",
                "RDEPEND": "dev-libs/B:=",
            },
            "dev-libs/C-1": {},
        }

        installed = {
            "dev-util/A-1": {},
            "dev-libs/B-1": {
                "EAPI": "8",
                "SLOT": "1",
                "IDEPEND": "dev-util/A",
                "RDEPEND": "dev-libs/B:2/2=",
            },
            "dev-libs/B-2": {
                "EAPI": "8",
                "SLOT": "2",
                "IDEPEND": "dev-util/A",
                "RDEPEND": "dev-libs/B:1/1=",
            },
            "dev-libs/C-1": {},
        }

        world = ("dev-libs/C",)

        test_cases = (
            # Remove dev-libs/B first because it IDEPENDs on dev-util/A
            ResolverPlaygroundTestCase(
                [],
                options={"--depclean": True},
                success=True,
                ordered=True,
                cleanlist=["dev-util/A-1", "dev-libs/B-1", "dev-libs/B-2"],
            ),
        )

        playground = ResolverPlayground(
            ebuilds=ebuilds, installed=installed, world=world
        )
        try:
            for test_case in test_cases:
                playground.run_TestCase(test_case)
                self.assertEqual(test_case.test_success, True, test_case.fail_msg)
        finally:
            playground.cleanup()
Comment 5 Larry the Git Cow gentoo-dev 2023-12-26 21:05:44 UTC
The bug has been referenced in the following commit(s):

https://gitweb.gentoo.org/proj/portage.git/commit/?id=64b16b76611e14ff0b38b762486f073039f21a05

commit 64b16b76611e14ff0b38b762486f073039f21a05
Author:     Zac Medico <zmedico@gentoo.org>
AuthorDate: 2023-12-25 02:53:57 +0000
Commit:     Zac Medico <zmedico@gentoo.org>
CommitDate: 2023-12-26 21:04:25 +0000

    depclean: Strengthen IDEPEND in unmerge order
    
    Increase priority of IDEPEND so that it is stronger
    than RDEPEND in unmerge order calculations. This
    causes IDEPEND to be unmerged afterwards when
    packages are involved in RDEPEND cycles.
    
    Bug: https://bugs.gentoo.org/916135
    Signed-off-by: Zac Medico <zmedico@gentoo.org>

 lib/_emerge/AbstractDepPriority.py                |  3 +-
 lib/_emerge/UnmergeDepPriority.py                 | 35 +++++++------
 lib/_emerge/actions.py                            |  5 +-
 lib/_emerge/depgraph.py                           |  4 +-
 lib/portage/tests/resolver/test_depclean_order.py | 63 +++++++++++++++++++++++
 5 files changed, 92 insertions(+), 18 deletions(-)
Comment 6 Larry the Git Cow gentoo-dev 2023-12-27 21:28:45 UTC
The bug has been closed via the following commit(s):

https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=0a1f19cdd7a598070b7eb08b3954e677aa4868ad

commit 0a1f19cdd7a598070b7eb08b3954e677aa4868ad
Author:     Sam James <sam@gentoo.org>
AuthorDate: 2023-12-27 21:27:55 +0000
Commit:     Sam James <sam@gentoo.org>
CommitDate: 2023-12-27 21:28:01 +0000

    sys-apps/portage: add 3.0.59
    
    Closes: https://bugs.gentoo.org/587088
    Closes: https://bugs.gentoo.org/822033
    Closes: https://bugs.gentoo.org/915494
    Closes: https://bugs.gentoo.org/916135
    Closes: https://bugs.gentoo.org/917120
    Closes: https://bugs.gentoo.org/919862
    Closes: https://bugs.gentoo.org/920095
    Closes: https://bugs.gentoo.org/920258
    Closes: https://bugs.gentoo.org/920537
    Closes: https://bugs.gentoo.org/920654
    Signed-off-by: Sam James <sam@gentoo.org>

 sys-apps/portage/Manifest              |   1 +
 sys-apps/portage/portage-3.0.59.ebuild | 246 +++++++++++++++++++++++++++++++++
 2 files changed, 247 insertions(+)