How to tidy up the .keywords file on a gentoo system?

There is an official package now for this task called app-portage/portpeek.

It can

  • find obsolete USE flags and
  • obsolete KEYWORDS and
  • clean the files, if -f (fix) is added as parameter.

I wrote a small python script that takes care of this problem. The logic looks at each line in the file package.accept_keywords and only acts on lines that start with = or <=. These lines have a maximum bound version so we can check if they are needed anymore. Lines without a qualifier or a >= are left as-is as we cannot know if they are obsolete.

The lines that we care about are then parsed and the installed version of the package is checked. If the installed version is newer than the keyworded version, or is not longer installed at all, the keyword is considered obsolete. If the installed package is the same version as the keyworded version then the installed package is checked to see if it is still keyworded. If it has been stabilized, the line is obsoleted, otherwise it is retained.

#!/bin/env python

import re
import portage

vartree = portage.db[portage.root]['vartree']

with open('/etc/portage/package.accept_keywords') as f:
    for x in f:
        # eat newline
        x = x.rstrip()
        # we only want lines with a bounded max version
        if re.match('^(=|<=)',x):
            # get the package cpv atom -- strip the =|<= and the trailing keyword(s)
            cpv_masked = re.sub('[<=]','',x.split(' ',1)[0])
            cat, pkg, ver, rev = portage.catpkgsplit(cpv_masked)
            # get cpv for all installed versions of the package
            cpv_installed = vartree.dep_match(cat+'/'+pkg)
            for cpv in cpv_installed:
                cmp = portage.pkgcmp(portage.pkgsplit(cpv), portage.pkgsplit(cpv_masked))
                # if the installed version is not newer than the masked version
                if (cmp <= 0):
                    # check if this version is still keyworded
                    cpv_keywords = vartree.dbapi.aux_get(cpv, ['KEYWORDS'])
                    # keep keyword if the package has no keywords (**)
                    if not cpv_keywords[0]:
                        print(x)
                        break
                    # check if the installed package is still keyworded
                    for cpv_keyword in cpv_keywords[0].split(' '):
                        if cpv_masked_keyword == cpv_keyword:
                            # it is, keep the atom and move on to the next one
                            print(x)
                            break                    
        else:
            # keep atoms that have an unbounded max version
            print(x)

This will print the new keywords file to standard out. Note: do not redirect the output back into /etc/portage/package.accept_keywords or you will clobber the file and lose everything.

This will go a long way toward cleaning up your keywords file and for your other concerns, sorting the file and then examining it for multiple lines for the same package will help resolve most of what is left.