diff options
Diffstat (limited to 'pym/gentoolkit/eclean/cli.py')
-rw-r--r-- | pym/gentoolkit/eclean/cli.py | 1099 |
1 files changed, 630 insertions, 469 deletions
diff --git a/pym/gentoolkit/eclean/cli.py b/pym/gentoolkit/eclean/cli.py index e31fde9..bb6deeb 100644 --- a/pym/gentoolkit/eclean/cli.py +++ b/pym/gentoolkit/eclean/cli.py @@ -4,10 +4,11 @@ # Distributed under the terms of the GNU General Public License v2 -__author__ = "Thomas de Grenier de Latour (tgl), " + \ - "modular re-write by: Brian Dolbec (dol-sen)" -__email__ = "degrenier@easyconnect.fr, " + \ - "brian.dolbec@gmail.com" +__author__ = ( + "Thomas de Grenier de Latour (tgl), " + + "modular re-write by: Brian Dolbec (dol-sen)" +) +__email__ = "degrenier@easyconnect.fr, " + "brian.dolbec@gmail.com" __version__ = "git" __productname__ = "eclean" __description__ = "A cleaning tool for Gentoo distfiles and binaries." @@ -23,494 +24,654 @@ import portage from portage.output import white, yellow, turquoise, green import gentoolkit.pprinter as pp -from gentoolkit.eclean.search import (DistfilesSearch, - findPackages, port_settings, pkgdir) -from gentoolkit.eclean.exclude import (parseExcludeFile, - ParseExcludeFileException) +from gentoolkit.eclean.search import ( + DistfilesSearch, + findPackages, + port_settings, + pkgdir, +) +from gentoolkit.eclean.exclude import parseExcludeFile, ParseExcludeFileException from gentoolkit.eclean.clean import CleanUp from gentoolkit.eclean.output import OutputControl -#from gentoolkit.eclean.dbapi import Dbapi + +# from gentoolkit.eclean.dbapi import Dbapi from gentoolkit.eprefix import EPREFIX + def printVersion(): - """Output the version info.""" - 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") + """Output the version info.""" + 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") def printUsage(_error=None, help=None): - """Print help message. May also print partial help to stderr if an - error from {'options','actions'} is specified.""" - - 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': - print( pp.error("Wrong time specification"), file=out) - print( "Time specification should be an integer followed by a"+ - " single letter unit.", file=out) - print( "Available units are: y (years), m (months), w (weeks), "+ - "d (days) and h (hours).", file=out) - print( "For instance: \"1y\" is \"one year\", \"2w\" is \"two"+ - " weeks\", etc. ", file=out) - return - if _error == 'size': - print( pp.error("Wrong size specification"), file=out) - print( "Size specification should be an integer followed by a"+ - " single letter unit.", file=out) - print( "Available units are: G, M, K and B.", file=out) - print("For instance: \"10M\" is \"ten megabytes\", \"200K\" "+ - "is \"two hundreds kilobytes\", etc.", file=out) - return - if _error in ('global-options', 'packages-options', 'distfiles-options', \ - 'merged-packages-options', 'merged-distfiles-options',): - print( pp.error("Wrong option on command line."), file=out) - print( file=out) - elif _error == 'actions': - print( pp.error("Wrong or missing action name on command line."), file=out) - print( file=out) - print( white("Usage:"), file=out) - if _error in ('actions','global-options', 'packages-options', \ - 'distfiles-options') or help == 'all': - print( " "+turquoise(__productname__), - yellow("[global-option] ..."), - green("<action>"), - yellow("[action-option] ..."), file=out) - if _error == 'merged-distfiles-options' or help in ('all','distfiles'): - print( " "+turquoise(__productname__+'-dist'), - yellow("[global-option, distfiles-option] ..."), file=out) - if _error == 'merged-packages-options' or help in ('all','packages'): - print( " "+turquoise(__productname__+'-pkg'), - yellow("[global-option, packages-option] ..."), file=out) - if _error in ('global-options', 'actions'): - print( " "+turquoise(__productname__), - yellow("[--help, --version]"), file=out) - if help == 'all': - print( " "+turquoise(__productname__+"(-dist,-pkg)"), - yellow("[--help, --version]"), file=out) - if _error == 'merged-packages-options' or help == 'packages': - print( " "+turquoise(__productname__+'-pkg'), - yellow("[--help, --version]"), file=out) - if _error == 'merged-distfiles-options' or help == 'distfiles': - print( " "+turquoise(__productname__+'-dist'), - yellow("[--help, --version]"), file=out) - print(file=out) - if _error in ('global-options', 'merged-packages-options', \ - 'merged-distfiles-options') or help: - print( "Available global", yellow("options")+":", file=out) - print( yellow(" -C, --nocolor")+ - " - turn off colors on output", file=out) - print( yellow(" -d, --deep")+ - " - only keep the minimum for a reinstallation", file=out) - print( yellow(" -e, --exclude-file=<path>")+ - " - path to the exclusion file", file=out) - print( yellow(" -i, --interactive")+ - " - ask confirmation before deletions", file=out) - print( yellow(" -n, --package-names")+ - " - protect all versions (when --deep)", file=out) - print( yellow(" -p, --pretend")+ - " - only display what would be cleaned", file=out) - print( yellow(" -q, --quiet")+ - " - be as quiet as possible", file=out) - print( yellow(" -t, --time-limit=<time>")+ - " - don't delete files modified since "+yellow("<time>"), file=out) - print( " "+yellow("<time>"), "is a duration: \"1y\" is"+ - " \"one year\", \"2w\" is \"two weeks\", etc. ", file=out) - print( " "+"Units are: y (years), m (months), w (weeks), "+ - "d (days) and h (hours).", file=out) - print( yellow(" -h, --help")+ \ - " - display the help screen", file=out) - print( yellow(" -V, --version")+ - " - display version info", file=out) - print( file=out) - if _error == 'actions' or help == 'all': - print( "Available", green("actions")+":", file=out) - print( green(" packages")+ - " - clean outdated binary packages from PKGDIR", file=out) - print( green(" distfiles")+ - " - clean outdated packages sources files from DISTDIR", file=out) - print( file=out) - if _error in ('packages-options','merged-packages-options') \ - or help in ('all','packages'): - print( "Available", yellow("options"),"for the", - green("packages"),"action:", file=out) - print( yellow(" --changed-deps")+ - " - delete packages for which ebuild dependencies have changed", file=out) - print( yellow(" -i, --ignore-failure")+ - " - ignore failure to locate PKGDIR", file=out) - print( file=out) - if _error in ('distfiles-options', 'merged-distfiles-options') \ - or help in ('all','distfiles'): - print("Available", yellow("options"),"for the", - green("distfiles"),"action:", file=out) - print( yellow(" -f, --fetch-restricted")+ - " - protect fetch-restricted files (when --deep)", file=out) - print( yellow(" -s, --size-limit=<size>")+ - " - don't delete distfiles bigger than "+yellow("<size>"), file=out) - print( " "+yellow("<size>"), "is a size specification: "+ - "\"10M\" is \"ten megabytes\", \"200K\" is", file=out) - print( " "+"\"two hundreds kilobytes\", etc. Units are: "+ - "G, M, K and B.", file=out) - print( file=out) - print( "More detailed instruction can be found in", - turquoise("`man %s`" % __productname__), file=out) + """Print help message. May also print partial help to stderr if an + error from {'options','actions'} is specified.""" + + 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": + print(pp.error("Wrong time specification"), file=out) + print( + "Time specification should be an integer followed by a" + + " single letter unit.", + file=out, + ) + print( + "Available units are: y (years), m (months), w (weeks), " + + "d (days) and h (hours).", + file=out, + ) + print( + 'For instance: "1y" is "one year", "2w" is "two' + ' weeks", etc. ', + file=out, + ) + return + if _error == "size": + print(pp.error("Wrong size specification"), file=out) + print( + "Size specification should be an integer followed by a" + + " single letter unit.", + file=out, + ) + print("Available units are: G, M, K and B.", file=out) + print( + 'For instance: "10M" is "ten megabytes", "200K" ' + + 'is "two hundreds kilobytes", etc.', + file=out, + ) + return + if _error in ( + "global-options", + "packages-options", + "distfiles-options", + "merged-packages-options", + "merged-distfiles-options", + ): + print(pp.error("Wrong option on command line."), file=out) + print(file=out) + elif _error == "actions": + print(pp.error("Wrong or missing action name on command line."), file=out) + print(file=out) + print(white("Usage:"), file=out) + if ( + _error in ("actions", "global-options", "packages-options", "distfiles-options") + or help == "all" + ): + print( + " " + turquoise(__productname__), + yellow("[global-option] ..."), + green("<action>"), + yellow("[action-option] ..."), + file=out, + ) + if _error == "merged-distfiles-options" or help in ("all", "distfiles"): + print( + " " + turquoise(__productname__ + "-dist"), + yellow("[global-option, distfiles-option] ..."), + file=out, + ) + if _error == "merged-packages-options" or help in ("all", "packages"): + print( + " " + turquoise(__productname__ + "-pkg"), + yellow("[global-option, packages-option] ..."), + file=out, + ) + if _error in ("global-options", "actions"): + print(" " + turquoise(__productname__), yellow("[--help, --version]"), file=out) + if help == "all": + print( + " " + turquoise(__productname__ + "(-dist,-pkg)"), + yellow("[--help, --version]"), + file=out, + ) + if _error == "merged-packages-options" or help == "packages": + print( + " " + turquoise(__productname__ + "-pkg"), + yellow("[--help, --version]"), + file=out, + ) + if _error == "merged-distfiles-options" or help == "distfiles": + print( + " " + turquoise(__productname__ + "-dist"), + yellow("[--help, --version]"), + file=out, + ) + print(file=out) + if ( + _error + in ("global-options", "merged-packages-options", "merged-distfiles-options") + or help + ): + print("Available global", yellow("options") + ":", file=out) + print( + yellow(" -C, --nocolor") + " - turn off colors on output", + file=out, + ) + print( + yellow(" -d, --deep") + + " - only keep the minimum for a reinstallation", + file=out, + ) + print( + yellow(" -e, --exclude-file=<path>") + " - path to the exclusion file", + file=out, + ) + print( + yellow(" -i, --interactive") + + " - ask confirmation before deletions", + file=out, + ) + print( + yellow(" -n, --package-names") + + " - protect all versions (when --deep)", + file=out, + ) + print( + yellow(" -p, --pretend") + + " - only display what would be cleaned", + file=out, + ) + print( + yellow(" -q, --quiet") + " - be as quiet as possible", + file=out, + ) + print( + yellow(" -t, --time-limit=<time>") + + " - don't delete files modified since " + + yellow("<time>"), + file=out, + ) + print( + " " + yellow("<time>"), + 'is a duration: "1y" is' + ' "one year", "2w" is "two weeks", etc. ', + file=out, + ) + print( + " " + + "Units are: y (years), m (months), w (weeks), " + + "d (days) and h (hours).", + file=out, + ) + print( + yellow(" -h, --help") + " - display the help screen", + file=out, + ) + print( + yellow(" -V, --version") + " - display version info", file=out + ) + print(file=out) + if _error == "actions" or help == "all": + print("Available", green("actions") + ":", file=out) + print( + green(" packages") + " - clean outdated binary packages from PKGDIR", + file=out, + ) + print( + green(" distfiles") + + " - clean outdated packages sources files from DISTDIR", + file=out, + ) + print(file=out) + if _error in ("packages-options", "merged-packages-options") or help in ( + "all", + "packages", + ): + print( + "Available", + yellow("options"), + "for the", + green("packages"), + "action:", + file=out, + ) + print( + yellow(" --changed-deps") + + " - delete packages for which ebuild dependencies have changed", + file=out, + ) + print( + yellow(" -i, --ignore-failure") + + " - ignore failure to locate PKGDIR", + file=out, + ) + print(file=out) + if _error in ("distfiles-options", "merged-distfiles-options") or help in ( + "all", + "distfiles", + ): + print( + "Available", + yellow("options"), + "for the", + green("distfiles"), + "action:", + file=out, + ) + print( + yellow(" -f, --fetch-restricted") + + " - protect fetch-restricted files (when --deep)", + file=out, + ) + print( + yellow(" -s, --size-limit=<size>") + + " - don't delete distfiles bigger than " + + yellow("<size>"), + file=out, + ) + print( + " " + yellow("<size>"), + "is a size specification: " + '"10M" is "ten megabytes", "200K" is', + file=out, + ) + print( + " " + '"two hundreds kilobytes", etc. Units are: ' + "G, M, K and B.", + file=out, + ) + print(file=out) + print( + "More detailed instruction can be found in", + turquoise("`man %s`" % __productname__), + file=out, + ) class ParseArgsException(Exception): - """For parseArgs() -> main() communications.""" - def __init__(self, value): - self.value = value # sdfgsdfsdfsd - def __str__(self): - return repr(self.value) + """For parseArgs() -> main() communications.""" + + def __init__(self, value): + self.value = value # sdfgsdfsdfsd + + def __str__(self): + return repr(self.value) def parseSize(size): - """Convert a file size "Xu" ("X" is an integer, and "u" in - [G,M,K,B]) into an integer (file size in Bytes). - - @raise ParseArgsException: in case of failure - """ - units = { - 'G': (1024**3), - 'M': (1024**2), - 'K': 1024, - 'B': 1 - } - try: - match = re.match(r"^(?P<value>\d+)(?P<unit>[GMKBgmkb])?$",size) - size = int(match.group('value')) - if match.group('unit'): - size *= units[match.group('unit').capitalize()] - except: - raise ParseArgsException('size') - return size + """Convert a file size "Xu" ("X" is an integer, and "u" in + [G,M,K,B]) into an integer (file size in Bytes). + + @raise ParseArgsException: in case of failure + """ + units = {"G": (1024 ** 3), "M": (1024 ** 2), "K": 1024, "B": 1} + try: + match = re.match(r"^(?P<value>\d+)(?P<unit>[GMKBgmkb])?$", size) + size = int(match.group("value")) + if match.group("unit"): + size *= units[match.group("unit").capitalize()] + except: + raise ParseArgsException("size") + return size def parseTime(timespec): - """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?). - """ - units = {'H' : (60 * 60)} - units['D'] = units['H'] * 24 - units['W'] = units['D'] * 7 - units['M'] = units['D'] * 30 - units['Y'] = units['D'] * 365 - try: - # parse the time specification - match = re.match(r"^(?P<value>\d+)(?P<unit>[YMWDHymwdh])?$",timespec) - value = int(match.group('value')) - if not match.group('unit'): unit = 'D' - else: unit = match.group('unit').capitalize() - except: - raise ParseArgsException('time') - return time.time() - (value * units[unit]) + """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?). + """ + units = {"H": (60 * 60)} + units["D"] = units["H"] * 24 + units["W"] = units["D"] * 7 + units["M"] = units["D"] * 30 + units["Y"] = units["D"] * 365 + try: + # parse the time specification + match = re.match(r"^(?P<value>\d+)(?P<unit>[YMWDHymwdh])?$", timespec) + value = int(match.group("value")) + if not match.group("unit"): + unit = "D" + else: + unit = match.group("unit").capitalize() + except: + raise ParseArgsException("time") + return time.time() - (value * units[unit]) def parseArgs(options={}): - """Parse the command line arguments. Raise exceptions on - errors or non-action modes (help/version). Returns an action, and affect - the options dict. - """ - - def optionSwitch(option,opts,action=None): - """local function for interpreting command line options - and setting options accordingly""" - return_code = True - do_help = False - for o, a in opts: - if o in ("-h", "--help"): - do_help = True - elif o in ("-V", "--version"): - raise ParseArgsException('version') - elif o in ("-C", "--nocolor"): - options['nocolor'] = True - pp.output.nocolor() - elif o in ("-d", "--deep", "--destructive"): - options['destructive'] = True - elif o in ("-D", "--deprecated"): - options['deprecated'] = True - elif o in ("-i", "--interactive") and not options['pretend']: - options['interactive'] = True - elif o in ("-p", "--pretend"): - options['pretend'] = True - options['interactive'] = False - elif o in ("-q", "--quiet"): - options['quiet'] = True - options['verbose'] = False - elif o in ("-t", "--time-limit"): - options['time-limit'] = parseTime(a) - elif o in ("-e", "--exclude-file"): - print("cli --exclude option") - options['exclude-file'] = a - elif o in ("-n", "--package-names"): - options['package-names'] = True - elif o in ("-f", "--fetch-restricted"): - options['fetch-restricted'] = True - elif o in ("-s", "--size-limit"): - options['size-limit'] = parseSize(a) - elif o in ("-v", "--verbose") and not options['quiet']: - options['verbose'] = True - elif o in ("--changed-deps"): - options['changed-deps'] = True - elif o in ("-i", "--ignore-failure"): - options['ignore-failure'] = True - else: - return_code = False - # sanity check of --deep only options: - for opt in ('fetch-restricted', 'package-names'): - if (not options['destructive']) and options[opt]: - if not options['quiet']: - print( pp.error( - "--%s only makes sense in --deep mode." % opt), file=sys.stderr) - options[opt] = False - if do_help: - if action: - raise ParseArgsException('help-'+action) - else: - raise ParseArgsException('help') - return return_code - - # here are the different allowed command line options (getopt args) - getopt_options = {'short':{}, 'long':{}} - getopt_options['short']['global'] = "CdDipqe:t:nhVv" - getopt_options['long']['global'] = ["nocolor", "deep", "destructive", - "deprecated", "interactive", "pretend", "quiet", "exclude-file=", - "time-limit=", "package-names", "help", "version", "verbose"] - getopt_options['short']['distfiles'] = "fs:" - getopt_options['long']['distfiles'] = ["fetch-restricted", "size-limit="] - getopt_options['short']['packages'] = "i" - getopt_options['long']['packages'] = ["ignore-failure", "changed-deps"] - # set default options, except 'nocolor', which is set in main() - options['interactive'] = False - options['pretend'] = False - options['quiet'] = False - options['accept_all'] = False - options['destructive'] = False - options['deprecated'] = False - options['time-limit'] = 0 - options['package-names'] = False - options['fetch-restricted'] = False - options['size-limit'] = 0 - options['verbose'] = False - options['changed-deps'] = False - options['ignore-failure'] = False - # if called by a well-named symlink, set the action accordingly: - action = None - # temp print line to ensure it is the svn/branch code running, etc.. - #print( "###### svn/branch/gentoolkit_eclean ####### ==> ", os.path.basename(sys.argv[0])) - if os.path.basename(sys.argv[0]).startswith(__productname__+'-pkg') or \ - os.path.basename(sys.argv[0]).startswith(__productname__+'-packages'): - action = 'packages' - elif os.path.basename(sys.argv[0]).startswith(__productname__+'-dist') or \ - os.path.basename(sys.argv[0]).startswith(__productname__+'distfiles'): - action = 'distfiles' - # prepare for the first getopt - if action: - short_opts = getopt_options['short']['global'] \ - + getopt_options['short'][action] - long_opts = getopt_options['long']['global'] \ - + getopt_options['long'][action] - opts_mode = 'merged-'+action - 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 options accordingly - optionSwitch(options,opts,action=action) - # if action was already set, there should be no more args - if action and len(args): - raise ParseArgsException(opts_mode+'-options') - # if action was set, there is nothing left to do - if action: - return action - # 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') - action = args.pop(0) - # parse the action specific options - try: - opts, args = getopt.getopt(args, \ - getopt_options['short'][action], \ - getopt_options['long'][action]) - except: - raise ParseArgsException(action+'-options') - # set options again, for action-specific options - optionSwitch(options,opts,action=action) - # any remaning args? Then die! - if len(args): - raise ParseArgsException(action+'-options') - # returns the action. Options dictionary is modified by side-effect. - return action - - -def doAction(action,options,exclude={}, output=None): - """doAction: execute one action, ie display a few message, call the right - find* function, and then call doCleanup with its result.""" - # define vocabulary for the output - if action == 'packages': - files_type = "binary packages" - else: - files_type = "distfiles" - saved = {} - deprecated = {} - # find files to delete, depending on the action - if not options['quiet']: - output.einfo("Building file list for "+action+" cleaning...") - if action == 'packages': - clean_me = findPackages( - options, - exclude=exclude, - destructive=options['destructive'], - package_names=options['package-names'], - time_limit=options['time-limit'], - pkgdir=pkgdir, - #port_dbapi=Dbapi(portage.db[portage.root]["porttree"].dbapi), - #var_dbapi=Dbapi(portage.db[portage.root]["vartree"].dbapi), - ) - else: - # accept defaults - engine = DistfilesSearch(output=options['verbose-output'], - #portdb=Dbapi(portage.db[portage.root]["porttree"].dbapi), - #var_dbapi=Dbapi(portage.db[portage.root]["vartree"].dbapi), - ) - clean_me, saved, deprecated = engine.findDistfiles( - exclude=exclude, - destructive=options['destructive'], - fetch_restricted=options['fetch-restricted'], - package_names=options['package-names'], - time_limit=options['time-limit'], - size_limit=options['size-limit'], - deprecate = options['deprecated'] - ) - - # initialize our cleaner - cleaner = CleanUp(output.progress_controller) - - # actually clean files if something was found - if clean_me: - # verbose pretend message - if options['pretend'] and not options['quiet']: - output.einfo("Here are the "+files_type+" that would be deleted:") - # verbose non-pretend message - elif not options['quiet']: - output.einfo("Cleaning " + files_type +"...") - # do the cleanup, and get size of deleted files - if options['pretend']: - clean_size = cleaner.pretend_clean(clean_me) - elif action in ['distfiles']: - clean_size = cleaner.clean_dist(clean_me) - elif action in ['packages']: - clean_size = cleaner.clean_pkgs(clean_me, - pkgdir) - # vocabulary for final message - if options['pretend']: - verb = "would be" - else: - verb = "were" - # display freed space - if not options['quiet']: - output.total('normal', clean_size, len(clean_me), verb, action) - # nothing was found - elif not options['quiet']: - output.einfo("Your "+action+" directory was already clean.") - if saved and not options['quiet']: - print() - print( (pp.emph(" The following ") + yellow("unavailable") + - pp.emph(" files were saved from cleaning due to exclusion file entries"))) - output.set_colors('deprecated') - clean_size = cleaner.pretend_clean(saved) - output.total('deprecated', clean_size, len(saved), verb, action) - if deprecated and not options['quiet']: - print() - print( (pp.emph(" The following ") + yellow("unavailable") + - pp.emph(" installed packages were found"))) - output.set_colors('deprecated') - output.list_pkgs(deprecated) + """Parse the command line arguments. Raise exceptions on + errors or non-action modes (help/version). Returns an action, and affect + the options dict. + """ + + def optionSwitch(option, opts, action=None): + """local function for interpreting command line options + and setting options accordingly""" + return_code = True + do_help = False + for o, a in opts: + if o in ("-h", "--help"): + do_help = True + elif o in ("-V", "--version"): + raise ParseArgsException("version") + elif o in ("-C", "--nocolor"): + options["nocolor"] = True + pp.output.nocolor() + elif o in ("-d", "--deep", "--destructive"): + options["destructive"] = True + elif o in ("-D", "--deprecated"): + options["deprecated"] = True + elif o in ("-i", "--interactive") and not options["pretend"]: + options["interactive"] = True + elif o in ("-p", "--pretend"): + options["pretend"] = True + options["interactive"] = False + elif o in ("-q", "--quiet"): + options["quiet"] = True + options["verbose"] = False + elif o in ("-t", "--time-limit"): + options["time-limit"] = parseTime(a) + elif o in ("-e", "--exclude-file"): + print("cli --exclude option") + options["exclude-file"] = a + elif o in ("-n", "--package-names"): + options["package-names"] = True + elif o in ("-f", "--fetch-restricted"): + options["fetch-restricted"] = True + elif o in ("-s", "--size-limit"): + options["size-limit"] = parseSize(a) + elif o in ("-v", "--verbose") and not options["quiet"]: + options["verbose"] = True + elif o in ("--changed-deps"): + options["changed-deps"] = True + elif o in ("-i", "--ignore-failure"): + options["ignore-failure"] = True + else: + return_code = False + # sanity check of --deep only options: + for opt in ("fetch-restricted", "package-names"): + if (not options["destructive"]) and options[opt]: + if not options["quiet"]: + print( + pp.error("--%s only makes sense in --deep mode." % opt), + file=sys.stderr, + ) + options[opt] = False + if do_help: + if action: + raise ParseArgsException("help-" + action) + else: + raise ParseArgsException("help") + return return_code + + # here are the different allowed command line options (getopt args) + getopt_options = {"short": {}, "long": {}} + getopt_options["short"]["global"] = "CdDipqe:t:nhVv" + getopt_options["long"]["global"] = [ + "nocolor", + "deep", + "destructive", + "deprecated", + "interactive", + "pretend", + "quiet", + "exclude-file=", + "time-limit=", + "package-names", + "help", + "version", + "verbose", + ] + getopt_options["short"]["distfiles"] = "fs:" + getopt_options["long"]["distfiles"] = ["fetch-restricted", "size-limit="] + getopt_options["short"]["packages"] = "i" + getopt_options["long"]["packages"] = ["ignore-failure", "changed-deps"] + # set default options, except 'nocolor', which is set in main() + options["interactive"] = False + options["pretend"] = False + options["quiet"] = False + options["accept_all"] = False + options["destructive"] = False + options["deprecated"] = False + options["time-limit"] = 0 + options["package-names"] = False + options["fetch-restricted"] = False + options["size-limit"] = 0 + options["verbose"] = False + options["changed-deps"] = False + options["ignore-failure"] = False + # if called by a well-named symlink, set the action accordingly: + action = None + # temp print line to ensure it is the svn/branch code running, etc.. + # print( "###### svn/branch/gentoolkit_eclean ####### ==> ", os.path.basename(sys.argv[0])) + if os.path.basename(sys.argv[0]).startswith( + __productname__ + "-pkg" + ) or os.path.basename(sys.argv[0]).startswith(__productname__ + "-packages"): + action = "packages" + elif os.path.basename(sys.argv[0]).startswith( + __productname__ + "-dist" + ) or os.path.basename(sys.argv[0]).startswith(__productname__ + "distfiles"): + action = "distfiles" + # prepare for the first getopt + if action: + short_opts = getopt_options["short"]["global"] + getopt_options["short"][action] + long_opts = getopt_options["long"]["global"] + getopt_options["long"][action] + opts_mode = "merged-" + action + 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 options accordingly + optionSwitch(options, opts, action=action) + # if action was already set, there should be no more args + if action and len(args): + raise ParseArgsException(opts_mode + "-options") + # if action was set, there is nothing left to do + if action: + return action + # 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") + action = args.pop(0) + # parse the action specific options + try: + opts, args = getopt.getopt( + args, getopt_options["short"][action], getopt_options["long"][action] + ) + except: + raise ParseArgsException(action + "-options") + # set options again, for action-specific options + optionSwitch(options, opts, action=action) + # any remaning args? Then die! + if len(args): + raise ParseArgsException(action + "-options") + # returns the action. Options dictionary is modified by side-effect. + return action + + +def doAction(action, options, exclude={}, output=None): + """doAction: execute one action, ie display a few message, call the right + find* function, and then call doCleanup with its result.""" + # define vocabulary for the output + if action == "packages": + files_type = "binary packages" + else: + files_type = "distfiles" + saved = {} + deprecated = {} + # find files to delete, depending on the action + if not options["quiet"]: + output.einfo("Building file list for " + action + " cleaning...") + if action == "packages": + clean_me = findPackages( + options, + exclude=exclude, + destructive=options["destructive"], + package_names=options["package-names"], + time_limit=options["time-limit"], + pkgdir=pkgdir, + # port_dbapi=Dbapi(portage.db[portage.root]["porttree"].dbapi), + # var_dbapi=Dbapi(portage.db[portage.root]["vartree"].dbapi), + ) + else: + # accept defaults + engine = DistfilesSearch( + output=options["verbose-output"], + # portdb=Dbapi(portage.db[portage.root]["porttree"].dbapi), + # var_dbapi=Dbapi(portage.db[portage.root]["vartree"].dbapi), + ) + clean_me, saved, deprecated = engine.findDistfiles( + exclude=exclude, + destructive=options["destructive"], + fetch_restricted=options["fetch-restricted"], + package_names=options["package-names"], + time_limit=options["time-limit"], + size_limit=options["size-limit"], + deprecate=options["deprecated"], + ) + + # initialize our cleaner + cleaner = CleanUp(output.progress_controller) + + # actually clean files if something was found + if clean_me: + # verbose pretend message + if options["pretend"] and not options["quiet"]: + output.einfo("Here are the " + files_type + " that would be deleted:") + # verbose non-pretend message + elif not options["quiet"]: + output.einfo("Cleaning " + files_type + "...") + # do the cleanup, and get size of deleted files + if options["pretend"]: + clean_size = cleaner.pretend_clean(clean_me) + elif action in ["distfiles"]: + clean_size = cleaner.clean_dist(clean_me) + elif action in ["packages"]: + clean_size = cleaner.clean_pkgs(clean_me, pkgdir) + # vocabulary for final message + if options["pretend"]: + verb = "would be" + else: + verb = "were" + # display freed space + if not options["quiet"]: + output.total("normal", clean_size, len(clean_me), verb, action) + # nothing was found + elif not options["quiet"]: + output.einfo("Your " + action + " directory was already clean.") + if saved and not options["quiet"]: + print() + print( + ( + pp.emph(" The following ") + + yellow("unavailable") + + pp.emph( + " files were saved from cleaning due to exclusion file entries" + ) + ) + ) + output.set_colors("deprecated") + clean_size = cleaner.pretend_clean(saved) + output.total("deprecated", clean_size, len(saved), verb, action) + if deprecated and not options["quiet"]: + print() + print( + ( + pp.emph(" The following ") + + yellow("unavailable") + + pp.emph(" installed packages were found") + ) + ) + output.set_colors("deprecated") + output.list_pkgs(deprecated) def main(): - """Parse command line and execute all actions.""" - # set default options - options = {} - options['nocolor'] = (port_settings.get("NOCOLOR") in ('yes','true') - or not sys.stdout.isatty()) - if options['nocolor']: - pp.output.nocolor() - # parse command line options and actions - try: - action = parseArgs(options) - # filter exception to know what message to display - except ParseArgsException as 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) - output = OutputControl(options) - options['verbose-output'] = lambda x: None - if not options['quiet']: - if options['verbose']: - options['verbose-output'] = output.einfo - # parse the exclusion file - if not 'exclude-file' in options: - # set it to the default exclude file if it exists - exclude_file = "%s/etc/%s/%s.exclude" % (EPREFIX,__productname__ , action) - if os.path.isfile(exclude_file): - options['exclude-file'] = exclude_file - if 'exclude-file' in options: - try: - exclude = parseExcludeFile(options['exclude-file'], - options['verbose-output']) - except ParseExcludeFileException as e: - print( pp.error(str(e)), file=sys.stderr) - print( pp.error( - "Invalid exclusion file: %s" % options['exclude-file']), file=sys.stderr) - print( pp.error( - "See format of this file in `man %s`" % __productname__), file=sys.stderr) - sys.exit(1) - else: - exclude = {} - # security check for non-pretend mode - if not options['pretend'] and portage.secpass == 0: - print( pp.error( - "Permission denied: you must be root or belong to " + - "the portage group."), file=sys.stderr) - sys.exit(1) - # execute action - doAction(action, options, exclude=exclude, - output=output) + """Parse command line and execute all actions.""" + # set default options + options = {} + options["nocolor"] = ( + port_settings.get("NOCOLOR") in ("yes", "true") or not sys.stdout.isatty() + ) + if options["nocolor"]: + pp.output.nocolor() + # parse command line options and actions + try: + action = parseArgs(options) + # filter exception to know what message to display + except ParseArgsException as 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) + output = OutputControl(options) + options["verbose-output"] = lambda x: None + if not options["quiet"]: + if options["verbose"]: + options["verbose-output"] = output.einfo + # parse the exclusion file + if not "exclude-file" in options: + # set it to the default exclude file if it exists + exclude_file = "%s/etc/%s/%s.exclude" % (EPREFIX, __productname__, action) + if os.path.isfile(exclude_file): + options["exclude-file"] = exclude_file + if "exclude-file" in options: + try: + exclude = parseExcludeFile( + options["exclude-file"], options["verbose-output"] + ) + except ParseExcludeFileException as e: + print(pp.error(str(e)), file=sys.stderr) + print( + pp.error("Invalid exclusion file: %s" % options["exclude-file"]), + file=sys.stderr, + ) + print( + pp.error("See format of this file in `man %s`" % __productname__), + file=sys.stderr, + ) + sys.exit(1) + else: + exclude = {} + # security check for non-pretend mode + if not options["pretend"] and portage.secpass == 0: + print( + pp.error( + "Permission denied: you must be root or belong to " + + "the portage group." + ), + file=sys.stderr, + ) + sys.exit(1) + # execute action + doAction(action, options, exclude=exclude, output=output) if __name__ == "__main__": - """actually call main() if launched as a script""" - try: - main() - except KeyboardInterrupt: - print( "Aborted.") - sys.exit(130) - sys.exit(0) + """actually call main() if launched as a script""" + try: + main() + except KeyboardInterrupt: + print("Aborted.") + sys.exit(130) + sys.exit(0) |