summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Varner <fuzzyray@gentoo.org>2010-12-28 18:30:14 -0600
committerPaul Varner <fuzzyray@gentoo.org>2010-12-28 18:30:14 -0600
commit879cf3ce1f3041854c9dc886b59ac7e136ae9b9b (patch)
tree7faa716b853d76a4c430d7b43024d82222d329e7
parent258a81471b4106f12bafee2e5c0a5458e2db2280 (diff)
parenta9ac560d5f3e056d14146d5033e5637bbc9f68ef (diff)
downloadgentoolkit-879cf3ce1f3041854c9dc886b59ac7e136ae9b9b.tar.gz
Merge branch 'gentoolkit' into euse
Conflicts: bin/euse
-rw-r--r--AUTHORS8
-rw-r--r--CREDITS16
-rw-r--r--ChangeLog96
-rw-r--r--DEVELOPING161
-rw-r--r--MANIFEST.in12
-rw-r--r--Makefile25
-rw-r--r--NEWS40
-rw-r--r--README.dev39
-rw-r--r--TODO37
-rw-r--r--VERSION1
-rwxr-xr-xbin/analyse48
-rwxr-xr-xbin/eclean849
-rwxr-xr-xbin/epkginfo284
-rwxr-xr-xbin/equery28
-rw-r--r--bin/eshowkw9
-rwxr-xr-xbin/euse137
-rwxr-xr-xbin/glsa-check126
-rwxr-xr-xbin/revdep-rebuild192
-rw-r--r--man/analyse.1210
-rw-r--r--man/eclean.131
-rw-r--r--man/epkginfo.160
-rw-r--r--man/equery.1862
-rw-r--r--man/eshowkw.147
-rw-r--r--man/glsa-check.114
-rw-r--r--man/revdep-rebuild.19
-rw-r--r--pylintrc321
-rw-r--r--pym/gentoolkit/__init__.py45
-rw-r--r--pym/gentoolkit/analyse/__init__.py128
-rw-r--r--pym/gentoolkit/analyse/analyse.py378
-rw-r--r--pym/gentoolkit/analyse/base.py134
-rw-r--r--pym/gentoolkit/analyse/lib.py352
-rw-r--r--pym/gentoolkit/analyse/output.py213
-rw-r--r--pym/gentoolkit/analyse/rebuild.py235
-rw-r--r--pym/gentoolkit/atom.py347
-rw-r--r--pym/gentoolkit/base.py149
-rw-r--r--pym/gentoolkit/cpv.py256
-rw-r--r--pym/gentoolkit/dbapi.py17
-rw-r--r--pym/gentoolkit/dependencies.py330
-rw-r--r--pym/gentoolkit/deprecated/helpers.py178
-rw-r--r--pym/gentoolkit/eclean/__init__.py4
-rw-r--r--pym/gentoolkit/eclean/clean.py149
-rw-r--r--pym/gentoolkit/eclean/cli.py504
-rw-r--r--pym/gentoolkit/eclean/exclude.py262
-rw-r--r--pym/gentoolkit/eclean/output.py187
-rw-r--r--pym/gentoolkit/eclean/pkgindex.py91
-rw-r--r--pym/gentoolkit/eclean/search.py569
-rw-r--r--pym/gentoolkit/eprefix.py34
-rw-r--r--pym/gentoolkit/equery/__init__.py198
-rw-r--r--pym/gentoolkit/equery/belongs.py168
-rw-r--r--pym/gentoolkit/equery/changes.py310
-rw-r--r--pym/gentoolkit/equery/check.py370
-rw-r--r--pym/gentoolkit/equery/depends.py283
-rw-r--r--pym/gentoolkit/equery/depgraph.py265
-rw-r--r--pym/gentoolkit/equery/files.py180
-rw-r--r--pym/gentoolkit/equery/has.py213
-rw-r--r--pym/gentoolkit/equery/hasuse.py152
-rw-r--r--pym/gentoolkit/equery/keywords.py8
-rw-r--r--pym/gentoolkit/equery/list_.py203
-rw-r--r--pym/gentoolkit/equery/meta.py609
-rw-r--r--pym/gentoolkit/equery/size.py137
-rw-r--r--pym/gentoolkit/equery/uses.py240
-rw-r--r--pym/gentoolkit/equery/which.py51
-rw-r--r--pym/gentoolkit/errors.py164
-rw-r--r--pym/gentoolkit/eshowkw/__init__.py130
-rw-r--r--pym/gentoolkit/eshowkw/display_pretty.py103
-rw-r--r--pym/gentoolkit/eshowkw/keywords_content.py325
-rw-r--r--pym/gentoolkit/eshowkw/keywords_header.py100
-rw-r--r--pym/gentoolkit/flag.py165
-rw-r--r--pym/gentoolkit/formatters.py99
-rw-r--r--pym/gentoolkit/glsa/__init__.py149
-rw-r--r--pym/gentoolkit/helpers.py616
-rw-r--r--pym/gentoolkit/helpers2.py437
-rw-r--r--pym/gentoolkit/keyword.py144
-rw-r--r--pym/gentoolkit/metadata.py307
-rw-r--r--pym/gentoolkit/package.py860
-rw-r--r--pym/gentoolkit/pprinter.py265
-rw-r--r--pym/gentoolkit/query.py382
-rw-r--r--pym/gentoolkit/sets.py57
-rw-r--r--pym/gentoolkit/test/__init__.py25
-rw-r--r--pym/gentoolkit/test/eclean/Packages1017
-rw-r--r--pym/gentoolkit/test/eclean/__init__.py6
-rw-r--r--pym/gentoolkit/test/eclean/creator.py242
-rw-r--r--pym/gentoolkit/test/eclean/distfiles.exclude11
-rw-r--r--pym/gentoolkit/test/eclean/distsupport.py482
-rw-r--r--pym/gentoolkit/test/eclean/test_clean.py152
-rw-r--r--pym/gentoolkit/test/eclean/test_search.py632
-rw-r--r--pym/gentoolkit/test/equery/__init__.py6
-rw-r--r--pym/gentoolkit/test/equery/test_init.py (renamed from pym/gentoolkit/tests/equery/test_init.py)10
-rw-r--r--pym/gentoolkit/test/test_atom.py153
-rw-r--r--pym/gentoolkit/test/test_cpv.py142
-rw-r--r--pym/gentoolkit/test/test_helpers.py122
-rw-r--r--pym/gentoolkit/test/test_keyword.py57
-rw-r--r--pym/gentoolkit/test/test_query.py109
-rw-r--r--pym/gentoolkit/test/test_syntax.py34
-rw-r--r--pym/gentoolkit/tests/test_helpers2.py39
-rw-r--r--pym/gentoolkit/tests/test_template.py38
-rw-r--r--pym/gentoolkit/textwrap_.py108
-rw-r--r--pym/gentoolkit/versionmatch.py153
-rwxr-xr-xsetup.py156
99 files changed, 14912 insertions, 4696 deletions
diff --git a/AUTHORS b/AUTHORS
index fa210a6..c8b89aa 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,19 +1,19 @@
* gentoolkit
Original author: Karl Trygve Kalleberg <karltk@gentoo.org>
- Further additions: Douglas Anderson <dja@gendja.com>
+ Library additions and refactor: Douglas Anderson <douglasjanderson@gmail.com>
Current maintainer: Paul Varner <fuzzyray@gentoo.org>
* eclean
Original author: Thomas de Grenier de Latour (tgl) <degrenier@easyconnect.fr>
-
+
* epkginfo
Author: Ned Ludd <solar@gentoo.org>
earch: Eldad Zack <eldad@gentoo.org>
- metadata: Olinger <EvvL AT RustedHalo DOT net>
+ metadata: Olinger <EvvL AT RustedHalo DOT net>
* equery
Original author: Karl Trygve Kalleberg <karltk@gentoo.org>
- Modular redesign: Douglas Anderson <dja@gendja.com>
+ 0.3.0 author: Douglas Anderson <douglasjanderson@gmail.com>
* eread
Original author: Donnie Berkholz <dberkholz@gentoo.org>
diff --git a/CREDITS b/CREDITS
new file mode 100644
index 0000000..3f9d6f0
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,16 @@
+
+ This is an incomplete list of people who have contributed non-
+ trivial patches to gentoolkit-0.3.0 and later, in the spirit of
+ the Linux kernel CREDITS file. It is sorted alphabetically by
+ name (N). Other fields are email (E) and description (D).
+ We'd like to thank:
+
+-------------------------------------------------------------------------------
+
+N: Brian Dolbec
+E: dol-sen@users.sourceforge.net
+D: Separating dependency gathering functionality from output in depends.py.
+
+
+
+# vim: set ts=8 sw=8 tw=80:
diff --git a/ChangeLog b/ChangeLog
index d20fca5..5ac6727 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,99 @@
+2010-11-24: Paul Varner <fuzzyray@gentoo.org>
+ * euse: Fix euse to look for portage configuration files in both the
+ old and new paths. bug 346519. Make euse work in prefix environments.
+
+2010-10-29: Tomáš Chvátal <scarabeus@gentoo.org>
+ * eshowkw: Add new module as drop-in replacement for eshowkw from
+ gentoolkit-dev
+
+2010-05-13: Christian Ruppert <idl0r@gentoo.org>
+ * eclean/cli.py: Fix typo, bug 319349, thanks to Ulrich Müller
+ <ulm@gentoo.org>.
+
+2010-05-06: Christian Ruppert <idl0r@gentoo.org>
+ * revdep-rebuild: Make revdep-rebuild more quiet, bug 97073. We might
+ improve that a bit more in the future.
+
+2010-04-17: Christian Ruppert <idl0r@gentoo.org>
+ * euse: Make python code of euse python-3 compatible, thanks to Arfrever
+ Frehtes Taifersar Arahesis <arfrever@gentoo.org>.
+
+2010-04-11: Christian Ruppert <idl0r@gentoo.org>
+ * euse: Fix check for /etc/make.profile, check if its readable and a
+ directory instead of checking if its a link. Thanks to Phillip Brink
+ (ohnobinki) <ohnobinki@ohnopublishing.net>.
+
+2010-04-06: Christian Ruppert <idl0r@gentoo.org>
+ * euse: Fix query to get USE, bug 181309, thanks to Jean-Baptiste Rouault
+ <jbrouault@gmail.com>.
+
+2010-04-01: Christian Ruppert <idl0r@gentoo.org>
+ * revdep-rebuild: Use EMERGE_OPTIONS _after_ EMERGE_DEFAULT_OPTS,
+ bug 307023, thanks to Albert W. Hopkins <marduk@python.net>.
+
+2010-03-31: Christian Ruppert <idl0r@gentoo.org>
+ * epkginfo: Fix epkginfo to handle ambiguous package names.
+ * revdep-rebuild: Check if $ERRORS_FILE exist before running gawk on it.
+
+2010-03-09: Paul Varner <fuzzyray@gentoo.org>
+ * gentoolkit: Add inital py3k support.
+ * analyse: Add new analyse utility from dol-sen. This will probably
+ change to a different name for final gentoolkit-0.3.0 release.
+
+2010-02-05: Paul Varner <fuzzyray@gentoo.org>
+ * revdep-rebuild: Update revdep-rebuild to use extended regular
+ expressions instead of basic regular expressions. (Bug 143498)
+
+2010-02-04: Paul Varner <fuzzyray@gentoo.org>
+ * revdep-rebuild: Fix revdep-rebuild to handle include statements in
+ /etc/ld.so.conf. (Bug 298651)
+
+2010-31-01: Christian Ruppert <idl0r@gentoo.org>
+ * revdep-rebuild: Check for eerror in die() since an invalid option(long) or
+ missing arg will end up in calling die() before /etc/init.d/functions.sh
+ has been sourced.
+
+2009-01-11: Paul Varner <fuzzyray@gentoo.org>
+ * revdep-rebuild: Set environment using 'portageq -v' (Bug 300229)
+
+2009-01-08: Paul Varner <fuzzyray@gentoo.org>
+ * epkginfo/equery: Fix expkinfo to not traceback if herds.xml is not
+ present in the tree. (Bug 300108)
+
+2009-12-17: Paul Varner <fuzzyray@gentoo.org>
+ * revdep-rebuild: Fix revdep-rebuild to properly honor
+ PORTAGE_NICENESS as an incremental to the current nice level (Bug
+ 297174).
+
+2009-12-08: Paul Varner <fuzzyray@gentoo.org>
+ * gentoolkit: Merge rev 113 from djanderson's genscripts repo
+
+2009-10-30: Christian Ruppert <idl0r@gentoo.org>
+ * revdep-rebuild: Speedup portageq queries. Include FuzzyRay's patch to
+ respect EMERGE_DEFAULT_OPTS.
+
+2009-10-27: Christian Ruppert <idl0r@gentoo.org>
+ * revdep-rebuild: Use realpath in get_file_owner to be able to get the
+ package containing e.g. /lib/libreadline.so.6 on amd64, fixes bug 280341.
+
+2009-10-27: Christian Ruppert <idl0r@gentoo.org>
+ * revdep-rebuild.1: Fix bug 281050, thanks to Jesús Guerrero
+ <i92guboj@gentoo.org>.
+
+2009-10-26: Christian Ruppert <idl0r@gentoo.org>
+ * euse: Don't add an additional new line at the end of file.
+ * equery.1: Fix bug 248844, thanks to J M W <ultip@ymail.com>.
+
+2009-08-18: Robert Buchholz <rbu@gentoo.org>
+ * glsa-check: Remove 'new' target from glsa-list, everyone
+ should use 'affected' or 'all'
+ * glsa-check: fix getminupgrade API doc and TypeError (Bug 281101)
+ * glsa-check: Make --pretend output in glsa-check nicer.
+ Add colors and fix linebreaks.
+
+2009-06-22: Paul Varner <fuzzyray@gentoo.org>
+ * glsa-check: Fix traceback with glsa-check -f (Bug 275105)
+
2009-06-05: Paul Varner <fuzzyray@gentoo.org>
* epkginfo: Add patch from Sebastian Mingramm to Make epkginfo slot
aware and only print keywords for the highest visible versions in each
diff --git a/DEVELOPING b/DEVELOPING
new file mode 100644
index 0000000..e8b7ab3
--- /dev/null
+++ b/DEVELOPING
@@ -0,0 +1,161 @@
+Python Code Guidelines
+----------------------
+These are a few guidelines to stick to when modifying or adding code to
+Gentoolkit. These guidelines do not apply to pym/gentoolkit/test/*.
+
+First, read Python's PEP 8: http://www.python.org/dev/peps/pep-0008/
+Next, read Portage's DEVELOPING file.
+Next, read Portage's Docstring specs:
+ http://www.gentoo.org/proj/en/portage/doc/policies/docstring-spec.xml
+
+(Portage DEVELOPING overrides PEP 8, so we use tabs instead of spaces.)
+
+Here are a few clarifications and changes to the above:
+
+Line-Wrapping
+-------------
+- Do NOT space out to an opening paren (etc). Different tab settings make this
+ difficult to read:
+
+ BAD:
+ foo = get_foo(keyword1=default,
+ keyword2=default,
+ ...)
+
+ OK:
+ foo = get_foo(keyword1=default, keyword2=default, keyword3=default,
+ keyword4=default, ...)
+
+ OK:
+ foo = get_foo(
+ keyword1=default,
+ keyword2=default,
+ ...
+ )
+
+- Preferred line length is 80 characters with a tab length of 4 characters.
+- "The preferred way of wrapping long lines is by using Python's implied line
+ continuation inside parentheses, brackets and braces" rather than using '\'
+ (PEP 8).
+ Even better than that is pre-defining a snippet of the long line in a
+ variable. So:
+
+ BAD: (too long)
+ self._descriptions = [e.text for e in self._xml_tree.findall("longdescription")]
+
+ OK:
+ self._descriptions = \
+ [e.text for e in self._xml_tree.findall("longdescription")]
+
+ OK:
+ self._descriptions = [
+ e.text for e in self._xml_tree.findall("longdescription")
+ ]
+
+ OK: (easiest to read and test)
+ long_descriptions = self._xml_tree.findall("longdescription")
+ self._descriptions = [e.text for e in long_descriptions]
+
+Imports:
+--------
+Same as PEP 8 and Portage's DEVELOPING spec, but make sure to split python
+default libraries, Gentoo system libraries (like portage) and lastly local
+modules (gentoolkit), so:
+
+ GOOD:
+ import os
+ import re
+
+ import portage
+ from portage.versions import catpkgsplit, pkgcmp
+
+ from gentoolkit import CONFIG
+ from gentoolkit import pprinter as pp
+ ...
+
+Exceptions:
+-----------
+Always raise exceptions and catch them at the last possible moment. This allows
+API consumers to catch them and decide what to do with them. Do not print an
+error to stderr from inside a consumable. Never "print" an error string, always
+write it to stderr.
+
+Lastly, if an error is fatal (should be raised all the way up), try to make
+sure it is an errors.Gentoolkit* error. That allows API consumers to catch all
+fatal errors with: except 'gentoolkit.errors.GentoolkitException'. See
+bin/equery to see how we catch those exceptions before they hit the user.
+
+ BAD:
+ try:
+ result = tree.aux_get(str(self.cpv), [var])
+ except KeyError:
+ sys.stderr.write("aux_get returned unexpected results")
+
+ BETTER:
+ # This line raises KeyError if self.cpv not found:
+ result = tree.aux_get(str(self.cpv), [var])
+
+ BEST:
+ try:
+ result = tree.aux_get(str(self.cpv), [var])
+ except KeyError:
+ err = "aux_get returned unexpected results"
+ raise errors.GentoolkitFatalError(err)
+
+Docstrings:
+-----------
+Follow this example for any complicated function or method. Don't worry about
+docstring for private methods (like _foo) or methods like __this__. It's
+definitely OK to write docstrings for these methods as well if there's
+something tricky about them, though.
+
+ def graph_reverse_depends(...):
+ # A ONE-LINE sentence describing the function in simplest terms:
+ """Graph direct reverse dependencies for self.
+
+ # An optional second paragraph to give more detailed information.
+ # Sometimes an 'Example usage' here can be worth more than a lengthy
+ # description. Examples should be an ACTUAL interpreter session:
+ Example usage:
+ >>> from gentoolkit.dependencies import Dependencies
+ >>> ffmpeg = Dependencies('media-video/ffmpeg-0.5_p20373')
+ >>> # I only care about installed packages that depend on me:
+ ... from gentoolkit.helpers import get_installed_cpvs
+ >>> # I want to pass in a sorted list. We can pass strings or
+ ... # Package or Atom types, so I'll use Package to sort:
+ ... from gentoolkit.package import Package
+ >>> installed = sorted(Package(x) for x in get_installed_cpvs())
+ >>> deptree = ffmpeg.graph_reverse_depends(
+ ... only_direct=False, # Include indirect revdeps
+ ... pkgset=installed) # from installed pkgset
+ >>> len(deptree)
+ 44
+
+ # Lastly use epydoc's special fields to document further.
+ # See: http://epydoc.sourceforge.net/fields.html
+ @type pkgset: iterable
+ @keyword pkgset: sorted pkg cpv strings or anything sublassing
+ L{gentoolkit.cpv.CPV} to use for calculate our revdep graph.
+ @type max_depth: int
+ @keyword max_depth: Maximum depth to recurse if only_direct=False.
+ -1 means no maximum depth;
+ 0 is the same as only_direct=True;
+ >0 means recurse only this many times;
+ @type only_direct: bool
+ @keyword only_direct: to recurse or not to recurse
+ @type printer_fn: callable
+ @keyword printer_fn: If None, no effect. If set, it will be applied to
+ each L{gentoolkit.atom.Atom} object as it is added to the results.
+ @rtype: list
+ @return: L{gentoolkit.dependencies.Dependencies} objects
+ """
+
+Other concerns:
+---------------
+- Choose names which are full, clear words (not necessary in small loops).
+- It is NEVER necessary to prefix names with "my". It adds no useful
+ information.
+- Comment and document in simple, unambiguous and non-repetitive English.
+- When adding a TODO, FIXME or XXX comment, please date it and add your name so
+ that other devs know who to ask about the proposed change.
+- Be careful of spelling.
diff --git a/MANIFEST.in b/MANIFEST.in
index 9bb143e..8f26bea 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,13 @@
-include AUTHORS ChangeLog COPYING NEWS README README.Developer THANKS TODO VERSION
+include AUTHORS
+include ChangeLog
+include COPYING
+include CREDITS
+include DEVELOPING
+include NEWS
+include README
+include TODO
+include setup.py
recursive-include data *
+recursive-include bin *
recursive-include man *
+recursive-include pym *.py *.txt
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 27dd699..0000000
--- a/Makefile
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2003-2004 Karl Trygve Kalleberg <karltk@gentoo.org>
-# Copyright 2003-2009 Gentoo Technologies, Inc.
-# Distributed under the terms of the GNU General Public License v2
-#
-# $Header$
-
-ifeq ($(origin VERSION), undefined)
- VERSION = 9999
-endif
-
-all:
- echo "YARMOUTH (vb.) To shout at foreigners in the belief that the louder you speak, the better they'll understand you."
- echo $(VERSION)
-
-clean:
- rm -rf release
-
-dist: dist-gentoolkit
-
-dist-gentoolkit:
- mkdir -p release
- echo "$(VERSION)" > VERSION
- python setup.py sdist --dist-dir release
- svn revert VERSION
- rm -f MANIFEST
diff --git a/NEWS b/NEWS
index e69de29..5e349be 100644
--- a/NEWS
+++ b/NEWS
@@ -0,0 +1,40 @@
+News (new features/major bug fixes)
+
+gentoolkit-0.3.0
+----------------
+
+epkginfo:
+ * is now a link to equery meta and has all the features equery meta has
+
+equery:
+ * --help menus cleaned up, using notations (b)elongs instead of belongs(b).
+ * man page has been rewritten.
+ * --quiet effects more modules.
+ * 2 new modules:
+ * changes - Gentoo ChangeLog viewer, try:
+ `equery changes portage` to see entry for portage version that emerge
+ wants to install;
+ `equery changes portage --from=2.2_rc20 --to=2.2_rc30` to see all entries
+ between the specified versions.
+ * meta - Displays information available in metadata.xml and keyword info.
+ Try `equery meta boost` to list herd, maintainers, keywords, and more.
+ * Modules which are meant to run on multiple packages (check, list, size) now
+ allow category and package name globbing, (so no more need for --exact-name
+ or --category).
+
+ # Exact name matching by default:
+ $ equery l zilla
+ * Searching for zilla ...
+
+ # Use globs to fuzzy match
+ $ equery l *zilla*
+ * Searching for *zilla* ...
+ [IP-] [ ] www-client/mozilla-firefox-3.5.4:0
+
+ # Use globs to 'category filter'
+ $ equery l www-client/*
+ * Searching for * in www-client ...
+ [I--] [XX] www-client/chromium-4.0.223.5:0
+ [IP-] [ ] www-client/epiphany-2.26.3-r2:0
+ [IP-] [ ] www-client/links-2.2:2
+ [IP-] [ ] www-client/mozilla-firefox-3.5.4:0
diff --git a/README.dev b/README.dev
new file mode 100644
index 0000000..84f7cf3
--- /dev/null
+++ b/README.dev
@@ -0,0 +1,39 @@
+Adding or modifying code:
+=========================
+- If you add new code, best practice is to write a test for it.
+- If you're modifying code that doesn't have a test and you can write a test
+ for it, please do.
+- Before committing your changes to a python file, please make sure it passes
+ pylint with:
+pylint --rcfile=pylintrc yourfile.py
+- If pylint raises a warning or error that you don't agree with, it's probably
+ better to just change your code. If you're sure you have a good reason for
+ doing what you're doing, you can add an exception to our pylintrc.
+
+Creating a release:
+===================
+Note: We are using VERSION="0.3.0" simply as an example.
+
+- Run Gentoolkit's test suite, make sure it passes:
+Note: requires dev-python/snakeoil
+
+Note: If running from the gentoolkit repository, please grab the eclean test file from:
+http://genscripts.googlecode.com/svn/trunk/gentoolkit/pym/gentoolkit/test/eclean/testdistfiles.tar.gz
+Please do not add/commit the test file to the gentoolkit repository
+
+./setup.py test
+
+- Create a tag for the release
+svn copy svn+ssh://<dev>@svn.gentoo.org/var/svnroot/gentoolkit/trunk/gentoolkit \
+ svn+ssh://<dev>@svn.gentoo.org/var/svnroot/gentoolkit/tags/gentoolkit-0.3.0 \
+ -m "Tagging the <VERSION> release of gentoolkit."
+svn update to pull the tag from subversion
+cd to the local tags/gentoolkit-0.3.0 directory
+
+- Create a source distribution (you need to add VERSION here):
+VERSION="0.3.0" ./setup.py sdist
+Transfer dist/gentoolkit-0.3.0.tar.gz to dev.gentoo.org:/space/distfiles-local
+
+- Clean up temporary files:
+./setup.py clean -a
+
diff --git a/TODO b/TODO
index d39ea85..1cee1a4 100644
--- a/TODO
+++ b/TODO
@@ -2,27 +2,12 @@
- add --overlay, --portdir to uses
- add glsa pkgspec
- query for current GLSAs on installed package(s)
- - add changelog <from> <to> [--last] [--lastentry] [--current]
- - displays changelog entries
- - add check <pkg-spec> [--full] [--display=pkgnames,full]
- - check md5 and timestamps (optionally also in CONFIG_PROTECT dirs)
- - add which
- - as ewhich
-- fully deprecate dep-clean and pkg-clean
- rewrite ekeywords and echangelog to use gentoolkit
-- add deep depends to equery
-- fully deprecate qpkg
-- fully deprecate pkg-size
- merge change and echangelog
- merge useflag and euse, have _one_ command line tool
- update ufed to rely on the CLI tool
- update ufed to rely on generate-use
- merge generate-use and ufed?
-- rewrite revdep-rebuild to use gentoolkit
- - drop qpkg dependency; use equery instead
-- rewrite distfiles-clean to use gentoolkit
-- look through forums.gentoo.org for additional scripts
-- write a Gentoolkit Guide
- write efeatures for turning on/off FEATURES in make.conf
- look at ekeys, ewatch
- revision bump tool
@@ -30,11 +15,19 @@
- -m for changelog entry
- use ~/.gentoo/gentoolkit/ebump.conf
- use /etc/gentoolkit/ebump.conf
-- should be removed:
- - etc-update
- - epm
- - useflag
-- add 'clean' target
-+ check esearch, eupdatedb: separate package for now
-
+equery (modern):
+ Add more --debug stuff
+ Write tests for Dependencies._parser
+ Profile Dependencies._parser
+ Extend PackageFormatter usage to everything that outputs packages to
+ allow for purvasive use of -F, --format goodness
+
+Ebuild changes:
+ - Add:
+ src_test() {
+ "${python}" setup.py test || die "testing returned non zero"
+ }
+
+For Next Release:
+ - write NEWS file
diff --git a/VERSION b/VERSION
deleted file mode 100644
index 3c2df07..0000000
--- a/VERSION
+++ /dev/null
@@ -1 +0,0 @@
-9999
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)
diff --git a/bin/eclean b/bin/eclean
index 9953fa7..e17f9f0 100755
--- a/bin/eclean
+++ b/bin/eclean
@@ -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:
diff --git a/bin/equery b/bin/equery
index bac8a3a..54c3a07 100755
--- a/bin/equery
+++ b/bin/equery
@@ -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
diff --git a/bin/euse b/bin/euse
index fc9d60f..c54135f 100755
--- a/bin/euse
+++ b/bin/euse
@@ -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
diff --git a/man/analyse.1 b/man/analyse.1
new file mode 100644
index 0000000..76044b8
--- /dev/null
+++ b/man/analyse.1
@@ -0,0 +1,210 @@
+.TH "ANALYSE" "22" "Febuary 2010" "GENTOOLKIT"
+.SH "NAME"
+analyse \- Gentoo Installed Package Analysis Tool
+
+.SH "SYNOPSIS"
+.BI "analyse " "[global-options] " "module " "[local-options]" "TARGET"
+
+.SH "DESCRIPTION"
+.B Analyse
+Is a collection of modules for analysing the state of installed Gentoo packages for
+USE flags or keywords used for installation, and their current masking status.
+.br
+It can also optionally (re)generate new /etc/portage/package.* files.
+.br
+
+.br
+.B CAUTION:
+This is beta software and is not yet feature complete. Some features, options or its name
+may change in the future. Any files that it generates are saved to your home directory
+and will not harm your system without effort on your part.
+.br
+
+.SH "GLOBAL OPTIONS"
+.HP
+.B \-h, \-\-help
+.br
+Output a help message.
+.HP
+.B \-q, \-\-quiet
+.br
+Be less verbose where possible. In some modules, this option can increase the output speed.
+.HP
+.B \-C, \-\-no-color
+.br
+Do not colorize output.
+.HP
+.B \-N, \-\-no\-pipe
+.br
+Turn off automatic pipe detection. Use this option if you do not want
+.B analyse
+To detect if the output is being directed to the screen or to another program
+and adjust color and verbosity accordingly.
+.HP
+.B \-V, \-\-version
+.br
+Display \fBGentoolkit\fP's version. Please include this in all bug reports. (see
+.B BUGS
+below)
+
+.SH "MODULES"
+.B Analyse
+Uses a system of modules. Each module has both a long and short name.
+The list below uses the notation "\fBmodule (m)\fP", where \fIm\fP is the short name
+and \fImodule\fP is the long name.
+.P
+You can view the
+.B help
+message for a specific module by using
+.BR "-h" ", " "--help "
+as either a global option (after
+.B analyse
+and before the module name) or as a local option (after the module name).
+
+.SS
+.BI "analyse (a) [OPTIONS] TARGET"
+Report on all installed packages for \fITARGET\fP.
+.P
+
+.IR "TARGET" ":"
+.HP
+.B use
+.br
+Will analyse the installed with USE flags for output results.
+.HP
+.B pkguse
+.br
+Will analyse the USE flags information from the installed pkg's 'PKGUSE' file which contains
+only flags settings from /etc/portage/package.use at the time of installation.
+.HP
+.B keywords
+.br
+Will analyse the recorded keywords for output results.
+.HP
+.B unmask
+.br
+Will analyse the installed packages and portage trees for pkgs that require unmasking and report them.
+.br
+.P
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-u, \-\-unset
+.br
+Will also include any USE flags used that were not enabled for some packages.
+.HP
+.B \-v, \-\-verebose
+.br
+Gives more detail about the results found and the current task being performed.
+
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+analyse a --verbose --unset use
+.EE
+.br
+Report on all use flags used to install the packages. (--unset) Include in the report all flags
+that have been used but also were not set enabled for some packages.
+(--verbose) Also list the packages that used the USE flag setting.
+The report will break down the useage and report the USE flag up to 3 times indicating its
+setting {"+","-"," "= unset} prepended to the flag name.
+It will also color the output, red = Disabled, blue = Enabled, plain text = unset
+.br
+
+.SS
+.BI "rebuild (r) [OPTIONS] TARGET"
+Create a list all packages for \fITARGET\fP settings that are needed for
+other than the default settings.
+
+.IR "TARGET" ":"
+.HP
+.B use
+.br
+Will analyse the USE flags for output results.
+.HP
+.B keywords
+.br
+Will analyse the keywords for output results.
+.HP
+.B unmask
+.br
+Will analyse the installed packages and portage trees for pkgs that require
+unmasking and produce output/a new /etc/portage/package.unmask file.
+.P
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-a, \-\-all
+.br
+Create files/output for all TARGET(s) found to need it. (not Implemented yet)
+.HP
+.B \-e, \-\-excact
+.br
+Will prepend the pkg with = as well as use the version information for the entries.
+.br
+eg.: =CAT/PKG-VER flag1 flag2
+.HP
+.B \-p, \-\-pretend
+.br
+Sends the output to the screen instead of a file.
+.HP
+.B \-v, \-\-verebose
+.br
+Gives more detail about the results found and the current task being performed.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+analyse rebuild -p use
+.EE
+.br
+Analyse the installed packages database and current system USE flag settings
+ and output the results in the form of:
+.br
+
+.br
+.EX
+CAT/PKG -flag1 -flag2 flag3 flag4...
+
+.SS
+.BI "clean (c) [OPTIONS] TARGET"
+Clean all packages for \fITARGET\fP settings that are found with obsolete settings
+for the current settings and pkg ebuild. (not Implemented yet)
+
+.IR "TARGET" ":"
+.HP
+.B use
+.br
+Will analyse the USE flags and /etc/portage/package.use file(s) for entries that
+are redundant or no longer used by the pkg.
+.HP
+.B keywords
+.br
+Will analyse the keywords and /etc/portage/package.keywords file(s) for entries
+that are no longer needed.
+.HP
+.B unmask
+.br
+Will analyse the installed packages, /etc/portage/package.unmask file(s) and
+portage trees for pkgs that no longer require unmasking.
+.P
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-a, \-\-all
+.br
+Clean files/output for all TARGET(s) found to need it. (not Implemented yet)
+.HP
+.B \-p, \-\-pretend
+.br
+Sends the output to the screen instead of a file.
+.HP
+.B \-v, \-\-verebose
+.br
+Gives more detail about the results found and the current task being performed.
+
+
+.SH "BUGS"
+Submit bug reports to http://bugs.gentoo.org.
+
+.SH "AUTHORS"
+.br
+Brian Dolbec <brian.dolbec@gmail.com>, 2010
diff --git a/man/eclean.1 b/man/eclean.1
index 7d785af..651cc71 100644
--- a/man/eclean.1
+++ b/man/eclean.1
@@ -21,7 +21,7 @@ whatever may still be useful, for instance to downgrade a package without downlo
its sources for the second time, or to reinstall a package you unmerge by mistake
without recompiling it. Sure, it's also a mode in which your DISTDIR and PKGDIR will
stay rather big (although still not growing infinitely). For the 'distfiles', this
-mode is also quit slow mode because it requiries some access to the whole Portage tree.
+mode is also quite slow because it requiries some access to the whole Portage tree.
.PP
If you use the \-\-destructive option, eclean will only protect files corresponding to
some currently installed package (taking their exact version into account). It will
@@ -76,6 +76,8 @@ Units are: y (years), m (months), w (weeks), d (days) and h (hours).
.TP
\fB\-h, \-\-help\fP display the help screen
.TP
+\fB\-v, \-\-verbose\fP display more verbose messages during processing
+.TP
\fB\-V, \-\-version\fP display version informations
.SS "Actions"
.TP
@@ -126,9 +128,16 @@ if a line contains a package name with an exclamation mark in front ("!sys\-apps
then this package will be excluded from protection. This is only useful if the category
itself was protected.
.IP o
-for distfiles protection, a line can also a filename to protect. This is useful if you have
-there some files which are not registered by the ebuilds, like OpenOffice.org i18n files
-("helpcontent_33_unix.tgz" for instance).
+for distfiles protection, a line can also be a filename to protect. This is useful if you have
+some files which are not registered by the ebuilds, like OpenOffice.org i18n files
+("helpcontent_33_unix.tgz" for instance). Another example are sources you want to protect
+that do not have an ebuild in the tree or any currently installed overlays.
+.IP o
+eclean will also inform you of any deprecated installed packages that are on your system as well
+as if it was able to locate the source filename(s) in order to protect them. If you want to protect
+all installed distfile sources, run elcelan in pretend mode first. Then check which sources it was
+not able to find the filename(s) for and add entries for them in the distfiles.exclude file before
+running eclean again.
.LP
By default, if it exists, /etc/eclean/packages.exclude (resp. distfiles.exclude) will be use
when action is "packages" (resp. "distfiles"). This can be overide with the \-\-exclude\-file
@@ -156,9 +165,14 @@ From a crontab, silently clean packages in the safest mode, and then distfiles i
mode but protecting files less than a week old, every sunday at 1am:
.br
.B 0 1 * * sun \ \ eclean \-C \-q packages ; eclean \-C \-q \-d \-t1w distfiles
+.SH "NOTE"
+.TP
+While running and searching distfiles for cleaning, elcean will report any deprecated packages
+it finds installed on your system. Those sources may not be protected if the SRC_URI is not recorded
+in the installed package database. The SRC_URI is no longer recorded by recent portage/pkgcore versions.
.".SH "BUGS"
.".TP
-."The policy used to decide wether a distfile can be removed or not relies on the SRC_URI variables ."of ebuilds. It means that if an ebuild uses files that are not part of its SRC_URI, eclean will ."probably remove them. This are ebuilds bugs, please report them as such on ."http://bugs.gentoo.org.
+."The policy used to decide if a distfile can be removed or not relies on the SRC_URI variables ."of ebuilds. It means that if an ebuild uses files that are not part of its SRC_URI, eclean will ."probably remove them. This are ebuilds bugs, please report them as such on ."http://bugs.gentoo.org.
.".TP
."In safest mode (default, without the \-\-destructive option), this script can be very slow. There
."is not much to do about it without hacking outside of the portage API.
@@ -169,8 +183,9 @@ The Gentoo forum thread that gave birth to eclean:
.TP
The bug report requesting eclean inclusion in gentoolkit:
.B http://bugs.gentoo.org/show_bug.cgi?id=33877
-.TP
-Yacleaner, one of the other similar tools:
-.B http://blog.tacvbo.net/data/files/yacleaner/
.SH "AUTHORS"
Thomas de Grenier de Latour (tgl) <degrenier@easyconnect.fr>
+.br
+modular re-write by:
+.br
+Brian Dolbec (dol-sen) <brian.dolbec@gmail.com>
diff --git a/man/epkginfo.1 b/man/epkginfo.1
index cefe602..bfab2d7 100644
--- a/man/epkginfo.1
+++ b/man/epkginfo.1
@@ -1,34 +1,38 @@
-.TH "epkginfo" "1" "0.4.1" "Ned Ludd" "gentoolkit"
+.TH "EPKGINFO" "1" "August 2009" "GENTOOLKIT"
+
.SH "NAME"
-.LP
epkginfo \- Displays metadata information from packages in portage
-.SH "SYNTAX"
-.LP
-epkginfo [\fIpackage\-cat/\fP]package
+
+.SH "SYNOPSIS"
+.BI "epkginfo " "[options] " "package name or atom"
+
+.SH "DESCRIPTION"
+.B Epkginfo
+is a shortcut to \fBequery meta\fP. For all available options and more
+examples, see \fBman equery\fP.
+
.SH "EXAMPLES"
-$ epkginfo app\-portage/gentoolkit
-.br
-\fBPackage:\fR app\-portage/gentoolkit
-.br
-\fBHerd:\fR tools\-portage
-.br
-\fBMaintainer:\fR tools\-portage
-.br
-\fBLocation:\fR /usr/portage/app\-portage/gentoolkit
-.br
-\fBKeywords:\fR gentoolkit\-0.2.2:
-.br
-\fBKeywords:\fR gentoolkit\-0.2.3: mips
-.br
-\fBKeywords:\fR gentoolkit\-0.2.3\-r1: ppc ppc64 alpha arm s390 amd64 hppa x86 sparc ia64 m68k sh
-.br
-\fBKeywords:\fR gentoolkit\-0.2.4_pre3:
-.br
-\fBKeywords:\fR gentoolkit\-0.2.4_pre4:
-.br
-\fBKeywords:\fR gentoolkit\-0.2.4_pre5: ~arm ~hppa ~x86 ~m68k ~amd64 ~ppc ~sh ~x86\-fbsd ~ia64 ~alpha ~sparc ~ppc64 ~sparc\-fbsd ~mips ~s390
+.nf
+ * app-portage/gentoolkit [portage]
+Location: /usr/portage/app-portage/gentoolkit
+Herd: tools-portage (tools-portage@gentoo.org)
+Maintainer: None specified
+Upstream: None specified
+Keywords: 0.2.4.5: alpha amd64 arm hppa ia64 m68k ~mips ppc ppc64
+ s390 sh sparc x86 -x86-fbsd
+Keywords: 0.3.0_rc5: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips
+ ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd
+ ~x86-fbsd
+Keywords: 0.3.0_rc6: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips
+ ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd
+ ~x86-fbsd
+Keywords: 0.3.0_rc7: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips
+ ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd
+ ~x86-fbsd
+.fi
+
.SH "AUTHORS"
-.LP
-Ned Ludd <solar@gentoo.org>
+.LP
+Douglas Anderson <douglasjanderson@gmail.com>
.SH "BUGS"
Please report any bugs to http://bugs.gentoo.org
diff --git a/man/equery.1 b/man/equery.1
index 27b8078..1a9c5e6 100644
--- a/man/equery.1
+++ b/man/equery.1
@@ -1,278 +1,636 @@
-.TH "equery" "1" "Oct 2005" "gentoolkit" ""
+.TH "EQUERY" "1" "August 2009" "GENTOOLKIT"
.SH "NAME"
-equery \- Gentoo: Package Query Tool
+equery \- Gentoo Package Query Tool
+
.SH "SYNOPSIS"
-.B equery
-.I [global\-opts] command [local\-opts]
-.PP
+.BI "equery " "[global-options] " "module " "[local-options]"
.SH "DESCRIPTION"
-equery is a flexible utility which may display various information about
-packages, such as the files they own, their USE flags, the md5sum
-of each file owned by a given package, and many other things.
-
-.SH "OPTIONS"
-The 'command' is the only mandatory option to equery. Most commands require
-a 'pkgspec' option, which is described by <cat/>packagename<\-version>;
-namely, the package name is mandatory, while the category and version are
-optional.
-
-[global\-opts] may be one of:
-
-.B \-q, \-\-quiet
-causes minimal output to be emitted
-.PP
-.B \-C, \-\-nocolor
-turns off colours
-.PP
+.B Equery
+is a collection of modules for querying the state of Gentoo packages, files and USE flags.
+
+.SH "GLOBAL OPTIONS"
+.HP
.B \-h, \-\-help
-displays a help summary
-.PP
-.B \-V, \-\-version
-displays the equery version
-.PP
+.br
+Output a help message.
+.HP
+.B \-q, \-\-quiet
+.br
+Be less verbose where possible. In some modules, this option can increase the output speed.
+.HP
+.B \-C, \-\-no-color
+.br
+Do not colorize output.
+.HP
.B \-N, \-\-no\-pipe
-turns off pipe detection
-.PP
+.br
+Turn off automatic pipe detection. Use this option if you do not want
+.B equery
+to detect if the output is being directed to the screen or to another program and adjust color and verbosity accordingly.
+.HP
+.B \-V, \-\-version
+.br
+Display \fBGentoolkit\fP's version. Please include this in all bug reports. (see
+.B BUGS
+below)
-Only one command will actually be run, at most. The possible commands are:
-.TP
-.B belongs <local\-opts> file
-This command lists all packages owning the specified file.
-.br
-Note: Normally, only one package will own a file. If multiple packages own the
-same file, it usually consitutes a problem, and should be reported (http://bugs.gentoo.org).
-.br
-.IP
-<local\-opts> is either or both of:
-.br
-.B \-c, \-\-category cat
-only search in category cat
-.br
-.B \-f, \-\-full\-regex
-supplied query is a regex
-.br
-.B \-e, \-\-earlyout
-stop when first match found
-
-.PP
-.B check pkgspec
-This command checks the files of the specified package against recorded MD5
-sums and timestamps.
-.PP
-.TP
-.B depends <local\-opts> pkgspec
-This command displays all dependencies matching pkgspec.
-.br
-<local\-opts> is either or both of:
-.br
-.B \-a, \-\-all\-packages
-search in all available packages (slow)
-.br
-.B \-d, \-\-direct
-search direct dependencies only (default)
-.br
+.SH "MODULES"
+.B Equery
+uses a system of modules. Each module has both a long and short name. The list below uses the notation "\fBmodule (m)\fP", where \fIm\fP is the short name and \fImodule\fP is the long name.
+.P
+You can view the
+.B help
+message for a specific module by using
+.BR "-h" ", " "--help "
+as either a global option (after
+.B equery
+and before the module name) or as a local option (after the module name).
+
+.SS
+.BI "belongs (b) [OPTIONS] " "FILE"
+List the package that owns \fIFILE\fP.
+.P
+.BI Note:
+Normally, only one package will own \fIFILE\fP. If multiple packages own the
+same file it should be reported. (see
+.B BUGS
+below)
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-f, \-\-full-regex
+.br
+The supplied query is a regular expression.
+.HP
+.B \-e, \-\-early-out
+.br
+Stop when the first match is found. This is generally a safe optimization when searching for the owner of a single file.
+.HP
+.B \-n, \-\-name-only
+.br
+Do not print the version.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery belongs --early-out /usr/bin/euse
+.EE
+.br
+Find out what package installed a certain command.
+.EX
+.HP
+emerge -p $(equery -q belongs -nf '^/usr/bin/g?vim.*')
+.EE
+.br
+Tell
+.B emerge
+to reinstall or update any package that installed a file matching a regular expression.
+
+.SS
+.BI "changes (c) [OPTIONS] " "PKG"
+Display the Gentoo ChangeLog entry for the latest installable version of \fIPKG\fP.
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-l, \-\-latest
+.br
+Display only the latest ChangeLog entry. It's not uncommon for changes to be prepended to the ChangeLog without a version header if the changes did not require a version bump. Use this option to display such entries.
+.HP
+.B \-f, \-\-full
+.br
+Display the full ChangeLog.
+.br
+\fBHint\fP: Try piping (|) the output to a pager, like 'less'.
+.HP
+.BI "\-\-limit=" "NUM"
+.br
+Limit the \fINUM\fP of entries displayed. Use this option in conjunction with \fB--full\fP. \fB--limit=3\fP would display the three latest entries.
+.HP
+.BI "\-\-from=" "VER"
+.br
+Set which \fIVER\fP to display from. Using this option by itself is equivalent to passing \fBchanges\fP a ranged package atom, e.g., '>=foo/bar-1.5'. It can be used in conjunction with \fB--to\fP to request a more complex range of entries.
+.HP
+.BI "\-\-to=" "VER"
+.br
+Set which \fIVER\fP to display to. (See \fB--from\fP)
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery changes portage
+.EE
+.br
+Display the Gentoo ChangeLog entry for the latest installable version of Portage.
+.EX
+.HP
+equery changes '=sys-apps/portage-2.1.6*'
+.EE
+.br
+Use Portage's atom syntax. (See \fBman 5 ebuild\fP)
+.EX
+.HP
+equery changes portage --from=2.2_rc1 --to=2.2
+.EE
+.br
+Display any ChangeLog entry within a range of versions.
+
+.SS
+.BI "check (k) [OPTIONS] " "PKG"
+Check timestamps and MD5 sums for files owned by \fIPKG\fP, where \fIPKG\fP is an installed package.
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-f, \-\-full-regex
+.br
+The supplied query is a regular expression.
+.HP
+.B \-o, \-\-only-failures
+.br
+Only display packages which don't pass all checks.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery check --only-failures '*'
+.EE
+.br
+Verify timestamps and MD5 sums for all installed packages and show only packages which fail checks.
+.EX
+.HP
+equery check 'dev-python/*' dev-lang/python
+.EE
+.br
+Verify every installed package in the \fBdev-python\fP category, and Python itself.
+
+.SS
+.BI "depends (d) [OPTIONS] " "PKG"
+List all packages that depend on \fIPKG\fP.
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-a, \-\-all-packages
+.br
+Include dependencies that are not installed. This can take a while.
+.HP
.B \-D, \-\-indirect
-search indirect dependencies (very slow)
-.br
-.B \-\-depth=n
-Limit depth of indirect dependency tree to n levels. Setting \-\-depth=0 is the same as not specifing \-\-indirect.
-.PP
-.TP
-.B depgraph <local\-opts> pkgspec
-This command display a dependency tree for pkgspec, by default indented to reflect
-how dependancies relate to each other.
-.br
-.IP
-<local\-opts> is either or both of:
-.br
-.B \-U, \-\-no\-useflags
-do not show USE flags.
-.br
-.B \-l, \-\-linear
-do not use fancy formatting
-.br
-.B \-\-depth=n
-Limit depth of dependency graph to n levels.
-.PP
-.TP
-.B files <local\-opts> pkgspec
-This lists files owned by a particular package, optionally with extra
-information specified by <local\-opts>
-.br
+.br
+Search for both direct and indirect dependencies.
+.HP
+.BI "\-\-depth=" "NUM"
+.br
+Limit the indirect dependency tree to a depth of \fINUM\fP. \fB--depth=0\fP is equivalent to not using \fB--indirect\fP.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery depends --indirect xulrunner
+.EE
+.br
+Figure out why a package got installed on your system.
-<local\-opts> is any combination of:
-.br
-.B \-\-timestamp
-output the timestamp of each file
-.br
-.B \-\-md5sum
-output the md5sum of each file
-.br
-.B \-\-type
-output the type of each file
-.br
+.SS
+.BI "depgraph (g) [OPTIONS] " "PKG"
+Display a direct dependency graph for every matching version of \fIPKG\fP. A dependency graph is an
+indented tree showing the relationship between packages and their dependencies.
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-A, \-\-no-atom
+.br
+Do not show the dependency atom that match the package.
+.HP
+.B \-U, \-\-no-useflags
+.br
+Do not show USE flags.
+.HP
+.B \-l, \-\-linear
+.br
+Do not format the graph by indenting dependencies. This option will print the
+recursion depth in square brackets before the package name for easier viewing
+in narrow terminals.
+.HP
+.BI "\-\-depth=" "NUM"
+.br
+Limit the dependency graph to a depth of \fINUM\fP. \fB--depth=0\fP means no
+maximum depth. Default depth is set to 1.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery depgraph --depth=0 portage
+.EE
+.br
+View a full tree of all direct and indirect compile-time, run-time, and post-merge dependencies for a package.
+
+.SS
+.BI "files (f) [OPTIONS] " "PKG"
+List files and directories installed by \fIPKG\fP.
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-m, \-\-md5sum
+.br
+Include the file's MD5 sum in the output.
+.HP
+.B \-s, \-\-timestamp
+.br
+Include the file's timestamp in the output.
+.HP
+.B \-t, \-\-type
+.br
+Include the file type in the output.
+.HP
.B \-\-tree
-display results in a tree (turns off all other options)
-.br
-.B \-\-filter=<rules>
-filter output based on files type or path
-.br
-.B \t<rules>
-is a comma separated list of filtering rules. Available rules are:
-.br
-.B \t\tdir\
-regular directories
-.br
-.B \t\tobj\
-regular files
-.br
-.B \t\tsym\
-symbolic links
-.br
-.B \t\tdev\
-device nodes
-.br
-.B \t\tfifo
-named pipes
-.br
-.B \t\tpath
-shortest paths where some files where installed
-.br
-.B \t\tconf
-configuration files (based on $CONFIG_PROTECT)
-.br
-.B \t\tcmd\
-user commands (based on $PATH)
-.br
-.B \t\tdoc\
-documentation files (from /usr/share/doc)
-.br
-.B \t\tman\
-manpages (from /usr/share/man)
-.br
-.B \t\tinfo
-info pages (from /usr/share/info)
-.PP
-.TP
-.B hasuse <local\-opts> useflag
-This command lists packages matching a particular USE flag in a user\-specified combination
-of installed packages, packages which are not installed, the portage tree, and
-the portage overlay tree.
-
-<local\-opts> must not include only \-I;
-if \-I is used, \-p and/or \-o must be also be present. By default, only installed
-packages are searched. \-o searches only the overlay tree [and possibly
-installed packages],
-.I not
-the main portage tree.
-
-.B \-i, \-\-installed
-search installed packages (default)
-.br
-.B \-I, \-\-exclude\-installed
-do not search installed packages
-.br
-.B \-p, \-\-portage\-tree
-also search in portage tree (/usr/portage)
-.br
-.B \-o, \-\-overlay\-tree
-also search in overlay tree (/usr/local/portage)
-.PP
-.TP
-.B list <local\-opts> pkgspec
-This command lists packages matching pkgspec in a user\-specified combination
-of installed packages, packages which are not installed, the portage tree, and
-the portage overlay tree. By default the list command searches for partial name matches.
-
-<local\-opts> \-I cannot be used by itself;
-if \-I is used, \-p and/or \-o must be also be present. By default, only installed
-packages are searched. \-o searches only the overlay tree [and possibly
-installed packages],
-\fInot\fR the main portage tree.
-
-.B \-i, \-\-installed
-search installed packages (default)
-.br
-.B \-I, \-\-exclude\-installed
-do not search installed packages
-.br
-.B \-p, \-\-portage\-tree
-also search in portage tree (/usr/portage)
-.br
-.B \-o, \-\-overlay\-tree
-also search in overlay tree (/usr/local/portage)
-.br
-.B \-f, \-\-full\-regex
-query is a regular expression
-.br
-.B \-e, \-\-exact\-name
-list only those packages that exactly match
-.br
+.br
+Display files in a tree format. This option turns off all other local options.
+.HP
+.BI "\-f, \-\-filter=" "RULES"
+.br
+Filter output by file type.
+.HP
+RULES
+.br
+A comma-separated list (no spaces); choose from:
+.br
+.B dir, obj, sym, dev, path, conf, cmd, doc, man, info
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery files --tree vlc
+.EE
+.br
+View a file tree of all files installed by a package.
+.EX
+.HP
+equery files --filter=cmd vlc
+.EE
+.br
+Find out where a package installed its executables.
+
+.SS
+.BI "has (a) [OPTIONS] " "KEY VALUE"
+List all installed packages that have a given \fIPKG\fP match.
+
+\fBNote\fP: \fBKEY\fP is case sensitive. Also \fBhas\fP does not currently have the ability to inteligently compare values dependending on the type of information being looked up. It performs a simple string match. It can only list which packages have the matching \fBVALUE\fP as given on the command line. It is a general purpose lookup for most information available via portage's dbapi.aux_get() function. Warning the quality of the results printed is dependant on the quality of the search (given the limited comparison method) and the recorded data available in the vardb. (See \fIEXAMPLES\fP)
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-I, \-\-exclude-installed
+.br
+Exclude installed packages from being output.
+.HP
+.B \-o, \-\-overlay-tree
+.br
+Include package from overlays in the search path.
+.HP
+.B \-p, \-\-portage-tree
+.br
+Include all packages from the Portage tree in the search path. Use this option to search through all standard Gentoo packages, including those that are not installed.
+.HP
+.B \-F, \-\-format=\fITMPL\fP
+.br
+Customize the output format of the matched packages using the template string \fITMPL\fP. See the \fB\-\-format\fP option for \fBlist\fP below for a description of the \fITMPL\fP argument.
+.P
+.IR "OUTPUT" ":"
+.HP
+(See \fIOUTPUT\fP for \fBlist\fP below)
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery has SLOT 2.4
+.EE
+.br
+View all installed Gentoo packages that have a recorded SLOT = "2.4".
+.EX
+.HP
+equery has repository sunrise
+.EE
+.br
+View all installed Gentoo packages that were recorded to be installed from ebuilds from the "sunrise" overlay.
+.EX
+.HP
+equery has EAPI 2
+.EE
+.br
+View all installed Gentoo packages that were installed with ebuilds with a recorded EAPI of "2".
+
+.SS
+.BI "hasuse (h) [OPTIONS] " "USE"
+List all installed packages that have a given \fIPKG\fP flag.
+
+\fBNote\fP: \fBhasuse\fP does not currently have the ability to display if packages are built with the given USE flag or not. It can only list which packages have the flag as an option. (See \fIEXAMPLES\fP)
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-I, \-\-exclude-installed
+.br
+Exclude installed packages from being output.
+.HP
+.B \-o, \-\-overlay-tree
+.br
+Include package from overlays in the search path.
+.HP
+.B \-p, \-\-portage-tree
+.br
+Include all packages from the Portage tree in the search path. Use this option to search through all standard Gentoo packages, including those that are not installed.
+.HP
+.B \-F, \-\-format=\fITMPL\fP
+.br
+Customize the output format of the matched packages using the template string \fITMPL\fP. See the \fB\-\-format\fP option for \fBlist\fP below for a description of the \fITMPL\fP argument.
+.P
+.IR "OUTPUT" ":"
+.HP
+(See \fIOUTPUT\fP for \fBlist\fP below)
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery hasuse -pI perl
+.EE
+.br
+View all Gentoo packages that have the "perl" USE flag, exluding installed packages.
+.EX
+.HP
+USE="perl"; for PKG in $(equery -q hasuse $USE); do echo $PKG: $(equery -q uses $PKG |grep $USE); done
+.EE
+.br
+This Bash one-liner uses \fBhasuse\fP to find a list of packages that have a certain USE flag, and \fBuses\fP to check whether the flag is enabled or disabled. Modify \fBUSE="perl"\fP to change the query.
+
+.SS
+.BI "list (l) [OPTIONS] " "PKG"
+List installed versions of \fIPKG\fP or all packages matching the query pattern.
+
+.IR "LOCAL OPTIONS" ":"
+.HP
.B \-d, \-\-duplicates
-only list installed duplicate packages
-.br
+.br
+List only packages with more than one version installed.
+.HP
+.B \-f, \-\-full-regex
+.br
+The supplied query is a regular expression.
+.HP
+.B \-m, \-\-mask-reason
+.br
+Print the reason why a package was masked.
+.HP
+.B \-I, \-\-exclude-installed
+.br
+Exclude installed packages from being output.
+.HP
+.B \-o, \-\-overlay-tree
+.br
+Include package from overlays in the search path.
+.HP
+.B \-p, \-\-portage-tree
+.br
+Include all packages from the Portage tree in the search path. Use this option to search through all standard Gentoo packages, including those that are not installed.
+.HP
+.B \-F, \-\-format=\fITMPL\fP
+.br
+Customize the output format of the matched packages using the template string \fITMPL\fP. \fITMPL\fP can contain the following placeholders:
+.RS
+.TP
+\fB$cp\fP \- Contains the category and the package name only (e.g 'app\-portage/gentoolkit').
+.TP
+\fB$cpv\fP \- Contains the category, the package name and the full version (e.g. 'app\-portage/gentoolkit\-0.3.0_rc10\-r1').
+.TP
+\fB$category\fP \- Contains just the category (e.g. 'app\-portage').
+.TP
+\fB$name\fP \- Contains just the package name (e.g. 'gentoolkit').
+.TP
+\fB$version\fP \- Contains the package version (without the revision) (e.g. '0.3.0_rc10').
+.TP
+\fB$revision\fP \- Contains the package revision (e.g. 'r1').
+.TP
+\fB$fullversion\fP \- Contains the package version including its revision (e.g. '0.3.0_rc10\-r1').
+.TP
+\fB$slot\fP \- Contains the package's slot.
+.TP
+\fB$repo\fP \- Contains the name of the package's repository (e.g. 'gentoo').
+.TP
+\fB$mask\fP \- The Mask\-status field (\fB~M\-??\fP), see \fIOUTPUT\fP below for an explanation.
+.TP
+\fB$mask2\fP \- Contains a verbose description of the packages masking status.
+.TP
+\fB$location\fP \- The Location field (\fBIPO\-\fP), see \fIOUTPUT\fP below for an explanation.
+.P
+Apart from the above placeholders the template string can contain arbitrary
+text as desired. Similar to bash variables, curly braces can be used to
+disambiguate the variable names from the enclosing text.
+.RE
+.P
+.IR "OUTPUT" ":"
-\fBOutput:\fR
+.EX
+$ equery list binutils
+ * Searching for binutils ...
+ [I--] [??] sys-devel/binutils-2.18-r1:i686-pc-linux-gnu-2.18
+ [IP-] [ ~] sys-devel/binutils-2.19.1-r1:i686-pc-linux-gnu-2.19.1
+.EE
+.HP
+Location field (\fB[IPO-]\fP):
+.br
+The first field shows the location and install status of the package. It consists of three letters in square brackets. \fBI\fP indicates the package is currently installed. \fBP\fP indicates the package is available in the Portage tree. \fBO\fP indicates the package is available in at least one overlay. \fB-\fP is a place holder and has no meaning. \fB[I-O]\fP would mean that the package is installed and available from an overlay, but not available from the Portage tree.
+.HP
+Mask-status field (\fB[ ~M-??]\fP):
+.br
+The second field shows the mask status of the package. Empty brackets indicate that the package is unmasked. A \fB~\fP means the package is masked by keyword, e.g., you are running a stable system and the package is marked testing). \fBM\fP means hard masked, e.g., the package maintainer has determined the package is unfit for widespread usage. \fB-\fP means arch masked, e.g., you are running an amd64 system, but this package only works on x86. Lastly, \fB??\fP only occurs when the location field is \fB[I--]\fP. Together, they indicate that the package was installed from the Portage tree or an overlay, but has since been removed from that tree; therefore \fBequery\fP can not determine a mask status for it.
+.HP
+Package name:
+.br
+The third field is the full package name and version.
+.HP
+Slot:
+.br
+The fourth field, after the colon, is the package's slot. \fB0\fP is the default slot. To find all packages with multiple slots installed, use \fB--duplicates\fP.
+.P
+\fBNote:\fP Because it takes extra processing time to determine the location, mask status and slot, you can speed up output by passing the \fB--quiet\fP global option to \fBequery\fP when you don't care about the extra information.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery list '*'
+.EE
+.br
+List all installed packages. This is equivalent to '\fBequery list\fP' in \fBGentoolkit\fP versions prior to 0.3.0.
+.EX
+.HP
+equery list -op mozilla-firefox
+.EE
+.br
+List all available versions of the package exactly matching 'mozilla-firefox'. This is equivalent to '\fBequery list --exact-name -o -p mozilla-firefox\fP' in \fBGentoolkit\fP versions prior to 0.3.0.
+.EX
+.HP
+equery list '*zilla*'
+.EE
+.br
+List all packages that contain (fuzzy match) 'zilla'. This is equivalent to '\fBequery list zilla\fP' in \fBGentoolkit\fP versions prior to 0.3.0.
+.EX
+.HP
+equery list 'www-client/*'
+.EE
+.br
+List all packages in the category \fBwww-client\fP. This is equivalent to '\fBequery list --category=www-client\fP' in \fBGentoolkit\fP versions prior to 0.3.0.
+.EX
+.HP
+equery list --duplicates '*'
+.EE
+.br
+List all packages with more than one version installed. This is equivalent to '\fBequery list --duplicates\fP' in \fBGentoolkit\fP versions prior to 0.3.0.
+.EX
+.HP
+equery list -F '$cp:$slot' '*'
+.EE
.br
-The list command searches packages for the name given. If found, the following info will be displayed: the package location between the first square brackets (I for Installed packages, P for Portage, O for Overlay), the possible masks between the second (~ by keyword, - by arch or M hard masked), then the category and complete name and last of all, the slot in which the package is stored.
+Get a list of slotted atoms for all installed packages.
+.EX
+.HP
+equery list -po -F '[$location] [$mask] $cpv:$slot [$repo]' '*'
+.EE
+.br
+Show all packages in the default (verbose) output format but also include their repository name.
-\fBExamples:\fR
+.SS
+.BI "meta (m) [OPTIONS] " "PKG"
+Display metadata about \fIPKG\fP.
-equery list zilla \- list all installed versions of packages containing the string 'zilla'
+\fBmeta\fP reads a file called metadata.xml which must be included with all Portage tree packages. \fBmeta\fP does not read ebuilds, so it can only return version independent metadata. Since until now there had not been an easy way for users to view metadata.xml, and because package maintainers are only required to fill out a very small portion of the file, there are still many packages without detailed metadata available. For more information about metadata.xml, see:
+.br
+.EX
+http://www.gentoo.org/proj/en/devrel/handbook/handbook.xml?part=2&chap=4
+.EE
-equery list \-\-exact\-name x11\-libs/gtk+ \- list all installed versions of x11\-libs/gtk+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-d, \-\-description
+.br
+Show an extended package description.
+.HP
+.B \-H, \-\-herd
+.br
+Show the herd(s) for the package. When not piping and not passing \fB--quiet\fP as a global option, also show the herd's email address. (shown by default)
+.HP
+.B \-k, \-\-keywords
+.br
+Show keywords for all matching versions. \fBkeywords\fP does not list all keywords for all versions. Instead, it filters the list to make it easier to spot versions that need bumping or are okay to remove from the tree. It filters by slot. For example:
+.br
+Keywords: 1.35.0-r3:\fB0\fP:
+.br
+Keywords: 1.35.0-r5:\fB0\fP: amd64 hppa ppc x86 ~alpha ~arm ~ia64 ~mips ~ppc64 ~s390 ~sh ~sparc
+.br
+In this output from \fBequery meta boost\fP, -r5 is the highest available version in slot 0, so all keywords are listed. The actual keywords for -r3 are "~amd64 ~hppa ~ppc ~x86", but since a higher version in the same slot has the same or more stable keywording, they are filtered out. Arch mask keywords (-*) are always shown.
+.HP
+.B \-m, \-\-maintainer
+.br
+Show the package maintainer(s) email address. If the metadata is available, also show the maitainer's name and/or job description. (shown by default)
+.HP
+.B \-u, \-\-useflags
+.br
+Show per-package USE flag descriptions. Per-package USE flag descriptions are sometimes added to metadata.xml if the affect of the USE flag is unusual, or if the USE flag is rare enough to be undefined in the global definition file. \fBequery uses\fP now displays these same local descriptions as well, so this option is left in \fBmeta\fP for completeness only.
+.HP
+.B \-U, \-\-upstream
+.br
+Show information about the package's upstream project, including the author's email, upstream bug tracker or upstream documentation. At the time of writing, most maintainers do not provide this information. (shown by default)
+.HP
+.B \-x, \-\-xml
+.br
+Dump the plain XML file to the screen.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery meta gnucash
+.EE
+.br
+Show general information about maintainership, including herd, maintainer and upstream.
+.EX
+.HP
+equery meta --description screen
+.EE
+.br
+See if the package maintainer has provided an extended description.
+.EX
+.HP
+equery -N meta -H gnome |grep -o --color=never '[^( ]*@gentoo.org'
+.EE
+.br
+Extract the herd's email address to let them know they're doing a great job. Remember, bug reports should go to bugs.gentoo.org. The above example will extract one or more emails if available, or return nothing if the herd is \fBno-herd\fP.
-equery list \-\-full\-regex '(mozilla\-firefox|mozilla\-thunderbird)' \- list all installed versions of mozilla\-firefox and mozilla\-thunderbird
+.SS
+.BI "size (s) [OPTIONS] " "PKG"
+Print total size of files contained in a given \fIPKG\fP.
-equery list \-\-duplicates \- list all installed slotted packages
-.PP
-.TP
-.B size <local\-opts> pkgspec
-This command outputs the number of files in the specified package, as well as
-their total size in an appropriate unit.
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-b, \-\-bytes
+.br
+Report package size in bytes.
+.HP
+.B \-f, \-\-full-regex
+.br
+The query is a regular expression.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery -q size 'www-client/*'
+.EE
+.br
+Get a one-line summary of the number of files and total size (in bytes) of those files for each installed package in a category.
-The possible values for <local\-opts>, if specified, are:
-.br
-.B \-b, \-\-bytes
-report size in bytes
-.br
-.B \-f, \-\-full\-regex
-query is a regular expression
-.br
-.B \-e, \-\-exact\-name
-list only those packages that exactly match
-.PP
-.TP
-.B uses <local\-opts> pkgspec
-display USE flags for pkgspec.
-
-The only possible value for <local\-opts>, if specified, is:
-.br
-.B \-a, \-\-all
-include all package versions
-.PP
-.B which pkgspec
-print full path to ebuild for package pkgspec
-.PP
+.SS
+.BI "uses (u) [OPTIONS] " "PKG"
+Display USE flag statuses and desriptions for a given \fRPKG\fP.
-.SH "Unimplemented Options"
-.PP
-.B changes
-.PP
-.B glsa \fR \- use glsa\-check for the time being.
-.PP
-.B stats
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-a, \-\-all
+.br
+Display all package versions. Without this option, \fBequery\fP will choose the best available version.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery uses app-misc/beagle
+.EE
+.br
+See which USE flags are enabled for a specific package.
+.EX
+.HP
+USE="perl"; for PKG in $(equery -q hasuse $USE); do echo $PKG: $(equery -q uses $PKG |grep $USE); done
+.EE
+.br
+This Bash one-liner uses \fBhasuse\fP to find a list of packages that have a certain USE flag, and \fBuses\fP to check whether the flag is enabled or disabled. Modify \fBUSE="perl"\fP to change the query.
+.SS
+.BI "which (w) [OPTIONS] " "PKG"
+Display the path to the ebuild that would be used by Portage with the current configuation.
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-m, \-\-include-masked
+.br
+Return the path to the hightest version ebuild available.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+less $(equery which xorg-server)
+.EE
+.br
+View the most recent installable ebuild.
.SH "BUGS"
-Many options aren't implemented. Command\-line parsing could use some work.
-.br
-Submit bug reports to http://bugs.gentoo.org
-.SH "AUTHORS"
-equery, original man page: Karl Trygve Kalleberg <karltk@gentoo.org>, 2003.
-.br
-Massive man page updates: Katerina Barone\-Adesi <katerinab@gmail.com>, 2004.
+Submit bug reports to http://bugs.gentoo.org.
+.SH "AUTHORS"
+Karl Trygve Kalleberg <karltk@gentoo.org>, 2003
+.br
+Katerina Barone\-Adesi <katerinab@gmail.com>, 2004
+.br
+Douglas Anderson <douglasjanderson@gmail.com>, 2009
diff --git a/man/eshowkw.1 b/man/eshowkw.1
new file mode 100644
index 0000000..29b49b8
--- /dev/null
+++ b/man/eshowkw.1
@@ -0,0 +1,47 @@
+.TH "eshowkw" "1" "" "Tomáš Chvátal" ""
+.SH "NAME"
+.LP
+eshowkw \- Gentoo: Tool to package keywords
+.SH "SYNOPSIS"
+.B eshowkw
+[OPTIONS] [\-\-] [package [package ...]]
+.SH "DESCRIPTION"
+Display keywords for specified package(s) or for package that is in the current working directory.
+
+.SH "OPTIONS"
+.TP
+.B package
+The packages to check. (The default is to read ebuilds in the current directory)
+.TP
+.B \-h | \-\-help
+Print usage.
+.TP
+.B \-v | \-\-version
+Show version and exit.
+.TP
+.B \-a=ARCH[,ARCH ...] | \-\-arch=ARCH[,ARCH ...]
+Display only specified architectures.
+.TP
+.B \-A {top,bottom} | \-\-align {top,bottom}
+Specify alignment for descriptions. (default: bottom)
+.TP
+.B \-T {archlist,versionlist} | \-\-top\-position {archlist,versionlist}
+Specify which fields we want to have in top listing. (default: archlist)
+.TP
+.B \-B | \-\-bold
+Print out each other column in bold for easier visual separation. (default: False)
+.TP
+.B \-C | \-\-color
+Force colored output (default: False)
+.TP
+.B \-O | \-\-overlays
+Search overlays (default: False)
+.TP
+.B \-P | \-\-prefix
+Display prefix keywords in output. (default: False)
+.TP
+.B \-S | \-\-ignore\-slot
+Treat slots as irelevant during detection of redundant packages. (default: False)
+.SH "BUGS"
+.LP
+Report bugs to <http://bugs.gentoo.org>.
diff --git a/man/glsa-check.1 b/man/glsa-check.1
index eee5151..be15992 100644
--- a/man/glsa-check.1
+++ b/man/glsa-check.1
@@ -1,4 +1,4 @@
-.TH "glsa-check" "1" "0.6" "Marius Mauch" "gentoolkit"
+.TH "glsa-check" "1" "0.3" "Marius Mauch" "gentoolkit"
.SH "NAME"
.LP
glsa\-check \- Gentoo: Tool to locally monitor and manage GLSAs
@@ -6,7 +6,7 @@ glsa\-check \- Gentoo: Tool to locally monitor and manage GLSAs
.LP
glsa\-check <\fIoption\fP> [\fIglsa\-list\fP]
-[\fIglsa\-list\fR] can contain an arbitrary number of GLSA ids, filenames containing GLSAs or the special identifiers 'all', 'new' and 'affected'
+[\fIglsa\-list\fR] can contain an arbitrary number of GLSA ids, filenames containing GLSAs or the special identifiers 'all' and 'affected'
.SH "DESCRIPTION"
.LP
This tool is used to locally monitor and manage Gentoo Linux Security Advisories.
@@ -21,19 +21,19 @@ Note: In order for this tool to be effective, you must regularly sync your local
.LP
.TP
.B \-l, \-\-list
-list all unapplied GLSA
+list the a summary for all GLSAs in glsa-list and whether they affect the system
.TP
.B \-d, \-\-dump, \-\-print
-show all information about the given GLSA
+show all information about the GLSAs in glsa-list
.TP
.B \-t, \-\-test
-test if this system is affected by the given GLSA
+test if this system is affected by the GLSAs in glsa-list and output the GLSA IDs
.TP
.B \-p, \-\-pretend
-show the necessary commands to apply this GLSA
+show the necessary steps to apply the GLSAs in glsa-list
.TP
.B \-f, \-\-fix
-try to auto\-apply this GLSA (experimental)
+try to auto\-apply the GLSAs in in glsa-list using emerge. This will only upgrade packages to later version, but not remove packages when no upgrade path exists (experimental)
.TP
.B \-i, \-\-inject
inject the given GLSA into the glsa_injected file
diff --git a/man/revdep-rebuild.1 b/man/revdep-rebuild.1
index a482f66..a03ed58 100644
--- a/man/revdep-rebuild.1
+++ b/man/revdep-rebuild.1
@@ -10,6 +10,9 @@ revdep\-rebuild scans libraries and binaries for missing shared library dependen
.TP
.B \-C | \-\-nocolor
Turn off colored output. (This option is also passed to portage.)
+.TP
+.B \-d | \-\-debug
+Print way too much information (uses bash's set -xv)
.TP
.B \-e | \-\-exact
Emerge the most recent version of found packages, without regard to SLOT.
@@ -40,15 +43,11 @@ Turn off the progress meter
.TP
.B \-q | \-\-quiet
Print less output and disable the progress meter. (This option is also passed to portage.)
-.TP
-.B \-u UTIL | \-\-no-util UTIL
-Do not use features provided by UTIL.
-UTIL can be one of portage-utils or pkgcore, or it can be a \fBquoted\fR space-delimited list.
.TP
.B \-v | \-\-verbose
More output. (Prints the revdep\-rebuild search environment.)
.TP
-.B All other options (including unrecognized ones) are passed to the emerge command. Single\-letter options may not be combined, so for example, \-pv is not valid. Please use \-p \-v.
+.B Options after -- are ignored by revdep-rebuild and passed directly to emerge.
.SH "CONFIGURATION"
revdep\-rebuild no longer uses hardcoded paths. To change the default behavior the following variables can be changed by the user.
diff --git a/pylintrc b/pylintrc
new file mode 100644
index 0000000..863a3f8
--- /dev/null
+++ b/pylintrc
@@ -0,0 +1,321 @@
+# lint Python modules using external checkers.
+#
+# This is the main checker controlling the other ones and the reports
+# generation. It is itself both a raw checker and an astng checker in order
+# to:
+# * handle message activation / deactivation at the module level
+# * handle some basic but necessary stats'data (number of classes, methods...)
+#
+[MASTER]
+
+# Specify a configuration file.
+#rcfile=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Profiled execution.
+profile=no
+
+# Add <file or directory> to the black list. It should be a base name, not a
+# path. You may set this option multiple times.
+ignore=CVS
+ignore=.svn
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Set the cache size for astng objects.
+cache-size=500
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+
+[MESSAGES CONTROL]
+
+# Enable only checker(s) with the given id(s). This option conflicts with the
+# disable-checker option
+#enable-checker=
+
+# Enable all checker(s) except those with the given id(s). This option
+# conflicts with the enable-checker option
+#disable-checker=
+
+# Enable all messages in the listed categories (IRCWEF).
+#enable-msg-cat=
+
+# Disable all messages in the listed categories (IRCWEF).
+disable-msg-cat=I
+
+# Enable the message(s) with the given id(s).
+#enable-msg=
+
+# Disable the message(s) with the given id(s).
+# :W0704: *Except doesn't do anything*
+# :W0231: *__init__ method from base class %r is not called*
+# :R0903: *Too few public methods (%s/%s)*
+disable-msg=W0704,W0231,R0903
+
+
+[REPORTS]
+
+# Set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html
+output-format=colorized
+
+# Include message's id in output
+include-ids=yes
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]".
+files-output=no
+
+# Tells wether to display a full report or only the messages
+reports=yes
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note). You have access to the variables errors warning, statement which
+# respectively contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (R0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (R0004).
+comment=no
+
+# Enable the report(s) with the given id(s).
+#enable-report=
+
+# Disable the report(s) with the given id(s).
+#disable-report=
+
+
+# checks for
+# * unused variables / imports
+# * undefined variables
+# * redefinition of variable from builtins or from an outer scope
+# * use of variable before assignment
+#
+[VARIABLES]
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching names used for dummy variables (i.e. not used).
+dummy-variables-rgx=_|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+
+# checks for :
+# * doc strings
+# * modules / classes / functions / methods / arguments / variables name
+# * number of arguments, local variables, branches, returns and statements in
+# functions, methods
+# * required module attributes
+# * dangerous default values as arguments
+# * redefinition of function / method / class
+# * uses of the global statement
+#
+[BASIC]
+
+# Required attributes for module, separated by a comma
+required-attributes=
+
+# Regular expression which should only match functions or classes name which do
+# not require a docstring
+# was no-docstring-rgx=__.*__
+no-docstring-rgx=_.*
+
+# Regular expression which should only match correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression which should only match correct module level names
+const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
+
+# Regular expression which should only match correct class names
+class-rgx=[A-Z_][a-zA-Z0-9]+$
+
+# Regular expression which should only match correct function names
+function-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct method names
+method-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct instance attribute names
+# was attr-rgx=[a-z_][a-z0-9_]{2,30}$
+attr-rgx=[a-z_][a-z0-9_]{1,30}$
+
+# Regular expression which should only match correct argument names
+# was argument-rgx=[a-z_][a-z0-9_]{2,30}$
+argument-rgx=[a-z_][a-z0-9_]{1,30}$
+
+# Regular expression which should only match correct variable names
+# was variable-rgx=[a-z_][a-z0-9_]{2,30}$
+variable-rgx=[a-z_][a-z0-9_]{1,30}$
+
+# Regular expression which should only match correct list comprehension /
+# generator expression variable names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter,apply,input
+
+
+# try to find bugs in the code using type inference
+#
+[TYPECHECK]
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# List of classes names for which member attributes should not be checked
+# (useful for classes with attributes dynamically set).
+ignored-classes=SQLObject
+
+# When zope mode is activated, add a predefined set of Zope acquired attributes
+# to generated-members.
+zope=no
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E0201 when accessed.
+generated-members=REQUEST,acl_users,aq_parent
+
+
+# checks for
+# * external modules dependencies
+# * relative / wildcard imports
+# * cyclic imports
+# * uses of deprecated modules
+#
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report R0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report R0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report R0402 must
+# not be disabled)
+int-import-graph=
+
+
+# checks for :
+# * methods without self as first argument
+# * overridden methods signature
+# * access only to existent members via self
+# * attributes not defined in the __init__ method
+# * supported interfaces implementation
+# * unreachable code
+#
+[CLASSES]
+
+# List of interface methods to ignore, separated by a comma. This is used for
+# instance to not check methods defines in Zope's Interface base class.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+
+# checks for sign of poor/misdesign:
+# * number of methods, attributes, local variables...
+# * size, complexity of functions, methods
+#
+[DESIGN]
+
+# Maximum number of arguments for function / method
+# was max-args=5
+max-args=10
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of return / yield for function / method body
+# was max-returns=6
+max-returns=10
+
+# Maximum number of branch for function / method body
+max-branchs=12
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+# was max-attributes=7
+max-attributes=10
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+
+# checks for:
+# * warning notes in the code like FIXME, XXX
+# * PEP 263: source code with non ascii character but no encoding declaration
+#
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
+
+
+# checks for similarities and duplicated code. This computation may be
+# memory / CPU intensive, so you should disable it if you experiments some
+# problems.
+#
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+
+# checks for :
+# * unauthorized constructions
+# * strict indentation
+# * line length
+# * use of <> instead of !=
+#
+[FORMAT]
+
+# Maximum number of characters on a single line.
+max-line-length=80
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string='\t'
+#indent-string=' '
diff --git a/pym/gentoolkit/__init__.py b/pym/gentoolkit/__init__.py
index 35d89ac..5032c8d 100644
--- a/pym/gentoolkit/__init__.py
+++ b/pym/gentoolkit/__init__.py
@@ -1,42 +1,25 @@
#!/usr/bin/python
#
# Copyright 2003-2004 Karl Trygve Kalleberg
-# Copyright 2003-2009 Gentoo Technologies, Inc.
+# Copyright 2003-2009 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
#
# $Header$
-# =======
-# Imports
-# =======
+"""Gentoolkit is a collection of administration scripts for Gentoo"""
-import portage
-try:
- from threading import Lock
-except ImportError:
- # If we don't have thread support, we don't need to worry about
- # locking the global settings object. So we define a "null" Lock.
- class Lock(object):
- def acquire(self):
- pass
- def release(self):
- pass
+import sys
-# =======
-# Globals
-# =======
-
-PORTDB = portage.db[portage.root]["porttree"].dbapi
-VARDB = portage.db[portage.root]["vartree"].dbapi
-VIRTUALS = portage.db[portage.root]["virtuals"]
-
-Config = {
- "verbosityLevel": 3
+CONFIG = {
+ # Color handling: -1: Use Portage settings, 0: Force off, 1: Force on
+ 'color': -1,
+ # Guess piping output:
+ 'piping': False if sys.stdout.isatty() else True,
+ # Set some defaults:
+ 'quiet': False,
+ # verbose is True if not quiet and not piping
+ 'verbose': True,
+ 'debug': False
}
-try:
- settingslock = Lock()
- settings = portage.config(clone=portage.settings)
-except portage.exception.PermissionDenied, err:
- sys.stderr.write("Permission denied: '%s'\n" % str(err))
- sys.exit(e.errno)
+# vim: set ts=8 sw=4 tw=79:
diff --git a/pym/gentoolkit/analyse/__init__.py b/pym/gentoolkit/analyse/__init__.py
new file mode 100644
index 0000000..46d6185
--- /dev/null
+++ b/pym/gentoolkit/analyse/__init__.py
@@ -0,0 +1,128 @@
+#!/usr/bin/python
+#
+# Copyright 2010 Brian Dolbec <brian.dolbec@gmail.com>
+# Copyright(c) 2010, Gentoo Foundation
+# Copyright 2003-2004 Karl Trygve Kalleberg
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""Gentoo's installed packages analysis and repair tool"""
+
+
+# Move to Imports section after Python 2.6 is stable
+
+
+__docformat__ = 'epytext'
+# version is dynamically set by distutils sdist
+__version__ = "svn"
+__productname__ = "analyse"
+__authors__ = (
+ 'Brian Dolbec, <brian.dolbec@gmail.com>'
+
+)
+
+# make an exportable copy of the info for help output
+MODULE_INFO = {
+ "__docformat__": __docformat__,
+ "__doc__": __doc__,
+ "__version__": __version__,
+ "__productname__": __productname__,
+ "__authors__": __authors__
+
+}
+
+import errno
+import sys
+import time
+from getopt import getopt, GetoptError
+
+import portage
+
+import gentoolkit as gen
+from gentoolkit import errors
+from gentoolkit import pprinter as pp
+from gentoolkit.base import (initialize_configuration, split_arguments,
+ parse_global_options, print_help)
+from gentoolkit.formatters import format_options
+
+
+NAME_MAP = {
+ 'a': 'analyse',
+ 'r': 'rebuild'
+}
+
+FORMATTED_OPTIONS = (
+ (" (a)nalyse",
+ "analyses the installed PKG database USE flag or keyword useage"),
+ (" (r)ebuild",
+ "analyses the Installed PKG database and generates files suitable"),
+ (" ",
+ "to replace corrupted or missing /etc/portage/package.* files")
+ )
+
+def expand_module_name(module_name):
+ """Returns one of the values of NAME_MAP or raises KeyError"""
+
+ if module_name == 'list':
+ # list is a Python builtin type, so we must rename our module
+ return 'list_'
+ elif module_name in NAME_MAP.values():
+ return module_name
+ else:
+ return NAME_MAP[module_name]
+
+
+def main():
+ """Parse input and run the program."""
+
+ short_opts = "hqCNV"
+ long_opts = (
+ 'help', 'quiet', 'nocolor', 'no-color', 'no-pipe', 'version', 'debug'
+ )
+
+ initialize_configuration()
+
+ try:
+ global_opts, args = getopt(sys.argv[1:], short_opts, long_opts)
+ except GetoptError as err:
+ sys.stderr.write(" \n")
+ sys.stderr.write(pp.error("Global %s\n" % err))
+ print_help(MODULE_INFO, FORMATTED_OPTIONS, with_description=False)
+ sys.exit(2)
+
+ # Parse global options
+ need_help = parse_global_options(global_opts, args, MODULE_INFO, FORMATTED_OPTIONS)
+
+ if gen.CONFIG['quiet']:
+ gen.CONFIG['verbose'] = False
+
+ try:
+ module_name, module_args = split_arguments(args)
+ except IndexError:
+ print_help(MODULE_INFO, FORMATTED_OPTIONS)
+ sys.exit(2)
+
+ if need_help:
+ module_args.append('--help')
+
+ try:
+ expanded_module_name = expand_module_name(module_name)
+ except KeyError:
+ sys.stderr.write(pp.error("Unknown module '%s'" % module_name))
+ print_help(MODULE_INFO, FORMATTED_OPTIONS, with_description=False)
+ sys.exit(2)
+
+ try:
+ loaded_module = __import__(
+ expanded_module_name, globals(), locals(), [], -1
+ )
+ loaded_module.main(module_args)
+ except portage.exception.AmbiguousPackageName as err:
+ raise errors.GentoolkitAmbiguousPackage(err.args[0])
+ except IOError as err:
+ if err.errno != errno.EPIPE:
+ raise
+
+if __name__ == '__main__':
+ main()
diff --git a/pym/gentoolkit/analyse/analyse.py b/pym/gentoolkit/analyse/analyse.py
new file mode 100644
index 0000000..f14aff5
--- /dev/null
+++ b/pym/gentoolkit/analyse/analyse.py
@@ -0,0 +1,378 @@
+#!/usr/bin/python
+#
+# Copyright 2010 Brian Dolbec <brian.dolbec@gmail.com>
+# Copyright(c) 2010, Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+
+"""Provides a breakdown list of USE flags or keywords used and by
+what packages according to the Installed package database"""
+
+from __future__ import print_function
+
+import sys
+
+import gentoolkit
+from gentoolkit.dbapi import PORTDB, VARDB
+from gentoolkit.analyse.base import ModuleBase
+from gentoolkit import pprinter as pp
+from gentoolkit.flag import get_installed_use, get_flags
+from gentoolkit.analyse.lib import FlagAnalyzer, KeywordAnalyser
+from gentoolkit.analyse.output import nl, AnalysisPrinter
+from gentoolkit.package import Package
+from gentoolkit.helpers import get_installed_cpvs
+
+import portage
+
+
+def gather_flags_info(
+ cpvs=None,
+ system_flags=None,
+ include_unset=False,
+ target="USE",
+ use_portage=False,
+ # override-able for testing
+ _get_flags=get_flags,
+ _get_used=get_installed_use
+ ):
+ """Analyse the installed pkgs USE flags for frequency of use
+
+ @type cpvs: list
+ @param cpvs: optional list of [cat/pkg-ver,...] to analyse or
+ defaults to entire installed pkg db
+ @type: system_flags: list
+ @param system_flags: the current default USE flags as defined
+ by portage.settings["USE"].split()
+ @type include_unset: bool
+ @param include_unset: controls the inclusion of unset USE flags in the report.
+ @type target: string
+ @param target: the environment variable being analysed
+ one of ["USE", "PKGUSE"]
+ @type _get_flags: function
+ @param _get_flags: ovride-able for testing,
+ defaults to gentoolkit.analyse.lib.get_flags
+ @param _get_used: ovride-able for testing,
+ defaults to gentoolkit.analyse.lib.get_installed_use
+ @rtype dict. {flag:{"+":[cat/pkg-ver,...], "-":[cat/pkg-ver,...], "unset":[]}
+ """
+ if cpvs is None:
+ cpvs = VARDB.cpv_all()
+ # pass them in to override for tests
+ flags = FlagAnalyzer(system_flags,
+ filter_defaults=False,
+ target=target,
+ _get_flags=_get_flags,
+ _get_used=get_installed_use
+ )
+ flag_users = {}
+ for cpv in cpvs:
+ if cpv.startswith("virtual"):
+ continue
+ if use_portage:
+ plus, minus, unset = flags.analyse_cpv(cpv)
+ else:
+ pkg = Package(cpv)
+ plus, minus, unset = flags.analyse_pkg(pkg)
+ for flag in plus:
+ if flag in flag_users:
+ flag_users[flag]["+"].append(cpv)
+ else:
+ flag_users[flag] = {"+": [cpv], "-": []}
+ for flag in minus:
+ if flag in flag_users:
+ flag_users[flag]["-"].append(cpv)
+ else:
+ flag_users[flag] = {"+":[], "-": [cpv]}
+ if include_unset:
+ for flag in unset:
+ if flag in flag_users:
+ if "unset" in flag_users[flag]:
+ flag_users[flag]["unset"].append(cpv)
+ else:
+ flag_users[flag]["unset"] = [cpv]
+ else:
+ flag_users[flag] = {"+": [], "-": [], "unset": [cpv]}
+ return flag_users
+
+
+def gather_keywords_info(
+ cpvs=None,
+ system_keywords=None,
+ use_portage=False,
+ # override-able for testing
+ keywords=portage.settings["ACCEPT_KEYWORDS"],
+ analyser = None
+ ):
+ """Analyse the installed pkgs 'keywords' for frequency of use
+
+ @param cpvs: optional list of [cat/pkg-ver,...] to analyse or
+ defaults to entire installed pkg db
+ @param system_keywords: list of the system keywords
+ @param keywords: user defined list of keywords to check and report on
+ or reports on all relevant keywords found to have been used.
+ @param _get_kwds: overridable function for testing
+ @param _get_used: overridable function for testing
+ @rtype dict. {keyword:{"stable":[cat/pkg-ver,...], "testing":[cat/pkg-ver,...]}
+ """
+ if cpvs is None:
+ cpvs = VARDB.cpv_all()
+ keyword_users = {}
+ for cpv in cpvs:
+ if cpv.startswith("virtual"):
+ continue
+ if use_portage:
+ keyword = analyser.get_inst_keyword_cpv(cpv)
+ else:
+ pkg = Package(cpv)
+ keyword = analyser.get_inst_keyword_pkg(pkg)
+ #print "returned keyword =", cpv, keyword, keyword[0]
+ key = keyword[0]
+ if key in ["~", "-"]:
+ _kwd = keyword[1:]
+ if _kwd in keyword_users:
+ if key in ["~"]:
+ keyword_users[_kwd]["testing"].append(cpv)
+ elif key in ["-"]:
+ #print "adding cpv to missing:", cpv
+ keyword_users[_kwd]["missing"].append(cpv)
+ else:
+ if key in ["~"]:
+ keyword_users[_kwd] = {"stable": [],
+ "testing": [cpv], "missing": []}
+ elif key in ["-"]:
+ keyword_users[_kwd] = {"stable": [],
+ "testing": [], "missing": [cpv]}
+ else:
+ keyword_users[_kwd] = {"stable": [cpv],
+ "testing": [], "missing": []}
+ elif keyword in keyword_users:
+ keyword_users[keyword]["stable"].append(cpv)
+ else:
+ keyword_users[keyword] = {"stable": [cpv], "testing": [], "missing": []}
+ return keyword_users
+
+
+class Analyse(ModuleBase):
+ """Installed db analysis tool to query the installed databse
+ and produce/output stats for USE flags or keywords/mask.
+ The 'rebuild' action output is in the form suitable for file type output
+ to create a new package.use, package.keywords, package.unmask
+ type files in the event of needing to rebuild the
+ /etc/portage/* user configs
+ """
+ def __init__(self):
+ ModuleBase.__init__(self)
+ self.module_name = "analyse"
+ self.options = {
+ "flags": False,
+ "keywords": False,
+ "unset": False,
+ "verbose": False,
+ "quiet": False,
+ 'prefix': False,
+ 'portage': True
+ }
+ self.module_opts = {
+ "-f": ("flags", "boolean", True),
+ "--flags": ("flags", "boolean", True),
+ "-k": ("keywords", "boolean", True),
+ "--keywords": ("keywords", "boolean", True),
+ "-u": ("unset", "boolean", True),
+ "--unset": ("unset", "boolean", True),
+ "-v": ("verbose", "boolean", True),
+ "--verbose": ("verbose", "boolean", True),
+ "-p": ("prefix", "boolean", True),
+ "--prefix": ("prefix", "boolean", True),
+ "-G": ("portage", "boolean", False),
+ "--portage": ("portage", "boolean", False),
+ }
+ self.formatted_options = [
+ (" -h, --help", "Outputs this useage message"),
+ (" -a, --analyse",
+ "Action, sets the module to gather data and output the"),
+ ("", "formatted stats/information to the screen"),
+ (" -u, --unset",
+ "Additionally include any unset USE flags and the packages"),
+ ("", "that could use them"),
+ (" -v, --verbose",
+ "Used in the analyse action to output more detailed information"),
+ (" -p, --prefix",
+ "Used for testing purposes only, runs report using " +
+ "a prefix keyword and 'prefix' USE flag"),
+ #(" -G, --portage",
+ #"Use portage directly instead of gentoolkit's Package " +
+ #"object for some operations. Usually a little faster."),
+ ]
+ self.formatted_args = [
+ (" use",
+ "causes the action to analyse the installed packages USE flags"),
+ (" pkguse",
+ "causes the action to analyse the installed packages PKGUSE flags"),
+ (" ",
+ "These are flags that have been set in /etc/portage/package.use"),
+ (" keywords",
+ "causes the action to analyse the installed packages keywords"),
+ ]
+ self.short_opts = "huvpG"
+ self.long_opts = ("help", "unset", "verbose", "prefix") #, "portage")
+ self.need_queries = True
+ self.arg_spec = "Target"
+ self.arg_options = ['use', 'pkguse','keywords']
+ self.arg_option = False
+ self.warning = (
+ " CAUTION",
+ "This is beta software and some features/options are incomplete,",
+ "some features may change in future releases includig its name.",
+ "Feedback will be appreciated, http://bugs.gentoo.org")
+
+
+ def run(self, input_args, quiet=False):
+ """runs the module
+
+ @param input_args: input arguments to be parsed
+ """
+ query = self.main_setup(input_args)
+ query = self.validate_query(query)
+ if query in ["use", "pkguse"]:
+ self.analyse_flags(query)
+ elif query in ["keywords"]:
+ self.analyse_keywords()
+
+ def analyse_flags(self, target):
+ """This will scan the installed packages db and analyse the
+ USE flags used for installation and produce a report on how
+ they were used.
+
+ @type target: string
+ @param target: the target to be analysed, one of ["use", "pkguse"]
+ """
+ system_use = portage.settings["USE"].split()
+ self.printer = AnalysisPrinter("use", self.options["verbose"], system_use)
+ if self.options["verbose"]:
+ cpvs = VARDB.cpv_all()
+ #cpvs = get_installed_cpvs()
+ #print "Total number of installed ebuilds =", len(cpvs)
+ flag_users = gather_flags_info(cpvs, system_use,
+ self.options["unset"], target=target.upper(),
+ use_portage=self.options['portage'])
+ else:
+ cpvs = get_installed_cpvs()
+ flag_users = gather_flags_info(cpvs, system_flags=system_use,
+ include_unset=self.options["unset"], target=target.upper(),
+ use_portage=self.options['portage'])
+ #print flag_users
+ flag_keys = sorted(flag_users)
+ if self.options["verbose"]:
+ print(" Flag System #pkgs cat/pkg-ver")
+ blankline = nl
+ elif not self.options['quiet']:
+ print(" Flag System #pkgs")
+ blankline = lambda: None
+ for flag in flag_keys:
+ flag_pos = flag_users[flag]["+"]
+ if len(flag_pos):
+ self.printer(flag, "+", flag_pos)
+ #blankline()
+ flag_neg = flag_users[flag]["-"]
+ if len(flag_neg):
+ self.printer(flag, "-", flag_neg)
+ #blankline()
+ if "unset" in flag_users[flag] and flag_users[flag]["unset"]:
+ flag_unset = flag_users[flag]["unset"]
+ self.printer(flag, "unset", flag_unset)
+ #blankline()
+ if not self.options['quiet']:
+ print("===================================================")
+ print("Total number of flags in report =", pp.output.red(str(len(flag_keys))))
+ if self.options["verbose"]:
+ print("Total number of installed ebuilds =", pp.output.red(str(len([x for x in cpvs]))))
+ print()
+
+
+ def analyse_keywords(self, keywords=None):
+ """This will scan the installed packages db and analyse the
+ keywords used for installation and produce a report on them.
+ """
+ print()
+ system_keywords = portage.settings["ACCEPT_KEYWORDS"]
+ arch = portage.settings["ARCH"]
+ if self.options["prefix"]:
+ # build a new keyword for testing
+ system_keywords = "~" + arch + "-linux"
+ if self.options["verbose"] or self.options["prefix"]:
+ print("Current system ARCH =", arch)
+ print("Current system ACCEPT_KEYWORDS =", system_keywords)
+ system_keywords = system_keywords.split()
+ self.printer = AnalysisPrinter("keywords", self.options["verbose"], system_keywords)
+ self.analyser = KeywordAnalyser( arch, system_keywords, VARDB)
+ #self.analyser.set_order(portage.settings["USE"].split())
+ # only for testing
+ test_use = portage.settings["USE"].split()
+ if self.options['prefix'] and 'prefix' not in test_use:
+ print("ANALYSE_KEYWORDS() 'prefix' flag not found in system USE flags!!! appending for testing")
+ print()
+ test_use.append('prefix')
+ self.analyser.set_order(test_use)
+ # /end testing
+
+ if self.options["verbose"]:
+ cpvs = VARDB.cpv_all()
+ #print "Total number of installed ebuilds =", len(cpvs)
+ keyword_users = gather_keywords_info(
+ cpvs=cpvs,
+ system_keywords=system_keywords,
+ use_portage=self.options['portage'],
+ keywords=keywords, analyser = self.analyser
+ )
+ blankline = nl
+ else:
+ keyword_users = gather_keywords_info(
+ system_keywords=system_keywords,
+ use_portage=self.options['portage'],
+ keywords=keywords,
+ analyser = self.analyser
+ )
+ blankline = lambda: None
+ #print keyword_users
+ keyword_keys = sorted(keyword_users)
+ if self.options["verbose"]:
+ print(" Keyword System #pkgs cat/pkg-ver")
+ elif not self.options['quiet']:
+ print(" Keyword System #pkgs")
+ for keyword in keyword_keys:
+ kwd_stable = keyword_users[keyword]["stable"]
+ if len(kwd_stable):
+ self.printer(keyword, " ", kwd_stable)
+ blankline()
+ kwd_testing = keyword_users[keyword]["testing"]
+ if len(kwd_testing):
+ self.printer(keyword, "~", kwd_testing)
+ blankline()
+ kwd_missing = keyword_users[keyword]["missing"]
+ if len(kwd_missing):
+ self.printer(keyword, "-", kwd_missing)
+ blankline
+ if not self.options['quiet']:
+ if self.analyser.mismatched:
+ print("_________________________________________________")
+ print(("The following packages were found to have a \n" +
+ "different recorded ARCH than the current system ARCH"))
+ for cpv in self.analyser.mismatched:
+ print("\t", pp.cpv(cpv))
+ print("===================================================")
+ print("Total number of keywords in report =", pp.output.red(str(len(keyword_keys))))
+ if self.options["verbose"]:
+ print("Total number of installed ebuilds =", pp.output.red(str(len(cpvs))))
+ print()
+
+
+def main(input_args):
+ """Common starting method by the analyse master
+ unless all modules are converted to this class method.
+
+ @param input_args: input args as supplied by equery master module.
+ """
+ query_module = Analyse()
+ query_module.run(input_args, gentoolkit.CONFIG['quiet'])
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/analyse/base.py b/pym/gentoolkit/analyse/base.py
new file mode 100644
index 0000000..d4febdb
--- /dev/null
+++ b/pym/gentoolkit/analyse/base.py
@@ -0,0 +1,134 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Copyright 2010 Brian Dolbec <brian.dolbec@gmail.com>
+# Copyright(c) 2010, Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header: $
+
+"""Analyse Base Module class to hold common module operation functions
+"""
+
+from __future__ import print_function
+
+__docformat__ = 'epytext'
+
+
+import errno
+import sys
+import time
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit.pprinter as pp
+from gentoolkit.formatters import format_options
+from gentoolkit.base import mod_usage
+
+
+class ModuleBase(object):
+ """Analyse base module class to parse module options print module help, etc.."""
+
+ def __init__(self):
+ self.module_name = None
+ self.options = {}
+ self.formatted_options = None
+ self.short_opts = None
+ self.long_opts = None
+ self.module_opts = {}
+ self.warning = None
+ self.need_queries = True
+
+
+ def print_help(self, with_description=True):
+ """Print description, usage and a detailed help message.
+
+ @type with_description: bool
+ @param with_description: if true, print module's __doc__ string
+ """
+
+ if with_description:
+ print()
+ print(__doc__.strip())
+ print()
+ if self.warning:
+ print()
+ for line in self.warning:
+ sys.stderr.write(pp.warn(line))
+ print()
+ print(mod_usage(mod_name=self.module_name, arg=self.arg_spec, optional=self.arg_option))
+ print()
+ print(pp.command("options"))
+ print(format_options( self.formatted_options ))
+ if self.formatted_args:
+ print()
+ print(pp.command(self.arg_spec))
+ print(format_options(self.formatted_args))
+ print()
+
+ def parse_module_options(self, module_opts):
+ """Parse module options and update self.options"""
+
+ opts = (x[0] for x in module_opts)
+ posargs = (x[1] for x in module_opts)
+ for opt, posarg in zip(opts, posargs):
+ if opt in ('-h', '--help'):
+ self.print_help()
+ sys.exit(0)
+ opt_name, opt_type, opt_setting = self.module_opts[opt]
+ if opt_type == 'boolean':
+ self.options[opt_name] = opt_setting
+ elif opt_type == 'int':
+ if posarg.isdigit():
+ val = int(posarg)
+ else:
+ print()
+ err = "Module option %s requires integer (got '%s')"
+ sys.stdout.write(pp.error(err % (opt,posarg)))
+ print()
+ self.print_help(with_description=False)
+ sys.exit(2)
+ self.options[opt_name] = val
+ if self.options['quiet']:
+ self.options['verbose'] = False
+
+ def validate_query(self, query, depth=0):
+ """check that the query meets the modules TargetSpec
+ If not it attempts to reduce it to a valid TargetSpec
+ or prints the help message and exits
+ """
+ if depth > 1:
+ return []
+ if len(query) > 1:
+ query = list(set(self.arg_options).intersection(query))
+ #print "reduced query =", query
+ query = self.validate_query(query, depth+1)
+ if isinstance(query, list):
+ query = query[0]
+ if query not in self.arg_options:
+ print()
+ print(pp.error(
+ "Error starting module. Incorrect or No TargetSpec specified!"
+ ))
+ print("query = ", query)
+ self.print_help()
+ sys.exit(2)
+ return query
+
+
+ def main_setup(self, input_args):
+ """Parse input and prepares the program"""
+
+ try:
+ module_opts, queries = gnu_getopt(input_args, self.short_opts, self.long_opts)
+ except GetoptError as err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print()
+ self.print_help(with_description=False)
+ sys.exit(2)
+ self.parse_module_options(module_opts)
+ if self.need_queries and not queries:
+ self.print_help()
+ sys.exit(2)
+ return queries
+
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/analyse/lib.py b/pym/gentoolkit/analyse/lib.py
new file mode 100644
index 0000000..3f383a8
--- /dev/null
+++ b/pym/gentoolkit/analyse/lib.py
@@ -0,0 +1,352 @@
+#!/usr/bin/python
+#
+# Copyright 2010 Brian Dolbec <brian.dolbec@gmail.com>
+# Copyright(c) 2010, Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+
+
+"""Provides support functions to analyse modules"""
+
+import sys
+
+from gentoolkit.dbapi import PORTDB, VARDB
+from gentoolkit import errors
+from gentoolkit.keyword import reduce_keywords
+from gentoolkit.flag import (reduce_flags, get_flags, get_all_cpv_use,
+ filter_flags, get_installed_use, get_iuse)
+#from gentoolkit.package import Package
+
+import portage
+
+
+class FlagAnalyzer(object):
+ """Specialty functions for analysing an installed package's
+ USE flags. Can be used for single or mulitple use without
+ needing to be reset unless the system USE flags are changed.
+
+ @type system: list or set
+ @param system: the default system USE flags.
+ @type _get_flags: function
+ @param _get_flags: Normally defaulted, can be overriden for testing
+ @type _get_used: function
+ @param _get_used: Normally defaulted, can be overriden for testing
+ """
+ def __init__(self,
+ system,
+ filter_defaults=False,
+ target="USE",
+ _get_flags=get_flags,
+ _get_used=get_installed_use
+ ):
+ self.get_flags = _get_flags
+ self.get_used = _get_used
+ self.filter_defaults = filter_defaults
+ self.target = target
+ self.reset(system)
+
+ def reset(self, system):
+ """Resets the internal system USE flags and use_expand variables
+ to the new setting. The use_expand variable is handled internally.
+
+ @type system: list or set
+ @param system: the default system USE flags.
+ """
+ self.system = set(system)
+ self.use_expand = portage.settings['USE_EXPAND'].lower().split()
+
+ def analyse_cpv(self, cpv):
+ """Gets all relavent USE flag info for a cpv and breaks them down
+ into 3 sets, plus (package.use enabled), minus ( package.use disabled),
+ unset.
+
+ @param cpv: string. 'cat/pkg-ver'
+ @rtype tuple of sets
+ @return (plus, minus, unset) sets of USE flags
+ """
+ installed = set(self.get_used(cpv, self.target))
+ iuse = set(reduce_flags(self.get_flags(cpv)))
+ return self._analyse(installed, iuse)
+
+ def _analyse(self, installed, iuse):
+ """Analyses the supplied info and returns the flag settings
+ that differ from the defaults
+
+ @type installed: set
+ @param installed: the installed with use flags
+ @type iuse: set
+ @param iuse: the current ebuilds IUSE
+ """
+ defaults = self.system.intersection(iuse)
+ usedflags = iuse.intersection(set(installed))
+ if self.filter_defaults:
+ plus = usedflags.difference(defaults)
+ else:
+ plus = usedflags
+ minus = defaults.difference(usedflags)
+ unset = iuse.difference(defaults, plus, minus)
+ cleaned = self.remove_expanding(unset)
+ return (plus, minus, cleaned)
+
+ def analyse_pkg(self, pkg):
+ """Gets all relevent USE flag info for a pkg and breaks them down
+ into 3 sets, plus (package.use enabled), minus ( package.use disabled),
+ unset.
+
+ @param pkg: gentoolkit.package.Package object
+ @rtype tuple of sets
+ @return (plus, minus, unset) sets of USE flags
+ """
+ installed = set(self.pkg_used(pkg))
+ iuse = set(reduce_flags(self.pkg_flags(pkg)))
+ return self._analyse(installed, iuse)
+
+ def pkg_used(self, pkg):
+ if self.target == "USE":
+ return pkg.use().split()
+ return pkg.environment(self.target).split()
+
+ def pkg_flags(self, pkg):
+ final_use, use_expand_hidden, usemasked, useforced = \
+ get_all_cpv_use(pkg.cpv)
+ flags = pkg.environment("IUSE", prefer_vdb=False).split()
+ return filter_flags(flags, use_expand_hidden, usemasked, useforced)
+
+ def redundant(self, cpv, iuse):
+ """Checks for redundant settings.
+ future function. Not yet implemented.
+ """
+ pass
+
+ def remove_expanding(self, flags):
+ """Remove unwanted USE_EXPAND flags
+ from unset IUSE sets
+
+ @param flags: short list or set of USE flags
+ @rtype set
+ @return USE flags
+ """
+ _flags = set(flags)
+ for expander in self.use_expand:
+ for flag in flags:
+ if expander in flag:
+ _flags.remove(flag)
+ if not _flags:
+ break
+ return _flags
+
+
+class KeywordAnalyser(object):
+ """Specialty functions for analysing the installed package db for
+ keyword useage and the packages that used them.
+
+ Note: should be initialized with the internal set_order() before use.
+ See internal set_order() for more details.
+ This class of functions can be used for single cpv checks or
+ used repeatedly for an entire package db.
+
+ @type arch: string
+ @param arch: the system ARCH setting
+ @type accept_keywords: list
+ @param accept_keywords: eg. ['x86', '~x86']
+ @type get_aux: function, defaults to: VARDB.aux_get
+ @param vardb: vardb class of functions, needed=aux_get()
+ to return => KEYWORDS & USE flags for a cpv
+ = aux_get(cpv, ["KEYWORDS", "USE"])
+ """
+
+ # parsing order to determine appropriate keyword used for installation
+ normal_order = ['stable', 'testing', 'prefix', 'testing_prefix', 'missing']
+ prefix_order = ['prefix', 'testing_prefix', 'stable', 'testing', 'missing']
+ parse_range = list(range(len(normal_order)))
+
+
+ def __init__(self, arch, accept_keywords, vardb=VARDB):
+ self.arch = arch
+ self.accept_keywords = accept_keywords
+ self.vardb = vardb
+ self.prefix = ''
+ self.parse_order = None
+ self.check_key = {
+ 'stable': self._stable,
+ 'testing': self._testing,
+ 'prefix': self._prefix,
+ 'testing_prefix': self._testing_prefix,
+ 'missing': self._missing
+ }
+ self.mismatched = []
+
+ def determine_keyword(self, keywords, used, cpv):
+ """Determine the keyword from the installed USE flags and
+ the KEYWORDS that was used to install a package.
+
+ @param keywords: list of keywords available to install a pkg
+ @param used: list of USE flalgs recorded for the installed pkg
+ @rtype: string
+ @return a keyword or null string
+ """
+ used = set(used)
+ kwd = None
+ result = ''
+ if keywords:
+ absolute_kwds = reduce_keywords(keywords)
+ kwd = list(used.intersection(absolute_kwds))
+ #if keywords == ['~ppc64']:
+ #print "Checked keywords for kwd", keywords, used, "kwd =", kwd
+ if not kwd:
+ #print "Checking for kwd against portage.archlist"
+ absolute_kwds = reduce_keywords(keywords)
+ # check for one against archlist then re-check
+ kwd = list(absolute_kwds.intersection(portage.archlist))
+ #print "determined keyword =", kwd
+ if len(kwd) == 1:
+ key = kwd[0]
+ #print "determined keyword =", key
+ elif not kwd:
+ #print "kwd != 1", kwd, cpv
+ result = self._missing(self.keyword, keywords)
+ else: # too many, try to narrow them dowm
+ #print "too many kwd's, trying to match against arch"
+ _kwd = list(set(kwd).intersection(self.arch))
+ key = ''
+ if _kwd:
+ #print "found one! :)", _kwd
+ key = _kwd
+ else: # try re-running the short list against archlist
+ #print "Checking kwd for _kwd against portage.archlist"
+ _kwd = list(set(kwd).intersection(portage.archlist))
+ if _kwd and len(_kwd) == 1:
+ #print "found one! :)", _kwd
+ key = _kwd[0]
+ else:
+ #print " :( didn't work, _kwd =", _kwd, "giving up on:", cpv
+ result = self._missing(self.keyword, keywords)
+ i = 0
+ while not result and i in self.parse_range:
+ parsekey = self.parse_order[i]
+ result = self.check_key[parsekey](key, keywords)
+ i += 1
+ return result
+
+ def _stable(self, key, keywords):
+ """test for a normal stable keyword"""
+ if key in keywords:
+ return key
+ return ''
+
+ def _testing(self, key, keywords):
+ """test for a normal testing keyword"""
+ if ("~" + key) in keywords:
+ return "~" + key
+ return ''
+
+ def _prefix(self, key, keywords):
+ """test for a stable prefix keyword"""
+ if not self.prefix:
+ return ''
+ _key = '-'.join([key, self.prefix])
+ if _key in keywords:
+ #print key, "is in", keywords
+ return _key
+ return ''
+
+ def _testing_prefix(self, key, keywords):
+ """test for a testing prefix keyword"""
+ if not self.prefix:
+ return ''
+ _key = "~" +'-'.join([key, self.prefix])
+ if _key in keywords:
+ #print key, "is in", keywords
+ return _key
+ return ''
+
+ def _missing(self, key, keywords):
+ """generates a missing keyword to return"""
+ if self.prefix and key != self.keyword:
+ _key = '-'.join([key, self.prefix])
+ else:
+ _key = '-' + key
+ #print "_missisng :( _key =", _key
+ return _key
+
+ def get_inst_keyword_cpv(self, cpv):
+ """Determines the installed with keyword for cpv
+
+ @type cpv: string
+ @param cpv: an installed CAT/PKG-VER
+ @rtype: string
+ @returns a keyword determined to have been used to install cpv
+ """
+ keywords, used = self.vardb.aux_get(cpv, ["KEYWORDS", "USE"])
+ keywords = keywords.split()
+ used = used.split()
+ return self._parse(keywords, used, cpv=cpv)
+
+ def get_inst_keyword_pkg(self, pkg):
+ """Determines the installed with keyword for cpv
+
+ @param pkg: gentoolkit.package.Package object
+ @rtype: string
+ @returns a keyword determined to have been used to install cpv
+ """
+ keywords, used = pkg.environment(["KEYWORDS", "USE"],
+ prefer_vdb=True, fallback=False)
+ keywords = keywords.split()
+ used = used.split()
+ return self._parse(keywords, used, pkg=pkg)
+
+ def _parse(self, keywords, used, pkg=None, cpv=None):
+ if pkg:
+ _cpv = pkg.cpv
+ else:
+ _cpv = cpv
+ if not self.parse_order:
+ self.set_order(used)
+ keyword = self.keyword
+ # sanity check
+ if self.arch not in used:
+ #print "Found a mismatch = ", cpv, self.arch, used
+ self.mismatched.append(_cpv)
+ if keyword in keywords:
+ #print "keyword", keyword, "is in", keywords
+ return keyword
+ elif "~"+keyword in keywords:
+ #print "~keyword", keyword, "is in", keywords
+ return "~"+keyword
+ else:
+ keyword = self.determine_keyword(keywords, used, _cpv)
+ if not keyword:
+ raise errors.GentoolkitUnknownKeyword(_cpv, ' '.join(keywords), used)
+ return keyword
+
+ def set_order(self, used):
+ """Used to set the parsing order to determine a keyword
+ used for installation.
+
+ This is needed due to the way prefix arch's and keywords
+ work with portage. It looks for the 'prefix' flag. A positive result
+ sets it to the prefix order and keyword.
+
+ @type used: list
+ @param used: a list of pkg USE flags or the system USE flags"""
+ if 'prefix' in used:
+ #print "SET_ORDER() Setting parse order to prefix"
+ prefix = None
+ self.parse_order = self.prefix_order
+ for key in self.accept_keywords:
+ #print "SET_ORDER() '"+key+"'"
+ if '-' in key:
+ #print "SET_ORDER()found prefix keyword :", key
+ if self.arch in key:
+ prefix = key.split('-')[1]
+ #print "prefix =", prefix
+ self.prefix = prefix
+ self.keyword = '-'.join([self.arch, prefix])
+ else:
+ #print "SET_ORDER() Setting parse order to normal"
+ self.parse_order = self.normal_order
+ self.keyword = self.arch
+ #print "SET_ORDER() completed: prefix =", self.prefix, ", keyword =", \
+ # self.keyword, "parse order =",self.parse_order
+ #print
+
diff --git a/pym/gentoolkit/analyse/output.py b/pym/gentoolkit/analyse/output.py
new file mode 100644
index 0000000..4844eb0
--- /dev/null
+++ b/pym/gentoolkit/analyse/output.py
@@ -0,0 +1,213 @@
+#!/usr/bin/python
+#
+# Copyright(c) 2010, Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+
+"""Provides various output classes and functions for
+both screen and file output
+"""
+
+from __future__ import print_function
+
+import gentoolkit
+from gentoolkit import pprinter as pp
+from gentoolkit.textwrap_ import TextWrapper
+from gentoolkit.cpv import split_cpv
+
+
+def nl(lines=1):
+ """small utility function to print blank lines
+
+ @type lines: integer
+ @param lines: optional number of blank lines to print
+ default = 1
+ """
+ print(('\n' * lines))
+
+class AnalysisPrinter(object):
+ """Printing functions"""
+ def __init__(self, target, verbose=True, references=None):
+ """@param references: list of accepted keywords or
+ the system use flags
+ """
+ self.references = references
+ self.set_target(target, verbose)
+
+ def set_target(self, target, verbose=True):
+ if target in ["use"]:
+ if verbose:
+ self.print_fn = self.print_use_verbose
+ else:
+ self.print_fn = self.print_use_quiet
+ elif target in ["keywords"]:
+ if verbose:
+ self.print_fn = self.print_keyword_verbose
+ else:
+ self.print_fn = self.print_keyword_quiet
+
+ def __call__(self, key, active, pkgs):
+ self._format_key(key, active, pkgs)
+
+ def _format_key(self, key, active, pkgs):
+ """Determines the stats for key, formats it and
+ calls the pre-determined print function
+ """
+ occurred = str(len(pkgs))
+ if active in ["-", "~"]:
+ _key = active + key
+ else:
+ _key = key
+ if _key in self.references:
+ default = "default"
+ else:
+ default = "......."
+ count = ' '*(5-len(occurred)) + occurred
+ pkgs.sort()
+ self.print_fn(key, active, default, count, pkgs)
+
+ @staticmethod
+ def print_use_verbose(key, active, default, count, pkgs):
+ """Verbosely prints a set of use flag info. including the pkgs
+ using them.
+ """
+ _pkgs = pkgs[:]
+ if active in ["+", "-"]:
+ _key = pp.useflag((active+key), active=="+")
+ else:
+ _key = (" " + key)
+ cpv = _pkgs.pop(0)
+ print(_key,'.'*(35-len(key)), default, pp.number(count), pp.cpv(cpv))
+ while _pkgs:
+ cpv = _pkgs.pop(0)
+ print(' '*52 + pp.cpv(cpv))
+
+ # W0613: *Unused argument %r*
+ # pylint: disable-msg=W0613
+ @staticmethod
+ def print_use_quiet(key, active, default, count, pkgs):
+ """Quietly prints a subset set of USE flag info..
+ """
+ if active in ["+", "-"]:
+ _key = pp.useflag((active+key), active=="+")
+ else:
+ _key = (" " + key)
+ print(_key,'.'*(35-len(key)), default, pp.number(count))
+
+ @staticmethod
+ def print_keyword_verbose(key, stability, default, count, pkgs):
+ """Verbosely prints a set of keywords info. including the pkgs
+ using them.
+ """
+ _pkgs = pkgs[:]
+ _key = (pp.keyword((stability+key),stable=(stability==" "),
+ hard_masked=stability=="-"))
+ cpv = _pkgs.pop(0)
+ print(_key,'.'*(20-len(key)), default, pp.number(count), pp.cpv(cpv))
+ while _pkgs:
+ cpv = _pkgs.pop(0)
+ print(' '*37 + pp.cpv(cpv))
+
+ # W0613: *Unused argument %r*
+ # pylint: disable-msg=W0613
+ @staticmethod
+ def print_keyword_quiet(key, stability, default, count, pkgs):
+ """Quietly prints a subset set of USE flag info..
+ """
+ _key = (pp.keyword((stability+key), stable=(stability==" "),
+ hard_masked=stability=="-"))
+ print(_key,'.'*(20-len(key)), default, pp.number(count))
+
+
+class RebuildPrinter(object):
+ """Output functions"""
+ def __init__(self, target, pretend=True, exact=False):
+ """@param references: list of accepted keywords or
+ the system use flags
+ """
+ self.target = target
+ self.set_target(target)
+ self.pretend = pretend
+ if pretend:
+ self.twrap = TextWrapper(width=gentoolkit.CONFIG['termWidth'])
+ self.spacer = ' '
+ self.init_indent = len(self.spacer)
+ else:
+ self.spacer = ''
+ self.exact = exact
+ self.data = {}
+
+
+ def set_target(self, target):
+ if target in ["use"]:
+ self.print_fn = self.print_use
+ self.use_lines = [self.header()]
+ elif target in ["keywords"]:
+ self.print_fn = self.print_keyword
+ elif target in ["unmask"]:
+ self.print_fn = self.print_mask
+
+
+ def __call__(self, key, values):
+ self._format_key(key, values)
+
+
+ def _format_key(self, key, values):
+ """Determines the stats for key, formats it and
+ calls the pre-determined print function
+ """
+ if self.exact:
+ _key = "=" + key
+ else:
+ parts = split_cpv(key)
+ _key = '/'.join(parts[:2])
+ values.sort()
+ self.data[_key] = values
+ self.print_fn( _key, values)
+
+ def _format_values(self, key, values):
+ """Format entry values ie. USE flags, keywords,...
+
+ @type key: str
+ @param key: a pre-formatted cpv
+ @type values: list of pre-formatted strings
+ @param values: ['flag1', 'flag2',...]
+ @rtype: str
+ @return: formatted options string
+ """
+
+ result = []
+ self.twrap.initial_indent = pp.cpv(key+" ")
+ self.twrap.subsequent_indent = " " * (len(key)+1)
+ result.append(self.twrap.fill(values))
+ return '\n'.join(result)
+
+ def print_use(self, key, values):
+ """Prints a USE flag string.
+ """
+ if self.pretend:
+ flags = []
+ for flag in values:
+ flags.append(pp.useflag(flag, (flag[0] != '-')))
+ print(self._format_values(self.spacer+key, ' '.join(flags)))
+ else:
+ line = ' '.join([key, ' '.join(values)])
+ self.use_lines.append(line)
+
+
+ def print_keyword(self):
+ pass
+
+
+ def print_unmask(self):
+ pass
+
+ def header(self):
+ """Generates a file header
+ """
+
+ h=("# This package.%s file was generated by "
+ %self.target +
+ "gentoolkit's 'analyse rebuild' module\n"
+ )
+ return h
diff --git a/pym/gentoolkit/analyse/rebuild.py b/pym/gentoolkit/analyse/rebuild.py
new file mode 100644
index 0000000..e04d8be
--- /dev/null
+++ b/pym/gentoolkit/analyse/rebuild.py
@@ -0,0 +1,235 @@
+#!/usr/bin/python
+#
+# Copyright 2010 Brian Dolbec <brian.dolbec@gmail.com>
+# Copyright(c) 2010, Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+
+
+"""Provides a rebuild file of USE flags or keywords used and by
+what packages according to the Installed package database"""
+
+
+from __future__ import print_function
+
+
+import os
+import sys
+
+import gentoolkit
+from gentoolkit.dbapi import PORTDB, VARDB
+from gentoolkit.analyse.base import ModuleBase
+from gentoolkit import pprinter as pp
+from gentoolkit.analyse.lib import (get_installed_use, get_flags,
+ abs_flag, abs_list, FlagAnalyzer)
+from gentoolkit.analyse.output import RebuildPrinter
+
+import portage
+
+
+def cpv_all_diff_use(
+ cpvs=None,
+ system_flags=None,
+ # override-able for testing
+ _get_flags=get_flags,
+ _get_used=get_installed_use
+ ):
+ """Data gathering and analysis function determines
+ the difference between the current default USE flag settings
+ and the currently installed pkgs recorded USE flag settings
+
+ @type cpvs: list
+ @param cpvs: optional list of [cat/pkg-ver,...] to analyse or
+ defaults to entire installed pkg db
+ @type: system_flags: list
+ @param system_flags: the current default USE flags as defined
+ by portage.settings["USE"].split()
+ @type _get_flags: function
+ @param _get_flags: ovride-able for testing,
+ defaults to gentoolkit.analyse.lib.get_flags
+ @param _get_used: ovride-able for testing,
+ defaults to gentoolkit.analyse.lib.get_installed_use
+ @rtype dict. {cpv:['flag1', '-flag2',...]}
+ """
+ if cpvs is None:
+ cpvs = VARDB.cpv_all()
+ cpvs.sort()
+ data = {}
+ # pass them in to override for tests
+ flags = FlagAnalyzer(system_flags,
+ filter_defaults=True,
+ target="USE",
+ _get_flags=_get_flags,
+ _get_used=get_installed_use
+ )
+ for cpv in cpvs:
+ plus, minus, unset = flags.analyse_cpv(cpv)
+ for flag in minus:
+ plus.add("-"+flag)
+ if len(plus):
+ data[cpv] = list(plus)
+ return data
+
+
+class Rebuild(ModuleBase):
+ """Installed db analysis tool to query the installed databse
+ and produce/output stats for USE flags or keywords/mask.
+ The 'rebuild' action output is in the form suitable for file type output
+ to create a new package.use, package.keywords, package.unmask
+ type files in the event of needing to rebuild the
+ /etc/portage/* user configs
+ """
+ def __init__(self):
+ ModuleBase.__init__(self)
+ self.module_name = "rebuild"
+ self.options = {
+ "use": False,
+ "keywords": False,
+ "unmask": False,
+ "verbose": False,
+ "quiet": False,
+ "exact": False,
+ "pretend": False,
+ #"unset": False
+ }
+ self.module_opts = {
+ "-p": ("pretend", "boolean", True),
+ "--pretend": ("pretend", "boolean", True),
+ "-e": ("exact", "boolean", True),
+ "--exact": ("exact", "boolean", True),
+ "-v": ("verbose", "boolean", True),
+ "--verbose": ("verbose", "boolean", True),
+ }
+ self.formatted_options = [
+ (" -h, --help", "Outputs this useage message"),
+ (" -p, --pretend", "Does not actually create the files."),
+ (" ", "It directs the outputs to the screen"),
+ (" -e, --exact", "will atomize the package with a"),
+ (" ", "leading '=' and include the version")
+ ]
+ self.formatted_args = [
+ (" use",
+ "causes the action to analyse the installed packages USE flags"),
+ (" keywords",
+ "causes the action to analyse the installed packages keywords"),
+ (" unmask",
+ "causes the action to analyse the installed packages " + \
+ "current mask status")
+ ]
+ self.short_opts = "hepv"
+ self.long_opts = ("help", "exact", "pretend", "verbose")
+ self.need_queries = True
+ self.arg_spec = "TargetSpec"
+ self.arg_options = ['use', 'keywords', 'unmask']
+ self.arg_option = False
+ self.warning = (
+ " CAUTION",
+ "This is beta software and some features/options are incomplete,",
+ "some features may change in future releases includig its name.",
+ "The file generated is saved in your home directory",
+ "Feedback will be appreciated, http://bugs.gentoo.org")
+
+
+
+ def run(self, input_args, quiet=False):
+ """runs the module
+
+ @param input_args: input arguments to be parsed
+ """
+ self.options['quiet'] = quiet
+ query = self.main_setup(input_args)
+ query = self.validate_query(query)
+ if query in ["use"]:
+ self.rebuild_use()
+ elif query in ["keywords"]:
+ self.rebuild_keywords()
+ elif query in ["unmask"]:
+ self.rebuild_unmask()
+
+
+ def rebuild_use(self):
+ if not self.options["quiet"]:
+ print()
+ print(" -- Scanning installed packages for USE flag settings that")
+ print(" do not match the default settings")
+ system_use = portage.settings["USE"].split()
+ output = RebuildPrinter("use", self.options["pretend"], self.options["exact"])
+ pkgs = cpv_all_diff_use(system_flags=system_use)
+ pkg_count = len(pkgs)
+ if self.options["verbose"]:
+ print()
+ print((pp.emph(" -- Found ") + pp.number(str(pkg_count)) +
+ pp.emph(" packages that need entries")))
+ #print pp.emph(" package.use to maintain their current setting")
+ if pkgs:
+ pkg_keys = sorted(pkgs)
+ #print len(pkgs)
+ if self.options["pretend"] and not self.options["quiet"]:
+ print()
+ print(pp.globaloption(
+ " -- These are the installed packages & flags " +
+ "that were detected"))
+ print(pp.globaloption(" to need flag settings other " +
+ "than the defaults."))
+ print()
+ elif not self.options["quiet"]:
+ print(" -- preparing pkgs for file entries")
+ flag_count = 0
+ unique_flags = set()
+ for pkg in pkg_keys:
+ if self.options['verbose']:
+ flag_count += len(pkgs[pkg])
+ unique_flags.update(abs_list(pkgs[pkg]))
+ output(pkg, pkgs[pkg])
+ if self.options['verbose']:
+ message = (pp.emph(" ") +
+ pp.number(str(len(unique_flags))) +
+ pp.emph(" unique flags\n") + " " +
+ pp.number(str(flag_count))+
+ pp.emph(" flag entries\n") + " " +
+ pp.number(str(pkg_count)) +
+ pp.emph(" different packages"))
+ print()
+ print(pp.globaloption(" -- Totals"))
+ print(message)
+ #print
+ #unique = list(unique_flags)
+ #unique.sort()
+ #print unique
+ if not self.options["pretend"]:
+ filepath = os.path.expanduser('~/package.use.test')
+ self.save_file(filepath, output.use_lines)
+
+ def rebuild_keywords(self):
+ print("Module action not yet available")
+ print()
+
+ def rebuild_unmask(self):
+ print("Module action not yet available")
+ print()
+
+
+ def save_file(self, filepath, data):
+ """Writes the data to the file determined by filepath
+
+ @param filepath: string. eg. '/path/to/filename'
+ @param data: list of lines to write to filepath
+ """
+ if not self.options["quiet"]:
+ print(' - Saving file: %s' %filepath)
+ with open(filepath, "w") as output:
+ output.write('\n'.join(data))
+ print(" - Done")
+
+
+def main(input_args):
+ """Common starting method by the analyse master
+ unless all modules are converted to this class method.
+
+ @param input_args: input args as supplied by equery master module.
+ """
+ query_module = Rebuild()
+ query_module.run(input_args, gentoolkit.CONFIG['quiet'])
+
+# vim: set ts=4 sw=4 tw=79:
+
diff --git a/pym/gentoolkit/atom.py b/pym/gentoolkit/atom.py
new file mode 100644
index 0000000..eb4a358
--- /dev/null
+++ b/pym/gentoolkit/atom.py
@@ -0,0 +1,347 @@
+#!/usr/bin/python
+#
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+"""Subclasses portage.dep.Atom to provide methods on a Gentoo atom string."""
+
+__all__ = ('Atom',)
+
+# =======
+# Imports
+# =======
+
+import weakref
+
+import portage
+
+from gentoolkit.cpv import CPV
+from gentoolkit.versionmatch import VersionMatch
+from gentoolkit import errors
+
+# =======
+# Classes
+# =======
+
+class Atom(portage.dep.Atom, CPV):
+ """Portage's Atom class with improvements from pkgcore.
+
+ portage.dep.Atom provides the following instance variables:
+
+ @type operator: str
+ @ivar operator: one of ('=', '=*', '<', '>', '<=', '>=', '~', None)
+ @type cp: str
+ @ivar cp: cat/pkg
+ @type cpv: str
+ @ivar cpv: cat/pkg-ver (if ver)
+ @type slot: str or None (modified to tuple if not None)
+ @ivar slot: slot passed in as cpv:#
+ """
+
+ # Necessary for Portage versions < 2.1.7
+ _atoms = weakref.WeakValueDictionary()
+
+ def __init__(self, atom):
+ self.atom = atom
+ self.operator = self.blocker = self.use = self.slot = None
+
+ try:
+ portage.dep.Atom.__init__(self, atom)
+ except portage.exception.InvalidAtom:
+ raise errors.GentoolkitInvalidAtom(atom)
+
+ # Make operator compatible with intersects
+ if self.operator is None:
+ self.operator = ''
+
+ CPV.__init__(self, self.cpv)
+
+ # use_conditional is USE flag condition for this Atom to be required:
+ # For: !build? ( >=sys-apps/sed-4.0.5 ), use_conditional = '!build'
+ self.use_conditional = None
+
+ def __eq__(self, other):
+ if not isinstance(other, self.__class__):
+ err = "other isn't of %s type, is %s"
+ raise TypeError(err % (self.__class__, other.__class__))
+
+ if self.operator != other.operator:
+ return False
+
+ if not CPV.__eq__(self, other):
+ return False
+
+ if bool(self.blocker) != bool(other.blocker):
+ return False
+
+ if self.blocker and other.blocker:
+ if self.blocker.overlap.forbid != other.blocker.overlap.forbid:
+ return False
+
+ # Don't believe Portage has something like this
+ #c = cmp(self.negate_vers, other.negate_vers)
+ #if c:
+ # return c
+
+ if self.slot != other.slot:
+ return False
+
+ this_use = None
+ if self.use is not None:
+ this_use = sorted(self.use.tokens)
+ that_use = None
+ if other.use is not None:
+ that_use = sorted(other.use.tokens)
+ if this_use != that_use:
+ return False
+
+ # Not supported by Portage Atom yet
+ #return cmp(self.repo_name, other.repo_name)
+ return True
+
+ def __hash__(self):
+ return hash(self.atom)
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __lt__(self, other):
+ if not isinstance(other, self.__class__):
+ err = "other isn't of %s type, is %s"
+ raise TypeError(err % (self.__class__, other.__class__))
+
+ if self.operator != other.operator:
+ return self.operator < other.operator
+
+ if not CPV.__eq__(self, other):
+ return CPV.__lt__(self, other)
+
+ if bool(self.blocker) != bool(other.blocker):
+ # We want non blockers, then blockers, so only return True
+ # if self.blocker is True and other.blocker is False.
+ return bool(self.blocker) > bool(other.blocker)
+
+ if self.blocker and other.blocker:
+ if self.blocker.overlap.forbid != other.blocker.overlap.forbid:
+ # we want !! prior to !
+ return (self.blocker.overlap.forbid <
+ other.blocker.overlap.forbid)
+
+ # Don't believe Portage has something like this
+ #c = cmp(self.negate_vers, other.negate_vers)
+ #if c:
+ # return c
+
+ if self.slot != other.slot:
+ if self.slot is None:
+ return False
+ elif other.slot is None:
+ return True
+ return self.slot < other.slot
+
+ this_use = []
+ if self.use is not None:
+ this_use = sorted(self.use.tokens)
+ that_use = []
+ if other.use is not None:
+ that_use = sorted(other.use.tokens)
+ if this_use != that_use:
+ return this_use < that_use
+
+ # Not supported by Portage Atom yet
+ #return cmp(self.repo_name, other.repo_name)
+
+ return False
+
+ def __gt__(self, other):
+ if not isinstance(other, self.__class__):
+ err = "other isn't of %s type, is %s"
+ raise TypeError(err % (self.__class__, other.__class__))
+
+ return not self <= other
+
+ def __le__(self, other):
+ if not isinstance(other, self.__class__):
+ raise TypeError("other isn't of %s type, is %s" % (
+ self.__class__, other.__class__)
+ )
+ return self < other or self == other
+
+ def __ge__(self, other):
+ if not isinstance(other, self.__class__):
+ raise TypeError("other isn't of %s type, is %s" % (
+ self.__class__, other.__class__)
+ )
+ return self > other or self == other
+
+ def __repr__(self):
+ uc = self.use_conditional
+ uc = "%s? " % uc if uc is not None else ''
+ return "<%s %r>" % (self.__class__.__name__, "%s%s" % (uc, self.atom))
+
+ def __setattr__(self, name, value):
+ object.__setattr__(self, name, value)
+
+ #R0911:121:Atom.intersects: Too many return statements (20/6)
+ #R0912:121:Atom.intersects: Too many branches (23/12)
+ # pylint: disable-msg=R0911,R0912
+ def intersects(self, other):
+ """Check if a passed in package atom "intersects" this atom.
+
+ Lifted from pkgcore.
+
+ Two atoms "intersect" if a package can be constructed that
+ matches both:
+ - if you query for just "dev-lang/python" it "intersects" both
+ "dev-lang/python" and ">=dev-lang/python-2.4"
+ - if you query for "=dev-lang/python-2.4" it "intersects"
+ ">=dev-lang/python-2.4" and "dev-lang/python" but not
+ "<dev-lang/python-2.3"
+
+ @type other: L{gentoolkit.atom.Atom} or
+ L{gentoolkit.versionmatch.VersionMatch}
+ @param other: other package to compare
+ @see: L{pkgcore.ebuild.atom}
+ """
+ # Our "cp" (cat/pkg) must match exactly:
+ if self.cp != other.cp:
+ # Check to see if one is name only:
+ # Avoid slow partitioning if we're definitely not matching
+ # (yes, this is hackish, but it's faster):
+ if self.cp[-1:] != other.cp[-1:]:
+ return False
+
+ if ((not self.category and self.name == other.name) or
+ (not other.category and other.name == self.name)):
+ return True
+ return False
+
+ # Slot dep only matters if we both have one. If we do they
+ # must be identical:
+ this_slot = getattr(self, 'slot', None)
+ that_slot = getattr(other, 'slot', None)
+ if (this_slot is not None and that_slot is not None and
+ this_slot != that_slot):
+ return False
+
+ # TODO: Uncomment when Portage's Atom supports repo
+ #if (self.repo_name is not None and other.repo_name is not None and
+ # self.repo_name != other.repo_name):
+ # return False
+
+ # Use deps are similar: if one of us forces a flag on and the
+ # other forces it off we do not intersect. If only one of us
+ # cares about a flag it is irrelevant.
+
+ # Skip the (very common) case of one of us not having use deps:
+ this_use = getattr(self, 'use', None)
+ that_use = getattr(other, 'use', None)
+ if this_use and that_use:
+ # Set of flags we do not have in common:
+ flags = set(this_use.tokens) ^ set(that_use.tokens)
+ for flag in flags:
+ # If this is unset and we also have the set version we fail:
+ if flag[0] == '-' and flag[1:] in flags:
+ return False
+
+ # Remaining thing to check is version restrictions. Get the
+ # ones we can check without actual version comparisons out of
+ # the way first.
+
+ # If one of us is unversioned we intersect:
+ if not self.operator or not other.operator:
+ return True
+
+ # If we are both "unbounded" in the same direction we intersect:
+ if (('<' in self.operator and '<' in other.operator) or
+ ('>' in self.operator and '>' in other.operator)):
+ return True
+
+ # If one of us is an exact match we intersect if the other matches it:
+ if self.operator == '=':
+ if other.operator == '=*':
+ return self.fullversion.startswith(other.fullversion)
+ return VersionMatch(other, op=other.operator).match(self)
+ if other.operator == '=':
+ if self.operator == '=*':
+ return other.fullversion.startswith(self.fullversion)
+ return VersionMatch(self, op=self.operator).match(other)
+
+ # If we are both ~ matches we match if we are identical:
+ if self.operator == other.operator == '~':
+ return (self.version == other.version and
+ self.revision == other.revision)
+
+ # If we are both glob matches we match if one of us matches the other.
+ if self.operator == other.operator == '=*':
+ return (self.fullversion.startswith(other.fullversion) or
+ other.fullversion.startswith(self.fullversion))
+
+ # If one of us is a glob match and the other a ~ we match if the glob
+ # matches the ~ (ignoring a revision on the glob):
+ if self.operator == '=*' and other.operator == '~':
+ return other.fullversion.startswith(self.version)
+ if other.operator == '=*' and self.operator == '~':
+ return self.fullversion.startswith(other.version)
+
+ # If we get here at least one of us is a <, <=, > or >=:
+ if self.operator in ('<', '<=', '>', '>='):
+ # pylint screwup:
+ # E0601: Using variable 'ranged' before assignment
+ # pylint: disable-msg=E0601
+ ranged, ranged.operator = self, self.operator
+ else:
+ ranged, ranged.operator = other, other.operator
+ other, other.operator = self, self.operator
+
+ if '<' in other.operator or '>' in other.operator:
+ # We are both ranged, and in the opposite "direction" (or
+ # we would have matched above). We intersect if we both
+ # match the other's endpoint (just checking one endpoint
+ # is not enough, it would give a false positive on <=2 vs >2)
+ return (
+ VersionMatch(other, op=other.operator).match(ranged) and
+ VersionMatch(ranged, op=ranged.operator).match(other)
+ )
+
+ if other.operator == '~':
+ # Other definitely matches its own version. If ranged also
+ # does we're done:
+ if VersionMatch(ranged, op=ranged.operator).match(other):
+ return True
+ # The only other case where we intersect is if ranged is a
+ # > or >= on other's version and a nonzero revision. In
+ # that case other will match ranged. Be careful not to
+ # give a false positive for ~2 vs <2 here:
+ return (ranged.operator in ('>', '>=') and
+ VersionMatch(other, op=other.operator).match(ranged))
+
+ if other.operator == '=*':
+ # a glob match definitely matches its own version, so if
+ # ranged does too we're done:
+ if VersionMatch(ranged, op=ranged.operator).match(other):
+ return True
+ if '<' in ranged.operator:
+ # If other.revision is not defined then other does not
+ # match anything smaller than its own fullversion:
+ if other.revision:
+ return False
+
+ # If other.revision is defined then we can always
+ # construct a package smaller than other.fullversion by
+ # tagging e.g. an _alpha1 on.
+ return ranged.fullversion.startswith(other.version)
+ else:
+ # Remaining cases where this intersects: there is a
+ # package greater than ranged.fullversion and
+ # other.fullversion that they both match.
+ return ranged.fullversion.startswith(other.version)
+
+ # Handled all possible ops.
+ raise NotImplementedError(
+ 'Someone added an operator without adding it to intersects')
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/base.py b/pym/gentoolkit/base.py
new file mode 100644
index 0000000..832d902
--- /dev/null
+++ b/pym/gentoolkit/base.py
@@ -0,0 +1,149 @@
+#!/usr/bin/python
+#
+# Copyright(c) 2009 - 2010, Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header: $
+
+"""Gentoolkit Base Module class to hold common module operation functions
+"""
+
+from __future__ import print_function
+
+__docformat__ = 'epytext'
+
+
+import errno
+import os
+import sys
+import time
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit
+from gentoolkit import errors
+#from gentoolkit.textwrap_ import TextWrapper
+import gentoolkit.pprinter as pp
+from gentoolkit.formatters import format_options
+
+
+GLOBAL_OPTIONS = (
+ (" -h, --help", "display this help message"),
+ (" -q, --quiet", "minimal output"),
+ (" -C, --no-color", "turn off colors"),
+ (" -N, --no-pipe", "turn off pipe detection"),
+ (" -V, --version", "display version info")
+)
+
+
+def initialize_configuration():
+ """Setup the standard equery config"""
+
+ # Get terminal size
+ term_width = pp.output.get_term_size()[1]
+ if term_width == -1:
+ # get_term_size() failed. Set a sane default width:
+ term_width = 80
+ # Terminal size, minus a 1-char margin for text wrapping
+ gentoolkit.CONFIG['termWidth'] = term_width - 1
+ # Guess color output
+ if (gentoolkit.CONFIG['color'] == -1 and (not sys.stdout.isatty() or
+ os.getenv("NOCOLOR") in ("yes", "true")) or gentoolkit.CONFIG['color'] == 0):
+ pp.output.nocolor()
+ gentoolkit.CONFIG['verbose'] = not gentoolkit.CONFIG['piping']
+
+
+def split_arguments(args):
+ """Separate module name from module arguments"""
+
+ return args.pop(0), args
+
+
+def main_usage(module_info):
+ """Return the main usage message for analyse"""
+ return "%(usage)s %(product)s [%(g_opts)s] %(mod_name)s [%(mod_opts)s]" % {
+ 'usage': pp.emph("Usage:"),
+ 'product': pp.productname(module_info["__productname__"]),
+ 'g_opts': pp.globaloption("global-options"),
+ 'mod_name': pp.command("module-name"),
+ 'mod_opts': pp.localoption("module-options")
+ }
+
+
+def print_version(module_info):
+ """Print the version of this tool to the console."""
+
+ print("%(product)s (%(version)s) - %(docstring)s" % {
+ "product": pp.productname(module_info["__productname__"]),
+ "version": module_info["__version__"],
+ "docstring": module_info["__doc__"]
+ })
+
+
+def print_help(module_info, formatted_options=None, with_description=True):
+ """Print description, usage and a detailed help message.
+
+ @param with_description (bool): Option to print module's __doc__ or not
+ """
+
+ if with_description:
+ print()
+ print(module_info["__doc__"])
+ print()
+ print(main_usage(module_info))
+ print()
+ print(pp.globaloption("global options"))
+ print(format_options(GLOBAL_OPTIONS))
+ print()
+ if formatted_options:
+ print(pp.command("modules") + " (" + pp.command("short name") + ")")
+ print(format_options(formatted_options))
+ else:
+ print("Error: calling function did not supply formatted options")
+ print()
+
+
+def parse_global_options(global_opts, args, module_info, formatted_options):
+ """Parse global input args and return True if we should display help for
+ the called module, else False (or display help and exit from here).
+ """
+
+ need_help = False
+ opts = (opt[0] for opt in global_opts)
+ for opt in opts:
+ if opt in ('-h', '--help'):
+ if args:
+ need_help = True
+ else:
+ print_help( module_info, formatted_options)
+ sys.exit(0)
+ elif opt in ('-q','--quiet'):
+ gentoolkit.CONFIG['quiet'] = True
+ elif opt in ('-C', '--no-color', '--nocolor'):
+ gentoolkit.CONFIG['color'] = 0
+ pp.output.nocolor()
+ elif opt in ('-N', '--no-pipe'):
+ gentoolkit.CONFIG['piping'] = False
+ elif opt in ('-V', '--version'):
+ print_version(module_info)
+ sys.exit(0)
+ elif opt in ('--debug'):
+ gentoolkit.CONFIG['debug'] = True
+ return need_help
+
+
+def mod_usage(mod_name="module", arg="pkgspec", optional=False):
+ """Provide a consistant usage message to the calling module.
+
+ @type arg: string
+ @param arg: what kind of argument the module takes (pkgspec, filename, etc)
+ @type optional: bool
+ @param optional: is the argument optional?
+ """
+
+ return "%(usage)s: %(mod_name)s [%(opts)s] %(arg)s" % {
+ 'usage': pp.emph("Usage"),
+ 'mod_name': pp.command(mod_name),
+ 'opts': pp.localoption("options"),
+ 'arg': ("[%s]" % pp.emph(arg)) if optional else pp.emph(arg)
+ }
+
diff --git a/pym/gentoolkit/cpv.py b/pym/gentoolkit/cpv.py
new file mode 100644
index 0000000..8d81c93
--- /dev/null
+++ b/pym/gentoolkit/cpv.py
@@ -0,0 +1,256 @@
+#!/usr/bin/python
+#
+# Copyright(c) 2005 Jason Stubbs <jstubbs@gentoo.org>
+# Copyright(c) 2005-2006 Brian Harring <ferringb@gmail.com>
+# Copyright(c) 2009-2010 Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+"""Provides attributes and methods for a category/package-version string."""
+
+__all__ = (
+ 'CPV',
+ 'compare_strs',
+ 'split_cpv'
+)
+
+# =======
+# Imports
+# =======
+
+import re
+
+from portage.versions import catpkgsplit, vercmp, pkgcmp
+
+from gentoolkit import errors
+
+# =======
+# Globals
+# =======
+
+isvalid_version_re = re.compile("^(?:cvs\\.)?(?:\\d+)(?:\\.\\d+)*[a-z]?"
+ "(?:_(p(?:re)?|beta|alpha|rc)\\d*)*$")
+isvalid_cat_re = re.compile("^(?:[a-zA-Z0-9][-a-zA-Z0-9+._]*(?:/(?!$))?)+$")
+_pkg_re = re.compile("^[a-zA-Z0-9+_]+$")
+# Prefix specific revision is of the form -r0<digit>+.<digit>+
+isvalid_rev_re = re.compile(r'(\d+|0\d+\.\d+)')
+
+# =======
+# Classes
+# =======
+
+class CPV(object):
+ """Provides methods on a category/package-version string.
+
+ Will also correctly split just a package or package-version string.
+
+ Example usage:
+ >>> from gentoolkit.cpv import CPV
+ >>> cpv = CPV('sys-apps/portage-2.2-r1')
+ >>> cpv.category, cpv.name, cpv.fullversion
+ ('sys-apps', 'portage', '2.2-r1')
+ >>> str(cpv)
+ 'sys-apps/portage-2.2-r1'
+ >>> # An 'rc' (release candidate) version is less than non 'rc' version:
+ ... CPV('sys-apps/portage-2') > CPV('sys-apps/portage-2_rc10')
+ True
+ """
+
+ def __init__(self, cpv, validate=False):
+ self.cpv = cpv
+ self._category = None
+ self._name = None
+ self._version = None
+ self._revision = None
+ self._cp = None
+ self._fullversion = None
+
+ self.validate = validate
+ if validate and not self.name:
+ raise errors.GentoolkitInvalidCPV(cpv)
+
+ @property
+ def category(self):
+ if self._category is None:
+ self._set_cpv_chunks()
+ return self._category
+
+ @property
+ def name(self):
+ if self._name is None:
+ self._set_cpv_chunks()
+ return self._name
+
+ @property
+ def version(self):
+ if self._version is None:
+ self._set_cpv_chunks()
+ return self._version
+
+ @property
+ def revision(self):
+ if self._revision is None:
+ self._set_cpv_chunks()
+ return self._revision
+
+ @property
+ def cp(self):
+ if self._cp is None:
+ sep = '/' if self.category else ''
+ self._cp = sep.join((self.category, self.name))
+ return self._cp
+
+ @property
+ def fullversion(self):
+ if self._fullversion is None:
+ sep = '-' if self.revision else ''
+ self._fullversion = sep.join((self.version, self.revision))
+ return self._fullversion
+
+ def _set_cpv_chunks(self):
+ chunks = split_cpv(self.cpv, validate=self.validate)
+ self._category = chunks[0]
+ self._name = chunks[1]
+ self._version = chunks[2]
+ self._revision = chunks[3]
+
+ def __eq__(self, other):
+ if not isinstance(other, self.__class__):
+ return False
+ return self.cpv == other.cpv
+
+ def __hash__(self):
+ return hash(self.cpv)
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __lt__(self, other):
+ if not isinstance(other, self.__class__):
+ raise TypeError("other isn't of %s type, is %s" % (
+ self.__class__, other.__class__)
+ )
+
+ if self.category != other.category:
+ return self.category < other.category
+ elif self.name != other.name:
+ return self.name < other.name
+ else:
+ # FIXME: this cmp() hack is for vercmp not using -1,0,1
+ # See bug 266493; this was fixed in portage-2.2_rc31
+ #return vercmp(self.fullversion, other.fullversion)
+ return vercmp(self.fullversion, other.fullversion) < 0
+
+ def __gt__(self, other):
+ if not isinstance(other, self.__class__):
+ raise TypeError("other isn't of %s type, is %s" % (
+ self.__class__, other.__class__)
+ )
+ return not self <= other
+
+ def __le__(self, other):
+ if not isinstance(other, self.__class__):
+ raise TypeError("other isn't of %s type, is %s" % (
+ self.__class__, other.__class__)
+ )
+ return self < other or self == other
+
+ def __ge__(self, other):
+ if not isinstance(other, self.__class__):
+ raise TypeError("other isn't of %s type, is %s" % (
+ self.__class__, other.__class__)
+ )
+ return self > other or self == other
+
+ def __repr__(self):
+ return "<%s %r>" % (self.__class__.__name__, str(self))
+
+ def __str__(self):
+ return self.cpv
+
+
+# =========
+# Functions
+# =========
+
+def compare_strs(pkg1, pkg2):
+ """Similar to the builtin cmp, but for package strings. Usually called
+ as: package_list.sort(cpv.compare_strs)
+
+ An alternative is to use the CPV descriptor from gentoolkit.cpv:
+ >>> cpvs = sorted(CPV(x) for x in package_list)
+
+ @see: >>> help(cmp)
+ """
+
+ pkg1 = catpkgsplit(pkg1)
+ pkg2 = catpkgsplit(pkg2)
+ if pkg1[0] != pkg2[0]:
+ return -1 if pkg1[0] < pkg2[0] else 1
+ elif pkg1[1] != pkg2[1]:
+ return -1 if pkg1[1] < pkg2[1] else 1
+ else:
+ return pkgcmp(pkg1[1:], pkg2[1:])
+
+
+def split_cpv(cpv, validate=True):
+ """Split a cpv into category, name, version and revision.
+
+ Modified from pkgcore.ebuild.cpv
+
+ @type cpv: str
+ @param cpv: pkg, cat/pkg, pkg-ver, cat/pkg-ver
+ @rtype: tuple
+ @return: (category, pkg_name, version, revision)
+ Each tuple element is a string or empty string ("").
+ """
+
+ category = name = version = revision = ''
+
+ try:
+ category, pkgver = cpv.rsplit("/", 1)
+ except ValueError:
+ pkgver = cpv
+ if validate and category and not isvalid_cat_re.match(category):
+ raise errors.GentoolkitInvalidCPV(cpv)
+ pkg_chunks = pkgver.split("-")
+ lpkg_chunks = len(pkg_chunks)
+ if lpkg_chunks == 1:
+ return (category, pkg_chunks[0], version, revision)
+ if isvalid_rev(pkg_chunks[-1]):
+ if lpkg_chunks < 3:
+ # needs at least ('pkg', 'ver', 'rev')
+ raise errors.GentoolkitInvalidCPV(cpv)
+ rev = pkg_chunks.pop(-1)
+ if rev:
+ revision = rev
+
+ if isvalid_version_re.match(pkg_chunks[-1]):
+ version = pkg_chunks.pop(-1)
+
+ if not isvalid_pkg_name(pkg_chunks):
+ raise errors.GentoolkitInvalidCPV(cpv)
+ name = '-'.join(pkg_chunks)
+
+ return (category, name, version, revision)
+
+
+def isvalid_pkg_name(chunks):
+ if not chunks[0]:
+ # this means a leading -
+ return False
+ mf = _pkg_re.match
+ if not all(not s or mf(s) for s in chunks):
+ return False
+ if chunks[-1].isdigit() or not chunks[-1]:
+ # not allowed.
+ return False
+ return True
+
+
+def isvalid_rev(s):
+ return s and s[0] == 'r' and isvalid_rev_re.match(s[1:])
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/dbapi.py b/pym/gentoolkit/dbapi.py
new file mode 100644
index 0000000..fbf0bc4
--- /dev/null
+++ b/pym/gentoolkit/dbapi.py
@@ -0,0 +1,17 @@
+#!/usr/bin/python
+#
+# Copyright 2009 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+"""Provides access to Portage database api"""
+
+import portage
+
+BINDB = portage.db[portage.root]["bintree"].dbapi
+PORTDB = portage.db[portage.root]["porttree"].dbapi
+VARDB = portage.db[portage.root]["vartree"].dbapi
+#virtuals = portage.db[portage.root]["virtuals"]
+
+# vim: set ts=8 sw=4 tw=79:
diff --git a/pym/gentoolkit/dependencies.py b/pym/gentoolkit/dependencies.py
new file mode 100644
index 0000000..feced63
--- /dev/null
+++ b/pym/gentoolkit/dependencies.py
@@ -0,0 +1,330 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""Provides a class for easy calculating dependencies for a given CPV."""
+
+__docformat__ = 'epytext'
+__all__ = ('Dependencies',)
+
+# =======
+# Imports
+# =======
+
+import portage
+from portage.dep import paren_reduce
+
+from gentoolkit import errors
+from gentoolkit.atom import Atom
+from gentoolkit.cpv import CPV
+from gentoolkit.helpers import uniqify
+from gentoolkit.dbapi import PORTDB, VARDB
+from gentoolkit.query import Query
+
+# =======
+# Classes
+# =======
+
+class Dependencies(Query):
+ """Access a package's dependencies and reverse dependencies.
+
+ Example usage:
+ >>> from gentoolkit.dependencies import Dependencies
+ >>> portage = Dependencies('sys-apps/portage-2.1.6.13')
+ >>> portage
+ <Dependencies 'sys-apps/portage-2.1.6.13'>
+ >>> # All methods return gentoolkit.atom.Atom instances
+ ... portage.get_depend()
+ [<Atom '>=dev-lang/python-2.5'>, <Atom '<dev-lang/python-3.0'>, ...]
+
+ """
+ def __init__(self, query, parser=None):
+ Query.__init__(self, query)
+ self.use = []
+ self.depatom = str()
+
+ # Allow a custom parser function:
+ self.parser = parser if parser else self._parser
+
+ def __eq__(self, other):
+ if self.atom != other.atom:
+ return False
+ else:
+ return True
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __hash__(self):
+ return hash((self.atom, self.depatom, tuple(self.use)))
+
+ def __repr__(self):
+ return "<%s %r>" % (self.__class__.__name__, self.atom)
+
+ def environment(self, envvars):
+ """Returns predefined env vars DEPEND, SRC_URI, etc."""
+
+ # Try to use the Portage tree first, since emerge only uses the tree
+ # when calculating dependencies
+ try:
+ result = PORTDB.aux_get(self.cpv, envvars)
+ except KeyError:
+ try:
+ result = VARDB.aux_get(self.cpv, envvars)
+ except KeyError:
+ return []
+ return result
+
+ def get_depend(self):
+ """Get the contents of DEPEND and parse it with self.parser."""
+
+ try:
+ return self.parser(self.environment(('DEPEND',))[0])
+ except portage.exception.InvalidPackageName as err:
+ raise errors.GentoolkitInvalidCPV(err)
+
+ def get_pdepend(self):
+ """Get the contents of PDEPEND and parse it with self.parser."""
+
+ try:
+ return self.parser(self.environment(('PDEPEND',))[0])
+ except portage.exception.InvalidPackageName as err:
+ raise errors.GentoolkitInvalidCPV(err)
+
+ def get_rdepend(self):
+ """Get the contents of RDEPEND and parse it with self.parser."""
+
+ try:
+ return self.parser(self.environment(('RDEPEND',))[0])
+ except portage.exception.InvalidPackageName as err:
+ raise errors.GentoolkitInvalidCPV(err)
+
+ def get_all_depends(self):
+ """Get the contents of ?DEPEND and parse it with self.parser."""
+
+ env_vars = ('DEPEND', 'PDEPEND', 'RDEPEND')
+ try:
+ return self.parser(' '.join(self.environment(env_vars)))
+ except portage.exception.InvalidPackageName as err:
+ raise errors.GentoolkitInvalidCPV(err)
+
+ def graph_depends(
+ self,
+ max_depth=1,
+ printer_fn=None,
+ # The rest of these are only used internally:
+ depth=0,
+ seen=None,
+ depcache=None,
+ result=None
+ ):
+ """Graph direct dependencies for self.
+
+ Optionally gather indirect dependencies.
+
+ @type max_depth: int
+ @keyword max_depth: Maximum depth to recurse if.
+ <1 means no maximum depth
+ >0 means recurse only this depth;
+ @type printer_fn: callable
+ @keyword printer_fn: If None, no effect. If set, it will be applied to
+ each result.
+ @rtype: list
+ @return: [(depth, pkg), ...]
+ """
+ if seen is None:
+ seen = set()
+ if depcache is None:
+ depcache = dict()
+ if result is None:
+ result = list()
+
+ pkgdep = None
+ deps = self.get_all_depends()
+ for dep in deps:
+ if dep.atom in depcache:
+ continue
+ try:
+ pkgdep = depcache[dep.atom]
+ except KeyError:
+ pkgdep = Query(dep.atom).find_best()
+ depcache[dep.atom] = pkgdep
+ if pkgdep and pkgdep.cpv in seen:
+ continue
+ if depth < max_depth or max_depth <= 0:
+
+ if printer_fn is not None:
+ printer_fn(depth, pkgdep, dep)
+ if not pkgdep:
+ continue
+
+ seen.add(pkgdep.cpv)
+ result.append((
+ depth,
+ pkgdep.deps.graph_depends(
+ max_depth=max_depth,
+ printer_fn=printer_fn,
+ # The rest of these are only used internally:
+ depth=depth+1,
+ seen=seen,
+ depcache=depcache,
+ result=result
+ )
+ ))
+
+ if depth == 0:
+ return result
+ return pkgdep
+
+ def graph_reverse_depends(
+ self,
+ pkgset=None,
+ max_depth=-1,
+ only_direct=True,
+ printer_fn=None,
+ # The rest of these are only used internally:
+ depth=0,
+ depcache=None,
+ seen=None,
+ result=None
+ ):
+ """Graph direct reverse dependencies for self.
+
+ Example usage:
+ >>> from gentoolkit.dependencies import Dependencies
+ >>> ffmpeg = Dependencies('media-video/ffmpeg-0.5_p20373')
+ >>> # I only care about installed packages that depend on me:
+ ... from gentoolkit.helpers import get_installed_cpvs
+ >>> # I want to pass in a sorted list. We can pass strings or
+ ... # Package or Atom types, so I'll use Package to sort:
+ ... from gentoolkit.package import Package
+ >>> installed = sorted(Package(x) for x in get_installed_cpvs())
+ >>> deptree = ffmpeg.graph_reverse_depends(
+ ... only_direct=False, # Include indirect revdeps
+ ... pkgset=installed) # from installed pkgset
+ >>> len(deptree)
+ 44
+
+ @type pkgset: iterable
+ @keyword pkgset: sorted pkg cpv strings or anything sublassing
+ L{gentoolkit.cpv.CPV} to use for calculate our revdep graph.
+ @type max_depth: int
+ @keyword max_depth: Maximum depth to recurse if only_direct=False.
+ -1 means no maximum depth;
+ 0 is the same as only_direct=True;
+ >0 means recurse only this many times;
+ @type only_direct: bool
+ @keyword only_direct: to recurse or not to recurse
+ @type printer_fn: callable
+ @keyword printer_fn: If None, no effect. If set, it will be applied to
+ each L{gentoolkit.atom.Atom} object as it is added to the results.
+ @rtype: list
+ @return: L{gentoolkit.dependencies.Dependencies} objects
+ """
+ if not pkgset:
+ err = ("%s kwarg 'pkgset' must be set. "
+ "Can be list of cpv strings or any 'intersectable' object.")
+ raise errors.GentoolkitFatalError(err % (self.__class__.__name__,))
+
+ if depcache is None:
+ depcache = dict()
+ if seen is None:
+ seen = set()
+ if result is None:
+ result = list()
+
+ if depth == 0:
+ pkgset = tuple(Dependencies(x) for x in pkgset)
+
+ pkgdep = None
+ for pkgdep in pkgset:
+ try:
+ all_depends = depcache[pkgdep]
+ except KeyError:
+ all_depends = uniqify(pkgdep.get_all_depends())
+ depcache[pkgdep] = all_depends
+
+ dep_is_displayed = False
+ for dep in all_depends:
+ # TODO: Add ability to determine if dep is enabled by USE flag.
+ # Check portage.dep.use_reduce
+ if dep.intersects(self):
+ pkgdep.depth = depth
+ pkgdep.matching_dep = dep
+ if printer_fn is not None:
+ printer_fn(pkgdep, dep_is_displayed=dep_is_displayed)
+ result.append(pkgdep)
+ dep_is_displayed = True
+
+ # if --indirect specified, call ourselves again with the dep
+ # Do not call if we have already called ourselves.
+ if (
+ dep_is_displayed and not only_direct and
+ pkgdep.cpv not in seen and
+ (depth < max_depth or max_depth == -1)
+ ):
+
+ seen.add(pkgdep.cpv)
+ result.append(
+ pkgdep.graph_reverse_depends(
+ pkgset=pkgset,
+ max_depth=max_depth,
+ only_direct=only_direct,
+ printer_fn=printer_fn,
+ depth=depth+1,
+ depcache=depcache,
+ seen=seen,
+ result=result
+ )
+ )
+
+ if depth == 0:
+ return result
+ return pkgdep
+
+ def _parser(self, deps, use_conditional=None, depth=0):
+ """?DEPEND file parser.
+
+ @rtype: list
+ @return: L{gentoolkit.atom.Atom} objects
+ """
+ result = []
+
+ if depth == 0:
+ deps = paren_reduce(deps)
+ for tok in deps:
+ if tok == '||':
+ continue
+ if tok[-1] == '?':
+ use_conditional = tok[:-1]
+ continue
+ if isinstance(tok, list):
+ sub_r = self._parser(tok, use_conditional, depth=depth+1)
+ result.extend(sub_r)
+ use_conditional = None
+ continue
+ # FIXME: This is a quick fix for bug #299260.
+ # A better fix is to not discard blockers in the parser,
+ # but to check for atom.blocker in whatever equery/depends
+ # (in this case) and ignore them there.
+ # TODO: Test to see how much a performance impact ignoring
+ # blockers here rather than checking for atom.blocker has.
+ if tok[0] == '!':
+ # We're not interested in blockers
+ continue
+ # skip it if it's empty
+ if tok and tok != '':
+ atom = Atom(tok)
+ if use_conditional is not None:
+ atom.use_conditional = use_conditional
+ result.append(atom)
+ else:
+ message = "dependencies.py: _parser() found an empty " +\
+ "dep string token for: %s, deps= %s"
+ raise errors.GentoolkitInvalidAtom(message %(self.cpv, deps))
+
+ return result
+
+# vim: set ts=4 sw=4 tw=0:
diff --git a/pym/gentoolkit/deprecated/helpers.py b/pym/gentoolkit/deprecated/helpers.py
new file mode 100644
index 0000000..59d6a2c
--- /dev/null
+++ b/pym/gentoolkit/deprecated/helpers.py
@@ -0,0 +1,178 @@
+#!/usr/bin/python2
+#
+# Copyright(c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+from __future__ import print_function
+
+import warnings
+
+import portage
+from gentoolkit import *
+from package import *
+from pprinter import warn
+try:
+ from portage.util import unique_array
+except ImportError:
+ from portage_util import unique_array
+
+def find_packages(search_key, masked=False):
+ """Returns a list of Package objects that matched the search key."""
+ warnings.warn("Deprecated. Use helpers2.find_packages.", DeprecationWarning)
+ try:
+ if masked:
+ t = portage.db[portage.root]["porttree"].dbapi.xmatch("match-all", search_key)
+ t += portage.db[portage.root]["vartree"].dbapi.match(search_key)
+ else:
+ t = portage.db[portage.root]["porttree"].dbapi.match(search_key)
+ t += portage.db[portage.root]["vartree"].dbapi.match(search_key)
+ # catch the "amgigous package" Exception
+ except ValueError as e:
+ if isinstance(e[0],list):
+ t = []
+ for cp in e[0]:
+ if masked:
+ t += portage.db[portage.root]["porttree"].dbapi.xmatch("match-all", cp)
+ t += portage.db[portage.root]["vartree"].dbapi.match(cp)
+ else:
+ t += portage.db[portage.root]["porttree"].dbapi.match(cp)
+ t += portage.db[portage.root]["vartree"].dbapi.match(cp)
+ else:
+ raise ValueError(e)
+ except portage_exception.InvalidAtom as e:
+ print(warn("Invalid Atom: '%s'" % str(e)))
+ return []
+ # Make the list of packages unique
+ t = unique_array(t)
+ t.sort()
+ return [Package(x) for x in t]
+
+def find_installed_packages(search_key, masked=False):
+ """Returns a list of Package objects that matched the search key."""
+ warnings.warn("Deprecated. Use helpers2.find_installed_packages.",
+ DeprecationWarning)
+ try:
+ t = portage.db[portage.root]["vartree"].dbapi.match(search_key)
+ # catch the "amgigous package" Exception
+ except ValueError as e:
+ if isinstance(e[0],list):
+ t = []
+ for cp in e[0]:
+ t += portage.db[portage.root]["vartree"].dbapi.match(cp)
+ else:
+ raise ValueError(e)
+ except portage_exception.InvalidAtom as e:
+ print(warn("Invalid Atom: '%s'" % str(e)))
+ return []
+ return [Package(x) for x in t]
+
+def find_best_match(search_key):
+ """Returns a Package object for the best available candidate that
+ matched the search key."""
+ warnings.warn("Deprecated. Use helpers2.find_best_match.",
+ DeprecationWarning)
+ t = portage.db[portage.root]["porttree"].dep_bestmatch(search_key)
+ if t:
+ return Package(t)
+ return None
+
+def find_system_packages(prefilter=None):
+ """Returns a tuple of lists, first list is resolved system packages,
+ second is a list of unresolved packages."""
+ pkglist = settings.packages
+ resolved = []
+ unresolved = []
+ for x in pkglist:
+ cpv = x.strip()
+ if len(cpv) and cpv[0] == "*":
+ pkg = find_best_match(cpv)
+ if pkg:
+ resolved.append(pkg)
+ else:
+ unresolved.append(cpv)
+ return (resolved, unresolved)
+
+def find_world_packages(prefilter=None):
+ """Returns a tuple of lists, first list is resolved world packages,
+ seond is unresolved package names."""
+ f = open(portage.root+portage.WORLD_FILE)
+ pkglist = f.readlines()
+ resolved = []
+ unresolved = []
+ for x in pkglist:
+ cpv = x.strip()
+ if len(cpv) and cpv[0] != "#":
+ pkg = find_best_match(cpv)
+ if pkg:
+ resolved.append(pkg)
+ else:
+ unresolved.append(cpv)
+ return (resolved,unresolved)
+
+def find_all_installed_packages(prefilter=None):
+ """Returns a list of all installed packages, after applying the prefilter
+ function"""
+ warnings.warn("Deprecated. Use helpers2.get_installed_cpvs.",
+ DeprecationWarning)
+ t = vartree.dbapi.cpv_all()
+ if prefilter:
+ t = list(filter(prefilter,t))
+ return [Package(x) for x in t]
+
+def find_all_uninstalled_packages(prefilter=None):
+ """Returns a list of all uninstalled packages, after applying the prefilter
+ function"""
+ warnings.warn("Deprecated. Use helpers2.get_uninstalled_cpvs.",
+ DeprecationWarning)
+ alist = find_all_packages(prefilter)
+ return [x for x in alist if not x.is_installed()]
+
+def find_all_packages(prefilter=None):
+ """Returns a list of all known packages, installed or not, after applying
+ the prefilter function"""
+ warnings.warn("Deprecated. Use helpers2.get_cpvs.", DeprecationWarning)
+ t = porttree.dbapi.cp_all()
+ t += vartree.dbapi.cp_all()
+ if prefilter:
+ t = list(filter(prefilter,t))
+ t = unique_array(t)
+ t2 = []
+ for x in t:
+ t2 += porttree.dbapi.cp_list(x)
+ t2 += vartree.dbapi.cp_list(x)
+ t2 = unique_array(t2)
+ return [Package(x) for x in t2]
+
+def split_package_name(name):
+ """Returns a list on the form [category, name, version, revision]. Revision will
+ be 'r0' if none can be inferred. Category and version will be empty, if none can
+ be inferred."""
+ warnings.warn("Deprecated. Just use portage.catpkgsplit or apply "
+ "gentoolkit.package.Package to access pkg.category, pkg.revision, etc.",
+ DeprecationWarning)
+ r = portage.catpkgsplit(name)
+ if not r:
+ r = name.split("/")
+ if len(r) == 1:
+ return ["", name, "", "r0"]
+ else:
+ return r + ["", "r0"]
+ else:
+ r = list(r)
+ if r[0] == 'null':
+ r[0] = ''
+ return r
+
+# XXX: Defunct: use helpers2.compare_package_strings
+#def sort_package_list(pkglist):
+# """Returns the list ordered in the same way portage would do with lowest version
+# at the head of the list."""
+# pkglist.sort(Package.compare_version)
+# return pkglist
+
+if __name__ == "__main__":
+ print("This module is for import only")
diff --git a/pym/gentoolkit/eclean/__init__.py b/pym/gentoolkit/eclean/__init__.py
new file mode 100644
index 0000000..edf9385
--- /dev/null
+++ b/pym/gentoolkit/eclean/__init__.py
@@ -0,0 +1,4 @@
+#!/usr/bin/python
+#
+# Copyright 2003-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
diff --git a/pym/gentoolkit/eclean/clean.py b/pym/gentoolkit/eclean/clean.py
new file mode 100644
index 0000000..b2cc562
--- /dev/null
+++ b/pym/gentoolkit/eclean/clean.py
@@ -0,0 +1,149 @@
+#!/usr/bin/python
+
+# Copyright 2003-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+
+from __future__ import print_function
+
+
+import os
+import sys
+
+import gentoolkit.pprinter as pp
+from gentoolkit.eclean.pkgindex import PkgIndex
+
+
+class CleanUp(object):
+ """Performs all cleaning actions to distfiles or package directories.
+
+ @param controller: a progress output/user interaction controller function
+ which returns a Boolean to control file deletion
+ or bypassing/ignoring
+ """
+
+ def __init__(self, controller):
+ self.controller = controller
+
+ def clean_dist(self, clean_dict):
+ """Calculate size of each entry for display, prompt user if needed,
+ delete files if approved and return the total size of files that
+ have been deleted.
+
+ @param clean_dict: dictionary of {'display name':[list of files]}
+
+ @rtype: int
+ @return: total size that was cleaned
+ """
+ file_type = 'file'
+ clean_keys = self._sort_keys(clean_dict)
+ clean_size = 0
+ # clean all entries one by one
+ for key in clean_keys:
+ clean_size += self._clean_files(clean_dict[key], key, file_type)
+ # return total size of deleted or to delete files
+ return clean_size
+
+ def clean_pkgs(self, clean_dict, pkgdir):
+ """Calculate size of each entry for display, prompt user if needed,
+ delete files if approved and return the total size of files that
+ have been deleted.
+
+ @param clean_dict: dictionary of {'display name':[list of files]}
+ @param metadata: package index of type portage.getbinpkg.PackageIndex()
+ @param pkgdir: path to the package directory to be cleaned
+
+ @rtype: int
+ @return: total size that was cleaned
+ """
+ file_type = 'binary package'
+ clean_keys = self._sort_keys(clean_dict)
+ clean_size = 0
+ # clean all entries one by one
+ for key in clean_keys:
+ clean_size += self._clean_files(clean_dict[key], key, file_type)
+
+ # run 'emaint --fix' here
+ if clean_size:
+ index_control = PkgIndex(self.controller)
+ # emaint is not yet importable so call it
+ # print a blank line here for separation
+ print()
+ clean_size += index_control.call_emaint()
+ # return total size of deleted or to delete files
+ return clean_size
+
+
+ def pretend_clean(self, clean_dict):
+ """Shortcut function that calculates total space savings
+ for the files in clean_dict.
+
+ @param clean_dict: dictionary of {'display name':[list of files]}
+ @rtype: integer
+ @return: total size that would be cleaned
+ """
+ file_type = 'file'
+ clean_keys = self._sort_keys(clean_dict)
+ clean_size = 0
+ # tally all entries one by one
+ for key in clean_keys:
+ key_size = self._get_size(clean_dict[key])
+ self.controller(key_size, key, clean_dict[key], file_type)
+ clean_size += key_size
+ return clean_size
+
+ def _get_size(self, key):
+ """Determine the total size for an entry (may be several files)."""
+ key_size = 0
+ for file_ in key:
+ #print file_
+ # get total size for an entry (may be several files, and
+ # links don't count
+ # ...get its statinfo
+ try:
+ statinfo = os.stat(file_)
+ if statinfo.st_nlink == 1:
+ key_size += statinfo.st_size
+ except EnvironmentError as er:
+ print( pp.error(
+ "Could not get stat info for:" + file_), file=sys.stderr)
+ print( pp.error("Error: %s" %str(er)), file=sys.stderr)
+ return key_size
+
+ def _sort_keys(self, clean_dict):
+ """Returns a list of sorted dictionary keys."""
+ # sorting helps reading
+ clean_keys = sorted(clean_dict)
+ return clean_keys
+
+ def _clean_files(self, files, key, file_type):
+ """File removal function."""
+ clean_size = 0
+ for file_ in files:
+ #print file_, type(file_)
+ # ...get its statinfo
+ try:
+ statinfo = os.stat(file_)
+ except EnvironmentError as er:
+ print( pp.error(
+ "Could not get stat info for:" + file_), file=sys.stderr)
+ print( pp.error(
+ "Error: %s" %str(er)), file=sys.stderr)
+ if self.controller(statinfo.st_size, key, file_, file_type):
+ # ... try to delete it.
+ try:
+ os.unlink(file_)
+ # only count size if successfully deleted and not a link
+ if statinfo.st_nlink == 1:
+ clean_size += statinfo.st_size
+ except EnvironmentError as er:
+ print( pp.error("Could not delete "+file_), file=sys.stderr)
+ print( pp.error("Error: %s" %str(er)), file=sys.stderr)
+ return clean_size
+
+
+
+
+
+
+
diff --git a/pym/gentoolkit/eclean/cli.py b/pym/gentoolkit/eclean/cli.py
new file mode 100644
index 0000000..9e4bfd9
--- /dev/null
+++ b/pym/gentoolkit/eclean/cli.py
@@ -0,0 +1,504 @@
+#!/usr/bin/python
+
+# Copyright 2003-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+
+from __future__ import print_function
+
+
+__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."
+
+
+import os
+import sys
+import re
+import time
+import getopt
+
+import portage
+from portage.output import white, yellow, turquoise, green, teal, red
+
+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.clean import CleanUp
+from gentoolkit.eclean.output import OutputControl
+#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")
+
+
+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, --destructive")+
+ " - 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 --destructive)", 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(" NONE :)"), 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 --destructive)", 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)
+
+
+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
+
+
+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])
+
+
+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
+ 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"):
+ options['nocolor'] = True
+ pp.output.nocolor()
+ elif o in ("-d", "--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
+ else:
+ return_code = False
+ # sanity check of --destructive 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 --destructive mode." % opt), file=sys.stderr)
+ options[opt] = False
+ 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", "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'] = ""
+ getopt_options['long']['packages'] = [""]
+ # 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
+ # if called by a well-named symlink, set the acction 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]) in \
+ (__productname__+'-pkg', __productname__+'-packages'):
+ action = 'packages'
+ elif os.path.basename(sys.argv[0]) in \
+ (__productname__+'-dist', __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']
+ )
+ # 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
+ cleaner = CleanUp( output.progress_controller)
+ 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, return
+ 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("Deprecated") +
+ 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("Deprecated") +
+ 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["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)
diff --git a/pym/gentoolkit/eclean/exclude.py b/pym/gentoolkit/eclean/exclude.py
new file mode 100644
index 0000000..5f48cab
--- /dev/null
+++ b/pym/gentoolkit/eclean/exclude.py
@@ -0,0 +1,262 @@
+#!/usr/bin/python
+
+# Copyright 2003-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+
+from __future__ import print_function
+
+
+import os
+import sys
+import re
+import portage
+
+from gentoolkit.pprinter import warn
+
+# Misc. shortcuts to some portage stuff:
+listdir = portage.listdir
+
+FILENAME_RE = [re.compile(r'(?P<pkgname>[-a-zA-z0-9\+]+)(?P<ver>-\d+\S+)'),
+ re.compile(r'(?P<pkgname>[-a-zA-z]+)(?P<ver>_\d+\S+)'),
+ re.compile(r'(?P<pkgname>[-a-zA-z_]+)(?P<ver>\d\d+\S+)'),
+ re.compile(r'(?P<pkgname>[-a-zA-z0-9_]+)(?P<ver>-default\S+)'),
+ re.compile(r'(?P<pkgname>[-a-zA-z0-9]+)(?P<ver>_\d\S+)'),
+ re.compile(r'(?P<pkgname>[-a-zA-z0-9\+\.]+)(?P<ver>-\d+\S+)'),
+ re.compile(r'(?P<pkgname>[-a-zA-z0-9\+\.]+)(?P<ver>.\d+\S+)')]
+
+debug_modules = []
+
+def dprint(module, message):
+ if module in debug_modules:
+ print(message)
+
+def isValidCP(cp):
+ """Check whether a string is a valid cat/pkg-name.
+
+ This is for 2.0.51 vs. CVS HEAD compatibility, I've not found any function
+ for that which would exists in both. Weird...
+
+ @param cp: catageory/package string
+ @rtype: bool
+ """
+
+ if not '/' in cp:
+ return False
+ try:
+ portage.cpv_getkey(cp+"-0")
+ except:
+ return False
+ else:
+ return True
+
+
+class ParseExcludeFileException(Exception):
+ """For parseExcludeFile() -> main() communication.
+
+ @param value: Error message string
+ """
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+
+
+def parseExcludeFile(filepath, output):
+ """Parses an exclusion file.
+
+ @param filepath: file containing the list of cat/pkg's to exclude
+ @param output: --verbose enabled output method or "lambda x: None"
+
+ @rtype: dict
+ @return: an exclusion dict
+ @raise ParseExcludeFileException: in case of fatal error
+ """
+
+ exclude = {
+ 'categories': {},
+ 'packages': {},
+ 'anti-packages': {},
+ 'filenames': {}
+ }
+ output("Parsing Exclude file: " + filepath)
+ try:
+ file_ = open(filepath,"r")
+ except IOError:
+ raise ParseExcludeFileException("Could not open exclusion file: " +
+ filepath)
+ filecontents = file_.readlines()
+ file_.close()
+ cat_re = re.compile('^(?P<cat>[a-zA-Z0-9]+-[a-zA-Z0-9]+)(/\*)?$')
+ cp_re = re.compile('^(?P<cp>[-a-zA-Z0-9_]+/[-a-zA-Z0-9_]+)$')
+ # used to output the line number for exception error reporting
+ linenum = 0
+ for line in filecontents:
+ # need to increment it here due to continue statements.
+ linenum += 1
+ line = line.strip()
+ if not len(line): # skip blank a line
+ continue
+ if line[0] == '#': # skip a comment line
+ continue
+ #print( "parseExcludeFile: line=", line)
+ try: # category matching
+ cat = cat_re.match(line).group('cat')
+ #print( "parseExcludeFile: found cat=", cat)
+ except:
+ pass
+ else:
+ if not cat in portage.settings.categories:
+ raise ParseExcludeFileException("Invalid category: "+cat +
+ " @line # " + str(linenum))
+ exclude['categories'][cat] = None
+ continue
+ dict_key = 'packages'
+ if line[0] == '!': # reverses category setting
+ dict_key = 'anti-packages'
+ line = line[1:]
+ try: # cat/pkg matching
+ cp = cp_re.match(line).group('cp')
+ #print( "parseExcludeFile: found cp=", cp)
+ if isValidCP(cp):
+ exclude[dict_key][cp] = None
+ continue
+ else:
+ raise ParseExcludeFileException("Invalid cat/pkg: "+cp +
+ " @line # " + str(linenum))
+ except:
+ pass
+ #raise ParseExcludeFileException("Invalid line: "+line)
+ try: # filename matching.
+ exclude['filenames'][line] = re.compile(line)
+ #print( "parseExcludeFile: found filenames", line)
+ except:
+ try:
+ exclude['filenames'][line] = re.compile(re.escape(line))
+ #print( "parseExcludeFile: found escaped filenames", line)
+ except:
+ raise ParseExcludeFileException("Invalid file name/regular " +
+ "expression: @line # " + str(linenum) + " line=" +line)
+ output("Exclude file parsed. Found " +
+ "%d categories, %d packages, %d anti-packages %d filenames"
+ %(len(exclude['categories']), len(exclude['packages']),
+ len(exclude['anti-packages']), len(exclude['filenames'])))
+ #print()
+ #print( "parseExcludeFile: final exclude_dict = ", exclude)
+ #print()
+ return exclude
+
+def cp_all(categories, portdb=portage.portdb ):
+ """temp function until the new portdb.cp_all([cat,...])
+ behaviour is fully available.
+
+ @param categories: list of categories to get all packages for
+ eg. ['app-portage', 'sys-apps',...]
+ @rtype: list of cat/pkg's ['foo/bar', 'foo/baz']
+ """
+ try:
+ cps = portdb.cp_all(categories)
+ # NOTE: the following backup code should be removed
+ # when all available versions of portage have the
+ # categories parameter in cp_all()
+ except: # new behaviour not available
+ #~ message = "Exception: eclean.exclude.cp_all() " +\
+ #~ "new portdb.cp_all() behavior not found. using fallback code"
+ #~ print( warn(message), file=sys.stderr)
+ cps = []
+ # XXX: i smell an access to something which is really out of API...
+ _pkg_dir_name_re = re.compile(r'^\w[-+\w]*$')
+ for tree in portdb.porttrees:
+ for cat in categories:
+ for pkg in listdir(os.path.join(tree,cat),
+ EmptyOnError=1, ignorecvs=1, dirsonly=1):
+ if not _pkg_dir_name_re.match(pkg) or pkg == "CVS":
+ continue
+ cps.append(cat+'/'+pkg)
+ #print( "cp_all: new cps list=", cps)
+ return cps
+
+def exclDictExpand(exclude):
+ """Returns a dictionary of all CP/CPV from porttree which match
+ the exclusion dictionary.
+ """
+ d = {}
+ if 'categories' in exclude:
+ # replace the following cp_all call with
+ # portage.portdb.cp_all([cat1, cat2])
+ # when it is available in all portage versions.
+ cps = cp_all(exclude['categories'])
+ for cp in cps:
+ d[cp] = None
+ if 'packages' in exclude:
+ for cp in exclude['packages']:
+ d[cp] = None
+ if 'anti-packages' in exclude:
+ for cp in exclude['anti-packages']:
+ if cp in d:
+ del d[cp]
+ return d
+
+def exclDictMatchCP(exclude,pkg):
+ """Checks whether a CP matches the exclusion rules."""
+ if 'anti-packages' in exclude and pkg in exclude['anti-packages']:
+ return False
+ if 'packages' in exclude and pkg in exclude['packages']:
+ return True
+ cat = pkg.split('/')[0]
+ if 'categories' in exclude and cat in exclude['categories']:
+ return True
+ return False
+
+def exclDictExpandPkgname(exclude):
+ """Returns a set of all pkgnames from porttree which match
+ the exclusion dictionary.
+ """
+ p = set()
+ if 'categories' in exclude:
+ # replace the following cp_all call with
+ # portage.portdb.cp_all([cat1, cat2])
+ # when it is available in all portage versions.
+ cps = cp_all(exclude['categories'])
+ for cp in cps:
+ pkgname = cp.split('/')[1]
+ p.add(pkgname)
+ if 'packages' in exclude:
+ for cp in exclude['packages']:
+ pkgname = cp.split('/')[1]
+ p.add(pkgname)
+ if 'anti-packages' in exclude:
+ for cp in exclude['anti-packages']:
+ if cp in p:
+ p.remove(cp)
+ return p
+
+
+def exclMatchFilename(exclude_names, filename):
+ """Attempts to split the package name out of a filename
+ and then checks if it matches any exclusion rules.
+
+ This is intended to be run on the cleaning list after all
+ normal checks and removal of protected files. This will reduce
+ the number of files to perform this last minute check on
+
+ @param exclude_names: a set of pkgnames to exlcude
+ @param filename:
+
+ @rtype: bool
+ """
+ found = False
+ index = 0
+ while not found and index < len(FILENAME_RE):
+ found = FILENAME_RE[index].match(filename)
+ index += 1
+ if not found:
+ dprint( "exclude", "exclMatchFilename: filename: " +\
+ "%s, Could not determine package name" %filename)
+ return False
+ pkgname = found.group('pkgname')
+ dprint("exclude", "exclMatchFilename: found pkgname = " +
+ "%s, %s, %d, %s" %(pkgname, str(pkgname in exclude_names),
+ index-1, filename))
+ return (pkgname in exclude_names)
+
diff --git a/pym/gentoolkit/eclean/output.py b/pym/gentoolkit/eclean/output.py
new file mode 100644
index 0000000..c8c3f78
--- /dev/null
+++ b/pym/gentoolkit/eclean/output.py
@@ -0,0 +1,187 @@
+#!/usr/bin/python
+
+# Copyright 2003-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+
+from __future__ import print_function
+
+
+import sys
+import portage
+from portage.output import *
+from gentoolkit.pprinter import cpv, number, emph
+
+
+class OutputControl(object):
+ """Outputs data according to predetermined options and handles any user
+ interaction.
+
+ @param options: dictionary of boolean options as determined in cli.py
+ used here: interactive, pretend, quiet, accept_all, nocolor.
+ """
+
+ def __init__(self, options):
+ if not options:
+ # set some defaults
+ self.options['interactive'] = False
+ self.options['pretend'] = True
+ self.options['quiet'] = False
+ self.options['accept_all'] = False
+ self.options['nocolor'] = False
+ else:
+ self.options = options
+ self.set_colors("normal")
+
+ def set_colors(self, mode):
+ """Sets the colors for the progress_controller
+ and prettysize output
+
+ @param mode: string, 1 of ["normal", "deprecated"]
+ """
+ if mode == "normal":
+ self.pkg_color = cpv # green
+ self.numbers = number # turquoise
+ self.brace = blue
+ elif mode == "deprecated":
+ self.pkg_color = yellow
+ self.numbers = teal # darkgreen
+ self.brace = blue
+
+ def einfo(self, message=""):
+ """Display an info message depending on a color mode.
+
+ @param message: text string to display
+
+ @outputs to stdout.
+ """
+ if not self.options['nocolor']:
+ prefix = " "+green('*')
+ else:
+ prefix = ">>>"
+ print(prefix,message)
+
+ def eprompt(self, message):
+ """Display a user question depending on a color mode.
+
+ @param message: text string to display
+
+ @output to stdout
+ """
+ if not self.options['nocolor']:
+ prefix = " "+red('>')+" "
+ else:
+ prefix = "??? "
+ sys.stdout.write(prefix+message)
+ sys.stdout.flush()
+
+ def prettySize(self, size, justify=False, color=None):
+ """int -> byte/kilo/mega/giga converter. Optionally
+ justify the result. Output is a string.
+
+ @param size: integer
+ @param justify: optional boolean, defaults to False
+ @param color: optional color, defaults to green
+ as defined in portage.output
+
+ @returns a formatted and (escape sequenced)
+ colorized text string
+ """
+ if color == None:
+ color = self.numbers
+ units = [" G"," M"," K"," B"]
+ approx = 0
+ # by using 1000 as the changeover, the integer portion
+ # of the number will never be more than 3 digits long
+ # but the true base 2 value of 1024 is used for the actual
+ # calulation to maintain better accuracy.
+ while len(units) and size >= 1000:
+ approx = 1
+ size = size / 1024.0
+ units.pop()
+ sizestr = "%.1f" %(round(size,1)) + units[-1]
+ if justify:
+ sizestr = " " + self.brace("[ ") + \
+ color(sizestr.rjust(8)) + self.brace(" ]")
+ return sizestr
+
+ def yesNoAllPrompt(self, message="Do you want to proceed?"):
+ """Print a prompt until user answer in yes/no/all. Return a
+ boolean for answer, and also may affect the 'accept_all' option.
+
+ @param message: optional different input string from the default
+ message of: "Do you want to proceed?"
+ @outputs to stdout
+ @modifies class var options['accept_all']
+ @rtype: bool
+ """
+ user_string="xxx"
+ while not user_string.lower() in ["","y","n","a","yes","no","all"]:
+ self.eprompt(message+" [Y/n/a]: ")
+ user_string = sys.stdin.readline().rstrip('\n')
+ user_string = user_string.strip()
+ if user_string.lower() in ["a","all"]:
+ self.options['accept_all'] = True
+ answer = user_string.lower() in ["","y","a","yes","all"]
+ return answer
+
+ def progress_controller(self, size, key, clean_list, file_type):
+ """Callback function for doCleanup. It outputs data according to the
+ options configured.
+ Alternatively it handles user interaction for decisions that are
+ required.
+
+ @param size: Integer of the file(s) size
+ @param key: the filename/pkgname currently being processed
+ @param clean_list: list of files being processed.
+ """
+ if not self.options['quiet']:
+ # pretty print mode
+ print(self.prettySize(size,True), self.pkg_color(key))
+ elif self.options['pretend'] or self.options['interactive']:
+ # file list mode
+ for file_ in clean_list:
+ print(file_)
+ if self.options['pretend']:
+ return False
+ elif not self.options['interactive'] \
+ or self.options['accept_all'] \
+ or self.yesNoAllPrompt("Do you want to delete this " + file_type + "?"):
+ return True
+ return False
+
+ def total(self, mode, size, num_files, verb, action):
+ """outputs the formatted totals to stdout
+
+ @param mode: sets color and message. 1 of ['normal', 'deprecated']
+ @param size: total space savings
+ @param num_files: total number of files
+ @param verb: string eg. 1 of ["would be", "has been"]
+ @param action: string eg 1 of ['distfiles', 'packages']
+ """
+ self.set_colors(mode)
+ if mode =="normal":
+ message="Total space from "+red(str(num_files))+" files "+\
+ verb+" freed in the " + action + " directory"
+ print( " ===========")
+ print( self.prettySize(size, True, red), message)
+ elif mode == "deprecated":
+ message = "Total space from "+red(str(num_files))+" package files\n"+\
+ " Re-run the last command with the -D " +\
+ "option to clean them as well"
+ print( " ===========")
+ print( self.prettySize(size, True, red), message)
+
+ def list_pkgs(self, pkgs):
+ """outputs the packages to stdout
+
+ @param pkgs: dict. of {cat/pkg-ver: src_uri,}
+ """
+ indent = ' ' * 12
+ for key in pkgs:
+ if pkgs[key]:
+ saved = ""
+ else:
+ saved = " ...distfile name(s) not known/saved"
+ print( indent,self.pkg_color(key) + saved)
+ print()
diff --git a/pym/gentoolkit/eclean/pkgindex.py b/pym/gentoolkit/eclean/pkgindex.py
new file mode 100644
index 0000000..108e1ef
--- /dev/null
+++ b/pym/gentoolkit/eclean/pkgindex.py
@@ -0,0 +1,91 @@
+#!/usr/bin/python
+
+# Copyright 2003-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+from __future__ import print_function
+
+
+import subprocess
+import os
+import sys
+
+import gentoolkit.pprinter as pp
+from gentoolkit.eprefix import EPREFIX
+
+import portage
+
+
+class PkgIndex(object):
+ """Handle the cleaning of the binpkg Package
+ Index file
+
+ @type output: class
+ @param output: optional output class for printing
+ """
+
+ def __init__(self, controller=None):
+ self.controller = controller
+ # backup command line call
+ self.emaint_cmd = "%s/usr/sbin/emaint --fix binhost" % EPREFIX
+
+
+ def _get_emaint_binhost(self):
+ """Obtain a reference to the binhost module class
+
+ @sets: self.binhost to BinhostHandler class
+ @rtype: boolean
+ """
+ try:
+ self.emaint_control = Modules()
+ self.binhost = self.emaint_control._get_class('binhost')
+ except InvalidModuleName as er:
+ print( pp.error("Error importing emaint binhost module"), file=sys.stderr)
+ print( pp.error("Original error: " + er), file=sys.stderr)
+ except:
+ return False
+ return True
+
+
+ def _load_modules(self):
+ """Import the emaint modules and report the success/fail of them
+ """
+ try:
+ from emaint.module import Modules
+ from emaint.main import TaskHandler
+ except ImportError as e:
+ return False
+ return True
+
+
+ def clean_pkgs_index(self,):
+ """This will clean the binpkgs packages index file"""
+ go = self._load_modules()
+ if go:
+ if self.get_emaint_binhost():
+ self.taskmaster = TaskHandler(show_progress_bar=True)
+ tasks = [self.binhost]
+ self.taskmaster.run_tasks(tasks)
+
+
+ def call_emaint(self):
+ """Run the stand alone emaint script from
+ a subprocess call.
+
+ @rtype: integer
+ @return: the difference in file size
+ """
+ file_ = os.path.join(portage.settings['PKGDIR'], 'Packages')
+ statinfo = os.stat(file_)
+ size1 = statinfo.st_size
+ try:
+ retcode = subprocess.call(self.emaint_cmd, shell=True)
+ if retcode < 0:
+ print( pp.error("Child was terminated by signal" + str(-retcode)), file=sys.stderr)
+ except OSError as e:
+ print( pp.error("Execution failed:" + e), file=sys.stderr)
+ print()
+ statinfo = os.stat(file_)
+ clean_size = size1 - statinfo.st_size
+ self.controller(clean_size, "Packages Index", file_, "Index")
+ return clean_size
diff --git a/pym/gentoolkit/eclean/search.py b/pym/gentoolkit/eclean/search.py
new file mode 100644
index 0000000..e29bbfc
--- /dev/null
+++ b/pym/gentoolkit/eclean/search.py
@@ -0,0 +1,569 @@
+#!/usr/bin/python
+
+# Copyright 2003-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+
+from __future__ import print_function
+
+
+import os
+import re
+import stat
+import sys
+from functools import partial
+
+import portage
+
+import gentoolkit
+import gentoolkit.pprinter as pp
+from gentoolkit.eclean.exclude import (exclDictMatchCP, exclDictExpand,
+ exclDictExpandPkgname, exclMatchFilename)
+
+
+# Misc. shortcuts to some portage stuff:
+port_settings = portage.settings
+pkgdir = port_settings["PKGDIR"]
+
+err = sys.stderr
+deprecated_message=""""Deprecation Warning: Installed package: %s
+ Is no longer in the tree or an installed overlay"""
+DEPRECATED = pp.warn(deprecated_message)
+
+debug_modules = []
+
+
+def dprint(module, message):
+ if module in debug_modules:
+ print(message)
+
+
+def get_distdir():
+ """Returns DISTDIR if sane, else barfs."""
+
+ d = portage.settings["DISTDIR"]
+ if not os.path.isdir(d):
+ e = pp.error("%s does not appear to be a directory.\n" % d)
+ e += pp.error("Please set DISTDIR to a sane value.\n")
+ e += pp.error("(Check your /etc/make.conf and environment).")
+ print( e, file=sys.stderr)
+ exit(1)
+ return d
+
+distdir = get_distdir()
+
+
+class DistfilesSearch(object):
+ """
+
+ @param output: verbose output method or (lambda x: None) to turn off
+ @param vardb: defaults to portage.db[portage.root]["vartree"].dbapi
+ is overridden for testing.
+ @param portdb: defaults to portage.portdb and is overriden for testing.
+"""
+
+ def __init__(self,
+ output,
+ portdb=portage.portdb,
+ vardb=portage.db[portage.root]["vartree"].dbapi,
+ ):
+ self.vardb =vardb
+ self.portdb = portdb
+ self.output = output
+ self.installed_cpvs = None
+
+ def findDistfiles(self,
+ exclude=None,
+ destructive=False,
+ fetch_restricted=False,
+ package_names=False,
+ time_limit=0,
+ size_limit=0,
+ _distdir=distdir,
+ deprecate=False,
+ extra_checks=()
+ ):
+ """Find all obsolete distfiles.
+
+ XXX: what about cvs ebuilds?
+ I should install some to see where it goes...
+
+ @param exclude: an exclusion dict as defined in
+ exclude.parseExcludeFile class.
+ @param destructive: boolean, defaults to False
+ @param fetch_restricted: boolean, defaults to False
+ @param package_names: boolean, defaults to False.
+ @param time_limit: integer time value as returned by parseTime()
+ @param size_limit: integer value of max. file size to keep or 0 to ignore.
+ @param _distdir: path to the distfiles dir being checked, defaults to portage.
+ @param deprecate: bool to control checking the clean dict. files for exclusion
+
+ @rtype: dict
+ @return dict. of package files to clean i.e. {'cat/pkg-ver.tbz2': [filename],}
+ """
+ if exclude is None:
+ exclude = {}
+ clean_me = {}
+ pkgs = {}
+ saved = {}
+ deprecated = {}
+ installed_included = False
+ # create a big CPV->SRC_URI dict of packages
+ # whose distfiles should be kept
+ if (not destructive) or fetch_restricted:
+ self.output("...non-destructive type search")
+ pkgs, _deprecated = self._non_destructive(destructive, fetch_restricted)
+ deprecated.update(_deprecated)
+ installed_included = True
+ if destructive:
+ self.output("...destructive type search: %d packages already found" %len(pkgs))
+ pkgs, _deprecated = self._destructive(package_names,
+ exclude, pkgs, installed_included)
+ deprecated.update(_deprecated)
+ # gather the files to be cleaned
+ self.output("...checking limits for %d ebuild sources"
+ %len(pkgs))
+
+ checks = self._get_default_checks(size_limit, time_limit, exclude)
+ checks.extend(extra_checks)
+ clean_me = self._check_limits(_distdir, checks, clean_me)
+ # remove any protected files from the list
+ self.output("...removing protected sources from %s candidates to clean"
+ %len(clean_me))
+ clean_me = self._remove_protected(pkgs, clean_me)
+ if not deprecate and len(exclude) and len(clean_me):
+ self.output("...checking final for exclusion from " +\
+ "%s remaining candidates to clean" %len(clean_me))
+ clean_me, saved = self._check_excludes(exclude, clean_me)
+ return clean_me, saved, deprecated
+
+
+####################### begin _check_limits code block
+
+ def _get_default_checks(self, size_limit, time_limit, excludes):
+ #checks =[(self._isreg_check_, "is_reg_check")]
+ checks =[self._isreg_check_]
+ if 'filenames' in excludes:
+ #checks.append((partial(self._filenames_check_, excludes), "Filenames_check"))
+ checks.append(partial(self._filenames_check_, excludes))
+ else:
+ self.output(" - skipping exclude filenames check")
+ if size_limit:
+ #checks.append((partial(self._size_check_, size_limit), "size_check"))
+ checks.append(partial(self._size_check_, size_limit))
+ else:
+ self.output(" - skipping size limit check")
+ if time_limit:
+ #print("time_limit = ", time_limit/1000000,"M sec")
+ #checks.append((partial(self._time_check_, time_limit), "time_check"))
+ checks.append(partial(self._time_check_, time_limit))
+ else:
+ self.output(" - skipping time limit check")
+ return checks
+
+
+ def _check_limits(self,
+ _distdir,
+ checks,
+ clean_me=None
+ ):
+ """Checks files if they exceed size and/or time_limits, etc.
+
+ To start with everything is considered dirty and is excluded
+ only if it matches some condition.
+ """
+ if clean_me is None:
+ clean_me = {}
+ for file in os.listdir(_distdir):
+ filepath = os.path.join(_distdir, file)
+ try:
+ file_stat = os.lstat(filepath)
+ except EnvironmentError:
+ continue
+ is_dirty = False
+ #for check, check_name in checks:
+ for check in checks:
+ should_break, is_dirty = check(file_stat, file)
+ if should_break:
+ break
+
+ if is_dirty:
+ #print( "%s Adding file to clean_list:" %check_name, file)
+ clean_me[file]=[filepath]
+ return clean_me
+
+ @staticmethod
+ def _isreg_check_(file_stat, file):
+ """check if file is a regular file."""
+ is_reg_file = stat.S_ISREG(file_stat[stat.ST_MODE])
+ return not is_reg_file, is_reg_file
+
+ @staticmethod
+ def _size_check_(size_limit, file_stat, file):
+ """checks if the file size exceeds the size_limit"""
+ if (file_stat[stat.ST_SIZE] >= size_limit):
+ #print( "size mismatch ", file, file_stat[stat.ST_SIZE])
+ return True, False
+ return False, True
+
+ @staticmethod
+ def _time_check_(time_limit, file_stat, file):
+ """checks if the file exceeds the time_limit
+ (think forward, not back, time keeps increasing)"""
+ if (file_stat[stat.ST_MTIME] >= time_limit):
+ #print( "time match too young ", file, file_stat[stat.ST_MTIME]/1000000,"M sec.")
+ return True, False
+ #print( "time match too old", file, file_stat[stat.ST_MTIME]/1000000,"M sec.")
+ return False, True
+
+ @staticmethod
+ def _filenames_check_(exclude, file_stat, file):
+ """checks if the file matches an exclusion file listing"""
+ # Try to match file name directly
+ if file in exclude['filenames']:
+ return True, False
+ # See if file matches via regular expression matching
+ else:
+ file_match = False
+ for file_entry in exclude['filenames']:
+ if exclude['filenames'][file_entry].match(file):
+ file_match = True
+ break
+ if file_match:
+ #print( "filename match ", file)
+ return True, False
+ return False, True
+
+####################### end _check_limits code block
+
+ @staticmethod
+ def _remove_protected(
+ pkgs,
+ clean_me
+ ):
+ """Remove files owned by some protected packages.
+
+ @returns packages to clean
+ @rtype: dictionary
+ """
+ for cpv in pkgs:
+ uris = pkgs[cpv].split()
+ uris.reverse()
+ while uris:
+ uri = uris.pop()
+ if uris and uris[-1] == "->":
+ operator = uris.pop()
+ file = uris.pop()
+ else:
+ file = os.path.basename(uri)
+ if file in clean_me:
+ del clean_me[file]
+ # no need to waste IO time if there is nothing left to clean
+ if not len(clean_me):
+ return clean_me
+ return clean_me
+
+ def _non_destructive(self,
+ destructive,
+ fetch_restricted,
+ pkgs_ = None,
+ hosts_cpvs=None
+ ):
+ """performs the non-destructive checks
+
+ @param destructive: boolean
+ @param pkgs_: starting dictionary to add to
+ defaults to {}.
+
+ @returns packages and thier SRC_URI's: {cpv: src_uri,}
+ @rtype: dictionary
+ """
+ if pkgs_ is None:
+ pkgs = {}
+ else:
+ pkgs = pkgs_.copy()
+ deprecated = {}
+ # the following code block was split to optimize for speed
+ # list all CPV from portree (yeah, that takes time...)
+ self.output(" - getting complete ebuild list")
+ cpvs = set(self.portdb.cpv_all())
+ installed_cpvs = set(self.vardb.cpv_all())
+ # now add any installed cpv's that are not in the tree or overlays
+ cpvs.update(installed_cpvs)
+ # Add any installed cpvs from hosts on the network, if any
+ if hosts_cpvs:
+ cpvs.update(hosts_cpvs)
+ installed_cpvs.update(hosts_cpvs)
+ if fetch_restricted and destructive:
+ self.output(" - getting source file names " +
+ "for %d installed ebuilds" %len(installed_cpvs))
+ pkgs, _deprecated = self._unrestricted(pkgs, installed_cpvs)
+ deprecated.update(_deprecated)
+ # remove the installed cpvs then check the remaining for fetch restiction
+ cpvs.difference_update(installed_cpvs)
+ self.output(" - getting fetch-restricted source file names " +
+ "for %d remaining ebuilds" %len(cpvs))
+ pkgs, _deprecated = self._fetch_restricted(pkgs, cpvs)
+ deprecated.update(_deprecated)
+ # save the installed cpv list to re-use in _destructive()
+ self.installed_cpvs = installed_cpvs.copy()
+ else:
+ self.output(" - getting source file names " +
+ "for %d ebuilds" %len(cpvs))
+ pkgs, _deprecated = self._unrestricted(pkgs, cpvs)
+ deprecated.update(_deprecated)
+ return pkgs, deprecated
+
+ def _fetch_restricted(self, pkgs_, cpvs):
+ """perform fetch restricted non-destructive source
+ filename lookups
+
+ @param pkgs_: starting dictionary to add to
+ @param cpvs: set of (cat/pkg-ver, ...) identifiers
+
+ @return a new pkg dictionary
+ @rtype: dictionary
+ """
+ if pkgs_ is None:
+ pkgs = {}
+ else:
+ pkgs = pkgs_.copy()
+ deprecated = {}
+ for cpv in cpvs:
+ # get SRC_URI and RESTRICT from aux_get
+ try: # main portdb
+ (src_uri,restrict) = \
+ self.portdb.aux_get(cpv,["SRC_URI","RESTRICT"])
+ # keep fetch-restricted check
+ # inside try so it is bypassed on KeyError
+ if 'fetch' in restrict:
+ pkgs[cpv] = src_uri
+ except KeyError:
+ try: # installed vardb
+ (src_uri,restrict) = \
+ self.vardb.aux_get(cpv,["SRC_URI","RESTRICT"])
+ deprecated[cpv] = src_uri
+ self.output(DEPRECATED %cpv)
+ # keep fetch-restricted check
+ # inside try so it is bypassed on KeyError
+ if 'fetch' in restrict:
+ pkgs[cpv] = src_uri
+ except KeyError:
+ self.output(" - Key Error looking up: " + cpv)
+ return pkgs, deprecated
+
+ def _unrestricted(self, pkgs_, cpvs):
+ """Perform unrestricted source filenames lookups
+
+ @param pkgs_: starting packages dictionary
+ @param cpvs: set of (cat/pkg-ver, ...) identifiers
+
+ @return a new pkg dictionary
+ @rtype: dictionary
+ """
+ if pkgs_ is None:
+ pkgs = {}
+ else:
+ pkgs = pkgs_.copy()
+ deprecated = {}
+ for cpv in cpvs:
+ # get SRC_URI from aux_get
+ try:
+ pkgs[cpv] = self.portdb.aux_get(cpv,["SRC_URI"])[0]
+ except KeyError:
+ try: # installed vardb
+ pkgs[cpv] = self.vardb.aux_get(cpv,["SRC_URI"])[0]
+ deprecated[cpv] = pkgs[cpv]
+ self.output(DEPRECATED %cpv)
+ except KeyError:
+ self.output(" - Key Error looking up: " + cpv)
+ return pkgs, deprecated
+
+ def _destructive(self,
+ package_names,
+ exclude,
+ pkgs_=None,
+ installed_included=False
+ ):
+ """Builds on pkgs according to input options
+
+ @param package_names: boolean
+ @param exclude: an exclusion dict as defined in
+ exclude.parseExcludeFile class.
+ @param pkgs: starting dictionary to add to
+ defaults to {}.
+ @param installed_included: bool. pkgs already
+ has the installed cpv's added.
+
+ @returns pkgs: {cpv: src_uri,}
+ """
+ if pkgs_ is None:
+ pkgs = {}
+ else:
+ pkgs = pkgs_.copy()
+ deprecated = {}
+ pkgset = set()
+ if not installed_included:
+ if not package_names:
+ # list all installed CPV's from vartree
+ #print( "_destructive: getting vardb.cpv_all")
+ if not self.installed_cpvs:
+ pkgset.update(self.vardb.cpv_all())
+ else:
+ pkgset.update(self.installed_cpvs)
+ self.output(" - processing %s installed ebuilds" % len(pkgset))
+ elif package_names:
+ # list all CPV's from portree for CP's in vartree
+ #print( "_destructive: getting vardb.cp_all")
+ cps = self.vardb.cp_all()
+ self.output(" - processing %s installed packages" % len(cps))
+ for package in cps:
+ pkgset.update(self.portdb.cp_list(package))
+ self.output(" - processing excluded")
+ excludes = self._get_excludes(exclude)
+ excludes_length = len(excludes)
+ dprint("excludes", "EXCLUDES LENGTH =%d" %excludes_length)
+ pkgset.update(excludes)
+ pkgs_done = set(list(pkgs))
+ pkgset.difference_update(pkgs_done)
+ self.output(
+ " - (%d of %d total) additional excluded packages to get source filenames for"
+ %(len(pkgset), excludes_length))
+ #self.output(" - processing %d ebuilds for filenames" %len(pkgset))
+ pkgs, _deprecated = self._unrestricted(pkgs, pkgset)
+ deprecated.update(_deprecated)
+ #self.output(" - done...")
+ return pkgs, deprecated
+
+ def _get_excludes(self, exclude):
+ """Expands the exclude dictionary into a set of
+ CPV's
+
+ @param exclude: dictionary of exclusion categories,
+ packages to exclude from the cleaning
+
+ @rtype: set
+ @return set of package cpv's
+ """
+ pkgset = set()
+ for cp in exclDictExpand(exclude):
+ # add packages from the exclude file
+ dprint("excludes", "_GET_EXCLUDES, cp=" + \
+ cp+", "+str(self.portdb.cp_list(cp)))
+ pkgset.update(self.portdb.cp_list(cp))
+ return pkgset
+
+ def _check_excludes(self, exclude, clean_me):
+ """Performs a last minute check on remaining filenames
+ to see if they should be protected. Since if the pkg-version
+ was deprecated it would not have been matched to a
+ source filename and removed.
+
+ @param exclude: an exclusion dictionary
+ @param clean_me: the list of filenames for cleaning
+
+ @rtype: dict of packages to clean
+ """
+ saved = {}
+ pn_excludes = exclDictExpandPkgname(exclude)
+ dprint("excludes", "_check_excludes: made it here ;)")
+ if not pn_excludes:
+ return clean_me, saved
+ dprint("excludes", pn_excludes)
+ for key in list(clean_me):
+ if exclMatchFilename(pn_excludes, key):
+ saved[key] = clean_me[key]
+ del clean_me[key]
+ self.output(" ...Saved excluded package filename: " + key)
+ return clean_me, saved
+
+
+def findPackages(
+ options,
+ exclude=None,
+ destructive=False,
+ time_limit=0,
+ package_names=False,
+ pkgdir=None,
+ port_dbapi=portage.db[portage.root]["porttree"].dbapi,
+ var_dbapi=portage.db[portage.root]["vartree"].dbapi
+ ):
+ """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.
+
+ @param options: dict of options determined at runtime
+ @param exclude: an exclusion dict as defined in
+ exclude.parseExcludeFile class.
+ @param destructive: boolean, defaults to False
+ @param time_limit: integer time value as returned by parseTime()
+ @param package_names: boolean, defaults to False.
+ used only if destructive=True
+ @param pkgdir: path to the binary package dir being checked
+ @param port_dbapi: defaults to portage.db[portage.root]["porttree"].dbapi
+ can be overridden for tests.
+ @param var_dbapi: defaults to portage.db[portage.root]["vartree"].dbapi
+ can be overridden for tests.
+
+ @rtype: dict
+ @return clean_me i.e. {'cat/pkg-ver.tbz2': [filepath],}
+ """
+ if exclude is None:
+ exclude = {}
+ clean_me = {}
+ # create a full package dictionary
+
+ # now do an access test, os.walk does not error for "no read permission"
+ try:
+ test = os.listdir(pkgdir)
+ del test
+ except EnvironmentError as er:
+ print( pp.error("Error accessing PKGDIR." ), file=sys.stderr)
+ print( pp.error("(Check your /etc/make.conf and environment)."), file=sys.stderr)
+ print( pp.error("Error: %s" %str(er)), file=sys.stderr)
+ 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]
+ st = os.lstat(path)
+ if time_limit and (st[stat.ST_MTIME] >= time_limit):
+ # time-limit exclusion
+ continue
+ # dict is cpv->[files] (2 files in general, because of symlink)
+ clean_me[cpv] = [path]
+ #if os.path.islink(path):
+ if stat.S_ISLNK(st[stat.ST_MODE]):
+ clean_me[cpv].append(os.path.realpath(path))
+ # keep only obsolete ones
+ if destructive:
+ dbapi = var_dbapi
+ if package_names:
+ cp_all = dict.fromkeys(dbapi.cp_all())
+ else:
+ cp_all = {}
+ else:
+ dbapi = port_dbapi
+ cp_all = {}
+ for cpv in list(clean_me):
+ if exclDictMatchCP(exclude,portage.cpv_getkey(cpv)):
+ # exclusion because of the exclude file
+ del clean_me[cpv]
+ continue
+ if dbapi.cpv_exists(cpv):
+ # exclusion because pkg still exists (in porttree or vartree)
+ del clean_me[cpv]
+ continue
+ if portage.cpv_getkey(cpv) in cp_all:
+ # exlusion because of --package-names
+ del clean_me[cpv]
+
+ return clean_me
diff --git a/pym/gentoolkit/eprefix.py b/pym/gentoolkit/eprefix.py
new file mode 100644
index 0000000..9a04e4b
--- /dev/null
+++ b/pym/gentoolkit/eprefix.py
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+
+# Copyright 2003-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+
+#from __future__ import print_function
+
+"""Eprefix support module to set the EPREFIX variable
+used in all gentoolkit modules
+
+Example useage: from gentoolkit.eprefix import EPREFIX
+then in code add it to the filepath eg.:
+ exclude_file = "%s/etc/%s/%s.exclude" % (EPREFIX,__productname__ , action)
+
+"""
+
+import os
+
+
+EPREFIX = ''
+
+# the following code is used to set it when
+# non-installed code is being run
+if 'EPREFIX' in os.environ:
+ EPREFIX = os.environ['EPREFIX']
+else:
+ try:
+ import portage.const
+ EPREFIX = portage.BPREFIX
+ except AttributeError:
+ EPREFIX = ''
+
+#print("EPREFIX set to:", EPREFIX)
diff --git a/pym/gentoolkit/equery/__init__.py b/pym/gentoolkit/equery/__init__.py
index c205938..e25ec54 100644
--- a/pym/gentoolkit/equery/__init__.py
+++ b/pym/gentoolkit/equery/__init__.py
@@ -6,8 +6,7 @@
"""Gentoo package query tool"""
-# Move to Imports section after Python 2.6 is stable
-from __future__ import with_statement
+from __future__ import print_function
__all__ = (
'format_options',
@@ -15,27 +14,53 @@ __all__ = (
'mod_usage'
)
__docformat__ = 'epytext'
+# version is dynamically set by distutils sdist
+__version__ = "svn"
# =======
# Imports
# =======
import errno
+import os
import sys
import time
from getopt import getopt, GetoptError
-from portage import exception
+import portage
import gentoolkit
-import gentoolkit.pprinter as pp
-from gentoolkit import settings, Config
+from gentoolkit import CONFIG
+from gentoolkit import errors
+from gentoolkit import pprinter as pp
from gentoolkit.textwrap_ import TextWrapper
__productname__ = "equery"
-__authors__ = """\
-Karl Trygve Kalleberg - Original author
-Douglas Anderson - Modular redesign; author of meta, changes"""
+__authors__ = (
+ 'Karl Trygve Kalleberg - Original author',
+ 'Douglas Anderson - 0.3.0 author'
+)
+
+# =======
+# Globals
+# =======
+
+NAME_MAP = {
+ 'b': 'belongs',
+ 'c': 'changes',
+ 'k': 'check',
+ 'd': 'depends',
+ 'g': 'depgraph',
+ 'f': 'files',
+ 'h': 'hasuse',
+ 'l': 'list_',
+ 'y': 'keywords',
+ 'a': 'has',
+ 'm': 'meta',
+ 's': 'size',
+ 'u': 'uses',
+ 'w': 'which'
+}
# =========
# Functions
@@ -48,60 +73,47 @@ def print_help(with_description=True):
"""
if with_description:
- print __doc__
- print main_usage()
- print
- print pp.globaloption("global options")
- print format_options((
+ print(__doc__)
+ print(main_usage())
+ print()
+ print(pp.globaloption("global options"))
+ print(format_options((
(" -h, --help", "display this help message"),
(" -q, --quiet", "minimal output"),
(" -C, --no-color", "turn off colors"),
(" -N, --no-pipe", "turn off pipe detection"),
(" -V, --version", "display version info")
- ))
- print
- print pp.command("modules") + " (" + pp.command("short name") + ")"
- print format_options((
+ )))
+ print()
+ print(pp.command("modules") + " (" + pp.command("short name") + ")")
+ print(format_options((
(" (b)elongs", "list what package FILES belong to"),
- (" (c)hanges", "list changelog entries for PKG"),
+ (" (c)hanges", "list changelog entries for ATOM"),
(" chec(k)", "verify checksums and timestamps for PKG"),
- (" (d)epends", "list all packages directly depending on PKG"),
+ (" (d)epends", "list all packages directly depending on ATOM"),
(" dep(g)raph", "display a tree of all dependencies for PKG"),
(" (f)iles", "list all files installed by PKG"),
+ (" h(a)s", "list all packages for matching ENVIRONMENT data stored in /var/db/pkg"),
(" (h)asuse", "list all packages that have USE flag"),
+ (" ke(y)words", "display keywords for specified PKG"),
(" (l)ist", "list package matching PKG"),
(" (m)eta", "display metadata about PKG"),
(" (s)ize", "display total size of all files owned by PKG"),
(" (u)ses", "display USE flags for PKG"),
(" (w)hich", "print full path to ebuild for PKG")
- ))
+ )))
def expand_module_name(module_name):
- """Returns one of the values of name_map or raises KeyError"""
-
- name_map = {
- 'b': 'belongs',
- 'c': 'changes',
- 'k': 'check',
- 'd': 'depends',
- 'g': 'depgraph',
- 'f': 'files',
- 'h': 'hasuse',
- 'l': 'list_',
- 'm': 'meta',
- 's': 'size',
- 'u': 'uses',
- 'w': 'which'
- }
+ """Returns one of the values of NAME_MAP or raises KeyError"""
if module_name == 'list':
# list is a Python builtin type, so we must rename our module
return 'list_'
- elif module_name in name_map.values():
+ elif module_name in NAME_MAP.values():
return module_name
else:
- return name_map[module_name]
+ return NAME_MAP[module_name]
def format_options(options):
@@ -114,21 +126,21 @@ def format_options(options):
"""
result = []
- twrap = TextWrapper(width=Config['termWidth'])
+ twrap = TextWrapper(width=CONFIG['termWidth'])
opts = (x[0] for x in options)
descs = (x[1] for x in options)
for opt, desc in zip(opts, descs):
twrap.initial_indent = pp.emph(opt.ljust(25))
twrap.subsequent_indent = " " * 25
result.append(twrap.fill(desc))
-
+
return '\n'.join(result)
def format_filetype(path, fdesc, show_type=False, show_md5=False,
show_timestamp=False):
"""Format a path for printing.
-
+
@type path: str
@param path: the path
@type fdesc: list
@@ -141,7 +153,7 @@ def format_filetype(path, fdesc, show_type=False, show_md5=False,
@type show_md5: bool
@param show_md5: if True, append MD5 sum to the formatted string
@type show_timestamp: bool
- @param show_timestamp: if True, append time-of-creation after pathname
+ @param show_timestamp: if True, append time-of-creation after pathname
@rtype: str
@return: formatted pathname with optional added information
"""
@@ -160,7 +172,7 @@ def format_filetype(path, fdesc, show_type=False, show_md5=False,
ftype = "sym"
stamp = format_timestamp(fdesc[1])
tgt = fdesc[2].split()[0]
- if Config["piping"]:
+ if CONFIG["piping"]:
fpath = path
else:
fpath = pp.path_symlink(path + " -> " + tgt)
@@ -168,7 +180,9 @@ def format_filetype(path, fdesc, show_type=False, show_md5=False,
ftype = "dev"
fpath = path
else:
- pp.print_error("%s has unknown type: %s" % (path, fdesc[0]))
+ sys.stderr.write(
+ pp.error("%s has unknown type: %s" % (path, fdesc[0]))
+ )
result = ""
if show_type:
@@ -198,28 +212,21 @@ def initialize_configuration():
term_width = 80
# Terminal size, minus a 1-char margin for text wrapping
- Config['termWidth'] = term_width - 1
-
- # Color handling: -1: Use Portage settings, 0: Force off, 1: Force on
- Config['color'] = -1
-
- Config['quiet'] = False
+ CONFIG['termWidth'] = term_width - 1
# Guess color output
- if (Config['color'] == -1 and (not sys.stdout.isatty() or
- settings["NOCOLOR"] in ("yes", "true")) or
- Config['color'] == 0):
+ if (CONFIG['color'] == -1 and (not sys.stdout.isatty() or
+ os.getenv("NOCOLOR") in ("yes", "true")) or CONFIG['color'] == 0):
pp.output.nocolor()
- # Guess piping output
- if not sys.stdout.isatty():
- Config["piping"] = True
- else:
- Config["piping"] = False
+ if CONFIG['piping']:
+ CONFIG['verbose'] = False
+
+ CONFIG['debug'] = bool(os.getenv('DEBUG', False))
def main_usage():
- """Print the main usage message for equery"""
+ """Return the main usage message for equery"""
return "%(usage)s %(product)s [%(g_opts)s] %(mod_name)s [%(mod_opts)s]" % {
'usage': pp.emph("Usage:"),
@@ -231,8 +238,8 @@ def main_usage():
def mod_usage(mod_name="module", arg="pkgspec", optional=False):
- """Provide a consistant usage message to the calling module.
-
+ """Provide a consistent usage message to the calling module.
+
@type arg: string
@param arg: what kind of argument the module takes (pkgspec, filename, etc)
@type optional: bool
@@ -262,88 +269,89 @@ def parse_global_options(global_opts, args):
print_help()
sys.exit(0)
elif opt in ('-q','--quiet'):
- Config["quiet"] = True
+ CONFIG['quiet'] = True
elif opt in ('-C', '--no-color', '--nocolor'):
- Config['color'] = 0
+ CONFIG['color'] = 0
pp.output.nocolor()
elif opt in ('-N', '--no-pipe'):
- Config["piping"] = False
+ CONFIG['piping'] = False
elif opt in ('-V', '--version'):
print_version()
sys.exit(0)
-
+ elif opt in ('--debug'):
+ CONFIG['debug'] = True
+
return need_help
-
+
def print_version():
"""Print the version of this tool to the console."""
- try:
- with open('/usr/share/gentoolkit/VERSION') as gentoolkit_version:
- version = gentoolkit_version.read().strip()
- except IOError, err:
- pp.die(2, str(err))
-
- print "%(product)s (%(version)s) - %(docstring)s" % {
+ print("%(product)s (%(version)s) - %(docstring)s" % {
"product": pp.productname(__productname__),
- "version": version,
+ "version": __version__,
"docstring": __doc__
- }
+ })
def split_arguments(args):
"""Separate module name from module arguments"""
-
+
return args.pop(0), args
def main():
"""Parse input and run the program."""
- initialize_configuration()
-
short_opts = "hqCNV"
- long_opts = ('help', 'quiet', 'nocolor', 'no-color', 'no-pipe', 'version')
+ long_opts = (
+ 'help', 'quiet', 'nocolor', 'no-color', 'no-pipe', 'version', 'debug'
+ )
+
+ initialize_configuration()
try:
global_opts, args = getopt(sys.argv[1:], short_opts, long_opts)
- except GetoptError, err:
- pp.print_error("Global %s" % err)
+ except GetoptError as err:
+ sys.stderr.write(pp.error("Global %s" % err))
print_help(with_description=False)
sys.exit(2)
# Parse global options
need_help = parse_global_options(global_opts, args)
+ # verbose is shorthand for the very common 'not quiet or piping'
+ if CONFIG['quiet'] or CONFIG['piping']:
+ CONFIG['verbose'] = False
+ else:
+ CONFIG['verbose'] = True
+
try:
module_name, module_args = split_arguments(args)
except IndexError:
print_help()
sys.exit(2)
-
+
if need_help:
module_args.append('--help')
- if Config['piping'] or Config['quiet']:
- Config['verbose'] = False
- else:
- Config['verbose'] = True
-
try:
expanded_module_name = expand_module_name(module_name)
except KeyError:
- pp.print_error("Unknown module '%s'" % module_name)
+ sys.stderr.write(pp.error("Unknown module '%s'" % module_name))
print_help(with_description=False)
sys.exit(2)
try:
- loaded_module = __import__(expanded_module_name, globals(),
- locals(), [], -1)
+ loaded_module = __import__(
+ expanded_module_name, globals(), locals(), [], -1
+ )
loaded_module.main(module_args)
- except exception.AmbiguousPackageName, err:
- pp.print_error("Ambiguous package name. Use one of: ")
- while err[0]:
- print " " + err[0].pop()
- except IOError, err:
+ except portage.exception.AmbiguousPackageName as err:
+ raise errors.GentoolkitAmbiguousPackage(err.args[0])
+ except IOError as err:
if err.errno != errno.EPIPE:
raise
+
+if __name__ == '__main__':
+ main()
diff --git a/pym/gentoolkit/equery/belongs.py b/pym/gentoolkit/equery/belongs.py
index d4da36f..7c20658 100644
--- a/pym/gentoolkit/equery/belongs.py
+++ b/pym/gentoolkit/equery/belongs.py
@@ -10,117 +10,126 @@ Note: Normally, only one package will own a file. If multiple packages own
the same file, it usually constitutes a problem, and should be reported.
"""
+from __future__ import print_function
+
__docformat__ = 'epytext'
# =======
# Imports
# =======
-import re
import sys
from getopt import gnu_getopt, GetoptError
import gentoolkit.pprinter as pp
-from gentoolkit.equery import format_filetype, format_options, mod_usage, \
- Config
-from gentoolkit.helpers2 import get_installed_cpvs
-from gentoolkit.package import Package
+from gentoolkit.equery import (format_filetype, format_options, mod_usage,
+ CONFIG)
+from gentoolkit.helpers import FileOwner
# =======
# Globals
# =======
QUERY_OPTS = {
- "fullRegex": False,
- "earlyOut": False,
- "nameOnly": False
+ "full_regex": False,
+ "early_out": False,
+ "name_only": False
}
+# =======
+# Classes
+# =======
+
+class BelongsPrinter(object):
+ """Outputs a formatted list of packages that claim to own a files."""
+
+ def __init__(self, verbose=True, name_only=False):
+ if verbose:
+ self.print_fn = self.print_verbose
+ else:
+ self.print_fn = self.print_quiet
+
+ self.name_only = name_only
+
+ def __call__(self, pkg, cfile):
+ self.print_fn(pkg, cfile)
+
+ # W0613: *Unused argument %r*
+ # pylint: disable-msg=W0613
+ def print_quiet(self, pkg, cfile):
+ "Format for minimal output."
+ if self.name_only:
+ name = pkg.cp
+ else:
+ name = str(pkg.cpv)
+ pp.uprint(name)
+
+ def print_verbose(self, pkg, cfile):
+ "Format for full output."
+ file_str = pp.path(format_filetype(cfile, pkg.parsed_contents()[cfile]))
+ if self.name_only:
+ name = pkg.cp
+ else:
+ name = str(pkg.cpv)
+ pp.uprint(pp.cpv(name), "(" + file_str + ")")
+
# =========
# Functions
# =========
def parse_module_options(module_opts):
- """Parse module options and update GLOBAL_OPTS"""
+ """Parse module options and update QUERY_OPTS"""
opts = (x[0] for x in module_opts)
for opt in opts:
if opt in ('-h','--help'):
print_help()
sys.exit(0)
- elif opt in ('-c', '--category'):
- # Remove this warning after a reasonable amount of time
- # (djanderson, 2/2009)
- pp.print_warn("Module option -c, --category not implemented")
- print
elif opt in ('-e', '--early-out', '--earlyout'):
if opt == '--earlyout':
- pp.print_warn("Use of --earlyout is deprecated.")
- pp.print_warn("Please use --early-out.")
- print
- QUERY_OPTS['earlyOut'] = True
+ sys.stderr.write(pp.warn("Use of --earlyout is deprecated."))
+ sys.stderr.write(pp.warn("Please use --early-out."))
+ print()
+ QUERY_OPTS['early_out'] = True
elif opt in ('-f', '--full-regex'):
- QUERY_OPTS['fullRegex'] = True
+ QUERY_OPTS['full_regex'] = True
elif opt in ('-n', '--name-only'):
- QUERY_OPTS['nameOnly'] = True
-
-
-def prepare_search_regex(queries):
- """Create a regex out of the queries"""
-
- if QUERY_OPTS["fullRegex"]:
- result = queries
- else:
- result = []
- # Trim trailing and multiple slashes from queries
- slashes = re.compile('/+')
- for query in queries:
- query = slashes.sub('/', query).rstrip('/')
- if query.startswith('/'):
- query = "^%s$" % re.escape(query)
- else:
- query = "/%s$" % re.escape(query)
- result.append(query)
-
- result = "|".join(result)
-
- return re.compile(result)
+ QUERY_OPTS['name_only'] = True
def print_help(with_description=True):
"""Print description, usage and a detailed help message.
-
+
@type with_description: bool
@param with_description: if true, print module's __doc__ string
"""
if with_description:
- print __doc__.strip()
- print
- print mod_usage(mod_name="belongs", arg="filename")
- print
- print pp.command("options")
- print format_options((
+ print(__doc__.strip())
+ print()
+ print(mod_usage(mod_name="belongs", arg="filename"))
+ print()
+ print(pp.command("options"))
+ print(format_options((
(" -h, --help", "display this help message"),
(" -f, --full-regex", "supplied query is a regex" ),
(" -e, --early-out", "stop when first match is found"),
(" -n, --name-only", "don't print the version")
- ))
+ )))
def main(input_args):
"""Parse input and run the program"""
- # -c, --category is not implemented
- short_opts = "hc:fen"
- long_opts = ('help', 'category=', 'full-regex', 'early-out', 'earlyout',
+ short_opts = "h:fen"
+ long_opts = ('help', 'full-regex', 'early-out', 'earlyout',
'name-only')
try:
module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
- except GetoptError, err:
- pp.print_error("Module %s" % err)
- print
+ except GetoptError as err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print()
print_help(with_description=False)
sys.exit(2)
@@ -130,31 +139,22 @@ def main(input_args):
print_help()
sys.exit(2)
- query_re = prepare_search_regex(queries)
-
- if Config['verbose']:
- pp.print_info(3, " * Searching for %s ... "
- % (pp.regexpquery(",".join(queries))))
-
- matches = get_installed_cpvs()
-
- # Print matches to screen or pipe
- found_match = False
- for pkg in [Package(x) for x in matches]:
- files = pkg.get_contents()
- for cfile in files:
- if query_re.search(cfile):
- if QUERY_OPTS["nameOnly"]:
- pkg_str = pkg.key
- else:
- pkg_str = pkg.cpv
- if Config['verbose']:
- file_str = pp.path(format_filetype(cfile, files[cfile]))
- print "%s (%s)" % (pkg_str, file_str)
- else:
- print pkg_str
-
- found_match = True
-
- if found_match and QUERY_OPTS["earlyOut"]:
- break
+ if CONFIG['verbose']:
+ pp.uprint(" * Searching for %s ... " % (
+ pp.regexpquery(",".join(queries)))
+ )
+
+ printer_fn = BelongsPrinter(
+ verbose=CONFIG['verbose'], name_only=QUERY_OPTS['name_only']
+ )
+
+ find_owner = FileOwner(
+ is_regex=QUERY_OPTS['full_regex'],
+ early_out=QUERY_OPTS['early_out'],
+ printer_fn=printer_fn
+ )
+
+ if not find_owner(queries):
+ sys.exit(1)
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/equery/changes.py b/pym/gentoolkit/equery/changes.py
index 604bb45..1e9be32 100644
--- a/pym/gentoolkit/equery/changes.py
+++ b/pym/gentoolkit/equery/changes.py
@@ -4,40 +4,34 @@
#
# $Header: $
-"""Display the Gentoo ChangeLog entry for the latest installable version of a
-given package
-"""
+"""Displays the ChangeLog entry for the latest installable version of an atom"""
-# Move to Imports sections when Python 2.6 is stable
-from __future__ import with_statement
+from __future__ import print_function
-__author__ = 'Douglas Anderson'
__docformat__ = 'epytext'
# =======
# Imports
# =======
-import os
import sys
+import os
from getopt import gnu_getopt, GetoptError
-from portage.versions import pkgsplit
-
import gentoolkit.pprinter as pp
from gentoolkit import errors
+from gentoolkit.atom import Atom
from gentoolkit.equery import format_options, mod_usage
-from gentoolkit.helpers2 import find_best_match, find_packages
-from gentoolkit.package import Package
-from gentoolkit.versionmatch import VersionMatch
+from gentoolkit.helpers import ChangeLog
+from gentoolkit.query import Query
# =======
# Globals
# =======
QUERY_OPTS = {
- 'onlyLatest': False,
- 'showFullLog': False,
+ 'only_latest': False,
+ 'show_full_log': False,
'limit': None,
'from': None,
'to': None
@@ -49,25 +43,25 @@ QUERY_OPTS = {
def print_help(with_description=True):
"""Print description, usage and a detailed help message.
-
+
@type with_description: bool
@param with_description: if true, print module's __doc__ string
"""
if with_description:
- print __doc__.strip()
- print
- print mod_usage(mod_name="changes")
- print
- print pp.emph("examples")
+ print(__doc__.strip())
+ print()
+ print(mod_usage(mod_name="changes"))
+ print()
+ print(pp.emph("examples"))
print (" c portage # show latest visible "
"version's entry")
- print " c portage --full --limit=3 # show 3 latest entries"
- print " c '=sys-apps/portage-2.1.6*' # use atom syntax"
- print " c portage --from=2.2_rc20 --to=2.2_rc30 # use version ranges"
- print
- print pp.command("options")
- print format_options((
+ print(" c portage --full --limit=3 # show 3 latest entries")
+ print(" c '=sys-apps/portage-2.1.6*' # use atom syntax")
+ print(" c portage --from=2.2_rc60 --to=2.2_rc70 # use version ranges")
+ print()
+ print(pp.command("options"))
+ print(format_options((
(" -h, --help", "display this help message"),
(" -l, --latest", "display only the latest ChangeLog entry"),
(" -f, --full", "display the full ChangeLog"),
@@ -75,82 +69,11 @@ def print_help(with_description=True):
"limit the number of entries displayed (with --full)"),
(" --from=VER", "set which version to display from"),
(" --to=VER", "set which version to display to"),
- ))
-
-
-def get_logpath(pkg):
- """Test that the package's ChangeLog path is valid and readable, else
- die.
-
- @type pkg: gentoolkit.package.Package
- @param pkg: package to find logpath for
- @rtype: str
- @return: a path to a readable ChangeLog
- """
-
- logpath = os.path.join(pkg.get_package_path(), 'ChangeLog')
- if not os.path.isfile(logpath) or not os.access(logpath, os.R_OK):
- raise errors.GentoolkitFatalError("%s does not exist or is unreadable"
- % pp.path(logpath))
-
- return logpath
-
-
-def get_match(query):
- """Find a valid package to get the ChangeLog path from or raise
- GentoolkitNoMatches.
- """
-
- match = matches = None
- match = find_best_match(query)
-
- if not match:
- matches = find_packages(query, include_masked=True)
- else:
- matches = [match]
-
- if not matches:
- pp.print_warn("Try using an unversioned query with "
- "--from and --to.")
- raise errors.GentoolkitNoMatches(query)
-
- return matches[0]
-
-
-def index_changelog(entries):
- """Convert the list from split_changelog into a dict with VersionMatch
- instance as the index.
-
- @todo: UPDATE THIS
- @type entries: list
- @param entries: output of split_changelog
- @rtype: dict
- @return: dict with gentoolkit.package.Package instances as keys and the
- corresponding ChangeLog entree as its value
- """
-
- result = []
- for entry in entries:
- # Extract the package name from the entry, ex:
- # *xterm-242 (07 Mar 2009) => xterm-242
- pkg_name = entry.split(' ', 1)[0].lstrip('*')
- if not pkg_name.strip():
- continue
- pkg_split = pkgsplit(pkg_name)
- result.append(
- (VersionMatch(op="=", ver=pkg_split[1], rev=pkg_split[2]), entry))
-
- return result
-
-
-def is_ranged(atom):
- """Return True if an atom string appears to be ranged, else False."""
-
- return atom.startswith(('~', '<', '>')) or atom.endswith('*')
+ )))
def parse_module_options(module_opts):
- """Parse module options and update GLOBAL_OPTS"""
+ """Parse module options and update QUERY_OPTS"""
opts = (x[0] for x in module_opts)
posargs = (x[1] for x in module_opts)
@@ -159,132 +82,44 @@ def parse_module_options(module_opts):
print_help()
sys.exit(0)
elif opt in ('-f', '--full'):
- QUERY_OPTS['showFullLog'] = True
+ QUERY_OPTS['show_full_log'] = True
elif opt in ('-l', '--latest'):
- QUERY_OPTS['onlyLatest'] = True
+ QUERY_OPTS['only_latest'] = True
elif opt in ('--limit',):
set_limit(posarg)
elif opt in ('--from',):
- set_from(posarg)
+ QUERY_OPTS['from'] = posarg
elif opt in ('--to',):
- set_to(posarg)
-
-
-def print_matching_entries(indexed_entries, pkg, first_run):
- """Print only the entries which interect with the pkg version."""
-
- from_restriction = QUERY_OPTS['from']
- to_restriction = QUERY_OPTS['to']
-
- for entry_set in indexed_entries:
- i, entry = entry_set
- # a little hackery, since versionmatch doesn't store the
- # package key, but intersects checks that it matches.
- i.key = pkg.key
- if from_restriction or to_restriction:
- if from_restriction and not from_restriction.match(i):
- continue
- if to_restriction and not to_restriction.match(i):
- continue
- elif not pkg.intersects(i):
- continue
+ QUERY_OPTS['to'] = posarg
- if not first_run:
- print "\n"
- print entry.strip()
- first_run = False
-
- return first_run
-
-
-def set_from(posarg):
- """Set a starting version to filter the ChangeLog with or die if posarg
- is not a valid version.
- """
- pkg_split = pkgsplit('null-%s' % posarg)
+def print_entries(entries):
+ """Print entries and strip trailing whitespace from the last entry."""
- if pkg_split and not is_ranged(posarg):
- ver_match = VersionMatch(
- op=">=",
- ver=pkg_split[1],
- rev=pkg_split[2] if pkg_split[2] != 'r0' else '')
- QUERY_OPTS['from'] = ver_match
- else:
- err = "Module option --from requires valid unranged version (got '%s')"
- pp.print_error(err % posarg)
- print
- print_help(with_description=False)
- sys.exit(2)
+ len_entries = len(entries)
+ for i, entry in enumerate(entries, start=1):
+ if i < len_entries:
+ pp.uprint(entry)
+ else:
+ pp.uprint(entry.strip())
def set_limit(posarg):
- """Set a limit in QUERY_OPTS on how many ChangeLog entries to display or
- die if posarg is not an integer.
+ """Set a limit in QUERY_OPTS on how many ChangeLog entries to display.
+
+ Die if posarg is not an integer.
"""
if posarg.isdigit():
QUERY_OPTS['limit'] = int(posarg)
else:
err = "Module option --limit requires integer (got '%s')"
- pp.print_error(err % posarg)
- print
+ sys.stderr.write(pp.error(err % posarg))
+ print()
print_help(with_description=False)
sys.exit(2)
-def set_to(posarg):
- """Set an ending version to filter the ChangeLog with or die if posarg
- is not a valid version.
- """
-
- pkg_split = pkgsplit('null-%s' % posarg)
- if pkg_split and not is_ranged(posarg):
- ver_match = VersionMatch(
- op="<=",
- ver=pkg_split[1],
- rev=pkg_split[2] if pkg_split[2] != 'r0' else '')
- QUERY_OPTS['to'] = ver_match
- else:
- err = "Module option --to requires valid unranged version (got '%s')"
- pp.print_error(err % posarg)
- print
- print_help(with_description=False)
- sys.exit(2)
-
-
-def split_changelog(logpath):
- """Split the changelog up into individual entries.
-
- @type logpath: str
- @param logpath: valid path to ChangeLog file
- @rtype: list
- @return: individual ChangeLog entrees
- """
-
- result = []
- partial_entries = []
- with open(logpath) as log:
- for line in log:
- if line.startswith('#'):
- continue
- elif line.startswith('*'):
- # Append last entry to result...
- entry = ''.join(partial_entries)
- if entry and not entry.isspace():
- result.append(entry)
- # ... and start a new entry
- partial_entries = [line]
- else:
- partial_entries.append(line)
- else:
- # Append the final entry
- entry = ''.join(partial_entries)
- result.append(entry)
-
- return result
-
-
def main(input_args):
"""Parse input and run the program"""
@@ -293,9 +128,9 @@ def main(input_args):
try:
module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
- except GetoptError, err:
- pp.print_error("Module %s" % err)
- print
+ except GetoptError as err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print()
print_help(with_description=False)
sys.exit(2)
@@ -306,40 +141,49 @@ def main(input_args):
sys.exit(2)
first_run = True
- for query in queries:
+ got_match = False
+ for query in (Query(x) for x in queries):
if not first_run:
- print
+ print()
- ranged_query = None
- if is_ranged(query):
- # Raises GentoolkitInvalidCPV here if invalid
- ranged_query = Package(query)
+ match = query.find_best()
+ if match is None:
+ continue
- pkg = get_match(query)
- logpath = get_logpath(pkg)
- log_entries = split_changelog(logpath)
- if not any(log_entries):
- raise errors.GentoolkitFatalError(
- "%s exists but doesn't contain entries." % pp.path(logpath))
- indexed_entries = index_changelog(log_entries)
+ got_match = True
+ changelog_path = os.path.join(match.package_path(), 'ChangeLog')
+ changelog = ChangeLog(changelog_path)
#
# Output
#
- if QUERY_OPTS['onlyLatest']:
- print log_entries[0].strip()
- elif QUERY_OPTS['showFullLog']:
- end = QUERY_OPTS['limit'] or len(log_entries)
- for entry in log_entries[:end]:
- print entry
- first_run = False
- elif log_entries and not indexed_entries:
- # We can't match anything, so just print latest:
- print log_entries[0].strip()
+ if (QUERY_OPTS['only_latest'] or (
+ changelog.entries and not changelog.indexed_entries
+ )):
+ pp.uprint(changelog.latest.strip())
else:
- if ranged_query:
- pkg = ranged_query
- first_run = print_matching_entries(indexed_entries, pkg, first_run)
+ end = QUERY_OPTS['limit'] or len(changelog.indexed_entries)
+ if QUERY_OPTS['to'] or QUERY_OPTS['from']:
+ print_entries(
+ changelog.entries_matching_range(
+ from_ver=QUERY_OPTS['from'],
+ to_ver=QUERY_OPTS['to']
+ )[:end]
+ )
+ elif QUERY_OPTS['show_full_log']:
+ print_entries(changelog.full[:end])
+ else:
+ # Raises GentoolkitInvalidAtom here if invalid
+ if query.is_ranged():
+ atom = Atom(str(query))
+ else:
+ atom = '=' + str(match.cpv)
+ print_entries(changelog.entries_matching_atom(atom)[:end])
first_run = False
+
+ if not got_match:
+ sys.exit(1)
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/equery/check.py b/pym/gentoolkit/equery/check.py
index 2531970..84be634 100644
--- a/pym/gentoolkit/equery/check.py
+++ b/pym/gentoolkit/equery/check.py
@@ -4,7 +4,9 @@
#
# $Header: $
-"""Check timestamps and MD5sums for files owned by a given installed package"""
+"""Checks timestamps and MD5 sums for files owned by a given installed package"""
+
+from __future__ import print_function
__docformat__ = 'epytext'
@@ -14,219 +16,275 @@ __docformat__ = 'epytext'
import os
import sys
+from functools import partial
from getopt import gnu_getopt, GetoptError
-try:
- import portage.checksum as checksum
-except ImportError:
- import portage_checksum as checksum
+import portage.checksum as checksum
import gentoolkit.pprinter as pp
-from gentoolkit.equery import format_options, mod_usage, Config
-from gentoolkit.helpers2 import do_lookup
+from gentoolkit import errors
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.query import Query
# =======
# Globals
# =======
QUERY_OPTS = {
- "categoryFilter": None,
- "includeInstalled": False,
- "includeOverlayTree": False,
- "includePortTree": False,
- "checkMD5sum": True,
- "checkTimestamp" : True,
- "isRegex": False,
- "matchExact": True,
- "printMatchInfo": False,
- "showSummary" : True,
- "showPassedFiles" : False,
- "showFailedFiles" : True
+ "in_installed": True,
+ "in_porttree": False,
+ "in_overlay": False,
+ "check_MD5sum": True,
+ "check_timestamp" : True,
+ "is_regex": False,
+ "only_failures": False,
+ "show_progress": False,
}
+# =======
+# Classes
+# =======
+
+class VerifyContents(object):
+ """Verify installed packages' CONTENTS files.
+
+ The CONTENTS file contains timestamps and MD5 sums for each file owned
+ by a package.
+ """
+ def __init__(self, printer_fn=None):
+ """Create a VerifyObjects instance.
+
+ @type printer_fn: callable
+ @param printer_fn: if defined, will be applied to each result as found
+ """
+ self.check_sums = True
+ self.check_timestamps = True
+ self.printer_fn = printer_fn
+
+ self.is_regex = False
+
+ def __call__(
+ self,
+ pkgs,
+ is_regex=False,
+ check_sums=True,
+ check_timestamps=True
+ ):
+ self.is_regex = is_regex
+ self.check_sums = check_sums
+ self.check_timestamps = check_timestamps
+
+ result = {}
+ for pkg in pkgs:
+ # _run_checks returns tuple(n_passed, n_checked, err)
+ check_results = self._run_checks(pkg.parsed_contents())
+ result[pkg.cpv] = check_results
+ if self.printer_fn is not None:
+ self.printer_fn(pkg.cpv, check_results)
+
+ return result
+
+ def _run_checks(self, files):
+ """Run some basic sanity checks on a package's contents.
+
+ If the file type (ftype) is not a directory or symlink, optionally
+ verify MD5 sums or mtimes via L{self._verify_obj}.
+
+ @see: gentoolkit.packages.get_contents()
+ @type files: dict
+ @param files: in form {'PATH': ['TYPE', 'TIMESTAMP', 'MD5SUM']}
+ @rtype: tuple
+ @return:
+ n_passed (int): number of files that passed all checks
+ n_checked (int): number of files checked
+ errs (list): check errors' descriptions
+ """
+ n_checked = 0
+ n_passed = 0
+ errs = []
+ for cfile in files:
+ n_checked += 1
+ ftype = files[cfile][0]
+ if not os.path.exists(cfile):
+ errs.append("%s does not exist" % cfile)
+ continue
+ elif ftype == "dir":
+ if not os.path.isdir(cfile):
+ err = "%(cfile)s exists, but is not a directory"
+ errs.append(err % locals())
+ continue
+ elif ftype == "obj":
+ obj_errs = self._verify_obj(files, cfile, errs)
+ if len(obj_errs) > len(errs):
+ errs = obj_errs[:]
+ continue
+ elif ftype == "sym":
+ target = files[cfile][2].strip()
+ if not os.path.islink(cfile):
+ err = "%(cfile)s exists, but is not a symlink"
+ errs.append(err % locals())
+ continue
+ tgt = os.readlink(cfile)
+ if tgt != target:
+ err = "%(cfile)s does not point to %(target)s"
+ errs.append(err % locals())
+ continue
+ else:
+ err = "%(cfile)s has unknown type %(ftype)s"
+ errs.append(err % locals())
+ continue
+ n_passed += 1
+
+ return n_passed, n_checked, errs
+
+ def _verify_obj(self, files, cfile, errs):
+ """Verify the MD5 sum and/or mtime and return any errors."""
+
+ obj_errs = errs[:]
+ if self.check_sums:
+ md5sum = files[cfile][2]
+ try:
+ cur_checksum = checksum.perform_md5(cfile, calc_prelink=1)
+ except IOError:
+ err = "Insufficient permissions to read %(cfile)s"
+ obj_errs.append(err % locals())
+ return obj_errs
+ if cur_checksum != md5sum:
+ err = "%(cfile)s has incorrect MD5sum"
+ obj_errs.append(err % locals())
+ return obj_errs
+ if self.check_timestamps:
+ mtime = int(files[cfile][1])
+ st_mtime = int(os.lstat(cfile).st_mtime)
+ if st_mtime != mtime:
+ err = (
+ "%(cfile)s has wrong mtime (is %(st_mtime)d, should be "
+ "%(mtime)d)"
+ )
+ obj_errs.append(err % locals())
+ return obj_errs
+
+ return obj_errs
+
# =========
# Functions
# =========
def print_help(with_description=True):
"""Print description, usage and a detailed help message.
-
+
@type with_description: bool
@param with_description: if true, print module's __doc__ string
"""
if with_description:
- print __doc__.strip()
- print
+ print(__doc__.strip())
+ print()
# Deprecation warning added by djanderson, 12/2008
- pp.print_warn("Default action for this module has changed in Gentoolkit 0.3.")
- pp.print_warn("Use globbing to simulate the old behavior (see man equery).")
- pp.print_warn("Use '*' to check all installed packages.")
- print
-
- print mod_usage(mod_name="check")
- print
- print pp.command("options")
- print format_options((
+ depwarning = (
+ "Default action for this module has changed in Gentoolkit 0.3.",
+ "Use globbing to simulate the old behavior (see man equery).",
+ "Use '*' to check all installed packages.",
+ "Use 'foo-bar/*' to filter by category."
+ )
+ for line in depwarning:
+ sys.stderr.write(pp.warn(line))
+ print()
+
+ print(mod_usage(mod_name="check"))
+ print()
+ print(pp.command("options"))
+ print(format_options((
(" -h, --help", "display this help message"),
- (" -c, --category CAT", "only check files from packages in CAT"),
(" -f, --full-regex", "query is a regular expression"),
- ))
+ (" -o, --only-failures", "only display packages that do not pass"),
+ )))
+
+
+def checks_printer(cpv, data, verbose=True, only_failures=False):
+ """Output formatted results of pkg file(s) checks"""
+ seen = []
+
+ n_passed, n_checked, errs = data
+ n_failed = n_checked - n_passed
+ if only_failures and not n_failed:
+ return
+ else:
+ if verbose:
+ if not cpv in seen:
+ pp.uprint("* Checking %s ..." % (pp.emph(str(cpv))))
+ seen.append(cpv)
+ else:
+ pp.uprint("%s:" % cpv, end=' ')
+
+ if verbose:
+ for err in errs:
+ sys.stderr.write(pp.error(err))
+
+ if verbose:
+ n_passed = pp.number(str(n_passed))
+ n_checked = pp.number(str(n_checked))
+ info = " %(n_passed)s out of %(n_checked)s files passed"
+ print(info % locals())
+ else:
+ print("failed(%s)" % n_failed)
def parse_module_options(module_opts):
- """Parse module options and update GLOBAL_OPTS"""
+ """Parse module options and update QUERY_OPTS"""
opts = (x[0] for x in module_opts)
- posargs = (x[1] for x in module_opts)
- for opt, posarg in zip(opts, posargs):
+ for opt in opts:
if opt in ('-h', '--help'):
print_help()
sys.exit(0)
- elif opt in ('-c', '--category'):
- QUERY_OPTS['categoryFilter'] = posarg
elif opt in ('-f', '--full-regex'):
- QUERY_OPTS['isRegex'] = True
-
-
-def run_checks(files):
- """Run some basic sanity checks on a package's contents.
-
- If the file type (ftype) is not a directory or symlink, optionally
- verify MD5 sums or mtimes via verify_obj().
-
- @see: gentoolkit.packages.get_contents()
- @type files: dict
- @param files: in form {'PATH': ['TYPE', 'TIMESTAMP', 'MD5SUM']}
- @rtype: tuple
- @return:
- passed (int): number of files that passed all checks
- checked (int): number of files checked
- errs (list): check errors' descriptions
- """
-
- checked = 0
- passed = 0
- errs = []
- for cfile in files:
- checked += 1
- ftype = files[cfile][0]
- if not os.path.exists(cfile):
- errs.append("%s does not exist" % cfile)
- continue
- elif ftype == "dir":
- if not os.path.isdir(cfile):
- err = "%(cfile)s exists, but is not a directory"
- errs.append(err % locals())
- continue
- elif ftype == "obj":
- new_errs = verify_obj(files, cfile, errs)
- if new_errs != errs:
- errs = new_errs
- continue
- elif ftype == "sym":
- target = files[cfile][2].strip()
- if not os.path.islink(cfile):
- err = "%(cfile)s exists, but is not a symlink"
- errs.append(err % locals())
- continue
- tgt = os.readlink(cfile)
- if tgt != target:
- err = "%(cfile)s does not point to %(target)s"
- errs.append(err % locals())
- continue
- else:
- err = "%(cfile)s has unknown type %(ftype)s"
- errs.append(err % locals())
- continue
- passed += 1
-
- return passed, checked, errs
-
-
-def verify_obj(files, cfile, errs):
- """Verify the MD5 sum and/or mtime and return any errors."""
-
- if QUERY_OPTS["checkMD5sum"]:
- md5sum = files[cfile][2]
- try:
- cur_checksum = checksum.perform_md5(cfile, calc_prelink=1)
- except IOError:
- err = "Insufficient permissions to read %(cfile)s"
- errs.append(err % locals())
- return errs
- if cur_checksum != md5sum:
- err = "%(cfile)s has incorrect MD5sum"
- errs.append(err % locals())
- return errs
- if QUERY_OPTS["checkTimestamp"]:
- mtime = int(files[cfile][1])
- st_mtime = os.lstat(cfile).st_mtime
- if st_mtime != mtime:
- err = "%(cfile)s has wrong mtime (is %(st_mtime)d, " + \
- "should be %(mtime)d)"
- errs.append(err % locals())
- return errs
-
- return errs
+ QUERY_OPTS['is_regex'] = True
+ elif opt in ('-o', '--only-failures'):
+ QUERY_OPTS['only_failures'] = True
def main(input_args):
"""Parse input and run the program"""
- short_opts = "hac:f"
- long_opts = ('help', 'all', 'category=', 'full-regex')
+ short_opts = "hof"
+ long_opts = ('help', 'only-failures', 'full-regex')
try:
module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
- except GetoptError, err:
- pp.print_error("Module %s" % err)
- print
+ except GetoptError as err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print()
print_help(with_description=False)
sys.exit(2)
parse_module_options(module_opts)
-
- if not queries and not QUERY_OPTS["includeInstalled"]:
+
+ if not queries:
print_help()
sys.exit(2)
- elif queries and not QUERY_OPTS["includeInstalled"]:
- QUERY_OPTS["includeInstalled"] = True
- elif QUERY_OPTS["includeInstalled"]:
- queries = ["*"]
-
- #
- # Output
- #
first_run = True
- for query in queries:
+ for query in (Query(x, QUERY_OPTS['is_regex']) for x in queries):
if not first_run:
- print
+ print()
- matches = do_lookup(query, QUERY_OPTS)
+ matches = query.smart_find(**QUERY_OPTS)
if not matches:
- pp.print_error("No package found matching %s" % query)
+ raise errors.GentoolkitNoMatches(query, in_installed=True)
matches.sort()
- for pkg in matches:
- if Config['verbose']:
- print " * Checking %s ..." % pp.emph(pkg.cpv)
- else:
- print "%s:" % pkg.cpv
-
- passed, checked, errs = run_checks(pkg.get_contents())
-
- if Config['verbose']:
- for err in errs:
- pp.print_error(err)
+ printer = partial(
+ checks_printer,
+ verbose=CONFIG['verbose'],
+ only_failures=QUERY_OPTS['only_failures']
+ )
+ check = VerifyContents(printer_fn=printer)
+ check(matches)
- passed = pp.number(str(passed))
- checked = pp.number(str(checked))
- info = " %(passed)s out of %(checked)s files passed"
- print info % locals()
+ first_run = False
- first_run = False
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/equery/depends.py b/pym/gentoolkit/equery/depends.py
index a1a0d20..9e5d8de 100644
--- a/pym/gentoolkit/equery/depends.py
+++ b/pym/gentoolkit/equery/depends.py
@@ -4,7 +4,9 @@
#
# $Header: $
-"""List all direct dependencies matching a given query"""
+"""List all packages that depend on a atom given query"""
+
+from __future__ import print_function
__docformat__ = 'epytext'
@@ -15,34 +17,89 @@ __docformat__ = 'epytext'
import sys
from getopt import gnu_getopt, GetoptError
-from portage.util import unique_array
-
import gentoolkit.pprinter as pp
-from gentoolkit.equery import format_options, mod_usage, Config
-from gentoolkit.helpers2 import compare_package_strings, do_lookup, \
- find_packages, get_cpvs, get_installed_cpvs
-from gentoolkit.package import Package
+from gentoolkit.dependencies import Dependencies
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import get_cpvs, get_installed_cpvs
+from gentoolkit.cpv import CPV
# =======
# Globals
# =======
QUERY_OPTS = {
- "categoryFilter": None,
- "includeInstalled": True,
- "includePortTree": False,
- "includeOverlayTree": False,
- "isRegex": False,
- "onlyDirect": True,
- "printMatchInfo": (not Config['quiet']),
- "indentLevel": 0,
- "depth": -1
+ "include_masked": False,
+ "only_direct": True,
+ "max_depth": -1,
}
-# Used to cache and detect looping
-PKGSEEN = set()
-PKGDEPS = {}
-DEPPKGS = {}
+# =======
+# Classes
+# =======
+
+class DependPrinter(object):
+ """Output L{gentoolkit.dependencies.Dependencies} objects."""
+ def __init__(self, verbose=True):
+ self.verbose = verbose
+
+ if verbose:
+ self.print_fn = self.print_verbose
+ else:
+ self.print_fn = self.print_quiet
+
+ def __call__(self, dep, dep_is_displayed=False):
+ self.format_depend(dep, dep_is_displayed)
+
+ @staticmethod
+ def print_verbose(indent, cpv, use_conditional, depatom):
+ """Verbosely prints a set of dep strings."""
+
+ sep = ' ? ' if (depatom and use_conditional) else ''
+ pp.uprint(indent + pp.cpv(cpv), "(" + use_conditional +
+ sep + depatom + ")")
+
+ # W0613: *Unused argument %r*
+ # pylint: disable-msg=W0613
+ @staticmethod
+ def print_quiet(indent, cpv, use_conditional, depatom):
+ """Quietly prints a subset set of dep strings."""
+
+ pp.uprint(indent + cpv)
+
+ def format_depend(self, dep, dep_is_displayed):
+ """Format a dependency for printing.
+
+ @type dep: L{gentoolkit.dependencies.Dependencies}
+ @param dep: the dependency to display
+ """
+
+ # Don't print blank lines
+ if dep_is_displayed and not self.verbose:
+ return
+
+ depth = getattr(dep, 'depth', 0)
+ indent = " " * depth
+ mdep = dep.matching_dep
+ use_conditional = ""
+ if mdep.use_conditional:
+ use_conditional = " & ".join(
+ pp.useflag(u) for u in mdep.use_conditional.split()
+ )
+ if mdep.operator == '=*':
+ formatted_dep = '=%s*' % str(mdep.cpv)
+ else:
+ formatted_dep = mdep.operator + str(mdep.cpv)
+ if mdep.slot:
+ formatted_dep += pp.emph(':') + pp.slot(mdep.slot)
+ if mdep.use:
+ useflags = pp.useflag(','.join(mdep.use.tokens))
+ formatted_dep += (pp.emph('[') + useflags + pp.emph(']'))
+
+ if dep_is_displayed:
+ indent = indent + " " * len(str(dep.cpv))
+ self.print_fn(indent, '', use_conditional, formatted_dep)
+ else:
+ self.print_fn(indent, str(dep.cpv), use_conditional, formatted_dep)
# =========
# Functions
@@ -50,138 +107,29 @@ DEPPKGS = {}
def print_help(with_description=True):
"""Print description, usage and a detailed help message.
-
+
@type with_description: bool
@param with_description: if true, print module's __doc__ string
"""
if with_description:
- print __doc__.strip()
- print
- print mod_usage(mod_name="depends")
- print
- print pp.command("options")
- print format_options((
+ print(__doc__.strip())
+ print()
+ print(mod_usage(mod_name="depends"))
+ print()
+ print(pp.command("options"))
+ print(format_options((
(" -h, --help", "display this help message"),
- (" -a, --all-packages",
- "include packages that are not installed (slow)"),
+ (" -a, --all-packages",
+ "include dependencies that are not installed (slow)"),
(" -D, --indirect",
"search both direct and indirect dependencies"),
(" --depth=N", "limit indirect dependency tree to specified depth")
- ))
-
-
-def cache_package_list(pkg_cache=None):
- """Ensure that the package cache is set."""
-
- if not pkg_cache:
- if QUERY_OPTS['includePortTree']:
- packages = [Package(x) for x in get_cpvs()]
- else:
- packages = [Package(x) for x in get_installed_cpvs()]
- packages.sort()
- pkg_cache = packages
- else:
- packages = pkg_cache
-
- return packages
-
-
-def display_dependencies(cpv_is_displayed, dependency, cpv):
- """Output dependencies calculated by find_dependencies.
-
- @type cpv_is_displayed: bool
- @param cpv_is_displayed: if True, the cpv has already been printed
- @see: gentoolkit.package.get_*_deps()
- @type dependency: tuple
- @param dependency: (comparator, [use flags], cpv)
- @type cpv: string
- @param cpv: cat/pkg-ver
- """
-
- atom = pp.pkgquery(dependency[0] + dependency[2])
- indent = " " * (QUERY_OPTS["indentLevel"] * 2)
- useflags = pp.useflag(" & ".join(dependency[1]))
-
- if not cpv_is_displayed:
- if dependency[1]:
- if Config['verbose']:
- print indent + pp.cpv(cpv),
- print "(" + useflags + " ? " + atom + ")"
- else:
- print indent + cpv
- else:
- if Config['verbose']:
- print indent + pp.cpv(cpv),
- print "(" + atom + ")"
- else:
- print indent + cpv
- elif Config['verbose']:
- indent = indent + " " * len(cpv)
- if dependency[1]:
- print indent + " (" + useflags + " ? " + atom + ")"
- else:
- print indent + " (" + atom + ")"
-
+ )))
-def find_dependencies(matches, pkg_cache):
- """Find dependencies for the packaged named in queries.
-
- @type queries: list
- @param queries: packages to find the dependencies for
- """
-
- for pkg in cache_package_list(pkg_cache):
- if not pkg.cpv in PKGDEPS:
- try:
- deps = pkg.get_runtime_deps() + pkg.get_compiletime_deps()
- deps.extend(pkg.get_postmerge_deps())
- except KeyError:
- # If the ebuild is not found...
- continue
- # Remove duplicate deps
- deps = unique_array(deps)
- PKGDEPS[pkg.cpv] = deps
- else:
- deps = PKGDEPS[pkg.cpv]
-
- cpv_is_displayed = False
- for dependency in deps:
- # TODO: (old) determine if dependency is enabled by USE flag
- # Find all packages matching the dependency
- depstr = dependency[0] + dependency[2]
- if not depstr in DEPPKGS:
- depcpvs = find_packages(depstr,
- include_masked=QUERY_OPTS["includePortTree"])
- DEPPKGS[depstr] = depcpvs
- else:
- depcpvs = DEPPKGS[depstr]
-
- for depcpv in depcpvs:
- is_match = False
- if depcpv in matches:
- is_match = True
-
- if is_match:
- display_dependencies(cpv_is_displayed, dependency, pkg.cpv)
- cpv_is_displayed = True
- break
-
- # if --indirect specified, call ourselves again with the dependency
- # Do not call if we have already called ourselves.
- if (cpv_is_displayed and not QUERY_OPTS["onlyDirect"] and
- pkg not in PKGSEEN and
- (QUERY_OPTS["indentLevel"] < QUERY_OPTS["depth"] or
- QUERY_OPTS["depth"] == -1)):
-
- PKGSEEN.add(pkg)
- QUERY_OPTS["indentLevel"] += 1
- find_dependencies([pkg], pkg_cache)
- QUERY_OPTS["indentLevel"] -= 1
-
def parse_module_options(module_opts):
- """Parse module options and update GLOBAL_OPTS"""
+ """Parse module options and update QUERY_OPTS"""
opts = (x[0] for x in module_opts)
posargs = (x[1] for x in module_opts)
@@ -190,39 +138,36 @@ def parse_module_options(module_opts):
print_help()
sys.exit(0)
elif opt in ('-a', '--all-packages'):
- QUERY_OPTS['includePortTree'] = True
- elif opt in ('-d', '--direct'):
- continue
+ QUERY_OPTS['include_masked'] = True
elif opt in ('-D', '--indirect'):
- QUERY_OPTS['onlyDirect'] = False
+ QUERY_OPTS['only_direct'] = False
elif opt in ('--depth'):
if posarg.isdigit():
depth = int(posarg)
else:
err = "Module option --depth requires integer (got '%s')"
- pp.print_error(err % posarg)
- print
+ sys.stdout.write(pp.error(err % posarg))
+ print()
print_help(with_description=False)
sys.exit(2)
- QUERY_OPTS["depth"] = depth
+ QUERY_OPTS["max_depth"] = depth
def main(input_args):
"""Parse input and run the program"""
-
short_opts = "hadD" # -d, --direct was old option for default action
long_opts = ('help', 'all-packages', 'direct', 'indirect', 'depth=')
try:
module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
- except GetoptError, err:
- pp.print_error("Module %s" % err)
- print
+ except GetoptError as err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print()
print_help(with_description=False)
sys.exit(2)
parse_module_options(module_opts)
-
+
if not queries:
print_help()
sys.exit(2)
@@ -231,21 +176,33 @@ def main(input_args):
# Output
#
+ dep_print = DependPrinter(verbose=CONFIG['verbose'])
+
first_run = True
+ got_match = False
for query in queries:
if not first_run:
- print
+ print()
- matches = do_lookup(query, QUERY_OPTS)
-
- if matches:
- find_dependencies(matches, None)
+ pkg = Dependencies(query)
+ if QUERY_OPTS['include_masked']:
+ pkggetter = get_cpvs
else:
- if QUERY_OPTS['includePortTree']:
- pp.print_error("No matching package found for %s" % query)
- else:
- pp.print_error(
- "No matching package or all versions masked for %s" % query
- )
+ pkggetter = get_installed_cpvs
+
+ if CONFIG['verbose']:
+ print(" * These packages depend on %s:" % pp.emph(pkg.cpv))
+ if pkg.graph_reverse_depends(
+ pkgset=sorted(pkggetter(), key=CPV),
+ max_depth=QUERY_OPTS["max_depth"],
+ only_direct=QUERY_OPTS["only_direct"],
+ printer_fn=dep_print
+ ):
+ got_match = True
first_run = False
+
+ if not got_match:
+ sys.exit(1)
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/equery/depgraph.py b/pym/gentoolkit/equery/depgraph.py
index b9cd0a1..9fbdd42 100644
--- a/pym/gentoolkit/equery/depgraph.py
+++ b/pym/gentoolkit/equery/depgraph.py
@@ -4,7 +4,9 @@
#
# $Header: $
-"""Display a dependency graph for a given package"""
+"""Display a direct dependency graph for a given package"""
+
+from __future__ import print_function
__docformat__ = 'epytext'
@@ -13,30 +15,32 @@ __docformat__ = 'epytext'
# =======
import sys
+from functools import partial
from getopt import gnu_getopt, GetoptError
-import gentoolkit
+import portage
+
import gentoolkit.pprinter as pp
from gentoolkit import errors
-from gentoolkit.equery import format_options, mod_usage, Config
-from gentoolkit.helpers2 import do_lookup, find_best_match
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.keyword import determine_keyword
+from gentoolkit.query import Query
# =======
# Globals
# =======
QUERY_OPTS = {
- "categoryFilter": None,
- "depth": 0,
- "displayUseflags": True,
- "fancyFormat": True,
- "includeInstalled": True,
- "includePortTree": True,
- "includeOverlayTree": True,
- "includeMasked": True,
- "isRegex": False,
- "matchExact": True,
- "printMatchInfo": (not Config['quiet'])
+ "depth": 1,
+ "no_atom": False,
+ "no_indent": False,
+ "no_useflags": False,
+ "no_mask": False,
+ "in_installed": True,
+ "in_porttree": True,
+ "in_overlay": True,
+ "include_masked": True,
+ "show_progress": (not CONFIG['quiet'])
}
# =========
@@ -45,74 +49,31 @@ QUERY_OPTS = {
def print_help(with_description=True):
"""Print description, usage and a detailed help message.
-
+
@type with_description: bool
@param with_description: if true, print module's __doc__ string
"""
if with_description:
- print __doc__.strip()
- print
- print mod_usage(mod_name="depgraph")
- print
- print pp.command("options")
- print format_options((
+ print(__doc__.strip())
+ print()
+ print("Default depth is set to 1 (direct only). Use --depth=0 for no max.")
+ print()
+ print(mod_usage(mod_name="depgraph"))
+ print()
+ print(pp.command("options"))
+ print(format_options((
(" -h, --help", "display this help message"),
+ (" -A, --no-atom", "do not show dependency atom"),
+ (" -M, --no-mask", "do not show masking status"),
(" -U, --no-useflags", "do not show USE flags"),
- (" -l, --linear", "do not use fancy formatting"),
+ (" -l, --linear", "do not format the graph by indenting dependencies"),
(" --depth=N", "limit dependency graph to specified depth")
- ))
-
-
-def display_graph(pkg, stats, level=0, seen_pkgs=None, suffix=""):
- """Display a dependency graph for a package
-
- @type pkg: gentoolkit.package.Package
- @param pkg: package to check dependencies of
- @type level: int
- @param level: current depth level
- @type seen_pkgs: set
- @param seen_pkgs: a set of all packages that have had their deps graphed
- """
-
- if not seen_pkgs:
- seen_pkgs = set()
-
- stats["packages"] += 1
- stats["maxdepth"] = max(stats["maxdepth"], level)
-
- pfx = ""
- if QUERY_OPTS["fancyFormat"]:
- pfx = (level * " ") + "`-- "
- pp.print_info(0, pfx + pkg.cpv + suffix)
-
- seen_pkgs.add(pkg.cpv)
-
- deps = pkg.get_runtime_deps() + pkg.get_compiletime_deps()
- deps.extend(pkg.get_postmerge_deps())
- for dep in deps:
- suffix = ""
- depcpv = dep[2]
- deppkg = find_best_match(dep[0] + depcpv)
- if not deppkg:
- print (pfx + dep[0] + depcpv),
- print "(unable to resolve: package masked or removed)"
- continue
- if deppkg.get_cpv() in seen_pkgs:
- continue
- if depcpv.find("virtual") == 0:
- suffix += " (%s)" % pp.cpv(depcpv)
- if dep[1] and QUERY_OPTS["displayUseflags"]:
- suffix += " [%s]" % pp.useflagon(' '.join(dep[1]))
- if (level < QUERY_OPTS["depth"] or QUERY_OPTS["depth"] <= 0):
- seen_pkgs, stats = display_graph(deppkg, stats, level+1,
- seen_pkgs, suffix)
-
- return seen_pkgs, stats
+ )))
def parse_module_options(module_opts):
- """Parse module options and update GLOBAL_OPTS"""
+ """Parse module options and update QUERY_OPTS"""
opts = (x[0] for x in module_opts)
posargs = (x[1] for x in module_opts)
@@ -120,38 +81,132 @@ def parse_module_options(module_opts):
if opt in ('-h', '--help'):
print_help()
sys.exit(0)
+ if opt in ('-A', '--no-atom'):
+ QUERY_OPTS["no_atom"] = True
if opt in ('-U', '--no-useflags'):
- QUERY_OPTS["displayUseflags"] = False
+ QUERY_OPTS["no_useflags"] = True
+ if opt in ('-M', '--no-mask'):
+ QUERY_OPTS["no_mask"] = True
if opt in ('-l', '--linear'):
- QUERY_OPTS["fancyFormat"] = False
+ QUERY_OPTS["no_indent"] = True
if opt in ('--depth'):
if posarg.isdigit():
depth = int(posarg)
else:
- err = "Module option --depth requires integer (got '%s')"
- pp.print_error(err % posarg)
- print
+ err = "Module option --depth requires integer (got '%s')"
+ sys.stderr.write(pp.error(err % posarg))
+ print()
print_help(with_description=False)
sys.exit(2)
QUERY_OPTS["depth"] = depth
+def depgraph_printer(
+ depth,
+ pkg,
+ dep,
+ no_use=False,
+ no_atom=False,
+ no_indent=False,
+ initial_pkg=False,
+ no_mask=False
+):
+ """Display L{gentoolkit.dependencies.Dependencies.graph_depends} results.
+
+ @type depth: int
+ @param depth: depth of indirection, used to calculate indent
+ @type pkg: L{gentoolkit.package.Package}
+ @param pkg: "best match" package matched by B{dep}
+ @type dep: L{gentoolkit.atom.Atom}
+ @param dep: dependency that matched B{pkg}
+ @type no_use: bool
+ @param no_use: don't output USE flags
+ @type no_atom: bool
+ @param no_atom: don't output dep atom
+ @type no_indent: bool
+ @param no_indent: don't output indent based on B{depth}
+ @type initial_pkg: bool
+ @param initial_pkg: somewhat of a hack used to print the root package of
+ the graph with absolutely no indent
+ """
+ indent = '' if no_indent or initial_pkg else ' ' + (' ' * depth)
+ decorator = '[%3d] ' % depth if no_indent else '`-- '
+ use = ''
+ atom = ''
+ mask = ''
+ try:
+ if not no_atom:
+ if dep.operator == '=*':
+ atom += ' (=%s*)' % dep.cpv
+ else:
+ atom += ' (%s%s)' % (dep.operator, dep.cpv)
+ if not no_use and dep is not None and dep.use:
+ use = ' [%s]' % ' '.join(
+ pp.useflag(x, enabled=True) for x in dep.use.tokens
+ )
+ except AttributeError:
+ # 'NoneType' object has no attribute 'atom'
+ pass
+ if pkg and not no_mask:
+ mask = pkg.mask_status()
+ if not mask:
+ mask = [determine_keyword(portage.settings["ARCH"],
+ portage.settings["ACCEPT_KEYWORDS"],
+ pkg.environment('KEYWORDS'))]
+ mask = pp.masking(mask)
+ try:
+ pp.uprint(' '.join(
+ (indent, decorator, pp.cpv(str(pkg.cpv)), atom, mask, use)
+ ))
+ except AttributeError:
+ # 'NoneType' object has no attribute 'cpv'
+ pp.uprint(''.join((indent, decorator, "(no match for %r)" % dep.atom)))
+
+
+def make_depgraph(pkg, printer_fn):
+ """Create and display depgraph for each package."""
+
+ print()
+ if CONFIG['verbose']:
+ pp.uprint(" * " + pp.subsection("dependency graph for ") +
+ pp.cpv(str(pkg.cpv)))
+ else:
+ pp.uprint("%s:" % pkg.cpv)
+
+ # Print out the first package
+ printer_fn(0, pkg, None, initial_pkg=True)
+
+ deps = pkg.deps.graph_depends(
+ max_depth=QUERY_OPTS['depth'],
+ printer_fn=printer_fn,
+ # Use this to set this pkg as the graph's root; better way?
+ result=[(0, pkg)]
+ )
+
+ if CONFIG['verbose']:
+ pkgname = pp.cpv(str(pkg.cpv))
+ n_packages = pp.number(str(len(deps)))
+ max_seen = pp.number(str(max(x[0] for x in deps)))
+ info = "[ %s stats: packages (%s), max depth (%s) ]"
+ pp.uprint(info % (pkgname, n_packages, max_seen))
+
+
def main(input_args):
"""Parse input and run the program"""
- short_opts = "hUl"
- long_opts = ('help', 'no-useflags', 'depth=')
+ short_opts = "hAMUl"
+ long_opts = ('help', 'no-atom', 'no-useflags', 'no-mask', 'depth=')
try:
module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
- except GetoptError, err:
- pp.print_error("Module %s" % err)
- print
+ except GetoptError as err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print()
print_help(with_description=False)
sys.exit(2)
parse_module_options(module_opts)
-
+
if not queries:
print_help()
sys.exit(2)
@@ -161,29 +216,37 @@ def main(input_args):
#
first_run = True
- for query in queries:
+ for query in (Query(x) for x in queries):
if not first_run:
- print
+ print()
- matches = do_lookup(query, QUERY_OPTS)
+ matches = query.smart_find(**QUERY_OPTS)
if not matches:
- errors.GentoolkitNoMatches(query)
+ raise errors.GentoolkitNoMatches(query)
+
+ matches.sort()
+
+ if CONFIG['verbose']:
+ printer = partial(
+ depgraph_printer,
+ no_atom=QUERY_OPTS['no_atom'],
+ no_indent=QUERY_OPTS['no_indent'],
+ no_use=QUERY_OPTS['no_useflags'],
+ no_mask=QUERY_OPTS['no_mask']
+ )
+ else:
+ printer = partial(
+ depgraph_printer,
+ no_atom=True,
+ no_indent=True,
+ no_use=True,
+ no_mask=True
+ )
for pkg in matches:
- stats = {"maxdepth": 0, "packages": 0}
-
- if Config['verbose']:
- pp.print_info(3, " * dependency graph for %s:" % pp.cpv(pkg.cpv))
- else:
- pp.print_info(0, "%s:" % pkg.cpv)
-
- stats = display_graph(pkg, stats)[1]
-
- if Config['verbose']:
- info = ''.join(["[ ", pp.cpv(pkg.cpv), " stats: packages (",
- pp.number(str(stats["packages"])), "), max depth (",
- pp.number(str(stats["maxdepth"])), ") ]"])
- pp.print_info(0, info)
+ make_depgraph(pkg, printer)
first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/equery/files.py b/pym/gentoolkit/equery/files.py
index 80caf1d..2843baf 100644
--- a/pym/gentoolkit/equery/files.py
+++ b/pym/gentoolkit/equery/files.py
@@ -4,7 +4,9 @@
#
# $Header: $
-"""List files owned by a given package"""
+"""List files owned by a given package."""
+
+from __future__ import print_function
__docformat__ = 'epytext'
@@ -16,34 +18,33 @@ import os
import sys
from getopt import gnu_getopt, GetoptError
-import gentoolkit
+import portage
+
import gentoolkit.pprinter as pp
-from gentoolkit.equery import format_filetype, format_options, mod_usage, \
- Config
-from gentoolkit.helpers2 import do_lookup
+from gentoolkit.equery import (format_filetype, format_options, mod_usage,
+ CONFIG)
+from gentoolkit.query import Query
# =======
# Globals
# =======
QUERY_OPTS = {
- "categoryFilter": None,
- "includeInstalled": True,
- "includePortTree": False,
- "includeOverlayTree": False,
- "includeMasked": True,
- "isRegex": False,
- "matchExact": True,
- "outputTree": False,
- "printMatchInfo": (not Config['quiet']),
- "showType": False,
- "showTimestamp": False,
- "showMD5": False,
- "typeFilter": None
+ "in_installed": True,
+ "in_porttree": False,
+ "in_overlay": False,
+ "include_masked": True,
+ "output_tree": False,
+ "show_progress": (not CONFIG['quiet']),
+ "show_type": False,
+ "show_timestamp": False,
+ "show_MD5": False,
+ "type_filter": None
}
-FILTER_RULES = ('dir', 'obj', 'sym', 'dev', 'path', 'conf', 'cmd', 'doc',
- 'man', 'info')
+FILTER_RULES = (
+ 'dir', 'obj', 'sym', 'dev', 'path', 'conf', 'cmd', 'doc', 'man', 'info'
+)
# =========
# Functions
@@ -51,78 +52,84 @@ FILTER_RULES = ('dir', 'obj', 'sym', 'dev', 'path', 'conf', 'cmd', 'doc',
def print_help(with_description=True):
"""Print description, usage and a detailed help message.
-
+
@type with_description: bool
@param with_description: if true, print module's __doc__ string
"""
if with_description:
- print __doc__.strip()
- print
- print mod_usage(mod_name="files")
- print
- print pp.command("options")
- print format_options((
+ print(__doc__.strip())
+ print()
+ print(mod_usage(mod_name="files"))
+ print()
+ print(pp.command("options"))
+ print(format_options((
(" -h, --help", "display this help message"),
(" -m, --md5sum", "include MD5 sum in output"),
(" -s, --timestamp", "include timestamp in output"),
(" -t, --type", "include file type in output"),
(" --tree", "display results in a tree (turns off other options)"),
(" -f, --filter=RULES", "filter output by file type"),
- (" RULES",
+ (" RULES",
"a comma-separated list (no spaces); choose from:")
- ))
- print " " * 24, ', '.join(pp.emph(x) for x in FILTER_RULES)
+ )))
+ print(" " * 24, ', '.join(pp.emph(x) for x in FILTER_RULES))
+# R0912: *Too many branches (%s/%s)*
+# pylint: disable-msg=R0912
def display_files(contents):
"""Display the content of an installed package.
- @see: gentoolkit.package.Package.get_contents
+ @see: gentoolkit.package.Package.parsed_contents
@type contents: dict
@param contents: {'path': ['filetype', ...], ...}
"""
- filenames = contents.keys()
+ filenames = list(contents.keys())
filenames.sort()
last = []
for name in filenames:
- if QUERY_OPTS["outputTree"]:
- basename = name.split("/")[1:]
+ if QUERY_OPTS["output_tree"]:
+ dirdepth = name.count('/')
+ indent = " "
+ if dirdepth == 2:
+ indent = " "
+ elif dirdepth > 2:
+ indent = " " * (dirdepth - 1)
+
+ basename = name.rsplit("/", dirdepth - 1)
if contents[name][0] == "dir":
if len(last) == 0:
last = basename
- print pp.path(" /" + basename[0])
+ pp.uprint(pp.path(indent + basename[0]))
continue
- numol = 0
for i, directory in enumerate(basename):
try:
if directory in last[i]:
- numol = i + 1
continue
- # W0704: Except doesn't do anything
- # pylint: disable-msg=W0704
except IndexError:
pass
last = basename
if len(last) == 1:
- print pp.path(" " + last[0])
+ pp.uprint(pp.path(indent + last[0]))
continue
- ind = " " * (numol * 3)
- print pp.path(ind + "> " + "/" + last[-1])
+ pp.uprint(pp.path(indent + "> /" + last[-1]))
elif contents[name][0] == "sym":
- print pp.path(" " * (len(last) * 3) + "+"),
- print pp.path_symlink(basename[-1] + " -> " + contents[name][2])
- else:
- print pp.path(" " * (len(last) * 3) + "+ ") + basename[-1]
+ pp.uprint(pp.path(indent + "+"), end=' ')
+ pp.uprint(pp.path_symlink(basename[-1] + " -> " +
+ contents[name][2]))
+ else:
+ pp.uprint(pp.path(indent + "+ ") + basename[-1])
else:
- pp.print_info(0, format_filetype(
+ pp.uprint(format_filetype(
name,
contents[name],
- show_type=QUERY_OPTS["showType"],
- show_md5=QUERY_OPTS["showMD5"],
- show_timestamp=QUERY_OPTS["showTimestamp"]))
+ show_type=QUERY_OPTS["show_type"],
+ show_md5=QUERY_OPTS["show_MD5"],
+ show_timestamp=QUERY_OPTS["show_timestamp"]
+ ))
def filter_by_doc(contents, content_filter):
@@ -136,7 +143,7 @@ def filter_by_doc(contents, content_filter):
for path in contents:
if contents[path][0] == 'obj' and path.startswith(docpath):
filtered_content[path] = contents[path]
-
+
return filtered_content
@@ -150,7 +157,7 @@ def filter_by_command(contents):
if (contents[path][0] in ['obj', 'sym'] and
os.path.dirname(path) in userpath):
filtered_content[path] = contents[path]
-
+
return filtered_content
@@ -172,7 +179,7 @@ def filter_by_path(contents):
if check_subdirs:
while (paths and paths[-1].startswith(basepath)):
paths.pop()
-
+
return filtered_content
@@ -180,9 +187,9 @@ def filter_by_conf(contents):
"""Return a copy of content filtered by configuration files."""
filtered_content = {}
- conf_path = gentoolkit.settings["CONFIG_PROTECT"].split()
+ conf_path = portage.settings["CONFIG_PROTECT"].split()
conf_path = tuple(os.path.normpath(x) for x in conf_path)
- conf_mask_path = gentoolkit.settings["CONFIG_PROTECT_MASK"].split()
+ conf_mask_path = portage.settings["CONFIG_PROTECT_MASK"].split()
conf_mask_path = tuple(os.path.normpath(x) for x in conf_mask_path)
for path in contents:
if contents[path][0] == 'obj' and path.startswith(conf_path):
@@ -195,18 +202,18 @@ def filter_by_conf(contents):
def filter_contents(contents):
"""Filter files by type if specified by the user.
- @see: gentoolkit.package.Package.get_contents
+ @see: gentoolkit.package.Package.parsed_contents
@type contents: dict
@param contents: {'path': ['filetype', ...], ...}
@rtype: dict
@return: contents with unrequested filetypes stripped
"""
- if QUERY_OPTS['typeFilter']:
- content_filter = QUERY_OPTS['typeFilter']
+ if QUERY_OPTS['type_filter']:
+ content_filter = QUERY_OPTS['type_filter']
else:
return contents
-
+
filtered_content = {}
if frozenset(('dir', 'obj', 'sym', 'dev')).intersection(content_filter):
# Filter elements by type (as recorded in CONTENTS)
@@ -221,12 +228,12 @@ def filter_contents(contents):
filtered_content.update(filter_by_conf(contents))
if frozenset(('doc' ,'man' ,'info')).intersection(content_filter):
filtered_content.update(filter_by_doc(contents, content_filter))
-
+
return filtered_content
def parse_module_options(module_opts):
- """Parse module options and update GLOBAL_OPTS"""
+ """Parse module options and update QUERY_OPTS"""
content_filter = []
opts = (x[0] for x in module_opts)
@@ -235,52 +242,53 @@ def parse_module_options(module_opts):
if opt in ('-h', '--help'):
print_help()
sys.exit(0)
- elif opt in ('-e', '--exact-name'):
- QUERY_OPTS["matchExact"] = True
elif opt in ('-m', '--md5sum'):
- QUERY_OPTS["showMD5"] = True
+ QUERY_OPTS["show_MD5"] = True
elif opt in ('-s', '--timestamp'):
- QUERY_OPTS["showTimestamp"] = True
+ QUERY_OPTS["show_timestamp"] = True
elif opt in ('-t', '--type'):
- QUERY_OPTS["showType"] = True
+ QUERY_OPTS["show_type"] = True
elif opt in ('--tree'):
- QUERY_OPTS["outputTree"] = True
+ QUERY_OPTS["output_tree"] = True
elif opt in ('-f', '--filter'):
f_split = posarg.split(',')
content_filter.extend(x.lstrip('=') for x in f_split)
for rule in content_filter:
if not rule in FILTER_RULES:
- pp.print_error("Invalid filter rule '%s'" % rule)
- print
+ sys.stderr.write(
+ pp.error("Invalid filter rule '%s'" % rule)
+ )
+ print()
print_help(with_description=False)
sys.exit(2)
- QUERY_OPTS["typeFilter"] = content_filter
+ QUERY_OPTS["type_filter"] = content_filter
def main(input_args):
"""Parse input and run the program"""
+ # -e, --exact-name is legacy option. djanderson '09
short_opts = "hemstf:"
long_opts = ('help', 'exact-name', 'md5sum', 'timestamp', 'type', 'tree',
- 'filter=')
+ 'filter=')
try:
module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
- except GetoptError, err:
- pp.print_error("Module %s" % err)
- print
+ except GetoptError as err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print()
print_help(with_description=False)
sys.exit(2)
parse_module_options(module_opts)
-
+
if not queries:
print_help()
sys.exit(2)
# Turn off filtering for tree output
- if QUERY_OPTS["outputTree"]:
- QUERY_OPTS["typeFilter"] = None
+ if QUERY_OPTS["output_tree"]:
+ QUERY_OPTS["type_filter"] = None
#
# Output files
@@ -289,18 +297,24 @@ def main(input_args):
first_run = True
for query in queries:
if not first_run:
- print
+ print()
- matches = do_lookup(query, QUERY_OPTS)
+ matches = Query(query).smart_find(**QUERY_OPTS)
if not matches:
- pp.print_error("No matching packages found for %s" % query)
+ sys.stderr.write(
+ pp.error("No matching packages found for %s" % query)
+ )
+
+ matches.sort()
for pkg in matches:
- if Config['verbose']:
- print " * Contents of %s:" % pp.cpv(pkg.cpv)
+ if CONFIG['verbose']:
+ pp.uprint(" * Contents of %s:" % pp.cpv(str(pkg.cpv)))
- contents = pkg.get_contents()
+ contents = pkg.parsed_contents()
display_files(filter_contents(contents))
first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/equery/has.py b/pym/gentoolkit/equery/has.py
new file mode 100644
index 0000000..9ea5f77
--- /dev/null
+++ b/pym/gentoolkit/equery/has.py
@@ -0,0 +1,213 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2 or higher
+#
+# $Header: $
+
+"""List all installed packages that match for a given ENVIRONMENT variable"""
+
+from __future__ import print_function
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import sys
+from getopt import gnu_getopt, GetoptError
+
+from portage import auxdbkeys
+
+import gentoolkit.pprinter as pp
+from gentoolkit import errors
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.package import PackageFormatter, FORMAT_TMPL_VARS
+from gentoolkit.query import Query
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+ "in_installed": True,
+ "in_porttree": False,
+ "in_overlay": False,
+ "include_masked": True,
+ "show_progress": False,
+ "package_format": None,
+ "package_filter": None,
+ "env_var": None
+}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+ """Print description, usage and a detailed help message.
+
+ @type with_description: bool
+ @param with_description: if true, print module's __doc__ string
+ """
+
+ if with_description:
+ print(__doc__.strip())
+ print()
+ print(mod_usage(mod_name="has", arg="env_var [expr]"))
+ print()
+ print(pp.command("options"))
+ print(format_options((
+ (" -h, --help", "display this help message"),
+ (" -I, --exclude-installed",
+ "exclude installed packages from search path"),
+ (" -o, --overlay-tree", "include overlays in search path"),
+ (" -p, --portage-tree", "include entire portage tree in search path"),
+ (" -F, --format=TMPL", "specify a custom output format"),
+ (" TMPL",
+ "a format template using (see man page):")
+ )))
+# print(" " * 24, ', '.join(pp.emph(x) for x in FORMAT_TMPL_VARS))
+
+
+def query_in_env(query, env_var, pkg):
+ """Check if the query is in the pkg's environment."""
+
+ try:
+ if env_var in ("USE", "IUSE"):
+ results = set(
+ [x.lstrip("+-") for x in pkg.environment(env_var).split()]
+ )
+ else:
+ results = set(pkg.environment(env_var).split())
+ except errors.GentoolkitFatalError:
+ # aux_get KeyError or other unexpected result
+ return False
+
+ if query in results:
+ return True
+
+ return False
+
+
+def display_pkg(query, env_var, pkg):
+ """Display information for a given package."""
+
+ if CONFIG['verbose']:
+ pkgstr = PackageFormatter(
+ pkg,
+ do_format=True,
+ custom_format=QUERY_OPTS["package_format"]
+ )
+ else:
+ pkgstr = PackageFormatter(
+ pkg,
+ do_format=False,
+ custom_format=QUERY_OPTS["package_format"]
+ )
+
+ if (QUERY_OPTS["in_installed"] and
+ not QUERY_OPTS["in_porttree"] and
+ not QUERY_OPTS["in_overlay"]):
+ if not 'I' in pkgstr.location:
+ return False
+ if (QUERY_OPTS["in_porttree"] and
+ not QUERY_OPTS["in_overlay"]):
+ if not 'P' in pkgstr.location:
+ return False
+ if (QUERY_OPTS["in_overlay"] and
+ not QUERY_OPTS["in_porttree"]):
+ if not 'O' in pkgstr.location:
+ return False
+ pp.uprint(pkgstr)
+
+ return True
+
+
+def parse_module_options(module_opts):
+ """Parse module options and update QUERY_OPTS"""
+
+ # Parse module options
+ opts = (x[0] for x in module_opts)
+ posargs = (x[1] for x in module_opts)
+ for opt, posarg in zip(opts, posargs):
+ if opt in ('-h', '--help'):
+ print_help()
+ sys.exit(0)
+ elif opt in ('-I', '--exclue-installed'):
+ QUERY_OPTS['in_installed'] = False
+ elif opt in ('-p', '--portage-tree'):
+ QUERY_OPTS['in_porttree'] = True
+ elif opt in ('-o', '--overlay-tree'):
+ QUERY_OPTS['in_overlay'] = True
+ elif opt in ('-F', '--format'):
+ QUERY_OPTS["package_format"] = posarg
+ elif opt in ('--package'):
+ QUERY_OPTS["package_filter"] = posarg
+
+
+def main(input_args):
+ """Parse input and run the program"""
+
+ short_opts = "hiIpoF:" # -i was option for default action
+ # --installed is no longer needed, kept for compatibility (djanderson '09)
+ long_opts = ('help', 'installed', 'exclude-installed', 'portage-tree',
+ 'overlay-tree', 'format=', 'package=')
+
+ try:
+ module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+ except GetoptError as err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print()
+ print_help(with_description=False)
+ sys.exit(2)
+
+ parse_module_options(module_opts)
+
+ if not queries:
+ print_help()
+ sys.exit(2)
+
+ query_scope = QUERY_OPTS['package_filter'] or '*'
+ matches = Query(query_scope).smart_find(**QUERY_OPTS)
+ matches.sort()
+
+ # split out the first query since it is suppose to be the env_var
+ QUERY_OPTS['env_var'] = queries.pop(0)
+ env_var = QUERY_OPTS['env_var']
+
+ #
+ # Output
+ #
+
+ if not queries:
+ if not QUERY_OPTS['package_filter']:
+ err = "Used ENV_VAR without match_expression or --package"
+ raise errors.GentoolkitFatalError(err, is_serious=False)
+ else:
+ if len(matches) > 1:
+ raise errors.AmbiguousPackageName(matches)
+ for match in matches:
+ env = QUERY_OPTS['env_var']
+ print(match.environment(env))
+
+ first_run = True
+ got_match = False
+ for query in queries:
+ if not first_run:
+ print()
+
+ if CONFIG['verbose']:
+ status = " * Searching for {0} {1} ... "
+ pp.uprint(status.format(env_var, pp.emph(query)))
+
+ for pkg in matches:
+ if query_in_env(query, env_var, pkg):
+ display_pkg(query, env_var, pkg)
+ got_match = True
+ first_run = False
+
+ if not got_match:
+ sys.exit(1)
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/equery/hasuse.py b/pym/gentoolkit/equery/hasuse.py
index c110a22..80c98fe 100644
--- a/pym/gentoolkit/equery/hasuse.py
+++ b/pym/gentoolkit/equery/hasuse.py
@@ -6,6 +6,8 @@
"""List all installed packages that have a given USE flag"""
+from __future__ import print_function
+
__docformat__ = 'epytext'
# =======
@@ -15,24 +17,23 @@ __docformat__ = 'epytext'
import sys
from getopt import gnu_getopt, GetoptError
-import gentoolkit
import gentoolkit.pprinter as pp
-from gentoolkit.equery import format_options, mod_usage, Config
-from gentoolkit.helpers2 import do_lookup, get_installed_cpvs, print_sequence
-from gentoolkit.package import Package, PackageFormatter
+from gentoolkit import errors
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.package import PackageFormatter, FORMAT_TMPL_VARS
+from gentoolkit.query import Query
# =======
# Globals
# =======
QUERY_OPTS = {
- "categoryFilter": None,
- "includeInstalled": True,
- "includePortTree": False,
- "includeOverlayTree": False,
- "includeMasked": True,
- "isRegex": False, # Necessary for do_lookup, don't change
- "printMatchInfo": False
+ "in_installed": True,
+ "in_porttree": False,
+ "in_overlay": False,
+ "include_masked": True,
+ "show_progress": False,
+ "package_format": None
}
# =========
@@ -41,56 +42,107 @@ QUERY_OPTS = {
def print_help(with_description=True):
"""Print description, usage and a detailed help message.
-
+
@type with_description: bool
@param with_description: if true, print module's __doc__ string
"""
if with_description:
- print __doc__.strip()
- print
- print mod_usage(mod_name="hasuse", arg="USE-flag")
- print
- print pp.command("options")
- print format_options((
+ print(__doc__.strip())
+ print()
+ print(mod_usage(mod_name="hasuse", arg="USE-flag"))
+ print()
+ print(pp.command("options"))
+ print(format_options((
(" -h, --help", "display this help message"),
(" -I, --exclude-installed",
"exclude installed packages from search path"),
(" -o, --overlay-tree", "include overlays in search path"),
- (" -p, --portage-tree", "include entire portage tree in search path")
- ))
+ (" -p, --portage-tree", "include entire portage tree in search path"),
+ (" -F, --format=TMPL", "specify a custom output format"),
+ (" TMPL",
+ "a format template using (see man page):")
+ )))
+ print(" " * 24, ', '.join(pp.emph(x) for x in FORMAT_TMPL_VARS))
+
+
+def display_useflags(query, pkg):
+ """Display USE flag information for a given package."""
+
+ try:
+ useflags = [x.lstrip("+-") for x in pkg.environment("IUSE").split()]
+ except errors.GentoolkitFatalError:
+ # aux_get KeyError or other unexpected result
+ return False
+
+ if query not in useflags:
+ return False
+
+ if CONFIG['verbose']:
+ pkgstr = PackageFormatter(
+ pkg,
+ do_format=True,
+ custom_format=QUERY_OPTS["package_format"]
+ )
+ else:
+ pkgstr = PackageFormatter(
+ pkg,
+ do_format=False,
+ custom_format=QUERY_OPTS["package_format"]
+ )
+
+ if (QUERY_OPTS["in_installed"] and
+ not QUERY_OPTS["in_porttree"] and
+ not QUERY_OPTS["in_overlay"]):
+ if not 'I' in pkgstr.location:
+ return False
+ if (QUERY_OPTS["in_porttree"] and
+ not QUERY_OPTS["in_overlay"]):
+ if not 'P' in pkgstr.location:
+ return False
+ if (QUERY_OPTS["in_overlay"] and
+ not QUERY_OPTS["in_porttree"]):
+ if not 'O' in pkgstr.location:
+ return False
+ pp.uprint(pkgstr)
+
+ return True
+
def parse_module_options(module_opts):
- """Parse module options and update GLOBAL_OPTS"""
+ """Parse module options and update QUERY_OPTS"""
# Parse module options
opts = (x[0] for x in module_opts)
- for opt in opts:
+ posargs = (x[1] for x in module_opts)
+ for opt, posarg in zip(opts, posargs):
if opt in ('-h', '--help'):
print_help()
sys.exit(0)
elif opt in ('-I', '--exclue-installed'):
- QUERY_OPTS['includeInstalled'] = False
+ QUERY_OPTS['in_installed'] = False
elif opt in ('-p', '--portage-tree'):
- QUERY_OPTS['includePortTree'] = True
+ QUERY_OPTS['in_porttree'] = True
elif opt in ('-o', '--overlay-tree'):
- QUERY_OPTS['includeOverlayTree'] = True
+ QUERY_OPTS['in_overlay'] = True
+ elif opt in ('-F', '--format'):
+ QUERY_OPTS["package_format"] = posarg
def main(input_args):
"""Parse input and run the program"""
- short_opts = "hiIpo" # -i was option for default action
+ short_opts = "hiIpoF:" # -i was option for default action
# --installed is no longer needed, kept for compatibility (djanderson '09)
long_opts = ('help', 'installed', 'exclude-installed', 'portage-tree',
- 'overlay-tree')
+ 'overlay-tree', 'format=')
try:
module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
- except GetoptError, err:
- pp.print_error("Module %s" % err)
- print
+ except GetoptError as err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print()
print_help(with_description=False)
sys.exit(2)
@@ -100,7 +152,7 @@ def main(input_args):
print_help()
sys.exit(2)
- matches = do_lookup("*", QUERY_OPTS)
+ matches = Query("*").smart_find(**QUERY_OPTS)
matches.sort()
#
@@ -108,37 +160,21 @@ def main(input_args):
#
first_run = True
+ got_match = False
for query in queries:
if not first_run:
- print
+ print()
- if Config['verbose']:
- print " * Searching for USE flag %s ... " % pp.emph(query)
+ if CONFIG['verbose']:
+ pp.uprint(" * Searching for USE flag %s ... " % pp.emph(query))
for pkg in matches:
-
- useflags = [x.lstrip("+-") for x in pkg.get_env_var("IUSE").split()]
- if query not in useflags:
- continue
-
- if Config['verbose']:
- pkgstr = PackageFormatter(pkg, format=True)
- else:
- pkgstr = PackageFormatter(pkg, format=False)
-
- if (QUERY_OPTS["includeInstalled"] and
- not QUERY_OPTS["includePortTree"] and
- not QUERY_OPTS["includeOverlayTree"]):
- if not 'I' in pkgstr.location:
- continue
- if (QUERY_OPTS["includePortTree"] and
- not QUERY_OPTS["includeOverlayTree"]):
- if not 'P' in pkgstr.location:
- continue
- if (QUERY_OPTS["includeOverlayTree"] and
- not QUERY_OPTS["includePortTree"]):
- if not 'O' in pkgstr.location:
- continue
- print pkgstr
+ if display_useflags(query, pkg):
+ got_match = True
first_run = False
+
+ if not got_match:
+ sys.exit(1)
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/equery/keywords.py b/pym/gentoolkit/equery/keywords.py
new file mode 100644
index 0000000..be79fe3
--- /dev/null
+++ b/pym/gentoolkit/equery/keywords.py
@@ -0,0 +1,8 @@
+# vim:fileencoding=utf-8
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+from gentoolkit.eshowkw import main as emain
+
+def main(input_args):
+ emain(input_args, True) \ No newline at end of file
diff --git a/pym/gentoolkit/equery/list_.py b/pym/gentoolkit/equery/list_.py
index dd13029..941827f 100644
--- a/pym/gentoolkit/equery/list_.py
+++ b/pym/gentoolkit/equery/list_.py
@@ -6,6 +6,8 @@
"""List installed packages matching the query pattern"""
+from __future__ import print_function
+
__docformat__ = 'epytext'
# =======
@@ -17,23 +19,26 @@ from getopt import gnu_getopt, GetoptError
import gentoolkit
import gentoolkit.pprinter as pp
-from gentoolkit.equery import format_options, mod_usage, Config
-from gentoolkit.helpers2 import do_lookup, get_installed_cpvs
-from gentoolkit.package import Package, PackageFormatter
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import get_installed_cpvs
+from gentoolkit.helpers import get_bintree_cpvs
+from gentoolkit.package import PackageFormatter, FORMAT_TMPL_VARS
+from gentoolkit.query import Query
# =======
# Globals
# =======
QUERY_OPTS = {
- "categoryFilter": None,
"duplicates": False,
- "includeInstalled": True,
- "includePortTree": False,
- "includeOverlayTree": False,
- "includeMasked": True,
- "isRegex": False,
- "printMatchInfo": (not Config['quiet'])
+ "in_installed": True,
+ "in_porttree": False,
+ "in_overlay": False,
+ "include_mask_reason": False,
+ "is_regex": False,
+ "show_progress": (not CONFIG['quiet']),
+ "package_format": None,
+ "binpkgs-missing": False
}
# =========
@@ -42,34 +47,44 @@ QUERY_OPTS = {
def print_help(with_description=True):
"""Print description, usage and a detailed help message.
-
+
@type with_description: bool
@param with_description: if true, print module's __doc__ string
"""
if with_description:
- print __doc__.strip()
- print
- # Deprecation warning added 04/09: djanderson
- pp.print_warn("Default action for this module has changed in Gentoolkit 0.3.")
- pp.print_warn("-e, --exact-name is now the default behavior.")
- pp.print_warn("Use globbing to simulate the old behavior (see man equery).")
- pp.print_warn("Use '*' to check all installed packages.")
- print
-
- print mod_usage(mod_name="list")
- print
- print pp.command("options")
- print format_options((
+ print(__doc__.strip())
+ print()
+
+ # Deprecation warning added by djanderson, 12/2008
+ depwarning = (
+ "Default action for this module has changed in Gentoolkit 0.3.",
+ "Use globbing to simulate the old behavior (see man equery).",
+ "Use '*' to check all installed packages.",
+ "Use 'foo-bar/*' to filter by category."
+ )
+ for line in depwarning:
+ sys.stderr.write(pp.warn(line))
+ print()
+
+ print(mod_usage(mod_name="list"))
+ print()
+ print(pp.command("options"))
+ print(format_options((
(" -h, --help", "display this help message"),
- (" -c, --category CAT", "only search in the category CAT"),
(" -d, --duplicates", "list only installed duplicate packages"),
+ (" -b, --missing-binpkgs", "list only installed packages without a corresponding binary package"),
(" -f, --full-regex", "query is a regular expression"),
+ (" -m, --mask-reason", "include reason for package mask"),
(" -I, --exclude-installed",
"exclude installed packages from output"),
(" -o, --overlay-tree", "list packages in overlays"),
- (" -p, --portage-tree", "list packages in the main portage tree")
- ))
+ (" -p, --portage-tree", "list packages in the main portage tree"),
+ (" -F, --format=TMPL", "specify a custom output format"),
+ (" TMPL",
+ "a format template using (see man page):")
+ )))
+ print(" " * 24, ', '.join(pp.emph(x) for x in FORMAT_TMPL_VARS))
def get_duplicates(matches):
@@ -78,10 +93,10 @@ def get_duplicates(matches):
dups = {}
result = []
for pkg in matches:
- if pkg.key in dups:
- dups[pkg.key].append(pkg)
+ if pkg.cp in dups:
+ dups[pkg.cp].append(pkg)
else:
- dups[pkg.key] = [pkg]
+ dups[pkg.cp] = [pkg]
for cpv in dups.values():
if len(cpv) > 1:
@@ -90,8 +105,22 @@ def get_duplicates(matches):
return result
+def get_binpkgs_missing(matches):
+ """Return only packages that do not have a corresponding binary package."""
+
+ result = []
+ binary_packages = set(get_bintree_cpvs())
+ matched_packages = set(x.cpv for x in matches)
+ missing_binary_packages = set(matched_packages.difference(binary_packages))
+
+ for pkg in matches:
+ if pkg.cpv in missing_binary_packages:
+ result.append(pkg)
+ return result
+
+
def parse_module_options(module_opts):
- """Parse module options and update GLOBAL_OPTS"""
+ """Parse module options and update QUERY_OPTS"""
opts = (x[0] for x in module_opts)
posargs = (x[1] for x in module_opts)
@@ -99,68 +128,79 @@ def parse_module_options(module_opts):
if opt in ('-h', '--help'):
print_help()
sys.exit(0)
- elif opt in ('-a', '--all'):
- QUERY_OPTS['listAllPackages'] = True
- elif opt in ('-c', '--category'):
- QUERY_OPTS['categoryFilter'] = posarg
elif opt in ('-I', '--exclude-installed'):
- QUERY_OPTS['includeInstalled'] = False
+ QUERY_OPTS['in_installed'] = False
elif opt in ('-p', '--portage-tree'):
- QUERY_OPTS['includePortTree'] = True
+ QUERY_OPTS['in_porttree'] = True
elif opt in ('-o', '--overlay-tree'):
- QUERY_OPTS['includeOverlayTree'] = True
+ QUERY_OPTS['in_overlay'] = True
elif opt in ('-f', '--full-regex'):
- QUERY_OPTS['isRegex'] = True
+ QUERY_OPTS['is_regex'] = True
+ elif opt in ('-m', '--mask-reason'):
+ QUERY_OPTS['include_mask_reason'] = True
elif opt in ('-e', '--exact-name'):
- pp.print_warn("-e, --exact-name is now default.")
- pp.print_warn("Use globbing to simulate the old behavior.")
- print
+ sys.stderr.write(pp.warn("-e, --exact-name is now default."))
+ sys.stderr.write(
+ pp.warn("Use globbing to simulate the old behavior.")
+ )
+ print()
elif opt in ('-d', '--duplicates'):
QUERY_OPTS['duplicates'] = True
+ elif opt in ('-b', '--binpkgs-missing'):
+ QUERY_OPTS['binpkgs-missing'] = True
+ elif opt in ('-F', '--format'):
+ QUERY_OPTS["package_format"] = posarg
def main(input_args):
"""Parse input and run the program"""
- short_opts = "hc:defiIop" # -i, -e were options for default actions
+ short_opts = "hdbefiImopF:" # -i, -e were options for default actions
# 04/09: djanderson
+ # --all is no longer needed. Kept for compatibility.
# --installed is no longer needed. Kept for compatibility.
# --exact-name is no longer needed. Kept for compatibility.
- long_opts = ('help', 'all', 'category=', 'installed', 'exclude-installed',
- 'portage-tree', 'overlay-tree', 'full-regex', 'exact-name', 'duplicates')
+ long_opts = ('help', 'all', 'installed', 'exclude-installed',
+ 'mask-reason', 'portage-tree', 'overlay-tree', 'format=', 'full-regex',
+ 'exact-name', 'duplicates', 'binpkgs-missing')
try:
module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
- except GetoptError, err:
- pp.print_error("Module %s" % err)
- print
+ except GetoptError as err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print()
print_help(with_description=False)
sys.exit(2)
parse_module_options(module_opts)
- # Only search installed packages when listing duplicate packages
- if QUERY_OPTS["duplicates"]:
- QUERY_OPTS["includeInstalled"] = True
- QUERY_OPTS["includePortTree"] = False
- QUERY_OPTS["includeOverlayTree"] = False
+ # Only search installed packages when listing duplicate or missing binary packages
+ if QUERY_OPTS["duplicates"] or QUERY_OPTS["binpkgs-missing"]:
+ QUERY_OPTS["in_installed"] = True
+ QUERY_OPTS["in_porttree"] = False
+ QUERY_OPTS["in_overlay"] = False
+ QUERY_OPTS["include_mask_reason"] = False
if not queries:
print_help()
sys.exit(2)
first_run = True
- for query in queries:
+ for query in (Query(x, QUERY_OPTS['is_regex']) for x in queries):
if not first_run:
- print
+ print()
- matches = do_lookup(query, QUERY_OPTS)
+ matches = query.smart_find(**QUERY_OPTS)
# Find duplicate packages
if QUERY_OPTS["duplicates"]:
matches = get_duplicates(matches)
+ # Find missing binary packages
+ if QUERY_OPTS["binpkgs-missing"]:
+ matches = get_binpkgs_missing(matches)
+
matches.sort()
#
@@ -168,24 +208,45 @@ def main(input_args):
#
for pkg in matches:
- if Config['verbose']:
- pkgstr = PackageFormatter(pkg, format=True)
- else:
- pkgstr = PackageFormatter(pkg, format=False)
-
- if (QUERY_OPTS["includeInstalled"] and
- not QUERY_OPTS["includePortTree"] and
- not QUERY_OPTS["includeOverlayTree"]):
- if not 'I' in pkgstr.location:
- continue
- if (QUERY_OPTS["includePortTree"] and
- not QUERY_OPTS["includeOverlayTree"]):
+ pkgstr = PackageFormatter(
+ pkg,
+ do_format=CONFIG['verbose'],
+ custom_format=QUERY_OPTS["package_format"]
+ )
+
+ if (QUERY_OPTS["in_porttree"] and
+ not QUERY_OPTS["in_overlay"]):
if not 'P' in pkgstr.location:
continue
- if (QUERY_OPTS["includeOverlayTree"] and
- not QUERY_OPTS["includePortTree"]):
+ if (QUERY_OPTS["in_overlay"] and
+ not QUERY_OPTS["in_porttree"]):
if not 'O' in pkgstr.location:
continue
- print pkgstr
+ pp.uprint(pkgstr)
+
+ if QUERY_OPTS["include_mask_reason"]:
+ ms_int, ms_orig = pkgstr.format_mask_status()
+ if ms_int < 3:
+ # ms_int is a number representation of mask level.
+ # Only 2 and above are "hard masked" and have reasons.
+ continue
+ mask_reason = pkg.mask_reason()
+ if not mask_reason:
+ # Package not on system or not masked
+ continue
+ elif not any(mask_reason):
+ print(" * No mask reason given")
+ else:
+ status = ', '.join(ms_orig)
+ explanation = mask_reason[0]
+ mask_location = mask_reason[1]
+ pp.uprint(" * Masked by %r" % status)
+ pp.uprint(" * %s:" % mask_location)
+ pp.uprint('\n'.join(
+ [' * %s' % line.lstrip(' #')
+ for line in explanation.splitlines()]
+ ))
first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/equery/meta.py b/pym/gentoolkit/equery/meta.py
index d847f56..b67cbc6 100644
--- a/pym/gentoolkit/equery/meta.py
+++ b/pym/gentoolkit/equery/meta.py
@@ -4,32 +4,29 @@
#
# $Header: $
-"""Display metadata about a given package"""
+"""Display metadata about a given package."""
-# Move to Imports section after Python-2.6 is stable
-from __future__ import with_statement
+from __future__ import print_function
-__author__ = "Douglas Anderson"
__docformat__ = 'epytext'
# =======
# Imports
# =======
+import re
import os
-import re
import sys
-import xml.etree.cElementTree as ET
from getopt import gnu_getopt, GetoptError
-
-from portage import settings
+from functools import partial
import gentoolkit.pprinter as pp
from gentoolkit import errors
-from gentoolkit.equery import format_options, mod_usage, Config
-from gentoolkit.helpers2 import find_packages, print_sequence, print_file, \
- uniqify
+from gentoolkit import keyword
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import print_sequence, print_file
from gentoolkit.textwrap_ import TextWrapper
+from gentoolkit.query import Query
# =======
# Globals
@@ -40,112 +37,298 @@ from gentoolkit.textwrap_ import TextWrapper
# pylint: disable-msg=E1101
QUERY_OPTS = {
- "current": False,
- "description": False,
- "herd": False,
- "maintainer": False,
- "useflags": False,
- "upstream": False,
- "xml": False
-}
-
-# Get the location of the main Portage tree
-PORTDIR = [settings["PORTDIR"] or os.path.join(os.sep, "usr", "portage")]
-# Check for overlays
-if settings["PORTDIR_OVERLAY"]:
- PORTDIR.extend(settings["PORTDIR_OVERLAY"].split())
+ 'current': False,
+ 'description': False,
+ 'herd': False,
+ 'keywords': False,
+ 'maintainer': False,
+ 'useflags': False,
+ 'upstream': False,
+ 'xml': False
+}
# =========
# Functions
# =========
-def print_help(with_description=True):
+def print_help(with_description=True, with_usage=True):
"""Print description, usage and a detailed help message.
-
+
@type with_description: bool
@param with_description: if true, print module's __doc__ string
"""
if with_description:
- print __doc__.strip()
- print
- print mod_usage(mod_name="meta")
- print
- print pp.command("options")
- print format_options((
+ print(__doc__.strip())
+ print()
+ if with_usage:
+ print(mod_usage(mod_name="meta"))
+ print()
+ print(pp.command("options"))
+ print(format_options((
(" -h, --help", "display this help message"),
- (" -c, --current", "parse metadata.xml in the current directory"),
(" -d, --description", "show an extended package description"),
(" -H, --herd", "show the herd(s) for the package"),
+ (" -k, --keywords", "show keywords for all matching package versions"),
(" -m, --maintainer", "show the maintainer(s) for the package"),
(" -u, --useflags", "show per-package USE flag descriptions"),
(" -U, --upstream", "show package's upstream information"),
- (" -x, --xml", "show the plain XML file")
- ))
+ (" -x, --xml", "show the plain metadata.xml file")
+ )))
+
+
+def filter_keywords(matches):
+ """Filters non-unique keywords per slot.
+
+ Does not filter arch mask keywords (-). Besides simple non-unique keywords,
+ also remove unstable keywords (~) if a higher version in the same slot is
+ stable. This view makes version bumps easier for package maintainers.
+
+ @type matches: array
+ @param matches: set of L{gentoolkit.package.Package} instances whose
+ 'key' are all the same.
+ @rtype: dict
+ @return: a dict with L{gentoolkit.package.Package} instance keys and
+ 'array of keywords not found in a higher version of pkg within the
+ same slot' values.
+ """
+ def del_archmask(keywords):
+ """Don't add arch_masked to filter set."""
+ return [x for x in keywords if not x.startswith('-')]
+
+ def add_unstable(keywords):
+ """Add unstable keyword for all stable keywords to filter set."""
+ result = list(keywords)
+ result.extend(
+ ['~%s' % x for x in keywords if not x.startswith(('-', '~'))]
+ )
+ return result
+
+ result = {}
+ slot_map = {}
+ # Start from the newest
+ rev_matches = reversed(matches)
+ for pkg in rev_matches:
+ keywords_str, slot = pkg.environment(('KEYWORDS', 'SLOT'),
+ prefer_vdb=False)
+ keywords = keywords_str.split()
+ result[pkg] = [x for x in keywords if x not in slot_map.get(slot, [])]
+ try:
+ slot_map[slot].update(del_archmask(add_unstable(keywords)))
+ except KeyError:
+ slot_map[slot] = set(del_archmask(add_unstable(keywords)))
+
+ return result
+
+
+def format_herds(herds):
+ """Format herd information for display."""
+
+ result = []
+ for herd in herds:
+ herdstr = ''
+ email = "(%s)" % herd[1] if herd[1] else ''
+ herdstr = herd[0]
+ if CONFIG['verbose']:
+ herdstr += " %s" % (email,)
+ result.append(herdstr)
+
+ return result
+
+
+def format_maintainers(maints):
+ """Format maintainer information for display."""
+
+ result = []
+ for maint in maints:
+ maintstr = ''
+ maintstr = maint.email
+ if CONFIG['verbose']:
+ maintstr += " (%s)" % (maint.name,) if maint.name else ''
+ maintstr += " - %s" % (maint.restrict,) if maint.restrict else ''
+ maintstr += "\n%s" % (
+ (maint.description,) if maint.description else ''
+ )
+ result.append(maintstr)
+
+ return result
+
+
+def format_upstream(upstream):
+ """Format upstream information for display."""
+
+ def _format_upstream_docs(docs):
+ result = []
+ for doc in docs:
+ doc_location = doc[0]
+ doc_lang = doc[1]
+ docstr = doc_location
+ if doc_lang is not None:
+ docstr += " (%s)" % (doc_lang,)
+ result.append(docstr)
+ return result
+
+ def _format_upstream_ids(ids):
+ result = []
+ for id_ in ids:
+ site = id_[0]
+ proj_id = id_[1]
+ idstr = "%s ID: %s" % (site, proj_id)
+ result.append(idstr)
+ return result
+
+ result = []
+ for up in upstream:
+ upmaints = format_maintainers(up.maintainers)
+ for upmaint in upmaints:
+ result.append(format_line(upmaint, "Maintainer: ", " " * 13))
+
+ for upchange in up.changelogs:
+ result.append(format_line(upchange, "ChangeLog: ", " " * 13))
+
+ updocs = _format_upstream_docs(up.docs)
+ for updoc in updocs:
+ result.append(format_line(updoc, "Docs: ", " " * 13))
+
+ for upbug in up.bugtrackers:
+ result.append(format_line(upbug, "Bugs-to: ", " " * 13))
+
+ upids = _format_upstream_ids(up.remoteids)
+ for upid in upids:
+ result.append(format_line(upid, "Remote-ID: ", " " * 13))
+
+ return result
+
+
+def format_useflags(useflags):
+ """Format USE flag information for display."""
+
+ result = []
+ for flag in useflags:
+ result.append(pp.useflag(flag.name))
+ result.append(flag.description)
+ result.append("")
+
+ return result
-def call_get_functions(metadata_path, package_dir, QUERY_OPTS):
+def format_keywords(keywords):
+ """Sort and colorize keywords for display."""
+
+ result = []
+
+ for kw in sorted(keywords, keyword.compare_strs):
+ if kw.startswith('-'):
+ # arch masked
+ kw = pp.keyword(kw, stable=False, hard_masked=True)
+ elif kw.startswith('~'):
+ # keyword masked
+ kw = pp.keyword(kw, stable=False, hard_masked=False)
+ else:
+ # stable
+ kw = pp.keyword(kw, stable=True, hard_masked=False)
+ result.append(kw)
+
+ return ' '.join(result)
+
+
+def format_keywords_line(pkg, fmtd_keywords, slot, verstr_len):
+ """Format the entire keywords line for display."""
+
+ ver = pkg.fullversion
+ result = "%s:%s: %s" % (ver, pp.slot(slot), fmtd_keywords)
+ if CONFIG['verbose'] and fmtd_keywords:
+ result = format_line(fmtd_keywords, "%s:%s: " % (ver, pp.slot(slot)),
+ " " * (verstr_len + 2))
+
+ return result
+
+
+# R0912: *Too many branches (%s/%s)*
+# pylint: disable-msg=R0912
+def call_format_functions(best_match, matches):
"""Call information gathering functions and display the results."""
-
- if Config['verbose']:
- print get_overlay_name(package_dir)
- try:
- xml_tree = ET.parse(metadata_path)
- except IOError:
- pp.print_error("No metadata available")
- first_run = False
- return
+ if CONFIG['verbose']:
+ repo = best_match.repo_name()
+ pp.uprint(" * %s [%s]" % (pp.cpv(best_match.cp), pp.section(repo)))
got_opts = False
- if (QUERY_OPTS["herd"] or QUERY_OPTS["description"] or
- QUERY_OPTS["useflags"] or QUERY_OPTS["maintainer"] or
- QUERY_OPTS["upstream"] or QUERY_OPTS["xml"]):
+ if any(QUERY_OPTS.values()):
# Specific information requested, less formatting
got_opts = True
if QUERY_OPTS["herd"] or not got_opts:
- herd = get_herd(xml_tree)
+ herds = best_match.metadata.herds(include_email=True)
+ if any(not h[0] for h in herds):
+ print(pp.warn("The packages metadata.xml has an empty <herd> tag"),
+ file = sys.stderr)
+ herds = [x for x in herds if x[0]]
+ herds = format_herds(herds)
if QUERY_OPTS["herd"]:
- herd = format_list(herd)
+ print_sequence(format_list(herds))
else:
- herd = format_list(herd, "Herd: ", " " * 13)
- print_sequence(herd)
+ for herd in herds:
+ pp.uprint(format_line(herd, "Herd: ", " " * 13))
if QUERY_OPTS["maintainer"] or not got_opts:
- maint = get_maitainer(xml_tree)
+ maints = format_maintainers(best_match.metadata.maintainers())
if QUERY_OPTS["maintainer"]:
- maint = format_list(maint)
+ print_sequence(format_list(maints))
else:
- maint = format_list(maint, "Maintainer: ", " " * 13)
- print_sequence(maint)
+ if not maints:
+ pp.uprint(format_line([], "Maintainer: ", " " * 13))
+ else:
+ for maint in maints:
+ pp.uprint(format_line(maint, "Maintainer: ", " " * 13))
if QUERY_OPTS["upstream"] or not got_opts:
- upstream = get_upstream(xml_tree)
+ upstream = format_upstream(best_match.metadata.upstream())
if QUERY_OPTS["upstream"]:
upstream = format_list(upstream)
else:
upstream = format_list(upstream, "Upstream: ", " " * 13)
print_sequence(upstream)
+ if not got_opts:
+ pkg_loc = best_match.package_path()
+ pp.uprint(format_line(pkg_loc, "Location: ", " " * 13))
+
+ if QUERY_OPTS["keywords"] or not got_opts:
+ # Get {<Package 'dev-libs/glib-2.20.5'>: [u'ia64', u'm68k', ...], ...}
+ keyword_map = filter_keywords(matches)
+
+ for match in matches:
+ slot = match.environment('SLOT')
+ verstr_len = len(match.fullversion) + len(slot)
+ fmtd_keywords = format_keywords(keyword_map[match])
+ keywords_line = format_keywords_line(
+ match, fmtd_keywords, slot, verstr_len
+ )
+ if QUERY_OPTS["keywords"]:
+ pp.uprint(keywords_line)
+ else:
+ indent = " " * (16 + verstr_len)
+ pp.uprint(format_line(keywords_line, "Keywords: ", indent))
+
if QUERY_OPTS["description"]:
- desc = get_description(xml_tree)
+ desc = best_match.metadata.descriptions()
print_sequence(format_list(desc))
if QUERY_OPTS["useflags"]:
- useflags = get_useflags(xml_tree)
+ useflags = format_useflags(best_match.metadata.use())
print_sequence(format_list(useflags))
if QUERY_OPTS["xml"]:
- print_file(metadata_path)
+ print_file(os.path.join(best_match.package_path(), 'metadata.xml'))
def format_line(line, first="", subsequent="", force_quiet=False):
"""Wrap a string at word boundaries and optionally indent the first line
and/or subsequent lines with custom strings.
- Preserve newlines if the longest line is not longer than
- Config['termWidth']. To force the preservation of newlines and indents,
+ Preserve newlines if the longest line is not longer than
+ CONFIG['termWidth']. To force the preservation of newlines and indents,
split the string into a list and feed it to format_line via format_list.
@see: format_list()
@@ -161,7 +344,7 @@ def format_line(line, first="", subsequent="", force_quiet=False):
"""
if line:
- line = line.expandtabs().strip("\n").splitlines()
+ line = line.expandtabs().strip("\n").splitlines()
else:
if force_quiet:
return
@@ -172,18 +355,18 @@ def format_line(line, first="", subsequent="", force_quiet=False):
wider_indent = first
else:
wider_indent = subsequent
-
+
widest_line_len = len(max(line, key=len)) + len(wider_indent)
-
- if widest_line_len > Config['termWidth']:
- twrap = TextWrapper(width=Config['termWidth'], expand_tabs=False,
+
+ if widest_line_len > CONFIG['termWidth']:
+ twrap = TextWrapper(width=CONFIG['termWidth'], expand_tabs=False,
initial_indent=first, subsequent_indent=subsequent)
line = " ".join(line)
line = re.sub("\s+", " ", line)
line = line.lstrip()
result = twrap.fill(line)
else:
- # line will fit inside Config['termWidth'], so preserve whitespace and
+ # line will fit inside CONFIG['termWidth'], so preserve whitespace and
# newlines
line[0] = first + line[0] # Avoid two newlines if len == 1
@@ -198,7 +381,7 @@ def format_line(line, first="", subsequent="", force_quiet=False):
result = "".join(line)
- return result.encode("utf-8")
+ return result
def format_list(lst, first="", subsequent="", force_quiet=False):
@@ -212,7 +395,7 @@ def format_list(lst, first="", subsequent="", force_quiet=False):
@type subsequent: string
@param subsequent: text to prepend to subsequent lines
@rtype: list
- @return: list with element text wrapped at Config['termWidth']
+ @return: list with element text wrapped at CONFIG['termWidth']
"""
result = []
@@ -229,7 +412,7 @@ def format_list(lst, first="", subsequent="", force_quiet=False):
# We don't want to send a blank line to format_line()
result.append("")
else:
- if Config['verbose']:
+ if CONFIG['verbose']:
if force_quiet:
result = None
else:
@@ -239,238 +422,22 @@ def format_list(lst, first="", subsequent="", force_quiet=False):
return result
-def get_herd(xml_tree):
- """Return a list of text nodes for <herd>."""
-
- result = []
- for elem in xml_tree.findall("herd"):
- herd_mail = get_herd_email(elem.text)
- if herd_mail and Config['verbose']:
- result.append("%s (%s)" % (elem.text, herd_mail))
- else:
- result.append(elem.text)
-
- return result
-
-
-def get_herd_email(herd):
- """Return the email of the given herd if it's in herds.xml, else None."""
-
- herds_path = os.path.join(PORTDIR[0], "metadata/herds.xml")
-
- try:
- herds_tree = ET.parse(herds_path)
- except IOError, err:
- pp.print_error(str(err))
- return None
-
- # Some special herds are not listed in herds.xml
- if herd in ('no-herd', 'maintainer-wanted', 'maintainer-needed'):
- return None
-
- for node in herds_tree.getiterator("herd"):
- if node.findtext("name") == herd:
- return node.findtext("email")
-
-
-def get_description(xml_tree):
- """Return a list of text nodes for <longdescription>.
-
- @todo: Support the `lang' attribute
- """
-
- return [e.text for e in xml_tree.findall("longdescription")]
-
-
-def get_maitainer(xml_tree):
- """Return a parsable tree of all maintainer elements and sub-elements."""
-
- first_run = True
- result = []
- for node in xml_tree.findall("maintainer"):
- if not first_run:
- result.append("")
- restrict = node.get("restrict")
- if restrict:
- result.append("(%s %s)" %
- (pp.emph("Restrict to"), pp.output.green(restrict)))
- result.extend(e.text for e in node)
- first_run = False
-
- return result
-
-
-def get_overlay_name(p_dir):
- """Determine the overlay name and return a formatted string."""
-
- result = []
- cat_pkg = '/'.join(p_dir.split('/')[-2:])
- result.append(" * %s" % pp.cpv(cat_pkg))
- o_dir = '/'.join(p_dir.split('/')[:-2])
- if o_dir != PORTDIR[0]:
- # o_dir is an overlay
- o_name = o_dir.split('/')[-1]
- o_name = ("[", o_name, "]")
- result.append(pp.output.turquoise("".join(o_name)))
-
- return ' '.join(result)
-
-
-def get_package_directory(query):
- """Find a package's portage directory."""
-
- matches = find_packages(query, include_masked=True)
- # Prefer a package that's in the Portage tree over one in an
- # overlay. Start with oldest first.
- pkg = None
- while list(reversed(matches)):
- pkg = matches.pop()
- if not pkg.is_overlay():
- break
-
- return pkg.get_package_path() if pkg else None
-
-
-def get_useflags(xml_tree):
- """Return a list of formatted <useflag> lines, including blank elements
- where blank lines should be printed."""
-
- first_run = True
- result = []
- for node in xml_tree.getiterator("flag"):
- if not first_run:
- result.append("")
- flagline = pp.useflag(node.get("name"))
- restrict = node.get("restrict")
- if restrict:
- result.append("%s (%s %s)" %
- (flagline, pp.emph("Restrict to"), pp.output.green(restrict)))
- else:
- result.append(flagline)
- # ElementTree handles nested element text in a funky way.
- # So we need to dump the raw XML and parse it manually.
- flagxml = ET.tostring(node)
- flagxml = re.sub("\s+", " ", flagxml)
- flagxml = re.sub("\n\t", "", flagxml)
- flagxml = re.sub("<(pkg|cat)>(.*?)</(pkg|cat)>",
- pp.cpv(r"\2"), flagxml)
- flagtext = re.sub("<.*?>", "", flagxml)
- result.append(flagtext)
- first_run = False
-
- return result
-
-
-def _get_upstream_bugtracker(node):
- """Extract and format upstream bugtracker information."""
-
- bt_loc = [e.text for e in node.findall("bugs-to")]
-
- return format_list(bt_loc, "Bugs to: ", " " * 12, force_quiet=True)
-
-
-def _get_upstream_changelog(node):
- """Extract and format upstream changelog information."""
-
- cl_paths = [e.text for e in node.findall("changelog")]
-
- return format_list(cl_paths, "Changelog: ", " " * 12, force_quiet=True)
-
-
-def _get_upstream_documentation(node):
- """Extract and format upstream documentation information."""
-
- doc = []
- for elem in node.findall("doc"):
- lang = elem.get("lang")
- if lang:
- lang = "(%s)" % pp.output.yellow(lang)
- else:
- lang = ""
- doc.append(" ".join([elem.text, lang]))
-
- return format_list(doc, "Docs: ", " " * 12, force_quiet=True)
-
-
-def _get_upstream_maintainer(node):
- """Extract and format upstream maintainer information."""
-
- maintainer = node.findall("maintainer")
- maint = []
- for elem in maintainer:
- if elem.find("name") != None:
- maint.append(elem.find("name").text)
- if elem.find("email") != None:
- maint.append(elem.find("email").text)
- if elem.get("status") == "active":
- maint.append("(%s)" % pp.output.green("active"))
- elif elem.get("status") == "inactive":
- maint.append("(%s)" % pp.output.red("inactive"))
- elif elem.get("status") != None:
- maint.append("(" + elem.get("status") + ")")
-
- return format_list(maint, "Maintainer: ", " " * 12, force_quiet=True)
-
-
-def _get_upstream_remoteid(node):
- """Extract and format upstream remote ID."""
-
- r_id = [e.get("type") + ": " + e.text for e in node.findall("remote-id")]
-
- return format_list(r_id, "Remote ID: ", " " * 12, force_quiet=True)
-
-
-def get_upstream(xml_tree):
- """Return a list of formatted <upstream> lines, including blank elements
- where blank lines should be printed."""
-
- first_run = True
- result = []
- for node in xml_tree.findall("upstream"):
- if not first_run:
- result.append("")
-
- maint = _get_upstream_maintainer(node)
- if maint:
- result.append("\n".join(maint))
-
- changelog = _get_upstream_changelog(node)
- if changelog:
- result.append("\n".join(changelog))
-
- documentation = _get_upstream_documentation(node)
- if documentation:
- result.append("\n".join(documentation))
-
- bugs_to = _get_upstream_bugtracker(node)
- if bugs_to:
- result.append("\n".join(bugs_to))
-
- remote_id = _get_upstream_remoteid(node)
- if remote_id:
- result.append("\n".join(remote_id))
-
- first_run = False
-
- return result
-
-
def parse_module_options(module_opts):
- """Parse module options and update GLOBAL_OPTS"""
+ """Parse module options and update QUERY_OPTS"""
opts = (x[0] for x in module_opts)
for opt in opts:
if opt in ('-h', '--help'):
print_help()
sys.exit(0)
- elif opt in ('-c', '--current'):
- QUERY_OPTS["current"] = True
elif opt in ('-d', '--description'):
QUERY_OPTS["description"] = True
elif opt in ('-H', '--herd'):
QUERY_OPTS["herd"] = True
elif opt in ('-m', '--maintainer'):
QUERY_OPTS["maintainer"] = True
+ elif opt in ('-k', '--keywords'):
+ QUERY_OPTS["keywords"] = True
elif opt in ('-u', '--useflags'):
QUERY_OPTS["useflags"] = True
elif opt in ('-U', '--upstream'):
@@ -482,44 +449,44 @@ def parse_module_options(module_opts):
def main(input_args):
"""Parse input and run the program."""
- short_opts = "hcdHmuUx"
- long_opts = ('help', 'current', 'description', 'herd', 'maintainer',
+ short_opts = "hdHkmuUx"
+ long_opts = ('help', 'description', 'herd', 'keywords', 'maintainer',
'useflags', 'upstream', 'xml')
try:
module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
- except GetoptError, err:
- pp.print_error("Module %s" % err)
- print
+ except GetoptError as err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print()
print_help(with_description=False)
sys.exit(2)
parse_module_options(module_opts)
-
+
# Find queries' Portage directory and throw error if invalid
- if not queries and not QUERY_OPTS["current"]:
+ if not queries:
print_help()
sys.exit(2)
-
- if QUERY_OPTS["current"]:
- package_dir = os.getcwd()
- metadata_path = os.path.join(package_dir, "metadata.xml")
- call_get_functions(metadata_path, package_dir, QUERY_OPTS)
- else:
- first_run = True
- for query in queries:
- package_dir = get_package_directory(query)
- if not package_dir:
- raise errors.GentoolkitNoMatches(query)
- metadata_path = os.path.join(package_dir, "metadata.xml")
-
- # --------------------------------
- # Check options and call functions
- # --------------------------------
-
- if not first_run:
- print
-
- call_get_functions(metadata_path, package_dir, QUERY_OPTS)
-
- first_run = False
+
+ first_run = True
+ for query in (Query(x) for x in queries):
+ best_match = query.find_best()
+ matches = query.find(include_masked=True)
+ if best_match is None or not matches:
+ raise errors.GentoolkitNoMatches(query)
+
+ if best_match.metadata is None:
+ print(pp.warn("Package {0} is missing "
+ "metadata.xml".format(best_match.cpv)),
+ file = sys.stderr)
+ continue
+
+ if not first_run:
+ print()
+
+ matches.sort()
+ call_format_functions(best_match, matches)
+
+ first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/equery/size.py b/pym/gentoolkit/equery/size.py
index 9cb6bc9..4151767 100644
--- a/pym/gentoolkit/equery/size.py
+++ b/pym/gentoolkit/equery/size.py
@@ -6,6 +6,8 @@
"""Print total size of files contained in a given package"""
+from __future__ import print_function
+
__docformat__ = 'epytext'
# =======
@@ -16,23 +18,21 @@ import sys
from getopt import gnu_getopt, GetoptError
import gentoolkit.pprinter as pp
-from gentoolkit.equery import format_options, mod_usage, Config
-from gentoolkit.helpers2 import do_lookup
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.query import Query
# =======
# Globals
# =======
QUERY_OPTS = {
- "categoryFilter": None,
- "includeInstalled": False,
- "includePortTree": False,
- "includeOverlayTree": False,
- "includeMasked": True,
- "isRegex": False,
- "matchExact": False,
- "printMatchInfo": False,
- "sizeInBytes": False
+ "in_installed": True,
+ "in_porttree": False,
+ "in_overlay": False,
+ "include_masked": True,
+ "is_regex": False,
+ "show_progress": False,
+ "size_in_bytes": False
}
# =========
@@ -41,31 +41,34 @@ QUERY_OPTS = {
def print_help(with_description=True):
"""Print description, usage and a detailed help message.
-
+
@type with_description: bool
@param with_description: if true, print module's __doc__ string
"""
if with_description:
- print __doc__.strip()
- print
-
- # Deprecation warning added 04/09: djanderson
- pp.print_warn("Default action for this module has changed in Gentoolkit 0.3.")
- pp.print_warn("-e, --exact-name is now the default behavior.")
- pp.print_warn("Use globbing to simulate the old behavior (see man equery).")
- pp.print_warn("Use '*' to check all installed packages.")
- print
-
- print mod_usage(mod_name="size")
- print
- print pp.command("options")
- print format_options((
+ print(__doc__.strip())
+ print()
+
+ # Deprecation warning added by djanderson, 12/2008
+ depwarning = (
+ "Default action for this module has changed in Gentoolkit 0.3.",
+ "Use globbing to simulate the old behavior (see man equery).",
+ "Use '*' to check all installed packages.",
+ "Use 'foo-bar/*' to filter by category."
+ )
+ for line in depwarning:
+ sys.stderr.write(pp.warn(line))
+ print()
+
+ print(mod_usage(mod_name="size"))
+ print()
+ print(pp.command("options"))
+ print(format_options((
(" -h, --help", "display this help message"),
(" -b, --bytes", "report size in bytes"),
- (" -c, --category CAT", "only search in the category CAT"),
(" -f, --full-regex", "query is a regular expression")
- ))
+ )))
def display_size(match_set):
@@ -76,25 +79,25 @@ def display_size(match_set):
"""
for pkg in match_set:
- (size, files, uncounted) = pkg.size()
+ size, files, uncounted = pkg.size()
- if Config['verbose']:
- print " * %s" % pp.cpv(pkg.cpv)
- print "Total files : %s".rjust(25) % pp.number(str(files))
+ if CONFIG['verbose']:
+ pp.uprint(" * %s" % pp.cpv(str(pkg.cpv)))
+ print("Total files : %s".rjust(25) % pp.number(str(files)))
if uncounted:
- pp.print_info(0, "Inaccessible files : %s".rjust(25) %
- pp.number(str(uncounted)))
+ print(("Inaccessible files : %s".rjust(25) %
+ pp.number(str(uncounted))))
- if QUERY_OPTS["sizeInBytes"]:
+ if QUERY_OPTS["size_in_bytes"]:
size_str = pp.number(str(size))
else:
size_str = "%s %s" % format_bytes(size)
- pp.print_info(0, "Total size : %s".rjust(25) % size_str)
+ print("Total size : %s".rjust(25) % size_str)
else:
info = "%s: total(%d), inaccessible(%d), size(%s)"
- print info % (pkg.cpv, files, uncounted, size)
+ pp.uprint(info % (str(pkg.cpv), files, uncounted, size))
def format_bytes(bytes_, precision=2):
@@ -106,10 +109,10 @@ def format_bytes(bytes_, precision=2):
"""
labels = (
- (1<<40L, 'TiB'),
- (1<<30L, 'GiB'),
- (1<<20L, 'MiB'),
- (1<<10L, 'KiB'),
+ (1<<40, 'TiB'),
+ (1<<30, 'GiB'),
+ (1<<20, 'MiB'),
+ (1<<10, 'KiB'),
(1, 'bytes')
)
@@ -134,24 +137,22 @@ def format_bytes(bytes_, precision=2):
def parse_module_options(module_opts):
- """Parse module options and update GLOBAL_OPTS"""
+ """Parse module options and update QUERY_OPTS"""
opts = (x[0] for x in module_opts)
- posargs = (x[1] for x in module_opts)
- for opt, posarg in zip(opts, posargs):
+ for opt in opts:
if opt in ('-h', '--help'):
print_help()
sys.exit(0)
elif opt in ('-b', '--bytes'):
- QUERY_OPTS["sizeInBytes"] = True
- elif opt in ('-c', '--category'):
- QUERY_OPTS['categoryFilter'] = posarg
+ QUERY_OPTS["size_in_bytes"] = True
elif opt in ('-e', '--exact-name'):
- pp.print_warn("-e, --exact-name is now default.")
- pp.print_warn("Use globbing to simulate the old behavior.")
- print
+ sys.stderr.write(pp.warn("-e, --exact-name is now default."))
+ warning = pp.warn("Use globbing to simulate the old behavior.")
+ sys.stderr.write(warning)
+ print()
elif opt in ('-f', '--full-regex'):
- QUERY_OPTS['isRegex'] = True
+ QUERY_OPTS['is_regex'] = True
def main(input_args):
@@ -159,41 +160,37 @@ def main(input_args):
# -e, --exact-name is no longer needed. Kept for compatibility.
# 04/09 djanderson
- short_opts = "hbc:fe"
- long_opts = ('help', 'bytes', 'category=', 'full-regex', 'exact-name')
+ short_opts = "hbfe"
+ long_opts = ('help', 'bytes', 'full-regex', 'exact-name')
try:
module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
- except GetoptError, err:
- pp.print_error("Module %s" % err)
- print
+ except GetoptError as err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print()
print_help(with_description=False)
sys.exit(2)
parse_module_options(module_opts)
-
- if not queries and not QUERY_OPTS["includeInstalled"]:
+
+ if not queries:
print_help()
sys.exit(2)
- elif queries and not QUERY_OPTS["includeInstalled"]:
- QUERY_OPTS["includeInstalled"] = True
- elif QUERY_OPTS["includeInstalled"]:
- queries = ["*"]
-
- #
- # Output
- #
first_run = True
- for query in queries:
+ for query in (Query(x, QUERY_OPTS['is_regex']) for x in queries):
if not first_run:
- print
+ print()
- matches = do_lookup(query, QUERY_OPTS)
+ matches = query.smart_find(**QUERY_OPTS)
if not matches:
- pp.print_error("No package found matching %s" % query)
+ sys.stderr.write(pp.error("No package found matching %s" % query))
+
+ matches.sort()
display_size(matches)
first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/equery/uses.py b/pym/gentoolkit/equery/uses.py
index 56fc78b..08ff585 100644
--- a/pym/gentoolkit/equery/uses.py
+++ b/pym/gentoolkit/equery/uses.py
@@ -6,8 +6,7 @@
"""Display USE flags for a given package"""
-# Move to imports section when Python 2.6 is stable
-from __future__ import with_statement
+from __future__ import print_function
__docformat__ = 'epytext'
@@ -16,27 +15,26 @@ __docformat__ = 'epytext'
# =======
import os
-import re
import sys
+from functools import partial
from getopt import gnu_getopt, GetoptError
from glob import glob
-import xml.etree.cElementTree as ET
-from portage.util import unique_array
+from portage import settings
-import gentoolkit
import gentoolkit.pprinter as pp
from gentoolkit import errors
-from gentoolkit.equery import format_options, mod_usage, Config
-from gentoolkit.helpers2 import compare_package_strings, find_best_match, \
- find_packages
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import uniqify
from gentoolkit.textwrap_ import TextWrapper
+from gentoolkit.query import Query
+from gentoolkit.flag import get_flags, reduce_flags
# =======
# Globals
# =======
-QUERY_OPTS = {"allVersions" : False}
+QUERY_OPTS = {"all_versions" : False}
# =========
# Functions
@@ -44,21 +42,21 @@ QUERY_OPTS = {"allVersions" : False}
def print_help(with_description=True):
"""Print description, usage and a detailed help message.
-
+
@type with_description: bool
@param with_description: if true, print module's __doc__ string
"""
if with_description:
- print __doc__.strip()
- print
- print mod_usage(mod_name=__name__.split('.')[-1])
- print
- print pp.command("options")
- print format_options((
+ print(__doc__.strip())
+ print()
+ print(mod_usage(mod_name=__name__.split('.')[-1]))
+ print()
+ print(pp.command("options"))
+ print(format_options((
(" -h, --help", "display this help message"),
(" -a, --all", "include all package versions")
- ))
+ )))
def display_useflags(output):
@@ -66,29 +64,31 @@ def display_useflags(output):
@type output: list
@param output: [(inuse, inused, flag, desc, restrict), ...]
- inuse (int) = 0 or 1; if 1, flag is set in make.conf
- inused (int) = 0 or 1; if 1, package is installed with flag enabled
- flag (str) = the name of the USE flag
- desc (str) = the flag's description
- restrict (str) = corresponds to the text of restrict in metadata
+ inuse (int) = 0 or 1; if 1, flag is set in make.conf
+ inused (int) = 0 or 1; if 1, package is installed with flag enabled
+ flag (str) = the name of the USE flag
+ desc (str) = the flag's description
+ restrict (str) = corresponds to the text of restrict in metadata
"""
maxflag_len = len(max([t[2] for t in output], key=len))
twrap = TextWrapper()
- twrap.width = Config['termWidth']
+ twrap.width = CONFIG['termWidth']
twrap.subsequent_indent = " " * (maxflag_len + 8)
markers = ("-", "+")
- color = [pp.useflagoff, pp.useflagon]
+ color = (
+ partial(pp.useflag, enabled=False), partial(pp.useflag, enabled=True)
+ )
for in_makeconf, in_installed, flag, desc, restrict in output:
- if Config['verbose']:
+ if CONFIG['verbose']:
flag_name = ""
if in_makeconf != in_installed:
- flag_name += pp.emph(" %s %s" %
+ flag_name += pp.emph(" %s %s" %
(markers[in_makeconf], markers[in_installed]))
else:
- flag_name += (" %s %s" %
+ flag_name += (" %s %s" %
(markers[in_makeconf], markers[in_installed]))
flag_name += " " + color[in_makeconf](flag.ljust(maxflag_len))
@@ -96,25 +96,25 @@ def display_useflags(output):
# print description
if restrict:
- restrict = "(%s %s)" % (pp.emph("Restricted to"),
+ restrict = "(%s %s)" % (pp.emph("Restricted to"),
pp.cpv(restrict))
twrap.initial_indent = flag_name
- print twrap.fill(restrict)
+ pp.uprint(twrap.fill(restrict))
if desc:
twrap.initial_indent = twrap.subsequent_indent
- print twrap.fill(desc)
+ pp.uprint(twrap.fill(desc))
else:
- print " : <unknown>"
+ print(" : <unknown>")
else:
if desc:
twrap.initial_indent = flag_name
desc = twrap.fill(desc)
- print desc
+ pp.uprint(desc)
else:
twrap.initial_indent = flag_name
- print twrap.fill("<unknown>")
+ print(twrap.fill("<unknown>"))
else:
- print markers[in_makeconf] + flag
+ pp.uprint(markers[in_makeconf] + flag)
def get_global_useflags():
@@ -128,8 +128,7 @@ def get_global_useflags():
global_usedesc = {}
# Get global USE flag descriptions
try:
- path = os.path.join(gentoolkit.settings["PORTDIR"], 'profiles',
- 'use.desc')
+ path = os.path.join(settings["PORTDIR"], 'profiles', 'use.desc')
with open(path) as open_file:
for line in open_file:
if line.startswith('#'):
@@ -139,13 +138,16 @@ def get_global_useflags():
if len(fields) == 2:
global_usedesc[fields[0]] = fields[1].rstrip()
except IOError:
- pp.print_warn("Could not load USE flag descriptions from %s" %
- pp.path(path))
+ sys.stderr.write(
+ pp.warn(
+ "Could not load USE flag descriptions from %s" % pp.path(path)
+ )
+ )
del path, open_file
# Add USE_EXPANDED variables to usedesc hash -- Bug #238005
- for path in glob(os.path.join(gentoolkit.settings["PORTDIR"],
- 'profiles', 'desc', '*.desc')):
+ for path in glob(os.path.join(settings["PORTDIR"],
+ 'profiles', 'desc', '*.desc')):
try:
with open(path) as open_file:
for line in open_file:
@@ -157,113 +159,69 @@ def get_global_useflags():
(path.split("/")[-1][0:-5], fields[0])
global_usedesc[expanded_useflag] = fields[1]
except IOError:
- pp.print_warn("Could not load USE flag descriptions from %s" %
- path)
+ sys.stderr.write(
+ pp.warn("Could not load USE flag descriptions from %s" % path)
+ )
return global_usedesc
-def get_local_useflags(pkg):
- """Parse package-specific flag descriptions from a package's metadata.xml.
-
- @see: http://www.gentoo.org/proj/en/glep/glep-0056.html
- @type pkg: gentoolkit.package.Package
- @param pkg: the package to find useflags for
- @rtype: dict
- @return: {string: tuple}
- string = flag's name
- tuple = (description, restrictions)
- """
-
- result = {}
-
- metadata = os.path.join(pkg.get_package_path(), 'metadata.xml')
- try:
- xml_tree = ET.parse(metadata)
- except IOError:
- pp.print_error("Could not open %s" % metadata)
- return result
-
- for node in xml_tree.getiterator("flag"):
- name = node.get("name")
- restrict = node.get("restrict")
- # ElementTree handles nested element text in a funky way.
- # So we need to dump the raw XML and parse it manually.
- flagxml = ET.tostring(node)
- flagxml = re.sub("\s+", " ", flagxml)
- flagxml = re.sub("\n\t", "", flagxml)
- flagxml = re.sub("<(pkg|cat)>([^<]*)</(pkg|cat)>",
- pp.cpv("%s" % r"\2"), flagxml)
- flagtext = re.sub("<.*?>", "", flagxml)
- result[name] = (flagtext, restrict)
-
- return result
-
-
-def get_matches(query):
- """Get packages matching query."""
-
- if not QUERY_OPTS["allVersions"]:
- matches = [find_best_match(query)]
- if None in matches:
- matches = find_packages(query, include_masked=False)
- if matches:
- matches = sorted(matches, compare_package_strings)[-1:]
- else:
- matches = find_packages(query, include_masked=True)
-
- if not matches:
- raise errors.GentoolkitNoMatches(query)
-
- return matches
-
-
def get_output_descriptions(pkg, global_usedesc):
"""Prepare descriptions and usage information for each USE flag."""
- local_usedesc = get_local_useflags(pkg)
- iuse = pkg.get_env_var("IUSE")
-
- if iuse:
- usevar = unique_array([x.lstrip('+-') for x in iuse.split()])
- usevar.sort()
+ if pkg.metadata is None:
+ local_usedesc = []
else:
- usevar = []
+ local_usedesc = pkg.metadata.use()
+
+ iuse, final_use = get_flags(pkg.cpv, final_setting=True)
+ usevar = reduce_flags(iuse)
+ usevar.sort()
+
if pkg.is_installed():
- used_flags = pkg.get_use_flags().split()
+ used_flags = pkg.use().split()
else:
- used_flags = gentoolkit.settings["USE"].split()
+ used_flags = settings["USE"].split()
# store (inuse, inused, flag, desc, restrict)
output = []
for flag in usevar:
inuse = False
inused = False
+
+ local_use = None
+ for use in local_usedesc:
+ if use.name == flag:
+ local_use = use
+ break
+
try:
- desc = local_usedesc[flag][0]
- except KeyError:
+ desc = local_use.description
+ except AttributeError:
try:
desc = global_usedesc[flag]
except KeyError:
desc = ""
+
try:
- restrict = local_usedesc[flag][1]
- except KeyError:
+ restrict = local_use.restrict
+ restrict = restrict if restrict is not None else ""
+ except AttributeError:
restrict = ""
- if flag in pkg.get_settings("USE").split():
+ if flag in final_use:
inuse = True
if flag in used_flags:
inused = True
output.append((inuse, inused, flag, desc, restrict))
-
+
return output
def parse_module_options(module_opts):
- """Parse module options and update GLOBAL_OPTS"""
+ """Parse module options and update QUERY_OPTS"""
opts = (x[0] for x in module_opts)
for opt in opts:
@@ -271,16 +229,16 @@ def parse_module_options(module_opts):
print_help()
sys.exit(0)
elif opt in ('-a', '--all'):
- QUERY_OPTS['allVersions'] = True
+ QUERY_OPTS['all_versions'] = True
-def print_legend(query):
+def print_legend():
"""Print a legend to explain the output format."""
- print "[ Legend : %s - flag is set in make.conf ]" % pp.emph("U")
- print "[ : %s - package is installed with flag ]" % pp.emph("I")
- print "[ Colors : %s, %s ]" % (
- pp.useflagon("set"), pp.useflagoff("unset"))
+ print("[ Legend : %s - final flag setting for installation]" % pp.emph("U"))
+ print("[ : %s - package is installed with flag ]" % pp.emph("I"))
+ print("[ Colors : %s, %s ]" % (
+ pp.useflag("set", enabled=True), pp.useflag("unset", enabled=False)))
def main(input_args):
@@ -291,9 +249,9 @@ def main(input_args):
try:
module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
- except GetoptError, err:
- pp.print_error("Module %s" % err)
- print
+ except GetoptError as err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print()
print_help(with_description=False)
sys.exit(2)
@@ -308,14 +266,19 @@ def main(input_args):
#
first_run = True
- for query in queries:
+ legend_printed = False
+ for query in (Query(x) for x in queries):
if not first_run:
- print
+ print()
- if Config['verbose']:
- print " * Searching for %s ..." % pp.pkgquery(query)
+ if QUERY_OPTS["all_versions"]:
+ matches = query.find(include_masked=True)
+ else:
+ matches = [query.find_best()]
+
+ if not any(matches):
+ raise errors.GentoolkitNoMatches(query)
- matches = get_matches(query)
matches.sort()
global_usedesc = get_global_useflags()
@@ -323,15 +286,20 @@ def main(input_args):
output = get_output_descriptions(pkg, global_usedesc)
if output:
- if Config['verbose']:
- print_legend(query)
- print (" * Found these USE flags for %s:" %
- pp.cpv(pkg.cpv))
- print pp.emph(" U I")
+ if CONFIG['verbose']:
+ if not legend_printed:
+ print_legend()
+ legend_printed = True
+ print((" * Found these USE flags for %s:" %
+ pp.cpv(str(pkg.cpv))))
+ print(pp.emph(" U I"))
display_useflags(output)
else:
- if Config['verbose']:
- pp.print_warn("No USE flags found for %s" %
- pp.cpv(pkg.cpv))
+ if CONFIG['verbose']:
+ sys.stderr.write(
+ pp.warn("No USE flags found for %s" % pp.cpv(pkg.cpv))
+ )
first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/equery/which.py b/pym/gentoolkit/equery/which.py
index 828dae1..5259d46 100644
--- a/pym/gentoolkit/equery/which.py
+++ b/pym/gentoolkit/equery/which.py
@@ -8,6 +8,8 @@
configuration
"""
+from __future__ import print_function
+
__docformat__ = 'epytext'
# =======
@@ -18,16 +20,17 @@ import os
import sys
from getopt import gnu_getopt, GetoptError
+
import gentoolkit.pprinter as pp
from gentoolkit import errors
from gentoolkit.equery import format_options, mod_usage
-from gentoolkit.helpers2 import find_packages
+from gentoolkit.query import Query
# =======
# Globals
# =======
-QUERY_OPTS = {"includeMasked": False}
+QUERY_OPTS = {"include_masked": False}
# =========
# Functions
@@ -35,25 +38,25 @@ QUERY_OPTS = {"includeMasked": False}
def print_help(with_description=True):
"""Print description, usage and a detailed help message.
-
+
@type with_description: bool
@param with_description: if true, print module's __doc__ string
"""
if with_description:
- print __doc__.strip()
- print
- print mod_usage(mod_name="which")
- print
- print pp.command("options")
- print format_options((
+ print(__doc__.strip())
+ print()
+ print(mod_usage(mod_name="which"))
+ print()
+ print(pp.command("options"))
+ print(format_options((
(" -h, --help", "display this help message"),
(" -m, --include-masked", "return highest version ebuild available")
- ))
+ )))
def parse_module_options(module_opts):
- """Parse module options and update GLOBAL_OPTS"""
+ """Parse module options and update QUERY_OPTS"""
opts = (x[0] for x in module_opts)
for opt in opts:
@@ -61,7 +64,7 @@ def parse_module_options(module_opts):
print_help()
sys.exit(0)
elif opt in ('-m', '--include-masked'):
- QUERY_OPTS['includeMasked'] = True
+ QUERY_OPTS['include_masked'] = True
def main(input_args):
@@ -72,9 +75,9 @@ def main(input_args):
try:
module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
- except GetoptError, err:
- pp.print_error("Module %s" % err)
- print
+ except GetoptError as err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print()
print_help(with_description=False)
sys.exit(2)
@@ -84,15 +87,21 @@ def main(input_args):
print_help()
sys.exit(2)
- for query in queries:
-
- matches = find_packages(query, QUERY_OPTS['includeMasked'])
+ for query in (Query(x) for x in queries):
+ matches = query.find(
+ include_masked=QUERY_OPTS['include_masked'],
+ in_installed=False
+ )
if matches:
pkg = sorted(matches).pop()
- ebuild_path = pkg.get_ebuild_path()
+ ebuild_path = pkg.ebuild_path()
if ebuild_path:
- print os.path.normpath(ebuild_path)
+ pp.uprint(os.path.normpath(ebuild_path))
else:
- pp.print_warn("No ebuilds to satisfy %s" % pkg.name)
+ sys.stderr.write(
+ pp.warn("No ebuilds to satisfy %s" % pkg.cpv)
+ )
else:
raise errors.GentoolkitNoMatches(query)
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/errors.py b/pym/gentoolkit/errors.py
index 9843b6b..152f660 100644
--- a/pym/gentoolkit/errors.py
+++ b/pym/gentoolkit/errors.py
@@ -4,97 +4,145 @@
"""Exception classes for gentoolkit"""
-__all__ = [
- 'FatalError',
+__all__ = (
'GentoolkitException',
+ 'GentoolkitFatalError',
+ 'GentoolkitAmbiguousPackage',
'GentoolkitInvalidAtom',
'GentoolkitInvalidCategory',
- 'GentoolkitInvalidPackageName',
+ 'GentoolkitInvalidPackage',
'GentoolkitInvalidCPV',
'GentoolkitInvalidRegex',
'GentoolkitInvalidVersion',
- 'GentoolkitNoMatches'
-]
-
-# =======
-# Imports
-# =======
-
-import sys
-
-import gentoolkit.pprinter as pp
+ 'GentoolkitNoMatches',
+ 'GentoolkitSetNotFound',
+ 'GentoolkitUnknownKeyword'
+)
# ==========
# Exceptions
# ==========
class GentoolkitException(Exception):
- """Base class for gentoolkit exceptions"""
- def __init__(self):
- pass
+ """Base class for gentoolkit exceptions."""
+ def __init__(self, is_serious=True):
+ self.is_serious = is_serious
class GentoolkitFatalError(GentoolkitException):
"""A fatal error occurred. Usually used to catch Portage exceptions."""
- def __init__(self, err):
- pp.print_error("Fatal error: %s" % err)
- sys.exit(2)
+ def __init__(self, err, is_serious=True):
+ GentoolkitException.__init__(self, is_serious=is_serious)
+ self.err = err
+
+ def __str__(self):
+ return "Fatal error: %s" % self.err
+
+
+class GentoolkitAmbiguousPackage(GentoolkitException):
+ """Got an ambiguous package name."""
+ def __init__(self, choices, is_serious=False):
+ GentoolkitException.__init__(self, is_serious=is_serious)
+ self.choices = choices
+
+ def __str__(self):
+ choices = '\n'.join(" %s" % x for x in self.choices)
+ return '\n'.join(("Ambiguous package name. Choose from:", choices))
class GentoolkitInvalidAtom(GentoolkitException):
- """Got a malformed package atom"""
- def __init__(self, atom):
- pp.print_error("Invalid atom: '%s'" % atom)
- sys.exit(2)
+ """Got a malformed package atom."""
+ def __init__(self, atom, is_serious=False):
+ GentoolkitException.__init__(self, is_serious=is_serious)
+ self.atom = atom
+
+ def __str__(self):
+ return "Invalid atom: '%s'" % self.atom
+
+
+class GentoolkitSetNotFound(GentoolkitException):
+ """Got unknown set."""
+ def __init__(self, setname, is_serious=False):
+ GentoolkitException.__init__(self, is_serious=is_serious)
+ self.setname = setname
+
+ def __str__(self):
+ return "Unknown set: '%s'" % self.setname
class GentoolkitInvalidCategory(GentoolkitException):
- """The category was not listed in portage.settings.categories"""
- def __init__(self, category):
- pp.print_error("Invalid category: '%s'" % category)
- if not category:
- pp.print_error("Try --category=cat1,cat2 with no spaces.")
- sys.exit(2)
+ """The category was not listed in portage.settings.categories."""
+ def __init__(self, category, is_serious=False):
+ GentoolkitException.__init__(self, is_serious=is_serious)
+ self.category = category
+
+ def __str__(self):
+ return "Invalid category: '%s'" % self.category
-class GentoolkitInvalidPackageName(GentoolkitException):
- """Got an unknown package name"""
- def __init__(self, package):
- pp.print_error("Invalid package name: '%s'" % package)
- sys.exit(2)
+class GentoolkitInvalidPackage(GentoolkitException):
+ """Got an unknown or invalid package."""
+ def __init__(self, package, is_serious=False):
+ GentoolkitException.__init__(self, is_serious=is_serious)
+ self.package = package
+
+ def __str__(self):
+ return "Invalid package: '%s'" % self.package
class GentoolkitInvalidCPV(GentoolkitException):
- """Got an unknown package name"""
- def __init__(self, cpv):
- pp.print_error("Invalid CPV: '%s'" % cpv)
- sys.exit(2)
+ """Got an invalid category/package-ver string."""
+ def __init__(self, cpv, is_serious=False):
+ GentoolkitException.__init__(self, is_serious=is_serious)
+ self.cpv = cpv
+
+ def __str__(self):
+ return "Invalid CPV: '%s'" % self.cpv
class GentoolkitInvalidRegex(GentoolkitException):
- """The regex could not be compiled"""
- def __init__(self, regex):
- pp.print_error("Invalid regex: '%s'" % regex)
- sys.exit(2)
+ """The regex could not be compiled."""
+ def __init__(self, regex, is_serious=False):
+ GentoolkitException.__init__(self, is_serious=is_serious)
+ self.regex = regex
+
+ def __str__(self):
+ return "Invalid regex: '%s'" % self.regex
class GentoolkitInvalidVersion(GentoolkitException):
- """Got a malformed version"""
- def __init__(self, version):
- pp.print_error("Malformed version: '%s'" % version)
- sys.exit(2)
+ """Got a malformed version."""
+ def __init__(self, version, is_serious=False):
+ GentoolkitException.__init__(self, is_serious=is_serious)
+ self.version = version
+
+ def __str__(self):
+ return "Malformed version: '%s'" % self.version
class GentoolkitNoMatches(GentoolkitException):
- """No packages were found matching the search query"""
- def __init__(self, query):
- pp.print_error("No packages matching '%s'" % query)
- sys.exit(2)
-
-
-# XXX: Deprecated
-class FatalError:
- def __init__(self, s):
- self._message = s
- def get_message(self):
- return self._message
+ """No packages were found matching the search query."""
+ def __init__(self, query, in_installed=False, is_serious=False):
+ GentoolkitException.__init__(self, is_serious=is_serious)
+ self.query = query
+ self.in_installed = in_installed
+
+ def __str__(self):
+ inst = 'installed ' if self.in_installed else ''
+ return "No %spackages matching '%s'" % (inst, self.query)
+
+
+class GentoolkitUnknownKeyword(GentoolkitException):
+ """No packages were found matching the search query."""
+ def __init__(self, query, keywords, use, is_serious=True):
+ GentoolkitException.__init__(self, is_serious=is_serious)
+ self.query = query
+ self.keywords = keywords
+ self.use = use
+
+ def __str__(self):
+ return ("Unable to determine the install keyword for:\n" +
+ "'%s', KEYWORDS = '%s'\nUSE flags = '%s'"
+ % (self.query, self.keywords, self.use))
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/eshowkw/__init__.py b/pym/gentoolkit/eshowkw/__init__.py
new file mode 100644
index 0000000..d5087a4
--- /dev/null
+++ b/pym/gentoolkit/eshowkw/__init__.py
@@ -0,0 +1,130 @@
+# vim:fileencoding=utf-8
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+__package__ = 'gentoolkit.eshowkw'
+__version__ = 'svn'
+__author__ = "Tomáš Chvátal <scarabeus@gentoo.org>"
+
+import sys, os, fnmatch
+import argparse
+from portage import output as porto
+from portage import settings as ports
+from portage import config as portc
+from portage import portdbapi as portdbapi
+from portage import db as portdb
+
+from .keywords_header import keywords_header
+from .keywords_content import keywords_content
+from .display_pretty import string_rotator
+from .display_pretty import display
+
+ignore_slots = False
+bold = False
+order = 'bottom'
+topper = 'versionlist'
+
+def process_display(package, keywords, dbapi):
+ portdata = keywords_content(package, keywords.keywords, dbapi, ignore_slots, order, bold, topper)
+ if topper == 'archlist':
+ header = string_rotator().rotateContent(keywords.content, keywords.length, bold)
+ extra = string_rotator().rotateContent(keywords.extra, keywords.length, bold, False)
+ # -1 : space is taken in account and appended by us
+ filler = ''.ljust(portdata.slot_length-1)
+ header = ['%s%s%s' % (x, filler, y) for x, y in zip(header, extra)]
+ content = portdata.content
+ header_length = portdata.version_length
+ content_length = keywords.length
+ else:
+ header = string_rotator().rotateContent(portdata.content, portdata.content_length, bold)
+ content = keywords.content
+ sep = [''.ljust(keywords.length) for x in range(portdata.slot_length-1)]
+ content.extend(sep)
+ content.extend(keywords.extra)
+ header_length = keywords.length
+ content_length = portdata.version_length
+ display(content, header, header_length, content_length, portdata.cp, topper)
+
+def process_args(argv):
+ """Option parsing via argc"""
+ parser = argparse.ArgumentParser(prog=__package__,
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ description='Display keywords for specified package or for package that is in pwd.')
+
+ parser.add_argument('-v', '--version', action='version', version=__version__, help='show package version and exit')
+
+ parser.add_argument('package', nargs='*', default=None, help='Packages to check.')
+
+ parser.add_argument('-a', '--arch', nargs='+', default=[], help='Display only specified arch(s)')
+
+ parser.add_argument('-A', '--align', nargs='?', default='bottom', choices=['top', 'bottom'],
+ help='Specify alignment for descriptions.')
+ parser.add_argument('-T', '--top-position', nargs='?', default='archlist', choices=['archlist', 'versionlist'],
+ help='Specify which fields we want to have in top listing.')
+
+ parser.add_argument('-B', '--bold', action='store_true', default=False,
+ help='Print out each other column in bold for easier visual separation.')
+ parser.add_argument('-C', '--color', action='store_true', default=False,
+ help='Force colored output')
+ parser.add_argument('-O', '--overlays', action='store_true', default=False,
+ help='Search also overlays')
+ parser.add_argument('-P', '--prefix', action='store_true', default=False,
+ help='Display prefix keywords in output.')
+ parser.add_argument('-S', '--ignore-slot', action='store_true', default=False,
+ help='Treat slots as irelevant during detection of redundant pacakges.')
+
+ return parser.parse_args(args=argv)
+
+def main(argv, indirect = False):
+ global ignore_slots, bold, order, topper
+
+ #opts parsing
+ opts = process_args(argv)
+ ignore_slots = opts.ignore_slot
+ use_overlays = opts.overlays
+ # user can do both --arch=a,b,c or --arch a b c
+ if len(opts.arch) > 1:
+ opts.arch = ','.join(opts.arch)
+ highlight_arch = ''.join(opts.arch).split(',')
+ bold = opts.bold
+ order = opts.align
+ topper = opts.top_position
+ prefix = opts.prefix
+ color = opts.color
+ package = opts.package
+
+ # equery support
+ if indirect and len(package) <= 0:
+ msg_err = 'No packages specified'
+ raise SystemExit(msg_err)
+
+ # disable colors when redirected and they are not forced on
+ if not color and not sys.stdout.isatty():
+ # disable colors
+ porto.nocolor()
+ keywords = keywords_header(prefix, highlight_arch, order)
+ if len(package) > 0:
+ dbapi = portdb[ports['ROOT']]['porttree'].dbapi
+ if not use_overlays:
+ dbapi.porttrees = [dbapi.porttree_root]
+ map(lambda x: process_display(x, keywords, dbapi), package)
+ else:
+ currdir = os.getcwd()
+ # check if there are actualy some ebuilds
+ ebuilds = ['%s' % x for x in os.listdir(currdir)
+ if fnmatch.fnmatch(x, '*.ebuild')]
+ if len(ebuilds) <= 0:
+ msg_err = 'No ebuilds at "%s"' % currdir
+ raise SystemExit(msg_err)
+ package= '%s/%s' % (os.path.basename(os.path.abspath('../')), os.path.basename(currdir))
+ ourtree = os.path.abspath('../../')
+ overlays = '%s %s' % (ports['PORTDIR_OVERLAY'], ourtree)
+ mysettings = portc(env={'PORTDIR_OVERLAY': overlays})
+ dbapi = portdbapi(mysettings=mysettings)
+ # specify that we want just our nice tree we are in cwd
+ dbapi.porttrees = [ourtree]
+ process_display(package, keywords, dbapi)
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/pym/gentoolkit/eshowkw/display_pretty.py b/pym/gentoolkit/eshowkw/display_pretty.py
new file mode 100644
index 0000000..270a0eb
--- /dev/null
+++ b/pym/gentoolkit/eshowkw/display_pretty.py
@@ -0,0 +1,103 @@
+# vim:fileencoding=utf-8
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+from portage.output import colorize
+from itertools import izip_longest
+
+__all__ = ['string_rotator', 'colorize_string', 'align_string', 'rotate_dash', 'print_content', 'display']
+
+def display(plain_list, rotated_list, plain_width, rotated_height, cp, toplist = 'archlist'):
+ """Render defauld display to show the keywords listing"""
+ # header
+ output = []
+ output.append('Keywords for %s:' % colorize('blue', cp))
+ # data
+ corner_image = [''.ljust(plain_width) for x in range(rotated_height)]
+ if toplist != 'archlist':
+ corner_image.extend(plain_list)
+ data_printout = ['%s%s' % (x, y)
+ for x, y in izip_longest(corner_image, rotated_list, fillvalue=corner_image[0])]
+ if toplist == 'archlist':
+ data_printout.extend(plain_list)
+ output.extend(data_printout)
+ print(print_content(output))
+
+def align_string(string, align, length):
+ """Align string to the specified alignment (left or right, and after rotation it becames top and bottom)"""
+ if align == 'top' or align == 'left':
+ string = string.ljust(length)
+ else:
+ string = string.rjust(length)
+ return string
+
+def colorize_string(color, string):
+ """Add coloring for specified string. Due to rotation we need to do that per character rather than per-line"""
+ tmp = []
+ for char in list(string):
+ # % is whitespace separator so we wont color that :)
+ if char != '%':
+ tmp.append(colorize(color, char))
+ else:
+ tmp.append(char)
+ return ''.join(tmp)
+
+def rotate_dash(string):
+ """Rotate special strings over 90 degrees for better readability."""
+ chars = ['-', '|']
+ subs = ['|', '-']
+ out = string
+ for x,y in zip(chars, subs):
+ if string.find(x) != -1:
+ out = out.replace(x, y)
+ return out
+
+def print_content(content):
+ """Print out content (strip it out of the temporary %)"""
+ return '\n'.join(content).replace('%','')
+
+class string_rotator:
+ __DASH_COUNT = 0
+ def __getChar(self, string, position, line, bold_separator = False):
+ """Return specified character from the string position"""
+
+ # first figure out what character we want to work with
+ # based on order and position in the string
+ isdash = False
+ if string.startswith('|') or string.startswith('-') or string.startswith('+'):
+ split = list(string)
+ isdash = True
+ self.__DASH_COUNT += 1
+ else:
+ split = string.split('%')
+ char = split[position]
+ # bolding
+ if not isdash and bold_separator \
+ and (line-self.__DASH_COUNT)%2 == 0 \
+ and char != ' ':
+ char = colorize('bold', char)
+ return char
+
+ def rotateContent(self, elements, length, bold_separator = False, strip = True):
+ """
+ Rotate string over 90 degrees:
+ string -> s
+ t
+ r
+ i
+ n
+ g
+ """
+ # join used to have list of lines rather than list of chars
+ tmp = []
+ for position in range(length):
+ x = ''
+ for i, string in enumerate(elements):
+ x += ' ' + self.__getChar(rotate_dash(string), position, i, bold_separator)
+ # spaces on dashed line should be dashed too
+ if x.find('+ -') != -1:
+ x = x.replace(' ', '-')
+ # strip all chars and remove empty lines
+ if not strip or len(x.strip(' |-')) > 0:
+ tmp.append(x)
+ return tmp
diff --git a/pym/gentoolkit/eshowkw/keywords_content.py b/pym/gentoolkit/eshowkw/keywords_content.py
new file mode 100644
index 0000000..4524b30
--- /dev/null
+++ b/pym/gentoolkit/eshowkw/keywords_content.py
@@ -0,0 +1,325 @@
+# vim:fileencoding=utf-8
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+import portage as port
+import os
+from portage.output import colorize
+
+__all__ = ['keywords_content']
+
+from display_pretty import colorize_string
+from display_pretty import align_string
+
+class keywords_content:
+ class RedundancyChecker:
+ def __listRedundant(self, keywords, ignoreslots, slots):
+ """List all redundant packages."""
+ if ignoreslots:
+ return self.__listRedundantAll(keywords)
+ else:
+ return self.__listRedundantSlots(keywords, slots)
+
+ def __listRedundantSlots(self, keywords, slots):
+ """Search for redundant packages walking per keywords for specified slot."""
+ result = [self.__compareSelected([k for k, s in zip(keywords, slots)
+ if s == slot])
+ for slot in self.__uniq(slots)]
+ # this is required because the list itself is not just one level depth
+ return list(''.join(result))
+
+ def __uniq(self, seq):
+ """Remove all duplicate elements from list."""
+ seen = {}
+ result = []
+ for item in seq:
+ if item in seen:
+ continue
+ seen[item] = 1
+ result.append(item)
+ return result
+
+ def __cleanKeyword(self, keyword):
+ """Remove masked arches and hardmasks from keywords since we don't care about that."""
+ return ["%s" % x for x in keyword.split()
+ if x != '-*' and not x.startswith('-')]
+
+ def __listRedundantAll(self, keywords):
+ """Search for redundant packages using all versions ignoring its slotting."""
+ return list(self.__compareSelected(list(keywords)))
+
+ def __compareSelected(self, kws):
+ """
+ Rotate over list of keywords and compare each element with others.
+ Selectively remove each already compared list from the remaining keywords.
+ """
+ result = []
+ kws.reverse()
+ for i in range(len(kws)):
+ kw = kws.pop()
+ if self.__compareKeywordWithRest(kw, kws):
+ result.append('#')
+ else:
+ result.append('o')
+ if len(result) == 0:
+ result.append('o')
+ return ''.join(result)
+
+ def __compareKeywordWithRest(self, keyword, keywords):
+ """Compare keywords with list of keywords."""
+ kw = self.__cleanKeyword(keyword)
+ for kwi in keywords:
+ kwi = self.__cleanKeyword(kwi)
+ if kwi:
+ kw = self.__checkShadow(kw, kwi)
+ if not kw:
+ return True
+ return False
+
+ def __checkShadow(self, old, new):
+ """Check if package version is overshadowed by other package version."""
+ tmp = set(new)
+ tmp.update("~%s" % x for x in new
+ if not x.startswith("~"))
+ return list(set(old).difference(tmp))
+
+ def __init__(self, keywords, slots, ignore_slots = False):
+ """Query all relevant data for redundancy package checking"""
+ self.redundant = self.__listRedundant(keywords, ignore_slots, slots)
+
+ class VersionChecker:
+ def __getVersions(self, packages):
+ """Obtain properly aligned version strings without colors."""
+ return map(lambda x: self.__separateVersion(x), packages)
+
+ def __separateVersion(self, cpv):
+ """Get version string for specfied cpv"""
+ #pv = port.versions.cpv_getversion(cpv)
+ return self.__prependVersionInfo(cpv, self.cpv_getversion(cpv))
+
+ # remove me when portage 2.1.9 is stable
+ def cpv_getversion(self, mycpv):
+ """Returns the v (including revision) from an cpv."""
+ cp = port.versions.cpv_getkey(mycpv)
+ if cp is None:
+ return None
+ return mycpv[len(cp+"-"):]
+
+ def __prependVersionInfo(self, cpv, pv):
+ """Prefix version with string based on whether version is installed or masked."""
+ mask = self.__getMaskStatus(cpv)
+ install = self.__getInstallStatus(cpv)
+
+ if mask and install:
+ pv = '[M][I]%s' % pv
+ elif mask:
+ pv = '[M]%s' % pv
+ elif install:
+ pv = '[I]%s' % pv
+ return pv
+
+ def __getMaskStatus(self, cpv):
+ """
+ Figure out if package is pmasked.
+ This also uses user settings in /etc/ so local changes are important.
+ """
+ try:
+ if port.getmaskingstatus(cpv) == ['package.mask']:
+ return True
+ except:
+ # occurs when package is not known by portdb
+ # so we consider it unmasked
+ pass
+ return False
+
+ def __getInstallStatus(self, cpv):
+ """Check if package version we test is installed."""
+ vartree = port.db[port.settings['ROOT']]['vartree'].dbapi
+ return vartree.cpv_exists(cpv)
+
+ def __init__(self, packages):
+ """Query all relevant data for version data formatting"""
+ self.versions = self.__getVersions(packages)
+
+ def __packages_sort(self, package_content):
+ """
+ Sort packages queried based on version and slot
+ %% pn , repo, slot, keywords
+ """
+ from operator import itemgetter
+
+ if len(package_content) > 1:
+ ver_map = {}
+ for cpv in package_content:
+ ver_map[cpv[0]] = '-'.join(port.versions.catpkgsplit(cpv[0])[2:])
+ def cmp_cpv(cpv1, cpv2):
+ return port.versions.vercmp(ver_map[cpv1[0]], ver_map[cpv2[0]])
+
+ package_content.sort(key=port.util.cmp_sort_key(cmp_cpv))
+ package_content.sort(key=itemgetter(2))
+
+ def __xmatch(self, pdb, package):
+ """xmatch function that searches for all packages over all repos"""
+ try:
+ mycp = port.dep_expand(package, mydb=pdb, settings=pdb.settings).cp
+ except port.exception.AmbiguousPackageName as Arg:
+ msg_err = 'Ambiguous package name "%s".\n' % package
+ found = 'Possibilities: %s' % Arg
+ raise SystemExit('%s%s' % (msg_err, found))
+ except port.exception.InvalidAtom:
+ msg_err = 'No such package "%s"' % package
+ raise SystemExit(msg_err)
+
+ mysplit = mycp.split('/')
+ mypkgs = []
+ for oroot in pdb.porttrees:
+ try:
+ file_list = os.listdir(os.path.join(oroot, mycp))
+ except OSError:
+ continue
+ for x in file_list:
+ pf = x[:-7] if x[-7:] == '.ebuild' else []
+ if pf:
+ ps = port.pkgsplit(pf)
+ if not ps or ps[0] != mysplit[1]:
+ # we got garbage or ebuild with wrong name in the dir
+ continue
+ ver_match = port.versions.ver_regexp.match("-".join(ps[1:]))
+ if ver_match is None or not ver_match.groups():
+ # version is not allowed by portage or unset
+ continue
+ # obtain related data from metadata and append to the pkg list
+ keywords, slot = self.__getMetadata(pdb, mysplit[0]+'/'+pf, oroot)
+ mypkgs.append([mysplit[0]+'/'+pf, oroot, slot, keywords])
+
+ self.__packages_sort(mypkgs)
+ return mypkgs
+
+ def __checkExist(self, pdb, package):
+ """Check if specified package even exists."""
+ matches = self.__xmatch(pdb, package)
+ if len(matches) <= 0:
+ msg_err = 'No such package "%s"' % package
+ raise SystemExit(msg_err)
+ return list(zip(*matches))
+
+ def __getMetadata(self, pdb, package, repo):
+ """Obtain all required metadata from portage auxdb"""
+ try:
+ metadata = pdb.aux_get(package, ['KEYWORDS', 'SLOT'], repo)
+ except KeyError:
+ # portage prints out more verbose error for us if we were lucky
+ raise SystemExit('Failed to obtain metadata')
+ return metadata
+
+ def __formatKeywords(self, keywords, keywords_list, usebold = False, toplist = 'archlist'):
+ """Loop over all keywords and replace them with nice visual identifier"""
+ # the % is fancy separator, we use it to split keywords for rotation
+ # so we wont loose the empty spaces
+ return ['% %'.join([self.__prepareKeywordChar(arch, i, version.split(), usebold, toplist)
+ for i, arch in enumerate(keywords_list)])
+ for version in keywords]
+
+ def __prepareKeywordChar(self, arch, field, keywords, usebold = False, toplist = 'archlist'):
+ """
+ Convert specified keywords for package into their visual replacements.
+ # possibilities:
+ # ~arch -> orange ~
+ # -arch -> red -
+ # arch -> green +
+ # -* -> red *
+ """
+ keys = [ '~%s' % arch, '-%s' % arch, '%s' % arch, '-*' ]
+ values = [
+ colorize('darkyellow', '~'),
+ colorize('darkred', '-'),
+ colorize('darkgreen', '+'),
+ colorize('darkred', '*')
+ ]
+ # check what keyword we have
+ # here we cant just append space because it would get stripped later
+ char = colorize('darkgray','o')
+ for k, v in zip(keys, values):
+ if k in keywords:
+ char = v
+ break
+ if toplist == 'archlist' and usebold and (field)%2 == 0 and char != ' ':
+ char = colorize('bold', char)
+ return char
+
+ def __formatVersions(self, versions, align, length):
+ """Append colors and align keywords properly"""
+ # % are used as separators for further split so we wont loose spaces and coloring
+ tmp = []
+ for pv in versions:
+ pv = align_string(pv, align, length)
+ pv = '%'.join(list(pv))
+ if pv.find('[%M%][%I%]') != -1:
+ tmp.append(colorize_string('darkyellow', pv))
+ elif pv.find('[%M%]') != -1:
+ tmp.append(colorize_string('darkred', pv))
+ elif pv.find('[%I%]') != -1:
+ tmp.append(colorize_string('bold', pv))
+ else:
+ tmp.append(pv)
+ return tmp
+
+ def __formatAdditional(self, additional, color, length):
+ """Align additional items properly"""
+ # % are used as separators for further split so we wont loose spaces and coloring
+ tmp = []
+ for x in additional:
+ tmpc = color
+ x = align_string(x, 'left', length)
+ x = '%'.join(list(x))
+ if x == 'o':
+ # the value is unset so the color is gray
+ tmpc = 'darkgray'
+ x = colorize_string(tmpc, x)
+ tmp.append(x)
+ return tmp
+
+ def __prepareContentResult(self, versions, keywords, redundant, slots, slot_length, repos, linesep):
+ """Parse version fields into one list with proper separators"""
+ content = []
+ oldslot = ''
+ fieldsep = '% %|% %'
+ normsep = '% %'
+ for v, k, r, s, t in zip(versions, keywords, redundant, slots, repos):
+ if oldslot != s:
+ oldslot = s
+ content.append(linesep)
+ else:
+ s = '%'.join(list(''.rjust(slot_length)))
+ content.append('%s%s%s%s%s%s%s%s%s' % (v, fieldsep, k, fieldsep, r, normsep, s, fieldsep, t))
+ return content
+
+ def __init__(self, package, keywords_list, porttree, ignoreslots = False, content_align = 'bottom', usebold = False, toplist = 'archlist'):
+ """Query all relevant data from portage databases."""
+ packages, self.repositories, self.slots, self.keywords = self.__checkExist(porttree, package)
+ # convert repositories from path to name
+ self.repositories = [porttree.getRepositoryName(x) for x in self.repositories]
+ self.slot_length = max([len(x) for x in self.slots])
+ repositories_length = max([len(x) for x in self.repositories])
+ self.keyword_length = len(keywords_list)
+ self.versions = self.VersionChecker(packages).versions
+ self.version_length = max([len(x) for x in self.versions])
+ self.version_count = len(self.versions)
+ self.redundant = self.RedundancyChecker(self.keywords, self.slots, ignoreslots).redundant
+ redundant_length = max([len(x) for x in self.redundant])
+
+ ver = self.__formatVersions(self.versions, content_align, self.version_length)
+ kws = self.__formatKeywords(self.keywords, keywords_list, usebold, toplist)
+ red = self.__formatAdditional(self.redundant, 'purple', redundant_length)
+ slt = self.__formatAdditional(self.slots, 'bold', self.slot_length)
+ rep = self.__formatAdditional(self.repositories, 'yellow', repositories_length)
+ # those + nubers are spaces in printout. keywords are multiplied also because of that
+ linesep = '%s+%s+%s+%s' % (''.ljust(self.version_length+1, '-'),
+ ''.ljust(self.keyword_length*2+1, '-'),
+ ''.ljust(redundant_length+self.slot_length+3, '-'),
+ ''.ljust(repositories_length+1, '-')
+ )
+
+ self.content = self.__prepareContentResult(ver, kws, red, slt, self.slot_length, rep, linesep)
+ self.content_length = len(linesep)
+ self.cp = port.cpv_getkey(packages[0])
diff --git a/pym/gentoolkit/eshowkw/keywords_header.py b/pym/gentoolkit/eshowkw/keywords_header.py
new file mode 100644
index 0000000..1d52749
--- /dev/null
+++ b/pym/gentoolkit/eshowkw/keywords_header.py
@@ -0,0 +1,100 @@
+# vim:fileencoding=utf-8
+# Copyright 2001-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+__all__ = ['keywords_header']
+
+from portage import settings as ports
+from portage.output import colorize
+from display_pretty import colorize_string
+from display_pretty import align_string
+
+class keywords_header:
+ __IMPARCHS = [ 'arm', 'amd64', 'x86' ]
+ __ADDITIONAL_FIELDS = [ 'unused', 'slot' ]
+ __EXTRA_FIELDS = [ 'repo' ]
+
+ def __readKeywords(self):
+ """Read all available keywords from portage."""
+ return [x for x in ports.archlist()
+ if not x.startswith('~')]
+
+ def __sortKeywords(self, keywords, prefix = False, required_keywords = []):
+ """Sort keywords with short archs first"""
+ # user specified only some keywords to display
+ if len(required_keywords) != 0:
+ tmpkeywords = [k for k in keywords
+ if k in required_keywords]
+ # idiots might specify non-existant archs
+ if len(tmpkeywords) != 0:
+ keywords = tmpkeywords
+
+ normal = [k for k in keywords
+ if len(k.split('-')) == 1]
+ normal.sort()
+
+ if prefix:
+ longer = [k for k in keywords
+ if len(k.split('-')) != 1]
+ longer.sort()
+ normal.extend(longer)
+ return normal
+
+ def __readAdditionalFields(self):
+ """Prepare list of aditional fileds displayed by eshowkw (2nd part)"""
+ return self.__ADDITIONAL_FIELDS
+
+ def __readExtraFields(self):
+ """Prepare list of extra fileds displayed by eshowkw (3rd part)"""
+ return self.__EXTRA_FIELDS
+
+ def __formatKeywords(self, keywords, align, length):
+ """Append colors and align keywords properly"""
+ tmp = []
+ for keyword in keywords:
+ tmp2 = keyword
+ keyword = align_string(keyword, align, length)
+ # % are used as separators for further split so we wont loose spaces and coloring
+ keyword = '%'.join(list(keyword))
+ if tmp2 in self.__IMPARCHS:
+ tmp.append(colorize_string('darkyellow', keyword))
+ else:
+ tmp.append(keyword)
+ return tmp
+
+ def __formatAdditional(self, additional, align, length):
+ """Align additional items properly"""
+ # % are used as separators for further split so we wont loose spaces and coloring
+ return ['%'.join(align_string(x, align, length)) for x in additional]
+
+ def __prepareExtra(self, extra, align, length):
+ content = []
+ content.append(''.ljust(length, '-'))
+ content.extend(self.__formatAdditional(extra, align, length))
+ return content
+
+ def __prepareResult(self, keywords, additional, align, length):
+ """Parse keywords and additional fields into one list with proper separators"""
+ content = []
+ content.append(''.ljust(length, '-'))
+ content.extend(self.__formatKeywords(keywords, align, length))
+ content.append(''.ljust(length, '-'))
+ content.extend(self.__formatAdditional(additional, align, length))
+ return content
+
+ def __init__(self, prefix = False, required_keywords = [], keywords_align = 'bottom'):
+ """Initialize keywords header."""
+ additional = self.__readAdditionalFields()
+ extra = self.__readExtraFields()
+ self.keywords = self.__sortKeywords(self.__readKeywords(), prefix, required_keywords)
+ self.length = max(
+ max([len(x) for x in self.keywords]),
+ max([len(x) for x in additional]),
+ max([len(x) for x in extra])
+ )
+ #len(max([max(self.keywords, key=len), max(additional, key=len)], key=len))
+ self.keywords_count = len(self.keywords)
+ self.additional_count = len(additional)
+ self.extra_count = len(extra)
+ self.content = self.__prepareResult(self.keywords, additional, keywords_align, self.length)
+ self.extra = self.__prepareExtra(extra, keywords_align, self.length)
diff --git a/pym/gentoolkit/flag.py b/pym/gentoolkit/flag.py
new file mode 100644
index 0000000..a7d944f
--- /dev/null
+++ b/pym/gentoolkit/flag.py
@@ -0,0 +1,165 @@
+#!/usr/bin/python
+#
+# Copyright(c) 2010, Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+
+
+"""Provides support functions for USE flag settings and analysis"""
+
+
+__all__ = (
+ 'get_iuse',
+ 'get_installed_use',
+ 'reduce_flag',
+ 'reduce_flags',
+ 'filter_flags',
+ 'get_all_cpv_use',
+ 'get_flags'
+)
+
+
+import sys
+
+from gentoolkit.dbapi import PORTDB, VARDB
+
+import portage
+
+
+def get_iuse(cpv):
+ """Gets the current IUSE flags from the tree
+
+ To be used when a gentoolkit package object is not needed
+ @type: cpv: string
+ @param cpv: cat/pkg-ver
+ @rtype list
+ @returns [] or the list of IUSE flags
+ """
+ try:
+ return PORTDB.aux_get(cpv, ["IUSE"])[0].split()
+ except:
+ return []
+
+
+def get_installed_use(cpv, use="USE"):
+ """Gets the installed USE flags from the VARDB
+
+ To be used when a gentoolkit package object is not needed
+ @type: cpv: string
+ @param cpv: cat/pkg-ver
+ @type use: string
+ @param use: 1 of ["USE", "PKGUSE"]
+ @rtype list
+ @returns [] or the list of IUSE flags
+ """
+ return VARDB.aux_get(cpv,[use])[0].split()
+
+
+def reduce_flag(flag):
+ """Absolute value function for a USE flag
+
+ @type flag: string
+ @param flag: the use flag to absolute.
+ @rtype: string
+ @return absolute USE flag
+ """
+ if flag[0] in ["+","-"]:
+ return flag[1:]
+ else:
+ return flag
+
+
+def reduce_flags(the_list):
+ """Absolute value function for a USE flag list
+
+ @type the_list: list
+ @param the_list: the use flags to absolute.
+ @rtype: list
+ @return absolute USE flags
+ """
+ r=[]
+ for member in the_list:
+ r.append(reduce_flag(member))
+ return r
+
+
+def filter_flags(use, use_expand_hidden, usemasked, useforced):
+ """Filter function to remove hidden or otherwise not normally
+ visible USE flags from a list.
+
+ @type use: list
+ @param use: the USE flag list to be filtered.
+ @type use_expand_hidden: list
+ @param use_expand_hidden: list of flags hidden.
+ @type usemasked: list
+ @param usemasked: list of masked USE flags.
+ @type useforced: list
+ @param useforced: the forced USE flags.
+ @rtype: list
+ @return the filtered USE flags.
+ """
+ # clean out some environment flags, since they will most probably
+ # be confusing for the user
+ for f in use_expand_hidden:
+ f=f.lower() + "_"
+ for x in use:
+ if f in x:
+ use.remove(x)
+ # clean out any arch's
+ archlist = portage.settings["PORTAGE_ARCHLIST"].split()
+ for a in use[:]:
+ if a in archlist:
+ use.remove(a)
+ # dbl check if any from usemasked or useforced are still there
+ masked = usemasked + useforced
+ for a in use[:]:
+ if a in masked:
+ use.remove(a)
+ return use
+
+
+def get_all_cpv_use(cpv):
+ """Uses portage to determine final USE flags and settings for an emerge
+
+ @type cpv: string
+ @param cpv: eg cat/pkg-ver
+ @rtype: lists
+ @return use, use_expand_hidden, usemask, useforce
+ """
+ use = None
+ PORTDB.settings.unlock()
+ try:
+ PORTDB.settings.setcpv(cpv, mydb=portage.portdb)
+ use = portage.settings['PORTAGE_USE'].split()
+ use_expand_hidden = portage.settings["USE_EXPAND_HIDDEN"].split()
+ usemask = list(PORTDB.settings.usemask)
+ useforce = list(PORTDB.settings.useforce)
+ except KeyError:
+ PORTDB.settings.reset()
+ PORTDB.settings.lock()
+ return [], [], [], []
+ # reset cpv filter
+ PORTDB.settings.reset()
+ PORTDB.settings.lock()
+ return use, use_expand_hidden, usemask, useforce
+
+
+def get_flags(cpv, final_setting=False):
+ """Retrieves all information needed to filter out hidded, masked, etc.
+ USE flags for a given package.
+
+ @type cpv: string
+ @param cpv: eg. cat/pkg-ver
+ @type final_setting: boolean
+ @param final_setting: used to also determine the final
+ enviroment USE flag settings and return them as well.
+ @rtype: list or list, list
+ @return IUSE or IUSE, final_flags
+ """
+ final_use, use_expand_hidden, usemasked, useforced = get_all_cpv_use(cpv)
+ iuse_flags = filter_flags(get_iuse(cpv), use_expand_hidden, usemasked, useforced)
+ #flags = filter_flags(use_flags, use_expand_hidden, usemasked, useforced)
+ if final_setting:
+ final_flags = filter_flags(final_use, use_expand_hidden, usemasked, useforced)
+ return iuse_flags, final_flags
+ return iuse_flags
diff --git a/pym/gentoolkit/formatters.py b/pym/gentoolkit/formatters.py
new file mode 100644
index 0000000..413a1c8
--- /dev/null
+++ b/pym/gentoolkit/formatters.py
@@ -0,0 +1,99 @@
+#!/usr/bin/python
+#
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright(c) 2010, Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+import errno
+import sys
+import time
+
+import gentoolkit
+from gentoolkit.textwrap_ import TextWrapper
+import gentoolkit.pprinter as pp
+
+
+def format_options(options):
+ """Format module options.
+
+ @type options: list
+ @param options: [('option 1', 'description 1'), ('option 2', 'des... )]
+ @rtype: str
+ @return: formatted options string
+ """
+
+ result = []
+ twrap = TextWrapper(width=gentoolkit.CONFIG['termWidth'])
+ opts = (x[0] for x in options)
+ descs = (x[1] for x in options)
+ for opt, desc in zip(opts, descs):
+ twrap.initial_indent = pp.emph(opt.ljust(25))
+ twrap.subsequent_indent = " " * 25
+ result.append(twrap.fill(desc))
+ return '\n'.join(result)
+
+
+def format_filetype(path, fdesc, show_type=False, show_md5=False,
+ show_timestamp=False):
+ """Format a path for printing.
+
+ @type path: str
+ @param path: the path
+ @type fdesc: list
+ @param fdesc: [file_type, timestamp, MD5 sum/symlink target]
+ file_type is one of dev, dir, obj, sym.
+ If file_type is dir, there is no timestamp or MD5 sum.
+ If file_type is sym, fdesc[2] is the target of the symlink.
+ @type show_type: bool
+ @param show_type: if True, prepend the file's type to the formatted string
+ @type show_md5: bool
+ @param show_md5: if True, append MD5 sum to the formatted string
+ @type show_timestamp: bool
+ @param show_timestamp: if True, append time-of-creation after pathname
+ @rtype: str
+ @return: formatted pathname with optional added information
+ """
+
+ ftype = fpath = stamp = md5sum = ""
+ if fdesc[0] == "obj":
+ ftype = "file"
+ fpath = path
+ stamp = format_timestamp(fdesc[1])
+ md5sum = fdesc[2]
+ elif fdesc[0] == "dir":
+ ftype = "dir"
+ fpath = pp.path(path)
+ elif fdesc[0] == "sym":
+ ftype = "sym"
+ stamp = format_timestamp(fdesc[1])
+ tgt = fdesc[2].split()[0]
+ if CONFIG["piping"]:
+ fpath = path
+ else:
+ fpath = pp.path_symlink(path + " -> " + tgt)
+ elif fdesc[0] == "dev":
+ ftype = "dev"
+ fpath = path
+ else:
+ sys.stderr.write(
+ pp.error("%s has unknown type: %s" % (path, fdesc[0]))
+ )
+ result = ""
+ if show_type:
+ result += "%4s " % ftype
+ result += fpath
+ if show_timestamp:
+ result += " " + stamp
+ if show_md5:
+ result += " " + md5sum
+ return result
+
+
+
+def format_timestamp(timestamp):
+ """Format a timestamp into, e.g., '2009-01-31 21:19:44' format"""
+
+ return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(timestamp)))
+
diff --git a/pym/gentoolkit/glsa/__init__.py b/pym/gentoolkit/glsa/__init__.py
index d283835..ab03947 100644
--- a/pym/gentoolkit/glsa/__init__.py
+++ b/pym/gentoolkit/glsa/__init__.py
@@ -11,16 +11,23 @@
# - getting GLSAs from http/ftp servers (not really useful without the fixed ebuilds)
# - GPG signing/verification (until key policy is clear)
+from __future__ import unicode_literals
+
__author__ = "Marius Mauch <genone@gentoo.org>"
-import os
+
import sys
-import urllib
+import os
+try:
+ from urllib import urlopen
+except ImportError:
+ from urllib.request import urlopen
import codecs
import re
import operator
import xml.dom.minidom
-from StringIO import StringIO
+from io import StringIO
+from functools import reduce
if sys.version_info[0:2] < (2,3):
raise NotImplementedError("Python versions below 2.3 have broken XML code " \
@@ -32,8 +39,9 @@ except ImportError:
sys.path.insert(0, "/usr/lib/portage/pym")
import portage
+
# Note: the space for rgt and rlt is important !!
-opMapping = {"le": "<=", "lt": "<", "eq": "=", "gt": ">", "ge": ">=",
+opMapping = {"le": "<=", "lt": "<", "eq": "=", "gt": ">", "ge": ">=",
"rge": ">=~", "rle": "<=~", "rgt": " >~", "rlt": " <~"}
NEWLINE_ESCAPE = "!;\\n" # some random string to mark newlines that should be preserved
SPACE_ESCAPE = "!;_" # some random string to mark spaces that should be preserved
@@ -42,7 +50,7 @@ def center(text, width):
"""
Returns a string containing I{text} that is padded with spaces on both
sides. If C{len(text) >= width} I{text} is returned unchanged.
-
+
@type text: String
@param text: the text to be embedded
@type width: Integer
@@ -65,15 +73,15 @@ def center(text, width):
def wrap(text, width, caption=""):
"""
Wraps the given text at column I{width}, optionally indenting
- it so that no text is under I{caption}. It's possible to encode
+ it so that no text is under I{caption}. It's possible to encode
hard linebreaks in I{text} with L{NEWLINE_ESCAPE}.
-
+
@type text: String
@param text: the text to be wrapped
@type width: Integer
@param width: the column at which the text should be wrapped
@type caption: String
- @param caption: this string is inserted at the beginning of the
+ @param caption: this string is inserted at the beginning of the
return value and the paragraph is indented up to
C{len(caption)}.
@rtype: String
@@ -84,7 +92,7 @@ def wrap(text, width, caption=""):
text = text.replace(2*NEWLINE_ESCAPE, NEWLINE_ESCAPE+" "+NEWLINE_ESCAPE)
words = text.split()
indentLevel = len(caption)+1
-
+
for w in words:
if line[-1] == "\n":
rValue += line
@@ -132,13 +140,13 @@ def get_glsa_list(repository, myconfig):
Returns a list of all available GLSAs in the given repository
by comparing the filelist there with the pattern described in
the config.
-
+
@type repository: String
@param repository: The directory or an URL that contains GLSA files
(Note: not implemented yet)
@type myconfig: portage.config
@param myconfig: a GLSA aware config instance (see L{checkconfig})
-
+
@rtype: List of Strings
@return: a list of GLSA IDs in this repository
"""
@@ -151,7 +159,7 @@ def get_glsa_list(repository, myconfig):
dirlist = os.listdir(repository)
prefix = myconfig["GLSA_PREFIX"]
suffix = myconfig["GLSA_SUFFIX"]
-
+
for f in dirlist:
try:
if f[:len(prefix)] == prefix and f[-1*len(suffix):] == suffix:
@@ -163,7 +171,7 @@ def get_glsa_list(repository, myconfig):
def getListElements(listnode):
"""
Get all <li> elements for a given <ol> or <ul> node.
-
+
@type listnode: xml.dom.Node
@param listnode: <ul> or <ol> list to get the elements for
@rtype: List of Strings
@@ -184,7 +192,7 @@ def getText(node, format, textfd = None):
parameter the text might be formatted by adding/removing newlines,
tabs and spaces. This function is only useful for the GLSA DTD,
it's not applicable for other DTDs.
-
+
@type node: xml.dom.Node
@param node: the root node to start with the parsing
@type format: String
@@ -251,7 +259,7 @@ def getMultiTagsText(rootnode, tagname, format):
"""
Returns a list with the text of all subnodes of type I{tagname}
under I{rootnode} (which itself is not parsed) using the given I{format}.
-
+
@type rootnode: xml.dom.Node
@param rootnode: the node to search for I{tagname}
@type tagname: String
@@ -267,9 +275,9 @@ def getMultiTagsText(rootnode, tagname, format):
def makeAtom(pkgname, versionNode):
"""
- creates from the given package name and information in the
+ creates from the given package name and information in the
I{versionNode} a (syntactical) valid portage atom.
-
+
@type pkgname: String
@param pkgname: the name of the package for this atom
@type versionNode: xml.dom.Node
@@ -292,9 +300,9 @@ def makeAtom(pkgname, versionNode):
def makeVersion(versionNode):
"""
- creates from the information in the I{versionNode} a
+ creates from the information in the I{versionNode} a
version string (format <op><version>).
-
+
@type versionNode: xml.dom.Node
@param versionNode: a <vulnerable> or <unaffected> Node that
contains the version information for this atom
@@ -314,21 +322,21 @@ def makeVersion(versionNode):
def match(atom, portdbname, match_type="default"):
"""
- wrapper that calls revisionMatch() or portage.dbapi.match() depending on
+ wrapper that calls revisionMatch() or portage.dbapi.match() depending on
the given atom.
-
+
@type atom: string
@param atom: a <~ or >~ atom or a normal portage atom that contains the atom to match against
@type portdb: portage.dbapi
@param portdb: one of the portage databases to use as information source
@type match_type: string
- @param match_type: if != "default" passed as first argument to dbapi.xmatch
+ @param match_type: if != "default" passed as first argument to dbapi.xmatch
to apply the wanted visibility filters
-
+
@rtype: list of strings
@return: a list with the matching versions
"""
- db = portage.db["/"][portdbname].dbapi
+ db = portage.db[portage.root][portdbname].dbapi
if atom[2] == "~":
return revisionMatch(atom, db, match_type=match_type)
elif match_type == "default" or not hasattr(db, "xmatch"):
@@ -341,15 +349,15 @@ def revisionMatch(revisionAtom, portdb, match_type="default"):
handler for the special >~, >=~, <=~ and <~ atoms that are supposed to behave
as > and < except that they are limited to the same version, the range only
applies to the revision part.
-
+
@type revisionAtom: string
@param revisionAtom: a <~ or >~ atom that contains the atom to match against
@type portdb: portage.dbapi
@param portdb: one of the portage databases to use as information source
@type match_type: string
- @param match_type: if != "default" passed as first argument to portdb.xmatch
+ @param match_type: if != "default" passed as first argument to portdb.xmatch
to apply the wanted visibility filters
-
+
@rtype: list of strings
@return: a list with the matching versions
"""
@@ -370,26 +378,26 @@ def revisionMatch(revisionAtom, portdb, match_type="default"):
if eval(r1+" "+revisionAtom[0:2]+" "+r2):
rValue.append(v)
return rValue
-
+
def getMinUpgrade(vulnerableList, unaffectedList, minimize=True):
"""
Checks if the systemstate is matching an atom in
I{vulnerableList} and returns string describing
- the lowest version for the package that matches an atom in
+ the lowest version for the package that matches an atom in
I{unaffectedList} and is greater than the currently installed
version. It will return an empty list if the system is affected,
and no upgrade is possible or None if the system is not affected.
Both I{vulnerableList} and I{unaffectedList} should have the
same base package.
-
+
@type vulnerableList: List of Strings
@param vulnerableList: atoms matching vulnerable package versions
@type unaffectedList: List of Strings
@param unaffectedList: atoms matching unaffected package versions
@type minimize: Boolean
@param minimize: True for a least-change upgrade, False for emerge-like algorithm
-
+
@rtype: String | None
@return: the lowest unaffected version that is greater than
the installed version.
@@ -397,7 +405,7 @@ def getMinUpgrade(vulnerableList, unaffectedList, minimize=True):
rValue = ""
v_installed = reduce(operator.add, [match(v, "vartree") for v in vulnerableList], [])
u_installed = reduce(operator.add, [match(u, "vartree") for u in unaffectedList], [])
-
+
# remove all unaffected atoms from vulnerable list
v_installed = list(set(v_installed).difference(set(u_installed)))
@@ -421,8 +429,8 @@ def getMinUpgrade(vulnerableList, unaffectedList, minimize=True):
i_pv = portage.catpkgsplit(vuln)
if portage.pkgcmp(c_pv[1:], i_pv[1:]) > 0 \
and (update == "" \
- or (minimize ^ (portage.pkgcmp(c_pv[1:], portage.catpkgsplit(rValue)[1:]) > 0))) \
- and portage.db["/"]["porttree"].dbapi.aux_get(c, ["SLOT"]) == portage.db["/"]["vartree"].dbapi.aux_get(vuln, ["SLOT"]):
+ or (minimize ^ (portage.pkgcmp(c_pv[1:], portage.catpkgsplit(update)[1:]) > 0))) \
+ and portage.db[portage.root]["porttree"].dbapi.aux_get(c, ["SLOT"]) == portage.db[portage.root]["vartree"].dbapi.aux_get(vuln, ["SLOT"]):
update = c_pv[0]+"/"+c_pv[1]+"-"+c_pv[2]
if c_pv[3] != "r0": # we don't like -r0 for display
update += "-"+c_pv[3]
@@ -434,7 +442,7 @@ def format_date(datestr):
"""
Takes a date (announced, revised) date from a GLSA and formats
it as readable text (i.e. "January 1, 2008").
-
+
@type date: String
@param date: the date string to reformat
@rtype: String
@@ -444,16 +452,16 @@ def format_date(datestr):
splitdate = datestr.split("-", 2)
if len(splitdate) != 3:
return datestr
-
+
# This cannot raise an error as we use () instead of []
splitdate = (int(x) for x in splitdate)
-
+
from datetime import date
try:
d = date(*splitdate)
except ValueError:
return datestr
-
+
# TODO We could format to local date format '%x' here?
return d.strftime("%B %d, %Y")
@@ -464,7 +472,7 @@ class GlsaTypeException(Exception):
class GlsaFormatException(Exception):
pass
-
+
class GlsaArgumentException(Exception):
pass
@@ -476,9 +484,9 @@ class Glsa:
"""
def __init__(self, myid, myconfig):
"""
- Simple constructor to set the ID, store the config and gets the
+ Simple constructor to set the ID, store the config and gets the
XML data by calling C{self.read()}.
-
+
@type myid: String
@param myid: String describing the id for the GLSA object (standard
GLSAs have an ID of the form YYYYMM-nn) or an existing
@@ -500,7 +508,7 @@ class Glsa:
"""
Here we build the filename from the config and the ID and pass
it to urllib to fetch it from the filesystem or a remote server.
-
+
@rtype: None
@return: None
"""
@@ -512,15 +520,15 @@ class Glsa:
myurl = "file://"+self.nr
else:
myurl = repository + self.config["GLSA_PREFIX"] + str(self.nr) + self.config["GLSA_SUFFIX"]
- self.parse(urllib.urlopen(myurl))
+ self.parse(urlopen(myurl))
return None
def parse(self, myfile):
"""
- This method parses the XML file and sets up the internal data
+ This method parses the XML file and sets up the internal data
structures by calling the different helper functions in this
module.
-
+
@type myfile: String
@param myfile: Filename to grab the XML data from
@rtype: None
@@ -543,26 +551,27 @@ class Glsa:
self.title = getText(myroot.getElementsByTagName("title")[0], format="strip")
self.synopsis = getText(myroot.getElementsByTagName("synopsis")[0], format="strip")
self.announced = format_date(getText(myroot.getElementsByTagName("announced")[0], format="strip"))
-
- count = 1
+
# Support both formats of revised:
# <revised>December 30, 2007: 02</revised>
# <revised count="2">2007-12-30</revised>
revisedEl = myroot.getElementsByTagName("revised")[0]
self.revised = getText(revisedEl, format="strip")
- if (revisedEl.attributes.has_key("count")):
- count = revisedEl.getAttribute("count")
- elif (self.revised.find(":") >= 0):
- (self.revised, count) = self.revised.split(":")
-
+ count = revisedEl.attributes.get("count")
+ if count is None:
+ if self.revised.find(":") >= 0:
+ (self.revised, count) = self.revised.split(":")
+ else:
+ count = 1
+
self.revised = format_date(self.revised)
-
+
try:
self.count = int(count)
except ValueError:
# TODO should this rais a GlsaFormatException?
self.count = 1
-
+
# now the optional and 0-n toplevel, #PCDATA tags and references
try:
self.access = getText(myroot.getElementsByTagName("access")[0], format="strip")
@@ -570,7 +579,7 @@ class Glsa:
self.access = ""
self.bugs = getMultiTagsText(myroot, "bug", format="strip")
self.references = getMultiTagsText(myroot.getElementsByTagName("references")[0], "uri", format="keep")
-
+
# and now the formatted text elements
self.description = getText(myroot.getElementsByTagName("description")[0], format="xml")
self.workaround = getText(myroot.getElementsByTagName("workaround")[0], format="xml")
@@ -580,7 +589,7 @@ class Glsa:
try:
self.background = getText(myroot.getElementsByTagName("background")[0], format="xml")
except IndexError:
- self.background = ""
+ self.background = ""
# finally the interesting tags (product, affected, package)
self.glsatype = myroot.getElementsByTagName("product")[0].getAttribute("type")
@@ -589,7 +598,7 @@ class Glsa:
self.packages = {}
for p in self.affected.getElementsByTagName("package"):
name = p.getAttribute("name")
- if not self.packages.has_key(name):
+ if name not in self.packages:
self.packages[name] = []
tmp = {}
tmp["arch"] = p.getAttribute("arch")
@@ -605,10 +614,10 @@ class Glsa:
def dump(self, outstream=sys.stdout, encoding="utf-8"):
"""
- Dumps a plaintext representation of this GLSA to I{outfile} or
+ Dumps a plaintext representation of this GLSA to I{outfile} or
B{stdout} if it is ommitted. You can specify an alternate
I{encoding} if needed (default is utf-8).
-
+
@type outstream: File
@param outfile: Stream that should be used for writing
(defaults to sys.stdout)
@@ -638,24 +647,24 @@ class Glsa:
pass
if len(self.bugs) > 0:
outstream.write("\nRelated bugs: ")
- outstream.write(", ".join(self.bugs))
- outstream.write("\n")
+ outstream.write(", ".join(self.bugs))
+ outstream.write("\n")
if self.background:
outstream.write("\n"+wrap(self.background, width, caption="Background: "))
outstream.write("\n"+wrap(self.description, width, caption="Description: "))
outstream.write("\n"+wrap(self.impact_text, width, caption="Impact: "))
outstream.write("\n"+wrap(self.workaround, width, caption="Workaround: "))
outstream.write("\n"+wrap(self.resolution, width, caption="Resolution: "))
- myreferences = " ".join(r.replace(" ", SPACE_ESCAPE)+NEWLINE_ESCAPE for r in self.references)
+ myreferences = " ".join(r.replace(" ", SPACE_ESCAPE)+NEWLINE_ESCAPE for r in self.references)
outstream.write("\n"+wrap(myreferences, width, caption="References: "))
outstream.write("\n")
-
+
def isVulnerable(self):
"""
Tests if the system is affected by this GLSA by checking if any
vulnerable package versions are installed. Also checks for affected
architectures.
-
+
@rtype: Boolean
@returns: True if the system is affected, False if not
"""
@@ -668,12 +677,12 @@ class Glsa:
rValue = rValue \
or (None != getMinUpgrade([v,], path["unaff_atoms"]))
return rValue
-
+
def isInjected(self):
"""
Looks if the GLSA ID is in the GLSA checkfile to check if this
GLSA should be marked as applied.
-
+
@rtype: Boolean
@returns: True if the GLSA is in the inject file, False if not
"""
@@ -685,7 +694,7 @@ class Glsa:
def inject(self):
"""
Puts the ID of this GLSA into the GLSA checkfile, so it won't
- show up on future checks. Should be called after a GLSA is
+ show up on future checks. Should be called after a GLSA is
applied or on explicit user request.
@rtype: None
@@ -696,13 +705,13 @@ class Glsa:
checkfile.write(self.nr+"\n")
checkfile.close()
return None
-
+
def getMergeList(self, least_change=True):
"""
Returns the list of package-versions that have to be merged to
- apply this GLSA properly. The versions are as low as possible
+ apply this GLSA properly. The versions are as low as possible
while avoiding downgrades (see L{getMinUpgrade}).
-
+
@type least_change: Boolean
@param least_change: True if the smallest possible upgrade should be selected,
False for an emerge-like algorithm
diff --git a/pym/gentoolkit/helpers.py b/pym/gentoolkit/helpers.py
index 6d272d3..ab0cdbd 100644
--- a/pym/gentoolkit/helpers.py
+++ b/pym/gentoolkit/helpers.py
@@ -1,163 +1,479 @@
-#!/usr/bin/python2
+# Copyright(c) 2009-2010, Gentoo Foundation
#
-# Copyright(c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
-# Copyright(c) 2009, Gentoo Foundation
-#
-# Licensed under the GNU General Public License, v2
+# Licensed under the GNU General Public License, v2 or higher
#
# $Header$
-import portage
-from gentoolkit import *
-from package import *
-from pprinter import print_warn
-try:
- from portage.util import unique_array
-except ImportError:
- from portage_util import unique_array
-
-def find_packages(search_key, masked=False):
- """Returns a list of Package objects that matched the search key."""
- try:
- if masked:
- t = portage.db["/"]["porttree"].dbapi.xmatch("match-all", search_key)
- t += portage.db["/"]["vartree"].dbapi.match(search_key)
- else:
- t = portage.db["/"]["porttree"].dbapi.match(search_key)
- t += portage.db["/"]["vartree"].dbapi.match(search_key)
- # catch the "amgigous package" Exception
- except ValueError, e:
- if isinstance(e[0],list):
- t = []
- for cp in e[0]:
- if masked:
- t += portage.db["/"]["porttree"].dbapi.xmatch("match-all", cp)
- t += portage.db["/"]["vartree"].dbapi.match(cp)
+"""Miscellaneous helper functions and classes.
+
+@note: find_* functions that previously lived here have moved to
+ the query module, where they are called as: Query('portage').find_*().
+"""
+
+__all__ = (
+ 'ChangeLog',
+ 'FileOwner',
+ 'get_cpvs',
+ 'get_installed_cpvs',
+ 'get_uninstalled_cpvs',
+ 'get_bintree_cpvs',
+ 'uniqify',
+)
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import os
+import sys
+import re
+import codecs
+from functools import partial
+from itertools import chain
+
+from gentoolkit import pprinter as pp
+from gentoolkit import errors
+from gentoolkit.atom import Atom
+from gentoolkit.cpv import CPV
+from gentoolkit.dbapi import BINDB, PORTDB, VARDB
+from gentoolkit.versionmatch import VersionMatch
+# This has to be imported below to stop circular import.
+#from gentoolkit.package import Package
+
+# =======
+# Classes
+# =======
+
+class ChangeLog(object):
+ """Provides methods for working with a Gentoo ChangeLog file.
+
+ Example usage:
+ >>> from gentoolkit.helpers import ChangeLog
+ >>> portage = ChangeLog('/usr/portage/sys-apps/portage/ChangeLog')
+ >>> print portage.latest.strip()
+ *portage-2.2_rc50 (15 Nov 2009)
+
+ 15 Nov 2009; Zac Medico <zmedico@gentoo.org> +portage-2.2_rc50.ebuild:
+ 2.2_rc50 bump. This includes all fixes in 2.1.7.5.
+ >>> len(portage.full)
+ 75
+ >>> len(portage.entries_matching_range(
+ ... from_ver='2.2_rc40',
+ ... to_ver='2.2_rc50'))
+ 11
+
+ """
+ def __init__(self, changelog_path, invalid_entry_is_fatal=False):
+ if not (os.path.isfile(changelog_path) and
+ os.access(changelog_path, os.R_OK)):
+ raise errors.GentoolkitFatalError(
+ "%s does not exist or is unreadable" % pp.path(changelog_path)
+ )
+ self.changelog_path = changelog_path
+ self.invalid_entry_is_fatal = invalid_entry_is_fatal
+
+ # Process the ChangeLog:
+ self.entries = self._split_changelog()
+ self.indexed_entries = self._index_changelog()
+ self.full = self.entries
+ self.latest = self.entries[0]
+
+ def __repr__(self):
+ return "<%s %r>" % (self.__class__.__name__, self.changelog_path)
+
+ def entries_matching_atom(self, atom):
+ """Return entries whose header versions match atom's version.
+
+ @type atom: L{gentoolkit.atom.Atom} or str
+ @param atom: a atom to find matching entries against
+ @rtype: list
+ @return: entries matching atom
+ @raise errors.GentoolkitInvalidAtom: if atom is a string and malformed
+ """
+ result = []
+
+ if not isinstance(atom, Atom):
+ atom = Atom(atom)
+
+ for entry_set in self.indexed_entries:
+ i, entry = entry_set
+ # VersionMatch doesn't store .cp, so we'll force it to match here:
+ i.cp = atom.cp
+ if atom.intersects(i):
+ result.append(entry)
+
+ return result
+
+ def entries_matching_range(self, from_ver=None, to_ver=None):
+ """Return entries whose header versions are within a range of versions.
+
+ @type from_ver: str
+ @param from_ver: valid Gentoo version
+ @type to_ver: str
+ @param to_ver: valid Gentoo version
+ @rtype: list
+ @return: entries between from_ver and to_ver
+ @raise errors.GentoolkitFatalError: if neither vers are set
+ @raise errors.GentoolkitInvalidVersion: if either ver is invalid
+ """
+ result = []
+
+ # Make sure we have at least one version set
+ if not (from_ver or to_ver):
+ raise errors.GentoolkitFatalError(
+ "Need to specifiy 'from_ver' or 'to_ver'"
+ )
+
+ # Create a VersionMatch instance out of from_ver
+ from_restriction = None
+ if from_ver:
+ try:
+ from_ver_rev = CPV("null-%s" % from_ver)
+ except errors.GentoolkitInvalidCPV:
+ raise errors.GentoolkitInvalidVersion(from_ver)
+ from_restriction = VersionMatch(from_ver_rev, op='>=')
+
+ # Create a VersionMatch instance out of to_ver
+ to_restriction = None
+ if to_ver:
+ try:
+ to_ver_rev = CPV("null-%s" % to_ver)
+ except errors.GentoolkitInvalidCPV:
+ raise errors.GentoolkitInvalidVersion(to_ver)
+ to_restriction = VersionMatch(to_ver_rev, op='<=')
+
+ # Add entry to result if version ranges intersect it
+ for entry_set in self.indexed_entries:
+ i, entry = entry_set
+ if from_restriction and not from_restriction.match(i):
+ continue
+ if to_restriction and not to_restriction.match(i):
+ # TODO: is it safe to break here?
+ continue
+ result.append(entry)
+
+ return result
+
+ def _index_changelog(self):
+ """Use the output of L{self._split_changelog} to create an index list
+ of L{gentoolkit.versionmatch.VersionMatch} objects.
+
+ @rtype: list
+ @return: tuples containing a VersionMatch instance for the release
+ version of each entry header as the first item and the entire entry
+ as the second item
+ @raise ValueError: if self.invalid_entry_is_fatal is True and we hit an
+ invalid entry
+ """
+
+ result = []
+ for entry in self.entries:
+ # Extract the package name from the entry header, ex:
+ # *xterm-242 (07 Mar 2009) => xterm-242
+ pkg_name = entry.split(' ', 1)[0].lstrip('*')
+ if not pkg_name.strip():
+ continue
+ try:
+ entry_ver = CPV(pkg_name)
+ except errors.GentoolkitInvalidCPV:
+ if self.invalid_entry_is_fatal:
+ raise ValueError(entry_ver)
+ continue
+
+ result.append((VersionMatch(entry_ver, op='='), entry))
+
+ return result
+
+ def _split_changelog(self):
+ """Split the ChangeLog into individual entries.
+
+ @rtype: list
+ @return: individual ChangeLog entries
+ """
+
+ result = []
+ partial_entries = []
+ with codecs.open(self.changelog_path, encoding="utf-8",
+ errors="replace") as log:
+ for line in log:
+ if line.startswith('#'):
+ continue
+ elif line.startswith('*'):
+ # Append last entry to result...
+ entry = ''.join(partial_entries)
+ if entry and not entry.isspace():
+ result.append(entry)
+ # ... and start a new entry
+ partial_entries = [line]
else:
- t += portage.db["/"]["porttree"].dbapi.match(cp)
- t += portage.db["/"]["vartree"].dbapi.match(cp)
- else:
- raise ValueError(e)
- except portage_exception.InvalidAtom, e:
- print_warn("Invalid Atom: '%s'" % str(e))
- return []
- # Make the list of packages unique
- t = unique_array(t)
- t.sort()
- return [Package(x) for x in t]
-
-def find_installed_packages(search_key, masked=False):
- """Returns a list of Package objects that matched the search key."""
- try:
- t = portage.db["/"]["vartree"].dbapi.match(search_key)
- # catch the "amgigous package" Exception
- except ValueError, e:
- if isinstance(e[0],list):
- t = []
- for cp in e[0]:
- t += portage.db["/"]["vartree"].dbapi.match(cp)
- else:
- raise ValueError(e)
- except portage_exception.InvalidAtom, e:
- print_warn("Invalid Atom: '%s'" % str(e))
- return []
- return [Package(x) for x in t]
-
-def find_best_match(search_key):
- """Returns a Package object for the best available candidate that
- matched the search key."""
- t = portage.db["/"]["porttree"].dep_bestmatch(search_key)
- if t:
- return Package(t)
- return None
-
-def find_system_packages(prefilter=None):
- """Returns a tuple of lists, first list is resolved system packages,
- second is a list of unresolved packages."""
- pkglist = settings.packages
- resolved = []
- unresolved = []
- for x in pkglist:
- cpv = x.strip()
- if len(cpv) and cpv[0] == "*":
- pkg = find_best_match(cpv)
- if pkg:
- resolved.append(pkg)
+ partial_entries.append(line)
else:
- unresolved.append(cpv)
- return (resolved, unresolved)
-
-def find_world_packages(prefilter=None):
- """Returns a tuple of lists, first list is resolved world packages,
- seond is unresolved package names."""
- f = open(portage.root+portage.WORLD_FILE)
- pkglist = f.readlines()
- resolved = []
- unresolved = []
- for x in pkglist:
- cpv = x.strip()
- if len(cpv) and cpv[0] != "#":
- pkg = find_best_match(cpv)
- if pkg:
- resolved.append(pkg)
+ # Append the final entry
+ entry = ''.join(partial_entries)
+ result.append(entry)
+
+ return result
+
+
+class FileOwner(object):
+ """Creates a function for locating the owner of filename queries.
+
+ Example usage:
+ >>> from gentoolkit.helpers import FileOwner
+ >>> findowner = FileOwner()
+ >>> findowner(('/usr/bin/vim',))
+ [(<Package app-editors/vim-7.2.182>, '/usr/bin/vim')]
+ """
+ def __init__(self, is_regex=False, early_out=False, printer_fn=None):
+ """Instantiate function.
+
+ @type is_regex: bool
+ @param is_regex: funtion args are regular expressions
+ @type early_out: bool
+ @param early_out: return when first result is found (safe)
+ @type printer_fn: callable
+ @param printer_fn: If defined, will be passed useful information for
+ printing each result as it is found.
+ """
+ self.is_regex = is_regex
+ self.early_out = early_out
+ self.printer_fn = printer_fn
+
+ def __call__(self, queries):
+ """Run the function.
+
+ @type queries: iterable
+ @param queries: filepaths or filepath regexes
+ """
+ query_re_string = self._prepare_search_regex(queries)
+ try:
+ query_re = re.compile(query_re_string)
+ except (TypeError, re.error) as err:
+ raise errors.GentoolkitInvalidRegex(err)
+
+ use_match = False
+ if ((self.is_regex or query_re_string.startswith('^\/'))
+ and '|' not in query_re_string ):
+ # If we were passed a regex or a single path starting with root,
+ # we can use re.match, else use re.search.
+ use_match = True
+
+ pkgset = get_installed_cpvs()
+
+ return self.find_owners(query_re, use_match=use_match, pkgset=pkgset)
+
+ def find_owners(self, query_re, use_match=False, pkgset=None):
+ """Find owners and feed data to supplied output function.
+
+ @type query_re: _sre.SRE_Pattern
+ @param query_re: file regex
+ @type use_match: bool
+ @param use_match: use re.match or re.search
+ @type pkgset: iterable or None
+ @param pkgset: list of packages to look through
+ """
+ # FIXME: Remove when lazyimport supports objects:
+ from gentoolkit.package import Package
+
+ if use_match:
+ query_fn = query_re.match
+ else:
+ query_fn = query_re.search
+
+ results = []
+ found_match = False
+ for pkg in sorted([Package(x) for x in pkgset]):
+ files = pkg.parsed_contents()
+ for cfile in files:
+ match = query_fn(cfile)
+ if match:
+ results.append((pkg, cfile))
+ if self.printer_fn is not None:
+ self.printer_fn(pkg, cfile)
+ if self.early_out:
+ found_match = True
+ break
+ if found_match:
+ break
+ return results
+
+ @staticmethod
+ def expand_abspaths(paths):
+ """Expand any relative paths (./file) to their absolute paths.
+
+ @type paths: list
+ @param paths: file path strs
+ @rtype: list
+ @return: the original list with any relative paths expanded
+ @raise AttributeError: if paths does not have attribute 'extend'
+ """
+
+ osp = os.path
+ expanded_paths = []
+ for p in paths:
+ if p.startswith('./'):
+ expanded_paths.append(osp.abspath(p))
else:
- unresolved.append(cpv)
- return (resolved,unresolved)
-
-def find_all_installed_packages(prefilter=None):
- """Returns a list of all installed packages, after applying the prefilter
- function"""
- t = vartree.dbapi.cpv_all()
- if prefilter:
- t = filter(prefilter,t)
- return [Package(x) for x in t]
-
-def find_all_uninstalled_packages(prefilter=None):
- """Returns a list of all uninstalled packages, after applying the prefilter
- function"""
- alist = find_all_packages(prefilter)
- return [x for x in alist if not x.is_installed()]
-
-def find_all_packages(prefilter=None):
- """Returns a list of all known packages, installed or not, after applying
- the prefilter function"""
- t = porttree.dbapi.cp_all()
- t += vartree.dbapi.cp_all()
- if prefilter:
- t = filter(prefilter,t)
- t = unique_array(t)
- t2 = []
- for x in t:
- t2 += porttree.dbapi.cp_list(x)
- t2 += vartree.dbapi.cp_list(x)
- t2 = unique_array(t2)
- return [Package(x) for x in t2]
-
-def split_package_name(name):
- """Returns a list on the form [category, name, version, revision]. Revision will
- be 'r0' if none can be inferred. Category and version will be empty, if none can
- be inferred."""
- r = portage.catpkgsplit(name)
- if not r:
- r = name.split("/")
- if len(r) == 1:
- return ["", name, "", "r0"]
+ expanded_paths.append(p)
+
+ return expanded_paths
+
+ @staticmethod
+ def extend_realpaths(paths):
+ """Extend a list of paths with the realpaths for any symlinks.
+
+ @type paths: list
+ @param paths: file path strs
+ @rtype: list
+ @return: the original list plus the realpaths for any symlinks
+ so long as the realpath doesn't already exist in the list
+ @raise AttributeError: if paths does not have attribute 'extend'
+ """
+
+ osp = os.path
+ paths.extend([osp.realpath(x) for x in paths
+ if osp.islink(x) and osp.realpath(x) not in paths])
+
+ return paths
+
+ def _prepare_search_regex(self, queries):
+ """Create a regex out of the queries"""
+
+ queries = list(queries)
+ if self.is_regex:
+ return '|'.join(queries)
else:
- return r + ["", "r0"]
+ result = []
+ # Trim trailing and multiple slashes from queries
+ slashes = re.compile('/+')
+ queries = self.expand_abspaths(queries)
+ queries = self.extend_realpaths(queries)
+ for query in queries:
+ query = slashes.sub('/', query).rstrip('/')
+ if query.startswith('/'):
+ query = "^%s$" % re.escape(query)
+ else:
+ query = "/%s$" % re.escape(query)
+ result.append(query)
+ result = "|".join(result)
+ return result
+
+# =========
+# Functions
+# =========
+
+def get_cpvs(predicate=None, include_installed=True):
+ """Get all packages in the Portage tree and overlays. Optionally apply a
+ predicate.
+
+ Example usage:
+ >>> from gentoolkit.helpers import get_cpvs
+ >>> len(set(get_cpvs()))
+ 26065
+ >>> fn = lambda x: x.startswith('app-portage')
+ >>> len(get_cpvs(fn, include_installed=False))
+ 112
+
+ @type predicate: function
+ @param predicate: a function to filter the package list with
+ @type include_installed: bool
+ @param include_installed:
+ If True: Return the union of all_cpvs and all_installed_cpvs
+ If False: Return the difference of all_cpvs and all_installed_cpvs
+ @rtype: generator
+ @return: a generator that yields unsorted cat/pkg-ver strings from the
+ Portage tree
+ """
+
+ if predicate:
+ all_cps = iter(x for x in PORTDB.cp_all() if predicate(x))
else:
- r = list(r)
- if r[0] == 'null':
- r[0] = ''
- return r
+ all_cps = PORTDB.cp_all()
-# XXX: Defunct: use helpers2.compare_package_strings
-#def sort_package_list(pkglist):
-# """Returns the list ordered in the same way portage would do with lowest version
-# at the head of the list."""
-# pkglist.sort(Package.compare_version)
-# return pkglist
+ all_cpvs = chain.from_iterable(PORTDB.cp_list(x) for x in all_cps)
+ all_installed_cpvs = set(get_installed_cpvs(predicate))
+
+ if include_installed:
+ for cpv in all_cpvs:
+ if cpv in all_installed_cpvs:
+ all_installed_cpvs.remove(cpv)
+ yield cpv
+ for cpv in all_installed_cpvs:
+ yield cpv
+ else:
+ for cpv in all_cpvs:
+ if cpv not in all_installed_cpvs:
+ yield cpv
-if __name__ == "__main__":
- print "This module is for import only"
+# pylint thinks this is a global variable
+# pylint: disable-msg=C0103
+get_uninstalled_cpvs = partial(get_cpvs, include_installed=False)
+
+
+def get_installed_cpvs(predicate=None):
+ """Get all installed packages. Optionally apply a predicate.
+
+ @type predicate: function
+ @param predicate: a function to filter the package list with
+ @rtype: generator
+ @return: a generator that yields unsorted installed cat/pkg-ver strings
+ from VARDB
+ """
+
+ if predicate:
+ installed_cps = iter(x for x in VARDB.cp_all() if predicate(x))
+ else:
+ installed_cps = VARDB.cp_all()
+
+ for cpv in chain.from_iterable(VARDB.cp_list(x) for x in installed_cps):
+ yield cpv
+
+
+def get_bintree_cpvs(predicate=None):
+ """Get all binary packages available. Optionally apply a predicate.
+
+ @type predicate: function
+ @param predicate: a function to filter the package list with
+ @rtype: generator
+ @return: a generator that yields unsorted binary package cat/pkg-ver strings
+ from BINDB
+ """
+
+ if predicate:
+ installed_cps = iter(x for x in BINDB.cp_all() if predicate(x))
+ else:
+ installed_cps = BINDB.cp_all()
+
+ for cpv in chain.from_iterable(BINDB.cp_list(x) for x in installed_cps):
+ yield cpv
+
+
+def print_file(path):
+ """Display the contents of a file."""
+
+ with open(path, "rb") as open_file:
+ lines = open_file.read()
+ pp.uprint(lines.strip())
+
+
+def print_sequence(seq):
+ """Print every item of a sequence."""
+
+ for item in seq:
+ pp.uprint(item)
+
+
+def uniqify(seq, preserve_order=True):
+ """Return a uniqified list. Optionally preserve order."""
+
+ if preserve_order:
+ seen = set()
+ result = [x for x in seq if x not in seen and not seen.add(x)]
+ else:
+ result = list(set(seq))
+
+ return result
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/helpers2.py b/pym/gentoolkit/helpers2.py
deleted file mode 100644
index f9d0c49..0000000
--- a/pym/gentoolkit/helpers2.py
+++ /dev/null
@@ -1,437 +0,0 @@
-# Copyright(c) 2009, Gentoo Foundation
-#
-# Licensed under the GNU General Public License, v2 or higher
-
-"""Improved versions of the original helpers functions.
-
-As a convention, functions ending in '_packages' or '_match{es}' return
-Package objects, while functions ending in 'cpvs' return a sequence of strings.
-Functions starting with 'get_' return a set of packages by default and can be
-filtered, while functions starting with 'find_' return nothing unless the
-query matches one or more packages.
-
-This should be merged into helpers when a clean path is found.
-"""
-
-# Move to Imports section after Python 2.6 is stable
-from __future__ import with_statement
-
-__all__ = (
- 'compare_package_strings',
- 'do_lookup',
- 'find_best_match',
- 'find_installed_packages',
- 'find_packages',
- 'get_cpvs',
- 'get_installed_cpvs',
- 'get_uninstalled_cpvs',
- 'uses_globbing'
-)
-__author__ = 'Douglas Anderson'
-__docformat__ = 'epytext'
-
-# =======
-# Imports
-# =======
-
-import re
-import fnmatch
-from functools import partial
-
-import portage
-from portage.util import unique_array
-
-import gentoolkit
-import gentoolkit.pprinter as pp
-from gentoolkit import Config, PORTDB, VARDB
-from gentoolkit import errors
-from gentoolkit.package import Package
-
-# =========
-# Functions
-# =========
-
-def compare_package_strings(pkg1, pkg2):
- """Similar to the builtin cmp, but for package strings. Usually called
- as: package_list.sort(compare_package_strings)
-
- An alternative is to use the Package descriptor from gentoolkit.package
- >>> pkgs = [Package(x) for x in package_list]
- >>> pkgs.sort()
-
- @see: >>> help(cmp)
- """
-
- pkg1 = portage.versions.catpkgsplit(pkg1)
- pkg2 = portage.versions.catpkgsplit(pkg2)
- # Compare categories
- if pkg1[0] != pkg2[0]:
- return cmp(pkg1[0], pkg2[0])
- # Compare names
- elif pkg1[1] != pkg2[1]:
- return cmp(pkg1[1], pkg2[1])
- # Compare versions
- else:
- return portage.versions.pkgcmp(pkg1[1:], pkg2[1:])
-
-
-def do_lookup(query, query_opts):
- """A high-level wrapper around gentoolkit package-finder functions.
-
- @type query: str
- @param query: pkg, cat/pkg, pkg-ver, cat/pkg-ver, atom or regex
- @type query_opts: dict
- @param query_opts: user-configurable options from the calling module
- Currently supported options are:
-
- categoryFilter = str or None
- includeInstalled = bool
- includePortTree = bool
- includeOverlayTree = bool
- isRegex = bool
- printMatchInfo = bool # Print info about the search
-
- @rtype: list
- @return: Package objects matching query
- """
-
- if query_opts["includeInstalled"]:
- if query_opts["includePortTree"] or query_opts["includeOverlayTree"]:
- simple_package_finder = partial(find_packages, include_masked=True)
- complex_package_finder = get_cpvs
- else:
- simple_package_finder = find_installed_packages
- complex_package_finder = get_installed_cpvs
- elif query_opts["includePortTree"] or query_opts["includeOverlayTree"]:
- simple_package_finder = partial(find_packages, include_masked=True)
- complex_package_finder = get_uninstalled_cpvs
- else:
- pp.die(2, "Not searching in installed, portage tree or overlay."
- " Nothing to do.")
-
- is_simple_query = True
- if query_opts["isRegex"] or uses_globbing(query):
- is_simple_query = False
-
- if is_simple_query:
- matches = _do_simple_lookup(query, simple_package_finder, query_opts)
- else:
- matches = _do_complex_lookup(query, complex_package_finder, query_opts)
-
- return matches
-
-
-def _do_complex_lookup(query, package_finder, query_opts):
- """Find matches for a query which is a regex or includes globbing."""
-
- # pylint: Too many branches (18/12)
- # pylint: disable-message=R0912
- result = []
-
- if query_opts["printMatchInfo"] and not Config["piping"]:
- print_query_info(query, query_opts)
-
- cats = prepare_categories(query_opts["categoryFilter"])
- cat = split_query(query)[0]
-
- pre_filter = []
- # The "get_" functions can pre-filter against the whole package key,
- # but since we allow globbing now, we run into issues like:
- # >>> portage.dep.dep_getkey("sys-apps/portage-*")
- # 'sys-apps/portage-'
- # So the only way to guarantee we don't overrun the key is to
- # prefilter by cat only.
- if cats:
- pre_filter = package_finder(predicate=lambda x: x.startswith(cats))
- if cat:
- if query_opts["isRegex"]:
- cat_re = cat
- else:
- cat_re = fnmatch.translate(cat)
- # [::-1] reverses a sequence, so we're emulating an ".rreplace()"
- # except we have to put our "new" string on backwards
- cat_re = cat_re[::-1].replace('$', '*./', 1)[::-1]
- predicate = lambda x: re.match(cat_re, x)
- if pre_filter:
- pre_filter = [x for x in pre_filter if predicate(x)]
- else:
- pre_filter = package_finder(predicate=predicate)
-
- # Post-filter
- if query_opts["isRegex"]:
- predicate = lambda x: re.match(query, x)
- else:
- if cat:
- query_re = fnmatch.translate(query)
- else:
- query_re = fnmatch.translate("*/%s" % query)
- predicate = lambda x: re.search(query_re, x)
- if pre_filter:
- result = [x for x in pre_filter if predicate(x)]
- else:
- result = package_finder(predicate=predicate)
-
- return [Package(x) for x in result]
-
-
-def _do_simple_lookup(query, package_finder, query_opts):
- """Find matches for a query which is an atom or string."""
-
- result = []
-
- cats = prepare_categories(query_opts["categoryFilter"])
- if query_opts["printMatchInfo"] and not Config["piping"]:
- print_query_info(query, query_opts)
-
- result = package_finder(query)
- if not query_opts["includeInstalled"]:
- result = [x for x in result if not x.is_installed()]
-
- if cats:
- result = [x for x in result if x.cpv.startswith(cats)]
-
- return result
-
-
-def find_best_match(query):
- """Return the highest unmasked version of a package matching query.
-
- @type query: str
- @param query: can be of the form: pkg, pkg-ver, cat/pkg, cat/pkg-ver, atom
- @rtype: str or None
- """
-
- match = PORTDB.xmatch("bestmatch-visible", query)
-
- return Package(match) if match else None
-
-
-def find_installed_packages(query):
- """Return a list of Package objects that matched the search key."""
-
- try:
- matches = VARDB.match(query)
- # catch the ambiguous package Exception
- except portage.exception.AmbiguousPackageName, err:
- matches = []
- for pkgkey in err[0]:
- matches.extend(VARDB.match(pkgkey))
- except portage.exception.InvalidAtom, err:
- pp.print_warn("Invalid Atom: '%s'" % str(err))
- return []
-
- return [Package(x) for x in matches]
-
-
-def find_packages(query, include_masked=False):
- """Returns a list of Package objects that matched the query.
-
- @type query: str
- @param query: can be of the form: pkg, pkg-ver, cat/pkg, cat/pkg-ver, atom
- @rtype: list
- @return: matching Package objects
- """
-
- if not query:
- return []
-
- try:
- if include_masked:
- matches = PORTDB.xmatch("match-all", query)
- else:
- matches = PORTDB.match(query)
- matches.extend(VARDB.match(query))
- except portage.exception.InvalidAtom, err:
- raise errors.GentoolkitInvalidAtom(str(err))
-
- return [Package(x) for x in unique_array(matches)]
-
-
-def get_cpvs(predicate=None, include_installed=True):
- """Get all packages in the Portage tree and overlays. Optionally apply a
- predicate.
-
- Example usage:
- >>> from gentoolkit.helpers2 import get_cpvs
- >>> len(get_cpvs())
- 26065
- >>> fn = lambda x: x.startswith('app-portage')
- >>> len(get_cpvs(fn, include_installed=False))
- 112
-
- @type predicate: function
- @param predicate: a function to filter the package list with
- @type include_installed: bool
- @param include_installed:
- If True: Return the union of all_cpvs and all_installed_cpvs
- If False: Return the difference of all_cpvs and all_installed_cpvs
- @rtype: list
- @return: ['cat/portdir_pkg-1', 'cat/overlay_pkg-2', ...]
- """
-
- if predicate:
- all_cps = [x for x in PORTDB.cp_all() if predicate(x)]
- else:
- all_cps = PORTDB.cp_all()
-
- all_cpvs = []
- for pkgkey in all_cps:
- all_cpvs.extend(PORTDB.cp_list(pkgkey))
-
- result = set(all_cpvs)
- all_installed_cpvs = get_installed_cpvs(predicate)
-
- if include_installed:
- result.update(all_installed_cpvs)
- else:
- result.difference_update(all_installed_cpvs)
-
- return list(result)
-
-
-# pylint thinks this is a global variable
-# pylint: disable-msg=C0103
-get_uninstalled_cpvs = partial(get_cpvs, include_installed=False)
-
-
-def get_installed_cpvs(predicate=None):
- """Get all installed packages. Optionally apply a predicate.
-
- @type predicate: function
- @param predicate: a function to filter the package list with
- @rtype: unsorted list
- @return: ['cat/installed_pkg-1', 'cat/installed_pkg-2', ...]
- """
-
- if predicate:
- all_installed_cps = [x for x in VARDB.cp_all() if predicate(x)]
- else:
- all_installed_cps = VARDB.cp_all()
-
- result = []
- for pkgkey in all_installed_cps:
- result.extend(VARDB.cp_list(pkgkey))
-
- return list(result)
-
-
-def prepare_categories(category_filter):
- """Return a tuple of validated categories. Expand globs.
-
- Example usage:
- >>> prepare_categories('app-portage,sys-apps')
- ('app-portage', 'sys-apps')
- """
-
- if not category_filter:
- return tuple()
-
- cats = [x.lstrip('=') for x in category_filter.split(',')]
- valid_cats = portage.settings.categories
- good_cats = []
- for cat in cats:
- if set('!*?[]').intersection(set(cat)):
- good_cats.extend(fnmatch.filter(valid_cats, cat))
- elif cat in valid_cats:
- good_cats.append(cat)
- else:
- raise errors.GentoolkitInvalidCategory(cat)
-
- return tuple(good_cats)
-
-
-def print_query_info(query, query_opts):
- """Print info about the query to the screen."""
-
- cats = prepare_categories(query_opts["categoryFilter"])
- cat, pkg, ver, rev = split_query(query)
- del ver, rev
- if cats:
- cat_str = "in %s " % ', '.join([pp.emph(x) for x in cats])
- elif cat and not query_opts["isRegex"]:
- cat_str = "in %s " % pp.emph(cat)
- else:
- cat_str = ""
-
- if query_opts["isRegex"]:
- pkg_str = query
- else:
- pkg_str = pkg
-
- print " * Searching for %s %s..." % (pp.emph(pkg_str), cat_str)
-
-
-def print_file(path):
- """Display the contents of a file."""
-
- with open(path) as open_file:
- lines = open_file.read()
- print lines.strip()
-
-
-def print_sequence(seq):
- """Print every item of a sequence."""
-
- for item in seq:
- print item
-
-
-def split_query(query):
- """Split a query into category, name, version and revision.
-
- @type query: str
- @param query: pkg, cat/pkg, pkg-ver, cat/pkg-ver, atom or regex
- @rtype: tuple
- @return: (category, pkg_name, version, revision)
- Each tuple element is a string or empty string ("").
- """
-
- result = portage.versions.catpkgsplit(query)
-
- if result:
- result = list(result)
- if result[0] == 'null':
- result[0] = ''
- if result[3] == 'r0':
- result[3] = ''
- else:
- result = query.split("/")
- if len(result) == 1:
- result = ['', query, '', '']
- else:
- result = result + ['', '']
-
- if len(result) != 4:
- pp.print_error("Too many slashes ('/').")
- raise errors.GentoolkitInvalidPackageName(query)
-
- return tuple(result)
-
-
-def uniqify(seq, preserve_order=True):
- """Return a uniqified list. Optionally preserve order."""
-
- if preserve_order:
- seen = set()
- result = [x for x in seq if x not in seen and not seen.add(x)]
- else:
- result = list(set(seq))
-
- return result
-
-
-def uses_globbing(query):
- """Check the query to see if it is using globbing.
-
- @rtype: bool
- @return: True if query uses globbing, else False
- """
-
- if set('!*?[]').intersection(set(query)):
- if portage.dep.get_operator(query):
- # Query may be an atom such as '=sys-apps/portage-2.2*'
- pass
- else:
- return True
-
- return False
diff --git a/pym/gentoolkit/keyword.py b/pym/gentoolkit/keyword.py
new file mode 100644
index 0000000..057f77c
--- /dev/null
+++ b/pym/gentoolkit/keyword.py
@@ -0,0 +1,144 @@
+#!/usr/bin/python
+#
+# Copyright(c) 2004-2010, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+"""Provides common methods on Gentoo GLEP 53 keywords.
+
+http://www.gentoo.org/proj/en/glep/glep-0053.html
+"""
+
+__all__ = (
+ 'Keyword',
+ 'compare_strs',
+ 'reduce_keywords',
+ 'determine_keyword'
+)
+
+# =======
+# Imports
+# =======
+
+
+# =======
+# Classes
+# =======
+
+class Keyword(object):
+ """Provides common methods on a GLEP 53 keyword."""
+
+ def __init__(self, keyword):
+ self.keyword = keyword
+ arch, sep, os = keyword.partition('-')
+ self.arch = arch
+ self.os = os
+
+ def __eq__(self, other):
+ if not isinstance(other, self.__class__):
+ return False
+ return self.keyword == other.keyword
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __lt__(self, other):
+ if not isinstance(other, self.__class__):
+ raise TypeError("other isn't of %s type, is %s" % (
+ self.__class__, other.__class__)
+ )
+ if self.os < other.os:
+ return True
+ return self.arch < other.arch
+
+ def __le__(self, other):
+ return self == other or self < other
+
+ def __gt__(self, other):
+ return not self <= other
+
+ def __ge__(self, other):
+ return self == other or self > other
+
+ def __str__(self):
+ return self.keyword
+
+ def __repr__(self):
+ return "<{0.__class__.__name__} {0.keyword!r}>".format(self)
+
+# =========
+# Functions
+# =========
+
+def compare_strs(kw1, kw2):
+ """Similar to the builtin cmp, but for keyword strings. Usually called
+ as: keyword_list.sort(keyword.compare_strs)
+
+ An alternative is to use the Keyword descriptor directly:
+ >>> kwds = sorted(Keyword(x) for x in keyword_list)
+
+ @see: >>> help(cmp)
+ """
+
+ kw1_arch, sep, kw1_os = kw1.partition('-')
+ kw2_arch, sep, kw2_os = kw2.partition('-')
+ if kw1_arch != kw2_arch:
+ if kw1_os != kw2_os:
+ return -1 if kw1_os < kw2_os else 1
+ return -1 if kw1_arch < kw2_arch else 1
+ if kw1_os == kw2_os:
+ return 0
+ return -1 if kw1_os < kw2_os else 1
+
+
+def reduce_keywords(keywords):
+ """Reduce a list of keywords to a unique set of stable keywords.
+
+ Example usage:
+ >>> reduce_keywords(['~amd64', 'x86', '~x86'])
+ set(['amd64', 'x86'])
+
+ @type keywords: array
+ @rtype: set
+ """
+ return set(x.lstrip('~') for x in keywords)
+
+
+abs_keywords = reduce_keywords
+
+
+# FIXME: this is unclear
+# dj, how about 'deduce_keyword'
+# I was trying to avoid a 2nd use of determine_keyword name (in analyse.lib)
+# but that one is a little different and not suitable for this task.
+def determine_keyword(arch, accepted, keywords):
+ """Determine a keyword from matching a dep's KEYWORDS
+ list against the ARCH & ACCEPT_KEYWORDS provided.
+
+ @type arch: string
+ @param arch: portage.settings["ARCH"]
+ @type accepted: string
+ @param accepted: portage.settings["ACCEPT_KEYWORDS"]
+ @type keywords: string
+ @param keywords: the pkg ebuilds keywords
+ """
+ if not keywords:
+ return ''
+ keys = keywords.split()
+ if arch in keys:
+ return arch
+ keyworded = "~" + arch
+ if keyworded in keys:
+ return keyworded
+ match = list(set(accepted.split(" ")).intersection(keys))
+ if len(match) > 1:
+ if arch in match:
+ return arch
+ if keyworded in match:
+ return keyworded
+ return 'unknown'
+ if match:
+ return match[0]
+ return 'unknown'
diff --git a/pym/gentoolkit/metadata.py b/pym/gentoolkit/metadata.py
new file mode 100644
index 0000000..f1099ac
--- /dev/null
+++ b/pym/gentoolkit/metadata.py
@@ -0,0 +1,307 @@
+#!/usr/bin/python
+#
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+"""Provides an easy-to-use python interface to Gentoo's metadata.xml file.
+
+ Example usage:
+ >>> from gentoolkit.metadata import MetaData
+ >>> pkg_md = MetaData('/usr/portage/app-misc/gourmet/metadata.xml')
+ >>> pkg_md
+ <MetaData '/usr/portage/app-misc/gourmet/metadata.xml'>
+ >>> pkg_md.herds()
+ ['no-herd']
+ >>> for maint in pkg_md.maintainers():
+ ... print "{0} ({1})".format(maint.email, maint.name)
+ ...
+ nixphoeni@gentoo.org (Joe Sapp)
+ >>> for flag in pkg_md.use():
+ ... print flag.name, "->", flag.description
+ ...
+ rtf -> Enable export to RTF
+ gnome-print -> Enable printing support using gnome-print
+ >>> upstream = pkg_md.upstream()
+ >>> upstream
+ [<_Upstream {'docs': [], 'remoteid': [], 'maintainer':
+ [<_Maintainer 'Thomas_Hinkle@alumni.brown.edu'>], 'bugtracker': [],
+ 'changelog': []}>]
+ >>> upstream[0].maintainer[0].name
+ 'Thomas Mills Hinkle'
+"""
+
+__all__ = ('MetaData',)
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import os
+import re
+import xml.etree.cElementTree as etree
+
+from portage import settings
+
+# =======
+# Classes
+# =======
+
+class _Maintainer(object):
+ """An object for representing one maintainer.
+
+ @type email: str or None
+ @ivar email: Maintainer's email address. Used for both Gentoo and upstream.
+ @type name: str or None
+ @ivar name: Maintainer's name. Used for both Gentoo and upstream.
+ @type description: str or None
+ @ivar description: Description of what a maintainer does. Gentoo only.
+ @type restrict: str or None
+ @ivar restrict: e.g. &gt;=portage-2.2 means only maintains versions
+ of Portage greater than 2.2. Should be DEPEND string with < and >
+ converted to &lt; and &gt; respectively.
+ @type status: str or None
+ @ivar status: If set, either 'active' or 'inactive'. Upstream only.
+ """
+
+ def __init__(self, node):
+ self.email = None
+ self.name = None
+ self.description = None
+ self.restrict = node.get('restrict')
+ self.status = node.get('status')
+ maint_attrs = node.getchildren()
+ for attr in maint_attrs:
+ setattr(self, attr.tag, attr.text)
+
+ def __repr__(self):
+ return "<%s %r>" % (self.__class__.__name__, self.email)
+
+
+class _Useflag(object):
+ """An object for representing one USE flag.
+
+ @todo: Is there any way to have a keyword option to leave in
+ <pkg> and <cat> for later processing?
+ @type name: str or None
+ @ivar name: USE flag
+ @type restrict: str or None
+ @ivar restrict: e.g. &gt;=portage-2.2 means flag is only avaiable in
+ versions greater than 2.2
+ @type description: str
+ @ivar description: description of the USE flag
+ """
+
+ def __init__(self, node):
+ self.name = node.get('name')
+ self.restrict = node.get('restrict')
+ _desc = ''
+ if node.text:
+ _desc = node.text
+ for child in node.getchildren():
+ _desc += child.text if child.text else ''
+ _desc += child.tail if child.tail else ''
+ # This takes care of tabs and newlines left from the file
+ self.description = re.sub('\s+', ' ', _desc)
+
+ def __repr__(self):
+ return "<%s %r>" % (self.__class__.__name__, self.name)
+
+
+class _Upstream(object):
+ """An object for representing one package's upstream.
+
+ @type maintainers: list
+ @ivar maintainers: L{_Maintainer} objects for each upstream maintainer
+ @type changelogs: list
+ @ivar changelogs: URLs to upstream's ChangeLog file in str format
+ @type docs: list
+ @ivar docs: Sequence of tuples containing URLs to upstream documentation
+ in the first slot and 'lang' attribute in the second, e.g.,
+ [('http.../docs/en/tut.html', None), ('http.../doc/fr/tut.html', 'fr')]
+ @type bugtrackers: list
+ @ivar bugtrackers: URLs to upstream's bugtracker. May also contain an email
+ address if prepended with 'mailto:'
+ @type remoteids: list
+ @ivar remoteids: Sequence of tuples containing the project's hosting site
+ name in the first slot and the project's ID name or number for that
+ site in the second, e.g., [('sourceforge', 'systemrescuecd')]
+ """
+
+ def __init__(self, node):
+ self.node = node
+ self.maintainers = self.upstream_maintainers()
+ self.changelogs = self.upstream_changelogs()
+ self.docs = self.upstream_documentation()
+ self.bugtrackers = self.upstream_bugtrackers()
+ self.remoteids = self.upstream_remoteids()
+
+ def __repr__(self):
+ return "<%s %r>" % (self.__class__.__name__, self.__dict__)
+
+ def upstream_bugtrackers(self):
+ """Retrieve upstream bugtracker location from xml node."""
+ return [e.text for e in self.node.findall('bugs-to')]
+
+ def upstream_changelogs(self):
+ """Retrieve upstream changelog location from xml node."""
+ return [e.text for e in self.node.findall('changelog')]
+
+ def upstream_documentation(self):
+ """Retrieve upstream documentation location from xml node."""
+ result = []
+ for elem in self.node.findall('doc'):
+ lang = elem.get('lang')
+ result.append((elem.text, lang))
+ return result
+
+ def upstream_maintainers(self):
+ """Retrieve upstream maintainer information from xml node."""
+ return [_Maintainer(m) for m in self.node.findall('maintainer')]
+
+ def upstream_remoteids(self):
+ """Retrieve upstream remote ID from xml node."""
+ return [(e.text, e.get('type')) for e in self.node.findall('remote-id')]
+
+
+class MetaData(object):
+ """Access metadata.xml"""
+
+ def __init__(self, metadata_path):
+ """Parse a valid metadata.xml file.
+
+ @type metadata_path: str
+ @param metadata_path: path to a valid metadata.xml file
+ @raise IOError: if C{metadata_path} can not be read
+ """
+
+ self.metadata_path = metadata_path
+ self._xml_tree = etree.parse(metadata_path)
+
+ # Used for caching
+ self._herdstree = None
+ self._descriptions = None
+ self._maintainers = None
+ self._useflags = None
+ self._upstream = None
+
+ def __repr__(self):
+ return "<%s %r>" % (self.__class__.__name__, self.metadata_path)
+
+ def _get_herd_email(self, herd):
+ """Get a herd's email address.
+
+ @type herd: str
+ @param herd: herd whose email you want
+ @rtype: str or None
+ @return: email address or None if herd is not in herds.xml
+ @raise IOError: if $PORTDIR/metadata/herds.xml can not be read
+ """
+
+ if self._herdstree is None:
+ herds_path = os.path.join(settings['PORTDIR'], 'metadata/herds.xml')
+ try:
+ self._herdstree = etree.parse(herds_path)
+ except IOError:
+ # For some trees, herds.xml may not exist. Bug #300108.
+ return None
+
+ # Some special herds are not listed in herds.xml
+ if herd in ('no-herd', 'maintainer-wanted', 'maintainer-needed'):
+ return None
+
+ for node in self._herdstree.getiterator('herd'):
+ if node.findtext('name') == herd:
+ return node.findtext('email')
+
+ def herds(self, include_email=False):
+ """Return a list of text nodes for <herd>.
+
+ @type include_email: bool
+ @keyword include_email: if True, also look up the herd's email
+ @rtype: list
+ @return: if include_email is False, return a list of strings;
+ if include_email is True, return a list of tuples containing:
+ [('herd1', 'herd1@gentoo.org'), ('no-herd', None);
+ """
+
+ result = []
+ for elem in self._xml_tree.findall('herd'):
+ text = elem.text
+ if text is None:
+ text = ''
+ if include_email:
+ herd_mail = self._get_herd_email(text)
+ result.append((text, herd_mail))
+ else:
+ result.append(text)
+
+ return result
+
+ def descriptions(self):
+ """Return a list of text nodes for <longdescription>.
+
+ @rtype: list
+ @return: package description in string format
+ @todo: Support the C{lang} attribute
+ """
+
+ if self._descriptions is not None:
+ return self._descriptions
+
+ long_descriptions = self._xml_tree.findall("longdescription")
+ self._descriptions = [e.text for e in long_descriptions]
+ return self._descriptions
+
+ def maintainers(self):
+ """Get maintainers' name, email and description.
+
+ @rtype: list
+ @return: a sequence of L{_Maintainer} objects in document order.
+ """
+
+ if self._maintainers is not None:
+ return self._maintainers
+
+ self._maintainers = []
+ for node in self._xml_tree.findall('maintainer'):
+ self._maintainers.append(_Maintainer(node))
+
+ return self._maintainers
+
+ def use(self):
+ """Get names and descriptions for USE flags defined in metadata.
+
+ @rtype: list
+ @return: a sequence of L{_Useflag} objects in document order.
+ """
+
+ if self._useflags is not None:
+ return self._useflags
+
+ self._useflags = []
+ for node in self._xml_tree.getiterator('flag'):
+ self._useflags.append(_Useflag(node))
+
+ return self._useflags
+
+ def upstream(self):
+ """Get upstream contact information.
+
+ @rtype: list
+ @return: a sequence of L{_Upstream} objects in document order.
+ """
+
+ if self._upstream is not None:
+ return self._upstream
+
+ self._upstream = []
+ for node in self._xml_tree.findall('upstream'):
+ self._upstream.append(_Upstream(node))
+
+ return self._upstream
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/package.py b/pym/gentoolkit/package.py
index 857470a..a4031a3 100644
--- a/pym/gentoolkit/package.py
+++ b/pym/gentoolkit/package.py
@@ -1,455 +1,397 @@
#!/usr/bin/python
#
# Copyright(c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
-# Copyright(c) 2004-2009, Gentoo Foundation
+# Copyright(c) 2004-2010, Gentoo Foundation
#
# Licensed under the GNU General Public License, v2
#
# $Header$
+"""Provides an interface to package information stored by package managers.
+
+The Package class is the heart of much of Gentoolkit. Given a CPV
+(category/package-version) string, it can reveal the package's status in the
+tree and VARDB (/var/db/), provide rich comparison and sorting, and expose
+important parts of Portage's back-end.
+
+Example usage:
+ >>> portage = Package('sys-apps/portage-2.1.6.13')
+ >>> portage.ebuild_path()
+ '/usr/portage/sys-apps/portage/portage-2.1.6.13.ebuild'
+ >>> portage.is_masked()
+ False
+ >>> portage.is_installed()
+ True
+"""
+
+__all__ = (
+ 'Package',
+ 'PackageFormatter',
+ 'FORMAT_TMPL_VARS'
+)
+
+# =======
+# Globals
+# =======
+
+FORMAT_TMPL_VARS = (
+ '$location', '$mask', '$mask2', '$cp', '$cpv', '$category', '$name',
+ '$version', '$revision', '$fullversion', '$slot', '$repo'
+)
+
# =======
-# Imports
+# Imports
# =======
import os
+from string import Template
import portage
-from portage.versions import catpkgsplit, vercmp
+from portage import settings
+from portage.util import LazyItemsDict
import gentoolkit.pprinter as pp
-from gentoolkit import settings, settingslock, PORTDB, VARDB
from gentoolkit import errors
-from gentoolkit.versionmatch import VersionMatch
+from gentoolkit.cpv import CPV
+from gentoolkit.dbapi import PORTDB, VARDB
+from gentoolkit.keyword import determine_keyword
+from gentoolkit.flag import get_flags
# =======
# Classes
# =======
-class Package(object):
- """Package descriptor. Contains convenience functions for querying the
- state of a package, its contents, name manipulation, ebuild info and
- similar."""
+class Package(CPV):
+ """Exposes the state of a given CPV."""
- def __init__(self, arg):
+ def __init__(self, cpv, validate=False):
+ if isinstance(cpv, CPV):
+ self.__dict__.update(cpv.__dict__)
+ else:
+ CPV.__init__(self, cpv, validate=validate)
- self._cpv = arg
- self.cpv = self._cpv
+ if validate and not all(
+ hasattr(self, x) for x in ('category', 'version')
+ ):
+ # CPV allows some things that Package must not
+ raise errors.GentoolkitInvalidPackage(self.cpv)
- if self.cpv[0] in ('<', '>'):
- if self.cpv[1] == '=':
- self.operator = self.cpv[:2]
- self.cpv = self.cpv[2:]
- else:
- self.operator = self.cpv[0]
- self.cpv = self.cpv[1:]
- elif self.cpv[0] == '=':
- if self.cpv[-1] == '*':
- self.operator = '=*'
- self.cpv = self.cpv[1:-1]
- else:
- self.cpv = self.cpv[1:]
- self.operator = '='
- elif self.cpv[0] == '~':
- self.operator = '~'
- self.cpv = self.cpv[1:]
- else:
- self.operator = '='
- self._cpv = '=%s' % self._cpv
+ # Set dynamically
+ self._package_path = None
+ self._dblink = None
+ self._metadata = None
+ self._deps = None
+ self._portdir_path = None
- if not portage.dep.isvalidatom(self._cpv):
- raise errors.GentoolkitInvalidCPV(self._cpv)
+ def __repr__(self):
+ return "<%s %r>" % (self.__class__.__name__, self.cpv)
- cpv_split = portage.catpkgsplit(self.cpv)
+ def __hash__(self):
+ return hash(self.cpv)
- try:
- self.key = "/".join(cpv_split[:2])
- except TypeError:
- # catpkgsplit returned None
- raise errors.GentoolkitInvalidCPV(self._cpv)
-
- cpv_split = list(cpv_split)
- if cpv_split[0] == 'null':
- cpv_split[0] = ''
- if cpv_split[3] == 'r0':
- cpv_split[3] = ''
- self.cpv_split = cpv_split
- self._scpv = self.cpv_split # XXX: namespace compatability 03/09
-
- self._db = None
- self._settings = settings
- self._settingslock = settingslock
- self._portdir_path = os.path.realpath(settings["PORTDIR"])
-
- self.category = self.cpv_split[0]
- self.name = self.cpv_split[1]
- self.version = self.cpv_split[2]
- self.revision = self.cpv_split[3]
- if not self.revision:
- self.fullversion = self.version
- else:
- self.fullversion = "%s-%s" % (self.version, self.revision)
+ def __contains__(self, key):
+ return key in self.cpv
- def __repr__(self):
- return "<%s %s @%#8x>" % (self.__class__.__name__, self._cpv, id(self))
+ def __str__(self):
+ return self.cpv
- def __eq__(self, other):
- return hash(self) == hash(other)
+ @property
+ def metadata(self):
+ """Instantiate a L{gentoolkit.metadata.MetaData} object here."""
- def __ne__(self, other):
- return hash(self) != hash(other)
+ from gentoolkit.metadata import MetaData
- def __lt__(self, other):
- if not isinstance(other, self.__class__):
- raise TypeError("other isn't of %s type, is %s" %
- (self.__class__, other.__class__))
+ if self._metadata is None:
+ metadata_path = os.path.join(
+ self.package_path(), 'metadata.xml'
+ )
+ try:
+ self._metadata = MetaData(metadata_path)
+ except IOError as e:
+ import errno
+ if e.errno != errno.ENOENT:
+ raise
+ return None
- if self.category != other.category:
- return self.category < other.category
- elif self.name != other.name:
- return self.name < other.name
- else:
- # FIXME: this cmp() hack is for vercmp not using -1,0,1
- # See bug 266493; this was fixed in portage-2.2_rc31
- #return portage.vercmp(self.fullversion, other.fullversion)
- result = cmp(portage.vercmp(self.fullversion, other.fullversion), 0)
- if result == -1:
- return True
- else:
- return False
+ return self._metadata
- def __gt__(self, other):
- return not self.__lt__(other)
+ @property
+ def dblink(self):
+ """Instantiate a L{portage.dbapi.vartree.dblink} object here."""
- def __hash__(self):
- return hash(self._cpv)
+ if self._dblink is None:
+ self._dblink = portage.dblink(
+ self.category,
+ "%s-%s" % (self.name, self.fullversion),
+ settings["ROOT"],
+ settings
+ )
- def __contains__(self, key):
- return key in self._cpv
-
- def __str__(self):
- return self._cpv
+ return self._dblink
+
+ @property
+ def deps(self):
+ """Instantiate a L{gentoolkit.dependencies.Dependencies} object here."""
+
+ from gentoolkit.dependencies import Dependencies
+
+ if self._deps is None:
+ self._deps = Dependencies(self.cpv)
+
+ return self._deps
+
+ def environment(self, envvars, prefer_vdb=True, fallback=True):
+ """Returns one or more of the predefined environment variables.
+
+ Some available envvars are:
+ ----------------------
+ BINPKGMD5 COUNTER FEATURES LICENSE SRC_URI
+ CATEGORY CXXFLAGS HOMEPAGE PDEPEND USE
+ CBUILD DEFINED_PHASES INHERITED PF
+ CFLAGS DEPEND IUSE PROVIDE
+ CHOST DESCRIPTION KEYWORDS RDEPEND
+ CONTENTS EAPI LDFLAGS SLOT
+
+ Example usage:
+ >>> pkg = Package('sys-apps/portage-2.1.6.13')
+ >>> pkg.environment('USE')
+ 'elibc_glibc kernel_linux userland_GNU x86'
+ >>> pkg.environment(('USE', 'IUSE'))
+ ['elibc_glibc kernel_linux userland_GNU x86',
+ 'build doc epydoc selinux linguas_pl']
+
+ @type envvars: str or array
+ @param envvars: one or more of (DEPEND, SRC_URI, etc.)
+ @type prefer_vdb: bool
+ @keyword prefer_vdb: if True, look in the vardb before portdb, else
+ reverse order. Specifically KEYWORDS will get more recent
+ information by preferring portdb.
+ @type fallback: bool
+ @keyword fallback: query only the preferred db if False
+ @rtype: str or list
+ @return: str if envvars is str, list if envvars is array
+ @raise KeyError: if key is not found in requested db(s)
+ """
- def get_name(self):
- """Returns base name of package, no category nor version"""
- return self.name
+ got_string = False
+ if isinstance(envvars, str):
+ got_string = True
+ envvars = (envvars,)
+ if prefer_vdb:
+ try:
+ result = VARDB.aux_get(self.cpv, envvars)
+ except KeyError:
+ try:
+ if not fallback:
+ raise KeyError
+ result = PORTDB.aux_get(self.cpv, envvars)
+ except KeyError:
+ err = "aux_get returned unexpected results"
+ raise errors.GentoolkitFatalError(err)
+ else:
+ try:
+ result = PORTDB.aux_get(self.cpv, envvars)
+ except KeyError:
+ try:
+ if not fallback:
+ raise KeyError
+ result = VARDB.aux_get(self.cpv, envvars)
+ except KeyError:
+ err = "aux_get returned unexpected results"
+ raise errors.GentoolkitFatalError(err)
+
+ if got_string:
+ return result[0]
+ return result
- def get_version(self):
- """Returns version of package, with revision number"""
- return self.fullversion
+ def exists(self):
+ """Return True if package exists in the Portage tree, else False"""
- def get_category(self):
- """Returns category of package"""
- return self.category
+ return bool(PORTDB.cpv_exists(self.cpv))
- def get_settings(self, key):
- """Returns the value of the given key for this package (useful
+ @staticmethod
+ def settings(key):
+ """Returns the value of the given key for this package (useful
for package.* files."""
+
+ if settings.locked:
+ settings.unlock()
try:
- self._settingslock.acquire()
- self._settings.setcpv(self.cpv)
- result = self._settings[key]
+ result = settings[key]
finally:
- self._settingslock.release()
+ settings.lock()
return result
- def get_cpv(self):
- """Returns full Category/Package-Version string"""
- return self.cpv
+ def mask_status(self):
+ """Shortcut to L{portage.getmaskingstatus}.
+
+ @rtype: None or list
+ @return: a list containing none or some of:
+ 'profile'
+ 'package.mask'
+ license(s)
+ "kmask" keyword
+ 'missing keyword'
+ """
+
+ if settings.locked:
+ settings.unlock()
+ try:
+ result = portage.getmaskingstatus(self.cpv,
+ settings=settings,
+ portdb=PORTDB)
+ except KeyError:
+ # getmaskingstatus doesn't support packages without ebuilds in the
+ # Portage tree.
+ result = None
- def get_provide(self):
- """Return a list of provides, if any"""
- if self.is_installed():
- result = VARDB.get_provide(self.cpv)
- else:
- try:
- result = [self.get_env_var('PROVIDE')]
- except KeyError:
- result = []
return result
- def get_dependants(self):
- """Retrieves a list of CPVs for all packages depending on this one"""
- raise NotImplementedError("Not implemented yet!")
+ def mask_reason(self):
+ """Shortcut to L{portage.getmaskingreason}.
- def get_runtime_deps(self):
- """Returns a linearised list of first-level run time dependencies for
- this package, on the form [(comparator, [use flags], cpv), ...]
+ @rtype: None or tuple
+ @return: empty tuple if pkg not masked OR
+ ('mask reason', 'mask location')
"""
- # Try to use the portage tree first, since emerge only uses the tree
- # when calculating dependencies
+
try:
- rdepends = self.get_env_var("RDEPEND", PORTDB).split()
+ result = portage.getmaskingreason(self.cpv,
+ settings=settings,
+ portdb=PORTDB,
+ return_location=True)
+ if result is None:
+ result = tuple()
except KeyError:
- rdepends = self.get_env_var("RDEPEND", VARDB).split()
- return self._parse_deps(rdepends)[0]
+ # getmaskingstatus doesn't support packages without ebuilds in the
+ # Portage tree.
+ result = None
- def get_compiletime_deps(self):
- """Returns a linearised list of first-level compile time dependencies
- for this package, on the form [(comparator, [use flags], cpv), ...]
+ return result
+
+ def ebuild_path(self, in_vartree=False):
+ """Returns the complete path to the .ebuild file.
+
+ Example usage:
+ >>> pkg.ebuild_path()
+ '/usr/portage/sys-apps/portage/portage-2.1.6.13.ebuild'
+ >>> pkg.ebuild_path(in_vartree=True)
+ '/var/db/pkg/sys-apps/portage-2.1.6.13/portage-2.1.6.13.ebuild'
"""
- # Try to use the portage tree first, since emerge only uses the tree
- # when calculating dependencies
- try:
- depends = self.get_env_var("DEPEND", PORTDB).split()
- except KeyError:
- depends = self.get_env_var("DEPEND", VARDB).split()
- return self._parse_deps(depends)[0]
- def get_postmerge_deps(self):
- """Returns a linearised list of first-level post merge dependencies
- for this package, on the form [(comparator, [use flags], cpv), ...]
+ if in_vartree:
+ return VARDB.findname(self.cpv)
+ return PORTDB.findname(self.cpv)
+
+ def package_path(self, in_vartree=False):
+ """Return the path to where the ebuilds and other files reside."""
+
+ if in_vartree:
+ return self.dblink.getpath()
+ return os.sep.join(self.ebuild_path().split(os.sep)[:-1])
+
+ def repo_name(self, fallback=True):
+ """Determine the repository name.
+
+ @type fallback: bool
+ @param fallback: if the repo_name file does not exist, return the
+ repository name from the path
+ @rtype: str
+ @return: output of the repository metadata file, which stores the
+ repo_name variable, or try to get the name of the repo from
+ the path.
+ @raise GentoolkitFatalError: if fallback is False and repo_name is
+ not specified by the repository.
"""
- # Try to use the portage tree first, since emerge only uses the tree
- # when calculating dependencies
+
try:
- postmerge_deps = self.get_env_var("PDEPEND", PORTDB).split()
- except KeyError:
- postmerge_deps = self.get_env_var("PDEPEND", VARDB).split()
- return self._parse_deps(postmerge_deps)[0]
+ return self.environment('repository')
+ except errors.GentoolkitFatalError:
+ if fallback:
+ return self.package_path().split(os.sep)[-3]
+ raise
- def intersects(self, other):
- """Check if a passed in package atom "intersects" this atom.
+ def use(self):
+ """Returns the USE flags active at time of installation."""
- Lifted from pkgcore.
+ return self.dblink.getstring("USE")
- Two atoms "intersect" if a package can be constructed that
- matches both:
- - if you query for just "dev-lang/python" it "intersects" both
- "dev-lang/python" and ">=dev-lang/python-2.4"
- - if you query for "=dev-lang/python-2.4" it "intersects"
- ">=dev-lang/python-2.4" and "dev-lang/python" but not
- "<dev-lang/python-2.3"
+ def use_status(self):
+ """Returns the USE flags active for installation."""
- @type other: L{gentoolkit.package.Package}
- @param other: other package to compare
- @see: pkgcore.ebuild.atom.py
+ iuse, final_flags = get_flags(self.cpv, final_setting=True)
+ return final_flags
+
+ def parsed_contents(self):
+ """Returns the parsed CONTENTS file.
+
+ @rtype: dict
+ @return: {'/full/path/to/obj': ['type', 'timestamp', 'md5sum'], ...}
"""
- # Our "key" (cat/pkg) must match exactly:
- if self.key != other.key:
- return False
-
- # If we are both "unbounded" in the same direction we intersect:
- if (('<' in self.operator and '<' in other.operator) or
- ('>' in self.operator and '>' in other.operator)):
- return True
-
- # If one of us is an exact match we intersect if the other matches it:
- if self.operator == '=':
- if other.operator == '=*':
- return self.fullversion.startswith(other.fullversion)
- return VersionMatch(other).match(self)
- if other.operator == '=':
- if self.operator == '=*':
- return other.fullversion.startswith(self.fullversion)
- return VersionMatch(self).match(other)
-
- # If we are both ~ matches we match if we are identical:
- if self.operator == other.operator == '~':
- return (self.version == other.version and
- self.revision == other.revision)
-
- # If we are both glob matches we match if one of us matches the other.
- if self.operator == other.operator == '=*':
- return (self.fullver.startswith(other.fullver) or
- other.fullver.startswith(self.fullver))
-
- # If one of us is a glob match and the other a ~ we match if the glob
- # matches the ~ (ignoring a revision on the glob):
- if self.operator == '=*' and other.operator == '~':
- return other.fullversion.startswith(self.version)
- if other.operator == '=*' and self.operator == '~':
- return self.fullversion.startswith(other.version)
-
- # If we get here at least one of us is a <, <=, > or >=:
- if self.operator in ('<', '<=', '>', '>='):
- ranged, other = self, other
- else:
- ranged, other = other, self
-
- if '<' in other.operator or '>' in other.operator:
- # We are both ranged, and in the opposite "direction" (or
- # we would have matched above). We intersect if we both
- # match the other's endpoint (just checking one endpoint
- # is not enough, it would give a false positive on <=2 vs >2)
- return (
- VersionMatch(other).match(ranged) and
- VersionMatch(ranged).match(other))
-
- if other.operator == '~':
- # Other definitely matches its own version. If ranged also
- # does we're done:
- if VersionMatch(ranged).match(other):
- return True
- # The only other case where we intersect is if ranged is a
- # > or >= on other's version and a nonzero revision. In
- # that case other will match ranged. Be careful not to
- # give a false positive for ~2 vs <2 here:
- return ranged.operator in ('>', '>=') and VersionMatch(
- other.operator, other.version, other.revision).match(ranged)
-
- if other.operator == '=*':
- # a glob match definitely matches its own version, so if
- # ranged does too we're done:
- if VersionMatch(
- ranged.operator, ranged.version, ranged.revision).match(other):
- return True
- if '<' in ranged.operator:
- # If other.revision is not defined then other does not
- # match anything smaller than its own fullver:
- if not other.revision:
- return False
-
- # If other.revision is defined then we can always
- # construct a package smaller than other.fullver by
- # tagging e.g. an _alpha1 on.
- return ranged.fullversion.startswith(other.version)
- else:
- # Remaining cases where this intersects: there is a
- # package greater than ranged.fullver and
- # other.fullver that they both match.
- return ranged.fullversion.startswith(other.version)
-
- # Handled all possible ops.
- raise NotImplementedError(
- 'Someone added an operator without adding it to intersects')
-
-
- def _parse_deps(self,deps,curuse=[],level=0):
- # store (comparator, [use predicates], cpv)
- r = []
- comparators = ["~","<",">","=","<=",">="]
- end = len(deps)
- i = 0
- while i < end:
- tok = deps[i]
- if tok == ')':
- return r,i
- if tok[-1] == "?":
- tok = tok.replace("?","")
- sr,l = self._parse_deps(deps[i+2:],curuse=curuse+[tok],level=level+1)
- r += sr
- i += l + 3
- continue
- if tok == "||":
- sr,l = self._parse_deps(deps[i+2:],curuse,level=level+1)
- r += sr
- i += l + 3
- continue
- # conjunction, like in "|| ( ( foo bar ) baz )" => recurse
- if tok == "(":
- sr,l = self._parse_deps(deps[i+1:],curuse,level=level+1)
- r += sr
- i += l + 2
+
+ return self.dblink.getcontents()
+
+ def size(self):
+ """Estimates the installed size of the contents of this package.
+
+ @rtype: tuple
+ @return: (size, number of files in total, number of uncounted files)
+ """
+
+ seen = set()
+ size = n_files = n_uncounted = 0
+ for f in self.parsed_contents():
+ try:
+ st = os.lstat(f)
+ except OSError:
continue
- # pkg block "!foo/bar" => ignore it
- if tok[0] == "!":
- i += 1
+
+ # Remove hardlinks by checking for duplicate inodes. Bug #301026.
+ file_inode = st.st_ino
+ if file_inode in seen:
continue
- # pick out comparator, if any
- cmp = ""
- for c in comparators:
- if tok.find(c) == 0:
- cmp = c
- tok = tok[len(cmp):]
- r.append((cmp,curuse,tok))
- i += 1
- return r,i
+ seen.add(file_inode)
+
+ try:
+ size += st.st_size
+ n_files += 1
+ except OSError:
+ n_uncounted += 1
+
+ return (size, n_files, n_uncounted)
def is_installed(self):
- """Returns True if this package is installed (merged)"""
- return VARDB.cpv_exists(self.cpv)
+ """Returns True if this package is installed (merged)."""
+
+ return self.dblink.exists()
def is_overlay(self):
"""Returns True if the package is in an overlay."""
- ebuild, tree = portage.portdb.findname2(self.cpv)
- return tree != self._portdir_path
- def is_masked(self):
- """Returns true if this package is masked against installation.
- Note: We blindly assume that the package actually exists on disk
- somewhere."""
- unmasked = portage.portdb.xmatch("match-visible", self.cpv)
- return self.cpv not in unmasked
-
- def get_ebuild_path(self, in_vartree=False):
- """Returns the complete path to the .ebuild file"""
- if in_vartree:
- return VARDB.getebuildpath(self.cpv)
- return PORTDB.findname(self.cpv)
+ ebuild, tree = PORTDB.findname2(self.cpv)
+ if not ebuild:
+ return None
+ if self._portdir_path is None:
+ self._portdir_path = os.path.realpath(settings["PORTDIR"])
+ return (tree and tree != self._portdir_path)
- def get_package_path(self):
- """Returns the path to where the ChangeLog, Manifest, .ebuild files
- reside"""
- ebuild_path = self.get_ebuild_path()
- path_split = ebuild_path.split("/")
- if path_split:
- return os.sep.join(path_split[:-1])
-
- def get_env_var(self, var, tree=None):
- """Returns one of the predefined env vars DEPEND, RDEPEND,
- SRC_URI,...."""
- if tree == None:
- tree = VARDB
- if not self.is_installed():
- tree = PORTDB
- result = tree.aux_get(self.cpv, [var])
- if not result:
- raise errors.GentoolkitFatalError("Could not find the package tree")
- if len(result) != 1:
- raise errors.GentoolkitFatalError("Should only get one element!")
- return result[0]
-
- def get_use_flags(self):
- """Returns the USE flags active at time of installation"""
- self._initdb()
- if self.is_installed():
- return self._db.getfile("USE")
-
- def get_contents(self):
- """Returns the full contents, as a dictionary, in the form
- ['/bin/foo' : [ 'obj', '1052505381', '45ca8b89751...' ], ... ]"""
- self._initdb()
- if self.is_installed():
- return self._db.getcontents()
- return {}
+ def is_masked(self):
+ """Returns True if this package is masked against installation.
- def size(self):
- """Estimates the installed size of the contents of this package,
- if possible.
- Returns (size, number of files in total, number of uncounted files)
+ @note: We blindly assume that the package actually exists on disk.
"""
- contents = self.get_contents()
- size = 0
- uncounted = 0
- files = 0
- for x in contents:
- try:
- size += os.lstat(x).st_size
- files += 1
- except OSError:
- uncounted += 1
- return (size, files, uncounted)
- def _initdb(self):
- """Internal helper function; loads package information from disk,
- when necessary.
- """
- if not self._db:
- self._db = portage.dblink(
- self.category,
- "%s-%s" % (self.name, self.fullversion),
- settings["ROOT"],
- settings
- )
+ unmasked = PORTDB.xmatch("match-visible", self.cpv)
+ return self.cpv not in unmasked
class PackageFormatter(object):
"""When applied to a L{gentoolkit.package.Package} object, determine the
location (Portage Tree vs. overlay), install status and masked status. That
information can then be easily formatted and displayed.
-
+
Example usage:
- >>> from gentoolkit.helpers2 import find_packages
+ >>> from gentoolkit.helpers import find_packages
>>> from gentoolkit.package import PackageFormatter
>>> pkgs = [PackageFormatter(x) for x in find_packages('gcc')]
>>> for pkg in pkgs:
@@ -457,49 +399,85 @@ class PackageFormatter(object):
... # tree
... if set('IP').issubset(pkg.location):
... print pkg
- ...
+ ...
[IP-] [ ] sys-devel/gcc-4.3.2-r3 (4.3)
@type pkg: L{gentoolkit.package.Package}
@param pkg: package to format
- @type format: L{bool}
- @param format: Whether to format the package name or not.
- Essentially C{format} should be set to False when piping or when
- quiet output is desired. If C{format} is False, only the location
+ @type do_format: bool
+ @param do_format: Whether to format the package name or not.
+ Essentially C{do_format} should be set to False when piping or when
+ quiet output is desired. If C{do_format} is False, only the location
attribute will be created to save time.
"""
- def __init__(self, pkg, format=True):
- location = ''
- maskmodes = [' ', ' ~', ' -', 'M ', 'M~', 'M-']
-
+ _tmpl_verbose = "[$location] [$mask] $cpv:$slot"
+ _tmpl_quiet = "$cpv"
+
+ def __init__(self, pkg, do_format=True, custom_format=None):
+ self._pkg = None
+ self._do_format = do_format
+ self._str = None
+ self._location = None
+ if not custom_format:
+ if do_format:
+ custom_format = self._tmpl_verbose
+ else:
+ custom_format = self._tmpl_quiet
+ self.tmpl = Template(custom_format)
+ self.format_vars = LazyItemsDict()
self.pkg = pkg
- self.format = format
- if format:
- self.arch = settings["ARCH"]
- self.mask = maskmodes[self.get_mask_status()]
- self.slot = pkg.get_env_var("SLOT")
- self.location = self.get_package_location()
def __repr__(self):
return "<%s %s @%#8x>" % (self.__class__.__name__, self.pkg, id(self))
def __str__(self):
- if self.format:
- return "[%(location)s] [%(mask)s] %(package)s (%(slot)s)" % {
- 'location': self.location,
- 'mask': pp.maskflag(self.mask),
- 'package': pp.cpv(self.pkg.cpv),
- 'slot': self.slot
- }
- else:
- return self.pkg.cpv
-
- def get_package_location(self):
- """Get the physical location of a package on disk.
+ if self._str is None:
+ self._str = self.tmpl.safe_substitute(self.format_vars)
+ return self._str
+
+ @property
+ def location(self):
+ if self._location is None:
+ self._location = self.format_package_location()
+ return self._location
+
+ @property
+ def pkg(self):
+ """Package to format"""
+ return self._pkg
+
+ @pkg.setter
+ def pkg(self, value):
+ if self._pkg == value:
+ return
+ self._pkg = value
+ self._location = None
+
+ fmt_vars = self.format_vars
+ self.format_vars.clear()
+ fmt_vars.addLazySingleton("location",
+ lambda: getattr(self, "location"))
+ fmt_vars.addLazySingleton("mask", self.format_mask)
+ fmt_vars.addLazySingleton("mask2", self.format_mask_status2)
+ fmt_vars.addLazySingleton("cpv", self.format_cpv)
+ fmt_vars.addLazySingleton("cp", self.format_cpv, "cp")
+ fmt_vars.addLazySingleton("category", self.format_cpv, "category")
+ fmt_vars.addLazySingleton("name", self.format_cpv, "name")
+ fmt_vars.addLazySingleton("version", self.format_cpv, "version")
+ fmt_vars.addLazySingleton("revision", self.format_cpv, "revision")
+ fmt_vars.addLazySingleton("fullversion", self.format_cpv,
+ "fullversion")
+ fmt_vars.addLazySingleton("slot", self.format_slot)
+ fmt_vars.addLazySingleton("repo", self.pkg.repo_name)
+
+ def format_package_location(self):
+ """Get the install status (in /var/db/?) and origin (from an overlay
+ and the Portage tree?).
@rtype: str
@return: one of:
+ 'I--' : Installed but ebuild doesn't exist on system anymore
'-P-' : Not installed and from the Portage tree
'--O' : Not installed and from an overlay
'IP-' : Installed and from the Portage tree
@@ -510,37 +488,87 @@ class PackageFormatter(object):
if self.pkg.is_installed():
result[0] = 'I'
- if self.pkg.is_overlay():
+
+ overlay = self.pkg.is_overlay()
+ if overlay is None:
+ pass
+ elif overlay:
result[2] = 'O'
else:
result[1] = 'P'
return ''.join(result)
- def get_mask_status(self):
- """Get the mask status of a given package.
+ def format_mask_status(self):
+ """Get the mask status of a given package.
- @type pkg: L{gentoolkit.package.Package}
- @param pkg: pkg to get mask status of
- @type arch: str
- @param arch: output of gentoolkit.settings["ARCH"]
- @rtype: int
- @return: an index for this list: [" ", " ~", " -", "M ", "M~", "M-"]
+ @rtype: tuple: (int, list)
+ @return: int = an index for this list:
+ [" ", " ~", " -", "M ", "M~", "M-", "??"]
0 = not masked
1 = keyword masked
2 = arch masked
3 = hard masked
4 = hard and keyword masked,
5 = hard and arch masked
+ 6 = ebuild doesn't exist on system anymore
+
+ list = original output of portage.getmaskingstatus
+ """
+
+ result = 0
+ masking_status = self.pkg.mask_status()
+ if masking_status is None:
+ return (6, [])
+
+ if ("~%s keyword" % self.pkg.settings("ARCH")) in masking_status:
+ result += 1
+ if "missing keyword" in masking_status:
+ result += 2
+ if set(('profile', 'package.mask')).intersection(masking_status):
+ result += 3
+
+ return (result, masking_status)
+
+ def format_mask_status2(self):
+ """Get the mask status of a given package.
"""
+ mask = self.pkg.mask_status()
+ if mask:
+ return pp.masking(mask)
+ else:
+ arch = self.pkg.settings("ARCH")
+ keywords = self.pkg.environment('KEYWORDS')
+ mask = [determine_keyword(arch,
+ portage.settings["ACCEPT_KEYWORDS"],
+ keywords)]
+ return pp.masking(mask)
+
+ def format_mask(self):
+ maskmodes = [' ', ' ~', ' -', 'M ', 'M~', 'M-', '??']
+ maskmode = maskmodes[self.format_mask_status()[0]]
+ return pp.keyword(
+ maskmode,
+ stable=not maskmode.strip(),
+ hard_masked=set(('M', '?', '-')).intersection(maskmode)
+ )
+
+ def format_cpv(self, attr=None):
+ if attr is None:
+ value = self.pkg.cpv
+ else:
+ value = getattr(self.pkg, attr)
+ if self._do_format:
+ return pp.cpv(value)
+ else:
+ return value
+
+ def format_slot(self):
+ value = self.pkg.environment("SLOT")
+ if self._do_format:
+ return pp.slot(value)
+ else:
+ return value
- keywords = self.pkg.get_env_var("KEYWORDS").split()
- mask_status = 0
- if self.pkg.is_masked():
- mask_status += 3
- if ("~%s" % self.arch) in keywords:
- mask_status += 1
- elif ("-%s" % self.arch) in keywords or "-*" in keywords:
- mask_status += 2
- return mask_status
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/pprinter.py b/pym/gentoolkit/pprinter.py
index ff92a26..7ab60dc 100644
--- a/pym/gentoolkit/pprinter.py
+++ b/pym/gentoolkit/pprinter.py
@@ -1,116 +1,185 @@
#!/usr/bin/python
#
# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
-# Copyright 2004 Gentoo Foundation
+# Copyright 2004-2009 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
#
# $Header$
-import sys
-import gentoolkit
-
-try:
- import portage.output as output
-except ImportError:
- import output
-
-
-def print_error(s):
- """Prints an error string to stderr."""
- sys.stderr.write(output.red("!!! ") + s + "\n")
-
-def print_info(lv, s, line_break = True):
- """Prints an informational string to stdout."""
- if gentoolkit.Config["verbosityLevel"] >= lv:
- sys.stdout.write(s)
- if line_break:
- sys.stdout.write("\n")
-
-def print_warn(s):
- """Print a warning string to stderr."""
- sys.stderr.write("!!! " + s + "\n")
-
-def die(err, s):
- """Print an error string and die with an error code."""
- print_error(s)
- sys.exit(err)
-
-# Colour settings
+"""Provides a consistent color scheme for Gentoolkit scripts."""
+
+__all__ = (
+ 'command',
+ 'cpv',
+ 'die',
+ 'emph',
+ 'error',
+ 'globaloption',
+ 'installedflag',
+ 'localoption',
+ 'number',
+ 'path',
+ 'path_symlink',
+ 'pkgquery',
+ 'productname',
+ 'regexpquery',
+ 'section',
+ 'slot',
+ 'subsection',
+ 'useflag',
+ 'warn'
+)
+
+# =======
+# Imports
+# =======
-def cpv(s):
- """Print a category/package-<version> string."""
- return output.green(s)
-
-def slot(s):
- """Print a slot string"""
- return output.bold(s)
-
-def useflag(s):
- """Print a USE flag strign"""
- return output.blue(s)
-
-def useflagon(s):
- """Print an enabled USE flag string"""
- # FIXME: Collapse into useflag with parameter
- return output.red(s)
-
-def useflagoff(s):
- """Print a disabled USE flag string"""
- # FIXME: Collapse into useflag with parameter
- return output.blue(s)
-
-def maskflag(s):
- """Print a masking flag string"""
- return output.red(s)
+import sys
+import locale
-def installedflag(s):
- """Print an installed flag string"""
- return output.bold(s)
-
-def number(s):
- """Print a number string"""
- return output.turquoise(s)
+import portage.output as output
+from portage import archlist
-def pkgquery(s):
- """Print a package query string."""
- return output.bold(s)
+# =========
+# Functions
+# =========
-def regexpquery(s):
- """Print a regular expression string"""
- return output.bold(s)
+# output creates color functions on the fly, which confuses pylint.
+# E1101: *%s %r has no %r member*
+# pylint: disable-msg=E1101
-def path(s):
- """Print a file or directory path string"""
- return output.bold(s)
+def command(string):
+ """Returns a program command string."""
+ return output.green(string)
-def path_symlink(s):
- """Print a symlink string."""
- return output.turquoise(s)
+def cpv(string):
+ """Returns a category/package-<version> string."""
+ return output.green(string)
-def productname(s):
- """Print a product name string, i.e. the program name."""
- return output.turquoise(s)
-
-def globaloption(s):
- """Print a global option string, i.e. the program global options."""
- return output.yellow(s)
+def die(err, string):
+ """Returns an error string and die with an error code."""
+ sys.stderr.write(error(string))
+ sys.exit(err)
-def localoption(s):
- """Print a local option string, i.e. the program local options."""
- return output.green(s)
+def emph(string):
+ """Returns a string as emphasized."""
+ return output.bold(string)
+
+def error(string):
+ """Prints an error string."""
+ return output.red("!!! ") + string + "\n"
+
+def globaloption(string):
+ """Returns a global option string, i.e. the program global options."""
+ return output.yellow(string)
+
+def localoption(string):
+ """Returns a local option string, i.e. the program local options."""
+ return output.green(string)
+
+def number(string):
+ """Returns a number string."""
+ return output.turquoise(string)
+
+def path(string):
+ """Returns a file or directory path string."""
+ return output.bold(string)
+
+def path_symlink(string):
+ """Returns a symlink string."""
+ return output.turquoise(string)
+
+def pkgquery(string):
+ """Returns a package query string."""
+ return output.bold(string)
+
+def productname(string):
+ """Returns a product name string, i.e. the program name."""
+ return output.turquoise(string)
+
+def regexpquery(string):
+ """Returns a regular expression string."""
+ return output.bold(string)
+
+def section(string):
+ """Returns a string as a section header."""
+ return output.turquoise(string)
+
+def slot(string):
+ """Returns a slot string"""
+ return output.bold(string)
+
+def subsection(string):
+ """Returns a string as a subsection header."""
+ return output.turquoise(string)
+
+def useflag(string, enabled=True):
+ """Returns a USE flag string."""
+ return output.blue(string) if enabled else output.red(string)
+
+def keyword(string, stable=True, hard_masked=False):
+ """Returns a keyword string."""
+ if stable:
+ return output.green(string)
+ if hard_masked:
+ return output.red(string)
+ # keyword masked:
+ return output.blue(string)
+
+def masking(mask):
+ """Returns a 'masked by' string."""
+ if 'package.mask' in mask or 'profile' in mask:
+ # use porthole wrap style to help clarify meaning
+ return output.red("M["+mask[0]+"]")
+ if mask is not []:
+ for status in mask:
+ if 'keyword' in status:
+ # keyword masked | " [missing keyword] " <=looks better
+ return output.blue("["+status+"]")
+ if status in archlist:
+ return output.green(status)
+ if 'unknown' in status:
+ return output.yellow(status)
+ return output.red(status)
+ return ''
+
+def warn(string):
+ """Returns a warning string."""
+ return "!!! " + string + "\n"
-def command(s):
- """Print a program command string."""
- return output.green(s)
-
-def section(s):
- """Print a string as a section header."""
- return output.turquoise(s)
+try:
+ unicode
+except NameError:
+ unicode = str
-def subsection(s):
- """Print a string as a subsection header."""
- return output.turquoise(s)
+def uprint(*args, **kw):
+ """Replacement for the builtin print function.
-def emph(s):
- """Print a string as emphasized."""
- return output.bold(s)
+ This version gracefully handles characters not representable in the
+ user's current locale (through the errors='replace' handler).
+
+ @see: >>> help(print)
+ """
+
+ sep = kw.pop('sep', ' ')
+ end = kw.pop('end', '\n')
+ file = kw.pop("file", sys.stdout)
+ if kw:
+ raise TypeError("got invalid keyword arguments: {0}".format(list(kw)))
+ file = getattr(file, 'buffer', file)
+
+ encoding = locale.getpreferredencoding()
+
+ def encoded_args():
+ for arg in args:
+ if isinstance(arg, bytes):
+ yield arg
+ else:
+ yield unicode(arg).encode(encoding, 'replace')
+
+ sep = sep.encode(encoding, 'replace')
+ end = end.encode(encoding, 'replace')
+ text = sep.join(encoded_args())
+ file.write(text + end)
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/query.py b/pym/gentoolkit/query.py
new file mode 100644
index 0000000..9ad9017
--- /dev/null
+++ b/pym/gentoolkit/query.py
@@ -0,0 +1,382 @@
+#!/usr/bin/python
+#
+# Copyright(c) 2004-2010, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+"""Provides common methods on a package query."""
+
+__all__ = (
+ 'Query',
+)
+
+# =======
+# Imports
+# =======
+
+import fnmatch
+import re
+from functools import partial
+from string import ascii_letters
+
+import portage
+
+from gentoolkit import CONFIG
+from gentoolkit import errors
+from gentoolkit import helpers
+from gentoolkit import pprinter as pp
+from gentoolkit.atom import Atom
+from gentoolkit.cpv import CPV
+from gentoolkit.dbapi import PORTDB, VARDB
+from gentoolkit.package import Package
+from gentoolkit.sets import get_set_atoms, SETPREFIX
+
+# =======
+# Classes
+# =======
+
+class Query(CPV):
+ """Provides common methods on a package query."""
+
+ def __init__(self, query, is_regex=False):
+ """Create query object.
+
+ @type is_regex: bool
+ @param is_regex: query is a regular expression
+ """
+
+ # We need at least one of these chars for a valid query
+ needed_chars = ascii_letters + '*'
+ if not set(query).intersection(needed_chars):
+ raise errors.GentoolkitInvalidPackage(query)
+
+ # Separate repository
+ repository = None
+ if query.count(':') == 2:
+ query, repository = query.rsplit(':', 1)
+ self.query = query.rstrip(':') # Don't leave dangling colon
+ self.repo_filter = repository
+ self.is_regex = is_regex
+ self.query_type = self._get_query_type()
+
+ # Name the rest of the chunks, if possible
+ if self.query_type != "set":
+ try:
+ atom = Atom(self.query)
+ self.__dict__.update(atom.__dict__)
+ except errors.GentoolkitInvalidAtom:
+ CPV.__init__(self, self.query)
+ self.operator = ''
+ self.atom = self.cpv
+
+ def __repr__(self):
+ rx = ''
+ if self.is_regex:
+ rx = ' regex'
+ repo = ''
+ if self.repo_filter:
+ repo = ' in %s' % self.repo_filter
+ return "<%s%s %r%s>" % (self.__class__.__name__, rx, self.query, repo)
+
+ def __str__(self):
+ return self.query
+
+ def print_summary(self):
+ """Print a summary of the query."""
+
+ if self.query_type == "set":
+ cat_str = ""
+ pkg_str = pp.emph(self.query)
+ else:
+ cat, pkg = self.category, self.name + self.fullversion
+ if cat and not self.is_regex:
+ cat_str = "in %s " % pp.emph(cat.lstrip('><=~!'))
+ else:
+ cat_str = ""
+
+ if self.is_regex:
+ pkg_str = pp.emph(self.query)
+ else:
+ pkg_str = pp.emph(pkg)
+
+ repo = ''
+ if self.repo_filter is not None:
+ repo = ' %s' % pp.section(self.repo_filter)
+
+ pp.uprint(" * Searching%s for %s %s..." % (repo, pkg_str, cat_str))
+
+ def smart_find(
+ self,
+ in_installed=True,
+ in_porttree=True,
+ in_overlay=True,
+ include_masked=True,
+ show_progress=True,
+ no_matches_fatal=True,
+ **kwargs
+ ):
+ """A high-level wrapper around gentoolkit package-finder functions.
+
+ @type in_installed: bool
+ @param in_installed: search for query in VARDB
+ @type in_porttree: bool
+ @param in_porttree: search for query in PORTDB
+ @type in_overlay: bool
+ @param in_overlay: search for query in overlays
+ @type show_progress: bool
+ @param show_progress: output search progress
+ @type no_matches_fatal: bool
+ @param no_matches_fatal: raise errors.GentoolkitNoMatches
+ @rtype: list
+ @return: Package objects matching query
+ """
+
+ if in_installed:
+ if in_porttree or in_overlay:
+ simple_package_finder = partial(
+ self.find,
+ include_masked=include_masked
+ )
+ complex_package_finder = helpers.get_cpvs
+ else:
+ simple_package_finder = self.find_installed
+ complex_package_finder = helpers.get_installed_cpvs
+ elif in_porttree or in_overlay:
+ simple_package_finder = partial(
+ self.find,
+ include_masked=include_masked,
+ in_installed=False
+ )
+ complex_package_finder = helpers.get_uninstalled_cpvs
+ else:
+ raise errors.GentoolkitFatalError(
+ "Not searching in installed, Portage tree, or overlay. "
+ "Nothing to do."
+ )
+
+ if self.query_type == "set":
+ self.package_finder = simple_package_finder
+ matches = self._do_set_lookup(show_progress=show_progress)
+ elif self.query_type == "simple":
+ self.package_finder = simple_package_finder
+ matches = self._do_simple_lookup(
+ in_installed=in_installed,
+ show_progress=show_progress
+ )
+ else:
+ self.package_finder = complex_package_finder
+ matches = self._do_complex_lookup(show_progress=show_progress)
+
+ if self.repo_filter is not None:
+ matches = self._filter_by_repository(matches)
+
+ if no_matches_fatal and not matches:
+ ii = in_installed and not (in_porttree or in_overlay)
+ raise errors.GentoolkitNoMatches(self.query, in_installed=ii)
+ return matches
+
+ def find(self, in_installed=True, include_masked=True):
+ """Returns a list of Package objects that matched the query.
+
+ @rtype: list
+ @return: matching Package objects
+ """
+
+ if not self.query:
+ return []
+
+ try:
+ if include_masked:
+ matches = PORTDB.xmatch("match-all", self.query)
+ else:
+ matches = PORTDB.match(self.query)
+ if in_installed:
+ matches.extend(VARDB.match(self.query))
+ except portage.exception.InvalidAtom as err:
+ message = "query.py: find(), query=%s, InvalidAtom=%s" %(
+ self.query, str(err))
+ raise errors.GentoolkitInvalidAtom(message)
+
+ return [Package(x) for x in set(matches)]
+
+ def find_installed(self):
+ """Return a list of Package objects that matched the search key."""
+
+ try:
+ matches = VARDB.match(self.query)
+ # catch the ambiguous package Exception
+ except portage.exception.AmbiguousPackageName as err:
+ matches = []
+ for pkgkey in err.args[0]:
+ matches.extend(VARDB.match(pkgkey))
+ except portage.exception.InvalidAtom as err:
+ raise errors.GentoolkitInvalidAtom(err)
+
+ return [Package(x) for x in set(matches)]
+
+ def find_best(self, include_keyworded=True, include_masked=True):
+ """Returns the "best" version available.
+
+ Order of preference:
+ highest available stable =>
+ highest available keyworded =>
+ highest available masked
+
+ @rtype: Package object or None
+ @return: best of up to three options
+ @raise errors.GentoolkitInvalidAtom: if query is not valid input
+ """
+
+ best = keyworded = masked = None
+ try:
+ best = PORTDB.xmatch("bestmatch-visible", self.query)
+ except portage.exception.InvalidAtom as err:
+ message = "query.py: find_best(), bestmatch-visible, " + \
+ "query=%s, InvalidAtom=%s" %(self.query, str(err))
+ raise errors.GentoolkitInvalidAtom(message)
+ # xmatch can return an empty string, so checking for None is not enough
+ if not best:
+ if not (include_keyworded or include_masked):
+ return None
+ try:
+ matches = PORTDB.xmatch("match-all", self.query)
+ except portage.exception.InvalidAtom as err:
+ message = "query.py: find_best(), match-all, query=%s, InvalidAtom=%s" %(
+ self.query, str(err))
+ raise errors.GentoolkitInvalidAtom(message)
+ masked = portage.best(matches)
+ keywordable = []
+ for m in matches:
+ status = portage.getmaskingstatus(m)
+ if 'package.mask' not in status or 'profile' not in status:
+ keywordable.append(m)
+ if matches:
+ keyworded = portage.best(keywordable)
+ else:
+ return Package(best)
+ if include_keyworded and keyworded:
+ return Package(keyworded)
+ if include_masked and masked:
+ return Package(masked)
+ return None
+
+ def uses_globbing(self):
+ """Check the query to see if it is using globbing.
+
+ @rtype: bool
+ @return: True if query uses globbing, else False
+ """
+
+ if set('!*?[]').intersection(self.query):
+ # Is query an atom such as '=sys-apps/portage-2.2*'?
+ if self.query[0] != '=':
+ return True
+
+ return False
+
+ def is_ranged(self):
+ """Return True if the query appears to be ranged, else False."""
+
+ q = self.query
+ return q.startswith(('~', '<', '>')) or q.endswith('*')
+
+ def _do_simple_lookup(self, in_installed=True, show_progress=True):
+ """Find matches for a query which is an atom or cpv."""
+
+ result = []
+
+ if show_progress and CONFIG['verbose']:
+ self.print_summary()
+
+ result = self.package_finder()
+ if not in_installed:
+ result = [x for x in result if not x.is_installed()]
+
+ return result
+
+ def _do_complex_lookup(self, show_progress=True):
+ """Find matches for a query which is a regex or includes globbing."""
+
+ result = []
+
+ if show_progress and not CONFIG["piping"]:
+ self.print_summary()
+
+ cat = CPV(self.query).category
+
+ pre_filter = []
+ # The "get_" functions can pre-filter against the whole package key,
+ # but since we allow globbing now, we run into issues like:
+ # >>> portage.dep.dep_getkey("sys-apps/portage-*")
+ # 'sys-apps/portage-'
+ # So the only way to guarantee we don't overrun the key is to
+ # prefilter by cat only.
+ if cat:
+ if self.is_regex:
+ cat_re = cat
+ else:
+ cat_re = fnmatch.translate(cat)
+ predicate = lambda x: re.match(cat_re, x.split("/", 1)[0])
+ pre_filter = self.package_finder(predicate=predicate)
+
+ # Post-filter
+ if self.is_regex:
+ predicate = lambda x: re.search(self.query, x)
+ else:
+ if cat:
+ query_re = fnmatch.translate(self.query)
+ else:
+ query_re = fnmatch.translate("*/%s" % self.query)
+ predicate = lambda x: re.search(query_re, x)
+ if pre_filter:
+ result = [x for x in pre_filter if predicate(x)]
+ else:
+ result = self.package_finder(predicate=predicate)
+
+ return [Package(x) for x in result]
+
+ def _do_set_lookup(self, show_progress=True):
+ """Find matches for a query that is a package set."""
+
+ if show_progress and not CONFIG["piping"]:
+ self.print_summary()
+
+ setname = self.query[len(SETPREFIX):]
+ result = []
+ try:
+ atoms = get_set_atoms(setname)
+ except errors.GentoolkitSetNotFound:
+ return result
+
+ q = self.query
+ for atom in atoms:
+ self.query = atom
+ result.extend(self._do_simple_lookup(show_progress=False))
+ self.query = q
+
+ return result
+
+ def _filter_by_repository(self, matches):
+ """Filter out packages which do not belong to self.repo_filter."""
+
+ result = []
+ for match in matches:
+ repo_name = match.repo_name()
+ if repo_name == self.repo_filter:
+ result.append(match)
+ elif (not repo_name and
+ self.repo_filter in ('unknown', 'null')):
+ result.append(match)
+
+ return result
+
+ def _get_query_type(self):
+ """Determine of what type the query is."""
+
+ if self.query.startswith(SETPREFIX):
+ return "set"
+ elif self.is_regex or self.uses_globbing():
+ return "complex"
+ return "simple"
diff --git a/pym/gentoolkit/sets.py b/pym/gentoolkit/sets.py
new file mode 100644
index 0000000..b9c3601
--- /dev/null
+++ b/pym/gentoolkit/sets.py
@@ -0,0 +1,57 @@
+# Copyright(c) 2010, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2 or higher
+#
+# $Header$
+
+"""Provides access to Portage sets api"""
+
+__docformat__ = 'epytext'
+
+import portage
+try:
+ import portage.sets
+ _sets_available = True
+ SETPREFIX = portage.sets.SETPREFIX
+except ImportError:
+ _sets_available = False
+ SETPREFIX = "@"
+
+from gentoolkit import errors
+from gentoolkit.atom import Atom
+
+
+_set_config = None
+def _init_set_config():
+ global _set_config
+ if _set_config is None:
+ _set_config = portage.sets.load_default_config(
+ portage.settings, portage.db[portage.root])
+
+def get_available_sets():
+ """Returns all available sets."""
+
+ if _sets_available:
+ _init_set_config()
+ return _set_config.getSets()
+ return {}
+
+def get_set_atoms(setname):
+ """Return atoms belonging to the given set
+
+ @type setname: string
+ @param setname: Name of the set
+ @rtype list
+ @return: List of atoms in the given set
+ """
+
+ if _sets_available:
+ _init_set_config()
+ try:
+ return set([Atom(str(x))
+ for x in _set_config.getSetAtoms(setname)])
+ except portage.sets.PackageSetNotFound:
+ raise errors.GentoolkitSetNotFound(setname)
+ raise errors.GentoolkitSetNotFound(setname)
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/test/__init__.py b/pym/gentoolkit/test/__init__.py
new file mode 100644
index 0000000..ea0f3c7
--- /dev/null
+++ b/pym/gentoolkit/test/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+# Copyright 2009 Gentoo Foundation
+#
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+__all__ = ['cmp']
+
+# py3k doesn't have cmp emulate it in order to keep testing cmp
+# in python-2.x
+#XXX: not sure if this is the best place for this
+try:
+ cmp = cmp
+except NameError:
+ def cmp(a, b):
+ if a == b:
+ return 0
+ elif a < b:
+ return -1
+ elif a > b:
+ return 1
+ # just to be safe, __lt__/ __gt__ above should have thrown
+ # something like this already
+ raise TypeError("Comparison between onorderable types")
diff --git a/pym/gentoolkit/test/eclean/Packages b/pym/gentoolkit/test/eclean/Packages
new file mode 100644
index 0000000..1275ccb
--- /dev/null
+++ b/pym/gentoolkit/test/eclean/Packages
@@ -0,0 +1,1017 @@
+ACCEPT_KEYWORDS: amd64 ~amd64
+ACCEPT_LICENSE: *
+ACCEPT_PROPERTIES: *
+CBUILD: x86_64-pc-linux-gnu
+CHOST: x86_64-pc-linux-gnu
+CONFIG_PROTECT: /etc /usr/share/X11/xkb /var/lib/hsqldb
+CONFIG_PROTECT_MASK: /etc/ca-certificates.conf /etc/env.d /etc/env.d/java/ /etc/fonts/fonts.conf /etc/gconf /etc/gentoo-release /etc/revdep-rebuild /etc/sandbox.d /etc/terminfo
+FEATURES: assume-digests buildpkg ccache distlocks fixpackages news parallel-fetch parralell-fetch preserve-libs protect-owned sandbox sfperms strict unmerge-logs unmerge-orphans userfetch
+GENTOO_MIRRORS: http://gentoo.osuosl.org/
+PACKAGES: 73
+PROFILE: default/linux/amd64/10.0/desktop
+SYNC: rsync://rsync.namerica.gentoo.org/gentoo-portage
+TIMESTAMP: 1264301192
+USE: X a52 aac aalib acl acpi adns alsa alsa_cards_ali5451 alsa_cards_als4000 alsa_cards_atiixp alsa_cards_atiixp-modem alsa_cards_bt87x alsa_cards_ca0106 alsa_cards_cmipci alsa_cards_emu10k1x alsa_cards_ens1370 alsa_cards_ens1371 alsa_cards_es1938 alsa_cards_es1968 alsa_cards_fm801 alsa_cards_hda-intel alsa_cards_intel8x0 alsa_cards_intel8x0m alsa_cards_maestro3 alsa_cards_trident alsa_cards_usb-audio alsa_cards_via82xx alsa_cards_via82xx-modem alsa_cards_ymfpci alsa_pcm_plugins_adpcm alsa_pcm_plugins_alaw alsa_pcm_plugins_asym alsa_pcm_plugins_copy alsa_pcm_plugins_dmix alsa_pcm_plugins_dshare alsa_pcm_plugins_dsnoop alsa_pcm_plugins_empty alsa_pcm_plugins_extplug alsa_pcm_plugins_file alsa_pcm_plugins_hooks alsa_pcm_plugins_iec958 alsa_pcm_plugins_ioplug alsa_pcm_plugins_ladspa alsa_pcm_plugins_lfloat alsa_pcm_plugins_linear alsa_pcm_plugins_meter alsa_pcm_plugins_mmap_emul alsa_pcm_plugins_mulaw alsa_pcm_plugins_multi alsa_pcm_plugins_null alsa_pcm_plugins_plug alsa_pcm_plugins_rate alsa_pcm_plugins_route alsa_pcm_plugins_share alsa_pcm_plugins_shm alsa_pcm_plugins_softvol amd64 apache2_modules_actions apache2_modules_alias apache2_modules_auth_basic apache2_modules_authn_alias apache2_modules_authn_anon apache2_modules_authn_dbm apache2_modules_authn_default apache2_modules_authn_file apache2_modules_authz_dbm apache2_modules_authz_default apache2_modules_authz_groupfile apache2_modules_authz_host apache2_modules_authz_owner apache2_modules_authz_user apache2_modules_autoindex apache2_modules_cache apache2_modules_dav apache2_modules_dav_fs apache2_modules_dav_lock apache2_modules_deflate apache2_modules_dir apache2_modules_disk_cache apache2_modules_env apache2_modules_expires apache2_modules_ext_filter apache2_modules_file_cache apache2_modules_filter apache2_modules_headers apache2_modules_include apache2_modules_info apache2_modules_log_config apache2_modules_logio apache2_modules_mem_cache apache2_modules_mime apache2_modules_mime_magic apache2_modules_negotiation apache2_modules_rewrite apache2_modules_setenvif apache2_modules_speling apache2_modules_status apache2_modules_unique_id apache2_modules_userdir apache2_modules_usertrack apache2_modules_vhost_alias avahi berkdb bidi bluetooth bonobo branding bzip2 cairo cddb cdr cleartype cli consolekit cracklib crypt css cups curl cxx dbus dlloader dri dts dv dvd dvdr dvdread eds elibc_glibc emboss encode evo fam fame fbcon ffmpeg flac fortran ftp gconf gdbm gecko gif gimpprint gnome gnutls gpm gs gstreamer gtk gtk2 gtkhtml h323 hal howl iconv imagemagic imap imlib2 innodb input_devices_evdev ipv6 java javascript jikes joystick jpeg kernel_linux lcd_devices_bayrad lcd_devices_cfontz lcd_devices_cfontz633 lcd_devices_glk lcd_devices_hd44780 lcd_devices_lb216 lcd_devices_lcdm001 lcd_devices_mtxorb lcd_devices_ncurses lcd_devices_text lcms ldap libnotify lm_sensors mad md5sum messages mikmod mime mmx mng modules moxnomail mozdevelop mp3 mp4 mpeg mpi mplayer mudflap multilib mysql nautilus ncurses nfs nls no-old-linux nptl nptlonly nptonly numeric nvidia ogg opengl openmp pam pcre pda pdf perl plugin png policykit posix ppds pppd python qt3support quicktime readline reflection rtc ruby_targets_ruby18 samba sdl session shm sndfile sox speex spell spl sqlite sqlite3 sse sse2 sse3 ssl startup-notification svg swat sysfs syslog tcpd threadsonly thumbnail thunar thunar-vfs tiff tk transcode truetype unicode usb userland_GNU utf8 v4l v4l2 vcd vdpau video_cards_nvidia vorbis x264 xine xml xorg xosd xpm xulrunner xv xvid xvmc zlib zvbi
+VERSION: 0
+
+CPV: app-arch/bzip2-1.0.5-r1
+DESC: A high-quality data compressor used extensively by Gentoo Linux
+IUSE: static
+KEYWORDS: alpha amd64 arm hppa ia64 m68k ~mips ppc ppc64 s390 sh sparc ~sparc-fbsd x86 ~x86-fbsd
+LICENSE: BZIP2
+MD5: ab8f256048b0167c0628f850f7187981
+MTIME: 1264262577
+SHA1: 576df7f5f21f87a3b41b99e282e5eec09a5f4325
+SIZE: 584218
+REPO: gentoo
+
+CPV: app-arch/cpio-2.10
+DESC: A file archival tool which can also read and write tar files
+IUSE: nls
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~sparc-fbsd ~x86 ~x86-fbsd
+LICENSE: GPL-3
+MD5: fd8328657cab407fd99306713187c7aa
+MTIME: 1264264179
+SHA1: f35d72268d1c7333c885454f774ce3acd14cc493
+SIZE: 196505
+USE: nls
+REPO: gentoo
+
+CPV: app-arch/cpio-2.10-r1
+DESC: A file archival tool which can also read and write tar files
+EAPI: 2
+IUSE: nls
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~sparc-fbsd ~x86 ~x86-fbsd
+LICENSE: GPL-3
+MD5: 5e69ec6d2493761c32744eca27e19009
+MTIME: 1264301003
+SHA1: 99715c7646655dc6627ec4433cefaabc9fba0076
+SIZE: 208357
+USE: nls
+REPO: gentoo
+
+CPV: app-arch/cpio-2.9-r2
+DESC: A file archival tool which can also read and write tar files
+IUSE: nls
+KEYWORDS: alpha amd64 arm hppa ia64 m68k ~mips ppc ppc64 s390 sh sparc ~sparc-fbsd x86 ~x86-fbsd
+LICENSE: GPL-3
+MD5: 55e4f7bc94b03cafce4b1afb6295c1ab
+MTIME: 1264264294
+SHA1: 3becefc751c6316966d2b7a6404c0785434e5368
+SIZE: 174202
+USE: nls
+REPO: gentoo
+
+CPV: app-arch/cpio-2.9-r3
+DESC: A file archival tool which can also read and write tar files
+IUSE: nls
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~sparc-fbsd ~x86 ~x86-fbsd
+LICENSE: GPL-3
+MD5: a947348c332c75683d66663928bbe45b
+MTIME: 1264264353
+SHA1: 6a48fb69f046e16b3c59f3b21129909d31026818
+SIZE: 174361
+USE: nls
+REPO: gentoo
+
+CPV: app-arch/file-roller-2.24.3
+DEPEND: >=dev-libs/glib-2.16.0 >=x11-libs/gtk+-2.13 >=gnome-base/libgnome-2.6 >=gnome-base/libgnomeui-2.6 >=gnome-base/libglade-2.4 >=gnome-base/gconf-2.6 >=gnome-base/nautilus-2.22.2 gnome-base/gnome-common sys-devel/gettext >=dev-util/intltool-0.35 >=dev-util/pkgconfig-0.19 >=app-text/gnome-doc-utils-0.3.2 >=sys-apps/sed-4
+DESC: archive manager for GNOME
+IUSE: nautilus
+KEYWORDS: alpha amd64 hppa ia64 ppc ppc64 sparc x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: 730b91a73bce36f7145de7dd1dc6c701
+MTIME: 1264264717
+RDEPEND: >=dev-libs/glib-2.16.0 >=x11-libs/gtk+-2.13 >=gnome-base/libgnome-2.6 >=gnome-base/libgnomeui-2.6 >=gnome-base/libglade-2.4 >=gnome-base/gconf-2.6 >=gnome-base/nautilus-2.22.2
+SHA1: df43031484955ee09402b53651d89cf3765315c3
+SIZE: 1151945
+USE: nautilus
+REPO: gentoo
+
+CPV: app-arch/file-roller-2.26.3
+DEPEND: >=dev-libs/glib-2.16.0 >=x11-libs/gtk+-2.13 gnome-base/gconf gnome-base/nautilus gnome-base/gnome-common sys-devel/gettext dev-util/intltool dev-util/pkgconfig app-text/gnome-doc-utils >=sys-apps/sed-4
+DESC: archive manager for GNOME
+EAPI: 2
+IUSE: nautilus
+KEYWORDS: alpha amd64 ~hppa ia64 ppc ppc64 sparc x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: 154f28c22b3f1c5836ba06b8f671f718
+MTIME: 1264264651
+RDEPEND: >=dev-libs/glib-2.16.0 >=x11-libs/gtk+-2.13 gnome-base/gconf gnome-base/nautilus
+SHA1: 035a1130cbd602e5872abe45e96d5f901a3de1f3
+SIZE: 1101683
+USE: nautilus
+REPO: gentoo
+
+CPV: app-arch/file-roller-2.28.1
+DEPEND: >=dev-libs/glib-2.16.0 >=x11-libs/gtk+-2.16 gnome-base/gconf gnome-base/nautilus gnome-base/gnome-common sys-devel/gettext dev-util/intltool dev-util/pkgconfig app-text/gnome-doc-utils >=sys-apps/sed-4
+DESC: archive manager for GNOME
+EAPI: 2
+IUSE: nautilus
+KEYWORDS: ~alpha ~amd64 ~hppa ~ia64 ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: 7f1154b8d357400206873187603b6e10
+MTIME: 1264264576
+RDEPEND: >=dev-libs/glib-2.16.0 >=x11-libs/gtk+-2.16 gnome-base/gconf gnome-base/nautilus
+SHA1: 43142dd2566cd1018e0f416da474b7bb441a96cb
+SIZE: 1313333
+USE: nautilus
+REPO: gentoo
+
+CPV: app-arch/file-roller-2.28.2
+DEPEND: >=dev-libs/glib-2.16.0 >=x11-libs/gtk+-2.16 gnome-base/gconf gnome-base/nautilus gnome-base/gnome-common sys-devel/gettext dev-util/intltool dev-util/pkgconfig app-text/gnome-doc-utils >=sys-apps/sed-4
+DESC: archive manager for GNOME
+EAPI: 2
+IUSE: nautilus
+KEYWORDS: ~alpha ~amd64 ~hppa ~ia64 ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd ~x86-freebsd ~amd64-linux ~x86-linux
+LICENSE: GPL-2
+MD5: ae10bc3d791c59af92f39d4a7fd87871
+MTIME: 1264301192
+RDEPEND: >=dev-libs/glib-2.16.0 >=x11-libs/gtk+-2.16 gnome-base/gconf gnome-base/nautilus
+SHA1: 3959f6022e38afd113beabff5102e500c353cf45
+SIZE: 1318363
+USE: nautilus
+REPO: gentoo
+
+CPV: app-arch/gzip-1.4
+DEPEND: sys-devel/gettext
+DESC: Standard GNU compressor
+IUSE: nls pic static
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-2
+MD5: 32ac853a6792046bda7f88767e5260db
+MTIME: 1264300929
+PROVIDE: virtual/gzip
+SHA1: eaf1de2ae3f1bc0e9c4e91c3da03e91de92e4e6b
+SIZE: 142419
+USE: nls
+REPO: gentoo
+
+CPV: app-arch/p7zip-4.57
+DESC: Port of 7-Zip archiver for Unix
+IUSE: static doc
+KEYWORDS: ~alpha amd64 hppa ~ia64 ppc ppc64 sparc x86 ~x86-fbsd
+LICENSE: LGPL-2.1
+MD5: 9ce142c7ffa21b85d5a877bb69e28677
+MTIME: 1264264235
+SHA1: 488fca992e2da7c51cb8f8325d996507f0f61b0a
+SIZE: 1533328
+REPO: gentoo
+
+CPV: app-arch/p7zip-4.58
+DESC: Port of 7-Zip archiver for Unix
+IUSE: static doc
+KEYWORDS: ~alpha amd64 hppa ~ia64 ppc ppc64 sparc x86 ~x86-fbsd
+LICENSE: LGPL-2.1
+MD5: 79249e03c6ff6c961627d65c68120e37
+MTIME: 1264264131
+SHA1: af33c7a2153e9c6acb914b8a0ed0e5c74bedfc9e
+SIZE: 1564378
+REPO: gentoo
+
+CPV: app-arch/p7zip-4.65
+DEPEND: x11-libs/wxGTK:2.8[X,-odbc]
+DESC: Port of 7-Zip archiver for Unix
+EAPI: 2
+IUSE: doc kde rar static wxwidgets
+KEYWORDS: ~alpha ~amd64 ~hppa ~ia64 ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd ~x86-freebsd ~x86-interix ~amd64-linux ~x86-linux ~ppc-macos ~x86-macos
+LICENSE: LGPL-2.1
+MD5: 95d47a6dd8feff8c0b84f997927a25b0
+MTIME: 1264301127
+RDEPEND: x11-libs/wxGTK:2.8[X,-odbc]
+SHA1: 82dcf81c00866f120bc35a3b8fb2237c8067df57
+SIZE: 2278496
+USE: wxwidgets
+REPO: gentoo
+
+CPV: app-arch/rar-3.9.0
+DESC: RAR compressor/uncompressor
+KEYWORDS: -* ~amd64 ~x86
+LICENSE: RAR
+MD5: c1de3dade9c34089da431aa69497dcb1
+MTIME: 1264262625
+RDEPEND: >=sys-libs/glibc-2.4
+SHA1: 83ba223e351dfaeac9a107f062ec55298b27615f
+SIZE: 308490
+REPO: gentoo
+
+CPV: app-arch/tar-1.20
+DEPEND: >=sys-devel/gettext-0.10.35
+DESC: Use this to make tarballs :)
+IUSE: nls static userland_GNU
+KEYWORDS: alpha amd64 arm hppa ia64 m68k ~mips ppc ppc64 s390 sh sparc x86 ~x86-fbsd
+LICENSE: GPL-3
+MD5: 887e5c0a8bf38f63546c8f0c80b57e49
+MTIME: 1264264511
+SHA1: 8f8d4305745b14d5ed2cd42fabc512775db3d7c4
+SIZE: 785279
+USE: nls userland_GNU
+REPO: gentoo
+
+CPV: app-arch/tar-1.21-r1
+DEPEND: >=sys-devel/gettext-0.10.35
+DESC: Use this to make tarballs :)
+IUSE: nls static userland_GNU
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-3
+MD5: 9ebea4a9c24d8c26270d41c6adc6771e
+MTIME: 1264264431
+SHA1: be19702b47d2aa00d51c8ed6889bcc0593edc556
+SIZE: 810009
+USE: nls userland_GNU
+REPO: gentoo
+
+CPV: app-arch/tar-1.22
+DEPEND: >=sys-devel/gettext-0.10.35
+DESC: Use this to make tarballs :)
+IUSE: nls static userland_GNU
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-3
+MD5: 3c302c5355d5f12f3809ccad18db7e0d
+MTIME: 1264300860
+SHA1: 0fac4f3d391d30fbedb36447c286fa54fc249cda
+SIZE: 813235
+USE: nls userland_GNU
+REPO: gentoo
+
+CPV: app-editors/nano-2.0.9
+DEPEND: >=sys-libs/ncurses-5.2 sys-devel/gettext
+DESC: GNU GPL'd Pico clone with more functionality
+IUSE: debug justify minimal ncurses nls slang spell unicode
+KEYWORDS: alpha amd64 arm hppa ia64 m68k ~mips ppc ppc64 s390 sh sparc ~sparc-fbsd x86 ~x86-fbsd
+LICENSE: GPL-3
+MD5: 274550c29c9b263296dbfdd5f544b94b
+MTIME: 1264265176
+RDEPEND: >=sys-libs/ncurses-5.2 sys-devel/gettext
+SHA1: 0f7ad9888b1df320dde6499548b868b7a89c2361
+SIZE: 454616
+USE: ncurses nls spell unicode
+REPO: gentoo
+
+CPV: app-editors/nano-2.1.10
+DEPEND: >=sys-libs/ncurses-5.2[unicode?] sys-devel/gettext
+DESC: GNU GPL'd Pico clone with more functionality
+EAPI: 2
+IUSE: debug justify minimal ncurses nls slang spell unicode
+KEYWORDS: alpha amd64 arm hppa ia64 m68k ~mips ppc ppc64 s390 sh sparc x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-3
+MD5: 5e2fdcc73fd3731aaf1bbd79a2c69635
+MTIME: 1264265216
+RDEPEND: >=sys-libs/ncurses-5.2[unicode?] sys-devel/gettext
+SHA1: 8b2a8e6bd12573fdaf89b03199caca9ea819e6e9
+SIZE: 448975
+USE: ncurses nls spell unicode
+REPO: gentoo
+
+CPV: app-editors/nano-2.2.0
+DEPEND: >=sys-libs/ncurses-5.2[unicode?] sys-devel/gettext
+DESC: GNU GPL'd Pico clone with more functionality
+EAPI: 2
+IUSE: debug justify minimal ncurses nls slang spell unicode
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-3
+MD5: 6d27422ba5aa436bdf05ee0dcad06bf9
+MTIME: 1264265255
+RDEPEND: >=sys-libs/ncurses-5.2[unicode?] sys-devel/gettext
+SHA1: 4c563f227bb0962f8c77e83f3472f3c466247451
+SIZE: 465543
+USE: ncurses nls spell unicode
+REPO: gentoo
+
+CPV: app-editors/nano-2.2.2
+DEPEND: >=sys-libs/ncurses-5.2[unicode?] sys-devel/gettext
+DESC: GNU GPL'd Pico clone with more functionality
+EAPI: 2
+IUSE: debug justify minimal ncurses nls slang spell unicode
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-3
+MD5: e2c33dd669b4380180cae217f772c472
+MTIME: 1264300332
+RDEPEND: >=sys-libs/ncurses-5.2[unicode?] sys-devel/gettext
+SHA1: dd374677cf847a53c36d4d34a9e4b7ca1b92e44c
+SIZE: 472482
+USE: ncurses nls spell unicode
+REPO: gentoo
+
+CPV: app-emulation/emul-linux-x86-compat-20091231-r1
+DEPEND: >=sys-apps/findutils-4.2.26
+DESC: 32 bit lib-compat, and also libgcc_s and libstdc++ from gcc 3.3 and 3.4 for non-multilib systems
+IUSE: multilib
+KEYWORDS: -* ~amd64 ~amd64-linux
+LICENSE: GPL-2
+MD5: b44ea7e3867b8bc5195044d6c5167edb
+MTIME: 1264300253
+RDEPEND: sys-libs/libstdc++-v3
+SHA1: 752218fa71c89932f50e9a487ce7034d2c89c019
+SIZE: 673489
+USE: multilib
+REPO: gentoo
+
+CPV: app-misc/gtypist-2.8.3
+DEPEND: >=sys-libs/ncurses-5.2
+DESC: Universal typing tutor
+IUSE: nls emacs xemacs
+KEYWORDS: ~amd64 ~ppc ~x86
+LICENSE: GPL-2
+MD5: 9cdee43a070f7f1a04048574f5d1df4d
+MTIME: 1264299719
+RDEPEND: >=sys-libs/ncurses-5.2
+SHA1: 0da2fef7e609a2de5c3ba62193421f0b55fbf1ef
+SIZE: 517754
+USE: nls
+REPO: gentoo
+
+CPV: app-portage/layman-1.2.3
+DEPEND: || ( dev-lang/python[xml] ( dev-lang/python dev-python/pyxml ) ) >=dev-lang/python-2.5 >=app-admin/eselect-python-20090804 virtual/python
+DESC: A python script for retrieving gentoo overlays.
+EAPI: 2
+IUSE: git subversion test
+KEYWORDS: alpha amd64 arm hppa ia64 ~mips ppc ppc64 sparc x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: 525788d2c144d63f93b70c3cb396ffd1
+MTIME: 1264265280
+RDEPEND: || ( dev-lang/python[xml] ( dev-lang/python dev-python/pyxml ) ) >=dev-lang/python-2.5 virtual/python
+SHA1: e69c712bf3d2decc6f2d591e5065fa9c32d268c4
+SIZE: 63035
+REPO: gentoo
+
+CPV: app-portage/layman-1.2.4-r3
+DEPEND: || ( dev-lang/python[xml] ( dev-lang/python dev-python/pyxml ) ) >=dev-lang/python-2.5 >=app-admin/eselect-python-20090804 virtual/python
+DESC: A python script for retrieving gentoo overlays.
+EAPI: 2
+IUSE: git subversion test
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: b16542f9539cb5c6648d165fa888ae25
+MTIME: 1264265308
+RDEPEND: || ( dev-lang/python[xml] ( dev-lang/python dev-python/pyxml ) ) >=dev-lang/python-2.5 virtual/python
+SHA1: 311bb22ceccd382fdf79d62708d586547e3d793c
+SIZE: 64942
+REPO: gentoo
+
+CPV: app-portage/layman-1.2.6
+DEPEND: dev-lang/python[xml] >=dev-lang/python-2.5 >=app-admin/eselect-python-20090804 virtual/python
+DESC: A python script for retrieving gentoo overlays.
+EAPI: 2
+IUSE: git subversion test
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: b3f903c492df0c2f8c82225f784b0d64
+MTIME: 1264265336
+RDEPEND: dev-lang/python[xml] >=dev-lang/python-2.5 virtual/python
+SHA1: c86bef390dbcb765f4d96d2affacedc072333074
+SIZE: 64924
+REPO: gentoo
+
+CPV: app-portage/layman-1.3.0
+DEPEND: dev-lang/python[xml] >=dev-lang/python-2.5 >=app-admin/eselect-python-20090804 virtual/python
+DESC: A python script for retrieving gentoo overlays.
+EAPI: 2
+IUSE: git subversion test
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: 9d79d89f556f51e6d6221d72a59f7927
+MTIME: 1264299669
+RDEPEND: dev-lang/python[xml] >=dev-lang/python-2.5 virtual/python
+SHA1: 4034c89723eb6f20a52ca432507f449d6963d4c3
+SIZE: 66077
+REPO: gentoo
+
+CPV: app-portage/layman-1.3.0_rc1
+DEPEND: dev-lang/python[xml] >=dev-lang/python-2.5 >=app-admin/eselect-python-20090804 virtual/python
+DESC: A python script for retrieving gentoo overlays.
+EAPI: 2
+IUSE: git subversion test
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: 9cabc319559778c6a59d11f325dba3d9
+MTIME: 1264265170
+RDEPEND: dev-lang/python[xml] >=dev-lang/python-2.5 virtual/python
+SHA1: 1e53103e6d204479add69fe73c2e35bb9aceb487
+SIZE: 68734
+REPO: gentoo
+
+CPV: app-portage/layman-1.3.0_rc1-r3
+DEPEND: dev-lang/python[xml] >=dev-lang/python-2.5 >=app-admin/eselect-python-20090804 virtual/python
+DESC: A python script for retrieving gentoo overlays.
+EAPI: 2
+IUSE: git subversion test
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: fc330556091381a323703a5c1392395c
+MTIME: 1264265364
+RDEPEND: dev-lang/python[xml] >=dev-lang/python-2.5 virtual/python
+SHA1: 07c7c177b1772663852b678d6afd2ba75a7c7200
+SIZE: 66374
+REPO: gentoo
+
+CPV: app-portage/portage-utils-0.3.1
+DESC: small and fast portage helper tools written in C
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-2
+MD5: 79c4da94810df0f3034fe30c26c05003
+MTIME: 1264299639
+SHA1: 06738d79cf54f92ee61e534fa730f3ca3ff8951f
+SIZE: 105322
+REPO: gentoo
+
+CPV: app-shells/bash-4.0_p37
+DEPEND: >=sys-libs/ncurses-5.2-r2 virtual/libintl
+DESC: The standard GNU Bourne again shell
+EAPI: 1
+IUSE: afs bashlogger examples mem-scramble +net nls plugins vanilla
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-3
+MD5: 1f6a93554c485d8025d98c9a4a8fa006
+MTIME: 1264300780
+RDEPEND: >=sys-libs/ncurses-5.2-r2 virtual/libintl !<sys-apps/portage-2.1.5 !<sys-apps/paludis-0.26.0_alpha5
+SHA1: b751ddb741a1581bf1e17867ca94df764fdf4ecc
+SIZE: 1013849
+USE: net nls
+REPO: gentoo
+
+CPV: dev-db/sqlite-3.6.22-r1
+DEPEND: dev-libs/icu sys-libs/readline dev-lang/tcl =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool
+DESC: A SQL Database Engine in a C Library
+EAPI: 2
+IUSE: debug doc extensions +fts3 icu +readline soundex tcl +threadsafe test
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~ppc-aix ~sparc-fbsd ~x86-fbsd ~x86-freebsd ~hppa-hpux ~ia64-hpux ~x86-interix ~amd64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris
+LICENSE: as-is
+MD5: 312fb5844ea57184e849aa51e180016e
+MTIME: 1264299603
+RDEPEND: dev-libs/icu sys-libs/readline dev-lang/tcl
+SHA1: 0abb9db0dc6a29207e2d3e0579a5715c77f1d3d2
+SIZE: 712591
+SLOT: 3
+USE: fts3 icu readline tcl threadsafe
+REPO: gentoo
+
+CPV: dev-java/icedtea6-bin-1.6.2-r2
+DEPEND: =dev-java/java-config-2* >=sys-apps/portage-2.1
+DESC: A Gentoo-made binary build of the icedtea6 JDK
+EAPI: 1
+IUSE: X alsa doc examples nsplugin source
+KEYWORDS: ~amd64 ~x86
+LICENSE: GPL-2-with-linking-exception
+MD5: 596d044b5894588a97eae356d2d05f02
+MTIME: 1264299533
+RDEPEND: >=sys-devel/gcc-4.3 >=sys-libs/glibc-2.9 >=media-libs/giflib-4.1.6-r1 =media-libs/jpeg-8* >=media-libs/libpng-1.2.38 >=sys-libs/zlib-1.2.3-r1 >=media-libs/alsa-lib-1.0.20 >=media-libs/freetype-2.3.9:2 >=media-libs/fontconfig-2.6.0-r2:1.0 >=x11-libs/libXext-1.0.5 >=x11-libs/libXi-1.2.1 >=x11-libs/libXtst-1.0.3 >=x11-libs/libX11-1.2.2 x11-libs/libXt =dev-java/java-config-2*
+SHA1: 75bd18425582a774d6f5544b73399fac3fe2b417
+SIZE: 35258682
+USE: X alsa
+REPO: gentoo
+
+CPV: dev-java/sun-jdk-1.6.0.18
+DEPEND: =dev-java/java-config-2* >=sys-apps/portage-2.1
+DESC: Sun's Java SE Development Kit
+IUSE: X alsa derby doc examples jce nsplugin odbc
+KEYWORDS: ~amd64 ~x86
+LICENSE: dlj-1.1
+MD5: f6898e4601bbefce5011e4506870250f
+MTIME: 1264299356
+RDEPEND: sys-libs/glibc media-libs/alsa-lib x11-libs/libXext x11-libs/libXi x11-libs/libXp x11-libs/libXtst x11-libs/libXt x11-libs/libX11 =dev-java/java-config-2*
+SHA1: 9263959ac184d2643518ed3ba36a40e4f7be92f9
+SIZE: 65027860
+SLOT: 1.6
+USE: X alsa nsplugin
+REPO: gentoo
+
+CPV: dev-libs/DirectFB-1.4.3
+DEPEND: media-libs/libsdl media-libs/giflib media-libs/libpng media-libs/jpeg sys-fs/sysfsutils sys-libs/zlib >=media-libs/freetype-2.0.1 x11-libs/libXext x11-libs/libX11 x11-proto/xextproto x11-proto/xproto
+DESC: Thin library on top of the Linux framebuffer devices
+IUSE: debug fbcon fusion gif jpeg mmx png sdl sse sysfs truetype v4l v4l2 X zlib video_cards_intel video_cards_mach64 video_cards_mga video_cards_neomagic video_cards_nsc video_cards_nvidia video_cards_r128 video_cards_radeon video_cards_s3 video_cards_savage video_cards_sis video_cards_tdfx video_cards_via video_cards_vmware input_devices_dynapro input_devices_elo2300 input_devices_evdev input_devices_joystick input_devices_keyboard input_devices_lirc input_devices_mouse input_devices_mutouch input_devices_tslib
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 -mips ~ppc ~ppc64 ~sh -sparc ~x86
+LICENSE: LGPL-2.1
+MD5: 0d62fd95d45383be9de1d3d63a8b687e
+MTIME: 1264299004
+RDEPEND: media-libs/libsdl media-libs/giflib media-libs/libpng media-libs/jpeg sys-fs/sysfsutils sys-libs/zlib >=media-libs/freetype-2.0.1 x11-libs/libXext x11-libs/libX11
+SHA1: f9ffc4909669626a3eb99c5f850a2f50790d4b69
+SIZE: 1594011
+USE: X fbcon gif input_devices_evdev jpeg mmx png sdl sse sysfs truetype v4l v4l2 video_cards_nvidia zlib
+REPO: gentoo
+
+CPV: dev-libs/dbus-glib-0.82-r1
+DEPEND: >=sys-apps/dbus-1.1 >=dev-libs/glib-2.10 >=dev-libs/expat-1.95.8 dev-util/pkgconfig sys-devel/gettext
+DESC: D-Bus bindings for glib
+EAPI: 2
+IUSE: bash-completion debug doc test bash-completion
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~x86-fbsd
+LICENSE: || ( GPL-2 AFL-2.1 )
+MD5: 414b1ca40951ec482c454896953a6b49
+MTIME: 1264298849
+RDEPEND: >=sys-apps/dbus-1.1 >=dev-libs/glib-2.10 >=dev-libs/expat-1.95.8
+SHA1: 9ac0d74ce34ddb83f6a25c6b1b64449b0769ccdc
+SIZE: 241344
+REPO: gentoo
+
+CPV: dev-libs/fribidi-0.19.2
+DESC: A free implementation of the unicode bidirectional algorithm
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sh ~sparc ~x86 ~x86-fbsd ~amd64-linux ~x86-linux ~ppc-macos ~x64-solaris ~x86-solaris
+LICENSE: LGPL-2.1
+MD5: 897e5a3a7d4d003dab4f3e74b32ed8e4
+MTIME: 1264298806
+SHA1: fcfc11f2cc834580f6d95e64dcdb7f4755e2a9fb
+SIZE: 107278
+REPO: gentoo
+
+CPV: dev-libs/gmime-2.4.13
+DEPEND: >=dev-libs/glib-2.12 sys-libs/zlib dev-util/pkgconfig >=sys-apps/sed-4
+DESC: Utilities for creating and parsing messages using MIME
+IUSE: doc mono debug
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: LGPL-2.1
+MD5: 09a3282f2061022a0ab844c13a192cf7
+MTIME: 1264298765
+RDEPEND: >=dev-libs/glib-2.12 sys-libs/zlib
+SHA1: c79a707cad05d18b183829890ebe23ea21efd4d8
+SIZE: 431050
+SLOT: 2.4
+REPO: gentoo
+
+CPV: dev-libs/libisofs-0.6.26
+DEPEND: virtual/acl sys-libs/zlib dev-util/pkgconfig
+DESC: libisofs is an open-source library for reading, mastering and writing optical discs.
+EAPI: 2
+IUSE: acl xattr zlib
+KEYWORDS: ~alpha ~amd64 ~hppa ~ppc ~ppc64 ~x86
+LICENSE: GPL-2
+MD5: 37ad8de72ab38bbd89c7641a20ac2b11
+MTIME: 1264298707
+RDEPEND: virtual/acl sys-libs/zlib
+SHA1: eed462d17ae2a2006dbc80437414054d51c69473
+SIZE: 199818
+USE: acl zlib
+REPO: gentoo
+
+CPV: dev-libs/libtasn1-2.4
+DEPEND: >=dev-lang/perl-5.6 sys-devel/bison
+DESC: provides ASN.1 structures parsing capabilities for use with GNUTLS
+IUSE: doc
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-3 LGPL-2.1
+MD5: 94c40f06b2028421eb318b320e7e1c79
+MTIME: 1264298652
+SHA1: 9f819c2613324c31d736affa6e4ee1dd5bc2a3aa
+SIZE: 149335
+REPO: gentoo
+
+CPV: dev-util/git-1.6.6.1
+DEPEND: sys-libs/zlib dev-lang/perl dev-lang/tk net-misc/curl dev-libs/expat app-arch/cpio dev-lang/perl[-build]
+DESC: GIT - the stupid content tracker, the revision control system heavily used by the Linux kernel team
+EAPI: 2
+IUSE: +blksha1 +curl cgi doc emacs gtk iconv +perl ppcsha1 tk +threads +webdav xinetd cvs subversion bash-completion
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-2
+MD5: 0d010cb874ae0fe8bdfedc383725bdc6
+MTIME: 1264298591
+RDEPEND: sys-libs/zlib dev-lang/perl dev-lang/tk net-misc/curl dev-libs/expat dev-perl/Error dev-perl/Net-SMTP-SSL dev-perl/Authen-SASL >=dev-python/pygtk-2.8 || ( dev-python/pygtksourceview:2 dev-python/gtksourceview-python ) dev-lang/perl[-build]
+SHA1: 528c6eefe610e85d3b81e12367f53fca5c13ca7b
+SIZE: 6604580
+USE: blksha1 curl gtk iconv perl threads tk webdav
+REPO: gentoo
+
+CPV: gnome-base/gdm-2.20.10-r3
+DEPEND: >=dev-libs/glib-2.12 >=x11-libs/gtk+-2.6 >=x11-libs/pango-1.3 >=gnome-base/libglade-2 >=gnome-base/libgnomecanvas-2 >=gnome-base/librsvg-1.1.1 >=dev-libs/libxml2-2.4.12 >=media-libs/libart_lgpl-2.3.11 x11-libs/gksu x11-libs/libXi x11-libs/libXau x11-libs/libX11 x11-libs/libXext x11-apps/sessreg x11-libs/libXdmcp sys-auth/consolekit dev-libs/dbus-glib virtual/pam >=sys-apps/tcp-wrappers-7.6 >=x11-misc/xdg-utils-1.0.2-r3 sys-devel/gettext x11-proto/inputproto >=dev-util/intltool-0.35 >=dev-util/pkgconfig-0.19 >=app-text/scrollkeeper-0.1.4 >=app-text/gnome-doc-utils-0.3.2 >=sys-apps/sed-4
+DESC: GNOME Display Manager
+EAPI: 2
+IUSE: accessibility afs branding +consolekit dmx ipv6 gnome-keyring pam remote selinux tcpd xinerama elibc_glibc debug
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~ppc ~ppc64 ~sh ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: dfe36693471fa40134f834ccd696f25e
+MTIME: 1264298505
+PDEPEND: >=sys-auth/pambase-20090430[consolekit=,gnome-keyring=]
+RDEPEND: >=dev-libs/glib-2.12 >=x11-libs/gtk+-2.6 >=x11-libs/pango-1.3 >=gnome-base/libglade-2 >=gnome-base/libgnomecanvas-2 >=gnome-base/librsvg-1.1.1 >=dev-libs/libxml2-2.4.12 >=media-libs/libart_lgpl-2.3.11 x11-libs/gksu x11-libs/libXi x11-libs/libXau x11-libs/libX11 x11-libs/libXext x11-apps/sessreg x11-libs/libXdmcp sys-auth/consolekit dev-libs/dbus-glib virtual/pam >=sys-apps/tcp-wrappers-7.6 >=x11-misc/xdg-utils-1.0.2-r3
+SHA1: 0f712fa78540566dc2b33252e8e00476546ecaa7
+SIZE: 3687872
+USE: branding consolekit elibc_glibc ipv6 pam tcpd
+REPO: gentoo
+
+CPV: gnome-extra/gnome-screensaver-2.28.0-r1
+DEPEND: >=gnome-base/gconf-2.6.1 >=x11-libs/gtk+-2.14.0 >=gnome-base/gnome-desktop-2.23.2 >=gnome-base/gnome-menus-2.12 >=dev-libs/glib-2.15 >=gnome-base/libgnomekbd-0.1 >=dev-libs/dbus-glib-0.71 x11-libs/libnotify virtual/opengl virtual/pam x11-libs/libX11 x11-libs/libXext x11-libs/libXrandr x11-libs/libXScrnSaver x11-libs/libXxf86misc x11-libs/libXxf86vm >=dev-util/pkgconfig-0.9 >=dev-util/intltool-0.40 x11-proto/xextproto x11-proto/randrproto x11-proto/scrnsaverproto x11-proto/xf86miscproto >=sys-apps/sed-4
+DESC: Replaces xscreensaver, integrating with the desktop.
+EAPI: 2
+IUSE: debug doc libnotify opengl pam kernel_linux debug
+KEYWORDS: ~alpha ~amd64 ~hppa ~ia64 ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: 79e247b6b43668c3e49ff706d47ea577
+MTIME: 1264298398
+RDEPEND: >=gnome-base/gconf-2.6.1 >=x11-libs/gtk+-2.14.0 >=gnome-base/gnome-desktop-2.23.2 >=gnome-base/gnome-menus-2.12 >=dev-libs/glib-2.15 >=gnome-base/libgnomekbd-0.1 >=dev-libs/dbus-glib-0.71 x11-libs/libnotify virtual/opengl virtual/pam x11-libs/libX11 x11-libs/libXext x11-libs/libXrandr x11-libs/libXScrnSaver x11-libs/libXxf86misc x11-libs/libXxf86vm
+SHA1: 9143a14b6736643bbb5aee20c8cdd6510dc7876f
+SIZE: 4621029
+USE: kernel_linux libnotify opengl pam
+REPO: gentoo
+
+CPV: gnome-extra/gnome-utils-2.28.3
+DEPEND: >=dev-libs/glib-2.20.0 >=x11-libs/gtk+-2.18.0 >=gnome-base/gnome-panel-2.13.4 >=gnome-base/libgtop-2.12 >=gnome-base/gconf-2 >=media-libs/libcanberra-0.4[gtk] x11-libs/libXext x11-proto/xextproto app-text/gnome-doc-utils app-text/scrollkeeper >=dev-util/intltool-0.40 >=dev-util/pkgconfig-0.9 >=sys-apps/sed-4
+DESC: Utilities for the Gnome2 desktop
+EAPI: 2
+IUSE: doc ipv6 test debug
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~ppc ~ppc64 ~sh ~sparc ~x86 ~x86-fbsd ~x86-freebsd ~amd64-linux ~x86-linux
+LICENSE: GPL-2
+MD5: 486f6257e2c76a6ddfbaea60cd8d9ae3
+MTIME: 1264298299
+RDEPEND: >=dev-libs/glib-2.20.0 >=x11-libs/gtk+-2.18.0 >=gnome-base/gnome-panel-2.13.4 >=gnome-base/libgtop-2.12 >=gnome-base/gconf-2 >=media-libs/libcanberra-0.4[gtk] x11-libs/libXext
+SHA1: df3899a81734cd33de0288d391328f682b858319
+SIZE: 6282009
+USE: ipv6
+REPO: gentoo
+
+CPV: media-libs/alsa-lib-1.0.22
+DEPEND: dev-lang/python >=media-sound/alsa-headers-1.0.22
+DESC: Advanced Linux Sound Architecture Library
+IUSE: doc debug alisp python alsa_pcm_plugins_copy alsa_pcm_plugins_linear alsa_pcm_plugins_route alsa_pcm_plugins_mulaw alsa_pcm_plugins_alaw alsa_pcm_plugins_adpcm alsa_pcm_plugins_rate alsa_pcm_plugins_plug alsa_pcm_plugins_multi alsa_pcm_plugins_shm alsa_pcm_plugins_file alsa_pcm_plugins_null alsa_pcm_plugins_empty alsa_pcm_plugins_share alsa_pcm_plugins_meter alsa_pcm_plugins_mmap_emul alsa_pcm_plugins_hooks alsa_pcm_plugins_lfloat alsa_pcm_plugins_ladspa alsa_pcm_plugins_dmix alsa_pcm_plugins_dshare alsa_pcm_plugins_dsnoop alsa_pcm_plugins_asym alsa_pcm_plugins_iec958 alsa_pcm_plugins_softvol alsa_pcm_plugins_extplug alsa_pcm_plugins_ioplug
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sh ~sparc ~x86 ~amd64-linux ~x86-linux
+LICENSE: LGPL-2.1
+MD5: 94b646ca40670aec529e2fb1b49b36df
+MTIME: 1264298087
+RDEPEND: dev-lang/python
+SHA1: 43ef8b55737d77122ac2373e13a3cf9e95486be3
+SIZE: 817219
+USE: alsa_pcm_plugins_adpcm alsa_pcm_plugins_alaw alsa_pcm_plugins_asym alsa_pcm_plugins_copy alsa_pcm_plugins_dmix alsa_pcm_plugins_dshare alsa_pcm_plugins_dsnoop alsa_pcm_plugins_empty alsa_pcm_plugins_extplug alsa_pcm_plugins_file alsa_pcm_plugins_hooks alsa_pcm_plugins_iec958 alsa_pcm_plugins_ioplug alsa_pcm_plugins_ladspa alsa_pcm_plugins_lfloat alsa_pcm_plugins_linear alsa_pcm_plugins_meter alsa_pcm_plugins_mmap_emul alsa_pcm_plugins_mulaw alsa_pcm_plugins_multi alsa_pcm_plugins_null alsa_pcm_plugins_plug alsa_pcm_plugins_rate alsa_pcm_plugins_route alsa_pcm_plugins_share alsa_pcm_plugins_shm alsa_pcm_plugins_softvol python
+REPO: gentoo
+
+CPV: media-libs/jpeg-8
+DESC: Library to load, handle and manipulate images in the JPEG format
+EAPI: 2
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd ~x64-freebsd ~x86-freebsd ~x86-interix ~amd64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris
+LICENSE: as-is
+MD5: bc17f6bbd3fe11172bbd524a1101c671
+MTIME: 1264298168
+SHA1: fb72dde71839e9368c81fa54a468a8dab9c94608
+SIZE: 397321
+REPO: gentoo
+
+CPV: media-sound/alsa-headers-1.0.22
+DESC: Header files for Advanced Linux Sound Architecture kernel modules
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sh ~sparc ~x86
+LICENSE: GPL-2
+MD5: 9ac32ab8b9ed4a675fb4987a144dbf25
+MTIME: 1264298022
+SHA1: 728ba7f1e1a3b56a17b486aa95002d391022a131
+SIZE: 166783
+REPO: gentoo
+
+CPV: media-sound/alsa-utils-1.0.22-r1
+DEPEND: >=sys-libs/ncurses-5.1 dev-util/dialog >=media-libs/alsa-lib-1.0.22
+DESC: Advanced Linux Sound Architecture Utils (alsactl, alsamixer, etc.)
+EAPI: 2
+IUSE: doc nls minimal
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sh ~sparc ~x86
+LICENSE: GPL-2
+MD5: de6300fd679058a875d7a0963620f05c
+MTIME: 1264298123
+RDEPEND: >=sys-libs/ncurses-5.1 dev-util/dialog >=media-libs/alsa-lib-1.0.22 virtual/modutils sys-apps/pciutils
+SHA1: 47574253fa21e2d0c44323b2983e2558abc49fed
+SIZE: 991245
+SLOT: 0.9
+USE: nls
+REPO: gentoo
+
+CPV: media-video/mjpegtools-1.9.0-r1
+DEPEND: media-libs/jpeg:0 x11-libs/gtk+:2 >=media-libs/libdv-0.99 virtual/quicktime media-libs/libpng >=media-libs/libsdl-1.2.7-r3 x11-libs/libX11 x11-libs/libXt dev-lang/nasm >=sys-apps/sed-4 dev-util/pkgconfig
+DESC: Tools for MJPEG video
+EAPI: 1
+IUSE: gtk dv quicktime sdl X yv12 v4l dga png mmx
+KEYWORDS: ~alpha ~amd64 ~ppc ~ppc64 ~sparc ~x86
+LICENSE: as-is
+MD5: 0f8d14bb49c7452144f9f42da59df67d
+MTIME: 1264297981
+RDEPEND: media-libs/jpeg:0 x11-libs/gtk+:2 >=media-libs/libdv-0.99 virtual/quicktime media-libs/libpng >=media-libs/libsdl-1.2.7-r3 x11-libs/libX11 x11-libs/libXt
+SHA1: 6eea5264c255be8aed69533455e5ad313506ff32
+SIZE: 1184020
+SLOT: 1
+USE: X dv gtk mmx png quicktime sdl v4l
+REPO: gentoo
+
+CPV: net-fs/samba-3.4.5
+DESC: Meta package for samba-{libs,client,server}
+EAPI: 2
+IUSE: +client +server
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86
+LICENSE: GPL-3
+MD5: ff6e613b5a052817e38edeea74296ae9
+MTIME: 1264297884
+RDEPEND: ~net-fs/samba-libs-3.4.5 ~net-fs/samba-client-3.4.5 ~net-fs/samba-server-3.4.5
+SHA1: a4d1669379c6727533b1c92725ef4fd7004e3d33
+SIZE: 4841
+USE: client server
+REPO: gentoo
+
+CPV: net-fs/samba-client-3.4.5
+DEPEND: !<net-fs/samba-3.3 !net-fs/mount-cifs dev-libs/popt dev-libs/iniparser virtual/libiconv net-print/cups net-nds/openldap virtual/logger sys-libs/tdb sys-libs/talloc ~net-fs/samba-libs-3.4.5[caps?,cups?,ldap?,syslog?,winbind?,ads?,samba4?,netapi] =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool
+DESC: Client bits of the samba network filesystem
+EAPI: 2
+IUSE: samba4 ads aio avahi caps cluster cups debug ldap minimal syslog winbind zeroconf
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86
+LICENSE: GPL-3
+MD5: f63cbda580c8e3cab3274e7899024ffa
+MTIME: 1264297864
+RDEPEND: !<net-fs/samba-3.3 !net-fs/mount-cifs dev-libs/popt dev-libs/iniparser virtual/libiconv net-print/cups net-nds/openldap virtual/logger sys-libs/tdb sys-libs/talloc ~net-fs/samba-libs-3.4.5[caps?,cups?,ldap?,syslog?,winbind?,ads?,samba4?,netapi]
+SHA1: 40a7f84d99285356d73969c51d34a2b03370c749
+SIZE: 16387850
+USE: avahi cups ldap syslog
+REPO: gentoo
+
+CPV: net-fs/samba-libs-3.4.5
+DEPEND: dev-libs/popt sys-libs/talloc sys-libs/tdb virtual/libiconv virtual/krb5 sys-fs/e2fsprogs sys-libs/libcap net-print/cups net-nds/openldap virtual/pam virtual/logger !<net-fs/samba-3.3 !=net-fs/samba-server-3.4.3[tools] =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool
+DESC: Library bits of the samba network filesystem
+EAPI: 2
+IUSE: samba4 ads aio caps cluster cups debug examples ldap pam syslog winbind ldb +netapi +smbclient smbsharemodes addns tools
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86
+LICENSE: GPL-3
+MD5: 9be0477a3595631183d8e9542ec9b7d8
+MTIME: 1264297437
+RDEPEND: dev-libs/popt sys-libs/talloc sys-libs/tdb virtual/libiconv virtual/krb5 sys-fs/e2fsprogs sys-libs/libcap net-print/cups net-nds/openldap virtual/pam virtual/logger !<net-fs/samba-3.3 !=net-fs/samba-server-3.4.3[tools]
+SHA1: e0a1459615d016fe5609546736c6fc5ea6089ba2
+SIZE: 3958751
+USE: ads caps cups ldap netapi pam smbclient syslog
+REPO: gentoo
+
+CPV: net-fs/samba-server-3.4.5
+DEPEND: !<net-fs/samba-3.3 dev-libs/popt virtual/libiconv net-dns/avahi sys-libs/libcap net-print/cups dev-libs/libgamin net-nds/openldap sys-libs/pam virtual/logger sys-libs/tdb sys-libs/talloc ~net-fs/samba-libs-3.4.5[caps?,cluster?,cups?,ldap?,syslog?,winbind?,ads?,samba4?] =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool
+DESC: Samba Server component
+EAPI: 2
+IUSE: samba4 acl ads aio avahi caps cluster cups debug doc examples fam ldap pam quota swat syslog winbind zeroconf
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86
+LICENSE: GPL-3
+MD5: a6a7786994543a7c7b4701c6e2b5176e
+MTIME: 1264297656
+RDEPEND: !<net-fs/samba-3.3 dev-libs/popt virtual/libiconv net-dns/avahi sys-libs/libcap net-print/cups dev-libs/libgamin net-nds/openldap sys-libs/pam virtual/logger sys-libs/tdb sys-libs/talloc ~net-fs/samba-libs-3.4.5[caps?,cluster?,cups?,ldap?,syslog?,winbind?,ads?,samba4?]
+SHA1: e1b9416b6ab579d126bd89e4a58298ec4be5282f
+SIZE: 16226041
+USE: avahi caps cups fam ldap pam swat syslog
+REPO: gentoo
+
+CPV: net-misc/rsync-3.0.7
+DEPEND: >=dev-libs/popt-1.5 virtual/acl virtual/libiconv
+DESC: File transfer program to keep remote files into sync
+IUSE: acl iconv ipv6 static xattr
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-3
+MD5: 49d5d5cc5d5b18eb811e3544114afe19
+MTIME: 1264300657
+RDEPEND: >=dev-libs/popt-1.5 virtual/acl virtual/libiconv
+SHA1: 6d018cbcef3e17d669a29789ed5ff0eb15cdd694
+SIZE: 335209
+USE: acl iconv ipv6
+REPO: gentoo
+
+CPV: net-misc/wget-1.12-r1
+DEPEND: >=dev-libs/openssl-0.9.6b sys-devel/gettext
+DESC: Network utility to retrieve files from the WWW
+IUSE: debug idn ipv6 nls ntlm ssl static
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-3
+MD5: 258c0fb155252501c242d52bf2215f25
+MTIME: 1264300596
+RDEPEND: >=dev-libs/openssl-0.9.6b
+SHA1: 09324271a9897a3709b79a8f9e93e8abc19cae6c
+SIZE: 561460
+USE: ipv6 nls ssl
+REPO: gentoo
+
+CPV: perl-core/Module-Build-0.36.03
+DEPEND: dev-perl/YAML-Tiny >=virtual/perl-ExtUtils-CBuilder-0.27 >=virtual/perl-Archive-Tar-1.09 >=virtual/perl-Test-Harness-3.16 dev-lang/perl[-build]
+DESC: Build and install Perl modules
+EAPI: 2
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd ~x86-freebsd ~x86-interix ~amd64-linux ~ia64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris
+LICENSE: || ( Artistic GPL-2 )
+MD5: c23f38d6da0a2a5bbd0cee762ce75bee
+MTIME: 1264296675
+PDEPEND: >=virtual/perl-ExtUtils-ParseXS-2.21
+RDEPEND: dev-perl/YAML-Tiny >=virtual/perl-ExtUtils-CBuilder-0.27 >=virtual/perl-Archive-Tar-1.09 >=virtual/perl-Test-Harness-3.16 dev-lang/perl[-build]
+SHA1: a348aea68a6d91432da625ef421ccc8ae9c2b74a
+SIZE: 186756
+REPO: gentoo
+
+CPV: perl-core/Test-Harness-3.20
+DEPEND: dev-lang/perl
+DESC: Runs perl standard test scripts with statistics
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~ppc-aix ~sparc-fbsd ~x86-fbsd ~x64-freebsd ~x86-freebsd ~hppa-hpux ~ia64-hpux ~x86-interix ~amd64-linux ~ia64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris
+LICENSE: || ( Artistic GPL-2 )
+MD5: 5585c6a28745f6a59bc477f74293d117
+MTIME: 1264296640
+RDEPEND: dev-lang/perl
+SHA1: fb3f4d9701a38bf199386e427473c5a501a13ff7
+SIZE: 129603
+REPO: gentoo
+
+CPV: sys-apps/openrc-0.6.0-r1
+DEPEND: virtual/init >=sys-libs/glibc-2.5 sys-libs/ncurses virtual/pam >=sys-apps/baselayout-2.0.0 !<sys-apps/module-init-tools-3.2.2-r2 !<sys-fs/udev-133 !<sys-apps/sysvinit-2.86-r11 virtual/os-headers
+DESC: OpenRC manages the services, startup and shutdown of a host
+EAPI: 1
+IUSE: debug elibc_glibc ncurses pam unicode kernel_linux kernel_FreeBSD
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~sparc-fbsd ~x86 ~x86-fbsd
+LICENSE: BSD-2
+MD5: 3cf179b08d4afaa8e36f12758c8ea19a
+MTIME: 1264297118
+RDEPEND: virtual/init >=sys-libs/glibc-2.5 sys-libs/ncurses virtual/pam >=sys-apps/baselayout-2.0.0 !<sys-apps/module-init-tools-3.2.2-r2 !<sys-fs/udev-133 !<sys-apps/sysvinit-2.86-r11
+SHA1: e88777495cef17c213c7935e2d50a08eecd1dfab
+SIZE: 229060
+USE: elibc_glibc kernel_linux ncurses pam unicode
+REPO: gentoo
+
+CPV: sys-apps/shadow-4.0.18.2
+DEPEND: >=sys-libs/cracklib-2.7-r3 >=sys-libs/pam-0.99 !sys-apps/pam-login !app-admin/nologin virtual/libintl sys-devel/gettext =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool
+DESC: Utilities to deal with user accounts
+IUSE: nls pam selinux skey nousuid cracklib
+KEYWORDS: alpha amd64 arm hppa ia64 m68k ~mips ppc ppc64 s390 sh sparc x86
+LICENSE: BSD GPL-2
+MD5: fcbd34cfbcb0e3399c2f3fa47938f90f
+MTIME: 1264265029
+RDEPEND: >=sys-libs/cracklib-2.7-r3 >=sys-libs/pam-0.99 !sys-apps/pam-login !app-admin/nologin virtual/libintl
+SHA1: 8c3bcdb01ae7be7ce50951cc94c80b57839a39f3
+SIZE: 1176337
+USE: cracklib nls pam
+REPO: gentoo
+
+CPV: sys-apps/shadow-4.1.2.2
+DEPEND: >=sys-libs/cracklib-2.7-r3 virtual/pam !sys-apps/pam-login !app-admin/nologin virtual/libintl sys-devel/gettext =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool
+DESC: Utilities to deal with user accounts
+IUSE: audit cracklib nls pam selinux skey
+KEYWORDS: alpha amd64 arm hppa ia64 m68k ~mips ppc ppc64 s390 sh sparc x86
+LICENSE: BSD GPL-2
+MD5: 0d1635fe78adf406111df9b0e7c711fb
+MTIME: 1264265117
+RDEPEND: >=sys-libs/cracklib-2.7-r3 virtual/pam !sys-apps/pam-login !app-admin/nologin virtual/libintl >=sys-auth/pambase-20080219.1
+SHA1: 7915518b563eeadb03eea21d56e6e9a1dd181e15
+SIZE: 1338463
+USE: cracklib nls pam
+REPO: gentoo
+
+CPV: sys-apps/shadow-4.1.4.2-r1
+DEPEND: >=sys-libs/cracklib-2.7-r3 virtual/pam !sys-apps/pam-login !app-admin/nologin virtual/libintl sys-devel/gettext
+DESC: Utilities to deal with user accounts
+IUSE: audit nls skey selinux pam cracklib
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86
+LICENSE: BSD GPL-2
+MD5: e96104ddb3a926dc483ffb5ddb1765b3
+MTIME: 1264264912
+RDEPEND: >=sys-libs/cracklib-2.7-r3 virtual/pam !sys-apps/pam-login !app-admin/nologin virtual/libintl >=sys-auth/pambase-20080219.1
+SHA1: 9ffc5e5b6711c65aef3b09de1cf543f713aae14d
+SIZE: 1465224
+USE: cracklib nls pam
+
+CPV: sys-apps/shadow-4.1.4.2-r2
+DEPEND: >=sys-libs/cracklib-2.7-r3 virtual/pam !sys-apps/pam-login !app-admin/nologin virtual/libintl sys-devel/gettext
+DESC: Utilities to deal with user accounts
+IUSE: audit cracklib nls pam selinux skey
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86
+LICENSE: BSD GPL-2
+MD5: d979450afe73a2f884e88249d515b359
+MTIME: 1264300537
+RDEPEND: >=sys-libs/cracklib-2.7-r3 virtual/pam !sys-apps/pam-login !app-admin/nologin virtual/libintl >=sys-auth/pambase-20080219.1
+SHA1: 7da596c64d4afcb33aa4b4fc221a3c0e2f3eb661
+SIZE: 1379534
+USE: cracklib nls pam
+REPO: gentoo
+
+CPV: sys-cluster/openmpi-1.4.1
+DEPEND: !sys-cluster/mpich !sys-cluster/lam-mpi !sys-cluster/mpich2 !sys-cluster/mpiexec =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool
+DESC: A high-performance message passing library (MPI)
+EAPI: 2
+IUSE: +cxx elibc_FreeBSD fortran heterogeneous ipv6 mpi-threads pbs romio threads vt debug
+KEYWORDS: ~alpha ~amd64 ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: BSD
+MD5: 89e732e99246058f5e2c165c3e0ac2e4
+MTIME: 1264297088
+RDEPEND: !sys-cluster/mpich !sys-cluster/lam-mpi !sys-cluster/mpich2 !sys-cluster/mpiexec
+SHA1: 095fa67854378fb6411a2d1f16fd1baea50f05e7
+SIZE: 1698447
+USE: cxx fortran ipv6
+REPO: gentoo
+
+CPV: sys-devel/make-3.81-r1
+DEPEND: sys-devel/gettext
+DESC: Standard tool to compile source trees
+IUSE: nls static
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-2
+MD5: dee4cc30ddd4572bfbf86006fad9f8ac
+MTIME: 1264300445
+RDEPEND: virtual/libintl
+SHA1: cd51781fb655f615ae6ceedbbc98a22dacf335c6
+SIZE: 480846
+USE: nls
+REPO: gentoo
+
+CPV: sys-fs/udev-150
+DEPEND: sys-apps/acl >=sys-apps/usbutils-0.82 virtual/libusb:0 sys-apps/pciutils dev-libs/glib:2 >=sys-apps/util-linux-2.16 >=sys-libs/glibc-2.9 dev-util/gperf >=sys-kernel/linux-headers-2.6.29
+DESC: Linux dynamic and persistent device naming support (aka userspace devfs)
+EAPI: 1
+IUSE: selinux +devfs-compat -extras test
+KEYWORDS: -alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 -sh ~sparc ~x86
+LICENSE: GPL-2
+MD5: 7c0af29d69df731992e14ec3c180f699
+MTIME: 1264300392
+PROVIDE: virtual/dev-manager
+RDEPEND: sys-apps/acl >=sys-apps/usbutils-0.82 virtual/libusb:0 sys-apps/pciutils dev-libs/glib:2 >=sys-apps/util-linux-2.16 >=sys-libs/glibc-2.9 !sys-apps/coldplug !<sys-fs/lvm2-2.02.45 !sys-fs/device-mapper >=sys-apps/baselayout-1.12.5
+SHA1: 817a08f5ea7fe1e2b0d5a24c9f7f217893247443
+SIZE: 411550
+USE: devfs-compat extras
+REPO: gentoo
+
+CPV: sys-libs/libstdc++-v3-3.3.6
+DESC: Compatibility package for running binaries linked against a pre gcc 3.4 libstdc++
+IUSE: multilib nls
+KEYWORDS: amd64 hppa ~mips ppc -ppc64 sparc x86 ~x86-fbsd
+LICENSE: GPL-2 LGPL-2.1
+MD5: bbd30835e34931e5cfbcf71fd552dcb3
+MTIME: 1264300213
+SHA1: 4aa8ec56fc28a78ab08f18ca9359cc3dce38395c
+SIZE: 515790
+SLOT: 5
+USE: multilib nls
+REPO: gentoo
+
+CPV: sys-libs/timezone-data-2010a
+DESC: Timezone data (/usr/share/zoneinfo) and utilities (tzselect/zic/zdump)
+IUSE: nls elibc_FreeBSD elibc_glibc
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: BSD public-domain
+MD5: 7d66db70226aafae9de4f3a2c0828d3d
+MTIME: 1264296710
+RDEPEND: !<sys-libs/glibc-2.3.5
+SHA1: 185776f76ed6ac0d877fbf656081fa0d7953da80
+SIZE: 596093
+USE: elibc_glibc nls
+REPO: gentoo
+
+CPV: virtual/perl-Module-Build-0.36.03
+DESC: Build and install Perl modules
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd ~x86-freebsd ~x86-interix ~amd64-linux ~ia64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris
+MD5: df63da5a04fff3934bd802441cdd4b78
+MTIME: 1264296690
+RDEPEND: ~perl-core/Module-Build-0.36.03
+SHA1: b50f0dfdef8a7459657d614c4ac82529437fc641
+SIZE: 4921
+REPO: gentoo
+
+CPV: virtual/perl-Test-Harness-3.20
+DESC: Virtual for Test-Harness
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~ppc-aix ~sparc-fbsd ~x86-fbsd ~x64-freebsd ~x86-freebsd ~hppa-hpux ~ia64-hpux ~x86-interix ~amd64-linux ~ia64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris
+MD5: e5f9572a047b55e74178b3904cc07893
+MTIME: 1264296653
+RDEPEND: ~perl-core/Test-Harness-3.20
+SHA1: 868ab70f38449b6909b6f90fcba633943465d01e
+SIZE: 5019
+REPO: gentoo
+
+CPV: x11-libs/libX11-1.3.3
+DEPEND: >=x11-libs/xtrans-1.2.3 x11-proto/kbproto >=x11-proto/xproto-7.0.13 >=x11-libs/libxcb-1.1.92 x11-proto/xf86bigfontproto x11-proto/inputproto x11-proto/xextproto =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool >=sys-devel/libtool-1.5 >=sys-devel/m4-1.4 >=dev-util/pkgconfig-0.18 >=x11-misc/util-macros-1.3.0 sys-devel/binutils
+DESC: X.Org X11 library
+EAPI: 1
+IUSE: doc ipv6 +xcb debug
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~x86-fbsd
+LICENSE: MIT
+MD5: e85bbc11b6eb49ea21291d8087069a18
+MTIME: 1264296590
+RDEPEND: >=x11-libs/xtrans-1.2.3 x11-proto/kbproto >=x11-proto/xproto-7.0.13 >=x11-libs/libxcb-1.1.92 !<=x11-base/xorg-x11-6.9
+SHA1: 0049699510acc9fa961429a87b2076800513897a
+SIZE: 2716558
+USE: ipv6 xcb
+REPO: gentoo
+
+CPV: x11-libs/pixman-0.17.4
+DEPEND: =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool >=sys-devel/libtool-1.5 >=sys-devel/m4-1.4 >=dev-util/pkgconfig-0.18 >=x11-misc/util-macros-1.3.0 sys-devel/binutils
+DESC: Low-level pixel manipulation routines
+IUSE: altivec mmx sse2 debug
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~x86-fbsd
+LICENSE: MIT
+MD5: e41323dc2670f013788f2cd6a0b2238e
+MTIME: 1264296471
+RDEPEND: !<=x11-base/xorg-x11-6.9
+SHA1: b5c213200abcbae0fd474ca18ee367ec12824117
+SIZE: 265452
+USE: mmx sse2
+REPO: gentoo
+
+CPV: x11-libs/wxGTK-2.8.10.1-r4
+DEPEND: dev-libs/expat media-libs/libsdl >=x11-libs/gtk+-2.4 >=dev-libs/glib-2.4 media-libs/jpeg media-libs/tiff x11-libs/libSM x11-libs/libXinerama x11-libs/libXxf86vm gnome-base/libgnomeprintui >=gnome-base/gconf-2.0 >=media-libs/gstreamer-0.10 virtual/opengl dev-util/pkgconfig x11-proto/xproto x11-proto/xineramaproto x11-proto/xf86vidmodeproto
+DESC: GTK+ version of wxWidgets, a cross-platform C++ GUI toolkit.
+EAPI: 2
+IUSE: X doc debug gnome gstreamer odbc opengl pch sdl
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sh ~sparc ~x86 ~x86-fbsd
+LICENSE: wxWinLL-3 GPL-2
+MD5: b4c3f83df18f8775816bb0849aeab73f
+MTIME: 1264296353
+PDEPEND: >=app-admin/eselect-wxwidgets-0.7
+RDEPEND: dev-libs/expat media-libs/libsdl >=x11-libs/gtk+-2.4 >=dev-libs/glib-2.4 media-libs/jpeg media-libs/tiff x11-libs/libSM x11-libs/libXinerama x11-libs/libXxf86vm gnome-base/libgnomeprintui >=gnome-base/gconf-2.0 >=media-libs/gstreamer-0.10 virtual/opengl
+SHA1: 67137d092bc59524eb26434fa49e60f0d6112dd3
+SIZE: 5349784
+SLOT: 2.8
+USE: X gnome gstreamer opengl sdl
+REPO: gentoo
+
+CPV: x11-misc/util-macros-1.5.0
+DEPEND: =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool >=sys-devel/libtool-1.5 >=sys-devel/m4-1.4 >=dev-util/pkgconfig-0.18
+DESC: X.Org autotools utility macros
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~ppc-aix ~x86-fbsd ~x64-freebsd ~x86-freebsd ~hppa-hpux ~ia64-hpux ~x86-interix ~amd64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris
+LICENSE: MIT
+MD5: 89e9ed49ae7e8ce4fbf90fda82010dff
+MTIME: 1264296041
+RDEPEND: !<=x11-base/xorg-x11-6.9
+SHA1: e732842c14219ce7f03186c8ac1e15ca45269eb2
+SIZE: 49929
+REPO: gentoo
+
diff --git a/pym/gentoolkit/test/eclean/__init__.py b/pym/gentoolkit/test/eclean/__init__.py
new file mode 100644
index 0000000..e5a3c12
--- /dev/null
+++ b/pym/gentoolkit/test/eclean/__init__.py
@@ -0,0 +1,6 @@
+#!/usr/bin/python
+# Copyright 2010 Gentoo Foundation
+#
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
diff --git a/pym/gentoolkit/test/eclean/creator.py b/pym/gentoolkit/test/eclean/creator.py
new file mode 100644
index 0000000..db0eba4
--- /dev/null
+++ b/pym/gentoolkit/test/eclean/creator.py
@@ -0,0 +1,242 @@
+#!/usr/bin/python
+#
+# Copyright 2010 Brian Dolbec <brian.dolbec@gmail.com>
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+from __future__ import with_statement
+from __future__ import print_function
+
+import os
+import sys
+import shutil
+import random
+
+import gentoolkit.pprinter as pp
+
+__version__= "0.0.1"
+__author__ = "Brian Dolbec"
+__email__ = "brian.dolbec@gmail.com"
+
+
+dir_mode = int('0774', 8)
+file_mode = int('0644', 8)
+
+
+def make_dir(path):
+ """create the directory at path
+
+ @param path: full pathname to create
+ capable of multiple intermediate directory creations.
+ Will Error and exit if the target dir already exits"""
+ try:
+ os.makedirs(path, dir_mode)
+ except EnvironmentError as er:
+ print( pp.error("Error creating path:%s" %path), file=sys.stderr)
+ print( pp.error("Error: %s" %str(er), file=sys.stderr))
+ sys.exit(1)
+
+
+def make_dist(path, files, clean_dict=None):
+ """Creates a small fake distfiles/binpkg directory @path populated
+ with generated files of small random sizes using real names from
+ the files list. udates the clean_dict with fullpathname.
+
+ @param path: the path to create the distfiles directory
+ @param files: list of file names to populate "path" with
+ @param clean_dict: dict of {file-key:[path/file-key,],}
+ that will be updated with full file-path-names
+ """
+ make_dir(path)
+ for file_ in files:
+ size = random.randint(1000,5000)
+ data = "0" * size
+ filepath = os.path.join(path, file_)
+ with open(filepath, 'w', file_mode) as new_file:
+ new_file.write(data)
+ if file_ not in clean_dict:
+ # it is included in a multifile target
+ continue
+ elif clean_dict[file_] == []:
+ clean_dict[file_] = filepath
+ else:
+ file_list = clean_dict[file_]
+ for key in range(len(file_list)):
+ file_list[key] = os.path.join(path, file_list[key])
+
+
+def make_pkgs(path, files_dict, clean_dict):
+ """Create a small fake packages directory and call make_dist() to
+ create and populate the category dir & package files
+
+ @param path: the path to create the packages directory
+ @param files_dict: dictionary of {cat: [pkg1, pkg2,...]}
+ """
+ make_dir(path)
+ for cat in files_dict.keys():
+ make_dist(os.path.join(path,cat),
+ files_dict[cat],
+ clean_dict)
+ # cp the Packages index file to path
+ source = os.path.join(os.path.dirname(__file__), 'Packages')
+ shutil.copy2(source, path)
+
+
+def make_symlinks(path, links, targets):
+ """Create some symlinks at path
+
+ @param path: the location to create the symlinks at
+ @param links: list of links to create
+ @param targets: list of targets to create links for,
+ and need to be in the same index order as links
+ """
+ for i in range(len(links)):
+ os.symlink(os.path.join(path,target[i]),
+ os.path.join(path, links[i]))
+
+
+class TestDirCreation(object):
+ """"""
+
+ distfile_list = ['ExtUtils-ParseXS-2.22.tar.gz',
+ 'xorg-server-1.5.3.tar.bz2',
+ 'portage-utils-0.2.1.tar.bz2',
+ 'sysvinit_2.87dsf.orig.tar.gz',
+ 'sysvinit-2.86.tar.gz',
+ 'ExtUtils-ParseXS-2.20.tar.gz',
+ 'libisofs-0.6.22.tar.gz',
+ 'pixman-0.16.0.tar.bz2',
+ 'libburn-0.7.2.pl01.tar.gz',
+ 'libisofs-0.6.24.tar.gz',
+ 'xorg-server-1.5.3-gentoo-patches-08.tar.bz2',
+ 'ExtUtils-ParseXS-2.200401.tar.gz',
+ 'sysvinit-2.87-patches-2.tar.bz2',
+ 'sysvinit-2.86-kexec.patch',
+ 'Module-Build-0.3601.tar.gz',
+ 'libisofs-0.6.20.tar.gz',
+ 'xine-lib-1.1.17.tar.bz2',
+ 'pixman-0.14.0.tar.bz2',
+ 'Archive-Tar-1.52.tar.gz',
+ 'libburn-0.6.8.pl00.tar.gz',
+ 'libexif-0.6.17.tar.bz2',
+ 'portage-utils-0.3.tar.bz2',
+ 'xine-lib-1.1.15-textrel-fix.patch',
+ 'Module-Build-0.34.tar.gz',
+ 'Archive-Tar-1.54.tar.gz',
+ 'pixman-0.16.2.tar.bz2',
+ 'libburn-0.7.4.pl00.tar.gz ',
+ 'Module-Build-0.340201.tar.gz',
+ 'pixman-0.17.2.tar.bz2',
+ 'util-macros-1.3.0.tar.bz2',
+ 'Module-Build-0.35.tar.gz',
+ 'libburn-0.7.2.pl00.tar.gz',
+ 'util-macros-1.4.1.tar.bz2',
+ 'xine-lib-1.1.16.3.tar.bz2',
+ 'sysvinit-2.86-extra.patch',
+ 'libburn-0.7.0.pl00.tar.gz',
+ 'ExtUtils-ParseXS-2.21.tar.gz',
+ 'libexif-0.6.19.tar.bz2',
+ 'sysvinit-2.87-patches-1.tar.bz2',
+ # now a base pkg with 2 additional symlink targets
+ 'symlink-test-1.2.3.tar.bz2',
+ 'target-1',
+ 'target-2'
+ ]
+
+ distfile_symlink = ['symlink-test-1.2.3-symlink1',
+ 'symlink-test-1.2.3-symlink2']
+
+ dist_clean = {
+ 'Archive-Tar-1.52.tar.gz': [],
+ 'ExtUtils-ParseXS-2.20.tar.gz': [],
+ 'ExtUtils-ParseXS-2.200401.tar.gz': [],
+ 'ExtUtils-ParseXS-2.21.tar.gz': [],
+ 'Module-Build-0.34.tar.gz': [],
+ 'Module-Build-0.340201.tar.gz': [],
+ 'Module-Build-0.35.tar.gz': [],
+ 'libburn-0.6.8.pl00.tar.gz': [],
+ 'libburn-0.7.0.pl00.tar.gz': [],
+ 'libburn-0.7.2.pl00.tar.gz': [],
+ 'libburn-0.7.2.pl01.tar.gz': [],
+ 'libexif-0.6.17.tar.bz2': [],
+ 'libisofs-0.6.20.tar.gz': [],
+ 'libisofs-0.6.22.tar.gz': [],
+ 'pixman-0.14.0.tar.bz2': [],
+ 'pixman-0.16.0.tar.bz2': [],
+ 'pixman-0.16.2.tar.bz2': [],
+ 'portage-utils-0.2.1.tar.bz2': [],
+ 'sysvinit-2.86.tar.gz': ['sysvinit-2.86.tar.gz',
+ 'sysvinit-2.86-kexec.patch', 'sysvinit-2.86-extra.patch'],
+ 'util-macros-1.3.0.tar.bz2': [],
+ 'xine-lib-1.1.15-textrel-fix.patch': [],
+ 'xine-lib-1.1.16.3.tar.bz2': [],
+ 'xorg-server-1.5.3.tar.bz2': ['xorg-server-1.5.3.tar.bz2',
+ 'xorg-server-1.5.3-gentoo-patches-08.tar.bz2'],
+ 'symlink-test-1.2.3.tar.bz2': distfile_symlink
+ }
+
+ package_dict = {
+ 'app-arch': ['p7zip-4.65.tbz2', 'p7zip-4.57.tbz2',
+ 'file-roller-2.26.3.tbz2', 'tar-1.20.tbz2',
+ 'p7zip-4.58.tbz2', 'file-roller-2.28.2.tbz2',
+ 'file-roller-2.24.3.tbz2', 'gzip-1.4.tbz2', 'rar-3.9.0.tbz2',
+ 'bzip2-1.0.5-r1.tbz2', 'cpio-2.10.tbz2', 'tar-1.21-r1.tbz2',
+ 'cpio-2.10-r1.tbz2', 'file-roller-2.28.1.tbz2', 'cpio-2.9-r2.tbz2',
+ 'tar-1.22.tbz2', 'cpio-2.9-r3.tbz2'],
+ 'app-editors': ['nano-2.2.0.tbz2', 'nano-2.1.10.tbz2',
+ 'nano-2.0.9.tbz2', 'nano-2.2.2.tbz2'],
+ 'app-portage': ['layman-1.3.0_rc1-r3.tbz2', 'layman-1.2.6.tbz2',
+ 'portage-utils-0.3.1.tbz2', 'layman-1.3.0.tbz2',
+ 'layman-1.2.4-r3.tbz2', 'layman-1.2.3.tbz2',
+ 'layman-1.3.0_rc1.tbz2'],
+ 'sys-apps': ['shadow-4.0.18.2.tbz2', 'shadow-4.1.2.2.tbz2',
+ 'openrc-0.6.0-r1.tbz2', 'shadow-4.1.4.2-r1.tbz2',
+ 'shadow-4.1.4.2-r2.tbz2']
+ }
+
+ pkg_clean = {
+ 'app-arch/p7zip-4.57.tbz2': [],
+ 'app-arch/file-roller-2.26.3.tbz2': [],
+ 'app-arch/tar-1.20.tbz2': [],
+ 'app-arch/p7zip-4.58.tbz2': [],
+ 'app-arch/file-roller-2.28.2.tbz2': [],
+ 'app-arch/file-roller-2.24.3.tbz2': [],
+ 'app-arch/bzip2-1.0.5-r1.tbz2': [],
+ 'app-arch/cpio-2.10.tbz2': [],
+ 'app-arch/tar-1.21-r1.tbz2': [],
+ 'app-arch/cpio-2.9-r2.tbz2': [],
+ 'app-arch/cpio-2.9-r3.tbz2': [],
+ 'app-editors/nano-2.2.0.tbz2': [],
+ 'app-editors/nano-2.1.10.tbz2': [],
+ 'app-editors/nano-2.0.9.tbz2': [],
+ 'app-portage/layman-1.3.0_rc1-r3.tbz2': [],
+ 'app-portage/layman-1.2.6.tbz2': [],
+ 'app-portage/layman-1.2.4-r3.tbz2': [],
+ 'app-portage/layman-1.2.3.tbz2': [],
+ 'app-portage/layman-1.3.0_rc1.tbz2': [],
+ 'sys-apps/shadow-4.0.18.2.tbz2': [],
+ 'sys-apps/shadow-4.1.2.2.tbz2': [],
+ 'sys-apps/shadow-4.1.4.2-r1.tbz2': [],
+ }
+
+ def __init__(self, options):
+ """Initialization
+
+ @param options: dict.
+ """
+ self.options = options
+ self.targets_init = False
+ # create distfiles dir and populate it
+ make_dist(self.options['target_path'], self.distfile_list, self.dist_clean)
+ # add some symlinks to it
+ path = os.path.join(self.options['target_path'], 'distfiles')
+ make_symlinks(path, distfile_symlink,
+ dist_clean['symlink-test-1.2.3.tar.bz2'])
+ # create the packages dir and populate it
+ path = os.path.join(self.options['target_path'], 'packages')
+ make_pkgs(path, self.package_dict, self.pkg_clean)
+ self.targets_init = True
+
+ #def get_
diff --git a/pym/gentoolkit/test/eclean/distfiles.exclude b/pym/gentoolkit/test/eclean/distfiles.exclude
new file mode 100644
index 0000000..700c1bb
--- /dev/null
+++ b/pym/gentoolkit/test/eclean/distfiles.exclude
@@ -0,0 +1,11 @@
+# test exclude file
+
+app-portage
+
+media-libs/sdl-pango
+
+sys-auth/consolekit-0.4.1
+
+!app-emulation/emul-linux-x86-baselibs
+
+help2man-1.37.1.tar.gz
diff --git a/pym/gentoolkit/test/eclean/distsupport.py b/pym/gentoolkit/test/eclean/distsupport.py
new file mode 100644
index 0000000..3a4b011
--- /dev/null
+++ b/pym/gentoolkit/test/eclean/distsupport.py
@@ -0,0 +1,482 @@
+# Copyright(c) 2009, Gentoo Foundation
+# Copyright 2010 Brian Dolbec <brian.dolbec@gmail.com>
+#
+# License: GPL2/BSD
+
+# $Header$
+
+
+from __future__ import print_function
+
+import re
+import os
+import unittest
+from tempfile import NamedTemporaryFile, mkdtemp
+import subprocess
+import portage
+
+
+dir_mode = 0o774
+
+CPVS = [
+ 'sys-auth/consolekit-0.4.1',
+ 'sys-apps/devicekit-power-014',
+ 'media-libs/sdl-pango-0.1.2',
+ 'sys-apps/help2man-1.37.1',
+ 'app-emulation/emul-linux-x86-baselibs-20100220'
+ ]
+
+PROPS = {
+ 'sys-apps/devicekit-power-014': {
+ 'SRC_URI':'http://hal.freedesktop.org/releases/DeviceKit-power-014.tar.gz',
+ "RESTRICT": ''},
+ 'sys-apps/help2man-1.37.1': {
+ "SRC_URI": 'mirror://gnu/help2man/help2man-1.37.1.tar.gz',
+ "RESTRICT": ''},
+ 'sys-auth/consolekit-0.4.1': {
+ "SRC_URI": 'http://www.freedesktop.org/software/ConsoleKit/dist/ConsoleKit-0.4.1.tar.bz2',
+ "RESTRICT": ''},
+ 'app-emulation/emul-linux-x86-baselibs-20100220': {
+ "SRC_URI": 'mirror://gentoo/emul-linux-x86-baselibs-20100220.tar.gz',
+ "RESTRICT": 'strip'},
+ 'media-libs/sdl-pango-0.1.2': {
+ "SRC_URI": 'mirror://sourceforge/sdlpango/SDL_Pango-0.1.2.tar.gz http://zarb.org/~gc/t/SDL_Pango-0.1.2-API-adds.patch',
+ "RESTRICT": ''},
+ 'x11-base/xorg-server-1.6.5-r1': {
+ "SRC_URI": 'http://xorg.freedesktop.org/releases/individual/xserver/xorg-server-1.6.5.tar.bz2 mirror://gentoo/xorg-server-1.6.5-gentoo-patches-01.tar.bz2',
+ "RESTRICT": ''},
+ 'perl-core/ExtUtils-ParseXS-2.20.0401': {
+ "SRC_URI": 'mirror://cpan/authors/id/D/DA/DAGOLDEN//ExtUtils-ParseXS-2.200401.tar.gz',
+ "RESTRICT": ''},
+ 'x11-misc/util-macros-1.3.0': {
+ "SRC_URI": 'http://xorg.freedesktop.org/releases/individual/util/util-macros-1.3.0.tar.bz2',
+ "RESTRICT": ''},
+ 'x11-base/xorg-server-1.7.5': {
+ "SRC_URI": 'http://xorg.freedesktop.org/releases/individual/xserver/xorg-server-1.7.5.tar.bz2',
+ "RESTRICT": ''},
+ 'app-portage/portage-utils-0.3.1': {
+ "SRC_URI": 'mirror://gentoo/portage-utils-0.3.1.tar.bz2',
+ "RESTRICT": ''},
+ 'x11-misc/util-macros-1.5.0': {
+ "SRC_URI": 'http://xorg.freedesktop.org/releases/individual/util/util-macros-1.5.0.tar.bz2',
+ "RESTRICT": ''},
+ 'perl-core/Module-Build-0.35': {
+ "SRC_URI": 'mirror://cpan/authors/id/D/DA/DAGOLDEN//Module-Build-0.35.tar.gz',
+ "RESTRICT": ''},
+ 'perl-core/ExtUtils-ParseXS-2.22.02': {
+ "SRC_URI": 'mirror://cpan/authors/id/D/DA/DAGOLDEN//ExtUtils-ParseXS-2.2202.tar.gz',
+ "RESTRICT": ''},
+ 'perl-core/ExtUtils-ParseXS-2.22.03': {
+ "SRC_URI": 'mirror://cpan/authors/id/D/DA/DAGOLDEN//ExtUtils-ParseXS-2.2203.tar.gz',
+ "RESTRICT": ''},
+ 'perl-core/ExtUtils-ParseXS-2.22.01': {
+ "SRC_URI": 'mirror://cpan/authors/id/D/DA/DAGOLDEN//ExtUtils-ParseXS-2.2201.tar.gz',
+ "RESTRICT": ''},
+ 'perl-core/Archive-Tar-1.38': {
+ "SRC_URI": 'mirror://cpan/authors/id/K/KA/KANE/Archive-Tar-1.38.tar.gz',
+ "RESTRICT": ''},
+ 'perl-core/Archive-Tar-1.58': {
+ "SRC_URI": 'mirror://cpan/authors/id/B/BI/BINGOS//Archive-Tar-1.58.tar.gz',
+ "RESTRICT": ''},
+ 'perl-core/Archive-Tar-1.54': {
+ "SRC_URI": 'mirror://cpan/authors/id/B/BI/BINGOS//Archive-Tar-1.54.tar.gz',
+ "RESTRICT": ''},
+ 'perl-core/Archive-Tar-1.56': {
+ "SRC_URI": 'mirror://cpan/authors/id/B/BI/BINGOS//Archive-Tar-1.56.tar.gz',
+ "RESTRICT": ''},
+ 'app-portage/portage-utils-0.2.1': {
+ "SRC_URI": 'mirror://gentoo/portage-utils-0.2.1.tar.bz2',
+ "RESTRICT": ''},
+ 'dev-libs/libisofs-0.6.20-r1': {
+ "SRC_URI": 'http://files.libburnia-project.org/releases/libisofs-0.6.20.tar.gz',
+ "RESTRICT": ''},
+ 'perl-core/ExtUtils-ParseXS-2.22.02-r1': {
+ "SRC_URI": 'mirror://cpan/authors/id/D/DA/DAGOLDEN//ExtUtils-ParseXS-2.2202.tar.gz',
+ "RESTRICT": ''},
+ 'x11-misc/util-macros-1.6.0': {
+ "SRC_URI": 'http://xorg.freedesktop.org/releases/individual/util/util-macros-1.6.0.tar.bz2',
+ "RESTRICT": ''},
+ 'x11-libs/pixman-0.16.0': {
+ "SRC_URI": 'http://xorg.freedesktop.org/releases/individual/lib/pixman-0.16.0.tar.bz2',
+ "RESTRICT": ''},
+ 'x11-libs/pixman-0.16.4': {
+ "SRC_URI": 'http://xorg.freedesktop.org/releases/individual/lib/pixman-0.16.4.tar.bz2',
+ "RESTRICT": ''},
+ 'x11-libs/pixman-0.17.4': {
+ "SRC_URI": 'http://xorg.freedesktop.org/releases/individual/lib/pixman-0.17.4.tar.bz2',
+ "RESTRICT": ''},
+ 'x11-libs/pixman-0.17.2': {
+ "SRC_URI": 'http://xorg.freedesktop.org/releases/individual/lib/pixman-0.17.2.tar.bz2',
+ "RESTRICT": ''},
+ 'dev-libs/libburn-0.7.6-r1': {
+ "SRC_URI": 'http://files.libburnia-project.org/releases/libburn-0.7.6.pl00.tar.gz',
+ "RESTRICT": ''},
+ 'dev-libs/libburn-0.7.0': {
+ "SRC_URI": 'http://files.libburnia-project.org/releases/libburn-0.7.0.pl00.tar.gz',
+ "RESTRICT": ''},
+ 'perl-core/Module-Build-0.34.0201': {
+ "SRC_URI": 'mirror://cpan/authors/id/D/DA/DAGOLDEN//Module-Build-0.340201.tar.gz',
+ "RESTRICT": ''},
+ 'dev-libs/libburn-0.6.8': {
+ "SRC_URI": 'http://files.libburnia-project.org/releases/libburn-0.6.8.pl00.tar.gz',
+ "RESTRICT": ''},
+ 'dev-libs/libburn-0.7.4': {
+ "SRC_URI": 'http://files.libburnia-project.org/releases/libburn-0.7.4.pl00.tar.gz',
+ "RESTRICT": ''},
+ 'perl-core/Module-Build-0.36.03': {
+ "SRC_URI": 'mirror://cpan/authors/id/D/DA/DAGOLDEN//Module-Build-0.3603.tar.gz',
+ "RESTRICT": ''},
+ 'perl-core/Module-Build-0.36.01': {
+ "SRC_URI": 'mirror://cpan/authors/id/D/DA/DAGOLDEN//Module-Build-0.3601.tar.gz',
+ "RESTRICT": ''},
+ 'x11-base/xorg-server-1.5.3-r6': {
+ "SRC_URI": 'http://xorg.freedesktop.org/releases/individual/xserver/xorg-server-1.5.3.tar.bz2 mirror://gentoo/xorg-server-1.5.3-gentoo-patches-08.tar.bz2',
+ "RESTRICT": ''},
+ 'dev-libs/libisofs-0.6.28': {
+ "SRC_URI": 'http://files.libburnia-project.org/releases/libisofs-0.6.28.tar.gz',
+ "RESTRICT": ''},
+ 'media-libs/xine-lib-1.1.17': {
+ "SRC_URI": 'mirror://sourceforge/xine/xine-lib-1.1.17.tar.bz2 mirror://gentoo/xine-lib-1.1.15-textrel-fix.patch',
+ "RESTRICT": ''},
+ 'media-libs/xine-lib-1.1.18': {
+ "SRC_URI": 'mirror://sourceforge/xine/xine-lib-1.1.18.tar.xz mirror://gentoo/xine-lib-1.1.15-textrel-fix.patch mirror://gentoo/xine-lib-1.1.18-compat.c.tbz2',
+ "RESTRICT": ''},
+ 'perl-core/ExtUtils-ParseXS-2.22': {
+ "SRC_URI": 'mirror://cpan/authors/id/D/DA/DAGOLDEN//ExtUtils-ParseXS-2.22.tar.gz',
+ "RESTRICT": ''},
+ 'perl-core/ExtUtils-ParseXS-2.21': {
+ "SRC_URI": 'mirror://cpan/authors/id/D/DA/DAGOLDEN//ExtUtils-ParseXS-2.21.tar.gz',
+ "RESTRICT": ''},
+ 'x11-base/xorg-server-1.7.5.901': {
+ "SRC_URI": 'http://xorg.freedesktop.org/releases/individual/xserver/xorg-server-1.7.5.901.tar.bz2',
+ "RESTRICT": ''},
+ 'dev-libs/libisofs-0.6.24': {
+ "SRC_URI": 'http://files.libburnia-project.org/releases/libisofs-0.6.24.tar.gz',
+ "RESTRICT": ''},
+ 'dev-libs/libisofs-0.6.26': {
+ "SRC_URI": 'http://files.libburnia-project.org/releases/libisofs-0.6.26.tar.gz',
+ "RESTRICT": ''},
+ 'app-portage/portage-utils-0.3.1': {
+ "SRC_URI": 'mirror://gentoo/portage-utils-0.3.1.tar.bz2',
+ "RESTRICT": ''},
+ 'app-portage/gentoolkit-0.3.0_rc8-r1': {
+ "SRC_URI": 'mirror://gentoo/gentoolkit-0.3.0_rc8.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.3.0_rc8.tar.gz',
+ "RESTRICT": ''},
+ 'app-portage/gentoolkit-0.2.4.6-r1': {
+ "SRC_URI": 'mirror://gentoo/gentoolkit-0.2.4.6.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.2.4.6.tar.gz',
+ "RESTRICT": ''},
+ 'app-portage/eix-0.20.2': {
+ "SRC_URI": 'mirror://berlios/eix/eix-0.20.2.tar.xz',
+ "RESTRICT": ''},
+ 'app-portage/gentoolkit-0.2.4.5': {
+ "SRC_URI": 'mirror://gentoo/gentoolkit-0.2.4.5.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.2.4.5.tar.gz',
+ "RESTRICT": ''},
+ 'app-portage/portage-utils-0.2.1': {
+ "SRC_URI": 'mirror://gentoo/portage-utils-0.2.1.tar.bz2',
+ "RESTRICT": ''},
+ 'app-portage/gentoolkit-0.3.0_rc8': {
+ "SRC_URI": 'mirror://gentoo/gentoolkit-0.3.0_rc8.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.3.0_rc8.tar.gz',
+ "RESTRICT": ''},
+ 'app-portage/gentoolkit-0.2.4.6': {
+ "SRC_URI": 'mirror://gentoo/gentoolkit-0.2.4.6.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.2.4.6.tar.gz',
+ "RESTRICT": ''},
+ 'app-portage/layman-1.3.0-r1': {
+ "SRC_URI": 'mirror://sourceforge/layman/layman-1.3.0.tar.gz',
+ "RESTRICT": ''},
+ 'app-portage/gentoolkit-0.3.0_rc7': {
+ "SRC_URI": 'mirror://gentoo/gentoolkit-0.3.0_rc7.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.3.0_rc7.tar.gz',
+ "RESTRICT": ''},
+ 'app-portage/layman-1.3.0': {
+ "SRC_URI": 'mirror://sourceforge/layman/layman-1.3.0.tar.gz',
+ "RESTRICT": ''},
+ 'app-portage/layman-1.3.1': {
+ "SRC_URI": 'mirror://sourceforge/layman/layman-1.3.1.tar.gz',
+ "RESTRICT": ''},
+ 'app-portage/layman-1.2.6': {
+ "SRC_URI": 'mirror://sourceforge/layman/layman-1.2.6.tar.gz',
+ "RESTRICT": ''},
+ 'app-portage/layman-9999': {
+ "SRC_URI": '',
+ "RESTRICT": ''},
+ 'app-portage/layman-1.2.5': {
+ "SRC_URI": 'mirror://sourceforge/layman/layman-1.2.5.tar.gz',
+ "RESTRICT": ''},
+ 'app-portage/layman-1.3.0_rc1-r3': {
+ "SRC_URI": 'mirror://sourceforge/layman/layman-1.3.0_rc1.tar.gz',
+ "RESTRICT": ''},
+ 'app-portage/gentoolkit-0.3.0_rc9': {
+ "SRC_URI": 'mirror://gentoo/gentoolkit-0.3.0_rc9.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.3.0_rc9.tar.gz',
+ "RESTRICT": ''},
+ 'app-portage/eix-0.20.1': {
+ "SRC_URI": 'mirror://sourceforge/eix/eix-0.20.1.tar.xz',
+ "RESTRICT": ''},
+ 'app-portage/eix-0.19.2': {
+ "SRC_URI": 'mirror://sourceforge/eix/eix-0.19.2.tar.xz',
+ "RESTRICT": ''},
+ 'app-portage/layman-1.3.2-r1': {
+ "SRC_URI": 'mirror://sourceforge/layman/layman-1.3.2.tar.gz',
+ "RESTRICT": ''},
+}
+
+PKGS = {
+ 'app-portage/layman-1.3.2-r1': 'mirror://sourceforge/layman/layman-1.3.2.tar.gz',
+ 'app-portage/eix-0.20.1': 'mirror://sourceforge/eix/eix-0.20.1.tar.xz',
+ 'app-portage/eix-0.19.2': 'mirror://sourceforge/eix/eix-0.19.2.tar.xz',
+ 'app-portage/gentoolkit-0.3.0_rc9': 'mirror://gentoo/gentoolkit-0.3.0_rc9.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.3.0_rc9.tar.gz',
+ 'app-portage/gentoolkit-0.2.4.6': 'mirror://gentoo/gentoolkit-0.2.4.6.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.2.4.6.tar.gz',
+ 'media-libs/xine-lib-1.1.18': 'mirror://sourceforge/xine/xine-lib-1.1.18.tar.xz mirror://gentoo/xine-lib-1.1.15-textrel-fix.patch mirror://gentoo/xine-lib-1.1.18-compat.c.tbz2',
+ 'perl-core/ExtUtils-ParseXS-2.21': 'mirror://cpan/authors/id/D/DA/DAGOLDEN//ExtUtils-ParseXS-2.21.tar.gz',
+ 'dev-libs/libisofs-0.6.24': 'http://files.libburnia-project.org/releases/libisofs-0.6.24.tar.gz',
+ }
+
+CLEAN_ME = {
+ 'layman-1.3.2.tar.gz': '/path/to/some/where/layman-1.3.2.tar.gz',
+ 'layman-1.2.5.tar.gz': '/path/to/some/where/layman-1.2.5.tar.gz',
+ 'eix-0.20.1.tar.xz': '/path/to/some/where/eix-0.20.1.tar.xz',
+ 'gentoolkit-0.3.0_rc9.tar.gz': '/path/to/some/where/gentoolkit-0.3.0_rc9.tar.gz',
+ 'xine-lib-1.1.18.tar.xz': '/path/to/some/where/xine-lib-1.1.18.tar.xz',
+ 'xine-lib-1.1.15-textrel-fix.patch': '/path/to/some/where/xine-lib-1.1.15-textrel-fix.patch',
+ 'xine-lib-1.1.18-compat.c.tbz2': '/path/to/some/where/xine-lib-1.1.18-compat.c.tbz2',
+ 'ExtUtils-ParseXS-2.21.tar.gz': '/path/to/some/where/ExtUtils-ParseXS-2.21.tar.gz',
+ 'libisofs-0.6.24.tar.gz': '/path/to/some/where/libisofs-0.6.24.tar.gz'
+ }
+
+CPVS2 = [
+ 'app-emulation/emul-linux-x86-baselibs-20100220',
+ 'app-portage/eix-0.19.2', 'app-portage/eix-0.20.1',
+ 'app-portage/eix-0.20.2',
+ 'app-portage/gentoolkit-0.2.4.5',
+ 'app-portage/gentoolkit-0.2.4.6',
+ 'app-portage/gentoolkit-0.2.4.6-r1',
+ 'app-portage/gentoolkit-0.3.0_rc7',
+ 'app-portage/gentoolkit-0.3.0_rc8',
+ 'app-portage/gentoolkit-0.3.0_rc8-r1',
+ 'app-portage/gentoolkit-0.3.0_rc9',
+ 'app-portage/layman-1.2.5',
+ 'app-portage/layman-1.2.6',
+ 'app-portage/layman-1.3.0',
+ 'app-portage/layman-1.3.0-r1',
+ 'app-portage/layman-1.3.0_rc1-r3',
+ 'app-portage/layman-1.3.1',
+ 'app-portage/layman-1.3.2-r1',
+ 'app-portage/layman-9999',
+ 'app-portage/portage-utils-0.2.1',
+ 'app-portage/portage-utils-0.3.1',
+ 'dev-libs/libburn-0.6.8',
+ 'dev-libs/libburn-0.7.0',
+ 'dev-libs/libburn-0.7.4',
+ 'dev-libs/libburn-0.7.6-r1',
+ 'dev-libs/libisofs-0.6.20-r1',
+ 'dev-libs/libisofs-0.6.24',
+ 'dev-libs/libisofs-0.6.26',
+ 'dev-libs/libisofs-0.6.28',
+ 'media-libs/sdl-pango-0.1.2',
+ 'media-libs/xine-lib-1.1.17',
+ 'media-libs/xine-lib-1.1.18',
+ 'perl-core/Archive-Tar-1.38',
+ 'perl-core/Archive-Tar-1.54',
+ 'perl-core/Archive-Tar-1.56',
+ 'perl-core/Archive-Tar-1.58',
+ 'perl-core/ExtUtils-ParseXS-2.20.0401',
+ 'perl-core/ExtUtils-ParseXS-2.21',
+ 'perl-core/ExtUtils-ParseXS-2.22',
+ 'perl-core/ExtUtils-ParseXS-2.22.01',
+ 'perl-core/ExtUtils-ParseXS-2.22.02',
+ 'perl-core/ExtUtils-ParseXS-2.22.02-r1',
+ 'perl-core/ExtUtils-ParseXS-2.22.03',
+ 'perl-core/Module-Build-0.34.0201',
+ 'perl-core/Module-Build-0.35',
+ 'perl-core/Module-Build-0.36.01',
+ 'perl-core/Module-Build-0.36.03',
+ 'sys-apps/devicekit-power-014',
+ 'sys-apps/help2man-1.37.1',
+ 'sys-auth/consolekit-0.4.1',
+ 'x11-base/xorg-server-1.5.3-r6',
+ 'x11-base/xorg-server-1.6.5-r1',
+ 'x11-base/xorg-server-1.7.5',
+ 'x11-base/xorg-server-1.7.5.901',
+ 'x11-libs/pixman-0.16.0',
+ 'x11-libs/pixman-0.16.4',
+ 'x11-libs/pixman-0.17.2',
+ 'x11-libs/pixman-0.17.4',
+ 'x11-misc/util-macros-1.3.0',
+ 'x11-misc/util-macros-1.5.0',
+ 'x11-misc/util-macros-1.6.0'
+ ]
+
+FILES = [
+ 'DeviceKit-power-014.tar.gz',
+ 'help2man-1.37.1.tar.gz',
+ 'ConsoleKit-0.4.1.tar.bz2',
+ 'emul-linux-x86-baselibs-20100220.tar.gz',
+ 'SDL_Pango-0.1.2.tar.gz',
+ 'SDL_Pango-0.1.2-API-adds.patch'
+ ]
+
+
+CPVS3 = [
+ 'x11-base/xorg-server-1.7.5',
+ 'x11-misc/util-macros-1.6.0',
+ 'x11-libs/pixman-0.16.4',
+ #'dev-libs/libisofs-0.6.28',
+ #'perl-core/Module-Build-0.36.03',
+ #'perl-core/ExtUtils-ParseXS-2.22.02-r1',
+ #'perl-core/Archive-Tar-1.56',
+ #'app-portage/gentoolkit-0.3.0_rc8-r1',
+ #'app-portage/layman-1.3.1',
+ #'app-portage/eix-0.20.1',
+ ]
+
+
+Exclude= {'packages': {
+ 'media-libs/sdl-pango': None,
+ },
+ 'anti-packages': {'app-emulation/emul-linux-x86-baselibs': None},
+ 'categories': {'app-portage': None,
+ 'app-portage/gentoolkit': None
+ },
+ 'filenames': {'sys-auth/consolekit-0.4.1': re.compile('sys-auth/consolekit-0.4.1')
+ }
+ }
+
+
+def get_props(cpvs):
+ props = {}
+ for cpv in cpvs:
+ props[cpv] = PROPS[cpv].copy()
+ return props
+
+class Dbapi(object):
+ """Fake portage dbapi class used to return
+ pre-determined test data in place of a live system
+
+ @param cp_all: list of cat/pkg's to use for testing
+ eg: ['app-portage/gentoolkit', 'app-portage/porthole',...]
+ @param cpv_all: list of cat/pkg-ver's to use for testing.
+ @param props: dictionary of ebuild properties to use for testing.
+ eg: {'cpv': {"SRC_URI": 'http://...', "RESTRICT": restriction},}
+ @param cp_list: ?????????
+ """
+
+ def __init__(self, cp_all=None, cpv_all=None, props=None,
+ cp_list=None, name=None):
+ self._cp_all = cp_all
+ self._cpv_all = cpv_all
+ self._props = props
+ self._cp_list = cp_list
+ self.name = name
+ #print(self.name, "DBAPI: cpv_all=")
+ #print(self._cpv_all)
+ #print(self.name, "DBAPI: props=")
+ #print(self._props)
+
+ def cp_all(self):
+ return self._cp_all[:]
+
+ def cp_list(self, package):
+ #print(self._cp_list)
+ if self._cp_list is None or self._cp_list==[]:
+ cplist = []
+ for cpv in self._cpv_all:
+ parts = portage.catpkgsplit(cpv)
+ cp='/'.join(parts[:2])
+ if cp == package:
+ cplist.append(cpv)
+ #print("package = %s, cplist = %s" %(package, cplist))
+ return cplist
+ else:
+ return self._cp_list
+
+ def cpv_all(self):
+ #print(self.name, type(self._cpv_all), self._cpv_all)
+ return self._cpv_all
+
+ def cpv_exists(self, cpv):
+ return cpv in self._cpv_all
+
+ def aux_get(self, cpv, prop_list):
+ """only need stubs for ["SRC_URI","RESTRICT"]
+ """
+ #print("FAKE DBAPI", cpv, prop_list)
+ props = []
+ for prop in prop_list:
+ if cpv in self._props:
+ props.append(self._props[cpv][prop])
+ else:
+ raise KeyError(self.name)
+ #print(self.name, "DBAPI", cpv, props)
+ return props
+
+
+class OutputSimulator(object):
+ """Simple output accumulator used for testing.
+ Simulates eclean.output.OutputControl class """
+
+ def __init__(self, callback):
+ self.callback = callback
+
+ def set_data(self, data):
+ """sets the data for the progress_controller to return
+ for the test being performed"""
+ self.data = data
+
+ def einfo(self, message=""):
+ self.callback('einfo', message)
+
+ def eprompt(self, message):
+ self.callback('eprompt', message)
+
+ def prettySize(self, size, justify=False, color=None):
+ self.callback('prettySize', size)
+
+ def yesNoAllPrompt(self, message="Dummy"):
+ self.callback('yesNoAllPrompt', message)
+
+ def progress_controller(self, size, key, clean_list, file_type):
+ self.callback('progress_controller', self.data)
+ return self.data
+
+ def total(self, mode, size, num_files, verb, action):
+ pass
+
+ def list_pkgs(self, pkgs):
+ self.callback('list_pkgs', pkgs)
+
+
+class TestDisfiles(object):
+
+ def __init__(self):
+ self.workdir = None
+ self.target_file = None
+ self.target_symlink = None
+ self.test_filepaths = None
+
+ def setUp(self):
+ # create the dist dir
+ self.tmpdir = mkdtemp()
+ #print("New tmpdir =", self.tmpdir)
+ os.chmod(self.tmpdir, dir_mode)
+ self.workdir = os.path.join(self.tmpdir, 'distfiles')
+ dir = os.path.dirname(os.path.abspath(__file__))
+ file = os.path.join(dir,"testdistfiles.tar.gz")
+ command = "tar -xpf %s -C %s" %(file, self.tmpdir)
+ retcode = subprocess.call(command, shell=True)
+ # create a symlink as part of the test files
+ #print()
+ self.target_symlink = "symlink-1.0.0.tar.gz"
+ os.symlink(file, os.path.join(self.workdir, self.target_symlink))
+ self.files = FILES[:]
+ self.files.append(self.target_symlink)
+ self.test_filepaths = []
+ for file in self.files:
+ self.test_filepaths.append(os.path.join(self.workdir, file))
+
+ def tearDown(self):
+ for file in self.test_filepaths:
+ os.unlink(file)
+ #print("deleting workdir =", self.workdir)
+ os.rmdir(self.workdir)
+ #print("deleting tmpdir =", self.tmpdir)
+ os.rmdir(self.tmpdir)
+
+
diff --git a/pym/gentoolkit/test/eclean/test_clean.py b/pym/gentoolkit/test/eclean/test_clean.py
new file mode 100644
index 0000000..25a760a
--- /dev/null
+++ b/pym/gentoolkit/test/eclean/test_clean.py
@@ -0,0 +1,152 @@
+#!/usr/bin/python
+#
+# Copyright 2010 Brian Dolbec <brian.dolbec@gmail.com>
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+from __future__ import print_function
+
+__version__= "0.0.1"
+__author__ = "Brian Dolbec"
+__email__ = "brian.dolbec@gmail.com"
+
+from getopt import gnu_getopt, GetoptError
+
+import unittest
+import os
+import sys
+
+import gentoolkit.pprinter as pp
+try:
+ from test import test_support
+except ImportError:
+ from test import support as test_support
+
+from gentoolkit.eclean.clean import CleanUp
+
+
+class Controllers(object):
+ """Contains controller methods for use in testing
+ the clean module methods"""
+
+ def __init__(self):
+ self.gathered_data = []
+ self.authorize = True
+ self.authorize_list = []
+ self.authorize_index = 0
+
+ def authorize_all_controller(self, size, key, clean_list):
+ """data gatherering controller.
+
+ @rtype: Boolean
+ @returns: self.authorize which controls the cleaning method
+ """
+ self.gathered_data.append([size, key, clean_list])
+ return self.authorize
+
+ def authorize_list_controller(self, size, key, clean_list):
+ """data gathering and controller which
+ authorizes acoring to a pre-determined list
+
+ @rtype: Boolean
+ @return self.authorize_list[self.authorize_index]"""
+ self.gathered_data.append([size, key, clean_list])
+ index = self.authorize_index
+ self.authorize_index =+ 1
+ return self.authorize_list[index]
+
+
+#class TestCleanUp(unittest.TestCase):
+# """Test module for the various CleanUp class methods
+#
+# @param options: dict of module options
+# @param testdata: dict. of path and test parameters
+# as created by the TestDirCreation class"""
+#
+# def __init__(self, options, testdata):
+# self.options = options
+# self.tesdata = testdata
+#
+#
+# def test_symlink_clean():
+# """Tests the symbolic link portion of the distfiles
+# cleaning"""
+# pass
+#
+#
+# def test_dist_clean():
+# """Test the distfiles cleaning"""
+# pass
+#
+#
+# def test_pkg_clean():
+# """Test the packages cleaning"""
+# pass
+#
+#
+# def test_pretend_clean():
+# """Test the pretend_clean output"""
+# controlller = Controllers().authorize_all_controller
+# clean = CleanUp(controller)
+# clean.pretend_clean(self.dist_clean)
+# data = controller.gathered_data
+
+
+
+def useage():
+ """output run options"""
+ print("Useage: test_clean [OPTONS] path=test-dir")
+ print(" where test-dir is the location to create and populate")
+ print("the testing distfiles and packages directories.")
+ print("All tests in this module test only the clean.py module functions")
+ print()
+ print("OPTIONS:")
+ print(" -a, --all run all tests")
+ print(" -c, --clean clean up any previous test dirs & files")
+ print(" -D, --distfiles run the distfiles cleaning test")
+ print(" -k, --keep-dirs keep the test directories and files after the test")
+ print(" -p, --pretend run the test in pretend mode only")
+ print(" -P, --packages run the packages cleaning test")
+ print(" -S, --symlinks run the symlinks test")
+ print(" --path the location to create the temporary distfiles")
+ print(" and packages directories that will be test cleaned")
+ print(" --version test module version")
+ print()
+
+
+def parse_opts():
+ """Parse the options dict
+
+ @return options: dictionary of module options"""
+ try:
+ opts, args = getopt(sys.argv[1:], 'acDkpPS', ["version",
+ "help", "path=", "all", "distfiles", "packages",
+ "pretend", "symlinks", "keep-dirs", "clean"])
+ #print opts
+ #print args
+ except GetoptError as e:
+ print(e.msg, file=sys.stderr)
+ usage()
+ sys.exit(1)
+
+
+
+def main(cmdline=False):
+ """parse options and run the tests"""
+
+ if cmdline:
+ options = parse_opts()
+
+
+if __name__ == "__main__":
+ """actually call main() if launched as a script"""
+ try:
+ main(True)
+ except KeyboardInterrupt:
+ print("Aborted.")
+ sys.exit(130)
+ sys.exit(0)
+
+
diff --git a/pym/gentoolkit/test/eclean/test_search.py b/pym/gentoolkit/test/eclean/test_search.py
new file mode 100644
index 0000000..328c543
--- /dev/null
+++ b/pym/gentoolkit/test/eclean/test_search.py
@@ -0,0 +1,632 @@
+#!/usr/bin/python
+
+
+# Copyright(c) 2009, Gentoo Foundation
+# Copyright 2010 Brian Dolbec <brian.dolbec@gmail.com>
+#
+# License: GPL2/BSD
+
+# $Header$
+
+
+from __future__ import print_function
+
+
+from tempfile import NamedTemporaryFile, mkdtemp
+import unittest
+import re
+
+try:
+ from test import test_support
+except ImportError:
+ from test import support as test_support
+
+from gentoolkit.test.eclean.distsupport import *
+import gentoolkit.eclean.search as search
+from gentoolkit.eclean.search import DistfilesSearch
+from gentoolkit.eclean.exclude import parseExcludeFile
+
+"""Tests for eclean's distfiles search functions."""
+
+
+class DistLimits(DistfilesSearch):
+ """subclass the DistfilesSearch class in order to override a number of
+ functions to isolate & test"""
+
+ def __init__(self,
+ output=lambda x: None,
+ portdb=None,
+ vardb=None,
+ ):
+ DistfilesSearch.__init__(self, output, portdb, vardb)
+ self.data = None
+
+ def set_data(self, data):
+ """sets the data for the functions to return for
+ the test being performed"""
+ self.data = data
+
+
+class TestCheckLimits(unittest.TestCase):
+ """Test the eclean.search.DistfilesSearch._check_limits() group.
+
+ it will test [ _get_default_checks(), _check_limits(),
+ _isreg_check_(), _size_check_(), _time_check_(), _filenames_check_()]
+ """
+
+ test_excludes = {
+ 'blank': {},
+ 'filenames': {
+ 'filenames': {'help2man-1.37.1.tar.gz': re.compile('help2man-1.37.1.tar.gz')}
+ }
+ }
+
+ def setUp(self):
+ self.testdata = [
+ # test is_reg_limit alone, will give a base clean_me
+ { 'test': 'is_reg_limit',
+ 'params': (0, 0, self.test_excludes['blank']),
+ 'results': FILES[:],
+ 'output': [" - skipping size limit check",
+ " - skipping time limit check",
+ " - skipping exclude filenames check"
+ ]
+ },
+ # test size_limit trip
+ { 'test': 'size_limit',
+ 'params': (1024000, 0, self.test_excludes['blank']),
+ 'results': FILES[:3] + FILES[4:],
+ 'output': [
+ " - skipping time limit check",
+ " - skipping exclude filenames check"
+ ]
+ },
+ # test time_limit trip
+ { 'test': 'time_limit',
+ 'params': (0,1112671872, self.test_excludes['blank']),
+ 'results': [FILES[4]], # + FILES[5:],
+ 'output': [" - skipping size limit check",
+ " - skipping exclude filenames check"
+ ]
+ },
+ # test filenames_limit trip
+ { 'test': 'filenames_limit',
+ 'params': (0, 0, self.test_excludes['filenames']),
+ 'results': FILES[:1] + FILES[2:],
+ 'output': [" - skipping size limit check",
+ " - skipping time limit check",
+ ]
+ }
+ ]
+
+ self.testwork = TestDisfiles()
+ self.testwork.setUp()
+ self.workdir = self.testwork.workdir
+ self.target_class = DistLimits() #DistCheckLimits()
+ self.output = OutputSimulator(self.callback)
+ self.target_class.output = self.output
+ self.callback_data = []
+ self.test_index = 0
+
+ def tearDown(self):
+ self.testwork.tearDown()
+ #pass
+
+ def get_test(self, num):
+ return self.testdata[num]
+
+ def callback(self, id, data):
+ self.callback_data.append(data)
+
+ def set_limits(self, test):
+ limit = {}
+ #set is_reg always to testdata[0]
+ t1 = self.testdata[0]
+ limit[t1['test']] = {}
+ name = test['test']
+ limit[name] = {}
+ limits = test['limits']
+ for i in range(6):
+ file = self.testwork.files[i]
+ limits = test['limits']
+ limit[t1['test']][file] = t1['limits'][i]
+ if name != t1['test']:
+ limit[name][file] = limits[i]
+ return limit
+
+
+ def test_check_limits(self):
+ """Testing DistfilesSearch._check_limits()"""
+ # pass in output=self.output.einfo
+ self.target_class.output = self.output.einfo
+ run_callbacks = []
+ run_results = []
+ print()
+ # run the tests
+ for i in range(4):
+ clean_me = {}
+ test = self.get_test(i)
+ #print("test =", test['test'])
+ if not test:
+ print("Error getting test data for index:", i)
+ #self.target_class.set_data(self.set_limits(test))
+ size_chk, time_chk, exclude = test["params"]
+ checks = self.target_class._get_default_checks(size_chk, time_chk, exclude)
+ clean_me = self.target_class._check_limits(self.workdir, checks, clean_me)
+ results = sorted(clean_me)
+ run_results.append(results)
+ self.callback_data.sort()
+ run_callbacks.append(self.callback_data)
+ self.callback_data = []
+ results = None
+
+ # check results
+ for i in range(4):
+ test = self.get_test(i)
+ print("test =", test['test'])
+ if not test:
+ print("Error getting test data for index:", i)
+ test['results'].sort()
+ #print("actual=", run_results[i])
+ #print("should-be=", test['results'])
+ self.failUnlessEqual(run_results[i], test["results"],
+ "/ntest_check_limits, test# %d, test=%s, diff=%s"
+ %(i, test['test'], str(set(run_results[i]).difference(test['results'])))
+ )
+ test['output'].sort()
+ self.failUnlessEqual(run_callbacks[i], test['output'])
+
+
+class TestFetchRestricted(unittest.TestCase):
+ """Tests eclean.search.DistfilesSearch._fetch_restricted and _unrestricted
+ functions
+ """
+
+ def setUp(self):
+ self.vardb = Dbapi(cp_all=[], cpv_all=CPVS,
+ props=PROPS, cp_list=[], name="FAKE VARDB")
+ self.portdb = Dbapi(cp_all=[], cpv_all=CPVS[:4],
+ props=get_props(CPVS[:4]), cp_list=[], name="FAKE PORTDB")
+ # set a fetch restricted pkg
+ self.portdb._props[CPVS[0]]["RESTRICT"] = 'fetch'
+ self.callback_data = []
+ self.output = self.output = OutputSimulator(self.callback)
+ self.target_class = DistfilesSearch(self.output.einfo, self.portdb, self.vardb)
+ self.target_class.portdb = self.portdb
+ self.target_class.portdb = self.portdb
+ self.results = {}
+ self.testdata = {
+ 'fetch_restricted1':{
+ 'deprecated':
+ {'app-emulation/emul-linux-x86-baselibs-20100220': 'mirror://gentoo/emul-linux-x86-baselibs-20100220.tar.gz'
+ },
+ 'pkgs':
+ {'sys-auth/consolekit-0.4.1': 'http://www.freedesktop.org/software/ConsoleKit/dist/ConsoleKit-0.4.1.tar.bz2'
+ },
+ 'output': [
+ '!!! "Deprecation Warning: Installed package: app-emulation/emul-linux-x86-baselibs-20100220\n\tIs no longer in the tree or an installed overlay\n'
+ ]
+ },
+ 'fetch_restricted2':{
+ 'deprecated':
+ {'app-emulation/emul-linux-x86-baselibs-20100220': 'mirror://gentoo/emul-linux-x86-baselibs-20100220.tar.gz'
+ },
+ 'pkgs':
+ {'sys-auth/consolekit-0.4.1': 'http://www.freedesktop.org/software/ConsoleKit/dist/ConsoleKit-0.4.1.tar.bz2'
+ },
+ 'output': [
+ '!!! "Deprecation Warning: Installed package: app-emulation/emul-linux-x86-baselibs-20100220\n\tIs no longer in the tree or an installed overlay\n',
+ ' - Key Error looking up: app-portage/deprecated-pkg-1.0.0'
+ ]
+ },
+ 'unrestricted1':{
+ 'deprecated':{
+ 'app-emulation/emul-linux-x86-baselibs-20100220': 'mirror://gentoo/emul-linux-x86-baselibs-20100220.tar.gz'
+ },
+ 'pkgs': {
+ 'sys-apps/devicekit-power-014': 'http://hal.freedesktop.org/releases/DeviceKit-power-014.tar.gz',
+ 'sys-apps/help2man-1.37.1': 'mirror://gnu/help2man/help2man-1.37.1.tar.gz',
+ 'sys-auth/consolekit-0.4.1': 'http://www.freedesktop.org/software/ConsoleKit/dist/ConsoleKit-0.4.1.tar.bz2',
+ 'app-emulation/emul-linux-x86-baselibs-20100220': 'mirror://gentoo/emul-linux-x86-baselibs-20100220.tar.gz',
+ 'media-libs/sdl-pango-0.1.2': 'mirror://sourceforge/sdlpango/SDL_Pango-0.1.2.tar.gz http://zarb.org/~gc/t/SDL_Pango-0.1.2-API-adds.patch'
+ },
+ 'output': [
+ '!!! "Deprecation Warning: Installed package: app-emulation/emul-linux-x86-baselibs-20100220\n\tIs no longer in the tree or an installed overlay\n',
+ ]
+ },
+ 'unrestricted2':{
+ 'deprecated':{
+ 'app-emulation/emul-linux-x86-baselibs-20100220': 'mirror://gentoo/emul-linux-x86-baselibs-20100220.tar.gz'
+ },
+ 'pkgs': {
+ 'sys-apps/devicekit-power-014': 'http://hal.freedesktop.org/releases/DeviceKit-power-014.tar.gz',
+ 'sys-apps/help2man-1.37.1': 'mirror://gnu/help2man/help2man-1.37.1.tar.gz',
+ 'sys-auth/consolekit-0.4.1': 'http://www.freedesktop.org/software/ConsoleKit/dist/ConsoleKit-0.4.1.tar.bz2',
+ 'app-emulation/emul-linux-x86-baselibs-20100220': 'mirror://gentoo/emul-linux-x86-baselibs-20100220.tar.gz',
+ 'media-libs/sdl-pango-0.1.2': 'mirror://sourceforge/sdlpango/SDL_Pango-0.1.2.tar.gz http://zarb.org/~gc/t/SDL_Pango-0.1.2-API-adds.patch'
+ },
+ 'output': [
+ '!!! "Deprecation Warning: Installed package: app-emulation/emul-linux-x86-baselibs-20100220\n\tIs no longer in the tree or an installed overlay\n',
+ ' - Key Error looking up: app-portage/deprecated-pkg-1.0.0'
+ ]
+ }
+ }
+
+
+ def callback(self, id, data):
+ self.callback_data.append(data)
+
+
+ def test__fetch_restricted(self):
+ self.results = {}
+ pkgs, deprecated = self.target_class._fetch_restricted(None, CPVS)
+ self.record_results('fetch_restricted1', pkgs, deprecated)
+
+ self.callback_data = []
+ cpvs = CPVS[:]
+ cpvs.append('app-portage/deprecated-pkg-1.0.0')
+ pkgs, deprecated = self.target_class._fetch_restricted(None, cpvs)
+ self.record_results('fetch_restricted2', pkgs, deprecated)
+ self.check_results("test_fetch_restricted")
+
+
+ def test_unrestricted(self):
+ self.results = {}
+ pkgs, deprecated = self.target_class._unrestricted(None, CPVS)
+ self.record_results('unrestricted1', pkgs, deprecated)
+ self.callback_data = []
+ cpvs = CPVS[:]
+ cpvs.append('app-portage/deprecated-pkg-1.0.0')
+ pkgs, deprecated = self.target_class._unrestricted(None, cpvs)
+ self.record_results('unrestricted2', pkgs, deprecated)
+ self.check_results("test_unrestricted")
+
+
+ def check_results(self, test_name):
+ print("\nChecking results for %s,............" %test_name)
+ for key in sorted(self.results):
+ testdata = self.testdata[key]
+ results = self.results[key]
+ for item in sorted(testdata):
+ if sorted(results[item]) == sorted(testdata[item]):
+ test = "OK"
+ else:
+ test = "FAILED"
+ print("comparing %s, %s" %(key, item), test)
+ self.failUnlessEqual(sorted(testdata[item]), sorted(results[item]),
+ "\n%s: %s %s data does not match\nresult=" %(test_name, key, item) +\
+ str(results[item]) + "\ntestdata=" + str(testdata[item]))
+
+
+ def record_results(self, test, pkgs, deprecated):
+ self.results[test] = {'pkgs': pkgs,
+ 'deprecated': deprecated,
+ 'output': self.callback_data
+ }
+
+
+ def tearDown(self):
+ del self.portdb, self.vardb
+
+
+class TestNonDestructive(unittest.TestCase):
+ """Tests eclean.search.DistfilesSearch._non_destructive and _destructive
+ functions, with addition useage tests of fetch_restricted() and _unrestricted()
+ """
+
+ def setUp(self):
+ self.vardb = Dbapi(cp_all=[], cpv_all=CPVS,
+ props=PROPS, cp_list=[], name="FAKE VARDB")
+ self.portdb = Dbapi(cp_all=[], cpv_all=CPVS[:4],
+ props=get_props(CPVS[:4]), cp_list=[], name="FAKE PORTDB")
+ print(self.portdb)
+ # set a fetch restricted pkg
+ self.portdb._props[CPVS[0]]["RESTRICT"] = 'fetch'
+ self.callback_data = []
+ self.output = OutputSimulator(self.callback)
+ self.target_class = DistfilesSearch(self.output.einfo, self.portdb, self.vardb)
+ search.exclDictExpand = self.exclDictExpand
+ self.exclude = parseExcludeFile(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'distfiles.exclude'), self.output.einfo)
+ #print(self.callback_data)
+ #print(self.exclude)
+ self.callback_data = []
+ self.results = {}
+ self.testdata = {
+ 'non_destructive1':{
+ 'deprecated':
+ {'app-emulation/emul-linux-x86-baselibs-20100220': 'mirror://gentoo/emul-linux-x86-baselibs-20100220.tar.gz'
+ },
+ 'pkgs': {
+ 'sys-auth/consolekit-0.4.1': 'http://www.freedesktop.org/software/ConsoleKit/dist/ConsoleKit-0.4.1.tar.bz2',
+ 'sys-apps/help2man-1.37.1': 'mirror://gnu/help2man/help2man-1.37.1.tar.gz',
+ 'sys-apps/devicekit-power-014': 'http://hal.freedesktop.org/releases/DeviceKit-power-014.tar.gz',
+ 'app-emulation/emul-linux-x86-baselibs-20100220': 'mirror://gentoo/emul-linux-x86-baselibs-20100220.tar.gz',
+ 'media-libs/sdl-pango-0.1.2': 'mirror://sourceforge/sdlpango/SDL_Pango-0.1.2.tar.gz http://zarb.org/~gc/t/SDL_Pango-0.1.2-API-adds.patch'
+ },
+ 'output': [
+ ' - getting complete ebuild list',
+ ' - getting source file names for 5 ebuilds',
+ '!!! "Deprecation Warning: Installed package: app-emulation/emul-linux-x86-baselibs-20100220\n\tIs no longer in the tree or an installed overlay\n'
+ ]
+ },
+ 'non_destructive2':{
+ 'deprecated': {
+ },
+ 'pkgs': {
+ 'sys-apps/devicekit-power-014': 'http://hal.freedesktop.org/releases/DeviceKit-power-014.tar.gz',
+ 'sys-auth/consolekit-0.4.1': 'http://www.freedesktop.org/software/ConsoleKit/dist/ConsoleKit-0.4.1.tar.bz2',
+ 'media-libs/sdl-pango-0.1.2': 'mirror://sourceforge/sdlpango/SDL_Pango-0.1.2.tar.gz http://zarb.org/~gc/t/SDL_Pango-0.1.2-API-adds.patch'
+ },
+ 'output': [
+ ' - getting complete ebuild list',
+ ' - getting source file names for 3 installed ebuilds',
+ ' - getting fetch-restricted source file names for 2 remaining ebuilds'
+ ]
+ },
+ 'non_destructive3':{
+ 'deprecated':{
+ },
+ 'pkgs': {
+ 'sys-apps/devicekit-power-014': 'http://hal.freedesktop.org/releases/DeviceKit-power-014.tar.gz',
+ 'sys-auth/consolekit-0.4.1': 'http://www.freedesktop.org/software/ConsoleKit/dist/ConsoleKit-0.4.1.tar.bz2',
+ 'app-emulation/emul-linux-x86-baselibs-20100220': 'mirror://gentoo/emul-linux-x86-baselibs-20100220.tar.gz',
+ },
+ 'output': [
+ ' - getting complete ebuild list',
+ ' - getting source file names for 2 installed ebuilds',
+ ' - getting fetch-restricted source file names for 3 remaining ebuilds'
+ ]
+ },
+ 'destructive1':{
+ 'deprecated':{
+ 'app-emulation/emul-linux-x86-baselibs-20100220': 'mirror://gentoo/emul-linux-x86-baselibs-20100220.tar.gz'
+ },
+ 'pkgs': {
+ 'sys-apps/devicekit-power-014': 'http://hal.freedesktop.org/releases/DeviceKit-power-014.tar.gz',
+ 'sys-apps/help2man-1.37.1': 'mirror://gnu/help2man/help2man-1.37.1.tar.gz',
+ 'sys-auth/consolekit-0.4.1': 'http://www.freedesktop.org/software/ConsoleKit/dist/ConsoleKit-0.4.1.tar.bz2',
+ 'app-emulation/emul-linux-x86-baselibs-20100220': 'mirror://gentoo/emul-linux-x86-baselibs-20100220.tar.gz',
+ 'media-libs/sdl-pango-0.1.2': 'mirror://sourceforge/sdlpango/SDL_Pango-0.1.2.tar.gz http://zarb.org/~gc/t/SDL_Pango-0.1.2-API-adds.patch'
+ },
+ 'output': [
+ ' - processing 5 installed ebuilds', ' - processing excluded',
+ ' - (5 of 0 total) additional excluded packages to get source filenames for',
+ '!!! "Deprecation Warning: Installed package: app-emulation/emul-linux-x86-baselibs-20100220\n\tIs no longer in the tree or an installed overlay\n'
+ ]
+ },
+ 'destructive2':{
+ 'deprecated':{
+ },
+ 'pkgs': {
+ },
+ 'output': [
+ ' - processing 0 installed packages',
+ ' - processing excluded', ' - (0 of 0 total) additional excluded packages to get source filenames for'
+ ]
+ },
+ 'destructive3':{
+ 'deprecated':{
+ },
+ 'pkgs': {
+ 'app-portage/gentoolkit-0.3.0_rc8-r1': 'mirror://gentoo/gentoolkit-0.3.0_rc8.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.3.0_rc8.tar.gz',
+ 'sys-apps/devicekit-power-014': 'http://hal.freedesktop.org/releases/DeviceKit-power-014.tar.gz',
+ 'app-portage/gentoolkit-0.3.0_rc8': 'mirror://gentoo/gentoolkit-0.3.0_rc8.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.3.0_rc8.tar.gz',
+ 'app-portage/gentoolkit-0.2.4.6-r1': 'mirror://gentoo/gentoolkit-0.2.4.6.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.2.4.6.tar.gz',
+ 'app-portage/gentoolkit-0.3.0_rc7': 'mirror://gentoo/gentoolkit-0.3.0_rc7.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.3.0_rc7.tar.gz',
+ 'app-portage/gentoolkit-0.2.4.6': 'mirror://gentoo/gentoolkit-0.2.4.6.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.2.4.6.tar.gz',
+ 'app-portage/eix-0.19.2': 'mirror://sourceforge/eix/eix-0.19.2.tar.xz',
+ 'app-portage/gentoolkit-0.2.4.5': 'mirror://gentoo/gentoolkit-0.2.4.5.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.2.4.5.tar.gz',
+ 'app-portage/gentoolkit-0.3.0_rc9': 'mirror://gentoo/gentoolkit-0.3.0_rc9.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.3.0_rc9.tar.gz',
+ 'app-portage/eix-0.20.1': 'mirror://sourceforge/eix/eix-0.20.1.tar.xz',
+ 'app-portage/eix-0.20.2': 'mirror://berlios/eix/eix-0.20.2.tar.xz'
+ },
+ 'output': [
+ ' - processing excluded',
+ ' - (10 of 10 total) additional excluded packages to get source filenames for'
+ ]
+ },
+ 'destructive4':{
+ 'deprecated':{
+ },
+ 'pkgs': {
+ 'sys-auth/consolekit-0.4.1':
+ 'http://www.freedesktop.org/software/ConsoleKit/dist/ConsoleKit-0.4.1.tar.bz2',
+ 'sys-apps/devicekit-power-014':
+ 'http://hal.freedesktop.org/releases/DeviceKit-power-014.tar.gz',
+ 'media-libs/sdl-pango-0.1.2':
+ 'mirror://sourceforge/sdlpango/SDL_Pango-0.1.2.tar.gz http://zarb.org/~gc/t/SDL_Pango-0.1.2-API-adds.patch'
+ },
+ 'output': [
+ ' - processing 3 installed ebuilds',
+ ' - processing excluded',
+ ' - (3 of 0 total) additional excluded packages to get source filenames for'
+ ]
+ },
+ 'destructive5':{
+ 'deprecated':{
+ },
+ 'pkgs': {
+ 'x11-base/xorg-server-1.7.5': 'http://xorg.freedesktop.org/releases/individual/xserver/xorg-server-1.7.5.tar.bz2',
+ 'app-portage/gentoolkit-0.3.0_rc8-r1': 'mirror://gentoo/gentoolkit-0.3.0_rc8.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.3.0_rc8.tar.gz',
+ 'sys-apps/devicekit-power-014': 'http://hal.freedesktop.org/releases/DeviceKit-power-014.tar.gz',
+ 'x11-misc/util-macros-1.6.0': 'http://xorg.freedesktop.org/releases/individual/util/util-macros-1.6.0.tar.bz2',
+ 'app-portage/eix-0.19.2': 'mirror://sourceforge/eix/eix-0.19.2.tar.xz',
+ 'app-portage/gentoolkit-0.3.0_rc8': 'mirror://gentoo/gentoolkit-0.3.0_rc8.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.3.0_rc8.tar.gz',
+ 'app-portage/gentoolkit-0.2.4.6-r1': 'mirror://gentoo/gentoolkit-0.2.4.6.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.2.4.6.tar.gz',
+ 'app-portage/gentoolkit-0.3.0_rc7': 'mirror://gentoo/gentoolkit-0.3.0_rc7.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.3.0_rc7.tar.gz',
+ 'sys-auth/consolekit-0.4.1': 'http://www.freedesktop.org/software/ConsoleKit/dist/ConsoleKit-0.4.1.tar.bz2',
+ 'app-portage/gentoolkit-0.2.4.6': 'mirror://gentoo/gentoolkit-0.2.4.6.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.2.4.6.tar.gz',
+ 'media-libs/sdl-pango-0.1.2': 'mirror://sourceforge/sdlpango/SDL_Pango-0.1.2.tar.gz http://zarb.org/~gc/t/SDL_Pango-0.1.2-API-adds.patch',
+ 'x11-libs/pixman-0.16.4': 'http://xorg.freedesktop.org/releases/individual/lib/pixman-0.16.4.tar.bz2',
+ 'app-portage/gentoolkit-0.2.4.5': 'mirror://gentoo/gentoolkit-0.2.4.5.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.2.4.5.tar.gz',
+ 'app-portage/gentoolkit-0.3.0_rc9': 'mirror://gentoo/gentoolkit-0.3.0_rc9.tar.gz http://dev.gentoo.org/~fuzzyray/distfiles/gentoolkit-0.3.0_rc9.tar.gz',
+ 'app-portage/eix-0.20.1': 'mirror://sourceforge/eix/eix-0.20.1.tar.xz',
+ 'app-portage/eix-0.20.2': 'mirror://berlios/eix/eix-0.20.2.tar.xz'
+ },
+ 'output': [
+ ' - processing 6 installed ebuilds',
+ ' - processing excluded',
+ ' - (16 of 10 total) additional excluded packages to get source filenames for'
+ ]
+ }
+ }
+
+
+ def callback(self, id, data):
+ self.callback_data.append(data)
+
+
+ def exclDictExpand(self, exclude):
+ #print("Using Fake Testing exclDictExpand()")
+ return [
+ #'app-portage/layman',
+ 'app-portage/eix',
+ 'app-portage/gentoolkit',
+ #app-portage/portage-utils',
+ ]
+
+
+ def test_non_destructive(self):
+ self.results = {}
+ pkgs, deprecated = self.target_class._non_destructive(destructive=False,
+ fetch_restricted=False, pkgs_=None)
+ self.record_results('non_destructive1', pkgs, deprecated)
+
+ pkgs = None
+ deprecated = None
+ self.callback_data = []
+ self.vardb._cpv_all=CPVS[:3]
+ self.vardb._props=get_props(CPVS[:3])
+ self.portdb._cpv_all=CPVS[:]
+ self.portdb._props=get_props(CPVS)
+ self.target_class.installed_cpvs = None
+ pkgs, deprecated = self.target_class._non_destructive(destructive=True,
+ fetch_restricted=True, pkgs_=None)
+ self.record_results('non_destructive2', pkgs, deprecated)
+
+ pkgs = None
+ deprecated = None
+ self.callback_data = []
+ self.vardb._cpv_all=CPVS[:2]
+ self.vardb._props=get_props(CPVS[:2])
+ self.portdb._cpv_all=CPVS[:]
+ self.portdb._props=get_props(CPVS)
+ # set a fetch restricted pkg
+ self.portdb._props[CPVS[4]]["RESTRICT"] = 'fetch'
+ pkgs = {'sys-apps/devicekit-power-014': 'http://hal.freedesktop.org/releases/DeviceKit-power-014.tar.gz'}
+ pkgs, deprecated = self.target_class._non_destructive(destructive=True,
+ fetch_restricted=True, pkgs_=pkgs)
+ self.record_results('non_destructive3', pkgs, deprecated)
+ self.check_results("test_non_destructive")
+
+
+ def check_results(self, test_name):
+ print("\nChecking results for %s,............" %test_name)
+ for key in sorted(self.results):
+ testdata = self.testdata[key]
+ results = self.results[key]
+ for item in sorted(testdata):
+ if sorted(results[item]) == sorted(testdata[item]):
+ test = "OK"
+ else:
+ test = "FAILED"
+ print("comparing %s, %s..." %(key, item), test)
+ if test == "FAILED":
+ print("", sorted(results[item]), "\n", sorted(testdata[item]))
+ self.failUnlessEqual(sorted(testdata[item]), sorted(results[item]),
+ "\n%s: %s, %s data does not match\n"
+ %(test_name, key, item) + \
+ "result=" + str(results[item]) + "\ntestdata=" + str(testdata[item])
+ )
+
+
+ def record_results(self, test, pkgs, deprecated):
+ self.results[test] = {'pkgs': pkgs,
+ 'deprecated': deprecated,
+ 'output': self.callback_data
+ }
+
+ def test_destructive(self):
+ self.results = {}
+ pkgs, deprecated = self.target_class._destructive(package_names=False,
+ exclude={}, pkgs_=None, installed_included=False )
+ self.record_results('destructive1', pkgs, deprecated)
+
+ self.callback_data = []
+ self.vardb._cpv_all=CPVS[:3]
+ self.vardb._props=get_props(CPVS[:3])
+ self.portdb._cpv_all=CPVS[:]
+ self.portdb._props=get_props(CPVS)
+ pkgs, deprecated = self.target_class._destructive(package_names=True,
+ exclude={}, pkgs_=None, installed_included=False )
+ self.record_results('destructive2', pkgs, deprecated)
+
+ self.callback_data = []
+ cpvs = CPVS[2:4]
+ cpvs.extend(CPVS3)
+ self.vardb._cpv_all=sorted(cpvs)
+ self.vardb._props= PROPS.update(get_props(CPVS3))
+ self.portdb._cpv_all=sorted(CPVS + CPVS2)
+ self.portdb._props=get_props(CPVS+CPVS2)
+ # set a fetch restricted pkg
+ self.portdb._props[CPVS[4]]["RESTRICT"] = 'fetch'
+ pkgs = {'sys-apps/devicekit-power-014': 'http://hal.freedesktop.org/releases/DeviceKit-power-014.tar.gz'}
+ pkgs, deprecated = self.target_class._destructive(package_names=True,
+ exclude={}, pkgs_=pkgs, installed_included=True )
+ self.record_results('destructive3', pkgs, deprecated)
+
+ self.callback_data = []
+ self.vardb._cpv_all=CPVS[:3]
+ self.vardb._props=get_props(CPVS[:3])
+ self.portdb._cpv_all=CPVS[:]
+ self.portdb._props=get_props(CPVS)
+ pkgs, deprecated = self.target_class._destructive(package_names=False,
+ exclude=self.exclude, pkgs_=None, installed_included=False )
+ self.record_results('destructive4', pkgs, deprecated)
+ self.check_results("test_destructive")
+
+ self.callback_data = []
+ self.vardb._cpv_all=CPVS[:3]
+ self.vardb._cpv_all.extend(CPVS3)
+ self.vardb._props=get_props(self.vardb._cpv_all)
+ self.portdb._cpv_all=CPVS2
+ #self.portdb._cpv_all.extend(CPVS2)
+ self.portdb._props=PROPS
+ pkgs, deprecated = self.target_class._destructive(package_names=False,
+ exclude=self.exclude, pkgs_=None, installed_included=False )
+ self.record_results('destructive5', pkgs, deprecated)
+ self.check_results("test_destructive")
+
+
+ def tearDown(self):
+ del self.portdb, self.vardb
+
+
+class TestRemoveProtected(unittest.TestCase):
+ """tests the eclean.search.DistfilesSearch._remove_protected()
+ """
+
+ def setUp(self):
+ self.target_class = DistfilesSearch(lambda x: None)
+ self.results = {'layman-1.2.5.tar.gz': '/path/to/some/where/layman-1.2.5.tar.gz'}
+
+ def test_remove_protected(self):
+ results = self.target_class._remove_protected(PKGS, CLEAN_ME)
+ self.failUnlessEqual(results, self.results,
+ "\ntest_remove_protected: data does not match\nresult=" +\
+ str(results) + "\ntestdata=" + str(self.results))
+
+
+def test_main():
+
+ # Run tests
+ test_support.run_unittest(TestCheckLimits('test_check_limits'))
+ test_support.run_unittest( TestFetchRestricted('test__fetch_restricted'))
+ test_support.run_unittest( TestFetchRestricted('test_unrestricted'))
+ test_support.run_unittest( TestNonDestructive('test_non_destructive'))
+ test_support.run_unittest( TestNonDestructive('test_destructive'))
+ test_support.run_unittest( TestRemoveProtected('test_remove_protected'))
+
+
+if __name__ == '__main__':
+ test_main()
diff --git a/pym/gentoolkit/test/equery/__init__.py b/pym/gentoolkit/test/equery/__init__.py
new file mode 100644
index 0000000..94423e9
--- /dev/null
+++ b/pym/gentoolkit/test/equery/__init__.py
@@ -0,0 +1,6 @@
+#!/usr/bin/python
+# Copyright 2009 Gentoo Foundation
+#
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
diff --git a/pym/gentoolkit/tests/equery/test_init.py b/pym/gentoolkit/test/equery/test_init.py
index 9756aba..520b97d 100644
--- a/pym/gentoolkit/tests/equery/test_init.py
+++ b/pym/gentoolkit/test/equery/test_init.py
@@ -1,5 +1,8 @@
import unittest
-from test import test_support
+try:
+ from test import test_support
+except ImportError:
+ from test import support as test_support
from gentoolkit import equery
@@ -14,6 +17,7 @@ class TestEqueryInit(unittest.TestCase):
def test_expand_module_name(self):
# Test that module names are properly expanded
name_map = {
+ 'a': 'has',
'b': 'belongs',
'c': 'changes',
'k': 'check',
@@ -21,12 +25,14 @@ class TestEqueryInit(unittest.TestCase):
'g': 'depgraph',
'f': 'files',
'h': 'hasuse',
+ 'y': 'keywords',
'l': 'list_',
'm': 'meta',
's': 'size',
'u': 'uses',
'w': 'which'
}
+ self.failUnlessEqual(equery.NAME_MAP, name_map)
for short_name, long_name in zip(name_map, name_map.values()):
self.failUnlessEqual(equery.expand_module_name(short_name),
long_name)
@@ -36,8 +42,10 @@ class TestEqueryInit(unittest.TestCase):
for key in unused_keys:
self.failUnlessRaises(KeyError, equery.expand_module_name, key)
+
def test_main():
test_support.run_unittest(TestEqueryInit)
+
if __name__ == '__main__':
test_main()
diff --git a/pym/gentoolkit/test/test_atom.py b/pym/gentoolkit/test/test_atom.py
new file mode 100644
index 0000000..b498545
--- /dev/null
+++ b/pym/gentoolkit/test/test_atom.py
@@ -0,0 +1,153 @@
+# Copyright(c) 2009, Gentoo Foundation
+# Copyright: 2006-2008 Brian Harring <ferringb@gmail.com>
+#
+# License: GPL2/BSD
+
+# $Header$
+
+import unittest
+try:
+ from test import test_support
+except ImportError:
+ from test import support as test_support
+
+from gentoolkit.atom import *
+from gentoolkit.test import cmp
+
+"""Atom test suite (verbatim) from pkgcore."""
+
+class TestGentoolkitAtom(unittest.TestCase):
+
+ def assertEqual2(self, o1, o2):
+ # logic bugs hidden behind short circuiting comparisons for metadata
+ # is why we test the comparison *both* ways.
+ self.assertEqual(o1, o2)
+ c = cmp(o1, o2)
+ self.assertEqual(c, 0,
+ msg="checking cmp for %r, %r, aren't equal: got %i" % (o1, o2, c))
+ self.assertEqual(o2, o1)
+ c = cmp(o2, o1)
+ self.assertEqual(c, 0,
+ msg="checking cmp for %r, %r,aren't equal: got %i" % (o2, o1, c))
+
+ def assertNotEqual2(self, o1, o2):
+ # is why we test the comparison *both* ways.
+ self.assertNotEqual(o1, o2)
+ c = cmp(o1, o2)
+ self.assertNotEqual(c, 0,
+ msg="checking cmp for %r, %r, not supposed to be equal, got %i"
+ % (o1, o2, c))
+ self.assertNotEqual(o2, o1)
+ c = cmp(o2, o1)
+ self.assertNotEqual(c, 0,
+ msg="checking cmp for %r, %r, not supposed to be equal, got %i"
+ % (o2, o1, c))
+
+ def test_comparison(self):
+ self.assertEqual2(Atom('cat/pkg'), Atom('cat/pkg'))
+ self.assertNotEqual2(Atom('cat/pkg'), Atom('cat/pkgb'))
+ self.assertNotEqual2(Atom('cata/pkg'), Atom('cat/pkg'))
+ self.assertNotEqual2(Atom('cat/pkg'), Atom('!cat/pkg'))
+ self.assertEqual2(Atom('!cat/pkg'), Atom('!cat/pkg'))
+ self.assertNotEqual2(Atom('=cat/pkg-0.1:0'),
+ Atom('=cat/pkg-0.1'))
+ self.assertNotEqual2(Atom('=cat/pkg-1[foon]'),
+ Atom('=cat/pkg-1'))
+ self.assertEqual2(Atom('=cat/pkg-0'), Atom('=cat/pkg-0'))
+ self.assertNotEqual2(Atom('<cat/pkg-2'), Atom('>cat/pkg-2'))
+ self.assertNotEqual2(Atom('=cat/pkg-2*'), Atom('=cat/pkg-2'))
+ # Portage Atom doesn't have 'negate_version' capability
+ #self.assertNotEqual2(Atom('=cat/pkg-2', True), Atom('=cat/pkg-2'))
+
+ # use...
+ self.assertNotEqual2(Atom('cat/pkg[foo]'), Atom('cat/pkg'))
+ self.assertNotEqual2(Atom('cat/pkg[foo]'),
+ Atom('cat/pkg[-foo]'))
+ self.assertEqual2(Atom('cat/pkg[foo,-bar]'),
+ Atom('cat/pkg[-bar,foo]'))
+
+ # repoid not supported by Portage Atom yet
+ ## repoid
+ #self.assertEqual2(Atom('cat/pkg::a'), Atom('cat/pkg::a'))
+ #self.assertNotEqual2(Atom('cat/pkg::a'), Atom('cat/pkg::b'))
+ #self.assertNotEqual2(Atom('cat/pkg::a'), Atom('cat/pkg'))
+
+ # slots.
+ self.assertNotEqual2(Atom('cat/pkg:1'), Atom('cat/pkg'))
+ self.assertEqual2(Atom('cat/pkg:2'), Atom('cat/pkg:2'))
+ # http://dev.gentoo.org/~tanderson/pms/eapi-2-approved/pms.html#x1-190002.1.2
+ self.assertEqual2(Atom('cat/pkg:AZaz09+_.-'), Atom('cat/pkg:AZaz09+_.-'))
+ for lesser, greater in (('0.1', '1'), ('1', '1-r1'), ('1.1', '1.2')):
+ self.assertTrue(Atom('=d/b-%s' % lesser) <
+ Atom('=d/b-%s' % greater),
+ msg="d/b-%s < d/b-%s" % (lesser, greater))
+ self.assertFalse(Atom('=d/b-%s' % lesser) >
+ Atom('=d/b-%s' % greater),
+ msg="!: d/b-%s < d/b-%s" % (lesser, greater))
+ self.assertTrue(Atom('=d/b-%s' % greater) >
+ Atom('=d/b-%s' % lesser),
+ msg="d/b-%s > d/b-%s" % (greater, lesser))
+ self.assertFalse(Atom('=d/b-%s' % greater) <
+ Atom('=d/b-%s' % lesser),
+ msg="!: d/b-%s > d/b-%s" % (greater, lesser))
+
+ #self.assertTrue(Atom("!!=d/b-1", eapi=2) > Atom("!=d/b-1"))
+ self.assertTrue(Atom("!=d/b-1") < Atom("!!=d/b-1"))
+ self.assertEqual(Atom("!=d/b-1"), Atom("!=d/b-1"))
+
+ def test_intersects(self):
+ for this, that, result in [
+ ('cat/pkg', 'pkg/cat', False),
+ ('cat/pkg', 'cat/pkg', True),
+ ('cat/pkg:1', 'cat/pkg:1', True),
+ ('cat/pkg:1', 'cat/pkg:2', False),
+ ('cat/pkg:1', 'cat/pkg[foo]', True),
+ ('cat/pkg[foo]', 'cat/pkg[-bar]', True),
+ ('cat/pkg[foo]', 'cat/pkg[-foo]', False),
+ ('>cat/pkg-3', '>cat/pkg-1', True),
+ ('>cat/pkg-3', '<cat/pkg-3', False),
+ ('>=cat/pkg-3', '<cat/pkg-3', False),
+ ('>cat/pkg-2', '=cat/pkg-2*', True),
+ # Portage vercmp disagrees with this one:
+ #('<cat/pkg-2_alpha1', '=cat/pkg-2*', True),
+ ('=cat/pkg-2', '=cat/pkg-2', True),
+ ('=cat/pkg-3', '=cat/pkg-2', False),
+ ('=cat/pkg-2', '>cat/pkg-2', False),
+ ('=cat/pkg-2', '>=cat/pkg-2', True),
+ ('~cat/pkg-2', '~cat/pkg-2', True),
+ ('~cat/pkg-2', '~cat/pkg-2.1', False),
+ ('=cat/pkg-2*', '=cat/pkg-2.3*', True),
+ ('>cat/pkg-2.4', '=cat/pkg-2*', True),
+ ('<cat/pkg-2.4', '=cat/pkg-2*', True),
+ ('<cat/pkg-1', '=cat/pkg-2*', False),
+ ('~cat/pkg-2', '>cat/pkg-2-r1', True),
+ ('~cat/pkg-2', '<=cat/pkg-2', True),
+ ('=cat/pkg-2-r2*', '<=cat/pkg-2-r20', True),
+ ('=cat/pkg-2-r2*', '<cat/pkg-2-r20', True),
+ ('=cat/pkg-2-r2*', '<=cat/pkg-2-r2', True),
+ ('~cat/pkg-2', '<cat/pkg-2', False),
+ ('=cat/pkg-1-r10*', '~cat/pkg-1', True),
+ ('=cat/pkg-1-r1*', '<cat/pkg-1-r1', False),
+ ('=cat/pkg-1*', '>cat/pkg-2', False),
+ ('>=cat/pkg-8.4', '=cat/pkg-8.3.4*', False),
+ # Repos not yet supported by Portage
+ #('cat/pkg::gentoo', 'cat/pkg', True),
+ #('cat/pkg::gentoo', 'cat/pkg::foo', False),
+ ('=sys-devel/gcc-4.1.1-r3', '=sys-devel/gcc-3.3*', False),
+ ('=sys-libs/db-4*', '~sys-libs/db-4.3.29', True),
+ ]:
+ this_atom = Atom(this)
+ that_atom = Atom(that)
+ self.assertEqual(
+ result, this_atom.intersects(that_atom),
+ '%s intersecting %s should be %s' % (this, that, result))
+ self.assertEqual(
+ result, that_atom.intersects(this_atom),
+ '%s intersecting %s should be %s' % (that, this, result))
+
+
+def test_main():
+ test_support.run_unittest(TestGentoolkitAtom)
+
+if __name__ == '__main__':
+ test_main()
diff --git a/pym/gentoolkit/test/test_cpv.py b/pym/gentoolkit/test/test_cpv.py
new file mode 100644
index 0000000..3749404
--- /dev/null
+++ b/pym/gentoolkit/test/test_cpv.py
@@ -0,0 +1,142 @@
+#!/usr/bin/python
+#
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+import unittest
+try:
+ from test import test_support
+except ImportError:
+ from test import support as test_support
+
+from gentoolkit.cpv import *
+from gentoolkit.test import cmp
+
+class TestGentoolkitCPV(unittest.TestCase):
+
+ def assertEqual2(self, o1, o2):
+ # logic bugs hidden behind short circuiting comparisons for metadata
+ # is why we test the comparison *both* ways.
+ self.assertEqual(o1, o2)
+ c = cmp(o1, o2)
+ self.assertEqual(c, 0,
+ msg="checking cmp for %r, %r, aren't equal: got %i" % (o1, o2, c))
+ self.assertEqual(o2, o1)
+ c = cmp(o2, o1)
+ self.assertEqual(c, 0,
+ msg="checking cmp for %r, %r,aren't equal: got %i" % (o2, o1, c))
+
+ def assertNotEqual2(self, o1, o2):
+ # is why we test the comparison *both* ways.
+ self.assertNotEqual(o1, o2)
+ c = cmp(o1, o2)
+ self.assertNotEqual(c, 0,
+ msg="checking cmp for %r, %r, not supposed to be equal, got %i"
+ % (o1, o2, c))
+ self.assertNotEqual(o2, o1)
+ c = cmp(o2, o1)
+ self.assertNotEqual(c, 0,
+ msg="checking cmp for %r, %r, not supposed to be equal, got %i"
+ % (o2, o1, c))
+
+ def test_comparison(self):
+ self.assertEqual2(CPV('pkg'), CPV('pkg'))
+ self.assertNotEqual2(CPV('pkg'), CPV('pkg1'))
+ self.assertEqual2(CPV('cat/pkg'), CPV('cat/pkg'))
+ self.assertNotEqual2(CPV('cat/pkg'), CPV('cat/pkgb'))
+ self.assertNotEqual2(CPV('cata/pkg'), CPV('cat/pkg'))
+ self.assertEqual2(CPV('cat/pkg-0.1'), CPV('cat/pkg-0.1'))
+ self.assertNotEqual2(CPV('cat/pkg-1.0'), CPV('cat/pkg-1'))
+ self.assertEqual2(CPV('cat/pkg-0'), CPV('cat/pkg-0'))
+ self.assertEqual2(CPV('cat/pkg-1-r1'), CPV('cat/pkg-1-r1'))
+ self.assertNotEqual2(CPV('cat/pkg-2-r1'), CPV('cat/pkg-2-r10'))
+ self.assertEqual2(CPV('cat/pkg-1_rc2'), CPV('cat/pkg-1_rc2'))
+ self.assertNotEqual2(CPV('cat/pkg-2_rc2-r1'), CPV('cat/pkg-2_rc1-r1'))
+
+ def test_compare_strs(self):
+ # Test ordering of package strings, Portage has test for vercmp,
+ # so just do the rest
+ version_tests = [
+ # different categories
+ ('sys-apps/portage-2.1.6.8', 'sys-auth/pambase-20080318'),
+ # different package names
+ ('sys-apps/pkgcore-0.4.7.15-r1', 'sys-apps/portage-2.1.6.8'),
+ # different package versions
+ ('sys-apps/portage-2.1.6.8', 'sys-apps/portage-2.2_rc25')
+ ]
+ # Check less than
+ for vt in version_tests:
+ self.failUnless(compare_strs(vt[0], vt[1]) == -1)
+ # Check greater than
+ for vt in version_tests:
+ self.failUnless(compare_strs(vt[1], vt[0]) == 1)
+ # Check equal
+ vt = ('sys-auth/pambase-20080318', 'sys-auth/pambase-20080318')
+ self.failUnless(compare_strs(vt[0], vt[1]) == 0)
+
+ def test_chunk_splitting(self):
+ all_tests = [
+ # simple
+ ('sys-apps/portage-2.2', {
+ 'category': 'sys-apps',
+ 'name': 'portage',
+ 'cp': 'sys-apps/portage',
+ 'version': '2.2',
+ 'revision': '',
+ 'fullversion': '2.2'
+ }),
+ # with rc
+ ('sys-apps/portage-2.2_rc10', {
+ 'category': 'sys-apps',
+ 'name': 'portage',
+ 'cp': 'sys-apps/portage',
+ 'version': '2.2_rc10',
+ 'revision': '',
+ 'fullversion': '2.2_rc10'
+ }),
+ # with revision
+ ('sys-apps/portage-2.2_rc10-r1', {
+ 'category': 'sys-apps',
+ 'name': 'portage',
+ 'cp': 'sys-apps/portage',
+ 'version': '2.2_rc10',
+ 'revision': 'r1',
+ 'fullversion': '2.2_rc10-r1'
+ }),
+ # with dash (-) in name (Bug #316961)
+ ('c-portage', {
+ 'category': '',
+ 'name': 'c-portage',
+ 'cp': 'c-portage',
+ 'version': '',
+ 'revision': '',
+ 'fullversion': ''
+ }),
+ # with dash (-) in name (Bug #316961)
+ ('sys-apps/c-portage-2.2_rc10-r1', {
+ 'category': 'sys-apps',
+ 'name': 'c-portage',
+ 'cp': 'sys-apps/c-portage',
+ 'version': '2.2_rc10',
+ 'revision': 'r1',
+ 'fullversion': '2.2_rc10-r1'
+ }),
+ ]
+
+ for test in all_tests:
+ cpv = CPV(test[0])
+ keys = ('category', 'name', 'cp', 'version', 'revision', 'fullversion')
+ for k in keys:
+ self.failUnlessEqual(
+ getattr(cpv, k), test[1][k]
+ )
+
+
+def test_main():
+ test_support.run_unittest(TestGentoolkitCPV)
+
+if __name__ == '__main__':
+ test_main()
diff --git a/pym/gentoolkit/test/test_helpers.py b/pym/gentoolkit/test/test_helpers.py
new file mode 100644
index 0000000..5bafaae
--- /dev/null
+++ b/pym/gentoolkit/test/test_helpers.py
@@ -0,0 +1,122 @@
+import os
+import unittest
+import warnings
+from tempfile import NamedTemporaryFile, mktemp
+try:
+ from test import test_support
+except ImportError:
+ from test import support as test_support
+
+from gentoolkit import helpers
+
+
+class TestChangeLog(unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+ def tearDown(self):
+ pass
+
+ def test_split_changelog(self):
+ changelog = """
+*portage-2.1.6.2 (20 Dec 2008)
+
+ 20 Dec 2008; Zac Medico <zmedico@gentoo.org> +portage-2.1.6.2.ebuild:
+ 2.1.6.2 bump. This fixes bug #251591 (repoman inherit.autotools false
+ positives) and bug #251616 (performance issue in build log search regex
+ makes emerge appear to hang). Bug #216231 tracks all bugs fixed since
+ 2.1.4.x.
+
+ 20 Dec 2008; Zac Medico <zmedico@gentoo.org> -portage-2.1.6.ebuild,
+ -portage-2.1.6.1.ebuild, -portage-2.2_rc17.ebuild:
+ Remove old versions.
+
+
+*portage-2.1.6.1 (12 Dec 2008)
+
+ 12 Dec 2008; Zac Medico <zmedico@gentoo.org> +portage-2.1.6.1.ebuild:
+ 2.1.6.1 bump. This fixes bug #250148 (emerge hangs with selinux if ebuild
+ spawns a daemon), bug #250166 (trigger download when generating manifest
+ if file size differs from existing entry), and bug #250212 (new repoman
+ upstream.workaround category for emake -j1 warnings). Bug #216231 tracks
+ all bugs fixed since 2.1.4.x.
+
+
+*portage-2.1.6 (07 Dec 2008)
+
+ 07 Dec 2008; Zac Medico <zmedico@gentoo.org> +portage-2.1.6.ebuild:
+ 2.1.6 final release. This fixes bug #249586. Bug #216231 tracks all bugs
+ fixed since 2.1.4.x.
+
+ 07 Dec 2008; Zac Medico <zmedico@gentoo.org> -portage-2.1.6_rc1.ebuild,
+ -portage-2.1.6_rc2.ebuild, -portage-2.1.6_rc3.ebuild,
+ -portage-2.2_rc16.ebuild:
+ Remove old versions.
+ """
+
+class TestFileOwner(unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+ def tearDown(self):
+ pass
+
+ def test_expand_abspaths(self):
+ expand_abspaths = helpers.FileOwner.expand_abspaths
+
+ initial_file_list = ['foo0', '/foo1', '~/foo2', './foo3']
+ # This function should only effect foo3, and not ordering:
+
+ final_file_list = [
+ 'foo0',
+ '/foo1',
+ '~/foo2',
+ os.path.join(os.getcwd(), os.path.normpath(initial_file_list[3]))
+ ]
+
+ self.failUnlessEqual(expand_abspaths(initial_file_list), final_file_list)
+
+ def test_extend_realpaths(self):
+ extend_realpaths = helpers.FileOwner.extend_realpaths
+
+ # Test that symlinks's realpaths are extended
+ f1 = NamedTemporaryFile(prefix='equeryunittest')
+ f2 = NamedTemporaryFile(prefix='equeryunittest')
+ f3 = NamedTemporaryFile(prefix='equeryunittest')
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+ sym1 = mktemp()
+ os.symlink(f1.name, sym1)
+ sym2 = mktemp()
+ os.symlink(f3.name, sym2)
+ # We've created 3 files and 2 symlinks for testing. We're going to pass
+ # in only the first two files and both symlinks. sym1 points to f1.
+ # Since f1 is already in the list, sym1's realpath should not be added.
+ # sym2 points to f3, but f3's not in our list, so sym2's realpath
+ # should be added to the list.
+ p = [f1.name, f2.name, sym1, sym2]
+ p_xr = extend_realpaths(p)
+
+ self.failUnlessEqual(p_xr[0], f1.name)
+ self.failUnlessEqual(p_xr[1], f2.name)
+ self.failUnlessEqual(p_xr[2], sym1)
+ self.failUnlessEqual(p_xr[3], sym2)
+ self.failUnlessEqual(p_xr[4], f3.name)
+
+ # Clean up
+ os.unlink(sym1)
+ os.unlink(sym2)
+
+ # Make sure we raise an exception if we don't get acceptable input
+ self.failUnlessRaises(AttributeError, extend_realpaths, 'str')
+ self.failUnlessRaises(AttributeError, extend_realpaths, set())
+
+
+def test_main():
+ test_support.run_unittest(TestGentoolkitHelpers2)
+
+
+if __name__ == '__main__':
+ test_main()
diff --git a/pym/gentoolkit/test/test_keyword.py b/pym/gentoolkit/test/test_keyword.py
new file mode 100644
index 0000000..3e72e5f
--- /dev/null
+++ b/pym/gentoolkit/test/test_keyword.py
@@ -0,0 +1,57 @@
+import sys
+import unittest
+import warnings
+from tempfile import NamedTemporaryFile
+try:
+ from test import test_support
+except ImportError:
+ from test import support as test_support
+
+from gentoolkit import keyword
+from gentoolkit.test import cmp
+
+class TestGentoolkitKeyword(unittest.TestCase):
+
+ def test_compare_strs(self):
+ compare_strs = keyword.compare_strs
+
+ # Test ordering of keyword strings
+ version_tests = [
+ # different archs
+ ('amd64', 'x86'),
+ # stable vs. unstable
+ ('amd64-linux', '~amd64-linux'),
+ # different OSes
+ ('~x86-linux', '~x86-solaris'),
+ # OS vs. no OS
+ ('x86', '~amd64-linux')
+ ]
+ # Check less than
+ for vt in version_tests:
+ self.failUnless(compare_strs(vt[0], vt[1]) == -1)
+ # Check greater than
+ for vt in version_tests:
+ self.failUnless(compare_strs(vt[1], vt[0]) == 1)
+ # Check equal
+ vt = ('~amd64-linux', '~amd64-linux')
+ self.failUnless(compare_strs(vt[0], vt[1]) == 0)
+
+ kwds_presort = [
+ '~amd64', '~amd64-linux', '~ppc', '~ppc-macos', '~x86',
+ '~x86-linux', '~x86-macos', '~x86-solaris'
+ ]
+ kwds_postsort = [
+ '~amd64', '~ppc', '~x86', '~amd64-linux', '~x86-linux',
+ '~ppc-macos', '~x86-macos', '~x86-solaris'
+ ]
+ if sys.hexversion < 0x3000000:
+ self.failUnlessEqual(sorted(kwds_presort, cmp=compare_strs), kwds_postsort)
+ self.failUnlessEqual(sorted(kwds_presort, key = keyword.Keyword), kwds_postsort)
+
+
+def test_main():
+ test_support.run_unittest(TestGentoolkitHelpers2)
+
+
+if __name__ == '__main__':
+ test_main()
diff --git a/pym/gentoolkit/test/test_query.py b/pym/gentoolkit/test/test_query.py
new file mode 100644
index 0000000..c020be1
--- /dev/null
+++ b/pym/gentoolkit/test/test_query.py
@@ -0,0 +1,109 @@
+import unittest
+import warnings
+from tempfile import NamedTemporaryFile, mktemp
+try:
+ from test import test_support
+except ImportError:
+ from test import support as test_support
+
+from gentoolkit import query
+from gentoolkit import errors
+
+
+class TestQuery(unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+ def tearDown(self):
+ pass
+
+ def test_init(self):
+ # valid queries must have at least one ascii letter or '*'
+ invalid_queries = [
+ '',
+ '1',
+ '/',
+ '-1',
+ '1/1',
+ ]
+ for q in invalid_queries:
+ self.failUnlessRaises(errors.GentoolkitInvalidPackage,
+ query.Query, q
+ )
+
+ q1 = query.Query('gentoolkit')
+ q1_tests = [
+ (q1.query, 'gentoolkit'),
+ (q1.is_regex, False),
+ (q1.repo_filter, None),
+ (q1.query_type, "simple")
+ ]
+ for t in q1_tests:
+ self.failUnlessEqual(t[0], t[1])
+
+ q2 = query.Query('gentoolkit-.*', is_regex=True)
+ q2_tests = [
+ (q2.query, 'gentoolkit-.*'),
+ (q2.is_regex, True),
+ (q2.repo_filter, None),
+ (q2.query_type, "complex")
+ ]
+ for t in q2_tests:
+ self.failUnlessEqual(t[0], t[1])
+
+ q3 = query.Query('*::gentoo')
+ q3_tests = [
+ (q3.query, '*'),
+ (q3.is_regex, False),
+ (q3.repo_filter, 'gentoo'),
+ (q3.query_type, "complex")
+ ]
+ for t in q3_tests:
+ self.failUnlessEqual(t[0], t[1])
+
+ q4 = query.Query('gcc:4.3')
+ q4_tests = [
+ (q4.query, 'gcc:4.3'),
+ (q4.is_regex, False),
+ (q4.repo_filter, None),
+ (q4.query_type, "simple")
+ ]
+ for t in q4_tests:
+ self.failUnlessEqual(t[0], t[1])
+
+ q5 = query.Query('@system')
+ q5_tests = [
+ (q5.query, '@system'),
+ (q5.is_regex, False),
+ (q5.repo_filter, None),
+ (q5.query_type, "set")
+ ]
+ for t in q5_tests:
+ self.failUnlessEqual(t[0], t[1])
+
+ def test_uses_globbing(self):
+ globbing_tests = [
+ ('sys-apps/portage-2.1.6.13', False),
+ ('>=sys-apps/portage-2.1.6.13', False),
+ ('<=sys-apps/portage-2.1.6.13', False),
+ ('~sys-apps/portage-2.1.6.13', False),
+ ('=sys-apps/portage-2*', False),
+ ('sys-*/*-2.1.6.13', True),
+ ('sys-app?/portage-2.1.6.13', True),
+ ('sys-apps/[bp]ortage-2.1.6.13', True),
+ ('sys-apps/[!p]ortage*', True)
+ ]
+
+ for gt in globbing_tests:
+ self.failUnless(
+ query.Query(gt[0]).uses_globbing() == gt[1]
+ )
+
+
+def test_main():
+ test_support.run_unittest(TestGentoolkitHelpers2)
+
+
+if __name__ == '__main__':
+ test_main()
diff --git a/pym/gentoolkit/test/test_syntax.py b/pym/gentoolkit/test/test_syntax.py
new file mode 100644
index 0000000..325034a
--- /dev/null
+++ b/pym/gentoolkit/test/test_syntax.py
@@ -0,0 +1,34 @@
+import unittest
+import py_compile
+
+import os
+osp = os.path
+
+"""Does a basic syntax check by compiling all modules. From Portage."""
+
+pym_dirs = os.walk(osp.dirname(osp.dirname(osp.dirname(__file__))))
+blacklist_dirs = frozenset(('.svn', 'test'))
+
+class TestForSyntaxErrors(unittest.TestCase):
+
+ def test_compileability(self):
+ compileables = []
+ for thisdir, subdirs, files in pym_dirs:
+ if os.path.basename(thisdir) in blacklist_dirs:
+ continue
+ compileables.extend([
+ osp.join(thisdir, f)
+ for f in files
+ if osp.splitext(f)[1] == '.py'
+ ])
+
+ for c in compileables:
+ py_compile.compile(c, doraise=True)
+
+
+def test_main():
+ test_support.run_unittest(TestGentoolkitHelpers2)
+
+
+if __name__ == '__main__':
+ test_main()
diff --git a/pym/gentoolkit/tests/test_helpers2.py b/pym/gentoolkit/tests/test_helpers2.py
deleted file mode 100644
index 615cfa1..0000000
--- a/pym/gentoolkit/tests/test_helpers2.py
+++ /dev/null
@@ -1,39 +0,0 @@
-import unittest
-from test import test_support
-
-from gentoolkit import helpers2
-
-class TestGentoolkitHelpers2(unittest.TestCase):
-
- def test_compare_package_strings(self):
- # Test ordering of package strings, Portage has test for vercmp,
- # so just do the rest
- version_tests = [
- # different categories
- ('sys-apps/portage-2.1.6.8', 'sys-auth/pambase-20080318'),
- # different package names
- ('sys-apps/pkgcore-0.4.7.15-r1', 'sys-apps/portage-2.1.6.8'),
- # different package versions
- ('sys-apps/portage-2.1.6.8', 'sys-apps/portage-2.2_rc25')
- ]
- # Check less than
- for vt in version_tests:
- self.failUnless(
- helpers2.compare_package_strings(vt[0], vt[1]) == -1
- )
- # Check greater than
- for vt in version_tests:
- self.failUnless(
- helpers2.compare_package_strings(vt[1], vt[0]) == 1
- )
- # Check equal
- vt = ('sys-auth/pambase-20080318', 'sys-auth/pambase-20080318')
- self.failUnless(
- helpers2.compare_package_strings(vt[0], vt[1]) == 0
- )
-
-def test_main():
- test_support.run_unittest(TestGentoolkitHelpers2)
-
-if __name__ == '__main__':
- test_main()
diff --git a/pym/gentoolkit/tests/test_template.py b/pym/gentoolkit/tests/test_template.py
deleted file mode 100644
index 84e8432..0000000
--- a/pym/gentoolkit/tests/test_template.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import unittest
-from test import test_support
-
-class MyTestCase1(unittest.TestCase):
-
- # Only use setUp() and tearDown() if necessary
-
- def setUp(self):
- ... code to execute in preparation for tests ...
-
- def tearDown(self):
- ... code to execute to clean up after tests ...
-
- def test_feature_one(self):
- # Test feature one.
- ... testing code ...
-
- def test_feature_two(self):
- # Test feature two.
- ... testing code ...
-
- ... more test methods ...
-
-class MyTestCase2(unittest.TestCase):
- ... same structure as MyTestCase1 ...
-
-... more test classes ...
-
-def test_main():
- test_support.run_unittest(
- MyTestCase1,
- MyTestCase2,
- ... list other tests ...
- )
-
-if __name__ == '__main__':
- test_main()
-
diff --git a/pym/gentoolkit/textwrap_.py b/pym/gentoolkit/textwrap_.py
index 6851402..845ae9d 100644
--- a/pym/gentoolkit/textwrap_.py
+++ b/pym/gentoolkit/textwrap_.py
@@ -1,5 +1,5 @@
"""This modification of textwrap allows it to wrap ANSI colorized text as if
-it weren't colorized. It also uses a much simpler word splitting regex to
+it weren't colorized. It also uses a much simpler word splitting regex to
prevent the splitting of ANSI colors as well as package names and versions."""
import re
@@ -13,81 +13,81 @@ class TextWrapper(textwrap.TextWrapper):
Split the text to wrap into indivisible chunks.
"""
- # Only split on whitespace to avoid mangling ANSI escape codes or
+ # Only split on whitespace to avoid mangling ANSI escape codes or
# package names.
wordsep_re = re.compile(r'(\s+)')
chunks = wordsep_re.split(text)
- chunks = filter(None, chunks)
+ chunks = [x for x in chunks if x is not None]
return chunks
- def _wrap_chunks(self, chunks):
- """_wrap_chunks(chunks : [string]) -> [string]
-
- Wrap a sequence of text chunks and return a list of lines of
- length 'self.width' or less. (If 'break_long_words' is false,
- some lines may be longer than this.) Chunks correspond roughly
- to words and the whitespace between them: each chunk is
- indivisible (modulo 'break_long_words'), but a line break can
- come between any two chunks. Chunks should not have internal
- whitespace; ie. a chunk is either all whitespace or a "word".
- Whitespace chunks will be removed from the beginning and end of
- lines, but apart from that whitespace is preserved.
- """
- lines = []
- if self.width <= 0:
- raise ValueError("invalid width %r (must be > 0)" % self.width)
-
- # Arrange in reverse order so items can be efficiently popped
- # from a stack of chunks.
- chunks.reverse()
+ def _wrap_chunks(self, chunks):
+ """_wrap_chunks(chunks : [string]) -> [string]
+
+ Wrap a sequence of text chunks and return a list of lines of
+ length 'self.width' or less. (If 'break_long_words' is false,
+ some lines may be longer than this.) Chunks correspond roughly
+ to words and the whitespace between them: each chunk is
+ indivisible (modulo 'break_long_words'), but a line break can
+ come between any two chunks. Chunks should not have internal
+ whitespace; ie. a chunk is either all whitespace or a "word".
+ Whitespace chunks will be removed from the beginning and end of
+ lines, but apart from that whitespace is preserved.
+ """
+ lines = []
+ if self.width <= 0:
+ raise ValueError("invalid width %r (must be > 0)" % self.width)
+
+ # Arrange in reverse order so items can be efficiently popped
+ # from a stack of chunks.
+ chunks.reverse()
# Regex to strip ANSI escape codes. It's only used for the
# length calculations of indent and each chuck.
ansi_re = re.compile('\x1b\[[0-9;]*m')
- while chunks:
+ while chunks:
- # Start the list of chunks that will make up the current line.
- # cur_len is just the length of all the chunks in cur_line.
- cur_line = []
- cur_len = 0
+ # Start the list of chunks that will make up the current line.
+ # cur_len is just the length of all the chunks in cur_line.
+ cur_line = []
+ cur_len = 0
- # Figure out which static string will prefix this line.
- if lines:
- indent = self.subsequent_indent
- else:
- indent = self.initial_indent
+ # Figure out which static string will prefix this line.
+ if lines:
+ indent = self.subsequent_indent
+ else:
+ indent = self.initial_indent
# Maximum width for this line. Ingore ANSI escape codes.
width = self.width - len(re.sub(ansi_re, '', indent))
- # First chunk on line is whitespace -- drop it, unless this
- # is the very beginning of the text (ie. no lines started yet).
- if chunks[-1].strip() == '' and lines:
- del chunks[-1]
+ # First chunk on line is whitespace -- drop it, unless this
+ # is the very beginning of the text (ie. no lines started yet).
+ if chunks[-1].strip() == '' and lines:
+ del chunks[-1]
- while chunks:
+ while chunks:
# Ignore ANSI escape codes.
- l = len(re.sub(ansi_re, '', chunks[-1]))
+ chunk_len = len(re.sub(ansi_re, '', chunks[-1]))
- # Can at least squeeze this chunk onto the current line.
- if cur_len + l <= width:
- cur_line.append(chunks.pop())
- cur_len += l
+ # Can at least squeeze this chunk onto the current line.
+ if cur_len + chunk_len <= width:
+ cur_line.append(chunks.pop())
+ cur_len += chunk_len
- # Nope, this line is full.
- else:
- break
+ # Nope, this line is full.
+ else:
+ break
- # The current line is full, and the next chunk is too big to
- # fit on *any* line (not just this one).
+ # The current line is full, and the next chunk is too big to
+ # fit on *any* line (not just this one).
# Ignore ANSI escape codes.
- if chunks and len(re.sub(ansi_re, '', chunks[-1])) > width:
- self._handle_long_word(chunks, cur_line, cur_len, width)
+ if chunks and len(re.sub(ansi_re, '', chunks[-1])) > width:
+ self._handle_long_word(chunks, cur_line, cur_len, width)
- # If the last chunk on this line is all whitespace, drop it.
- if cur_line and cur_line[-1].strip() == '':
- del cur_line[-1]
+ # If the last chunk on this line is all whitespace, drop it.
+ if cur_line and cur_line[-1].strip() == '':
+ del cur_line[-1]
# Convert current line back to a string and store it in list
# of all lines (return value).
@@ -95,3 +95,5 @@ class TextWrapper(textwrap.TextWrapper):
lines.append(indent + ''.join(cur_line))
return lines
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/versionmatch.py b/pym/gentoolkit/versionmatch.py
index 4cafa00..914672f 100644
--- a/pym/gentoolkit/versionmatch.py
+++ b/pym/gentoolkit/versionmatch.py
@@ -1,6 +1,6 @@
#! /usr/bin/python
#
-# Copyright(c) 2009, Gentoo Foundation
+# Copyright(c) 2009 Gentoo Foundation
# Licensed under the GNU General Public License, v2
#
# Copyright: 2005-2007 Brian Harring <ferringb@gmail.com>
@@ -8,52 +8,7 @@
#
# $Header$
-"""Gentoo package version comparison object from pkgcore.ebuild.atom_restricts.
-
-The VersionMatch class allows you to compare package versions according to
-Gentoo's versioning rules.
-
-The simplest way to use it is to test simple equality. In this example I've
-passed in the keyword arguments op (operator), ver (version), and
-rev (revision) explicitly:
->>> from gentoolkit.versionmatch import VersionMatch
->>> VersionMatch(op='=',ver='1',rev='') == VersionMatch(op='=',ver='1',rev='')
-True
->>> VersionMatch(op='=',ver='1',rev='') == VersionMatch(op='=',ver='2',rev='')
-False
-
-A more flexible way to use it is to pass it a single gentoolkit.package.Package
-instance which it uses to determine op, ver and rev:
->>> from gentoolkit.package import Package
->>> from gentoolkit.versionmatch import VersionMatch
->>> pkg1 = Package('sys-apps/portage-2.2')
->>> pkg2 = Package('sys-apps/portage-1.6')
->>> VersionMatch(pkg1) == VersionMatch(pkg2)
-False
-
-Simple equality tests aren't actually very useful because they don't understand
-different operators:
->>> VersionMatch(op='>', ver='1.5', rev='') == \
-... VersionMatch(op='=', ver='2', rev='')
-False
-
-For more complicated comparisons, we can use the match method:
->>> from gentoolkit.package import Package
->>> from gentoolkit.versionmatch import VersionMatch
->>> pkg1 = Package('>=sys-apps/portage-2.2')
->>> pkg2 = Package('=sys-apps/portage-2.2_rc30')
->>> # An "rc" (release candidate) version compares less than a non "rc" version
-... VersionMatch(pkg1).match(pkg2)
-False
->>> pkg2 = Package('=sys-apps/portage-2.2-r6')
->>> # But an "r" (revision) version compares greater than a non "r" version
-... VersionMatch(pkg1).match(pkg2)
-True
-
-@see: gentoolkit.equery.changes for examples of use in gentoolkit.
-@see: gentoolkit.package.Package.intersects for a higher level version
- comparison method.
-"""
+"""Gentoo version comparison object from pkgcore.ebuild.atom_restricts."""
# =======
# Imports
@@ -61,15 +16,15 @@ True
from portage.versions import vercmp
-import gentoolkit
from gentoolkit import errors
+from gentoolkit.cpv import CPV
# =======
# Classes
# =======
class VersionMatch(object):
- """Gentoo package version comparison object from pkgcore.ebuild.atom_restricts.
+ """Gentoo version comparison object from pkgcore.ebuild.atom_restricts.
Any overriding of this class *must* maintain numerical order of
self.vals, see intersect for reason why. vals also must be a tuple.
@@ -77,42 +32,26 @@ class VersionMatch(object):
_convert_op2int = {(-1,):"<", (-1, 0): "<=", (0,):"=",
(0, 1):">=", (1,):">"}
- _convert_int2op = dict([(v, k) for k, v in _convert_op2int.iteritems()])
- del k, v
+ _convert_int2op = dict([(v, k) for k, v in _convert_op2int.items()])
- def __init__(self, *args, **kwargs):
- """This class will either create a VersionMatch instance out of
- a Package instance, or from explicitly passed in operator, version,
- and revision.
+ def __init__(self, cpv, op='='):
+ """Initialize a VersionMatch instance.
- Takes EITHER one arg:
- <gentoolkit.package.Package> instance
-
- OR
-
- three keyword args:
- op=str: version comparison to do,
- valid operators are ('<', '<=', '=', '>=', '>', '~')
- ver=str: version to base comparison on
- rev=str: revision to base comparison on
+ @type cpv: L{gentoolkit.cpv.CPV}
+ @param cpv: cpv object
+ @type op: str
+ @keyword op: operator
"""
- if args and isinstance(args[0], (gentoolkit.package.Package,
- self.__class__)):
- self.operator = args[0].operator
- self.version = args[0].version
- self.revision = args[0].revision
- self.fullversion = args[0].fullversion
- elif set(('op', 'ver', 'rev')) == set(kwargs):
- self.operator = kwargs['op']
- self.version = kwargs['ver']
- self.revision = kwargs['rev']
- if not self.revision:
- self.fullversion = self.version
- else:
- self.fullversion = "%s-%s" % (self.version, self.revision)
- else:
- raise TypeError('__init__() takes either a Package instance '
- 'argument or op=, ver= and rev= all passed in as keyword args')
+
+ if not isinstance(cpv, (CPV, self.__class__)):
+ err = "cpv must be a gentoolkit.cpv.CPV "
+ err += "or gentoolkit.versionmatch.VersionMatch instance"
+ raise ValueError(err)
+ self.cpv = cpv
+ self.operator = op
+ self.version = cpv.version
+ self.revision = cpv.revision
+ self.fullversion = cpv.fullversion
if self.operator != "~" and self.operator not in self._convert_int2op:
raise errors.GentoolkitInvalidVersion(
@@ -121,42 +60,33 @@ class VersionMatch(object):
if self.operator == "~":
if not self.version:
raise errors.GentoolkitInvalidVersion(
- "for ~ op, version must be specified")
+ "for ~ op, ver must be specified")
self.droprevision = True
self.values = (0,)
else:
self.droprevision = False
self.values = self._convert_int2op[self.operator]
- def match(self, pkginst):
- """See whether a passed in VersionMatch or Package instance matches
- self.
+ def match(self, other):
+ """See whether a passed in VersionMatch or CPV instance matches self.
Example usage:
>>> from gentoolkit.versionmatch import VersionMatch
- >>> VersionMatch(op='>',ver='1.5',rev='').match(
- ... VersionMatch(op='=',ver='2.0',rev=''))
+ >>> from gentoolkit.cpv import CPV
+ >>> VersionMatch(CPV('foo/bar-1.5'), op='>').match(
+ ... VersionMatch(CPV('foo/bar-2.0')))
True
- @type pkginst: gentoolkit.versionmatch.VersionMatch OR
- gentoolkit.package.Package
- @param pkginst: version to compare with self's version
+ @type other: gentoolkit.versionmatch.VersionMatch OR
+ gentoolkit.cpv.CPV
+ @param other: version to compare with self's version
@rtype: bool
"""
if self.droprevision:
- ver1, ver2 = self.version, pkginst.version
+ ver1, ver2 = self.version, other.version
else:
- ver1, ver2 = self.fullversion, pkginst.fullversion
-
- #print "== VersionMatch.match DEBUG START =="
- #print "ver1:", ver1
- #print "ver2:", ver2
- #print "vercmp(ver2, ver1):", vercmp(ver2, ver1)
- #print "self.values:", self.values
- #print "vercmp(ver2, ver1) in values?",
- #print "vercmp(ver2, ver1) in self.values"
- #print "== VersionMatch.match DEBUG END =="
+ ver1, ver2 = self.fullversion, other.fullversion
return vercmp(ver2, ver1) in self.values
@@ -165,10 +95,12 @@ class VersionMatch(object):
if self.droprevision or not self.revision:
return "ver %s %s" % (operator, self.version)
- return "ver-rev %s %s-%s" % (operator, self.version, self.revision)
+ return "ver-rev %s %s-%s" % (
+ operator, self.version, self.revision
+ )
def __repr__(self):
- return "<%s %s @%#8x>" % (self.__class__.__name__, str(self), id(self))
+ return "<%s %r>" % (self.__class__.__name__, str(self))
@staticmethod
def _convert_ops(inst):
@@ -188,6 +120,15 @@ class VersionMatch(object):
return False
+ def __ne__(self, other):
+ return not self == other
+
def __hash__(self):
- return hash((self.droprevision, self.version, self.revision,
- self.values))
+ return hash((
+ self.droprevision,
+ self.version,
+ self.revision,
+ self.values
+ ))
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/setup.py b/setup.py
index 446b3c7..91f9e9c 100755
--- a/setup.py
+++ b/setup.py
@@ -1,10 +1,116 @@
#!/usr/bin/env python
-from distutils.core import setup
+from __future__ import print_function
-__version__ = open("VERSION").read().strip()
-setup(
+import re
+import sys
+import distutils
+from distutils import core, log
+from glob import glob
+
+import os
+import io
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'pym'))
+
+__version__ = os.getenv('VERSION', default='9999')
+
+cwd = os.getcwd()
+
+# Load EPREFIX from Portage, fall back to the empty string if it fails
+try:
+ from portage.const import EPREFIX
+except ImportError:
+ EPREFIX='/'
+
+
+# Bash files that need `VERSION=""` subbed, relative to this dir:
+bash_scripts = [os.path.join(cwd, path) for path in (
+ 'bin/euse',
+ 'bin/revdep-rebuild'
+)]
+
+# Python files that need `__version__ = ""` subbed, relative to this dir:
+python_scripts = [os.path.join(cwd, path) for path in (
+ 'bin/eclean',
+ 'bin/epkginfo',
+ 'bin/glsa-check',
+ 'pym/gentoolkit/eclean/cli.py',
+ 'pym/gentoolkit/analyse/__init__.py',
+ 'pym/gentoolkit/equery/__init__.py',
+ 'pym/gentoolkit/eshowkw/__init__.py'
+)]
+
+
+class set_version(core.Command):
+ """Set python __version__ and bash VERSION to our __version__."""
+ description = "hardcode scripts' version using VERSION from environment"
+ user_options = [] # [(long_name, short_name, desc),]
+
+ def initialize_options (self):
+ pass
+
+ def finalize_options (self):
+ pass
+
+ def run(self):
+ ver = 'svn' if __version__ == '9999' else __version__
+ print("Settings version to %s" % ver)
+ def sub(files, pattern):
+ for f in files:
+ updated_file = []
+ with io.open(f, 'r', 1, 'utf_8') as s:
+ for line in s:
+ newline = re.sub(pattern, '"%s"' % ver, line, 1)
+ if newline != line:
+ log.info("%s: %s" % (f, newline))
+ updated_file.append(newline)
+ with io.open(f, 'w', 1, 'utf_8') as s:
+ s.writelines(updated_file)
+ quote = r'[\'"]{1}'
+ bash_re = r'(?<=VERSION=)' + quote + '[^\'"]*' + quote
+ sub(bash_scripts, bash_re)
+ python_re = r'(?<=^__version__ = )' + quote + '[^\'"]*' + quote
+ sub(python_scripts, python_re)
+
+
+def load_test():
+ """Only return the real test class if it's actually being run so that we
+ don't depend on snakeoil just to install."""
+
+ desc = "run the test suite"
+ if 'test' in sys.argv[1:]:
+ try:
+ from snakeoil import distutils_extensions
+ except ImportError:
+ sys.stderr.write("Error: We depend on dev-python/snakeoil ")
+ sys.stderr.write("to run tests.\n")
+ sys.exit(1)
+ class test(distutils_extensions.test):
+ description = desc
+ default_test_namespace = 'gentoolkit.test'
+ else:
+ class test(core.Command):
+ description = desc
+
+ return test
+
+packages = [
+ str('.'.join(root.split(os.sep)[1:]))
+ for root, dirs, files in os.walk('pym/gentoolkit')
+ if '__init__.py' in files
+]
+
+test_data = {
+ 'gentoolkit': [
+ 'test/eclean/Packages',
+ 'test/eclean/testdistfiles.tar.gz',
+ 'test/eclean/distfiles.exclude'
+ ]
+}
+
+core.setup(
name='gentoolkit',
version=__version__,
description='Set of tools that work with and enhance portage.',
@@ -16,35 +122,19 @@ setup(
download_url='http://distfiles.gentoo.org/distfiles/gentoolkit-%s.tar.gz'\
% __version__,
package_dir={'': 'pym'},
- packages=(
- 'gentoolkit',
- 'gentoolkit.equery',
- 'gentoolkit.glsa'
- ),
- scripts=(
- 'bin/eclean',
- 'bin/epkginfo',
- 'bin/equery',
- 'bin/eread',
- 'bin/euse',
- 'bin/glsa-check',
- 'bin/revdep-rebuild'
- ),
+ packages=packages,
+ package_data = test_data,
+ scripts=(glob('bin/*')),
data_files=(
- ('/etc/env.d', ['data/99gentoolkit-env']),
- ('/etc/revdep-rebuild', ['data/revdep-rebuild/99revdep-rebuild']),
- ('/etc/eclean', [
- 'data/eclean/distfiles.exclude',
- 'data/eclean/packages.exclude'
- ]),
- ('/usr/share/man/man1', [
- 'man/eclean.1',
- 'man/epkginfo.1',
- 'man/equery.1',
- 'man/eread.1',
- 'man/euse.1',
- 'man/glsa-check.1',
- 'man/revdep-rebuild.1'
- ])
- )
+ (os.path.join(EPREFIX, 'etc/env.d'), ['data/99gentoolkit-env']),
+ (os.path.join(EPREFIX, 'etc/revdep-rebuild'), ['data/revdep-rebuild/99revdep-rebuild']),
+ (os.path.join(EPREFIX, 'etc/eclean'), glob('data/eclean/*')),
+ (os.path.join(EPREFIX, 'usr/share/man/man1'), glob('man/*')),
+ ),
+ cmdclass={
+ 'test': load_test(),
+ 'set_version': set_version,
+ },
)
+
+# vim: set ts=4 sw=4 tw=79: