# author: Christian Ruppert <idl0r@gentoo.org>
-VERSION = "0.0.3"
+VERSION = "0.0.4"
# works just with stable keywords!
-MAIN_ARCH = "amd64" # can be overridden by -m ARCH
-TARGET_ARCH = "x86" # can be overridden by -t ARCH
+MAIN_ARCH = "auto" # can be overridden by -m ARCH
+TARGET_ARCH = "auto" # can be overridden by -t ARCH
+# auto means e.g.:
+# MAIN_ARCH = amd64
+# TARGET_ARCH = ~amd64
+# That will show you general stable candidates for amd64.
+# The arch will be taken from your portage settings (e.g. make.conf).
################################
# do not change anything below #
from sys import stderr, stdout
from os import stat
from time import time
+from xml.dom import minidom, NotFoundErr
+from xml.parsers.expat import ExpatError
# TODO: just import needed stuff to safe memory and maybe use "as foo"
import portage
col2 = 20
_header = "%s candidates for 'gentoo' on '%s'"
- _helper = "category/package our version best version"
+ _helper = "category/package[:SLOT] our version best version"
_cand = ""
header = ""
if conf["FILE"] != "stdout":
out.close()
+def _get_metadata(metadata, element, tag):
+ values = []
+
+ try:
+ metadatadom = minidom.parse(metadata)
+ except ExpatError, e:
+ raise ExpatError("%s: %s" % (metadata, e,))
+
+ try:
+ elements = metadatadom.getElementsByTagName(element)
+ if not elements:
+ return values
+ except NotFoundErr:
+ return values
+
+ try:
+ for _element in elements:
+ node = _element.getElementsByTagName(tag)
+
+ if tag == "herd" and (not node or not node[0].childNodes):
+ print >> stderr, "'%s' is missing a <herd> tag or it is empty," % metadata
+ print >> stderr, "please file a bug at https://bugs.gentoo.org and refer to http://www.gentoo.org/proj/en/devrel/handbook/handbook.xml?part=2&chap=4"
+ values.append("no-herd")
+ continue
+
+ values.append(node[0].childNodes[0].data)
+ except NotFoundErr:
+ raise NotFoundErr("%s: Malformed input: missing 'flag' tag(s)" % (metadata))
+
+ metadatadom.unlink()
+ return values
+
+def is_maintainer(maintainer, metadata):
+ data = []
+
+ if maintainer == None:
+ return True
+
+ mtainer = maintainer.split(",")
+
+ data = _get_metadata(metadata, "maintainer", "email")
+
+ if not data and len(maintainer) == 0:
+ return True
+ elif not data and len(maintainer) > 0:
+ return False
+ else:
+ for addy in data:
+ for foo in mtainer:
+ if addy == foo:
+ return True
+ if addy.startswith(foo):
+ return True
+ return False
+
+def is_herd(herd, metadata):
+ data = []
+
+ if herd == None:
+ return True
+
+ hrd = herd.split(",")
+ data = _get_metadata(metadata, "pkgmetadata", "herd")
+
+ if not data and len(herd) == 0:
+ return True
+ elif not data and len(herd) > 0:
+ return False
+ else:
+ for hd in data:
+ for hd2 in hrd:
+ if hd == hd2:
+ return True
+ if hd.startswith(hd2):
+ return True
+
+ return False
+
+
# fetch a list of arch (just stable) packages
# -* is important to be sure that just arch is used
def get_packages( conf ):
conf["portdb"].settings )
for cp in conf["portdb"].dbapi.cp_all():
- cpvr = portage.best( conf["portdb"].dbapi.match( cp ) )
- if cpvr:
- ( cat, pkg, ver, rev ) = portage.catpkgsplit( cpvr )
+ cpvrs = []
+ slots = {}
- if not cat in _pkgs.keys():
- _pkgs[cat] = {}
- if not pkg in _pkgs[cat].keys():
- _pkgs[cat][pkg] = []
+ if conf["USER_PKGS"]:
+ if not cp in conf["USER_PKGS"]:
+ continue
- if rev != "r0":
- ver = "%s-%s" % ( ver, rev )
+ # None is important to match also on empty string
+ if conf["MAINTAINER"] != None:
+ if not is_maintainer(conf["MAINTAINER"], join(conf["PORTDIR"], cp, "metadata.xml")):
+ continue
+ if conf["HERD"] != None:
+ if not is_herd(conf["HERD"], join(conf["PORTDIR"], cp, "metadata.xml")):
+ continue
+
+ cpvrs = conf["portdb"].dbapi.match( cp )
+
+ for cpvr in cpvrs:
+ slot = conf["portdb"].dbapi.aux_get( cpvr, ["SLOT"] )[0]
+ if not slot in slots:
+ slots[slot] = []
+ slots[slot].append(cpvr)
+
+ for slot in sorted(slots):
+ cpvr = portage.best( slots[slot] )
+
+ if cpvr:
+ ( cat, pkg, ver, rev ) = portage.catpkgsplit( cpvr )
+
+ if not cat in _pkgs.keys():
+ _pkgs[cat] = {}
+ if not pkg in _pkgs[cat].keys():
+ _pkgs[cat][pkg] = []
- _pkgs[cat][pkg].append( ver )
+ if rev != "r0":
+ ver = "%s-%s" % ( ver, rev )
+
+ _pkgs[cat][pkg].append( ver )
return _pkgs
for cat in sorted( pkgs.keys() ):
for pkg in sorted( pkgs[cat].keys() ):
- cpvr = ""
- abs_pkg = ""
- kwds = ""
- our = ""
- our_ver = ""
- mtime = 0
-
- # 0 = none(default), 1 = testing(~arch), 2 = stable(arch),
- # 3 = exclude(-arch), 4 = exclude_all(-*)
- # -* would be overridden by ~arch or arch
- kwd_type = 0
-
- cpvr = "%s/%s-%s" % ( cat, pkg, pkgs[cat][pkg][0] )
-
- # absolute ebuild path for mtime check
- abs_pkg = join( conf["PORTDIR"], cat, pkg, basename( cpvr ) )
- abs_pkg = "%s.ebuild" % str( abs_pkg )
-
- kwds = conf["portdb"].dbapi.aux_get( cpvr, ["KEYWORDS"] )[0]
-
- # sorted() to keep the right order
- # e.g. -* first, -arch second, arch third and ~arch fourth
- # -* -foo ~arch
- # example: -* would be overridden by ~arch
- for kwd in sorted( kwds.split() ):
- if kwd == stable:
- kwd_type = 2
- break
- elif kwd == exclude:
- kwd_type = 3
- break
- # EXPERIMENTAL
- elif kwd == exclude_all and conf["EXPERIMENTAL"]:
- kwd_type = 4
- elif kwd == testing:
- kwd_type = 1
- break
-
- # ignore -arch and already stabilized packages
- if kwd_type == 3 or kwd_type == 2:
- continue
- # EXPERIMENTAL
- # drop packages without ~arch or arch but -*
- # even if there is another version which includes arch or ~arch
- if kwd_type == 4 and conf["EXPERIMENTAL"]:
- continue
- # drop "stable candidates" with mtime < 30 days
- # Shall we use gmtime/UTC here?
- if kwd_type == 1:
- mtime = int( ( time() - stat( abs_pkg ).st_mtime ) / 60 / 60 / 24 )
- if mtime < 30:
- continue
-
- # look for an existing stable version
- our = portage.best( conf["portdb"].dbapi.match( "%s/%s" % ( cat, pkg ) ) )
- if our:
- _foo = portage.pkgsplit( our )
- our_ver = _foo[1]
- if _foo[2] != "r0":
- our_ver = "%s-%s" % ( our_ver, _foo[2] )
- else:
+ for vr in pkgs[cat][pkg]:
+ cpvr = ""
+ abs_pkg = ""
+ kwds = ""
+ our = ""
our_ver = ""
-
- # we just need the version if > our_ver
- if our_ver:
- if portage.vercmp( our_ver, pkgs[cat][pkg][0] ) >= 0:
+ mtime = 0
+ slot = 0
+
+ # 0 = none(default), 1 = testing(~arch), 2 = stable(arch),
+ # 3 = exclude(-arch), 4 = exclude_all(-*)
+ # -* would be overridden by ~arch or arch
+ kwd_type = 0
+
+ cpvr = "%s/%s-%s" % ( cat, pkg, vr )
+
+ # absolute ebuild path for mtime check
+ abs_pkg = join( conf["PORTDIR"], cat, pkg, basename( cpvr ) )
+ abs_pkg = "%s.ebuild" % str( abs_pkg )
+
+ kwds = conf["portdb"].dbapi.aux_get( cpvr, ["KEYWORDS"] )[0]
+
+ # FIXME: %s is bad.. maybe even cast it, else there are issues because its unicode
+ slot = ":%s" % conf["portdb"].dbapi.aux_get( cpvr, ["SLOT"] )[0]
+ if slot == ":0":
+ slot = ""
+
+ # sorted() to keep the right order
+ # e.g. -* first, -arch second, arch third and ~arch fourth
+ # -* -foo ~arch
+ # example: -* would be overridden by ~arch
+ for kwd in sorted( kwds.split() ):
+ if kwd == stable:
+ kwd_type = 2
+ break
+ elif kwd == exclude:
+ kwd_type = 3
+ break
+ # EXPERIMENTAL
+ elif kwd == exclude_all and conf["EXPERIMENTAL"]:
+ kwd_type = 4
+ elif kwd == testing:
+ kwd_type = 1
+ break
+
+ # ignore -arch and already stabilized packages
+ if kwd_type == 3 or kwd_type == 2:
continue
-
- if kwd_type == 1 and conf["STABLE"]:
- imlate = _add_ent( imlate, cat, pkg, pkgs[cat][pkg][0], our_ver )
- conf["STABLE_SUM"] += 1
- elif kwd_type == 0 and conf["KEYWORD"]:
- conf["KEYWORD_SUM"] += 1
- imlate = _add_ent( imlate, cat, ( "~%s" % str( pkg ) ),
- pkgs[cat][pkg][0], our_ver )
+ # EXPERIMENTAL
+ # drop packages without ~arch or arch but -*
+ # even if there is another version which includes arch or ~arch
+ if kwd_type == 4 and conf["EXPERIMENTAL"]:
+ continue
+ # drop "stable candidates" with mtime < 30 days
+ # Shall we use gmtime/UTC here?
+ if kwd_type == 1:
+ mtime = int( ( time() - stat( abs_pkg ).st_mtime ) / 60 / 60 / 24 )
+ if mtime < conf["MTIME"]:
+ continue
+
+ # look for an existing stable version
+ our = portage.best( conf["portdb"].dbapi.match( "%s/%s%s" % ( cat, pkg, slot ) ) )
+ if our:
+ _foo = portage.pkgsplit( our )
+ our_ver = _foo[1]
+ if _foo[2] != "r0":
+ our_ver = "%s-%s" % ( our_ver, _foo[2] )
+ else:
+ our_ver = ""
+
+ # we just need the version if > our_ver
+ if our_ver:
+ if portage.vercmp( our_ver, vr ) >= 0:
+ continue
+
+ if kwd_type == 1 and conf["STABLE"]:
+ imlate = _add_ent( imlate, cat, ("%s%s" % (pkg, slot)), vr, our_ver )
+ conf["STABLE_SUM"] += 1
+ elif kwd_type == 0 and conf["KEYWORD"]:
+ conf["KEYWORD_SUM"] += 1
+ imlate = _add_ent( imlate, cat, ( "~%s%s" % (pkg, slot) ),
+ vr, our_ver )
return imlate
# TODO: maybe we should improve it a bit ;)
mysettings = portage.config( config_incrementals = portage.const.INCREMENTALS, local_config = False )
+ if conf["MAIN_ARCH"] == "auto":
+ conf["MAIN_ARCH"] = "%s" % mysettings["ACCEPT_KEYWORDS"].split(" ")[0].lstrip("~")
+ if conf["TARGET_ARCH"] == "auto":
+ conf["TARGET_ARCH"] = "~%s" % mysettings["ACCEPT_KEYWORDS"].split(" ")[0].lstrip("~")
+
# TODO: exclude overlay categories from check
if conf["CATEGORIES"]:
_mycats = []
pkgs = {}
parser = OptionParser( version = "%prog " + VERSION )
+ parser.usage = "%prog [options] [category/package] ..."
parser.disable_interspersed_args()
parser.add_option( "-f", "--file", dest = "filename", action = "store", type = "string",
help = "set main ARCH (e.g. your arch) [default: %default]", metavar = "ARCH", default = MAIN_ARCH )
parser.add_option( "-t", "--target", dest = "target_arch", action = "store", type = "string",
help = "set target ARCH (e.g. x86) [default: %default]", metavar = "ARCH", default = TARGET_ARCH )
+ parser.add_option( "--mtime", dest = "mtime", action = "store", type = "int",
+ help = "set minimum MTIME in days [default: %default]", metavar = "MTIME", default = 30 )
# TODO: leave a good comment here (about True/False) :)
parser.add_option( "-s", "--stable", dest = "stable", action = "store_true", default = False,
parser.add_option( "-k", "--keyword", dest = "keyword", action = "store_true", default = False,
help = "just show keyword candidates (e.g. -s and -k is the default result) [default: True]" )
+ parser.add_option( "-M", "--maintainer", dest = "maintainer", action = "store", type = "string",
+ help = "Show only packages from the specified maintainer", metavar = "MAINTAINER", default = None)
+
+ parser.add_option( "-H", "--herd", dest = "herd", action = "store", type = "string",
+ help = "Show only packages from the specified herd", metavar = "HERD", default = None)
+
# EXPERIMENTAL
parser.add_option( "-e", "--experimental", dest = "experimental", action = "store_true", default = False,
help = "enables experimental functions/features (have a look for # EXPERIMENTAL comments in the source) [default: %default]" )
( options, args ) = parser.parse_args()
if len( args ) > 0:
- parser.print_help()
- parser.error( "unknown arg(s)" )
+ conf["USER_PKGS"] = args
+ else:
+ conf["USER_PKGS"] = []
# cleanup optparse
try:
conf["KEYWORD_SUM"] = 0
conf["STABLE_SUM"] = 0
- if not options.main_arch in portage.archlist:
+ if not options.main_arch in portage.archlist and options.main_arch != "auto":
raise ValueError, "invalid MAIN ARCH defined!"
- if not options.target_arch in portage.archlist:
+ if not options.target_arch in portage.archlist and options.target_arch != "auto":
raise ValueError, "invalid TARGET ARCH defined!"
conf["MAIN_ARCH"] = options.main_arch
conf["TARGET_ARCH"] = options.target_arch
conf["FILE"] = options.filename
+ conf["MTIME"] = options.mtime
if not options.stable and not options.keyword:
conf["STABLE"] = True
conf["EXPERIMENTAL"] = options.experimental
conf["CATEGORIES"] = options.categories
+ conf["MAINTAINER"] = options.maintainer
+ conf["HERD"] = options.herd
+
# append to our existing
conf = get_settings( conf )
pkgs = get_packages( conf )