diff options
| author | Paul Varner <fuzzyray@gentoo.org> | 2010-12-28 18:30:14 -0600 |
|---|---|---|
| committer | Paul Varner <fuzzyray@gentoo.org> | 2010-12-28 18:30:14 -0600 |
| commit | 879cf3ce1f3041854c9dc886b59ac7e136ae9b9b (patch) | |
| tree | 7faa716b853d76a4c430d7b43024d82222d329e7 /bin | |
| parent | 258a81471b4106f12bafee2e5c0a5458e2db2280 (diff) | |
| parent | a9ac560d5f3e056d14146d5033e5637bbc9f68ef (diff) | |
| download | gentoolkit-879cf3ce1f3041854c9dc886b59ac7e136ae9b9b.tar.gz | |
Merge branch 'gentoolkit' into euse
Conflicts:
bin/euse
Diffstat (limited to 'bin')
| -rwxr-xr-x | bin/analyse | 48 | ||||
| -rwxr-xr-x | bin/eclean | 849 | ||||
| -rwxr-xr-x | bin/epkginfo | 284 | ||||
| -rwxr-xr-x | bin/equery | 28 | ||||
| -rw-r--r-- | bin/eshowkw | 9 | ||||
| -rwxr-xr-x | bin/euse | 137 | ||||
| -rwxr-xr-x | bin/glsa-check | 126 | ||||
| -rwxr-xr-x | bin/revdep-rebuild | 192 |
8 files changed, 452 insertions, 1221 deletions
diff --git a/bin/analyse b/bin/analyse new file mode 100755 index 0000000..a90410b --- /dev/null +++ b/bin/analyse @@ -0,0 +1,48 @@ +#!/usr/bin/python +# +# Copyright 2010 Brian Dolbec <brian.dolbec@gmail.com> +# Copyright 2002-2010 Gentoo Technologies, Inc. +# Distributed under the terms of the GNU General Public License v2 or later +# +# $Header$ + +"""'analyse' is a flexible utility for Gentoo linux which can display various +information about installed packages, such as the USE flags used and the +packages that use them. It can also be used to help rebuild /etc/portage/package.* +files in the event of corruption, and possibly more. +""" + +from __future__ import print_function + +import sys +# This block ensures that ^C interrupts are handled quietly. +try: + import signal + + def exithandler(signum,frame): + signal.signal(signal.SIGINT, signal.SIG_IGN) + signal.signal(signal.SIGTERM, signal.SIG_IGN) + print() + sys.exit(1) + + signal.signal(signal.SIGINT, exithandler) + signal.signal(signal.SIGTERM, exithandler) + signal.signal(signal.SIGPIPE, signal.SIG_DFL) + +except KeyboardInterrupt: + print() + sys.exit(1) + +from gentoolkit import analyse, errors + +try: + analyse.main() +except errors.GentoolkitException as err: + if '--debug' in sys.argv: + raise + else: + from gentoolkit import pprinter as pp + sys.stderr.write(pp.error(str(err))) + print() + print("Add '--debug' to global options for traceback.") + sys.exit(1) @@ -1,834 +1,49 @@ #!/usr/bin/python -# Copyright 2003-2005 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 -# $Header: $ -from __future__ import with_statement +"""Copyright 2003-2010 Gentoo Foundation +Distributed under the terms of the GNU General Public License v2 +""" + +from __future__ import print_function + -############################################################################### # Meta: -__author__ = "Thomas de Grenier de Latour (tgl)" -__email__ = "degrenier@easyconnect.fr" -__version__ = open('/usr/share/gentoolkit/VERSION').read().strip() +__author__ = "Thomas de Grenier de Latour (tgl), " + \ + "modular re-write by: Brian Dolbec (dol-sen)" +__email__ = "degrenier@easyconnect.fr, " + \ + "brian.dolbec@gmail.com" +__version__ = "svn" __productname__ = "eclean" __description__ = "A cleaning tool for Gentoo distfiles and binaries." -############################################################################### -# Python imports: - import sys -import os, stat -import re -import time -import getopt -import fpformat -import signal - -import portage -from portage.output import * - -listdir = portage.listdir - -############################################################################### -# Misc. shortcuts to some portage stuff: -port_settings = portage.settings -distdir = port_settings["DISTDIR"] -pkgdir = port_settings["PKGDIR"] - - -############################################################################### -# printVersion: -def printVersion(): - print "%s (%s) - %s" \ - % (__productname__, __version__, __description__) - print - print "Author: %s <%s>" % (__author__,__email__) - print "Copyright 2003-2009 Gentoo Foundation" - print "Distributed under the terms of the GNU General Public License v2" - - -############################################################################### -# printUsage: print help message. May also print partial help to stderr if an -# error from {'options','actions'} is specified. -def printUsage(error=None,help=None): - out = sys.stdout - if error: out = sys.stderr - if not error in ('actions', 'global-options', \ - 'packages-options', 'distfiles-options', \ - 'merged-packages-options', 'merged-distfiles-options', \ - 'time', 'size'): - error = None - if not error and not help: help = 'all' - if error == 'time': - eerror("Wrong time specification") - print >>out, "Time specification should be an integer followed by a"+ \ - " single letter unit." - print >>out, "Available units are: y (years), m (months), w (weeks), "+ \ - "d (days) and h (hours)." - print >>out, "For instance: \"1y\" is \"one year\", \"2w\" is \"two"+ \ - " weeks\", etc. " - return - if error == 'size': - eerror("Wrong size specification") - print >>out, "Size specification should be an integer followed by a"+ \ - " single letter unit." - print >>out, "Available units are: G, M, K and B." - print >>out, "For instance: \"10M\" is \"ten megabytes\", \"200K\" "+ \ - "is \"two hundreds kilobytes\", etc." - return - if error in ('global-options', 'packages-options', 'distfiles-options', \ - 'merged-packages-options', 'merged-distfiles-options',): - eerror("Wrong option on command line.") - print >>out - elif error == 'actions': - eerror("Wrong or missing action name on command line.") - print >>out - print >>out, white("Usage:") - if error in ('actions','global-options', 'packages-options', \ - 'distfiles-options') or help == 'all': - print >>out, " "+turquoise(__productname__), \ - yellow("[global-option] ..."), \ - green("<action>"), \ - yellow("[action-option] ...") - if error == 'merged-distfiles-options' or help in ('all','distfiles'): - print >>out, " "+turquoise(__productname__+'-dist'), \ - yellow("[global-option, distfiles-option] ...") - if error == 'merged-packages-options' or help in ('all','packages'): - print >>out, " "+turquoise(__productname__+'-pkg'), \ - yellow("[global-option, packages-option] ...") - if error in ('global-options', 'actions'): - print >>out, " "+turquoise(__productname__), \ - yellow("[--help, --version]") - if help == 'all': - print >>out, " "+turquoise(__productname__+"(-dist,-pkg)"), \ - yellow("[--help, --version]") - if error == 'merged-packages-options' or help == 'packages': - print >>out, " "+turquoise(__productname__+'-pkg'), \ - yellow("[--help, --version]") - if error == 'merged-distfiles-options' or help == 'distfiles': - print >>out, " "+turquoise(__productname__+'-dist'), \ - yellow("[--help, --version]") - print >>out - if error in ('global-options', 'merged-packages-options', \ - 'merged-distfiles-options') or help: - print >>out, "Available global", yellow("options")+":" - print >>out, yellow(" -C, --nocolor")+ \ - " - turn off colors on output" - print >>out, yellow(" -d, --destructive")+ \ - " - only keep the minimum for a reinstallation" - print >>out, yellow(" -e, --exclude-file=<path>")+ \ - " - path to the exclusion file" - print >>out, yellow(" -i, --interactive")+ \ - " - ask confirmation before deletions" - print >>out, yellow(" -n, --package-names")+ \ - " - protect all versions (when --destructive)" - print >>out, yellow(" -p, --pretend")+ \ - " - only display what would be cleaned" - print >>out, yellow(" -q, --quiet")+ \ - " - be as quiet as possible" - print >>out, yellow(" -t, --time-limit=<time>")+ \ - " - don't delete files modified since "+yellow("<time>") - print >>out, " "+yellow("<time>"), "is a duration: \"1y\" is"+ \ - " \"one year\", \"2w\" is \"two weeks\", etc. " - print >>out, " "+"Units are: y (years), m (months), w (weeks), "+ \ - "d (days) and h (hours)." - print >>out, yellow(" -h, --help")+ \ - " - display the help screen" - print >>out, yellow(" -V, --version")+ \ - " - display version info" - print >>out - if error == 'actions' or help == 'all': - print >>out, "Available", green("actions")+":" - print >>out, green(" packages")+ \ - " - clean outdated binary packages from:" - print >>out, " ",teal(pkgdir) - print >>out, green(" distfiles")+ \ - " - clean outdated packages sources files from:" - print >>out, " ",teal(distdir) - print >>out - if error in ('packages-options','merged-packages-options') \ - or help in ('all','packages'): - print >>out, "Available", yellow("options"),"for the", \ - green("packages"),"action:" - print >>out, yellow(" NONE :)") - print >>out - if error in ('distfiles-options', 'merged-distfiles-options') \ - or help in ('all','distfiles'): - print >>out, "Available", yellow("options"),"for the", \ - green("distfiles"),"action:" - print >>out, yellow(" -f, --fetch-restricted")+ \ - " - protect fetch-restricted files (when --destructive)" - print >>out, yellow(" -s, --size-limit=<size>")+ \ - " - don't delete distfiles bigger than "+yellow("<size>") - print >>out, " "+yellow("<size>"), "is a size specification: "+ \ - "\"10M\" is \"ten megabytes\", \"200K\" is" - print >>out, " "+"\"two hundreds kilobytes\", etc. Units are: "+ \ - "G, M, K and B." - print >>out - print >>out, "More detailed instruction can be found in", \ - turquoise("`man %s`" % __productname__) - - -############################################################################### -# einfo: display an info message depending on a color mode -def einfo(message="", nocolor=False): - if not nocolor: prefix = " "+green('*') - else: prefix = ">>>" - print prefix,message - - -############################################################################### -# eerror: display an error depending on a color mode -def eerror(message="", nocolor=False): - if not nocolor: prefix = " "+red('*') - else: prefix = "!!!" - print >>sys.stderr,prefix,message - - -############################################################################### -# eprompt: display a user question depending on a color mode. -def eprompt(message, nocolor=False): - if not nocolor: prefix = " "+red('>')+" " - else: prefix = "??? " - sys.stdout.write(prefix+message) - sys.stdout.flush() - - -############################################################################### -# prettySize: integer -> byte/kilo/mega/giga converter. Optionnally justify the -# result. Output is a string. -def prettySize(size,justify=False): - units = [" G"," M"," K"," B"] - approx = 0 - while len(units) and size >= 1000: - approx = 1 - size = size / 1024. - units.pop() - sizestr = fpformat.fix(size,approx)+units[-1] - if justify: - sizestr = " " + blue("[ ") + " "*(7-len(sizestr)) \ - + green(sizestr) + blue(" ]") - return sizestr - - -############################################################################### -# yesNoAllPrompt: print a prompt until user answer in yes/no/all. Return a -# boolean for answer, and also may affect the 'accept_all' option. -# Note: i gave up with getch-like functions, to much bugs in case of escape -# sequences. Back to raw_input. -def yesNoAllPrompt(myoptions,message="Do you want to proceed?"): - user_string="xxx" - while not user_string.lower() in ["","y","n","a","yes","no","all"]: - eprompt(message+" [Y/n/a]: ", myoptions['nocolor']) - user_string = raw_input() - if user_string.lower() in ["a","all"]: - myoptions['accept_all'] = True - myanswer = user_string.lower() in ["","y","a","yes","all"] - return myanswer - - -############################################################################### -# ParseArgsException: for parseArgs() -> main() communication -class ParseArgsException(Exception): - def __init__(self, value): - self.value = value - def __str__(self): - return repr(self.value) - - -############################################################################### -# parseSize: convert a file size "Xu" ("X" is an integer, and "u" in [G,M,K,B]) -# into an integer (file size in Bytes). Raises ParseArgsException('size') in -# case of failure. -def parseSize(size): - myunits = { \ - 'G': (1024**3), \ - 'M': (1024**2), \ - 'K': 1024, \ - 'B': 1 \ - } - try: - mymatch = re.match(r"^(?P<value>\d+)(?P<unit>[GMKBgmkb])?$",size) - mysize = int(mymatch.group('value')) - if mymatch.group('unit'): - mysize *= myunits[mymatch.group('unit').capitalize()] - except: - raise ParseArgsException('size') - return mysize - - -############################################################################### -# parseTime: convert a duration "Xu" ("X" is an int, and "u" a time unit in -# [Y,M,W,D,H]) into an integer which is a past EPOCH date. -# Raises ParseArgsException('time') in case of failure. -# (yep, big approximations inside... who cares?) -def parseTime(timespec): - myunits = {'H' : (60 * 60)} - myunits['D'] = myunits['H'] * 24 - myunits['W'] = myunits['D'] * 7 - myunits['M'] = myunits['D'] * 30 - myunits['Y'] = myunits['D'] * 365 - try: - # parse the time specification - mymatch = re.match(r"^(?P<value>\d+)(?P<unit>[YMWDHymwdh])?$",timespec) - myvalue = int(mymatch.group('value')) - if not mymatch.group('unit'): myunit = 'D' - else: myunit = mymatch.group('unit').capitalize() - except: raise ParseArgsException('time') - # calculate the limit EPOCH date - mytime = time.time() - (myvalue * myunits[myunit]) - return mytime +# This block ensures that ^C interrupts are handled quietly. +try: + import signal -############################################################################### -# parseCmdLine: parse the command line arguments. Raise exceptions on errors or -# non-action modes (help/version). Returns an action, and affect the options -# dict. -def parseArgs(myoptions={}): - - # local function for interpreting command line options - # and setting myoptions accordingly - def optionSwitch(myoption,opts,action=None): - return_code = True - for o, a in opts: - if o in ("-h", "--help"): - if action: raise ParseArgsException('help-'+action) - else: raise ParseArgsException('help') - elif o in ("-V", "--version"): - raise ParseArgsException('version') - elif o in ("-C", "--nocolor"): - myoptions['nocolor'] = True - nocolor() - elif o in ("-d", "--destructive"): - myoptions['destructive'] = True - elif o in ("-i", "--interactive") and not myoptions['pretend']: - myoptions['interactive'] = True - elif o in ("-p", "--pretend"): - myoptions['pretend'] = True - myoptions['interactive'] = False - elif o in ("-q", "--quiet"): - myoptions['quiet'] = True - elif o in ("-t", "--time-limit"): - myoptions['time-limit'] = parseTime(a) - elif o in ("-e", "--exclude-file"): - myoptions['exclude-file'] = a - elif o in ("-n", "--package-names"): - myoptions['package-names'] = True - elif o in ("-f", "--fetch-restricted"): - myoptions['fetch-restricted'] = True - elif o in ("-s", "--size-limit"): - myoptions['size-limit'] = parseSize(a) - else: return_code = False - # sanity check of --destructive only options: - for myopt in ('fetch-restricted', 'package-names'): - if (not myoptions['destructive']) and myoptions[myopt]: - if not myoptions['quiet']: - eerror("--%s only makes sense in --destructive mode." \ - % myopt, myoptions['nocolor']) - myoptions[myopt] = False - return return_code - - # here are the different allowed command line options (getopt args) - getopt_options = {'short':{}, 'long':{}} - getopt_options['short']['global'] = "Cdipqe:t:nhV" - getopt_options['long']['global'] = ["nocolor", "destructive", \ - "interactive", "pretend", "quiet", "exclude-file=", "time-limit=", \ - "package-names", "help", "version"] - getopt_options['short']['distfiles'] = "fs:" - getopt_options['long']['distfiles'] = ["fetch-restricted", "size-limit="] - getopt_options['short']['packages'] = "" - getopt_options['long']['packages'] = [""] - # set default options, except 'nocolor', which is set in main() - myoptions['interactive'] = False - myoptions['pretend'] = False - myoptions['quiet'] = False - myoptions['accept_all'] = False - myoptions['destructive'] = False - myoptions['time-limit'] = 0 - myoptions['package-names'] = False - myoptions['fetch-restricted'] = False - myoptions['size-limit'] = 0 - # if called by a well-named symlink, set the acction accordingly: - myaction = None - if os.path.basename(sys.argv[0]) in \ - (__productname__+'-pkg', __productname__+'-packages'): - myaction = 'packages' - elif os.path.basename(sys.argv[0]) in \ - (__productname__+'-dist', __productname__+'-distfiles'): - myaction = 'distfiles' - # prepare for the first getopt - if myaction: - short_opts = getopt_options['short']['global'] \ - + getopt_options['short'][myaction] - long_opts = getopt_options['long']['global'] \ - + getopt_options['long'][myaction] - opts_mode = 'merged-'+myaction - else: - short_opts = getopt_options['short']['global'] - long_opts = getopt_options['long']['global'] - opts_mode = 'global' - # apply getopts to command line, show partial help on failure - try: opts, args = getopt.getopt(sys.argv[1:], short_opts, long_opts) - except: raise ParseArgsException(opts_mode+'-options') - # set myoptions accordingly - optionSwitch(myoptions,opts,action=myaction) - # if action was already set, there should be no more args - if myaction and len(args): raise ParseArgsException(opts_mode+'-options') - # if action was set, there is nothing left to do - if myaction: return myaction - # So, we are in "eclean --foo action --bar" mode. Parse remaining args... - # Only two actions are allowed: 'packages' and 'distfiles'. - if not len(args) or not args[0] in ('packages','distfiles'): - raise ParseArgsException('actions') - myaction = args.pop(0) - # parse the action specific options - try: opts, args = getopt.getopt(args, \ - getopt_options['short'][myaction], \ - getopt_options['long'][myaction]) - except: raise ParseArgsException(myaction+'-options') - # set myoptions again, for action-specific options - optionSwitch(myoptions,opts,action=myaction) - # any remaning args? Then die! - if len(args): raise ParseArgsException(myaction+'-options') - # returns the action. Options dictionary is modified by side-effect. - return myaction - -############################################################################### -# isValidCP: check wether 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... -def isValidCP(cp): - if not '/' in cp: return False - try: portage.cpv_getkey(cp+"-0") - except: return False - else: return True - - -############################################################################### -# ParseExcludeFileException: for parseExcludeFile() -> main() communication -class ParseExcludeFileException(Exception): - def __init__(self, value): - self.value = value - def __str__(self): - return repr(self.value) - - -############################################################################### -# parseExcludeFile: parses an exclusion file, returns an exclusion dictionnary -# Raises ParseExcludeFileException in case of fatal error. -def parseExcludeFile(filepath): - excl_dict = { \ - 'categories':{}, \ - 'packages':{}, \ - 'anti-packages':{}, \ - 'garbage':{} } - try: file = open(filepath,"r") - except IOError: - raise ParseExcludeFileException("Could not open exclusion file.") - 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_]+)$') - for line in filecontents: - line = line.strip() - if not len(line): continue - if line[0] == '#': continue - try: mycat = cat_re.match(line).group('cat') - except: pass - else: - if not mycat in portage.settings.categories: - raise ParseExcludeFileException("Invalid category: "+mycat) - excl_dict['categories'][mycat] = None - continue - dict_key = 'packages' - if line[0] == '!': - dict_key = 'anti-packages' - line = line[1:] - try: - mycp = cp_re.match(line).group('cp') - if isValidCP(mycp): - excl_dict[dict_key][mycp] = None - continue - else: raise ParseExcludeFileException("Invalid cat/pkg: "+mycp) - except: pass - #raise ParseExcludeFileException("Invalid line: "+line) - try: - excl_dict['garbage'][line] = re.compile(line) - except: - try: - excl_dict['garbage'][line] = re.compile(re.escape(line)) - except: - raise ParseExcludeFileException("Invalid file name/regular expression: "+line) - return excl_dict - - -############################################################################### -# exclDictExpand: returns a dictionary of all CP from porttree which match -# the exclusion dictionary -def exclDictExpand(excl_dict): - mydict = {} - if 'categories' in excl_dict: - # XXX: i smell an access to something which is really out of API... - for mytree in portage.portdb.porttrees: - for mycat in excl_dict['categories']: - for mypkg in listdir(os.path.join(mytree,mycat),ignorecvs=1): - mydict[mycat+'/'+mypkg] = None - if 'packages' in excl_dict: - for mycp in excl_dict['packages']: - mydict[mycp] = None - if 'anti-packages' in excl_dict: - for mycp in excl_dict['anti-packages']: - if mycp in mydict: - del mydict[mycp] - return mydict - - -############################################################################### -# exclDictMatch: checks whether a CP matches the exclusion rules -def exclDictMatch(excl_dict,pkg): - if 'anti-packages' in excl_dict \ - and pkg in excl_dict['anti-packages']: - return False - if 'packages' in excl_dict \ - and pkg in excl_dict['packages']: - return True - mycat = pkg.split('/')[0] - if 'categories' in excl_dict \ - and mycat in excl_dict['categories']: - return True - return False - - -############################################################################### -# findDistfiles: find all obsolete distfiles. -# XXX: what about cvs ebuilds? i should install some to see where it goes... -def findDistfiles( \ - myoptions, \ - exclude_dict={}, \ - destructive=False,\ - fetch_restricted=False, \ - package_names=False, \ - time_limit=0, \ - size_limit=0,): - # this regexp extracts files names from SRC_URI. It is not very precise, - # but we don't care (may return empty strings, etc.), since it is fast. - file_regexp = re.compile('([a-zA-Z0-9_,\.\-\+\~]*)[\s\)]') - clean_dict = {} - keep = [] - pkg_dict = {} - - # create a big CPV->SRC_URI dict of packages whose distfiles should be kept - if (not destructive) or fetch_restricted: - # list all CPV from portree (yeah, that takes time...) - for package in portage.portdb.cp_all(): - for my_cpv in portage.portdb.cp_list(package): - # get SRC_URI and RESTRICT from aux_get - try: (src_uri,restrict) = \ - portage.portdb.aux_get(my_cpv,["SRC_URI","RESTRICT"]) - except KeyError: continue - # keep either all or fetch-restricted only - if (not destructive) or ('fetch' in restrict): - pkg_dict[my_cpv] = src_uri - if destructive: - if not package_names: - # list all CPV from vartree - pkg_list = portage.db[portage.root]["vartree"].dbapi.cpv_all() - else: - # list all CPV from portree for CP in vartree - pkg_list = [] - for package in portage.db[portage.root]["vartree"].dbapi.cp_all(): - pkg_list += portage.portdb.cp_list(package) - for my_cp in exclDictExpand(exclude_dict): - # add packages from the exclude file - pkg_list += portage.portdb.cp_list(my_cp) - for my_cpv in pkg_list: - # skip non-existing CPV (avoids ugly aux_get messages) - if not portage.portdb.cpv_exists(my_cpv): continue - # get SRC_URI from aux_get - try: pkg_dict[my_cpv] = \ - portage.portdb.aux_get(my_cpv,["SRC_URI"])[0] - except KeyError: continue - del pkg_list - - # create a dictionary of files which should be deleted - if not (os.path.isdir(distdir)): - eerror("%s does not appear to be a directory." % distdir, myoptions['nocolor']) - eerror("Please set DISTDIR to a sane value.", myoptions['nocolor']) - eerror("(Check your /etc/make.conf and environment).", myoptions['nocolor']) - exit(1) - for file in os.listdir(distdir): - filepath = os.path.join(distdir, file) - try: file_stat = os.stat(filepath) - except: continue - if not stat.S_ISREG(file_stat[stat.ST_MODE]): continue - if size_limit and (file_stat[stat.ST_SIZE] >= size_limit): - continue - if time_limit and (file_stat[stat.ST_MTIME] >= time_limit): - continue - if 'garbage' in exclude_dict: - # Try to match file name directly - if file in exclude_dict['garbage']: - file_match = True - # See if file matches via regular expression matching - else: - file_match = False - for file_entry in exclude_dict['garbage']: - if exclude_dict['garbage'][file_entry].match(file): - file_match = True - break - - if file_match: - continue - # this is a candidate for cleaning - clean_dict[file]=[filepath] - # remove files owned by some protected packages - for my_cpv in pkg_dict: - for file in file_regexp.findall(pkg_dict[my_cpv]+"\n"): - if file in clean_dict: - del clean_dict[file] - # no need to waste IO time if there is nothing left to clean - if not len(clean_dict): return clean_dict - return clean_dict - - -############################################################################### -# findPackages: find all obsolete binary packages. -# XXX: packages are found only by symlinks. Maybe i should also return .tbz2 -# files from All/ that have no corresponding symlinks. -def findPackages( \ - myoptions, \ - exclude_dict={}, \ - destructive=False, \ - time_limit=0, \ - package_names=False): - clean_dict = {} - # create a full package dictionary - - if not (os.path.isdir(pkgdir)): - eerror("%s does not appear to be a directory." % pkgdir, myoptions['nocolor']) - eerror("Please set PKGDIR to a sane value.", myoptions['nocolor']) - eerror("(Check your /etc/make.conf and environment).", myoptions['nocolor']) - exit(1) - for root, dirs, files in os.walk(pkgdir): - if root[-3:] == 'All': continue - for file in files: - if not file[-5:] == ".tbz2": - # ignore non-tbz2 files - continue - path = os.path.join(root, file) - category = os.path.split(root)[-1] - cpv = category+"/"+file[:-5] - mystat = os.lstat(path) - if time_limit and (mystat[stat.ST_MTIME] >= time_limit): - # time-limit exclusion - continue - # dict is cpv->[files] (2 files in general, because of symlink) - clean_dict[cpv] = [path] - #if os.path.islink(path): - if stat.S_ISLNK(mystat[stat.ST_MODE]): - clean_dict[cpv].append(os.path.realpath(path)) - # keep only obsolete ones - if destructive: - mydbapi = portage.db[portage.root]["vartree"].dbapi - if package_names: cp_all = dict.fromkeys(mydbapi.cp_all()) - else: cp_all = {} - else: - mydbapi = portage.db[portage.root]["porttree"].dbapi - cp_all = {} - for mycpv in clean_dict.keys(): - if exclDictMatch(exclude_dict,portage.cpv_getkey(mycpv)): - # exclusion because of the exclude file - del clean_dict[mycpv] - continue - if mydbapi.cpv_exists(mycpv): - # exclusion because pkg still exists (in porttree or vartree) - del clean_dict[mycpv] - continue - if portage.cpv_getkey(mycpv) in cp_all: - # exlusion because of --package-names - del clean_dict[mycpv] - - return clean_dict - - -############################################################################### -# doCleanup: takes a dictionnary {'display name':[list of files]}. Calculate -# size of each entry for display, prompt user if needed, delete files if needed -# and return the total size of files that [have been / would be] deleted. -def doCleanup(clean_dict,action,myoptions): - # define vocabulary of this action - if action == 'distfiles': file_type = 'file' - else: file_type = 'binary package' - # sorting helps reading - clean_keys = clean_dict.keys() - clean_keys.sort() - clean_size = 0 - # clean all entries one by one - for mykey in clean_keys: - key_size = 0 - for file in clean_dict[mykey]: - # get total size for an entry (may be several files, and - # symlinks count zero) - if os.path.islink(file): continue - try: key_size += os.path.getsize(file) - except: eerror("Could not read size of "+file, \ - myoptions['nocolor']) - if not myoptions['quiet']: - # pretty print mode - print prettySize(key_size,True),teal(mykey) - elif myoptions['pretend'] or myoptions['interactive']: - # file list mode - for file in clean_dict[mykey]: print file - #else: actually delete stuff, but don't print anything - if myoptions['pretend']: clean_size += key_size - elif not myoptions['interactive'] \ - or myoptions['accept_all'] \ - or yesNoAllPrompt(myoptions, \ - "Do you want to delete this " \ - + file_type+"?"): - # non-interactive mode or positive answer. - # For each file, try to delete the file and clean it out - # of Packages metadata file - if action == 'packages': - metadata = portage.getbinpkg.PackageIndex() - with open(os.path.join(pkgdir, 'Packages')) as metadata_file: - metadata.read(metadata_file) - for file in clean_dict[mykey]: - # ...get its size... - filesize = 0 - if not os.path.exists(file): continue - if not os.path.islink(file): - try: filesize = os.path.getsize(file) - except: eerror("Could not read size of "\ - +file, myoptions['nocolor']) - # ...and try to delete it. - try: - os.unlink(file) - except: - eerror("Could not delete "+file, \ - myoptions['nocolor']) - # only count size if successfully deleted - else: - clean_size += filesize - if action == 'packages': - metadata.packages[:] = [p for p in metadata.packages if 'CPV' in p and p['CPV'] != file] - - if action == 'packages': - with open(os.path.join(pkgdir, 'Packages'), 'w') as metadata_file: - metadata.write(metadata_file) - - # return total size of deleted or to delete files - return clean_size - + def exithandler(signum,frame): + signal.signal(signal.SIGINT, signal.SIG_IGN) + signal.signal(signal.SIGTERM, signal.SIG_IGN) + print() + sys.exit(1) -############################################################################### -# doAction: execute one action, ie display a few message, call the right find* -# function, and then call doCleanup with its result. -def doAction(action,myoptions,exclude_dict={}): - # define vocabulary for the output - if action == 'packages': files_type = "binary packages" - else: files_type = "distfiles" - # find files to delete, depending on the action - if not myoptions['quiet']: - einfo("Building file list for "+action+" cleaning...", \ - myoptions['nocolor']) - if action == 'packages': - clean_dict = findPackages( - myoptions, \ - exclude_dict=exclude_dict, \ - destructive=myoptions['destructive'], \ - package_names=myoptions['package-names'], \ - time_limit=myoptions['time-limit']) - else: - clean_dict = findDistfiles( \ - myoptions, \ - exclude_dict=exclude_dict, \ - destructive=myoptions['destructive'], \ - fetch_restricted=myoptions['fetch-restricted'], \ - package_names=myoptions['package-names'], \ - time_limit=myoptions['time-limit'], \ - size_limit=myoptions['size-limit']) - # actually clean files if something was found - if len(clean_dict.keys()): - # verbose pretend message - if myoptions['pretend'] and not myoptions['quiet']: - einfo("Here are "+files_type+" that would be deleted:", \ - myoptions['nocolor']) - # verbose non-pretend message - elif not myoptions['quiet']: - einfo("Cleaning "+files_type+"...",myoptions['nocolor']) - # do the cleanup, and get size of deleted files - clean_size = doCleanup(clean_dict,action,myoptions) - # vocabulary for final message - if myoptions['pretend']: verb = "would be" - else: verb = "has been" - # display freed space - if not myoptions['quiet']: - einfo("Total space that "+verb+" freed in " \ - + action + " directory: " \ - + red(prettySize(clean_size)), \ - myoptions['nocolor']) - # nothing was found, return - elif not myoptions['quiet']: - einfo("Your "+action+" directory was already clean.", \ - myoptions['nocolor']) + signal.signal(signal.SIGINT, exithandler) + signal.signal(signal.SIGTERM, exithandler) + signal.signal(signal.SIGPIPE, signal.SIG_DFL) +except KeyboardInterrupt: + print() + sys.exit(1) -############################################################################### -# main: parse command line and execute all actions -def main(): - # set default options - myoptions = {} - myoptions['nocolor'] = port_settings["NOCOLOR"] in ('yes','true') \ - and sys.stdout.isatty() - if myoptions['nocolor']: nocolor() - # parse command line options and actions - try: myaction = parseArgs(myoptions) - # filter exception to know what message to display - except ParseArgsException, e: - if e.value == 'help': - printUsage(help='all') - sys.exit(0) - elif e.value[:5] == 'help-': - printUsage(help=e.value[5:]) - sys.exit(0) - elif e.value == 'version': - printVersion() - sys.exit(0) - else: - printUsage(e.value) - sys.exit(2) - # parse the exclusion file - if not 'exclude-file' in myoptions: - my_exclude_file = "/etc/%s/%s.exclude" % (__productname__ , myaction) - if os.path.isfile(my_exclude_file): - myoptions['exclude-file'] = my_exclude_file - if 'exclude-file' in myoptions: - try: exclude_dict = parseExcludeFile(myoptions['exclude-file']) - except ParseExcludeFileException, e: - eerror(e, myoptions['nocolor']) - eerror("Invalid exclusion file: %s" % myoptions['exclude-file'], \ - myoptions['nocolor']) - eerror("See format of this file in `man %s`" % __productname__, \ - myoptions['nocolor']) - sys.exit(1) - else: exclude_dict={} - # security check for non-pretend mode - if not myoptions['pretend'] and portage.secpass == 0: - eerror("Permission denied: you must be root or belong to the portage group.", \ - myoptions['nocolor']) - sys.exit(1) - # execute action - doAction(myaction, myoptions, exclude_dict=exclude_dict) +from gentoolkit.eclean.cli import main -############################################################################### -# actually call main() if launched as a script -if __name__ == "__main__": - try: main() - except KeyboardInterrupt: - print "Aborted." - sys.exit(130) - sys.exit(0) +try: + main() +except KeyboardInterrupt: + print("Aborted.") + sys.exit(130) +sys.exit(0) diff --git a/bin/epkginfo b/bin/epkginfo index 747527a..953b4a4 100755 --- a/bin/epkginfo +++ b/bin/epkginfo @@ -1,244 +1,54 @@ #!/usr/bin/python -############################################################################## -# $Header: $ -############################################################################## -# Distributed under the terms of the GNU General Public License, v2 or later -# Author: Ned Ludd <solar@gentoo.org> (glue all the parts together) -# Author: Eldad Zack <eldad@gentoo.org> (earch) -# Author : Eric Olinger <EvvL AT RustedHalo DOT net> (metadata) +# +# Copyright 2009 Gentoo Technologies, Inc. +# Distributed under the terms of the GNU General Public License v2 or later +# +# $Header$ -# Gentoo metadata xml and arch keyword checking tool. +"""Shortcut to equery meta""" -import os -import sys -import re -from stat import * -from xml.sax import saxutils, make_parser, handler -from xml.sax.handler import feature_namespaces - -import portage -from portage.output import * - -version = open('/usr/share/gentoolkit/VERSION').read().strip() - -def earch(workdir): - """Prints arch keywords for a given dir""" - portdir = portage.settings["PORTDIR"] - #workdir = "." - os.chdir(workdir) - - archdict = {} - ebuildlist = [] - for file in os.listdir(workdir): - if re.search("\.ebuild$",file): - ebuildlist.append(re.split("\.ebuild$",file)[0]) - - ebuildlist.sort(lambda x,y: portage.pkgcmp(portage.pkgsplit(x),portage.pkgsplit(y))) - - slot_list = [] - - for pkg in ebuildlist: - portdb = portage.portdbapi(portdir) - aux = portdb.aux_get(workdir.rsplit("/")[-2] + "/" + pkg, ['SLOT', 'KEYWORDS']) - - slot = aux[0] - keywords = keywords = re.split(' ',aux[1]) - - if not slot in slot_list: - slot_list.append(slot) - - for arch in keywords: - if arch in archdict: - archdict[arch].append((pkg, slot)) - else: - archdict[arch] = [ (pkg, slot) ] - - archlist = archdict.keys(); - archlist.sort() - - slot_list.sort() - - for slot in slot_list: - visible_stable = {} - visible_unstable = {} - - for arch in archlist: - visible_stable[arch] = None - visible_unstable[arch] = None - - for pkg in ebuildlist: - for arch in archlist: - if (arch and (pkg, slot) in archdict[arch]): - if arch[0] == "-": - pass - elif "~" == arch[0]: - visible_unstable[arch] = pkg - else: - visible_unstable[arch] = None - visible_stable[arch] = pkg - - for pkg in ebuildlist: - found = False - for arch in archlist: - if (pkg, slot) in archdict[arch]: - found = True - - if not found: - continue - - if not pkg == ebuildlist[0]: - print "" - - print darkgreen("Keywords: ") + pkg + "[" + slot + "]:", - - for arch in archlist: - if (arch and (pkg, slot) in archdict[arch]): - if arch[0] == "-": - print red(arch), - elif "~" == arch[0]: - if visible_unstable[arch] == pkg: - print blue(arch), - else: - if visible_stable[arch] == pkg: - print green(arch), - - -class Metadata_XML(handler.ContentHandler): - _inside_herd="No" - _inside_maintainer="No" - _inside_email="No" - _inside_longdescription="No" - - _herd = [] - _maintainers = [] - _longdescription = "" - - def startElement(self, tag, attr): - if tag == "herd": - self._inside_herd="Yes" - if tag == "longdescription": - self._inside_longdescription="Yes" - if tag == "maintainer": - self._inside_maintainer="Yes" - if tag == "email": - self._inside_email="Yes" - - def endElement(self, tag): - if tag == "herd": - self._inside_herd="No" - if tag == "longdescription": - self._inside_longdescription="No" - if tag == "maintainer": - self._inside_maintainer="No" - if tag == "email": - self._inside_email="No" - - def characters(self, contents): - if self._inside_herd == "Yes": - self._herd.append(contents) - - if self._inside_longdescription == "Yes": - self._longdescription = contents - - if self._inside_maintainer=="Yes" and self._inside_email=="Yes": - self._maintainers.append(contents) - - -def check_metadata(full_package): - """Checks that the primary maintainer is still an active dev and list the herd the package belongs to""" - metadata_file=portage.settings["PORTDIR"] + "/" + portage.pkgsplit(full_package)[0] + "/metadata.xml" - if not os.path.exists(metadata_file): - print darkgreen("Maintainer: ") + red("Error (Missing metadata.xml)") - return 1 +from __future__ import print_function - parser = make_parser() - handler = Metadata_XML() - handler._maintainers = [] - parser.setContentHandler(handler) - parser.parse( metadata_file ) - - if handler._herd: - herds = ", ".join(handler._herd) - print darkgreen("Herd: ") + herds - else: - print darkgreen("Herd: ") + red("Error (No Herd)") - return 1 - - - if handler._maintainers: - print darkgreen("Maintainer: ") + ", ".join(handler._maintainers) - else: - print darkgreen("Maintainer: ") + "none" - - if len(handler._longdescription) > 1: - print darkgreen("Description: ") + handler._longdescription - print darkgreen("Location: ") + os.path.normpath(portage.settings["PORTDIR"] + "/" + portage.pkgsplit(full_package)[0]) - - -def usage(code): - """Prints the uage information for this script""" - print green("epkginfo"), "(%s)" % version - print - print "Usage: epkginfo [package-cat/]package" - sys.exit(code) - - -# default color setup -if ( not sys.stdout.isatty() ) or ( portage.settings["NOCOLOR"] in ["yes","true"] ): - nocolor() - -def fc(x,y): - return cmp(y[0], x[0]) +__authors__ = ( + 'Douglas Anderson <douglasjanderson@gmail.com>: equery meta', + 'Ned Ludd <solar@gentoo.org>: first full implimentation' + 'Eldad Zack <eldad@gentoo.org>: earch', + 'Eric Olinger <EvvL AT RustedHalo DOT net>: metadata' + ) +import sys -def grab_changelog_devs(catpkg): +from gentoolkit import equery, errors +from gentoolkit.equery import mod_usage +from gentoolkit.equery.meta import main, print_help +from portage.exception import AmbiguousPackageName + +def print_epkginfo_help(): + print(mod_usage(mod_name="epkginfo")) + print() + print_help(with_usage=False) + +equery.initialize_configuration() +args = sys.argv[1:] +if not args or set(('-h', '--help')).intersection(args): + print_epkginfo_help() +else: try: - os.chdir(portage.settings["PORTDIR"] + "/" + catpkg) - foo="" - r=re.compile("<[^@]+@gentoo.org>", re.I) - s="\n".join(portage.grabfile("ChangeLog")) - d={} - for x in r.findall(s): - if x not in d: - d[x] = 0 - d[x] += 1 - - l=[(d[x], x) for x in d.keys()] - #l.sort(lambda x,y: cmp(y[0], x[0])) - l.sort(fc) - for x in l: - p = str(x[0]) +" "+ x[1].lstrip("<").rstrip(">") - foo += p[:p.find("@")]+", " - return foo - except: - raise - -def main (): - if len( sys.argv ) < 2: - usage(1) - - for pkg in sys.argv[1:]: - - if sys.argv[1:][:1] == "-": - print "NOT WORKING?=="+sys.argv[1:] - continue - - try: - package_list = portage.portdb.xmatch("match-all", pkg) - if package_list: - - catpkg = portage.pkgsplit(package_list[0])[0] - - print darkgreen("Package: ") + catpkg - check_metadata(package_list[0]) - earch(portage.settings["PORTDIR"] + "/" + catpkg) - #print darkgreen("ChangeLog: ") + grab_changelog_devs(catpkg) - print "" - else: - print "!!! No package '%s'" % pkg - except: - print red("Error: "+pkg+"\n") - - -if __name__ == '__main__': - main() + main(args) + except AmbiguousPackageName as e: + pkgs = e.args[0] + for candidate in pkgs: + print(candidate) + + from gentoolkit import pprinter as pp + from os.path import basename # To get the short name + + print(file=sys.stderr) + print(pp.error("The short ebuild name '%s' is ambiguous. Please specify" % basename(pkgs[0])), + file=sys.stderr, end="") + pp.die(1, "one of the above fully-qualified ebuild names instead.") + except errors.GentoolkitException as err: + from gentoolkit import pprinter as pp + pp.die(1, str(err)) + +# vim: set ts=4 sw=4 tw=79: @@ -2,12 +2,17 @@ # # Copyright 2002-2009 Gentoo Technologies, Inc. # Distributed under the terms of the GNU General Public License v2 or later +# +# $Header$ -"""equery is a flexible utility for Gentoo linux which can display various -information about packages, such as the files they own, their USE flags, +"""equery is a flexible utility for Gentoo linux which can display various +information about packages, such as the files they own, their USE flags, the MD5 sum of each file owned by a given package, and many other things. """ +from __future__ import print_function + +import os import sys # This block ensures that ^C interrupts are handled quietly. try: @@ -16,7 +21,7 @@ try: def exithandler(signum,frame): signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGTERM, signal.SIG_IGN) - print + print() sys.exit(1) signal.signal(signal.SIGINT, exithandler) @@ -24,9 +29,20 @@ try: signal.signal(signal.SIGPIPE, signal.SIG_DFL) except KeyboardInterrupt: - print + print() sys.exit(1) -from gentoolkit import equery +from gentoolkit import equery, errors -equery.main() +try: + equery.main() +except errors.GentoolkitException as err: + if '--debug' in sys.argv or bool(os.getenv('DEBUG', False)): + raise + else: + from gentoolkit import pprinter as pp + sys.stderr.write(pp.error(str(err))) + if err.is_serious: + print() + print("Add '--debug' to global options for traceback.") + sys.exit(1) diff --git a/bin/eshowkw b/bin/eshowkw new file mode 100644 index 0000000..e987cce --- /dev/null +++ b/bin/eshowkw @@ -0,0 +1,9 @@ +#!/usr/bin/python +# vim:fileencoding=utf-8 +# Copyright 2010 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +import sys +from gentoolkit.eshowkw import main as emain + +sys.exit(emain(sys.argv[1:]))
\ No newline at end of file @@ -7,13 +7,53 @@ # Licensed under the GPL v2 PROGRAM_NAME=euse -PROGRAM_VERSION=$(cat /usr/share/gentoolkit/VERSION) +VERSION="svn" -MAKE_CONF_PATH=/etc/make.conf -MAKE_GLOBALS_PATH=/etc/make.globals -MAKE_PROFILE_PATH=/etc/make.profile -MAKE_CONF_BACKUP_PATH=/etc/make.conf.euse_backup -PACKAGE_USE_PATH=/etc/portage/package.use +EPREFIX=${EPREFIX:-$(portageq envvar EPREFIX)} +ETC="${EPREFIX}/etc" +USR_SHARE_PORTAGE="${EPREFIX}/usr/share/portage" + +# define error functions so they can be used immediately +fatal() { + echo -e "ERROR: ${*}" + set +f + exit 1 +} + +error() { + echo -e "ERROR: ${*}" +} + +warn() { + echo -e "WARNING: ${*}" +} + +# /etc/make.conf can now exist in /etc/portage/make.conf, prefer it over /etc/make.conf for changes +if [ -e "${ETC}/portage/make.conf" ]; then + MAKE_CONF_PATH="${ETC}/portage/make.conf" +elif [ -e "${ETC}/make.conf" ]; then + MAKE_CONF_PATH="${ETC}/make.conf" +else + fatal "make.conf does not exist" +fi +MAKE_CONF_BACKUP_PATH="${MAKE_CONF_PATH}.euse_backup" + +# /etc/make.globals has been moved to /usr/share/portage/config/make.globals +if [ -e "${USR_SHARE_PORTAGE}/config/make.globals" ]; then + MAKE_GLOBALS_PATH="${USR_SHARE_PORTAGE}/config/make.globals" +else + MAKE_GLOBALS_PATH="${ETC}/make.globals" +fi + +# /etc/make.profile or /etc/portage/make.profile, if /etc/make.profile exists, it will be used +if [ -e "${ETC}/make.profile" ]; then + MAKE_PROFILE_PATH="${ETC}/make.profile" +elif [ -e "${ETC}/portage/make.profile" ]; then + MAKE_PROFILE_PATH="${ETC}/portage/make.profile" +else + fatal "make.profile does not exist" +fi +PACKAGE_USE_PATH=${ETC}/portage/package.use [ -z "${MODE}" ] && MODE="showhelp" # available operation modes: showhelp, showversion, showdesc, showflags, modify @@ -51,20 +91,6 @@ parse_arguments() { done } -fatal() { - echo -e "${*}" | sed -e "s/^/ERROR: /g" - set +f - exit 1 -} - -error() { - echo -e "${*}" | sed -e "s/^/ERROR: /g" -} - -warn() { - echo -e "${*}" | sed -e "s/^/WARNING: /g" -} - get_real_path() { set -P cd "$1" @@ -79,16 +105,22 @@ check_sanity() { # file permission tests local descdir local make_defaults - + local make_conf + + [[ ! -d "${MAKE_PROFILE_PATH}" || ! -r "${MAKE_PROFILE_PATH}" ]] && error "${MAKE_PROFILE_PATH} is not readable" + # + for make_conf in $(get_all_make_conf); do + [ ! -r "${make_conf}" ] && fatal "${make_conf} is not readable" + done + descdir="$(get_portdir)/profiles" - [ ! -r "${MAKE_CONF_PATH}" ] && fatal "${MAKE_CONF_PATH} is not readable" [ ! -r "${MAKE_GLOBALS_PATH}" ] && fatal "${MAKE_GLOBALS_PATH} is not readable" - [ ! -h "${MAKE_PROFILE_PATH}" ] && fatal "${MAKE_PROFILE_PATH} is not a symlink" [ -z "$(get_portdir)" ] && fatal "\$PORTDIR couldn't be determined" [ ! -d "${descdir}" ] && fatal "${descdir} does not exist or is not a directory" [ ! -r "${descdir}/use.desc" ] && fatal "${descdir}/use.desc is not readable" [ ! -r "${descdir}/use.local.desc" ] && fatal "${descdir}/use.local.desc is not readable" + for make_defaults in $(get_all_make_defaults); do [ ! -r "$make_defaults" ] && fatal "$_make_defaults is not readable" done @@ -98,7 +130,7 @@ check_sanity() { showhelp() { cat << HELP -${PROGRAM_NAME} (${PROGRAM_VERSION}-JJ0) +${PROGRAM_NAME} (${VERSION}) Syntax: ${PROGRAM_NAME} <option> [suboptions] [useflaglist] @@ -128,7 +160,7 @@ HELP showversion() { cat << VER -${PROGRAM_NAME} (${PROGRAM_VERSION}) +${PROGRAM_NAME} (${VERSION}) Written by Marius Mauch Copyright (C) 2004-2009 Gentoo Foundation, Inc. @@ -238,8 +270,10 @@ get_useflags() { ACTIVE_FLAGS[0]="$(reduce_incrementals ${USE})" USE="" - source "${MAKE_CONF_PATH}" - ACTIVE_FLAGS[1]="$(reduce_incrementals ${USE})" + for x in $(get_all_make_conf); do + source "${x}" + ACTIVE_FLAGS[1]="$(reduce_incrementals ${ACTIVE_FLAGS[1]} ${USE})" + done USE="" for x in $(get_all_make_defaults); do source "${x}" @@ -275,7 +309,7 @@ get_portageuseflags() { # get the currently active USE flags as seen by portage, this has to be after # restoring USE or portage won't see the original environment # Bug 181309, emerge may complain if EMERGE_DEFAULT_OPTS="--ask" is set - ACTIVE_FLAGS[9]="$(emerge --ignore-default-opts --info | grep 'USE=' | cut -b 5- | sed -e 's:"::g')" #' + ACTIVE_FLAGS[9]="$(portageq envvar USE)" #' _PORTAGE_USE_FLAGS_CALCULATED=1 } # }}} @@ -379,6 +413,13 @@ get_useflaglist_ebuild() { done } # }}} +# get all make.conf files that exist on the system +get_all_make_conf() { + # At least one of the files exists or we would not have made it this far + for x in ${ETC}/make.conf ${ETC}/portage/make.conf; do + [ -e "${x}" ] && echo "${x}" + done +} # Function: traverse_profile {{{ # General method of collecting the contents of a profile # component by traversing through the cascading profile @@ -631,7 +672,9 @@ get_portdir() { for x in $(get_all_make_defaults); do source "${x}" done - source "${MAKE_CONF_PATH}" + for x in $(get_all_make_conf); do + source "${x}" + done USE="${use_backup}" fi echo "${PORTDIR}" @@ -687,7 +730,7 @@ showdesc() { set -f args="${*:-*}" - + if [ -z "${SCOPE}" ]; then SCOPE="global" showdesc ${args} echo @@ -704,7 +747,7 @@ showdesc() { if [ "${args}" == "*" ]; then args="${useflags[*]}" fi - + set ${args} foundone=0 @@ -718,7 +761,7 @@ showdesc() { foundone=1 fi fi - # local flags are a bit more complicated as there can be multiple + # local flags are a bit more complicated as there can be multiple # entries per flag and we can't pipe into printf if [[ "${SCOPE}" == "local" ]]; then if array_contains "${useflags[*]}" "$1"; then @@ -788,13 +831,13 @@ showinstdesc() { descdir="$(get_portdir)/profiles" echo "************************************************************" - + if [ "${args}" = "*" ]; then args="$(get_useflaglist | sort -u)" fi - + set "${args[@]}" - + while [ -n "${1}" ]; do case "${SCOPE}" in "global") @@ -834,7 +877,7 @@ showinstdesc() { esac shift done - + if [ ${foundone} -lt 1 ]; then echo "no matching entries found" fi @@ -847,13 +890,13 @@ showflags() { local args get_useflags - + args="${*:-*}" - + if [ "${args}" == "*" ]; then args="$(get_useflaglist | sort -u)" fi - + set ${args} get_portageuseflags @@ -1127,11 +1170,11 @@ modify() { set $(get_useflaglist | sort -u) fi fi - + get_useflags - + NEW_MAKE_CONF_USE=" ${ACTIVE_FLAGS[1]} " - + while [ -n "${1}" ]; do if [ "${ACTION}" == "add" ]; then if echo " ${NEW_MAKE_CONF_USE} " | grep " ${1} " > /dev/null; then @@ -1176,13 +1219,13 @@ modify() { shift fi done - + #echo "old flags:" #echo ${ACTIVE_FLAGS[1]} #echo #echo "new flags:" #echo ${NEW_MAKE_CONF_USE} - + # a little loop to add linebreaks so we don't end with one ultra-long line NEW_MAKE_CONF_USE_2="" for x in ${NEW_MAKE_CONF_USE}; do @@ -1195,9 +1238,9 @@ modify() { # make a backup just in case the user doesn't like the new make.conf cp -p "${MAKE_CONF_PATH}" "${MAKE_CONF_BACKUP_PATH}" - + # as sed doesn't really work with multi-line patterns we have to replace USE - # on our own here. Basically just skip everything between USE=" and the + # on our own here. Basically just skip everything between USE=" and the # closing ", printing our new USE line there instead. inuse=0 had_use=0 @@ -1226,7 +1269,7 @@ modify() { echo -ne "${NEW_MAKE_CONF_USE_2%% }" echo '"' fi ) < "${MAKE_CONF_BACKUP_PATH}" | sed -e 's:\\ $:\\:' > "${MAKE_CONF_PATH}" - + echo "${MAKE_CONF_PATH} was modified, a backup copy has been placed at ${MAKE_CONF_BACKUP_PATH}" } # }}} diff --git a/bin/glsa-check b/bin/glsa-check index c24ef23..a8c0188 100755 --- a/bin/glsa-check +++ b/bin/glsa-check @@ -3,35 +3,29 @@ # $Header: $ # This program is licensed under the GPL, version 2 -import os import sys +import os import codecs -try: - import portage -except ImportError: - sys.path.insert(0, "/usr/lib/portage/pym") - import portage +from functools import reduce -try: - from portage.output import * -except ImportError: - from output import * +import portage +from portage.output import * from getopt import getopt, GetoptError __program__ = "glsa-check" __author__ = "Marius Mauch <genone@gentoo.org>" -__version__ = open("/usr/share/gentoolkit/VERSION").read().strip() +__version__ = "svn" optionmap = [ -["-l", "--list", "list all unapplied GLSA"], -["-d", "--dump", "--print", "show all information about the given GLSA"], -["-t", "--test", "test if this system is affected by the given GLSA"], -["-p", "--pretend", "show the necessary commands to apply this GLSA"], -["-f", "--fix", "try to auto-apply this GLSA (experimental)"], +["-l", "--list", "list the GLSAs"], +["-d", "--dump", "--print", "show all information about the GLSAs"], +["-t", "--test", "test if this system is affected by the GLSAs"], +["-p", "--pretend", "show the necessary steps to apply the GLSAs"], +["-f", "--fix", "try to auto-apply the GLSAs (experimental)"], ["-i", "--inject", "inject the given GLSA into the glsa_injected file"], ["-n", "--nocolor", "disable colors (option)"], -["-e", "--emergelike", "do not use a least-change algorithm (option)"], +["-e", "--emergelike", "upgrade to latest version (not least-change, option)"], ["-h", "--help", "show this help message"], ["-V", "--version", "some information about this tool"], ["-v", "--verbose", "print more information (option)"], @@ -55,12 +49,12 @@ try: args, params = getopt(sys.argv[1:], "".join([o[0][1] for o in optionmap]), \ [x[2:] for x in reduce(lambda x,y: x+y, [z[1:-1] for z in optionmap])]) args = [a for a,b in args] - + for option in ["--nocolor", "-n"]: if option in args: nocolor() args.remove(option) - + verbose = False for option in ["--verbose", "-v"]: if option in args: @@ -72,7 +66,7 @@ try: if option in args: list_cve = True args.remove(option) - + least_change = True for option in ["--emergelike", "-e"]: if option in args: @@ -100,7 +94,7 @@ try: if args in [o for o in m[:-1]]: mode = m[1][2:] -except GetoptError, e: +except GetoptError as e: sys.stderr.write("unknown option given: ") sys.stderr.write(str(e)+"\n") mode = "HELP" @@ -112,8 +106,8 @@ if len(params) <= 0 and mode in ["fix", "test", "pretend", "dump", "inject", "ma sys.stderr.write("(specify \"all\" as parameter)\n\n") mode = "HELP" elif len(params) <= 0 and mode == "list": - params.append("new") - + params.append("affected") + # show help message if mode == "help" or mode == "HELP": msg = "Syntax: glsa-check <option> [glsa-list]\n\n" @@ -123,7 +117,7 @@ if mode == "help" or mode == "HELP": msg += "\t" + o + "\n" msg += "\nglsa-list can contain an arbitrary number of GLSA ids, \n" msg += "filenames containing GLSAs or the special identifiers \n" - msg += "'all', 'new' and 'affected'\n" + msg += "'all' and 'affected'\n" if mode == "help": sys.stdout.write(msg) sys.exit(0) @@ -154,8 +148,8 @@ glsaconfig = checkconfig(portage.config(clone=portage.settings)) if quiet: glsaconfig["EMERGE_OPTS"] += " --quiet" -vardb = portage.db["/"]["vartree"].dbapi -portdb = portage.db["/"]["porttree"].dbapi +vardb = portage.db[portage.root]["vartree"].dbapi +portdb = portage.db[portage.root]["porttree"].dbapi # Check that we really have a glsa dir to work on if not (os.path.exists(glsaconfig["GLSA_DIR"]) and os.path.isdir(glsaconfig["GLSA_DIR"])): @@ -173,18 +167,19 @@ todolist = [e for e in completelist if e not in checklist] glsalist = [] if "new" in params: - glsalist = todolist params.remove("new") - + sys.stderr.write("Warning: The 'new' glsa-list target has been removed, using 'affected'.\n") + params.append("affected") + if "all" in params: glsalist = completelist params.remove("all") + if "affected" in params: - # replaced completelist with todolist on request of wschlich for x in todolist: try: myglsa = Glsa(x, glsaconfig) - except (GlsaTypeException, GlsaFormatException), e: + except (GlsaTypeException, GlsaFormatException) as e: if verbose: sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (x, e))) continue @@ -201,6 +196,13 @@ for p in params[:]: glsalist.extend([g for g in params if g not in glsalist]) def summarylist(myglsalist, fd1=sys.stdout, fd2=sys.stderr, encoding="utf-8"): + # Get to the raw streams in py3k before wrapping them with an encoded writer + # to avoid writing bytes to a text stream (stdout/stderr are text streams + # by default in py3k) + if hasattr(fd1, "buffer"): + fd1 = fd1.buffer + if hasattr(fd2, "buffer"): + fd2 = fd2.buffer fd1 = codecs.getwriter(encoding)(fd1) fd2 = codecs.getwriter(encoding)(fd2) if not quiet: @@ -212,7 +214,7 @@ def summarylist(myglsalist, fd1=sys.stdout, fd2=sys.stderr, encoding="utf-8"): for myid in myglsalist: try: myglsa = Glsa(myid, glsaconfig) - except (GlsaTypeException, GlsaFormatException), e: + except (GlsaTypeException, GlsaFormatException) as e: if verbose: fd2.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e))) continue @@ -233,7 +235,7 @@ def summarylist(myglsalist, fd1=sys.stdout, fd2=sys.stderr, encoding="utf-8"): fd1.write(color(myglsa.nr) + " " + color(status) + " " + color(access) + myglsa.title + " (") if not verbose: - for pkg in myglsa.packages.keys()[:3]: + for pkg in list(myglsa.packages.keys())[:3]: fd1.write(" " + pkg + " ") if len(myglsa.packages) > 3: fd1.write("... ") @@ -258,17 +260,21 @@ if mode in ["dump", "fix", "inject", "pretend"]: for myid in glsalist: try: myglsa = Glsa(myid, glsaconfig) - except (GlsaTypeException, GlsaFormatException), e: + except (GlsaTypeException, GlsaFormatException) as e: if verbose: sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e))) continue if mode == "dump": myglsa.dump() elif mode == "fix": - sys.stdout.write("Fixing GLSA "+myid+"\n") + if not quiet: + sys.stdout.write("Fixing GLSA "+myid+"\n") if not myglsa.isVulnerable(): - sys.stdout.write(">>> no vulnerable packages installed\n") + if not quiet: + sys.stdout.write(">>> no vulnerable packages installed\n") else: + if quiet: + sys.stdout.write("Fixing GLSA "+myid+"\n") mergelist = myglsa.getMergeList(least_change=least_change) if mergelist == []: sys.stdout.write(">>> cannot fix GLSA, no unaffected packages available\n") @@ -287,24 +293,36 @@ if mode in ["dump", "fix", "inject", "pretend"]: exitcode >>= 8 if exitcode: sys.exit(exitcode) - if len(mergelist): - sys.stdout.write("\n") + if len(mergelist): + sys.stdout.write("\n") elif mode == "pretend": - sys.stdout.write("Checking GLSA "+myid+"\n") + if not quiet: + sys.stdout.write("Checking GLSA "+myid+"\n") if not myglsa.isVulnerable(): - sys.stdout.write(">>> no vulnerable packages installed\n") + if not quiet: + sys.stdout.write(">>> no vulnerable packages installed\n") else: + if quiet: + sys.stdout.write("Checking GLSA "+myid+"\n") mergedict = {} for (vuln, update) in myglsa.getAffectionTable(least_change=least_change): mergedict.setdefault(update, []).append(vuln) - - sys.stdout.write(">>> The following updates will be performed for this GLSA:\n") - for pkg in mergedict: - if pkg != "": - sys.stdout.write(" " + pkg + " (vulnerable: " + ", ".join(mergedict[pkg]) + ")\n") + + # first, extract the atoms that cannot be upgraded (where key == "") + no_upgrades = [] if "" in mergedict: - sys.stdout.write("\n>>> For the following packages, no upgrade path exists:\n") - sys.stdout.write(" " + ", ".join(mergedict[""])) + no_upgrades = mergedict[""] + del mergedict[""] + + # see if anything is left that can be upgraded + if mergedict: + sys.stdout.write(">>> Updates that will be performed:\n") + for (upd, vuln) in mergedict.items(): + sys.stdout.write(" " + green(upd) + " (vulnerable: " + red(", ".join(vuln)) + ")\n") + + if no_upgrades: + sys.stdout.write(">>> No upgrade path exists for these packages:\n") + sys.stdout.write(" " + red(", ".join(no_upgrades)) + "\n") elif mode == "inject": sys.stdout.write("injecting " + myid + "\n") myglsa.inject() @@ -316,7 +334,7 @@ if mode == "test": for myid in glsalist: try: myglsa = Glsa(myid, glsaconfig) - except (GlsaTypeException, GlsaFormatException), e: + except (GlsaTypeException, GlsaFormatException) as e: if verbose: sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e))) continue @@ -338,9 +356,9 @@ if mode == "mail": import portage.mail as portage_mail except ImportError: import portage_mail - + import socket - from StringIO import StringIO + from io import StringIO try: from email.mime.text import MIMEText except ImportError: @@ -353,7 +371,7 @@ if mode == "mail": myrecipient = glsaconfig["PORTAGE_ELOG_MAILURI"].split()[0] else: myrecipient = "root@localhost" - + if "PORTAGE_ELOG_MAILFROM" in glsaconfig: myfrom = glsaconfig["PORTAGE_ELOG_MAILFROM"] else: @@ -373,7 +391,7 @@ if mode == "mail": for myid in glsalist: try: myglsa = Glsa(myid, glsaconfig) - except (GlsaTypeException, GlsaFormatException), e: + except (GlsaTypeException, GlsaFormatException) as e: if verbose: sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e))) continue @@ -382,12 +400,12 @@ if mode == "mail": myattachments.append(MIMEText(str(myfd.getvalue()), _charset="utf8")) myfd.close() - if glsalist or not quiet: + if glsalist or not quiet: mymessage = portage_mail.create_message(myfrom, myrecipient, mysubject, summary, myattachments) portage_mail.send_mail(glsaconfig, mymessage) - + sys.exit(0) - + # something wrong here, all valid paths are covered with sys.exit() sys.stderr.write("nothing more to do\n") sys.exit(2) diff --git a/bin/revdep-rebuild b/bin/revdep-rebuild index b44dadc..e034124 100755 --- a/bin/revdep-rebuild +++ b/bin/revdep-rebuild @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 1999-2008 Gentoo Foundation +# Copyright 1999-2010 Gentoo Foundation # revdep-rebuild: Reverse dependency rebuilder. # Original Author: Stanislav Brabec @@ -18,6 +18,7 @@ unset GREP_OPTIONS # Readonly variables: declare -r APP_NAME="${0##*/}" # The name of this application +declare -r VERSION="svn" declare -r OIFS="$IFS" # Save the IFS declare -r ENV_FILE=0_env.rr # Contains environment variables declare -r FILES_FILE=1_files.rr # Contains a list of files to search @@ -89,11 +90,12 @@ declare WORKING_DIR # Working directory where cache files are kept main() { # preliminary setup + portage_settings get_opts "$@" setup_portage setup_search_paths_and_masks get_search_env - echo + [[ $QUIET -ne 1 ]] && echo # Search for broken binaries get_files @@ -163,6 +165,11 @@ EW print_usage() { cat << EOF +${APP_NAME}: (${VERSION}) + +Copyright (C) 2003-2010 Gentoo Foundation, Inc. +This is free software; see the source for copying conditions. + Usage: $APP_NAME [OPTIONS] [--] [EMERGE_OPTIONS] Broken reverse dependency rebuilder. @@ -189,6 +196,7 @@ Calls emerge, options after -- are ignored by $APP_NAME and passed directly to emerge. Report bugs to <http://bugs.gentoo.org> + EOF } ## @@ -233,7 +241,17 @@ clean_var() { die() { local status=$1 shift - eerror "$@" + + # Check if eerror has been loaded. + # Its loaded _after_ opt parsing but not before due to RC_NOCOLOR. + type eerror &> /dev/null + + if [[ $? -eq 0 ]]; + then + eerror "$@" + else + echo " * ${@}" >> /dev/stderr + fi exit $status } ## @@ -246,19 +264,35 @@ clean_exit() { builtin cd; rmdir "$WORKING_DIR" fi fi - echo - einfo "$OK_TEXT... All done. " + if [[ $QUIET -ne 1 ]]; + then + echo + einfo "$OK_TEXT... All done. " + fi exit 0 } ## # Get the name of the package that owns a file or list of files given as args. +# NOTE: depends on app-misc/realpath! get_file_owner() { local IFS=$'\n' - # ${*/%/ } adds a space to the end of each object name to prevent false + + rpath=$(realpath "${*}" 2>/dev/null) + # To ensure we always have something in rpath... + [[ -z $rpath ]] && rpath=${*} + + # Workaround for bug 280341 + mlib=$(echo ${*}|sed 's:/lib/:/lib64/:') + [[ "${*}" == "${mlib}" ]] && mlib=$(echo ${*}|sed 's:/lib64/:/lib/:') + + # Add a space to the end of each object name to prevent false # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460). - find -L /var/db/pkg -name CONTENTS -print0 | - xargs -0 grep -Fl "${*/%/ }" | - sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:' + # The same for "${rpath} ". + # Don't match an entry with a '-' at the start of the package name. This + # prevents us from matching invalid -MERGING entries. (bug #338031) + find -L /var/db/pkg -type f -name CONTENTS -print0 | + xargs -0 grep -m 1 -Fl -e "${*} " -e "${rpath} " -e "${mlib} " | + sed 's:/var/db/pkg/\(.*\)/\([^-].*\)/CONTENTS:\1/\2:' } ## # Normalize some EMERGE_OPTIONS @@ -273,7 +307,6 @@ normalize_emerge_opts() { setup_color() { # This should still work if NOCOLOR is set by the -C flag or in the user's # environment. - export NOCOLOR=$(portageq envvar NOCOLOR) [[ $NOCOLOR = yes || $NOCOLOR = true ]] && export RC_NOCOLOR=yes # HACK! (grr) . /etc/init.d/functions.sh } @@ -342,10 +375,10 @@ get_longopts() { --no-ld-path) unset FULL_LD_PATH;; --no-order) unset ORDER_PKGS;; --no-progress) progress() { :; };; - --pretend) EMERGE_OPTIONS+=("--pretend");; - --quiet) echo_v() { :; } - progress() { :; } - quiet=1 + --pretend) EMERGE_OPTIONS+=("--pretend") + PRETEND=1;; + --quiet) progress() { :; } + QUIET=1 EMERGE_OPTIONS+=($1);; --verbose) VERBOSE=1 EMERGE_OPTIONS+=("--verbose");; @@ -381,10 +414,10 @@ get_shortopts() { l) unset FULL_LD_PATH;; o) unset ORDER_PKGS;; P) progress() { :; };; - p) EMERGE_OPTIONS+=("--pretend");; - q) echo_v() { :; } - progress() { :; } - quiet=1 + p) EMERGE_OPTIONS+=("--pretend") + PRETEND=1;; + q) progress() { :; } + QUIET=1 EMERGE_OPTIONS+=("--quiet");; v) VERBOSE=1 EMERGE_OPTIONS+=("--verbose");; @@ -520,7 +553,7 @@ verify_tmpdir() { get_search_env() { local new_env local old_env - local uid=$(python -c 'import os; import pwd; print pwd.getpwuid(os.getuid())[0]') + local uid=$(python -c 'import os; import pwd; print(pwd.getpwuid(os.getuid())[0])') # Find a place to put temporary files if [[ "$uid" == "root" ]]; then local tmp_target="/var/cache/${APP_NAME}" @@ -606,16 +639,19 @@ get_search_env() { [[ $VERBOSE ]] && echo $'\n'"$APP_NAME environment:"$'\n'"$new_env" - echo - einfo "Checking reverse dependencies" - einfo "Packages containing binaries and libraries $HEAD_TEXT" - einfo "will be emerged." + if [[ $QUIET -ne 1 ]]; + then + echo + einfo "Checking reverse dependencies" + einfo "Packages containing binaries and libraries $HEAD_TEXT" + einfo "will be emerged." + fi } get_files() { - einfo "Collecting system binaries and libraries" + [[ $QUIET -ne 1 ]] && einfo "Collecting system binaries and libraries" if [[ -r "$FILES_FILE" && -s "$FILES_FILE" ]]; then - einfo "Found existing $FILES_FILE" + [[ $QUIET -ne 1 ]] && einfo "Found existing $FILES_FILE" else # Be safe and remove any extraneous temporary files # Don't remove 0_env.rr - The first file in the array @@ -633,29 +669,48 @@ get_files() { -name '*.so' -o -name '*.so.*' -o -name '*.la' \) -print 2> /dev/null | sort -u > "$FILES_FILE" || die $? "find failed to list binary files (This is a bug.)" - einfo "Generated new $FILES_FILE" + [[ $QUIET -ne 1 ]] && einfo "Generated new $FILES_FILE" fi } +parse_ld_so_conf() { + # FIXME: not safe for paths with spaces + local include + for path in $(sed '/^#/d;s/#.*$//' < /etc/ld.so.conf); do + if [[ $include = true ]]; then + for include_path in $(sed '/^#/d;s/#.*$//' /etc/${path} 2>/dev/null); do + echo $include_path + done + include="" + continue + fi + if [[ $path != include ]]; then + echo $path + else + include="true" + continue + fi + done +} get_ldpath() { local COMPLETE_LD_LIBRARY_PATH [[ $SEARCH_BROKEN && $FULL_LD_PATH ]] || return - einfo 'Collecting complete LD_LIBRARY_PATH' + [[ $QUIET -ne 1 ]] && einfo 'Collecting complete LD_LIBRARY_PATH' if [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]]; then - einfo "Found existing $LDPATH_FILE." + [[ $QUIET -ne 1 ]] && einfo "Found existing $LDPATH_FILE." else clean_trap "$LDPATH_FILE" # Ensure that the "trusted" lib directories are at the start of the path COMPLETE_LD_LIBRARY_PATH=( /lib* /usr/lib* - $(sed '/^#/d;s/#.*$//' < /etc/ld.so.conf) + $(parse_ld_so_conf) $(sed 's:/[^/]*$::' < "$FILES_FILE" | sort -ru) ) IFS=':' COMPLETE_LD_LIBRARY_PATH="${COMPLETE_LD_LIBRARY_PATH[*]}" IFS="$OIFS" echo "$COMPLETE_LD_LIBRARY_PATH" > "$LDPATH_FILE" - einfo "Generated new $LDPATH_FILE" + [[ $QUIET -ne 1 ]] && einfo "Generated new $LDPATH_FILE" fi } main_checks() { @@ -671,9 +726,9 @@ main_checks() { die 1 "Unable to find $LDPATH_FILE" COMPLETE_LD_LIBRARY_PATH=$(<"$LDPATH_FILE") fi - einfo "Checking dynamic linking $WORKING_TEXT" + [[ $QUIET -ne 1 ]] && einfo "Checking dynamic linking $WORKING_TEXT" if [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]]; then - einfo "Found existing $BROKEN_FILE." + [[ $QUIET -ne 1 ]] && einfo "Found existing $BROKEN_FILE." else clean_trap "$BROKEN_FILE" "$ERRORS_FILE" files=($(<"$FILES_FILE")) @@ -686,10 +741,10 @@ main_checks() { ldd_status=$? # TODO: Check this for problems with sort # HACK: if LD_LIBRARY_MASK is null or undefined grep -vF doesn't work if grep -vF "${LD_LIBRARY_MASK:=$'\a'}" <<< "$ldd_output" | - grep -q "$SONAME_SEARCH"; then + grep -q -E "$SONAME_SEARCH"; then if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then if LD_LIBRARY_PATH="$COMPLETE_LD_LIBRARY_PATH" ldd "$target_file" 2>/dev/null | - grep -vF "$LD_LIBRARY_MASK" | grep -q "$SONAME_SEARCH"; then + grep -vF "$LD_LIBRARY_MASK" | grep -q -E "$SONAME_SEARCH"; then # FIXME: I hate duplicating code # Only build missing direct dependencies MISSING_LIBS=$( @@ -710,8 +765,8 @@ main_checks() { # FIXME: I hate duplicating code # Only rebuild for direct dependencies MISSING_LIBS=$( - expr="/$SONAME_SEARCH/s/^[[:space:]]*\([^[:space:]]*\).*$/\1/p" - sort -u <<< "$ldd_output" | sed -n "$expr" + expr="s/^[[:space:]]*\([^[:space:]]*\).*$/\1/p" + sort -u <<< "$ldd_output" | grep -E "$SONAME" | sed -n "$expr" ) REQUIRED_LIBS=$( expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p'; @@ -768,7 +823,7 @@ main_checks() { progress $((++i)) $numFiles $target_file || progress $((++i)) $numFiles done - if [[ $SEARCH_BROKEN ]]; then + if [[ $SEARCH_BROKEN && -f $ERRORS_FILE ]]; then # Look for missing version while read target_file; do echo "obj $target_file" >> "$BROKEN_FILE" @@ -786,7 +841,7 @@ main_checks() { fi [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]] || clean_exit sort -u "$BROKEN_FILE" -o "$BROKEN_FILE" - einfo "Generated new $BROKEN_FILE" + [[ $QUIET -ne 1 ]] && einfo "Generated new $BROKEN_FILE" fi } get_packages() { @@ -814,7 +869,7 @@ get_packages() { echo_v " $target_file -> (none)" fi done < "$BROKEN_FILE" - einfo "Generated new $RAW_FILE and $OWNERS_FILE" + [[ $QUIET -ne 1 ]] && einfo "Generated new $RAW_FILE and $OWNERS_FILE" fi # if we find '(none)' on every line, exit out if ! grep -qvF '(none)' "$OWNERS_FILE"; then @@ -831,12 +886,12 @@ get_packages() { fi } clean_packages() { - einfo 'Cleaning list of packages to rebuild' + [[ $QUIET -ne 1 ]] && einfo 'Cleaning list of packages to rebuild' if [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then - einfo "Found existing $PKGS_FILE" + [[ $QUIET -ne 1 ]] && einfo "Found existing $PKGS_FILE" else sort -u "$RAW_FILE" > "$PKGS_FILE" - einfo "Generated new $PKGS_FILE" + [[ $QUIET -ne 1 ]] && einfo "Generated new $PKGS_FILE" fi } assign_packages_to_ebuilds() { @@ -855,7 +910,7 @@ assign_packages_to_ebuilds() { SLOT=$(</var/db/pkg/$EXACT_PKG/SLOT) echo "$PKG:$SLOT" done < "$PKGS_FILE" > "$EBUILDS_FILE" - einfo "Generated new $EBUILDS_FILE" + [[ $QUIET -ne 1 ]] && einfo "Generated new $EBUILDS_FILE" else einfo 'Nothing to rebuild.' die 1 '(The program should have already quit, so this is a minor bug.)' @@ -869,7 +924,7 @@ get_exact_ebuilds() { rebuildList=" $(<"$BROKEN_FILE") " rebuildList=(${rebuildList//[[:space:]]obj[[:space:]]/ }) get_file_owner "${rebuildList[@]}" | sed 's/^/=/' > "$EBUILDS_FILE" - einfo "Generated new $EBUILDS_FILE" + [[ $QUIET -ne 1 ]] && einfo "Generated new $EBUILDS_FILE" else einfo 'Nothing to rebuild.' die 1 '(The program should have already quit, so this is a minor bug.)' @@ -893,7 +948,7 @@ get_build_order() { einfo 'Skipping package ordering' return fi - einfo 'Evaluating package order' + [[ $QUIET -ne 1 ]] && einfo 'Evaluating package order' if [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]]; then einfo "Found existing $ORDER_FILE" else @@ -952,7 +1007,7 @@ get_build_order() { die 1 '(The program should have already quit, so this is a minor bug.)' fi fi - [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]] && einfo "Generated new $ORDER_FILE" + [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" && $QUIET -ne 1 ]] && einfo "Generated new $ORDER_FILE" } show_unowned_files() { @@ -964,14 +1019,31 @@ show_unowned_files() { done < "$OWNERS_FILE" | gawk '!s[$0]++' # (omit dupes) fi } + +# Get multiple portage variables at once to speedup revdep-rebuild. +portage_settings() { + local ORIG_SEARCH_DIRS="$SEARCH_DIRS" + local ORIG_SEARCH_DIRS_MASK="$SEARCH_DIRS_MASK" + local ORIG_LD_LIBRARY_MASK="$LD_LIBRARY_MASK" + unset SEARCH_DIRS + unset SEARCH_DIRS_MASK + unset LD_LIBRARY_MASK + + eval $(portageq envvar -v PORTAGE_ROOT PORTAGE_NICENESS EMERGE_DEFAULT_OPTS NOCOLOR SEARCH_DIRS SEARCH_DIRS_MASK LD_LIBRARY_MASK) + export NOCOLOR + + SEARCH_DIRS="$ORIG_SEARCH_DIRS $SEARCH_DIRS" + SEARCH_DIRS_MASK="$ORIG_SEARCH_DIRS_MASK $SEARCH_DIRS_MASK" + LD_LIBRARY_MASK="$ORIG_LD_LIBRARY_MASK $LD_LIBRARY_MASK" +} + ## # Setup portage and the search paths setup_portage() { - local PORTAGE_NICENESS=$(portageq envvar PORTAGE_NICENESS) - PORTAGE_ROOT=$(portageq envvar ROOT) - - # Obey PORTAGE_NICENESS + # Obey PORTAGE_NICENESS (which is incremental to the current nice value) if [[ $PORTAGE_NICENESS ]]; then + current_niceness=$(nice) + let PORTAGE_NICENESS=${current_niceness}+${PORTAGE_NICENESS} renice $PORTAGE_NICENESS $$ > /dev/null # Since we have already set our nice value for our processes, # reset PORTAGE_NICENESS to zero to avoid having emerge renice again. @@ -986,7 +1058,7 @@ setup_portage() { setup_search_paths_and_masks() { local configfile sdir mdir skip_me filter_SEARCH_DIRS - einfo "Configuring search environment for $APP_NAME" + [[ $QUIET -ne 1 ]] && einfo "Configuring search environment for $APP_NAME" # Update the incremental variables using /etc/profile.env, /etc/ld.so.conf, # portage, and the environment @@ -994,9 +1066,9 @@ setup_search_paths_and_masks() { # Read the incremental variables from environment and portage # Until such time as portage supports these variables as incrementals # The value will be what is in /etc/make.conf - SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS) - SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK) - LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK) +# SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS) +# SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK) +# LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK) # Add the defaults if [[ -d /etc/revdep-rebuild ]]; then @@ -1018,7 +1090,7 @@ setup_search_paths_and_masks() { # Get the directories from /etc/ld.so.conf if [[ -r /etc/ld.so.conf && -s /etc/ld.so.conf ]]; then - SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' /etc/ld.so.conf) + SEARCH_DIRS+=" "$(parse_ld_so_conf) fi # Set the final variables @@ -1048,8 +1120,8 @@ rebuild() { trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM - einfo 'All prepared. Starting rebuild' - echo "emerge --oneshot ${EMERGE_OPTIONS[@]} $REBUILD_LIST" + [[ $QUIET -ne 1 ]] && einfo 'All prepared. Starting rebuild' + echo "emerge --oneshot ${EMERGE_DEFAULT_OPTS} ${EMERGE_OPTIONS[@]} $REBUILD_LIST" is_real_merge && countdown 10 @@ -1058,7 +1130,7 @@ rebuild() { # Run in background to correctly handle Ctrl-C { - EMERGE_DEFAULT_OPTS="--oneshot ${EMERGE_OPTIONS[@]}" emerge $REBUILD_LIST <&6 + emerge --oneshot ${EMERGE_DEFAULT_OPTS} ${EMERGE_OPTIONS[@]} $REBUILD_LIST <&6 echo $? > "$STATUS_FILE" } & wait @@ -1069,7 +1141,7 @@ rebuild() { ## # Finish up cleanup() { - if (( $(<"$STATUS_FILE") != 0 )); then + if [[ (( $(<"$STATUS_FILE") != 0 )) && ! is_real_merge ]]; then ewarn ewarn "$APP_NAME failed to emerge all packages." ewarn 'you have the following choices:' @@ -1104,7 +1176,7 @@ cleanup() { if [[ -r "$OWNERS_FILE" && -s "$OWNERS_FILE" ]]; then show_unowned_files fi - [[ $KEEP_TEMP ]] || rm "${FILES[@]}" + [[ $KEEP_TEMP ]] || rm -f "${FILES[@]}" else einfo 'Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.' fi |
