1 # Copyright 2010-2013 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
4 from __future__ import unicode_literals
13 from configparser import Error as ConfigParserError
14 if sys.hexversion >= 0x3020000:
15 from configparser import ConfigParser as SafeConfigParser
17 from configparser import SafeConfigParser
19 from ConfigParser import SafeConfigParser, Error as ConfigParserError
21 from portage import eclass_cache, os
22 from portage.const import (MANIFEST2_HASH_FUNCTIONS, MANIFEST2_REQUIRED_HASH,
23 PORTAGE_BASE_PATH, REPO_NAME_LOC, USER_CONFIG_PATH)
24 from portage.eapi import eapi_allows_directories_on_profile_level_and_repository_level
25 from portage.env.loaders import KeyValuePairFileLoader
26 from portage.util import (normalize_path, read_corresponding_eapi_file, shlex_split,
27 stack_lists, writemsg, writemsg_level, _recursive_file_list)
28 from portage.util._path import exists_raise_eaccess, isdir_raise_eaccess
29 from portage.localization import _
30 from portage import _unicode_decode
31 from portage import _unicode_encode
32 from portage import _encodings
33 from portage import manifest
35 if sys.hexversion >= 0x3000000:
38 # Characters prohibited by repoman's file.name check.
39 _invalid_path_char_re = re.compile(r'[^a-zA-Z0-9._\-+:/]')
41 _valid_profile_formats = frozenset(
42 ['pms', 'portage-1', 'portage-2'])
44 _portage1_profiles_allow_directories = frozenset(
45 ["portage-1-compat", "portage-1", 'portage-2'])
47 _repo_name_sub_re = re.compile(r'[^\w-]')
49 def _gen_valid_repo(name):
51 Substitute hyphen in place of characters that don't conform to PMS 3.1.5,
52 and strip hyphen from left side if necessary. This returns None if the
53 given name contains no valid characters.
55 name = _repo_name_sub_re.sub(' ', name.strip())
56 name = '-'.join(name.split())
57 name = name.lstrip('-')
62 def _find_invalid_path_char(path, pos=0, endpos=None):
64 Returns the position of the first invalid character found in basename,
65 or -1 if no invalid characters are found.
70 m = _invalid_path_char_re.search(path, pos=pos, endpos=endpos)
76 class RepoConfig(object):
77 """Stores config of one repository"""
79 __slots__ = ('aliases', 'allow_missing_manifest', 'allow_provide_virtual',
80 'cache_formats', 'create_manifest', 'disable_manifest', 'eapi',
81 'eclass_db', 'eclass_locations', 'eclass_overrides',
82 'find_invalid_path_char', 'force', 'format', 'local_config', 'location',
83 'main_repo', 'manifest_hashes', 'masters', 'missing_repo_name',
84 'name', 'portage1_profiles', 'portage1_profiles_compat', 'priority',
85 'profile_formats', 'sign_commit', 'sign_manifest', 'sync_cvs_repo',
86 'sync_type', 'sync_uri', 'thin_manifest', 'update_changelog',
87 'user_location', '_eapis_banned', '_eapis_deprecated')
89 def __init__(self, name, repo_opts, local_config=True):
90 """Build a RepoConfig with options in repo_opts
91 Try to read repo_name in repository location, but if
92 it is not found use variable name as repository name"""
94 force = repo_opts.get('force')
96 force = tuple(force.split())
101 self.local_config = local_config
103 if local_config or 'aliases' in force:
104 aliases = repo_opts.get('aliases')
105 if aliases is not None:
106 aliases = tuple(aliases.split())
110 self.aliases = aliases
112 if local_config or 'eclass-overrides' in force:
113 eclass_overrides = repo_opts.get('eclass-overrides')
114 if eclass_overrides is not None:
115 eclass_overrides = tuple(eclass_overrides.split())
117 eclass_overrides = None
119 self.eclass_overrides = eclass_overrides
120 # Eclass databases and locations are computed later.
121 self.eclass_db = None
122 self.eclass_locations = None
124 if local_config or 'masters' in force:
125 # Masters from repos.conf override layout.conf.
126 masters = repo_opts.get('masters')
127 if masters is not None:
128 masters = tuple(masters.split())
132 self.masters = masters
134 #The main-repo key makes only sense for the 'DEFAULT' section.
135 self.main_repo = repo_opts.get('main-repo')
137 priority = repo_opts.get('priority')
138 if priority is not None:
140 priority = int(priority)
143 self.priority = priority
145 sync_cvs_repo = repo_opts.get('sync-cvs-repo')
146 if sync_cvs_repo is not None:
147 sync_cvs_repo = sync_cvs_repo.strip()
148 self.sync_cvs_repo = sync_cvs_repo or None
150 sync_type = repo_opts.get('sync-type')
151 if sync_type is not None:
152 sync_type = sync_type.strip()
153 self.sync_type = sync_type or None
155 sync_uri = repo_opts.get('sync-uri')
156 if sync_uri is not None:
157 sync_uri = sync_uri.strip()
158 self.sync_uri = sync_uri or None
161 format = repo_opts.get('format')
162 if format is not None:
163 format = format.strip()
166 location = repo_opts.get('location')
167 self.user_location = location
168 if location is not None and location.strip():
169 if os.path.isdir(location) or portage._sync_disabled_warnings:
170 location = os.path.realpath(location)
173 self.location = location
178 if self.location is not None:
179 if os.path.isdir(location):
180 eapi = read_corresponding_eapi_file(os.path.join(self.location, REPO_NAME_LOC))
181 self.name, missing = self._read_valid_repo_name(self.location)
183 missing = not portage._sync_disabled_warnings
184 elif name == "DEFAULT":
188 self.missing_repo_name = missing
189 # sign_commit is disabled by default, since it requires Git >=1.7.9,
190 # and key_id configured by `git config user.signingkey key_id`
191 self.sign_commit = False
192 self.sign_manifest = True
193 self.thin_manifest = False
194 self.allow_missing_manifest = False
195 self.allow_provide_virtual = False
196 self.create_manifest = True
197 self.disable_manifest = False
198 self.manifest_hashes = None
199 self.update_changelog = False
200 self.cache_formats = None
201 self.portage1_profiles = True
202 self.portage1_profiles_compat = False
203 self.find_invalid_path_char = _find_invalid_path_char
207 layout_data = parse_layout_conf(self.location, self.name)[0]
209 # layout.conf masters may be overridden here if we have a masters
210 # setting from the user's repos.conf
211 if self.masters is None:
212 self.masters = layout_data['masters']
214 if (local_config or 'aliases' in force) and layout_data['aliases']:
215 aliases = self.aliases
218 # repos.conf aliases come after layout.conf aliases, giving
219 # them the ability to do incremental overrides
220 self.aliases = layout_data['aliases'] + tuple(aliases)
222 if layout_data['repo-name']:
223 # allow layout.conf to override repository name
224 # useful when having two copies of the same repo enabled
225 # to avoid modifying profiles/repo_name in one of them
226 self.name = layout_data['repo-name']
228 for value in ('allow-missing-manifest',
229 'allow-provide-virtual', 'cache-formats',
230 'create-manifest', 'disable-manifest', 'manifest-hashes',
232 'sign-commit', 'sign-manifest', 'thin-manifest', 'update-changelog'):
233 setattr(self, value.lower().replace("-", "_"), layout_data[value])
235 self.portage1_profiles = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \
236 any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats'])
237 self.portage1_profiles_compat = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \
238 layout_data['profile-formats'] == ('portage-1-compat',)
240 self._eapis_banned = frozenset(layout_data['eapis-banned'])
241 self._eapis_deprecated = frozenset(layout_data['eapis-deprecated'])
243 def eapi_is_banned(self, eapi):
244 return eapi in self._eapis_banned
246 def eapi_is_deprecated(self, eapi):
247 return eapi in self._eapis_deprecated
249 def iter_pregenerated_caches(self, auxdbkeys, readonly=True, force=False):
251 Reads layout.conf cache-formats from left to right and yields cache
252 instances for each supported type that's found. If no cache-formats
253 are specified in layout.conf, 'pms' type is assumed if the
254 metadata/cache directory exists or force is True.
256 formats = self.cache_formats
260 # The default egencache format was 'pms' prior to portage-2.1.11.32
261 # (portage versions prior to portage-2.1.11.14 will NOT
262 # recognize md5-dict format unless it is explicitly listed in
264 formats = ('md5-dict',)
269 from portage.cache.metadata import database
270 name = 'metadata/cache'
271 elif fmt == 'md5-dict':
272 from portage.cache.flat_hash import md5_database as database
273 name = 'metadata/md5-cache'
276 yield database(self.location, name,
277 auxdbkeys, readonly=readonly)
279 def get_pregenerated_cache(self, auxdbkeys, readonly=True, force=False):
281 Returns the first cache instance yielded from
282 iter_pregenerated_caches(), or None if no cache is available or none
283 of the available formats are supported.
285 return next(self.iter_pregenerated_caches(
286 auxdbkeys, readonly=readonly, force=force), None)
288 def load_manifest(self, *args, **kwds):
289 kwds['thin'] = self.thin_manifest
290 kwds['allow_missing'] = self.allow_missing_manifest
291 kwds['allow_create'] = self.create_manifest
292 kwds['hashes'] = self.manifest_hashes
293 if self.disable_manifest:
294 kwds['from_scratch'] = True
295 kwds['find_invalid_path_char'] = self.find_invalid_path_char
296 return manifest.Manifest(*args, **portage._native_kwargs(kwds))
298 def update(self, new_repo):
299 """Update repository with options in another RepoConfig"""
301 keys = set(self.__slots__)
302 keys.discard("missing_repo_name")
304 v = getattr(new_repo, k, None)
308 if new_repo.name is not None:
309 self.missing_repo_name = new_repo.missing_repo_name
312 def _read_valid_repo_name(repo_path):
313 name, missing = RepoConfig._read_repo_name(repo_path)
314 # We must ensure that the name conforms to PMS 3.1.5
315 # in order to avoid InvalidAtom exceptions when we
316 # use it to generate atoms.
317 name = _gen_valid_repo(name)
319 # name only contains invalid characters
320 name = "x-" + os.path.basename(repo_path)
321 name = _gen_valid_repo(name)
322 # If basename only contains whitespace then the
323 # end result is name = 'x-'.
327 def _read_repo_name(repo_path):
329 Read repo_name from repo_path.
330 Returns repo_name, missing.
332 repo_name_path = os.path.join(repo_path, REPO_NAME_LOC)
336 _unicode_encode(repo_name_path,
337 encoding=_encodings['fs'], errors='strict'),
338 mode='r', encoding=_encodings['repo.content'],
340 return f.readline().strip(), False
341 except EnvironmentError:
342 return "x-" + os.path.basename(repo_path), True
347 def info_string(self):
349 Returns a formatted string containing informations about the repository.
350 Used by emerge --info.
354 repo_msg.append(self.name)
356 repo_msg.append(indent + "format: " + self.format)
357 if self.user_location:
358 repo_msg.append(indent + "location: " + self.user_location)
359 if self.sync_cvs_repo:
360 repo_msg.append(indent + "sync-cvs-repo: " + self.sync_cvs_repo)
362 repo_msg.append(indent + "sync-type: " + self.sync_type)
364 repo_msg.append(indent + "sync-uri: " + self.sync_uri)
366 repo_msg.append(indent + "masters: " + " ".join(master.name for master in self.masters))
367 if self.priority is not None:
368 repo_msg.append(indent + "priority: " + str(self.priority))
370 repo_msg.append(indent + "aliases: " + " ".join(self.aliases))
371 if self.eclass_overrides:
372 repo_msg.append(indent + "eclass-overrides: " + \
373 " ".join(self.eclass_overrides))
375 return "\n".join(repo_msg)
378 return "<portage.repository.config.RepoConfig(name=%r, location=%r)>" % (self.name, _unicode_decode(self.location))
382 for k in self.__slots__:
383 d[k] = getattr(self, k, None)
386 if sys.hexversion < 0x3000000:
388 __unicode__ = __str__
391 return _unicode_encode(self.__unicode__())
393 class RepoConfigLoader(object):
394 """Loads and store config of several repositories, loaded from PORTDIR_OVERLAY or repos.conf"""
397 def _add_repositories(portdir, portdir_overlay, prepos,
398 ignored_map, ignored_location_map, local_config, default_portdir):
399 """Add overlays in PORTDIR_OVERLAY as repositories"""
403 portdir = normalize_path(portdir)
404 portdir_orig = portdir
405 overlays.append(portdir)
407 port_ov = [normalize_path(i) for i in shlex_split(portdir_overlay)]
408 except ValueError as e:
409 #File "/usr/lib/python3.2/shlex.py", line 168, in read_token
410 # raise ValueError("No closing quotation")
411 writemsg(_("!!! Invalid PORTDIR_OVERLAY:"
412 " %s: %s\n") % (e, portdir_overlay), noiselevel=-1)
414 overlays.extend(port_ov)
415 default_repo_opts = {}
416 if prepos['DEFAULT'].aliases is not None:
417 default_repo_opts['aliases'] = \
418 ' '.join(prepos['DEFAULT'].aliases)
419 if prepos['DEFAULT'].eclass_overrides is not None:
420 default_repo_opts['eclass-overrides'] = \
421 ' '.join(prepos['DEFAULT'].eclass_overrides)
422 if prepos['DEFAULT'].masters is not None:
423 default_repo_opts['masters'] = \
424 ' '.join(prepos['DEFAULT'].masters)
427 # We need a copy of the original repos.conf data, since we're
428 # going to modify the prepos dict and some of the RepoConfig
429 # objects that we put in prepos may have to be discarded if
430 # they get overridden by a repository with the same name but
431 # a different location. This is common with repoman, for example,
432 # when temporarily overriding an rsync repo with another copy
433 # of the same repo from CVS.
434 repos_conf = prepos.copy()
435 #overlay priority is negative because we want them to be looked before any other repo
438 if isdir_raise_eaccess(ov):
439 repo_opts = default_repo_opts.copy()
440 repo_opts['location'] = ov
441 repo = RepoConfig(None, repo_opts, local_config=local_config)
442 # repos_conf_opts contains options from repos.conf
443 repos_conf_opts = repos_conf.get(repo.name)
444 if repos_conf_opts is not None:
445 # Selectively copy only the attributes which
446 # repos.conf is allowed to override.
447 for k in ('aliases', 'eclass_overrides', 'force', 'masters',
448 'priority', 'sync_cvs_repo', 'sync_type', 'sync_uri'):
449 v = getattr(repos_conf_opts, k, None)
453 if repo.name in prepos:
454 old_location = prepos[repo.name].location
455 if old_location is not None and \
456 old_location != repo.location and \
457 not (old_location == default_portdir and
458 not exists_raise_eaccess(old_location)):
459 ignored_map.setdefault(repo.name, []).append(old_location)
460 ignored_location_map[old_location] = repo.name
461 if old_location == portdir:
462 portdir = repo.user_location
464 if repo.priority is None:
465 if base_priority == 0 and ov == portdir_orig:
466 # If it's the original PORTDIR setting and it's not
467 # in PORTDIR_OVERLAY, then it will be assigned a
468 # special priority setting later.
471 repo.priority = base_priority
474 prepos[repo.name] = repo
477 if not portage._sync_disabled_warnings:
478 writemsg(_("!!! Invalid PORTDIR_OVERLAY (not a dir): '%s'\n") % ov, noiselevel=-1)
483 def _parse(paths, prepos, ignored_map, ignored_location_map, local_config, portdir):
484 """Parse files in paths to load config"""
485 parser = SafeConfigParser()
487 # use read_file/readfp in order to control decoding of unicode
490 read_file = parser.read_file
491 source_kwarg = 'source'
492 except AttributeError:
493 read_file = parser.readfp
494 source_kwarg = 'filename'
498 if isinstance(p, basestring):
499 recursive_paths.extend(_recursive_file_list(p))
501 recursive_paths.append(p)
503 for p in recursive_paths:
504 if isinstance(p, basestring):
507 f = io.open(_unicode_encode(p,
508 encoding=_encodings['fs'], errors='strict'),
509 mode='r', encoding=_encodings['repo.content'],
511 except EnvironmentError:
514 # The 'source' keyword argument is needed since otherwise
515 # ConfigParser in Python <3.3.3 may throw a TypeError
516 # because it assumes that f.name is a native string rather
517 # than binary when constructing error messages.
518 kwargs = {source_kwarg: p}
519 read_file(f, **portage._native_kwargs(kwargs))
523 elif isinstance(p, io.StringIO):
524 kwargs = {source_kwarg: "<io.StringIO>"}
525 read_file(p, **portage._native_kwargs(kwargs))
527 raise TypeError("Unsupported type %r of element %r of 'paths' argument" % (type(p), p))
529 prepos['DEFAULT'] = RepoConfig("DEFAULT",
530 parser.defaults(), local_config=local_config)
532 for sname in parser.sections():
534 for oname in parser.options(sname):
535 optdict[oname] = parser.get(sname, oname)
537 repo = RepoConfig(sname, optdict, local_config=local_config)
539 if repo.sync_type is not None and repo.sync_uri is None:
540 writemsg_level("!!! %s\n" % _("Repository '%s' has sync-type attribute, but is missing sync-uri attribute") %
541 sname, level=logging.ERROR, noiselevel=-1)
544 if repo.sync_uri is not None and repo.sync_type is None:
545 writemsg_level("!!! %s\n" % _("Repository '%s' has sync-uri attribute, but is missing sync-type attribute") %
546 sname, level=logging.ERROR, noiselevel=-1)
549 if repo.sync_type not in (None, "cvs", "git", "rsync"):
550 writemsg_level("!!! %s\n" % _("Repository '%s' has sync-type attribute set to unsupported value: '%s'") %
551 (sname, repo.sync_type), level=logging.ERROR, noiselevel=-1)
554 if repo.sync_type == "cvs" and repo.sync_cvs_repo is None:
555 writemsg_level("!!! %s\n" % _("Repository '%s' has sync-type=cvs, but is missing sync-cvs-repo attribute") %
556 sname, level=logging.ERROR, noiselevel=-1)
559 # For backward compatibility with locations set via PORTDIR and
560 # PORTDIR_OVERLAY, delay validation of the location and repo.name
561 # until after PORTDIR and PORTDIR_OVERLAY have been processed.
564 def __init__(self, paths, settings):
565 """Load config from files in paths"""
571 ignored_location_map = {}
573 if "PORTAGE_REPOSITORIES" in settings:
577 portdir = settings.get("PORTDIR", "")
578 portdir_overlay = settings.get("PORTDIR_OVERLAY", "")
581 self._parse(paths, prepos, ignored_map,
582 ignored_location_map, settings.local_config,
584 except ConfigParserError as e:
586 _("!!! Error while reading repo config file: %s\n") % e,
588 # The configparser state is unreliable (prone to quirky
589 # exceptions) after it has thrown an error, so use empty
590 # config and try to fall back to PORTDIR{,_OVERLAY}.
592 prepos['DEFAULT'] = RepoConfig('DEFAULT',
593 {}, local_config=settings.local_config)
597 ignored_location_map.clear()
599 default_portdir = os.path.join(os.sep,
600 settings['EPREFIX'].lstrip(os.sep), 'usr', 'portage')
602 # If PORTDIR_OVERLAY contains a repo with the same repo_name as
603 # PORTDIR, then PORTDIR is overridden.
604 portdir = self._add_repositories(portdir, portdir_overlay, prepos,
605 ignored_map, ignored_location_map, settings.local_config,
607 if portdir and portdir.strip():
608 portdir = os.path.realpath(portdir)
610 ignored_repos = tuple((repo_name, tuple(paths)) \
611 for repo_name, paths in ignored_map.items())
613 self.missing_repo_names = frozenset(repo.location
614 for repo in prepos.values()
615 if repo.location is not None and repo.missing_repo_name)
617 # Do this before expanding aliases, so that location_map and
618 # treemap consistently map unaliased names whenever available.
619 for repo_name, repo in list(prepos.items()):
620 if repo.location is None:
621 if repo_name != 'DEFAULT':
622 # Skip this warning for repoman (bug #474578).
623 if settings.local_config and paths:
624 writemsg_level(_("Location undefined for " \
625 "repository '%s' referenced in '%s'\n") % \
626 (repo.name, (paths if len(paths) > 1 else paths[0])),
627 level=logging.ERROR, noiselevel=-1)
628 del prepos[repo_name]
630 if repo.name != repo_name and not portage._sync_disabled_warnings:
631 writemsg_level("!!! %s\n" % _("Section name '%s' set in repos.conf differs from name '%s' set inside repository") %
632 (repo_name, repo.name), level=logging.ERROR, noiselevel=-1)
633 del prepos[repo_name]
636 if repo.location and not exists_raise_eaccess(repo.location) and not portage._sync_disabled_warnings:
637 writemsg_level("!!! %s\n" % _("Repository '%s' has location attribute set to nonexistent directory: '%s'") %
638 (repo_name, repo.location), level=logging.ERROR, noiselevel=-1)
639 del prepos[repo_name]
642 location_map[repo.location] = repo_name
643 treemap[repo_name] = repo.location
645 # Add alias mappings, but never replace unaliased mappings.
646 for repo_name, repo in list(prepos.items()):
650 aliases = stack_lists([repo.aliases], incremental=True)
651 names.update(aliases)
654 if name in prepos and prepos[name].location is not None:
655 if name == repo_name:
656 # unaliased names already handled earlier
658 writemsg_level(_("!!! Repository name or alias '%s', " + \
659 "defined for repository '%s', overrides " + \
660 "existing alias or repository.\n") % (name, repo_name), level=logging.WARNING, noiselevel=-1)
661 # Never replace an unaliased mapping with
662 # an aliased mapping.
665 if repo.location is not None:
666 if repo.location not in location_map:
667 # Never replace an unaliased mapping with
668 # an aliased mapping.
669 location_map[repo.location] = name
670 treemap[name] = repo.location
672 main_repo = prepos['DEFAULT'].main_repo
673 if main_repo is None or main_repo not in prepos:
674 #setting main_repo if it was not set in repos.conf
675 main_repo = location_map.get(portdir)
676 if main_repo is not None:
677 prepos['DEFAULT'].main_repo = main_repo
679 prepos['DEFAULT'].main_repo = None
680 if not portage._sync_disabled_warnings:
681 writemsg(_("!!! main-repo not set in DEFAULT and PORTDIR is empty.\n"), noiselevel=-1)
683 if main_repo is not None and prepos[main_repo].priority is None:
684 # This happens if main-repo has been set in repos.conf.
685 prepos[main_repo].priority = -1000
687 # Include repo.name in sort key, for predictable sorting
688 # even when priorities are equal.
689 prepos_order = sorted(prepos.items(),
690 key=lambda r:(r[1].priority or 0, r[1].name))
692 # filter duplicates from aliases, by only including
693 # items where repo.name == key
694 prepos_order = [repo.name for (key, repo) in prepos_order
695 if repo.name == key and key != 'DEFAULT' and
696 repo.location is not None]
699 self.prepos_order = prepos_order
700 self.ignored_repos = ignored_repos
701 self.location_map = location_map
702 self.treemap = treemap
703 self._prepos_changed = True
704 self._repo_location_list = []
706 #The 'masters' key currently contains repo names. Replace them with the matching RepoConfig.
707 for repo_name, repo in prepos.items():
708 if repo_name == "DEFAULT":
710 if repo.masters is None:
711 if self.mainRepo() and repo_name != self.mainRepo().name:
712 repo.masters = self.mainRepo(),
716 if repo.masters and isinstance(repo.masters[0], RepoConfig):
717 # This one has already been processed
718 # because it has an alias.
721 for master_name in repo.masters:
722 if master_name not in prepos:
723 layout_filename = os.path.join(repo.user_location,
724 "metadata", "layout.conf")
725 writemsg_level(_("Unavailable repository '%s' " \
726 "referenced by masters entry in '%s'\n") % \
727 (master_name, layout_filename),
728 level=logging.ERROR, noiselevel=-1)
730 master_repos.append(prepos[master_name])
731 repo.masters = tuple(master_repos)
733 #The 'eclass_overrides' key currently contains repo names. Replace them with the matching repo paths.
734 for repo_name, repo in prepos.items():
735 if repo_name == "DEFAULT":
738 eclass_locations = []
739 eclass_locations.extend(master_repo.location for master_repo in repo.masters)
740 # Only append the current repo to eclass_locations if it's not
741 # there already. This allows masters to have more control over
742 # eclass override order, which may be useful for scenarios in
743 # which there is a plan to migrate eclasses to a master repo.
744 if repo.location not in eclass_locations:
745 eclass_locations.append(repo.location)
747 if repo.eclass_overrides:
748 for other_repo_name in repo.eclass_overrides:
749 if other_repo_name in self.treemap:
750 eclass_locations.append(self.get_location_for_name(other_repo_name))
752 writemsg_level(_("Unavailable repository '%s' " \
753 "referenced by eclass-overrides entry for " \
754 "'%s'\n") % (other_repo_name, repo_name), \
755 level=logging.ERROR, noiselevel=-1)
756 repo.eclass_locations = tuple(eclass_locations)
759 for repo_name, repo in prepos.items():
760 if repo_name == "DEFAULT":
764 for eclass_location in repo.eclass_locations:
765 tree_db = eclass_dbs.get(eclass_location)
767 tree_db = eclass_cache.cache(eclass_location)
768 eclass_dbs[eclass_location] = tree_db
769 if eclass_db is None:
770 eclass_db = tree_db.copy()
772 eclass_db.append(tree_db)
773 repo.eclass_db = eclass_db
775 for repo_name, repo in prepos.items():
776 if repo_name == "DEFAULT":
779 if parse_layout_conf(repo.location, repo_name)[0]["masters"] is None and self.mainRepo() and \
780 repo_name != self.mainRepo().name and not portage._sync_disabled_warnings:
781 writemsg_level("!!! %s\n" % _("Repository '%s' is missing masters attribute in '%s'") %
782 (repo_name, os.path.join(repo.location, "metadata", "layout.conf")) +
783 "!!! %s\n" % _("Set 'masters = %s' in this file for future compatibility") %
784 self.mainRepo().name, level=logging.WARNING, noiselevel=-1)
786 self._prepos_changed = True
787 self._repo_location_list = []
789 self._check_locations()
791 def repoLocationList(self):
792 """Get a list of repositories location. Replaces PORTDIR_OVERLAY"""
793 if self._prepos_changed:
794 _repo_location_list = []
795 for repo in self.prepos_order:
796 if self.prepos[repo].location is not None:
797 _repo_location_list.append(self.prepos[repo].location)
798 self._repo_location_list = tuple(_repo_location_list)
800 self._prepos_changed = False
801 return self._repo_location_list
803 def repoUserLocationList(self):
804 """Get a list of repositories location. Replaces PORTDIR_OVERLAY"""
805 user_location_list = []
806 for repo in self.prepos_order:
807 if self.prepos[repo].location is not None:
808 user_location_list.append(self.prepos[repo].user_location)
809 return tuple(user_location_list)
811 def mainRepoLocation(self):
812 """Returns the location of main repo"""
813 main_repo = self.prepos['DEFAULT'].main_repo
814 if main_repo is not None and main_repo in self.prepos:
815 return self.prepos[main_repo].location
820 """Returns the main repo"""
821 main_repo = self.prepos['DEFAULT'].main_repo
822 if main_repo is None:
824 return self.prepos[main_repo]
826 def _check_locations(self):
827 """Check if repositories location are correct and show a warning message if not"""
828 for (name, r) in self.prepos.items():
829 if name != 'DEFAULT':
830 if r.location is None:
831 writemsg(_("!!! Location not set for repository %s\n") % name, noiselevel=-1)
833 if not isdir_raise_eaccess(r.location) and not portage._sync_disabled_warnings:
834 self.prepos_order.remove(name)
835 writemsg(_("!!! Invalid Repository Location"
836 " (not a dir): '%s'\n") % r.location, noiselevel=-1)
838 def repos_with_profiles(self):
839 for repo_name in self.prepos_order:
840 repo = self.prepos[repo_name]
841 if repo.format != "unavailable":
844 def get_name_for_location(self, location):
845 return self.location_map[location]
847 def get_location_for_name(self, repo_name):
848 if repo_name is None:
849 # This simplifies code in places where
850 # we want to be able to pass in Atom.repo
851 # even if it is None.
853 return self.treemap[repo_name]
855 def get_repo_for_location(self, location):
856 return self.prepos[self.get_name_for_location(location)]
858 def __setitem__(self, repo_name, repo):
859 # self.prepos[repo_name] = repo
860 raise NotImplementedError
862 def __getitem__(self, repo_name):
863 return self.prepos[repo_name]
865 def __delitem__(self, repo_name):
866 location = self.prepos[repo_name].location
867 del self.prepos[repo_name]
868 if repo_name in self.prepos_order:
869 self.prepos_order.remove(repo_name)
870 for k, v in self.location_map.copy().items():
872 del self.location_map[k]
873 if repo_name in self.treemap:
874 del self.treemap[repo_name]
875 self._repo_location_list = tuple(x for x in self._repo_location_list if x != location)
878 for repo_name in self.prepos_order:
879 yield self.prepos[repo_name]
881 def __contains__(self, repo_name):
882 return repo_name in self.prepos
884 def config_string(self):
885 str_or_int_keys = ("format", "location", "main_repo", "priority", "sync_cvs_repo", "sync_type", "sync_uri")
886 str_tuple_keys = ("aliases", "eclass_overrides", "force")
887 repo_config_tuple_keys = ("masters",)
888 keys = str_or_int_keys + str_tuple_keys + repo_config_tuple_keys
890 for repo_name, repo in sorted(self.prepos.items()):
891 config_string += "\n[%s]\n" % repo_name
892 for key in sorted(keys):
893 if key == "main_repo" and repo_name != "DEFAULT":
895 if getattr(repo, key) is not None:
896 if key in str_or_int_keys:
897 config_string += "%s = %s\n" % (key.replace("_", "-"), getattr(repo, key))
898 elif key in str_tuple_keys:
899 config_string += "%s = %s\n" % (key.replace("_", "-"), " ".join(getattr(repo, key)))
900 elif key in repo_config_tuple_keys:
901 config_string += "%s = %s\n" % (key.replace("_", "-"), " ".join(x.name for x in getattr(repo, key)))
902 return config_string.lstrip("\n")
904 def load_repository_config(settings, extra_files=None):
906 if "PORTAGE_REPOSITORIES" in settings:
907 repoconfigpaths.append(io.StringIO(settings["PORTAGE_REPOSITORIES"]))
909 if portage._working_copy:
910 repoconfigpaths.append(os.path.join(PORTAGE_BASE_PATH, "cnf", "repos.conf"))
912 repoconfigpaths.append(os.path.join(settings.global_config_path, "repos.conf"))
913 repoconfigpaths.append(os.path.join(settings["PORTAGE_CONFIGROOT"], USER_CONFIG_PATH, "repos.conf"))
915 repoconfigpaths.extend(extra_files)
916 return RepoConfigLoader(repoconfigpaths, settings)
918 def _get_repo_name(repo_location, cached=None):
919 if cached is not None:
921 name, missing = RepoConfig._read_repo_name(repo_location)
926 def parse_layout_conf(repo_location, repo_name=None):
927 eapi = read_corresponding_eapi_file(os.path.join(repo_location, REPO_NAME_LOC))
929 layout_filename = os.path.join(repo_location, "metadata", "layout.conf")
930 layout_file = KeyValuePairFileLoader(layout_filename, None, None)
931 layout_data, layout_errors = layout_file.load()
935 # None indicates abscence of a masters setting, which later code uses
936 # to trigger a backward compatibility fallback that sets an implicit
937 # master. In order to avoid this fallback behavior, layout.conf can
938 # explicitly set masters to an empty value, which will result in an
939 # empty tuple here instead of None.
940 masters = layout_data.get('masters')
941 if masters is not None:
942 masters = tuple(masters.split())
943 data['masters'] = masters
944 data['aliases'] = tuple(layout_data.get('aliases', '').split())
946 data['allow-provide-virtual'] = \
947 layout_data.get('allow-provide-virtuals', 'false').lower() == 'true'
949 data['eapis-banned'] = tuple(layout_data.get('eapis-banned', '').split())
950 data['eapis-deprecated'] = tuple(layout_data.get('eapis-deprecated', '').split())
952 data['sign-commit'] = layout_data.get('sign-commits', 'false').lower() \
955 data['sign-manifest'] = layout_data.get('sign-manifests', 'true').lower() \
958 data['thin-manifest'] = layout_data.get('thin-manifests', 'false').lower() \
961 data['repo-name'] = _gen_valid_repo(layout_data.get('repo-name', ''))
963 manifest_policy = layout_data.get('use-manifests', 'strict').lower()
964 data['allow-missing-manifest'] = manifest_policy != 'strict'
965 data['create-manifest'] = manifest_policy != 'false'
966 data['disable-manifest'] = manifest_policy == 'false'
968 # for compatibility w/ PMS, fallback to pms; but also check if the
969 # cache exists or not.
970 cache_formats = layout_data.get('cache-formats', '').lower().split()
971 if not cache_formats:
972 # Auto-detect cache formats, and prefer md5-cache if available.
973 # This behavior was deployed in portage-2.1.11.14, so that the
974 # default egencache format could eventually be changed to md5-dict
975 # in portage-2.1.11.32. WARNING: Versions prior to portage-2.1.11.14
976 # will NOT recognize md5-dict format unless it is explicitly
977 # listed in layout.conf.
979 if os.path.isdir(os.path.join(repo_location, 'metadata', 'md5-cache')):
980 cache_formats.append('md5-dict')
981 if os.path.isdir(os.path.join(repo_location, 'metadata', 'cache')):
982 cache_formats.append('pms')
983 data['cache-formats'] = tuple(cache_formats)
985 manifest_hashes = layout_data.get('manifest-hashes')
986 if manifest_hashes is not None:
987 manifest_hashes = frozenset(manifest_hashes.upper().split())
988 if MANIFEST2_REQUIRED_HASH not in manifest_hashes:
989 repo_name = _get_repo_name(repo_location, cached=repo_name)
990 warnings.warn((_("Repository named '%(repo_name)s' has a "
991 "'manifest-hashes' setting that does not contain "
992 "the '%(hash)s' hash which is required by this "
993 "portage version. You will have to upgrade portage "
994 "if you want to generate valid manifests for this "
995 "repository: %(layout_filename)s") %
996 {"repo_name": repo_name or 'unspecified',
997 "hash":MANIFEST2_REQUIRED_HASH,
998 "layout_filename":layout_filename}),
1000 unsupported_hashes = manifest_hashes.difference(
1001 MANIFEST2_HASH_FUNCTIONS)
1002 if unsupported_hashes:
1003 repo_name = _get_repo_name(repo_location, cached=repo_name)
1004 warnings.warn((_("Repository named '%(repo_name)s' has a "
1005 "'manifest-hashes' setting that contains one "
1006 "or more hash types '%(hashes)s' which are not supported by "
1007 "this portage version. You will have to upgrade "
1008 "portage if you want to generate valid manifests for "
1009 "this repository: %(layout_filename)s") %
1010 {"repo_name": repo_name or 'unspecified',
1011 "hashes":" ".join(sorted(unsupported_hashes)),
1012 "layout_filename":layout_filename}),
1014 data['manifest-hashes'] = manifest_hashes
1016 data['update-changelog'] = layout_data.get('update-changelog', 'false').lower() \
1019 raw_formats = layout_data.get('profile-formats')
1020 if raw_formats is None:
1021 if eapi_allows_directories_on_profile_level_and_repository_level(eapi):
1022 raw_formats = ('portage-1',)
1024 raw_formats = ('portage-1-compat',)
1026 raw_formats = set(raw_formats.split())
1027 unknown = raw_formats.difference(_valid_profile_formats)
1029 repo_name = _get_repo_name(repo_location, cached=repo_name)
1030 warnings.warn((_("Repository named '%(repo_name)s' has unsupported "
1031 "profiles in use ('profile-formats = %(unknown_fmts)s' setting in "
1032 "'%(layout_filename)s; please upgrade portage.") %
1033 dict(repo_name=repo_name or 'unspecified',
1034 layout_filename=layout_filename,
1035 unknown_fmts=" ".join(unknown))),
1037 raw_formats = tuple(raw_formats.intersection(_valid_profile_formats))
1038 data['profile-formats'] = raw_formats
1040 return data, layout_errors