summaryrefslogtreecommitdiff
blob: 818c207d3ce9b8c7d3c09975ab6f3149e8f651cf (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
131
#!/usr/bin/env python2
# vim: set sts=4 sw=4 et tw=0 :
#
# Author(s): Nirbheek Chauhan <nirbheek@gentoo.org>
# License: MIT
#
# Package to figure out which stable or unstable ebuilds aren't needed
# based on the current keywords
#

import os
import sys

import portage

def usage():
    print "Usage: %s <cpv>" % sys.argv[0]

###############
## Constants ##
###############
PORTDIR = portage.settings["PORTDIR"]
portdb = portage.portdb
settings = portage.settings
portdb.porttrees = [PORTDIR]
STABLE_ARCHES = tuple(settings['PORTAGE_ARCHLIST'].split())
UNSTABLE_ARCHES = tuple(map(lambda x: '~'+x, STABLE_ARCHES))
ALL_ARCHES = STABLE_ARCHES+UNSTABLE_ARCHES

######################
## Define Functions ##
######################
def convert_kw(kw):
    "Convert ~arch to arch and leave arch alone"
    if kw.startswith('~'):
        return kw[1:]
    return kw

def get_kws(cpv, arches=ALL_ARCHES):
    """
    Returns an array of KEYWORDS matching 'arches'
    """
    kws = []
    for kw in portdb.aux_get(cpv, ['KEYWORDS'])[0].split():
        if kw in arches:
            kws.append(kw)
    return kws

def cmp_kws(new_kws, old_kws):
    """
    old_kws and new_kws must be sets
    Returns:
     False if new_kws is the same as or worse than old_kws
       Ex: (a, b)       vs (a, b, c) or
           (a, b, c)    vs (a, b, c) or
           (a, b, ~c)   vs (a, b, c), etc
     True if new_kws is better than old_kws
       Ex: (a, b, c)    vs (a, b) or
           (a, b, c)    vs (a, b, ~c), etc
       OR if new_kws is neither better nor worse than old_kws
       Ex: (a, b, c)    vs (a, c, d) or
           (a, b, ~d)   vs (a, c, d), etc
    """
    if not old_kws:
        return True
    if not new_kws:
        # No keywords
        return False
    if new_kws.issubset(old_kws):
        return False
    # If there are some new keywords in new_kws, and they aren't ~arch versions of the kws in old_kws
    if set(map(convert_kw, new_kws.difference(old_kws))).difference(old_kws):
        return True
    return False


def get_obsolete(cp):
    """
    @param cp: cat/pkg atom
    @type cp: string
    @param check_kws: Which keywords to check for obsolete ebuilds, both/stable/unstable
    @type check_kws: String
    """
    cpvs = portdb.xmatch('match-all', cp)
    obsolete_cpvs = []
    not_pmasked = []
    slot_cpvs = {}

    # This is copied from portage/dbapi/porttree.py:visible()
    # Ignore PORTDIR package.masked cpvs
    for cpv in cpvs:
        try:
            metadata = {'SLOT': portdb.aux_get(cpv, ['SLOT'])[0]}
        except KeyError:
            # masked by corruption
            continue
        if settings._getMaskAtom(cpv, metadata):
            continue
        # We skip the profile check because we don't care about that
        not_pmasked.append(cpv)
    # We start with the latest cpvs first so that we never mark newer ebuilds as obsolete
    not_pmasked.reverse()
    
    # Generate a slot-sorted hashtable for cpvs
    for cpv in not_pmasked:
        slot = portdb.aux_get(cpv, ['SLOT'])[0]
        if not slot_cpvs.has_key(slot):
            slot_cpvs[slot] = []
        slot_cpvs[slot].append(cpv)

    # Consider each slot separately for obsolete-detection
    for (slot, cpvs) in slot_cpvs.iteritems():
        all_kws = set()
        for cpv in cpvs:
            kws = set(get_kws(cpv, arches=ALL_ARCHES))
            if cmp_kws(kws, all_kws):
                # Keywords list is unique or better, so add it to the list
                all_kws.update(kws)
            else:
                # Same or worse keywords (unstable and stable) => can be punted
                obsolete_cpvs.append(cpv)
    return obsolete_cpvs

if __name__ == "__main__":
    if len(sys.argv) < 2:
        usage()
        sys.exit(1)
    if sys.argv[1] == '.':
        sys.argv[1] = '/'.join((os.path.basename(os.path.dirname(os.getcwd())), os.path.basename(os.getcwd())))
    for i in get_obsolete(sys.argv[1]):
        print portage.catsplit(i)[-1]+'.ebuild',