blob: ef7c1d9381105bf5983498e3f3013174b617b9c6 [file] [log] [blame]
# Copyright 2007 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Id$
__all__ = ["SETPREFIX", "get_boolean", "SetConfigError",
"SetConfig", "load_default_config"]
import os
from ConfigParser import SafeConfigParser, NoOptionError
from portage import load_mod
from portage.const import USER_CONFIG_PATH, GLOBAL_CONFIG_PATH
from portage.exception import PackageSetNotFound
SETPREFIX = "@"
def get_boolean(options, name, default):
if not name in options:
return default
elif options[name].lower() in ("1", "yes", "on", "true"):
return True
elif options[name].lower() in ("0", "no", "off", "false"):
return False
else:
raise SetConfigError("invalid value '%s' for option '%s'" % (options[name], name))
class SetConfigError(Exception):
pass
class SetConfig(object):
def __init__(self, paths, settings, trees):
self._parser = SafeConfigParser()
self._parser.read(paths)
self.errors = []
self.psets = {}
self.trees = trees
self.settings = settings
self._parsed = False
self.active = []
def update(self, setname, options):
parser = self._parser
self.errors = []
if not setname in self.psets:
options["name"] = setname
options["world-candidate"] = "False"
# for the unlikely case that there is already a section with the requested setname
import random
while setname in parser.sections():
setname = "%08d" % random.randint(0, 10**10)
parser.add_section(setname)
for k, v in options.items():
parser.set(setname, k, v)
else:
section = self.psets[setname].creator
if parser.has_option(section, "multiset") and \
parser.getboolean(section, "multiset"):
self.errors.append("Invalid request to reconfigure set '%s' generated by multiset section '%s'" % (setname, section))
return
for k, v in options.items():
parser.set(section, k, v)
self._parse(update=True)
def _parse(self, update=False):
if self._parsed and not update:
return
parser = self._parser
for sname in parser.sections():
# find classname for current section, default to file based sets
if not parser.has_option(sname, "class"):
classname = "portage.sets.files.StaticFileSet"
else:
classname = parser.get(sname, "class")
# try to import the specified class
try:
setclass = load_mod(classname)
except (ImportError, AttributeError):
try:
setclass = load_mod("portage.sets."+classname)
except (ImportError, AttributeError):
self.errors.append("Could not import '%s' for section '%s'" % (classname, sname))
continue
# prepare option dict for the current section
optdict = {}
for oname in parser.options(sname):
optdict[oname] = parser.get(sname, oname)
# create single or multiple instances of the given class depending on configuration
if parser.has_option(sname, "multiset") and \
parser.getboolean(sname, "multiset"):
if hasattr(setclass, "multiBuilder"):
newsets = {}
try:
newsets = setclass.multiBuilder(optdict, self.settings, self.trees)
except SetConfigError, e:
self.errors.append("Configuration error in section '%s': %s" % (sname, str(e)))
continue
for x in newsets:
if x in self.psets and not update:
self.errors.append("Redefinition of set '%s' (sections: '%s', '%s')" % (x, self.psets[x].creator, sname))
newsets[x].creator = sname
if parser.has_option(sname, "world-candidate") and \
not parser.getboolean(sname, "world-candidate"):
newsets[x].world_candidate = False
self.psets.update(newsets)
else:
self.errors.append("Section '%s' is configured as multiset, but '%s' doesn't support that configuration" % (sname, classname))
continue
else:
try:
setname = parser.get(sname, "name")
except NoOptionError:
setname = sname
if setname in self.psets and not update:
self.errors.append("Redefinition of set '%s' (sections: '%s', '%s')" % (setname, self.psets[setname].creator, sname))
if hasattr(setclass, "singleBuilder"):
try:
self.psets[setname] = setclass.singleBuilder(optdict, self.settings, self.trees)
self.psets[setname].creator = sname
if parser.has_option(sname, "world-candidate") and \
not parser.getboolean(sname, "world-candidate"):
self.psets[setname].world_candidate = False
except SetConfigError, e:
self.errors.append("Configuration error in section '%s': %s" % (sname, str(e)))
continue
else:
self.errors.append("'%s' does not support individual set creation, section '%s' must be configured as multiset" % (classname, sname))
continue
self._parsed = True
def getSets(self):
self._parse()
return self.psets.copy()
def getSetAtoms(self, setname, ignorelist=None):
"""
This raises PackageSetNotFound if the give setname does not exist.
"""
self._parse()
try:
myset = self.psets[setname]
except KeyError:
raise PackageSetNotFound(setname)
myatoms = myset.getAtoms()
parser = self._parser
extend = set()
remove = set()
intersect = set()
if ignorelist is None:
ignorelist = set()
if not setname in ignorelist:
if parser.has_option(myset.creator, "extend"):
extend.update(parser.get(myset.creator, "extend").split())
if parser.has_option(myset.creator, "remove"):
remove.update(parser.get(myset.creator, "remove").split())
if parser.has_option(myset.creator, "intersect"):
intersect.update(parser.get(myset.creator, "intersect").split())
ignorelist.add(setname)
for n in myset.getNonAtoms():
if n.startswith(SETPREFIX):
s = n[len(SETPREFIX):]
if s in self.psets:
extend.add(n[len(SETPREFIX):])
else:
raise PackageSetNotFound(s)
for s in ignorelist:
extend.discard(s)
remove.discard(s)
intersect.discard(s)
for s in extend:
myatoms.update(self.getSetAtoms(s, ignorelist=ignorelist))
for s in remove:
myatoms.difference_update(self.getSetAtoms(s, ignorelist=ignorelist))
for s in intersect:
myatoms.intersection_update(self.getSetAtoms(s, ignorelist=ignorelist))
return myatoms
def load_default_config(settings, trees):
setconfigpaths = [os.path.join(GLOBAL_CONFIG_PATH, "sets.conf")]
setconfigpaths.append(os.path.join(settings["PORTDIR"], "sets.conf"))
setconfigpaths += [os.path.join(x, "sets.conf") for x in settings["PORTDIR_OVERLAY"].split()]
setconfigpaths.append(os.path.join(settings["PORTAGE_CONFIGROOT"],
USER_CONFIG_PATH.lstrip(os.path.sep), "sets.conf"))
return SetConfig(setconfigpaths, settings, trees)
# adhoc test code
if __name__ == "__main__":
import portage
sc = load_default_config(portage.settings, portage.db["/"])
l, e = sc.getSets()
for x in l:
print x+":"
print "DESCRIPTION = %s" % l[x].getMetadata("Description")
for n in sorted(l[x].getAtoms()):
print "- "+n
print