[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Dependency merging in APT



Hi all,

yesterday I enabled a new feature in APT's new solver, where
it takes overlapping dependencies from a package and merges
them to their intersection.

In particular this addresses the problem with version ranges and
versioned provides such as:

gnupg Depends: gpg (>= 2.4.7-19) overlaps Depends: gpg (<< 2.4.7-19.1~)
 -  solutions: ['gpg=2.4.7-19']
 -  eliminated: [] ['gpg-from-sq=0.13.1-3']

Where gpg-from-sq would be considered a valid solution for the
<< depends as it provides 2.2.something, but not the greater
depends and apt may decide to upgrade gnupg by installing gpg-from-sq.


Some bits just eliminate funny redundancies such as:

dpkg-dev Recommends: sqv | sqopv | rsopv | sopv | gosop | pgpainless-cli | gpgv-sq | gpgv overlaps Recommends: sq | sqop | rsop | gosop | pgpainless-cli | gpg-sq | gnupg
 -  solutions: ['sqop=0.37.1-1+b1', 'rsop=0.6.4-1', 'gosop=1.1.0-3', 'gosop=1.1.0-3', 'pgpainless-cli=1.6.9-1']
 -  eliminated: ['sqv=1.3.0-2', 'sqopv=0.37.1-1+b1', 'rsopv=0.6.4-1', 'sopv-gpgv=0.1.4-1', 'sqopv=0.37.1-1+b1', 'rsopv=0.6.4-1', 'gpgv-sq=0.13.1-3+b1', 'gpgv=2.4.7-19', 'gpgv-from-sq=0.13.1-3'] ['sq=1.3.1-2+b1', 'gpg-sq=0.13.1-3+b1', 'gnupg=2.4.7-19']

Or modules merged into greater packages such as perl:

razor Depends: libtest-simple-perl overlaps Depends: libmime-base64-perl
 -  solutions: ['perl=5.40.1-3']
 -  eliminated: ['libtest-simple-perl=1.302210-1'] []

A complete list of mergeable dependencies is attached.

Please note that we also merge Depends into Recommends, such as

ddclient Recommends: libdigest-sha-perl overlaps Depends: perl:any
 -  solutions: ['perl=5.40.1-3']
 -  eliminated: ['libdigest-sha-perl=6.04-1+b3'] []

That is, the Recommends is restricted to the bits that intersect
with the overlapping dependency, here the

    Recommends: libdigest-sha-perl

effectively becomes

    Recommends: perl

There may be some unexpected issues that need some refinement.
-- 
debian developer - deb.li/jak | jak-linux.org - free software dev
ubuntu core developer                              i speak de, en
import apt_pkg

apt_pkg.init()


def render(group: list[apt_pkg.Dependency]) -> str:
    def render1(dep: apt_pkg.Dependency) -> str:
        if dep.comp_type_deb:
            return f"{dep.target_pkg.name} ({dep.comp_type_deb} {dep.target_ver})"
        return dep.target_pkg.name

    return f"{group[0].dep_type_untranslated}: " + " | ".join(
        render1(dep) for dep in group
    )


def check_overlap(v: apt_pkg.Version) -> None:
    deps: list[tuple[list[apt_pkg.Version], list[apt_pkg.Dependency]]] = []
    for kind_name, kind in v.depends_list.items():
        if kind_name in ["Conflicts", "Breaks", "Replaces", "Suggests", "Enhances"]:
            continue
        for group in kind:
            targets: list[apt_pkg.Version] = []
            for dep in group:
                targets.extend(dep.all_targets())
            deps.append((targets, group))

    for i in range(len(deps)):
        for j in range(i):
            lhs = set(v.id for v in deps[i][0])
            rhs = set(v.id for v in deps[j][0])
            overlap = lhs & rhs
            if overlap and lhs != rhs:
                print(
                    v.parent_pkg.name,
                    render(deps[i][1]),
                    "overlaps",
                    render(deps[j][1]),
                )
                print(
                    " -  solutions:",
                    [
                        v.parent_pkg.name + "=" + v.ver_str
                        for v in deps[i][0]
                        if v.id in overlap
                    ],
                )
                print(
                    " -  eliminated:",
                    [
                        v.parent_pkg.name + "=" + v.ver_str
                        for v in deps[i][0]
                        if v.id not in overlap
                    ],
                    [
                        v.parent_pkg.name + "=" + v.ver_str
                        for v in deps[j][0]
                        if v.id not in overlap
                    ],
                )
                print()


cache = apt_pkg.Cache(None)

for pkg in cache.packages:
    if pkg.version_list:
        check_overlap(pkg.version_list[0])

Attachment: dependency-merges.unstable.txt.gz
Description: application/gzip


Reply to: