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', '_masters_orig')
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
204 self._masters_orig = None
208 layout_data = parse_layout_conf(self.location, self.name)[0]
209 self._masters_orig = layout_data['masters']
211 # layout.conf masters may be overridden here if we have a masters
212 # setting from the user's repos.conf
213 if self.masters is None:
214 self.masters = layout_data['masters']
216 if (local_config or 'aliases' in force) and layout_data['aliases']:
217 aliases = self.aliases
220 # repos.conf aliases come after layout.conf aliases, giving
221 # them the ability to do incremental overrides
222 self.aliases = layout_data['aliases'] + tuple(aliases)
224 if layout_data['repo-name']:
225 # allow layout.conf to override repository name
226 # useful when having two copies of the same repo enabled
227 # to avoid modifying profiles/repo_name in one of them
228 self.name = layout_data['repo-name']
230 for value in ('allow-missing-manifest',
231 'allow-provide-virtual', 'cache-formats',
232 'create-manifest', 'disable-manifest', 'manifest-hashes',
234 'sign-commit', 'sign-manifest', 'thin-manifest', 'update-changelog'):
235 setattr(self, value.lower().replace("-", "_"), layout_data[value])
237 self.portage1_profiles = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \
238 any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats'])
239 self.portage1_profiles_compat = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \
240 layout_data['profile-formats'] == ('portage-1-compat',)
242 self._eapis_banned = frozenset(layout_data['eapis-banned'])
243 self._eapis_deprecated = frozenset(layout_data['eapis-deprecated'])
245 def eapi_is_banned(self, eapi):
246 return eapi in self._eapis_banned
248 def eapi_is_deprecated(self, eapi):
249 return eapi in self._eapis_deprecated
251 def iter_pregenerated_caches(self, auxdbkeys, readonly=True, force=False):
253 Reads layout.conf cache-formats from left to right and yields cache
254 instances for each supported type that's found. If no cache-formats
255 are specified in layout.conf, 'pms' type is assumed if the
256 metadata/cache directory exists or force is True.
258 formats = self.cache_formats
262 # The default egencache format was 'pms' prior to portage-2.1.11.32
263 # (portage versions prior to portage-2.1.11.14 will NOT
264 # recognize md5-dict format unless it is explicitly listed in
266 formats = ('md5-dict',)
271 from portage.cache.metadata import database
272 name = 'metadata/cache'
273 elif fmt == 'md5-dict':
274 from portage.cache.flat_hash import md5_database as database
275 name = 'metadata/md5-cache'
278 yield database(self.location, name,
279 auxdbkeys, readonly=readonly)
281 def get_pregenerated_cache(self, auxdbkeys, readonly=True, force=False):
283 Returns the first cache instance yielded from
284 iter_pregenerated_caches(), or None if no cache is available or none
285 of the available formats are supported.
287 return next(self.iter_pregenerated_caches(
288 auxdbkeys, readonly=readonly, force=force), None)
290 def load_manifest(self, *args, **kwds):
291 kwds['thin'] = self.thin_manifest
292 kwds['allow_missing'] = self.allow_missing_manifest
293 kwds['allow_create'] = self.create_manifest
294 kwds['hashes'] = self.manifest_hashes
295 if self.disable_manifest:
296 kwds['from_scratch'] = True
297 kwds['find_invalid_path_char'] = self.find_invalid_path_char
298 return manifest.Manifest(*args, **portage._native_kwargs(kwds))
300 def update(self, new_repo):
301 """Update repository with options in another RepoConfig"""
303 keys = set(self.__slots__)
304 keys.discard("missing_repo_name")
306 v = getattr(new_repo, k, None)
310 if new_repo.name is not None:
311 self.missing_repo_name = new_repo.missing_repo_name
314 def _read_valid_repo_name(repo_path):
315 name, missing = RepoConfig._read_repo_name(repo_path)
316 # We must ensure that the name conforms to PMS 3.1.5
317 # in order to avoid InvalidAtom exceptions when we
318 # use it to generate atoms.
319 name = _gen_valid_repo(name)
321 # name only contains invalid characters
322 name = "x-" + os.path.basename(repo_path)
323 name = _gen_valid_repo(name)
324 # If basename only contains whitespace then the
325 # end result is name = 'x-'.
329 def _read_repo_name(repo_path):
331 Read repo_name from repo_path.
332 Returns repo_name, missing.
334 repo_name_path = os.path.join(repo_path, REPO_NAME_LOC)
338 _unicode_encode(repo_name_path,
339 encoding=_encodings['fs'], errors='strict'),
340 mode='r', encoding=_encodings['repo.content'],
342 return f.readline().strip(), False
343 except EnvironmentError:
344 return "x-" + os.path.basename(repo_path), True
349 def info_string(self):
351 Returns a formatted string containing informations about the repository.
352 Used by emerge --info.
356 repo_msg.append(self.name)
358 repo_msg.append(indent + "format: " + self.format)
359 if self.user_location:
360 repo_msg.append(indent + "location: " + self.user_location)
361 if self.sync_cvs_repo:
362 repo_msg.append(indent + "sync-cvs-repo: " + self.sync_cvs_repo)
364 repo_msg.append(indent + "sync-type: " + self.sync_type)
366 repo_msg.append(indent + "sync-uri: " + self.sync_uri)
368 repo_msg.append(indent + "masters: " + " ".join(master.name for master in self.masters))
369 if self.priority is not None:
370 repo_msg.append(indent + "priority: " + str(self.priority))
372 repo_msg.append(indent + "aliases: " + " ".join(self.aliases))
373 if self.eclass_overrides:
374 repo_msg.append(indent + "eclass-overrides: " + \
375 " ".join(self.eclass_overrides))
377 return "\n".join(repo_msg)
380 return "<portage.repository.config.RepoConfig(name=%r, location=%r)>" % (self.name, _unicode_decode(self.location))
384 for k in self.__slots__:
385 d[k] = getattr(self, k, None)
388 if sys.hexversion < 0x3000000:
390 __unicode__ = __str__
393 return _unicode_encode(self.__unicode__())
395 class RepoConfigLoader(object):
396 """Loads and store config of several repositories, loaded from PORTDIR_OVERLAY or repos.conf"""
399 def _add_repositories(portdir, portdir_overlay, prepos,
400 ignored_map, ignored_location_map, local_config, default_portdir):
401 """Add overlays in PORTDIR_OVERLAY as repositories"""
405 portdir = normalize_path(portdir)
406 portdir_orig = portdir
407 overlays.append(portdir)
409 port_ov = [normalize_path(i) for i in shlex_split(portdir_overlay)]
410 except ValueError as e:
411 #File "/usr/lib/python3.2/shlex.py", line 168, in read_token
412 # raise ValueError("No closing quotation")
413 writemsg(_("!!! Invalid PORTDIR_OVERLAY:"
414 " %s: %s\n") % (e, portdir_overlay), noiselevel=-1)
416 overlays.extend(port_ov)
417 default_repo_opts = {}
418 if prepos['DEFAULT'].aliases is not None:
419 default_repo_opts['aliases'] = \
420 ' '.join(prepos['DEFAULT'].aliases)
421 if prepos['DEFAULT'].eclass_overrides is not None:
422 default_repo_opts['eclass-overrides'] = \
423 ' '.join(prepos['DEFAULT'].eclass_overrides)
424 if prepos['DEFAULT'].masters is not None:
425 default_repo_opts['masters'] = \
426 ' '.join(prepos['DEFAULT'].masters)
429 # We need a copy of the original repos.conf data, since we're
430 # going to modify the prepos dict and some of the RepoConfig
431 # objects that we put in prepos may have to be discarded if
432 # they get overridden by a repository with the same name but
433 # a different location. This is common with repoman, for example,
434 # when temporarily overriding an rsync repo with another copy
435 # of the same repo from CVS.
436 repos_conf = prepos.copy()
437 #overlay priority is negative because we want them to be looked before any other repo
440 if isdir_raise_eaccess(ov):
441 repo_opts = default_repo_opts.copy()
442 repo_opts['location'] = ov
443 repo = RepoConfig(None, repo_opts, local_config=local_config)
444 # repos_conf_opts contains options from repos.conf
445 repos_conf_opts = repos_conf.get(repo.name)
446 if repos_conf_opts is not None:
447 # Selectively copy only the attributes which
448 # repos.conf is allowed to override.
449 for k in ('aliases', 'eclass_overrides', 'force', 'masters',
450 'priority', 'sync_cvs_repo', 'sync_type', 'sync_uri'):
451 v = getattr(repos_conf_opts, k, None)
455 if repo.name in prepos:
456 old_location = prepos[repo.name].location
457 if old_location is not None and \
458 old_location != repo.location and \
459 not (old_location == default_portdir and
460 not exists_raise_eaccess(old_location)):
461 ignored_map.setdefault(repo.name, []).append(old_location)
462 ignored_location_map[old_location] = repo.name
463 if old_location == portdir:
464 portdir = repo.user_location
466 if repo.priority is None:
467 if base_priority == 0 and ov == portdir_orig:
468 # If it's the original PORTDIR setting and it's not
469 # in PORTDIR_OVERLAY, then it will be assigned a
470 # special priority setting later.
473 repo.priority = base_priority
476 prepos[repo.name] = repo
479 if not portage._sync_disabled_warnings:
480 writemsg(_("!!! Invalid PORTDIR_OVERLAY (not a dir): '%s'\n") % ov, noiselevel=-1)
485 def _parse(paths, prepos, ignored_map, ignored_location_map, local_config, portdir):
486 """Parse files in paths to load config"""
487 parser = SafeConfigParser()
489 # use read_file/readfp in order to control decoding of unicode
492 read_file = parser.read_file
493 source_kwarg = 'source'
494 except AttributeError:
495 read_file = parser.readfp
496 source_kwarg = 'filename'
500 if isinstance(p, basestring):
501 recursive_paths.extend(_recursive_file_list(p))
503 recursive_paths.append(p)
505 for p in recursive_paths:
506 if isinstance(p, basestring):
509 f = io.open(_unicode_encode(p,
510 encoding=_encodings['fs'], errors='strict'),
511 mode='r', encoding=_encodings['repo.content'],
513 except EnvironmentError:
516 # The 'source' keyword argument is needed since otherwise
517 # ConfigParser in Python <3.3.3 may throw a TypeError
518 # because it assumes that f.name is a native string rather
519 # than binary when constructing error messages.
520 kwargs = {source_kwarg: p}
521 read_file(f, **portage._native_kwargs(kwargs))
525 elif isinstance(p, io.StringIO):
526 kwargs = {source_kwarg: "<io.StringIO>"}
527 read_file(p, **portage._native_kwargs(kwargs))
529 raise TypeError("Unsupported type %r of element %r of 'paths' argument" % (type(p), p))
531 prepos['DEFAULT'] = RepoConfig("DEFAULT",
532 parser.defaults(), local_config=local_config)
534 for sname in parser.sections():
536 for oname in parser.options(sname):
537 optdict[oname] = parser.get(sname, oname)
539 repo = RepoConfig(sname, optdict, local_config=local_config)
541 if repo.sync_type is not None and repo.sync_uri is None:
542 writemsg_level("!!! %s\n" % _("Repository '%s' has sync-type attribute, but is missing sync-uri attribute") %
543 sname, level=logging.ERROR, noiselevel=-1)
546 if repo.sync_uri is not None and repo.sync_type is None:
547 writemsg_level("!!! %s\n" % _("Repository '%s' has sync-uri attribute, but is missing sync-type attribute") %
548 sname, level=logging.ERROR, noiselevel=-1)
551 if repo.sync_type not in (None, "cvs", "git", "rsync"):
552 writemsg_level("!!! %s\n" % _("Repository '%s' has sync-type attribute set to unsupported value: '%s'") %
553 (sname, repo.sync_type), level=logging.ERROR, noiselevel=-1)
556 if repo.sync_type == "cvs" and repo.sync_cvs_repo is None:
557 writemsg_level("!!! %s\n" % _("Repository '%s' has sync-type=cvs, but is missing sync-cvs-repo attribute") %
558 sname, level=logging.ERROR, noiselevel=-1)
561 # For backward compatibility with locations set via PORTDIR and
562 # PORTDIR_OVERLAY, delay validation of the location and repo.name
563 # until after PORTDIR and PORTDIR_OVERLAY have been processed.
566 def __init__(self, paths, settings):
567 """Load config from files in paths"""
573 ignored_location_map = {}
575 if "PORTAGE_REPOSITORIES" in settings:
580 portdir = settings.get("PORTDIR", "")
581 portdir_overlay = settings.get("PORTDIR_OVERLAY", "")
582 portdir_sync = settings.get("SYNC", "")
585 self._parse(paths, prepos, ignored_map,
586 ignored_location_map, settings.local_config,
588 except ConfigParserError as e:
590 _("!!! Error while reading repo config file: %s\n") % e,
592 # The configparser state is unreliable (prone to quirky
593 # exceptions) after it has thrown an error, so use empty
594 # config and try to fall back to PORTDIR{,_OVERLAY}.
596 prepos['DEFAULT'] = RepoConfig('DEFAULT',
597 {}, local_config=settings.local_config)
601 ignored_location_map.clear()
603 default_portdir = os.path.join(os.sep,
604 settings['EPREFIX'].lstrip(os.sep), 'usr', 'portage')
606 # If PORTDIR_OVERLAY contains a repo with the same repo_name as
607 # PORTDIR, then PORTDIR is overridden.
608 portdir = self._add_repositories(portdir, portdir_overlay, prepos,
609 ignored_map, ignored_location_map, settings.local_config,
611 if portdir and portdir.strip():
612 portdir = os.path.realpath(portdir)
614 ignored_repos = tuple((repo_name, tuple(paths)) \
615 for repo_name, paths in ignored_map.items())
617 self.missing_repo_names = frozenset(repo.location
618 for repo in prepos.values()
619 if repo.location is not None and repo.missing_repo_name)
621 # Do this before expanding aliases, so that location_map and
622 # treemap consistently map unaliased names whenever available.
623 for repo_name, repo in list(prepos.items()):
624 if repo.location is None:
625 if repo_name != 'DEFAULT':
626 # Skip this warning for repoman (bug #474578).
627 if settings.local_config and paths:
628 writemsg_level("!!! %s\n" % _("Section '%s' in repos.conf is missing location attribute") %
629 repo.name, level=logging.ERROR, noiselevel=-1)
630 del prepos[repo_name]
633 if not portage._sync_disabled_warnings:
634 if not isdir_raise_eaccess(repo.location):
635 writemsg_level("!!! %s\n" % _("Section '%s' in repos.conf has location attribute set "
636 "to nonexistent directory: '%s'") %
637 (repo_name, repo.location), level=logging.ERROR, noiselevel=-1)
638 del prepos[repo_name]
641 # After removing support for PORTDIR_OVERLAY, the following check can be:
642 # if repo.missing_repo_name:
643 if repo.missing_repo_name and repo.name != repo_name:
644 writemsg_level("!!! %s\n" % _("Section '%s' in repos.conf refers to repository "
645 "without repository name set in '%s'") %
646 (repo_name, os.path.join(repo.location, REPO_NAME_LOC)), level=logging.ERROR, noiselevel=-1)
647 del prepos[repo_name]
650 if repo.name != repo_name:
651 writemsg_level("!!! %s\n" % _("Section '%s' in repos.conf has name different "
652 "from repository name '%s' set inside repository") %
653 (repo_name, repo.name), level=logging.ERROR, noiselevel=-1)
654 del prepos[repo_name]
657 location_map[repo.location] = repo_name
658 treemap[repo_name] = repo.location
660 # Add alias mappings, but never replace unaliased mappings.
661 for repo_name, repo in list(prepos.items()):
665 aliases = stack_lists([repo.aliases], incremental=True)
666 names.update(aliases)
669 if name in prepos and prepos[name].location is not None:
670 if name == repo_name:
671 # unaliased names already handled earlier
673 writemsg_level(_("!!! Repository name or alias '%s', " + \
674 "defined for repository '%s', overrides " + \
675 "existing alias or repository.\n") % (name, repo_name), level=logging.WARNING, noiselevel=-1)
676 # Never replace an unaliased mapping with
677 # an aliased mapping.
680 if repo.location is not None:
681 if repo.location not in location_map:
682 # Never replace an unaliased mapping with
683 # an aliased mapping.
684 location_map[repo.location] = name
685 treemap[name] = repo.location
687 main_repo = prepos['DEFAULT'].main_repo
688 if main_repo is None or main_repo not in prepos:
689 #setting main_repo if it was not set in repos.conf
690 main_repo = location_map.get(portdir)
691 if main_repo is not None:
692 prepos['DEFAULT'].main_repo = main_repo
694 prepos['DEFAULT'].main_repo = None
695 if not portage._sync_disabled_warnings:
696 writemsg(_("!!! main-repo not set in DEFAULT and PORTDIR is empty.\n"), noiselevel=-1)
698 if main_repo is not None and prepos[main_repo].priority is None:
699 # This happens if main-repo has been set in repos.conf.
700 prepos[main_repo].priority = -1000
702 # Backward compatible SYNC support for mirrorselect.
703 if portdir_sync and main_repo is not None:
704 if portdir_sync.startswith("rsync://"):
705 prepos[main_repo].sync_uri = portdir_sync
706 prepos[main_repo].sync_type = "rsync"
708 # Include repo.name in sort key, for predictable sorting
709 # even when priorities are equal.
710 prepos_order = sorted(prepos.items(),
711 key=lambda r:(r[1].priority or 0, r[1].name))
713 # filter duplicates from aliases, by only including
714 # items where repo.name == key
715 prepos_order = [repo.name for (key, repo) in prepos_order
716 if repo.name == key and key != 'DEFAULT' and
717 repo.location is not None]
720 self.prepos_order = prepos_order
721 self.ignored_repos = ignored_repos
722 self.location_map = location_map
723 self.treemap = treemap
724 self._prepos_changed = True
725 self._repo_location_list = []
727 #The 'masters' key currently contains repo names. Replace them with the matching RepoConfig.
728 for repo_name, repo in prepos.items():
729 if repo_name == "DEFAULT":
731 if repo.masters is None:
732 if self.mainRepo() and repo_name != self.mainRepo().name:
733 repo.masters = self.mainRepo(),
737 if repo.masters and isinstance(repo.masters[0], RepoConfig):
738 # This one has already been processed
739 # because it has an alias.
742 for master_name in repo.masters:
743 if master_name not in prepos:
744 layout_filename = os.path.join(repo.user_location,
745 "metadata", "layout.conf")
746 writemsg_level(_("Unavailable repository '%s' " \
747 "referenced by masters entry in '%s'\n") % \
748 (master_name, layout_filename),
749 level=logging.ERROR, noiselevel=-1)
751 master_repos.append(prepos[master_name])
752 repo.masters = tuple(master_repos)
754 #The 'eclass_overrides' key currently contains repo names. Replace them with the matching repo paths.
755 for repo_name, repo in prepos.items():
756 if repo_name == "DEFAULT":
759 eclass_locations = []
760 eclass_locations.extend(master_repo.location for master_repo in repo.masters)
761 # Only append the current repo to eclass_locations if it's not
762 # there already. This allows masters to have more control over
763 # eclass override order, which may be useful for scenarios in
764 # which there is a plan to migrate eclasses to a master repo.
765 if repo.location not in eclass_locations:
766 eclass_locations.append(repo.location)
768 if repo.eclass_overrides:
769 for other_repo_name in repo.eclass_overrides:
770 if other_repo_name in self.treemap:
771 eclass_locations.append(self.get_location_for_name(other_repo_name))
773 writemsg_level(_("Unavailable repository '%s' " \
774 "referenced by eclass-overrides entry for " \
775 "'%s'\n") % (other_repo_name, repo_name), \
776 level=logging.ERROR, noiselevel=-1)
777 repo.eclass_locations = tuple(eclass_locations)
780 for repo_name, repo in prepos.items():
781 if repo_name == "DEFAULT":
785 for eclass_location in repo.eclass_locations:
786 tree_db = eclass_dbs.get(eclass_location)
788 tree_db = eclass_cache.cache(eclass_location)
789 eclass_dbs[eclass_location] = tree_db
790 if eclass_db is None:
791 eclass_db = tree_db.copy()
793 eclass_db.append(tree_db)
794 repo.eclass_db = eclass_db
796 for repo_name, repo in prepos.items():
797 if repo_name == "DEFAULT":
800 if repo._masters_orig is None and self.mainRepo() and \
801 repo_name != self.mainRepo().name and not portage._sync_disabled_warnings:
802 writemsg_level("!!! %s\n" % _("Repository '%s' is missing masters attribute in '%s'") %
803 (repo_name, os.path.join(repo.location, "metadata", "layout.conf")) +
804 "!!! %s\n" % _("Set 'masters = %s' in this file for future compatibility") %
805 self.mainRepo().name, level=logging.WARNING, noiselevel=-1)
807 self._prepos_changed = True
808 self._repo_location_list = []
810 self._check_locations()
812 def repoLocationList(self):
813 """Get a list of repositories location. Replaces PORTDIR_OVERLAY"""
814 if self._prepos_changed:
815 _repo_location_list = []
816 for repo in self.prepos_order:
817 if self.prepos[repo].location is not None:
818 _repo_location_list.append(self.prepos[repo].location)
819 self._repo_location_list = tuple(_repo_location_list)
821 self._prepos_changed = False
822 return self._repo_location_list
824 def repoUserLocationList(self):
825 """Get a list of repositories location. Replaces PORTDIR_OVERLAY"""
826 user_location_list = []
827 for repo in self.prepos_order:
828 if self.prepos[repo].location is not None:
829 user_location_list.append(self.prepos[repo].user_location)
830 return tuple(user_location_list)
832 def mainRepoLocation(self):
833 """Returns the location of main repo"""
834 main_repo = self.prepos['DEFAULT'].main_repo
835 if main_repo is not None and main_repo in self.prepos:
836 return self.prepos[main_repo].location
841 """Returns the main repo"""
842 main_repo = self.prepos['DEFAULT'].main_repo
843 if main_repo is None:
845 return self.prepos[main_repo]
847 def _check_locations(self):
848 """Check if repositories location are correct and show a warning message if not"""
849 for (name, r) in self.prepos.items():
850 if name != 'DEFAULT':
851 if r.location is None:
852 writemsg(_("!!! Location not set for repository %s\n") % name, noiselevel=-1)
854 if not isdir_raise_eaccess(r.location) and not portage._sync_disabled_warnings:
855 self.prepos_order.remove(name)
856 writemsg(_("!!! Invalid Repository Location"
857 " (not a dir): '%s'\n") % r.location, noiselevel=-1)
859 def repos_with_profiles(self):
860 for repo_name in self.prepos_order:
861 repo = self.prepos[repo_name]
862 if repo.format != "unavailable":
865 def get_name_for_location(self, location):
866 return self.location_map[location]
868 def get_location_for_name(self, repo_name):
869 if repo_name is None:
870 # This simplifies code in places where
871 # we want to be able to pass in Atom.repo
872 # even if it is None.
874 return self.treemap[repo_name]
876 def get_repo_for_location(self, location):
877 return self.prepos[self.get_name_for_location(location)]
879 def __setitem__(self, repo_name, repo):
880 # self.prepos[repo_name] = repo
881 raise NotImplementedError
883 def __getitem__(self, repo_name):
884 return self.prepos[repo_name]
886 def __delitem__(self, repo_name):
887 location = self.prepos[repo_name].location
888 del self.prepos[repo_name]
889 if repo_name in self.prepos_order:
890 self.prepos_order.remove(repo_name)
891 for k, v in self.location_map.copy().items():
893 del self.location_map[k]
894 if repo_name in self.treemap:
895 del self.treemap[repo_name]
896 self._repo_location_list = tuple(x for x in self._repo_location_list if x != location)
899 for repo_name in self.prepos_order:
900 yield self.prepos[repo_name]
902 def __contains__(self, repo_name):
903 return repo_name in self.prepos
905 def config_string(self):
906 str_or_int_keys = ("format", "location", "main_repo", "priority", "sync_cvs_repo", "sync_type", "sync_uri")
907 str_tuple_keys = ("aliases", "eclass_overrides", "force")
908 repo_config_tuple_keys = ("masters",)
909 keys = str_or_int_keys + str_tuple_keys + repo_config_tuple_keys
911 for repo_name, repo in sorted(self.prepos.items()):
912 config_string += "\n[%s]\n" % repo_name
913 for key in sorted(keys):
914 if key == "main_repo" and repo_name != "DEFAULT":
916 if getattr(repo, key) is not None:
917 if key in str_or_int_keys:
918 config_string += "%s = %s\n" % (key.replace("_", "-"), getattr(repo, key))
919 elif key in str_tuple_keys:
920 config_string += "%s = %s\n" % (key.replace("_", "-"), " ".join(getattr(repo, key)))
921 elif key in repo_config_tuple_keys:
922 config_string += "%s = %s\n" % (key.replace("_", "-"), " ".join(x.name for x in getattr(repo, key)))
923 return config_string.lstrip("\n")
925 def load_repository_config(settings, extra_files=None):
927 if "PORTAGE_REPOSITORIES" in settings:
928 repoconfigpaths.append(io.StringIO(settings["PORTAGE_REPOSITORIES"]))
930 if portage._working_copy:
931 repoconfigpaths.append(os.path.join(PORTAGE_BASE_PATH, "cnf", "repos.conf"))
933 repoconfigpaths.append(os.path.join(settings.global_config_path, "repos.conf"))
934 repoconfigpaths.append(os.path.join(settings["PORTAGE_CONFIGROOT"], USER_CONFIG_PATH, "repos.conf"))
936 repoconfigpaths.extend(extra_files)
937 return RepoConfigLoader(repoconfigpaths, settings)
939 def _get_repo_name(repo_location, cached=None):
940 if cached is not None:
942 name, missing = RepoConfig._read_repo_name(repo_location)
947 def parse_layout_conf(repo_location, repo_name=None):
948 eapi = read_corresponding_eapi_file(os.path.join(repo_location, REPO_NAME_LOC))
950 layout_filename = os.path.join(repo_location, "metadata", "layout.conf")
951 layout_file = KeyValuePairFileLoader(layout_filename, None, None)
952 layout_data, layout_errors = layout_file.load()
956 # None indicates abscence of a masters setting, which later code uses
957 # to trigger a backward compatibility fallback that sets an implicit
958 # master. In order to avoid this fallback behavior, layout.conf can
959 # explicitly set masters to an empty value, which will result in an
960 # empty tuple here instead of None.
961 masters = layout_data.get('masters')
962 if masters is not None:
963 masters = tuple(masters.split())
964 data['masters'] = masters
965 data['aliases'] = tuple(layout_data.get('aliases', '').split())
967 data['allow-provide-virtual'] = \
968 layout_data.get('allow-provide-virtuals', 'false').lower() == 'true'
970 data['eapis-banned'] = tuple(layout_data.get('eapis-banned', '').split())
971 data['eapis-deprecated'] = tuple(layout_data.get('eapis-deprecated', '').split())
973 data['sign-commit'] = layout_data.get('sign-commits', 'false').lower() \
976 data['sign-manifest'] = layout_data.get('sign-manifests', 'true').lower() \
979 data['thin-manifest'] = layout_data.get('thin-manifests', 'false').lower() \
982 data['repo-name'] = _gen_valid_repo(layout_data.get('repo-name', ''))
984 manifest_policy = layout_data.get('use-manifests', 'strict').lower()
985 data['allow-missing-manifest'] = manifest_policy != 'strict'
986 data['create-manifest'] = manifest_policy != 'false'
987 data['disable-manifest'] = manifest_policy == 'false'
989 # for compatibility w/ PMS, fallback to pms; but also check if the
990 # cache exists or not.
991 cache_formats = layout_data.get('cache-formats', '').lower().split()
992 if not cache_formats:
993 # Auto-detect cache formats, and prefer md5-cache if available.
994 # This behavior was deployed in portage-2.1.11.14, so that the
995 # default egencache format could eventually be changed to md5-dict
996 # in portage-2.1.11.32. WARNING: Versions prior to portage-2.1.11.14
997 # will NOT recognize md5-dict format unless it is explicitly
998 # listed in layout.conf.
1000 if os.path.isdir(os.path.join(repo_location, 'metadata', 'md5-cache')):
1001 cache_formats.append('md5-dict')
1002 if os.path.isdir(os.path.join(repo_location, 'metadata', 'cache')):
1003 cache_formats.append('pms')
1004 data['cache-formats'] = tuple(cache_formats)
1006 manifest_hashes = layout_data.get('manifest-hashes')
1007 if manifest_hashes is not None:
1008 manifest_hashes = frozenset(manifest_hashes.upper().split())
1009 if MANIFEST2_REQUIRED_HASH not in manifest_hashes:
1010 repo_name = _get_repo_name(repo_location, cached=repo_name)
1011 warnings.warn((_("Repository named '%(repo_name)s' has a "
1012 "'manifest-hashes' setting that does not contain "
1013 "the '%(hash)s' hash which is required by this "
1014 "portage version. You will have to upgrade portage "
1015 "if you want to generate valid manifests for this "
1016 "repository: %(layout_filename)s") %
1017 {"repo_name": repo_name or 'unspecified',
1018 "hash":MANIFEST2_REQUIRED_HASH,
1019 "layout_filename":layout_filename}),
1021 unsupported_hashes = manifest_hashes.difference(
1022 MANIFEST2_HASH_FUNCTIONS)
1023 if unsupported_hashes:
1024 repo_name = _get_repo_name(repo_location, cached=repo_name)
1025 warnings.warn((_("Repository named '%(repo_name)s' has a "
1026 "'manifest-hashes' setting that contains one "
1027 "or more hash types '%(hashes)s' which are not supported by "
1028 "this portage version. You will have to upgrade "
1029 "portage if you want to generate valid manifests for "
1030 "this repository: %(layout_filename)s") %
1031 {"repo_name": repo_name or 'unspecified',
1032 "hashes":" ".join(sorted(unsupported_hashes)),
1033 "layout_filename":layout_filename}),
1035 data['manifest-hashes'] = manifest_hashes
1037 data['update-changelog'] = layout_data.get('update-changelog', 'false').lower() \
1040 raw_formats = layout_data.get('profile-formats')
1041 if raw_formats is None:
1042 if eapi_allows_directories_on_profile_level_and_repository_level(eapi):
1043 raw_formats = ('portage-1',)
1045 raw_formats = ('portage-1-compat',)
1047 raw_formats = set(raw_formats.split())
1048 unknown = raw_formats.difference(_valid_profile_formats)
1050 repo_name = _get_repo_name(repo_location, cached=repo_name)
1051 warnings.warn((_("Repository named '%(repo_name)s' has unsupported "
1052 "profiles in use ('profile-formats = %(unknown_fmts)s' setting in "
1053 "'%(layout_filename)s; please upgrade portage.") %
1054 dict(repo_name=repo_name or 'unspecified',
1055 layout_filename=layout_filename,
1056 unknown_fmts=" ".join(unknown))),
1058 raw_formats = tuple(raw_formats.intersection(_valid_profile_formats))
1059 data['profile-formats'] = raw_formats
1061 return data, layout_errors