aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/gentoolkit')
-rw-r--r--src/gentoolkit/AUTHORS2
-rw-r--r--src/gentoolkit/Makefile22
-rw-r--r--src/gentoolkit/README17
-rw-r--r--src/gentoolkit/TODO0
-rw-r--r--src/gentoolkit/__init__.py59
-rw-r--r--src/gentoolkit/errors.py14
-rw-r--r--src/gentoolkit/helpers.py162
-rw-r--r--src/gentoolkit/package.py241
-rw-r--r--src/gentoolkit/pprinter.py116
9 files changed, 633 insertions, 0 deletions
diff --git a/src/gentoolkit/AUTHORS b/src/gentoolkit/AUTHORS
new file mode 100644
index 0000000..0dfa694
--- /dev/null
+++ b/src/gentoolkit/AUTHORS
@@ -0,0 +1,2 @@
+Original author:
+Karl Trygve Kalleberg <karltk@gentoo.org> \ No newline at end of file
diff --git a/src/gentoolkit/Makefile b/src/gentoolkit/Makefile
new file mode 100644
index 0000000..831bcfd
--- /dev/null
+++ b/src/gentoolkit/Makefile
@@ -0,0 +1,22 @@
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+all:
+ echo "LISTOWEL (n.) The small mat on the bar designed to be more absorbent than the bar, but not as absorbent as your elbows."
+
+dist:
+ mkdir -p ../../${distdir}/src/gentoolkit
+ cp Makefile AUTHORS README TODO errors.py package.py helpers.py pprinter.py __init__.py ../../${distdir}/src/gentoolkit/
+
+install:
+ install -d $(docdir)/gentoolkit
+ install -m 0644 AUTHORS README TODO $(docdir)/gentoolkit/
+ install -d $(DESTDIR)/usr/lib/gentoolkit/pym/gentoolkit
+ install -m 0644 package.py pprinter.py helpers.py errors.py $(DESTDIR)/usr/lib/gentoolkit/pym/gentoolkit/
+ install -m 0644 __init__.py $(DESTDIR)/usr/lib/gentoolkit/pym/gentoolkit/
+
diff --git a/src/gentoolkit/README b/src/gentoolkit/README
new file mode 100644
index 0000000..916dc81
--- /dev/null
+++ b/src/gentoolkit/README
@@ -0,0 +1,17 @@
+
+Package : gentoolkit
+Version : see __init__.py
+Author : See AUTHORS
+
+MOTIVATION
+
+This is the Python API for Gentoolkit. It contains common functionality shared
+by the Gentoolkit tools written in Python.
+
+MECHANICS
+
+N/A
+
+IMPROVEMENTS
+
+N/A
diff --git a/src/gentoolkit/TODO b/src/gentoolkit/TODO
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/gentoolkit/TODO
diff --git a/src/gentoolkit/__init__.py b/src/gentoolkit/__init__.py
new file mode 100644
index 0000000..28b56be
--- /dev/null
+++ b/src/gentoolkit/__init__.py
@@ -0,0 +1,59 @@
+#!/usr/bin/python
+#
+# Copyright 2003-2004 Karl Trygve Kalleberg
+# Copyright 2003-2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+# Author: Karl Trygve Kalleberg <karltk@gentoo.org>
+#
+# Portions written ripped from
+# - etcat, by Alistair Tse <liquidx@gentoo.org>
+#
+
+__author__ = "Karl Trygve Kalleberg"
+__email__ = "karltk@gentoo.org"
+__version__ = "0.1.1"
+__productname__ = "gentoolkit"
+__description__ = "Gentoolkit Common Library"
+
+import os
+import sys
+try:
+ import portage
+except ImportError:
+ sys.path.insert(0, "/usr/lib/portage/pym")
+ import portage
+import re
+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:
+ def acquire(self):
+ pass
+ def release(self):
+ pass
+
+try:
+ import portage.exception as portage_exception
+except ImportError:
+ import portage_exception
+
+try:
+ settingslock = Lock()
+ settings = portage.config(clone=portage.settings)
+ porttree = portage.db[portage.root]["porttree"]
+ vartree = portage.db[portage.root]["vartree"]
+ virtuals = portage.db[portage.root]["virtuals"]
+except portage_exception.PermissionDenied, e:
+ sys.stderr.write("Permission denied: '%s'\n" % str(e))
+ sys.exit(e.errno)
+
+Config = {
+ "verbosityLevel": 3
+}
+
+from helpers import *
+from package import *
diff --git a/src/gentoolkit/errors.py b/src/gentoolkit/errors.py
new file mode 100644
index 0000000..db81721
--- /dev/null
+++ b/src/gentoolkit/errors.py
@@ -0,0 +1,14 @@
+#! /usr/bin/python2
+#
+# Copyright(c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright(c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+class FatalError:
+ def __init__(self, s):
+ self._message = s
+ def get_message(self):
+ return self._message \ No newline at end of file
diff --git a/src/gentoolkit/helpers.py b/src/gentoolkit/helpers.py
new file mode 100644
index 0000000..bf2b1b1
--- /dev/null
+++ b/src/gentoolkit/helpers.py
@@ -0,0 +1,162 @@
+#!/usr/bin/python2
+#
+# Copyright(c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright(c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $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)
+ 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 installed candidate that
+ matched the search key."""
+ t = portage.db["/"]["vartree"].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"""
+ 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"]
+ else:
+ return r + ["", "r0"]
+ else:
+ r = list(r)
+ if r[0] == 'null':
+ r[0] = ''
+ return r
+
+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/src/gentoolkit/package.py b/src/gentoolkit/package.py
new file mode 100644
index 0000000..3319860
--- /dev/null
+++ b/src/gentoolkit/package.py
@@ -0,0 +1,241 @@
+#! /usr/bin/python2
+#
+# Copyright(c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright(c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+from errors import FatalError
+import portage
+from gentoolkit import *
+
+class Package:
+ """Package descriptor. Contains convenience functions for querying the
+ state of a package, its contents, name manipulation, ebuild info and
+ similar."""
+
+ def __init__(self,cpv):
+ self._cpv = cpv
+ self._scpv = portage.catpkgsplit(self._cpv)
+
+ if not self._scpv:
+ raise FatalError("invalid cpv: %s" % cpv)
+ self._db = None
+ self._settings = settings
+ self._settingslock = settingslock
+
+ def get_name(self):
+ """Returns base name of package, no category nor version"""
+ return self._scpv[1]
+
+ def get_version(self):
+ """Returns version of package, with revision number"""
+ v = self._scpv[2]
+ if self._scpv[3] != "r0":
+ v += "-" + self._scpv[3]
+ return v
+
+ def get_category(self):
+ """Returns category of package"""
+ return self._scpv[0]
+
+ def get_settings(self, key):
+ """Returns the value of the given key for this package (useful
+ for package.* files."""
+ self._settingslock.acquire()
+ self._settings.setcpv(self._cpv)
+ v = self._settings[key]
+ self._settingslock.release()
+ return v
+
+ def get_cpv(self):
+ """Returns full Category/Package-Version string"""
+ return self._cpv
+
+ def get_provide(self):
+ """Return a list of provides, if any"""
+ if not self.is_installed():
+ try:
+ x = [self.get_env_var('PROVIDE')]
+ except KeyError:
+ x = []
+ return x
+ else:
+ return vartree.get_provide(self._cpv)
+
+ def get_dependants(self):
+ """Retrieves a list of CPVs for all packages depending on this one"""
+ raise NotImplementedError("Not implemented yet!")
+
+ 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), ...]"""
+ # Try to use the portage tree first, since emerge only uses the tree when calculating dependencies
+ try:
+ cd = self.get_env_var("RDEPEND", porttree).split()
+ except KeyError:
+ cd = self.get_env_var("RDEPEND", vartree).split()
+ r,i = self._parse_deps(cd)
+ return r
+
+ 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), ...]"""
+ # Try to use the portage tree first, since emerge only uses the tree when calculating dependencies
+ try:
+ rd = self.get_env_var("DEPEND", porttree).split()
+ except KeyError:
+ rd = self.get_env_var("DEPEND", vartree).split()
+ r,i = self._parse_deps(rd)
+ return r
+
+ 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), ...]"""
+ # Try to use the portage tree first, since emerge only uses the tree when calculating dependencies
+ try:
+ pd = self.get_env_var("PDEPEND", porttree).split()
+ except KeyError:
+ pd = self.get_env_var("PDEPEND", vartree).split()
+ r,i = self._parse_deps(pd)
+ return r
+
+ 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
+ # conjonction, 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
+ continue
+ # pkg block "!foo/bar" => ignore it
+ if tok[0] == "!":
+ i += 1
+ 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
+
+ def is_installed(self):
+ """Returns true if this package is installed (merged)"""
+ self._initdb()
+ return os.path.exists(self._db.getpath())
+
+ def is_overlay(self):
+ """Returns true if the package is in an overlay."""
+ dir,ovl = portage.portdb.findname2(self._cpv)
+ return ovl != settings["PORTDIR"]
+
+ 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=0):
+ """Returns the complete path to the .ebuild file"""
+ if in_vartree:
+ return vartree.getebuildpath(self._cpv)
+ else:
+ return portage.portdb.findname(self._cpv)
+
+ def get_package_path(self):
+ """Returns the path to where the ChangeLog, Manifest, .ebuild files reside"""
+ p = self.get_ebuild_path()
+ sp = p.split("/")
+ if len(sp):
+ return "/".join(sp[:-1])
+
+ def get_env_var(self, var, tree=""):
+ """Returns one of the predefined env vars DEPEND, RDEPEND, SRC_URI,...."""
+ if tree == "":
+ mytree = vartree
+ if not self.is_installed():
+ mytree = porttree
+ else:
+ mytree = tree
+ r = mytree.dbapi.aux_get(self._cpv,[var])
+ if not r:
+ raise FatalError("Could not find the package tree")
+ if len(r) != 1:
+ raise FatalError("Should only get one element!")
+ return r[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")
+ return ""
+
+ def get_contents(self):
+ """Returns the full contents, as a dictionary, on the form
+ [ '/bin/foo' : [ 'obj', '1052505381', '45ca8b8975d5094cd75bdc61e9933691' ], ... ]"""
+ self._initdb()
+ if self.is_installed():
+ return self._db.getcontents()
+ return {}
+
+ def compare_version(self,other):
+ """Compares this package's version to another's CPV; returns -1, 0, 1"""
+ v1 = self._scpv
+ v2 = portage.catpkgsplit(other.get_cpv())
+ # if category is different
+ if v1[0] != v2[0]:
+ return cmp(v1[0],v2[0])
+ # if name is different
+ elif v1[1] != v2[1]:
+ return cmp(v1[1],v2[1])
+ # Compare versions
+ else:
+ return portage.pkgcmp(v1[1:],v2[1:])
+
+ 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]"""
+ 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:
+ cat = self.get_category()
+ pnv = self.get_name()+"-"+self.get_version()
+ self._db = portage.dblink(cat,pnv,settings["ROOT"],settings)
diff --git a/src/gentoolkit/pprinter.py b/src/gentoolkit/pprinter.py
new file mode 100644
index 0000000..39665f8
--- /dev/null
+++ b/src/gentoolkit/pprinter.py
@@ -0,0 +1,116 @@
+#!/usr/bin/python
+#
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2004 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
+
+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)
+
+def installedflag(s):
+ """Print an installed flag string"""
+ return output.bold(s)
+
+def number(s):
+ """Print a number string"""
+ return output.turquoise(s)
+
+def pkgquery(s):
+ """Print a package query string."""
+ return output.bold(s)
+
+def regexpquery(s):
+ """Print a regular expression string"""
+ return output.bold(s)
+
+def path(s):
+ """Print a file or directory path string"""
+ return output.bold(s)
+
+def path_symlink(s):
+ """Print a symlink string."""
+ return output.turquoise(s)
+
+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 localoption(s):
+ """Print a local option string, i.e. the program local options."""
+ return output.green(s)
+
+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)
+
+def subsection(s):
+ """Print a string as a subsection header."""
+ return output.turquoise(s)
+
+def emph(s):
+ """Print a string as emphasized."""
+ return output.bold(s)