aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'pym/gentoolkit/eclean/exclude.py')
-rw-r--r--pym/gentoolkit/eclean/exclude.py262
1 files changed, 262 insertions, 0 deletions
diff --git a/pym/gentoolkit/eclean/exclude.py b/pym/gentoolkit/eclean/exclude.py
new file mode 100644
index 0000000..74a982a
--- /dev/null
+++ b/pym/gentoolkit/eclean/exclude.py
@@ -0,0 +1,262 @@
+#!/usr/bin/python
+
+# Copyright 2003-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+
+from __future__ import print_function
+
+
+import sys
+import re
+import portage
+
+from portage import os
+from gentoolkit.pprinter import warn
+
+# Misc. shortcuts to some portage stuff:
+listdir = portage.listdir
+
+FILENAME_RE = [re.compile(r'(?P<pkgname>[-a-zA-z0-9\+]+)(?P<ver>-\d+\S+)'),
+ re.compile(r'(?P<pkgname>[-a-zA-z]+)(?P<ver>_\d+\S+)'),
+ re.compile(r'(?P<pkgname>[-a-zA-z_]+)(?P<ver>\d\d+\S+)'),
+ re.compile(r'(?P<pkgname>[-a-zA-z0-9_]+)(?P<ver>-default\S+)'),
+ re.compile(r'(?P<pkgname>[-a-zA-z0-9]+)(?P<ver>_\d\S+)'),
+ re.compile(r'(?P<pkgname>[-a-zA-z0-9\+\.]+)(?P<ver>-\d+\S+)'),
+ re.compile(r'(?P<pkgname>[-a-zA-z0-9\+\.]+)(?P<ver>.\d+\S+)')]
+
+debug_modules = []
+
+def dprint(module, message):
+ if module in debug_modules:
+ print(message)
+
+def isValidCP(cp):
+ """Check whether a string is a valid cat/pkg-name.
+
+ This is for 2.0.51 vs. CVS HEAD compatibility, I've not found any function
+ for that which would exists in both. Weird...
+
+ @param cp: catageory/package string
+ @rtype: bool
+ """
+
+ if not '/' in cp:
+ return False
+ try:
+ portage.cpv_getkey(cp+"-0")
+ except:
+ return False
+ else:
+ return True
+
+
+class ParseExcludeFileException(Exception):
+ """For parseExcludeFile() -> main() communication.
+
+ @param value: Error message string
+ """
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+
+
+def parseExcludeFile(filepath, output):
+ """Parses an exclusion file.
+
+ @param filepath: file containing the list of cat/pkg's to exclude
+ @param output: --verbose enabled output method or "lambda x: None"
+
+ @rtype: dict
+ @return: an exclusion dict
+ @raise ParseExcludeFileException: in case of fatal error
+ """
+
+ exclude = {
+ 'categories': {},
+ 'packages': {},
+ 'anti-packages': {},
+ 'filenames': {}
+ }
+ output("Parsing Exclude file: " + filepath)
+ try:
+ file_ = open(filepath,"r")
+ except IOError:
+ raise ParseExcludeFileException("Could not open exclusion file: " +
+ filepath)
+ filecontents = file_.readlines()
+ file_.close()
+ cat_re = re.compile('^(?P<cat>[a-zA-Z0-9]+-[a-zA-Z0-9]+)(/\*)?$')
+ cp_re = re.compile('^(?P<cp>[-a-zA-Z0-9_]+/[-a-zA-Z0-9_]+)$')
+ # used to output the line number for exception error reporting
+ linenum = 0
+ for line in filecontents:
+ # need to increment it here due to continue statements.
+ linenum += 1
+ line = line.strip()
+ if not len(line): # skip blank a line
+ continue
+ if line[0] == '#': # skip a comment line
+ continue
+ #print( "parseExcludeFile: line=", line)
+ try: # category matching
+ cat = cat_re.match(line).group('cat')
+ #print( "parseExcludeFile: found cat=", cat)
+ except:
+ pass
+ else:
+ if not cat in portage.settings.categories:
+ raise ParseExcludeFileException("Invalid category: "+cat +
+ " @line # " + str(linenum))
+ exclude['categories'][cat] = None
+ continue
+ dict_key = 'packages'
+ if line[0] == '!': # reverses category setting
+ dict_key = 'anti-packages'
+ line = line[1:]
+ try: # cat/pkg matching
+ cp = cp_re.match(line).group('cp')
+ #print( "parseExcludeFile: found cp=", cp)
+ if isValidCP(cp):
+ exclude[dict_key][cp] = None
+ continue
+ else:
+ raise ParseExcludeFileException("Invalid cat/pkg: "+cp +
+ " @line # " + str(linenum))
+ except:
+ pass
+ #raise ParseExcludeFileException("Invalid line: "+line)
+ try: # filename matching.
+ exclude['filenames'][line] = re.compile(line)
+ #print( "parseExcludeFile: found filenames", line)
+ except:
+ try:
+ exclude['filenames'][line] = re.compile(re.escape(line))
+ #print( "parseExcludeFile: found escaped filenames", line)
+ except:
+ raise ParseExcludeFileException("Invalid file name/regular " +
+ "expression: @line # " + str(linenum) + " line=" +line)
+ output("Exclude file parsed. Found " +
+ "%d categories, %d packages, %d anti-packages %d filenames"
+ %(len(exclude['categories']), len(exclude['packages']),
+ len(exclude['anti-packages']), len(exclude['filenames'])))
+ #print()
+ #print( "parseExcludeFile: final exclude_dict = ", exclude)
+ #print()
+ return exclude
+
+def cp_all(categories):
+ """temp function until the new portdb.cp_all([cat,...])
+ behaviour is fully available.
+
+ @param categories: list of categories to get all packages for
+ eg. ['app-portage', 'sys-apps',...]
+ @rtype: list of cat/pkg's ['foo/bar', 'foo/baz']
+ """
+ try:
+ cps = portage.portdb.cp_all(categories)
+ message = "Deprication Warning: eclean.exclude.cp_all()\n" + \
+ "New portage functionality is available " +\
+ "Please migrate code permanently"
+ print( warn(message), file=sys.stderr)
+ except: # new behaviour not available
+ #~ message = "Exception: eclean.exclude.cp_all() " +\
+ #~ "new portdb.cp_all() behavior not found. using fallback code"
+ #~ print( warn(message), file=sys.stderr)
+ cps = []
+ # XXX: i smell an access to something which is really out of API...
+ _pkg_dir_name_re = re.compile(r'^\w[-+\w]*$')
+ for tree in portage.portdb.porttrees:
+ for cat in categories:
+ for pkg in listdir(os.path.join(tree,cat),
+ EmptyOnError=1, ignorecvs=1, dirsonly=1):
+ if not _pkg_dir_name_re.match(pkg) or pkg == "CVS":
+ continue
+ cps.append(cat+'/'+pkg)
+ #print( "cp_all: new cps list=", cps)
+ return cps
+
+def exclDictExpand(exclude):
+ """Returns a dictionary of all CP/CPV from porttree which match
+ the exclusion dictionary.
+ """
+ d = {}
+ if 'categories' in exclude:
+ # replace the following cp_all call with
+ # portage.portdb.cp_all([cat1, cat2])
+ # when it is available in all portage versions.
+ cps = cp_all(exclude['categories'])
+ for cp in cps:
+ d[cp] = None
+ if 'packages' in exclude:
+ for cp in exclude['packages']:
+ d[cp] = None
+ if 'anti-packages' in exclude:
+ for cp in exclude['anti-packages']:
+ if cp in d:
+ del d[cp]
+ return d
+
+def exclDictMatchCP(exclude,pkg):
+ """Checks whether a CP matches the exclusion rules."""
+ if 'anti-packages' in exclude and pkg in exclude['anti-packages']:
+ return False
+ if 'packages' in exclude and pkg in exclude['packages']:
+ return True
+ cat = pkg.split('/')[0]
+ if 'categories' in exclude and cat in exclude['categories']:
+ return True
+ return False
+
+def exclDictExpandPkgname(exclude):
+ """Returns a set of all pkgnames from porttree which match
+ the exclusion dictionary.
+ """
+ p = set()
+ if 'categories' in exclude:
+ # replace the following cp_all call with
+ # portage.portdb.cp_all([cat1, cat2])
+ # when it is available in all portage versions.
+ cps = cp_all(exclude['categories'])
+ for cp in cps:
+ pkgname = cp.split('/')[1]
+ p.add(pkgname)
+ if 'packages' in exclude:
+ pkgname = cp.split('/')[1]
+ p.add(pkgname)
+ if 'anti-packages' in exclude:
+ for cp in exclude['anti-packages']:
+ if cp in p:
+ p.remove(cp)
+ return p
+
+
+def exclMatchFilename(exclude_names, filename):
+ """Attempts to split the package name out of a filename
+ and then checks if it matches any exclusion rules.
+
+ This is intended to be run on the cleaning list after all
+ normal checks and removal of protected files. This will reduce
+ the number of files to perform this last minute check on
+
+ @param exclude_names: a set of pkgnames to exlcude
+ @param filename:
+
+ @rtype: bool
+ """
+ found = False
+ index = 0
+ while not found and index < len(FILENAME_RE):
+ found = FILENAME_RE[index].match(filename)
+ index += 1
+ if not found:
+ dprint( "exclude", "exclMatchFilename: filename: " +\
+ "%s, Could not determine package name" %filename)
+ return False
+ pkgname = found.group('pkgname')
+ dprint("exclude", "exclMatchFilename: found pkgname = " +
+ "%s, %s, %d, %s" %(pkgname, str(pkgname in exclude_names),
+ index-1, filename))
+ return (pkgname in exclude_names)
+