1 # Copyright 2007-2012 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
4 from __future__ import print_function
6 __all__ = ["SETPREFIX", "get_boolean", "SetConfigError",
7 "SetConfig", "load_default_config"]
13 from configparser import NoOptionError, ParsingError
14 if sys.hexversion >= 0x3020000:
15 from configparser import ConfigParser as SafeConfigParser
17 from configparser import SafeConfigParser
19 from ConfigParser import SafeConfigParser, NoOptionError, ParsingError
20 from portage import os
21 from portage import load_mod
22 from portage import _unicode_decode
23 from portage import _unicode_encode
24 from portage import _encodings
25 from portage.const import USER_CONFIG_PATH, GLOBAL_CONFIG_PATH
26 from portage.const import _ENABLE_SET_CONFIG
27 from portage.exception import PackageSetNotFound
28 from portage.localization import _
29 from portage.util import writemsg_level
33 def get_boolean(options, name, default):
34 if not name in options:
36 elif options[name].lower() in ("1", "yes", "on", "true"):
38 elif options[name].lower() in ("0", "no", "off", "false"):
41 raise SetConfigError(_("invalid value '%(value)s' for option '%(option)s'") % {"value": options[name], "option": name})
43 class SetConfigError(Exception):
46 class SetConfig(object):
47 def __init__(self, paths, settings, trees):
48 self._parser = SafeConfigParser(
50 "EPREFIX" : settings["EPREFIX"],
51 "EROOT" : settings["EROOT"],
52 "PORTAGE_CONFIGROOT" : settings["PORTAGE_CONFIGROOT"],
53 "ROOT" : settings["ROOT"],
56 if _ENABLE_SET_CONFIG:
57 # use read_file/readfp in order to control decoding of unicode
60 read_file = self._parser.read_file
61 except AttributeError:
62 read_file = self._parser.readfp
67 f = io.open(_unicode_encode(p,
68 encoding=_encodings['fs'], errors='strict'),
69 mode='r', encoding=_encodings['repo.content'],
71 except EnvironmentError:
76 except ParsingError as e:
77 writemsg_level(_unicode_decode(
78 _("!!! Error while reading sets config file: %s\n")
79 ) % e, level=logging.ERROR, noiselevel=-1)
84 self._create_default_config()
89 self.settings = settings
93 def _create_default_config(self):
95 Create a default hardcoded set configuration for a portage version
96 that does not support set configuration files. This is only used
97 in the current branch of portage if _ENABLE_SET_CONFIG is False.
98 Even if it's not used in this branch, keep it here in order to
99 minimize the diff between branches.
102 class = portage.sets.base.DummyPackageSet
103 packages = @selected @system
106 class = portage.sets.files.WorldSelectedSet
109 class = portage.sets.profiles.PackagesSystemSet
112 parser = self._parser
114 parser.remove_section("world")
115 parser.add_section("world")
116 parser.set("world", "class", "portage.sets.base.DummyPackageSet")
117 parser.set("world", "packages", "@selected @system")
119 parser.remove_section("selected")
120 parser.add_section("selected")
121 parser.set("selected", "class", "portage.sets.files.WorldSelectedSet")
123 parser.remove_section("system")
124 parser.add_section("system")
125 parser.set("system", "class", "portage.sets.profiles.PackagesSystemSet")
127 parser.remove_section("usersets")
128 parser.add_section("usersets")
129 parser.set("usersets", "class", "portage.sets.files.StaticFileSet")
130 parser.set("usersets", "multiset", "true")
131 parser.set("usersets", "directory", "%(PORTAGE_CONFIGROOT)setc/portage/sets")
132 parser.set("usersets", "world-candidate", "true")
134 parser.remove_section("live-rebuild")
135 parser.add_section("live-rebuild")
136 parser.set("live-rebuild", "class", "portage.sets.dbapi.VariableSet")
137 parser.set("live-rebuild", "variable", "INHERITED")
138 parser.set("live-rebuild", "includes", "bzr cvs darcs git git-2 mercurial subversion tla")
140 parser.remove_section("module-rebuild")
141 parser.add_section("module-rebuild")
142 parser.set("module-rebuild", "class", "portage.sets.dbapi.OwnerSet")
143 parser.set("module-rebuild", "files", "/lib/modules")
145 parser.remove_section("preserved-rebuild")
146 parser.add_section("preserved-rebuild")
147 parser.set("preserved-rebuild", "class", "portage.sets.libs.PreservedLibraryConsumerSet")
149 parser.remove_section("x11-module-rebuild")
150 parser.add_section("x11-module-rebuild")
151 parser.set("x11-module-rebuild", "class", "portage.sets.dbapi.OwnerSet")
152 parser.set("x11-module-rebuild", "files", "/usr/lib/xorg/modules")
153 parser.set("x11-module-rebuild", "exclude-files", "/usr/bin/Xorg")
155 def update(self, setname, options):
156 parser = self._parser
158 if not setname in self.psets:
159 options["name"] = setname
160 options["world-candidate"] = "False"
162 # for the unlikely case that there is already a section with the requested setname
164 while setname in parser.sections():
165 setname = "%08d" % random.randint(0, 10**10)
167 parser.add_section(setname)
168 for k, v in options.items():
169 parser.set(setname, k, v)
171 section = self.psets[setname].creator
172 if parser.has_option(section, "multiset") and \
173 parser.getboolean(section, "multiset"):
174 self.errors.append(_("Invalid request to reconfigure set '%(set)s' generated "
175 "by multiset section '%(section)s'") % {"set": setname, "section": section})
177 for k, v in options.items():
178 parser.set(section, k, v)
179 self._parse(update=True)
181 def _parse(self, update=False):
182 if self._parsed and not update:
184 parser = self._parser
185 for sname in parser.sections():
186 # find classname for current section, default to file based sets
187 if not parser.has_option(sname, "class"):
188 classname = "portage._sets.files.StaticFileSet"
190 classname = parser.get(sname, "class")
192 if classname.startswith('portage.sets.'):
193 # The module has been made private, but we still support
194 # the previous namespace for sets.conf entries.
195 classname = classname.replace('sets', '_sets', 1)
197 # try to import the specified class
199 setclass = load_mod(classname)
200 except (ImportError, AttributeError):
202 setclass = load_mod("portage._sets." + classname)
203 except (ImportError, AttributeError):
204 self.errors.append(_("Could not import '%(class)s' for section "
205 "'%(section)s'") % {"class": classname, "section": sname})
207 # prepare option dict for the current section
209 for oname in parser.options(sname):
210 optdict[oname] = parser.get(sname, oname)
212 # create single or multiple instances of the given class depending on configuration
213 if parser.has_option(sname, "multiset") and \
214 parser.getboolean(sname, "multiset"):
215 if hasattr(setclass, "multiBuilder"):
218 newsets = setclass.multiBuilder(optdict, self.settings, self.trees)
219 except SetConfigError as e:
220 self.errors.append(_("Configuration error in section '%s': %s") % (sname, str(e)))
223 if x in self.psets and not update:
224 self.errors.append(_("Redefinition of set '%s' (sections: '%s', '%s')") % (x, self.psets[x].creator, sname))
225 newsets[x].creator = sname
226 if parser.has_option(sname, "world-candidate") and \
227 parser.getboolean(sname, "world-candidate"):
228 newsets[x].world_candidate = True
229 self.psets.update(newsets)
231 self.errors.append(_("Section '%(section)s' is configured as multiset, but '%(class)s' "
232 "doesn't support that configuration") % {"section": sname, "class": classname})
236 setname = parser.get(sname, "name")
237 except NoOptionError:
239 if setname in self.psets and not update:
240 self.errors.append(_("Redefinition of set '%s' (sections: '%s', '%s')") % (setname, self.psets[setname].creator, sname))
241 if hasattr(setclass, "singleBuilder"):
243 self.psets[setname] = setclass.singleBuilder(optdict, self.settings, self.trees)
244 self.psets[setname].creator = sname
245 if parser.has_option(sname, "world-candidate") and \
246 parser.getboolean(sname, "world-candidate"):
247 self.psets[setname].world_candidate = True
248 except SetConfigError as e:
249 self.errors.append(_("Configuration error in section '%s': %s") % (sname, str(e)))
252 self.errors.append(_("'%(class)s' does not support individual set creation, section '%(section)s' "
253 "must be configured as multiset") % {"class": classname, "section": sname})
259 return self.psets.copy()
261 def getSetAtoms(self, setname, ignorelist=None):
263 This raises PackageSetNotFound if the give setname does not exist.
267 myset = self.psets[setname]
269 raise PackageSetNotFound(setname)
270 myatoms = myset.getAtoms()
272 if ignorelist is None:
275 ignorelist.add(setname)
276 for n in myset.getNonAtoms():
277 if n.startswith(SETPREFIX):
278 s = n[len(SETPREFIX):]
280 if s not in ignorelist:
281 myatoms.update(self.getSetAtoms(s,
282 ignorelist=ignorelist))
284 raise PackageSetNotFound(s)
288 def load_default_config(settings, trees):
290 if not _ENABLE_SET_CONFIG:
291 return SetConfig(None, settings, trees)
293 global_config_path = GLOBAL_CONFIG_PATH
294 if settings['EPREFIX']:
295 global_config_path = os.path.join(settings['EPREFIX'],
296 GLOBAL_CONFIG_PATH.lstrip(os.sep))
298 for path, dirs, files in os.walk(os.path.join(global_config_path, "sets")):
300 if not f.startswith(b'.'):
301 yield os.path.join(path, f)
303 dbapi = trees["porttree"].dbapi
304 for repo in dbapi.getRepositories():
305 path = dbapi.getRepositoryPath(repo)
306 yield os.path.join(path, "sets.conf")
308 yield os.path.join(settings["PORTAGE_CONFIGROOT"],
309 USER_CONFIG_PATH, "sets.conf")
311 return SetConfig(_getfiles(), settings, trees)