summaryrefslogtreecommitdiff
blob: 554b534bc7549b4f09e8c63231da973ba7c3633f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# Copyright 2007 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Id$

import os
from ConfigParser import SafeConfigParser, NoOptionError
from portage import load_mod
from portage.const import USER_CONFIG_PATH, GLOBAL_CONFIG_PATH

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(SafeConfigParser):
	def __init__(self, paths, settings, trees):
		SafeConfigParser.__init__(self)
		self.read(paths)
		self.errors = []
		self.psets = {}
		self.trees = trees
		self.settings = settings
		self._parsed = False
		self.active = []

	def _parse(self):
		if self._parsed:
			return
		for sname in self.sections():
			# find classname for current section, default to file based sets
			if not self.has_option(sname, "class"):
				classname = "portage.sets.files.StaticFileSet"
			else:
				classname = self.get(sname, "class")
			
			# try to import the specified class
			try:
				setclass = load_mod(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 self.options(sname):
				optdict[oname] = self.get(sname, oname)
			
			# create single or multiple instances of the given class depending on configuration
			if self.has_option(sname, "multiset") and self.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:
							self.errors.append("Redefinition of set '%s' (sections: '%s', '%s')" % (setname, self.psets[setname].creator, sname))
						newsets[x].creator = sname
					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 = self.get(sname, "name")
				except NoOptionError:
					setname = sname
				if setname in self.psets:
					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
					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):
		myset = self.getSets()[setname]
		myatoms = myset.getAtoms()
		if ignorelist is None:
			ignorelist = set()
		ignorelist.add(setname)
		for n in myset.getNonAtoms():
			if n[0] == SETPREFIX and n[1:] in self.psets:
				if n[1:] not in ignorelist:
					myatoms.update(self.getSetAtoms(n[1:],
						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