aboutsummaryrefslogtreecommitdiff
blob: 1dc958b5052ab41d03b4a1cd056d89a7b5cf5d66 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/env python
# Rewrite of genrdeps-index to stop using horrible Portage API.
# (c) 2020 Michał Górny
# 2-clause BSD license

import argparse
import collections
import errno
import os
import os.path
import shutil
import subprocess
import sys
import tempfile

import pkgcore.config
from pkgcore.ebuild.atom import atom
from pkgcore.restrictions.boolean import AndRestriction, OrRestriction
from pkgcore.restrictions.packages import Conditional


DepTuple = collections.namedtuple('DepTuple', ('cpv', 'blocks', 'use'))


GROUPS = (
    ('bdepend', 'bindex'),
    ('depend', 'dindex'),
    ('pdepend', 'pindex'),
    ('rdepend', 'rindex'),
)


def process_deps(deps, useflags=frozenset()):
    for d in deps:
        if isinstance(d, atom):
            yield DepTuple(d.key, d.blocks, useflags)
        elif isinstance(d, OrRestriction) or isinstance(d, AndRestriction):
            # || deps and nested () blocks
            for sd in process_deps(d, useflags):
                yield sd
        elif isinstance(d, Conditional):
            # foo? deps
            assert d.attr == 'use'
            assert len(d.restriction.vals) == 1
            r = next(iter(d.restriction.vals))
            if d.restriction.negate:
                r = '!' + r
            for sd in process_deps(d, useflags | frozenset((r,))):
                yield sd
        else:
            raise AssertionError("Unknown dep type: " + d.__class__)


def rmtree_ignore_enoent(func, path, exc_info):
    if not isinstance(exc_info[1], FileNotFoundError):
        raise


def main():
    argp = argparse.ArgumentParser()
    argp.add_argument('outputdir',
                      help='Directory to create rdep index in')
    args = argp.parse_args()

    c = pkgcore.config.load_config()
    repo = c.repo['gentoo']

    rindex = {}
    for g, gi in GROUPS:
        rindex[g] = collections.defaultdict(set)

    for p in repo:
        for g, gi in GROUPS:
            deps = frozenset(process_deps(getattr(p, g)))
            for dep, blocks, flags in deps:
                rindex[g][dep].add(DepTuple(p.cpvstr, blocks, flags))

    for g, gi in GROUPS:
        outdir = os.path.join(args.outputdir, '.' + gi + '.new')
        shutil.rmtree(outdir, onerror=rmtree_ignore_enoent)

        for p, revdeps in rindex[g].items():
            outpath = os.path.join(outdir, p)
            os.makedirs(os.path.dirname(outpath), exist_ok=True)
            with open(outpath, 'w') as f:
                for dep, blocks, flags in sorted(revdeps):
                    if blocks:
                        dep = '[B]' + dep
                    if flags:
                        dep += ':' + '+'.join(sorted(flags))
                    f.write(dep + '\n')

    for g, gi in GROUPS:
        outdir = os.path.join(args.outputdir, gi)
        olddir = os.path.join(args.outputdir, '.' + gi + '.old')
        newdir = os.path.join(args.outputdir, '.' + gi + '.new')

        shutil.rmtree(olddir, onerror=rmtree_ignore_enoent)
        try:
            os.rename(outdir, olddir)
        except FileNotFoundError as e:
            pass
        os.rename(newdir, outdir)
        shutil.rmtree(olddir, onerror=rmtree_ignore_enoent)

    with tempfile.NamedTemporaryFile(prefix='.tmp.rdeps-', suffix='.tar', dir=args.outputdir, delete=False) as tmpf:
        try:
            subprocess.check_call(
                ['tar', '-cf', tmpf.name] + [gi for g, gi in GROUPS],
                cwd=args.outputdir)
            subprocess.check_call(
                ['xz', '-9', tmpf.name],
                cwd=args.outputdir)
            os.rename(tmpf.name + '.xz', os.path.join(args.outputdir, 'rdeps.tar.xz'))
            os.chmod(os.path.join(args.outputdir, 'rdeps.tar.xz'), 0o644)
        except Exception as e:
            raise e
        finally:
            # Cleanup:
            for f in [tmpf.name, (tmpf.name + '.xz')]:
                try:
                    os.unlink(f)
                except FileNotFoundError as e:
                    pass

    return 0


if __name__ == '__main__':
    sys.exit(main())