def parse_layout_conf(repo_location, repo_name=None): eapi = read_corresponding_eapi_file(os.path.join(repo_location, REPO_NAME_LOC)) layout_filename = os.path.join(repo_location, "metadata", "layout.conf") layout_file = KeyValuePairFileLoader(layout_filename, None, None) layout_data, layout_errors = layout_file.load() data = {} # None indicates abscence of a masters setting, which later code uses # to trigger a backward compatibility fallback that sets an implicit # master. In order to avoid this fallback behavior, layout.conf can # explicitly set masters to an empty value, which will result in an # empty tuple here instead of None. masters = layout_data.get('masters') if masters is not None: masters = tuple(masters.split()) data['masters'] = masters data['aliases'] = tuple(layout_data.get('aliases', '').split()) data['eapis-banned'] = tuple(layout_data.get('eapis-banned', '').split()) data['eapis-deprecated'] = tuple(layout_data.get('eapis-deprecated', '').split()) data['sign-commit'] = layout_data.get('sign-commits', 'false').lower() \ == 'true' data['sign-manifest'] = layout_data.get('sign-manifests', 'true').lower() \ == 'true' data['thin-manifest'] = layout_data.get('thin-manifests', 'false').lower() \ == 'true' data['repo-name'] = _gen_valid_repo(layout_data.get('repo-name', '')) manifest_policy = layout_data.get('use-manifests', 'strict').lower() data['allow-missing-manifest'] = manifest_policy != 'strict' data['create-manifest'] = manifest_policy != 'false' data['disable-manifest'] = manifest_policy == 'false' # for compatibility w/ PMS, fallback to pms; but also check if the # cache exists or not. cache_formats = layout_data.get('cache-formats', '').lower().split() if not cache_formats: # Auto-detect cache formats, and prefer md5-cache if available. # This behavior was deployed in portage-2.1.11.14, so that the # default egencache format could eventually be changed to md5-dict # in portage-2.1.11.32. WARNING: Versions prior to portage-2.1.11.14 # will NOT recognize md5-dict format unless it is explicitly # listed in layout.conf. cache_formats = [] if os.path.isdir(os.path.join(repo_location, 'metadata', 'md5-cache')): cache_formats.append('md5-dict') if os.path.isdir(os.path.join(repo_location, 'metadata', 'cache')): cache_formats.append('pms') data['cache-formats'] = tuple(cache_formats) manifest_hashes = layout_data.get('manifest-hashes') manifest_required_hashes = layout_data.get('manifest-required-hashes') if manifest_required_hashes is not None and manifest_hashes is None: repo_name = _get_repo_name(repo_location, cached=repo_name) warnings.warn((_("Repository named '%(repo_name)s' specifies " "'manifest-required-hashes' setting without corresponding " "'manifest-hashes'. Portage will default it to match " "the required set but please add the missing entry " "to: %(layout_filename)s") % {"repo_name": repo_name or 'unspecified', "layout_filename":layout_filename}), SyntaxWarning) manifest_hashes = manifest_required_hashes if manifest_hashes is not None: # require all the hashes unless specified otherwise if manifest_required_hashes is None: manifest_required_hashes = manifest_hashes manifest_required_hashes = frozenset(manifest_required_hashes.upper().split()) manifest_hashes = frozenset(manifest_hashes.upper().split()) missing_required_hashes = manifest_required_hashes.difference( manifest_hashes) if missing_required_hashes: repo_name = _get_repo_name(repo_location, cached=repo_name) warnings.warn((_("Repository named '%(repo_name)s' has a " "'manifest-hashes' setting that does not contain " "the '%(hash)s' hashes which are listed in " "'manifest-required-hashes'. Please fix that file " "if you want to generate valid manifests for this " "repository: %(layout_filename)s") % {"repo_name": repo_name or 'unspecified', "hash": ' '.join(missing_required_hashes), "layout_filename":layout_filename}), SyntaxWarning) unsupported_hashes = manifest_hashes.difference( get_valid_checksum_keys()) if unsupported_hashes: repo_name = _get_repo_name(repo_location, cached=repo_name) warnings.warn((_("Repository named '%(repo_name)s' has a " "'manifest-hashes' setting that contains one " "or more hash types '%(hashes)s' which are not supported by " "this portage version. You will have to upgrade " "portage if you want to generate valid manifests for " "this repository: %(layout_filename)s") % {"repo_name": repo_name or 'unspecified', "hashes":" ".join(sorted(unsupported_hashes)), "layout_filename":layout_filename}), DeprecationWarning) data['manifest-hashes'] = manifest_hashes data['manifest-required-hashes'] = manifest_required_hashes data['update-changelog'] = layout_data.get('update-changelog', 'false').lower() \ == 'true' raw_formats = layout_data.get('profile-formats') if raw_formats is None: if eapi_allows_directories_on_profile_level_and_repository_level(eapi): raw_formats = ('portage-1',) else: raw_formats = ('portage-1-compat',) else: raw_formats = set(raw_formats.split()) unknown = raw_formats.difference(_valid_profile_formats) if unknown: repo_name = _get_repo_name(repo_location, cached=repo_name) warnings.warn((_("Repository named '%(repo_name)s' has unsupported " "profiles in use ('profile-formats = %(unknown_fmts)s' setting in " "'%(layout_filename)s; please upgrade portage.") % dict(repo_name=repo_name or 'unspecified', layout_filename=layout_filename, unknown_fmts=" ".join(unknown))), DeprecationWarning) raw_formats = tuple(raw_formats.intersection(_valid_profile_formats)) data['profile-formats'] = raw_formats try: eapi = layout_data['profile_eapi_when_unspecified'] except KeyError: pass else: if 'profile-default-eapi' not in raw_formats: warnings.warn((_("Repository named '%(repo_name)s' has " "profile_eapi_when_unspecified setting in " "'%(layout_filename)s', but 'profile-default-eapi' is " "not listed in the profile-formats field. Please " "report this issue to the repository maintainer.") % dict(repo_name=repo_name or 'unspecified', layout_filename=layout_filename)), SyntaxWarning) elif not portage.eapi_is_supported(eapi): warnings.warn((_("Repository named '%(repo_name)s' has " "unsupported EAPI '%(eapi)s' setting in " "'%(layout_filename)s'; please upgrade portage.") % dict(repo_name=repo_name or 'unspecified', eapi=eapi, layout_filename=layout_filename)), SyntaxWarning) else: data['profile_eapi_when_unspecified'] = eapi return data, layout_errors
def __init__(self, name, repo_opts, local_config=True): """Build a RepoConfig with options in repo_opts Try to read repo_name in repository location, but if it is not found use variable name as repository name""" force = repo_opts.get('force') if force is not None: force = tuple(force.split()) self.force = force if force is None: force = () self.local_config = local_config if local_config or 'aliases' in force: aliases = repo_opts.get('aliases') if aliases is not None: aliases = tuple(aliases.split()) else: aliases = None self.aliases = aliases if local_config or 'eclass-overrides' in force: eclass_overrides = repo_opts.get('eclass-overrides') if eclass_overrides is not None: eclass_overrides = tuple(eclass_overrides.split()) else: eclass_overrides = None self.eclass_overrides = eclass_overrides # Eclass databases and locations are computed later. self.eclass_db = None self.eclass_locations = None if local_config or 'masters' in force: # Masters from repos.conf override layout.conf. masters = repo_opts.get('masters') if masters is not None: masters = tuple(masters.split()) else: masters = None self.masters = masters #The main-repo key makes only sense for the 'DEFAULT' section. self.main_repo = repo_opts.get('main-repo') priority = repo_opts.get('priority') if priority is not None: try: priority = int(priority) except ValueError: priority = None self.priority = priority sync_type = repo_opts.get('sync-type') if sync_type is not None: sync_type = sync_type.strip() self.sync_type = sync_type or None sync_umask = repo_opts.get('sync-umask') if sync_umask is not None: sync_umask = sync_umask.strip() self.sync_umask = sync_umask or None sync_uri = repo_opts.get('sync-uri') if sync_uri is not None: sync_uri = sync_uri.strip() self.sync_uri = sync_uri or None sync_user = repo_opts.get('sync-user') if sync_user is not None: sync_user = sync_user.strip() self.sync_user = sync_user or None auto_sync = repo_opts.get('auto-sync', 'yes') if auto_sync is not None: auto_sync = auto_sync.strip().lower() self.auto_sync = auto_sync self.clone_depth = repo_opts.get('clone-depth') self.sync_depth = repo_opts.get('sync-depth') if self.sync_depth is not None: warnings.warn(_("repos.conf: sync-depth is deprecated," " use clone-depth instead")) self.sync_hooks_only_on_change = repo_opts.get( 'sync-hooks-only-on-change', 'false').lower() == 'true' self.strict_misc_digests = repo_opts.get( 'strict-misc-digests', 'true').lower() == 'true' self.sync_openpgp_key_path = repo_opts.get( 'sync-openpgp-key-path', None) for k in ('sync_openpgp_key_refresh_retry_count', 'sync_openpgp_key_refresh_retry_delay_max', 'sync_openpgp_key_refresh_retry_delay_exp_base', 'sync_openpgp_key_refresh_retry_delay_mult', 'sync_openpgp_key_refresh_retry_overall_timeout'): setattr(self, k, repo_opts.get(k.replace('_', '-'), None)) self.module_specific_options = {} # Not implemented. format = repo_opts.get('format') if format is not None: format = format.strip() self.format = format location = repo_opts.get('location') if location is not None and location.strip(): if os.path.isdir(location) or portage._sync_mode: location = os.path.realpath(location) else: location = None self.location = location missing = True self.name = name if self.location is not None: self.name, missing = self._read_valid_repo_name(self.location) if missing: # The name from repos.conf has to be used here for # things like emerge-webrsync to work when the repo # is empty (bug #484950). if name is not None: self.name = name if portage._sync_mode: missing = False elif name == "DEFAULT": missing = False self.eapi = None self.missing_repo_name = missing # sign_commit is disabled by default, since it requires Git >=1.7.9, # and key_id configured by `git config user.signingkey key_id` self.sign_commit = False self.sign_manifest = True self.thin_manifest = False self.allow_missing_manifest = False self.allow_provide_virtual = False self.create_manifest = True self.disable_manifest = False self.manifest_hashes = None self.manifest_required_hashes = None self.update_changelog = False self.cache_formats = None self.portage1_profiles = True self.portage1_profiles_compat = False self.find_invalid_path_char = _find_invalid_path_char self._masters_orig = None # Parse layout.conf. if self.location: layout_data = parse_layout_conf(self.location, self.name)[0] self._masters_orig = layout_data['masters'] # layout.conf masters may be overridden here if we have a masters # setting from the user's repos.conf if self.masters is None: self.masters = layout_data['masters'] if (local_config or 'aliases' in force) and layout_data['aliases']: aliases = self.aliases if aliases is None: aliases = () # repos.conf aliases come after layout.conf aliases, giving # them the ability to do incremental overrides self.aliases = layout_data['aliases'] + tuple(aliases) if layout_data['repo-name']: # allow layout.conf to override repository name # useful when having two copies of the same repo enabled # to avoid modifying profiles/repo_name in one of them self.name = layout_data['repo-name'] self.missing_repo_name = False for value in ('allow-missing-manifest', 'cache-formats', 'create-manifest', 'disable-manifest', 'manifest-hashes', 'manifest-required-hashes', 'profile-formats', 'sign-commit', 'sign-manifest', 'thin-manifest', 'update-changelog'): setattr(self, value.lower().replace("-", "_"), layout_data[value]) # If profile-formats specifies a default EAPI, then set # self.eapi to that, otherwise set it to "0" as specified # by PMS. self.eapi = layout_data.get( 'profile_eapi_when_unspecified', '0') eapi = read_corresponding_eapi_file( os.path.join(self.location, REPO_NAME_LOC), default=self.eapi) self.portage1_profiles = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \ any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats']) self.portage1_profiles_compat = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \ layout_data['profile-formats'] == ('portage-1-compat',) self._eapis_banned = frozenset(layout_data['eapis-banned']) self._eapis_deprecated = frozenset(layout_data['eapis-deprecated'])
def __init__(self, name, repo_opts, local_config=True): """Build a RepoConfig with options in repo_opts Try to read repo_name in repository location, but if it is not found use variable name as repository name""" force = repo_opts.get('force') if force is not None: force = tuple(force.split()) self.force = force if force is None: force = () self.local_config = local_config if local_config or 'aliases' in force: aliases = repo_opts.get('aliases') if aliases is not None: aliases = tuple(aliases.split()) else: aliases = None self.aliases = aliases if local_config or 'eclass-overrides' in force: eclass_overrides = repo_opts.get('eclass-overrides') if eclass_overrides is not None: eclass_overrides = tuple(eclass_overrides.split()) else: eclass_overrides = None self.eclass_overrides = eclass_overrides # Eclass databases and locations are computed later. self.eclass_db = None self.eclass_locations = None if local_config or 'masters' in force: # Masters from repos.conf override layout.conf. masters = repo_opts.get('masters') if masters is not None: masters = tuple(masters.split()) else: masters = None self.masters = masters #The main-repo key makes only sense for the 'DEFAULT' section. self.main_repo = repo_opts.get('main-repo') priority = repo_opts.get('priority') if priority is not None: try: priority = int(priority) except ValueError: priority = None self.priority = priority sync_cvs_repo = repo_opts.get('sync-cvs-repo') if sync_cvs_repo is not None: sync_cvs_repo = sync_cvs_repo.strip() self.sync_cvs_repo = sync_cvs_repo or None sync_type = repo_opts.get('sync-type') if sync_type is not None: sync_type = sync_type.strip() self.sync_type = sync_type or None sync_uri = repo_opts.get('sync-uri') if sync_uri is not None: sync_uri = sync_uri.strip() self.sync_uri = sync_uri or None # Not implemented. format = repo_opts.get('format') if format is not None: format = format.strip() self.format = format location = repo_opts.get('location') self.user_location = location if location is not None and location.strip(): if os.path.isdir(location) or portage._sync_mode: location = os.path.realpath(location) else: location = None self.location = location eapi = None missing = True self.name = name if self.location is not None: eapi = read_corresponding_eapi_file(os.path.join(self.location, REPO_NAME_LOC)) self.name, missing = self._read_valid_repo_name(self.location) if missing: # The name from repos.conf has to be used here for # things like emerge-webrsync to work when the repo # is empty (bug #484950). if name is not None: self.name = name if portage._sync_mode: missing = False elif name == "DEFAULT": missing = False self.eapi = eapi self.missing_repo_name = missing # sign_commit is disabled by default, since it requires Git >=1.7.9, # and key_id configured by `git config user.signingkey key_id` self.sign_commit = False self.sign_manifest = True self.thin_manifest = False self.allow_missing_manifest = False self.allow_provide_virtual = False self.create_manifest = True self.disable_manifest = False self.manifest_hashes = None self.update_changelog = False self.cache_formats = None self.portage1_profiles = True self.portage1_profiles_compat = False self.find_invalid_path_char = _find_invalid_path_char self._masters_orig = None # Parse layout.conf. if self.location: layout_data = parse_layout_conf(self.location, self.name)[0] self._masters_orig = layout_data['masters'] # layout.conf masters may be overridden here if we have a masters # setting from the user's repos.conf if self.masters is None: self.masters = layout_data['masters'] if (local_config or 'aliases' in force) and layout_data['aliases']: aliases = self.aliases if aliases is None: aliases = () # repos.conf aliases come after layout.conf aliases, giving # them the ability to do incremental overrides self.aliases = layout_data['aliases'] + tuple(aliases) if layout_data['repo-name']: # allow layout.conf to override repository name # useful when having two copies of the same repo enabled # to avoid modifying profiles/repo_name in one of them self.name = layout_data['repo-name'] for value in ('allow-missing-manifest', 'allow-provide-virtual', 'cache-formats', 'create-manifest', 'disable-manifest', 'manifest-hashes', 'profile-formats', 'sign-commit', 'sign-manifest', 'thin-manifest', 'update-changelog'): setattr(self, value.lower().replace("-", "_"), layout_data[value]) self.portage1_profiles = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \ any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats']) self.portage1_profiles_compat = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \ layout_data['profile-formats'] == ('portage-1-compat',) self._eapis_banned = frozenset(layout_data['eapis-banned']) self._eapis_deprecated = frozenset(layout_data['eapis-deprecated'])
def _addProfile(self, currentPath, repositories, known_repos): current_abs_path = os.path.abspath(currentPath) allow_directories = True allow_parent_colon = True repo_loc = None compat_mode = False eapi_file = os.path.join(currentPath, "eapi") eapi = "0" f = None try: f = io.open(_unicode_encode(eapi_file, encoding=_encodings['fs'], errors='strict'), mode='r', encoding=_encodings['content'], errors='replace') eapi = f.readline().strip() except IOError: pass else: if not eapi_is_supported(eapi): raise ParseError(_( "Profile contains unsupported " "EAPI '%s': '%s'") % \ (eapi, os.path.realpath(eapi_file),)) finally: if f is not None: f.close() intersecting_repos = [x for x in known_repos if current_abs_path.startswith(x[0])] if intersecting_repos: # protect against nested repositories. Insane configuration, but the longest # path will be the correct one. repo_loc, layout_data = max(intersecting_repos, key=lambda x:len(x[0])) allow_directories = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \ any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats']) compat_mode = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \ layout_data['profile-formats'] == ('portage-1-compat',) allow_parent_colon = any(x in _allow_parent_colon for x in layout_data['profile-formats']) if compat_mode: offenders = _PORTAGE1_DIRECTORIES.intersection(os.listdir(currentPath)) offenders = sorted(x for x in offenders if os.path.isdir(os.path.join(currentPath, x))) if offenders: warnings.warn(_("Profile '%(profile_path)s' in repository " "'%(repo_name)s' is implicitly using 'portage-1' profile format, but " "the repository profiles are not marked as that format. This will break " "in the future. Please either convert the following paths " "to files, or add\nprofile-formats = portage-1\nto the " "repositories layout.conf. Files: '%(files)s'\n") % dict(profile_path=currentPath, repo_name=repo_loc, files=', '.join(offenders))) parentsFile = os.path.join(currentPath, "parent") if os.path.exists(parentsFile): parents = grabfile(parentsFile) if not parents: raise ParseError( _("Empty parent file: '%s'") % parentsFile) for parentPath in parents: abs_parent = parentPath[:1] == os.sep if not abs_parent and allow_parent_colon: parentPath = self._expand_parent_colon(parentsFile, parentPath, repo_loc, repositories) # NOTE: This os.path.join() call is intended to ignore # currentPath if parentPath is already absolute. parentPath = normalize_path(os.path.join( currentPath, parentPath)) if abs_parent or repo_loc is None or \ not parentPath.startswith(repo_loc): # It seems that this parent may point outside # of the current repo, so realpath it. parentPath = os.path.realpath(parentPath) if os.path.exists(parentPath): self._addProfile(parentPath, repositories, known_repos) else: raise ParseError( _("Parent '%s' not found: '%s'") % \ (parentPath, parentsFile)) self.profiles.append(currentPath) self.profiles_complex.append( _profile_node(currentPath, allow_directories))
def __init__(self, name, repo_opts, local_config=True): """Build a RepoConfig with options in repo_opts Try to read repo_name in repository location, but if it is not found use variable name as repository name""" force = repo_opts.get('force') if force is not None: force = tuple(force.split()) self.force = force if force is None: force = () self.local_config = local_config if local_config or 'aliases' in force: aliases = repo_opts.get('aliases') if aliases is not None: aliases = tuple(aliases.split()) else: aliases = None self.aliases = aliases if local_config or 'eclass-overrides' in force: eclass_overrides = repo_opts.get('eclass-overrides') if eclass_overrides is not None: eclass_overrides = tuple(eclass_overrides.split()) else: eclass_overrides = None self.eclass_overrides = eclass_overrides # Eclass databases and locations are computed later. self.eclass_db = None self.eclass_locations = None if local_config or 'masters' in force: # Masters from repos.conf override layout.conf. masters = repo_opts.get('masters') if masters is not None: masters = tuple(masters.split()) else: masters = None self.masters = masters #The main-repo key makes only sense for the 'DEFAULT' section. self.main_repo = repo_opts.get('main-repo') priority = repo_opts.get('priority') if priority is not None: try: priority = int(priority) except ValueError: priority = None self.priority = priority sync_type = repo_opts.get('sync-type') if sync_type is not None: sync_type = sync_type.strip() self.sync_type = sync_type or None sync_umask = repo_opts.get('sync-umask') if sync_umask is not None: sync_umask = sync_umask.strip() self.sync_umask = sync_umask or None sync_uri = repo_opts.get('sync-uri') if sync_uri is not None: sync_uri = sync_uri.strip() self.sync_uri = sync_uri or None sync_user = repo_opts.get('sync-user') if sync_user is not None: sync_user = sync_user.strip() self.sync_user = sync_user or None auto_sync = repo_opts.get('auto-sync', 'yes') if auto_sync is not None: auto_sync = auto_sync.strip().lower() self.auto_sync = auto_sync self.sync_depth = repo_opts.get('sync-depth') self.sync_hooks_only_on_change = repo_opts.get( 'sync-hooks-only-on-change', 'false').lower() == 'true' self.module_specific_options = {} # Not implemented. format = repo_opts.get('format') if format is not None: format = format.strip() self.format = format location = repo_opts.get('location') if location is not None and location.strip(): if os.path.isdir(location) or portage._sync_mode: location = os.path.realpath(location) else: location = None self.location = location missing = True self.name = name if self.location is not None: self.name, missing = self._read_valid_repo_name(self.location) if missing: # The name from repos.conf has to be used here for # things like emerge-webrsync to work when the repo # is empty (bug #484950). if name is not None: self.name = name if portage._sync_mode: missing = False elif name == "DEFAULT": missing = False self.eapi = None self.missing_repo_name = missing # sign_commit is disabled by default, since it requires Git >=1.7.9, # and key_id configured by `git config user.signingkey key_id` self.sign_commit = False self.sign_manifest = True self.thin_manifest = False self.allow_missing_manifest = False self.allow_provide_virtual = False self.create_manifest = True self.disable_manifest = False self.manifest_hashes = None self.update_changelog = False self.cache_formats = None self.portage1_profiles = True self.portage1_profiles_compat = False self.find_invalid_path_char = _find_invalid_path_char self._masters_orig = None # Parse layout.conf. if self.location: layout_data = parse_layout_conf(self.location, self.name)[0] self._masters_orig = layout_data['masters'] # layout.conf masters may be overridden here if we have a masters # setting from the user's repos.conf if self.masters is None: self.masters = layout_data['masters'] if (local_config or 'aliases' in force) and layout_data['aliases']: aliases = self.aliases if aliases is None: aliases = () # repos.conf aliases come after layout.conf aliases, giving # them the ability to do incremental overrides self.aliases = layout_data['aliases'] + tuple(aliases) if layout_data['repo-name']: # allow layout.conf to override repository name # useful when having two copies of the same repo enabled # to avoid modifying profiles/repo_name in one of them self.name = layout_data['repo-name'] self.missing_repo_name = False for value in ('allow-missing-manifest', 'allow-provide-virtual', 'cache-formats', 'create-manifest', 'disable-manifest', 'manifest-hashes', 'profile-formats', 'sign-commit', 'sign-manifest', 'thin-manifest', 'update-changelog'): setattr(self, value.lower().replace("-", "_"), layout_data[value]) # If profile-formats specifies a default EAPI, then set # self.eapi to that, otherwise set it to "0" as specified # by PMS. self.eapi = layout_data.get('profile_eapi_when_unspecified', '0') eapi = read_corresponding_eapi_file(os.path.join( self.location, REPO_NAME_LOC), default=self.eapi) self.portage1_profiles = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \ any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats']) self.portage1_profiles_compat = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \ layout_data['profile-formats'] == ('portage-1-compat',) self._eapis_banned = frozenset(layout_data['eapis-banned']) self._eapis_deprecated = frozenset(layout_data['eapis-deprecated'])
def _addProfile(self, currentPath, repositories, known_repos): current_abs_path = os.path.abspath(currentPath) allow_directories = True allow_parent_colon = True repo_loc = None compat_mode = False current_formats = () eapi = None intersecting_repos = [ x for x in known_repos if current_abs_path.startswith(x[0]) ] if intersecting_repos: # Handle nested repositories. The longest path # will be the correct one. repo_loc, layout_data = max(intersecting_repos, key=lambda x: len(x[0])) eapi = layout_data.get("profile_eapi_when_unspecified") eapi_file = os.path.join(currentPath, "eapi") eapi = eapi or "0" f = None try: f = io.open(_unicode_encode(eapi_file, encoding=_encodings['fs'], errors='strict'), mode='r', encoding=_encodings['content'], errors='replace') eapi = f.readline().strip() except IOError: pass else: if not eapi_is_supported(eapi): raise ParseError(_( "Profile contains unsupported " "EAPI '%s': '%s'") % \ (eapi, os.path.realpath(eapi_file),)) finally: if f is not None: f.close() if intersecting_repos: allow_directories = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \ any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats']) compat_mode = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \ layout_data['profile-formats'] == ('portage-1-compat',) allow_parent_colon = any(x in _allow_parent_colon for x in layout_data['profile-formats']) current_formats = tuple(layout_data['profile-formats']) if compat_mode: offenders = _PORTAGE1_DIRECTORIES.intersection( os.listdir(currentPath)) offenders = sorted(x for x in offenders if os.path.isdir(os.path.join(currentPath, x))) if offenders: warnings.warn( _("\nThe selected profile is implicitly using the 'portage-1' format:\n" "\tprofile = %(profile_path)s\n" "But this repository is not using that format:\n" "\trepo = %(repo_name)s\n" "This will break in the future. Please convert these dirs to files:\n" "\t%(files)s\n" "Or, add this line to the repository's layout.conf:\n" "\tprofile-formats = portage-1") % dict(profile_path=currentPath, repo_name=repo_loc, files='\n\t'.join(offenders))) parentsFile = os.path.join(currentPath, "parent") if exists_raise_eaccess(parentsFile): parents = grabfile(parentsFile) if not parents: raise ParseError(_("Empty parent file: '%s'") % parentsFile) for parentPath in parents: abs_parent = parentPath[:1] == os.sep if not abs_parent and allow_parent_colon: parentPath = self._expand_parent_colon( parentsFile, parentPath, repo_loc, repositories) # NOTE: This os.path.join() call is intended to ignore # currentPath if parentPath is already absolute. parentPath = normalize_path( os.path.join(currentPath, parentPath)) if abs_parent or repo_loc is None or \ not parentPath.startswith(repo_loc): # It seems that this parent may point outside # of the current repo, so realpath it. parentPath = os.path.realpath(parentPath) if exists_raise_eaccess(parentPath): self._addProfile(parentPath, repositories, known_repos) else: raise ParseError( _("Parent '%s' not found: '%s'") % \ (parentPath, parentsFile)) self.profiles.append(currentPath) self.profiles_complex.append( _profile_node(currentPath, allow_directories, False, current_formats, eapi, 'build-id' in current_formats))
def _addProfile(self, currentPath, repositories, known_repos): current_abs_path = os.path.abspath(currentPath) allow_directories = True allow_parent_colon = True repo_loc = None compat_mode = False current_formats = () eapi = None intersecting_repos = [x for x in known_repos if current_abs_path.startswith(x[0])] if intersecting_repos: # Handle nested repositories. The longest path # will be the correct one. repo_loc, layout_data = max(intersecting_repos, key=lambda x:len(x[0])) eapi = layout_data.get("profile_eapi_when_unspecified") eapi_file = os.path.join(currentPath, "eapi") eapi = eapi or "0" f = None try: f = io.open(_unicode_encode(eapi_file, encoding=_encodings['fs'], errors='strict'), mode='r', encoding=_encodings['content'], errors='replace') eapi = f.readline().strip() except IOError: pass else: if not eapi_is_supported(eapi): raise ParseError(_( "Profile contains unsupported " "EAPI '%s': '%s'") % \ (eapi, os.path.realpath(eapi_file),)) finally: if f is not None: f.close() if intersecting_repos: allow_directories = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \ any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats']) compat_mode = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \ layout_data['profile-formats'] == ('portage-1-compat',) allow_parent_colon = any(x in _allow_parent_colon for x in layout_data['profile-formats']) current_formats = tuple(layout_data['profile-formats']) if compat_mode: offenders = _PORTAGE1_DIRECTORIES.intersection(os.listdir(currentPath)) offenders = sorted(x for x in offenders if os.path.isdir(os.path.join(currentPath, x))) if offenders: warnings.warn(_( "\nThe selected profile is implicitly using the 'portage-1' format:\n" "\tprofile = %(profile_path)s\n" "But this repository is not using that format:\n" "\trepo = %(repo_name)s\n" "This will break in the future. Please convert these dirs to files:\n" "\t%(files)s\n" "Or, add this line to the repository's layout.conf:\n" "\tprofile-formats = portage-1") % dict(profile_path=currentPath, repo_name=repo_loc, files='\n\t'.join(offenders))) parentsFile = os.path.join(currentPath, "parent") if exists_raise_eaccess(parentsFile): parents = grabfile(parentsFile) if not parents: raise ParseError( _("Empty parent file: '%s'") % parentsFile) for parentPath in parents: abs_parent = parentPath[:1] == os.sep if not abs_parent and allow_parent_colon: parentPath = self._expand_parent_colon(parentsFile, parentPath, repo_loc, repositories) # NOTE: This os.path.join() call is intended to ignore # currentPath if parentPath is already absolute. parentPath = normalize_path(os.path.join( currentPath, parentPath)) if abs_parent or repo_loc is None or \ not parentPath.startswith(repo_loc): # It seems that this parent may point outside # of the current repo, so realpath it. parentPath = os.path.realpath(parentPath) if exists_raise_eaccess(parentPath): self._addProfile(parentPath, repositories, known_repos) else: raise ParseError( _("Parent '%s' not found: '%s'") % \ (parentPath, parentsFile)) self.profiles.append(currentPath) self.profiles_complex.append( _profile_node(currentPath, allow_directories, False, current_formats, eapi))
def parse_layout_conf(repo_location, repo_name=None): eapi = read_corresponding_eapi_file( os.path.join(repo_location, REPO_NAME_LOC)) layout_filename = os.path.join(repo_location, "metadata", "layout.conf") layout_file = KeyValuePairFileLoader(layout_filename, None, None) layout_data, layout_errors = layout_file.load() data = {} # None indicates abscence of a masters setting, which later code uses # to trigger a backward compatibility fallback that sets an implicit # master. In order to avoid this fallback behavior, layout.conf can # explicitly set masters to an empty value, which will result in an # empty tuple here instead of None. masters = layout_data.get('masters') if masters is not None: masters = tuple(masters.split()) data['masters'] = masters data['aliases'] = tuple(layout_data.get('aliases', '').split()) data['allow-provide-virtual'] = \ layout_data.get('allow-provide-virtuals', 'false').lower() == 'true' data['eapis-banned'] = tuple(layout_data.get('eapis-banned', '').split()) data['eapis-deprecated'] = tuple( layout_data.get('eapis-deprecated', '').split()) data['sign-commit'] = layout_data.get('sign-commits', 'false').lower() \ == 'true' data['sign-manifest'] = layout_data.get('sign-manifests', 'true').lower() \ == 'true' data['thin-manifest'] = layout_data.get('thin-manifests', 'false').lower() \ == 'true' data['repo-name'] = _gen_valid_repo(layout_data.get('repo-name', '')) manifest_policy = layout_data.get('use-manifests', 'strict').lower() data['allow-missing-manifest'] = manifest_policy != 'strict' data['create-manifest'] = manifest_policy != 'false' data['disable-manifest'] = manifest_policy == 'false' # for compatibility w/ PMS, fallback to pms; but also check if the # cache exists or not. cache_formats = layout_data.get('cache-formats', '').lower().split() if not cache_formats: # Auto-detect cache formats, and prefer md5-cache if available. # This behavior was deployed in portage-2.1.11.14, so that the # default egencache format could eventually be changed to md5-dict # in portage-2.1.11.32. WARNING: Versions prior to portage-2.1.11.14 # will NOT recognize md5-dict format unless it is explicitly # listed in layout.conf. cache_formats = [] if os.path.isdir(os.path.join(repo_location, 'metadata', 'md5-cache')): cache_formats.append('md5-dict') if os.path.isdir(os.path.join(repo_location, 'metadata', 'cache')): cache_formats.append('pms') data['cache-formats'] = tuple(cache_formats) manifest_hashes = layout_data.get('manifest-hashes') if manifest_hashes is not None: manifest_hashes = frozenset(manifest_hashes.upper().split()) if MANIFEST2_REQUIRED_HASH not in manifest_hashes: repo_name = _get_repo_name(repo_location, cached=repo_name) warnings.warn( (_("Repository named '%(repo_name)s' has a " "'manifest-hashes' setting that does not contain " "the '%(hash)s' hash which is required by this " "portage version. You will have to upgrade portage " "if you want to generate valid manifests for this " "repository: %(layout_filename)s") % { "repo_name": repo_name or 'unspecified', "hash": MANIFEST2_REQUIRED_HASH, "layout_filename": layout_filename }), DeprecationWarning) unsupported_hashes = manifest_hashes.difference( MANIFEST2_HASH_FUNCTIONS) if unsupported_hashes: repo_name = _get_repo_name(repo_location, cached=repo_name) warnings.warn(( _("Repository named '%(repo_name)s' has a " "'manifest-hashes' setting that contains one " "or more hash types '%(hashes)s' which are not supported by " "this portage version. You will have to upgrade " "portage if you want to generate valid manifests for " "this repository: %(layout_filename)s") % { "repo_name": repo_name or 'unspecified', "hashes": " ".join(sorted(unsupported_hashes)), "layout_filename": layout_filename }), DeprecationWarning) data['manifest-hashes'] = manifest_hashes data['update-changelog'] = layout_data.get('update-changelog', 'false').lower() \ == 'true' raw_formats = layout_data.get('profile-formats') if raw_formats is None: if eapi_allows_directories_on_profile_level_and_repository_level(eapi): raw_formats = ('portage-1', ) else: raw_formats = ('portage-1-compat', ) else: raw_formats = set(raw_formats.split()) unknown = raw_formats.difference(_valid_profile_formats) if unknown: repo_name = _get_repo_name(repo_location, cached=repo_name) warnings.warn((_( "Repository named '%(repo_name)s' has unsupported " "profiles in use ('profile-formats = %(unknown_fmts)s' setting in " "'%(layout_filename)s; please upgrade portage.") % dict(repo_name=repo_name or 'unspecified', layout_filename=layout_filename, unknown_fmts=" ".join(unknown))), DeprecationWarning) raw_formats = tuple(raw_formats.intersection(_valid_profile_formats)) data['profile-formats'] = raw_formats try: eapi = layout_data['profile_eapi_when_unspecified'] except KeyError: pass else: if 'profile-default-eapi' not in raw_formats: warnings.warn( (_("Repository named '%(repo_name)s' has " "profile_eapi_when_unspecified setting in " "'%(layout_filename)s', but 'profile-default-eapi' is " "not listed in the profile-formats field. Please " "report this issue to the repository maintainer.") % dict(repo_name=repo_name or 'unspecified', layout_filename=layout_filename)), SyntaxWarning) elif not portage.eapi_is_supported(eapi): warnings.warn( (_("Repository named '%(repo_name)s' has " "unsupported EAPI '%(eapi)s' setting in " "'%(layout_filename)s'; please upgrade portage.") % dict(repo_name=repo_name or 'unspecified', eapi=eapi, layout_filename=layout_filename)), SyntaxWarning) else: data['profile_eapi_when_unspecified'] = eapi return data, layout_errors
def __init__(self, name, repo_opts): """Build a RepoConfig with options in repo_opts Try to read repo_name in repository location, but if it is not found use variable name as repository name""" aliases = repo_opts.get('aliases') if aliases is not None: aliases = tuple(aliases.split()) self.aliases = aliases eclass_overrides = repo_opts.get('eclass-overrides') if eclass_overrides is not None: eclass_overrides = tuple(eclass_overrides.split()) self.eclass_overrides = eclass_overrides # Eclass databases and locations are computed later. self.eclass_db = None self.eclass_locations = None # Masters from repos.conf override layout.conf. masters = repo_opts.get('masters') if masters is not None: masters = tuple(masters.split()) self.masters = masters #The main-repo key makes only sense for the 'DEFAULT' section. self.main_repo = repo_opts.get('main-repo') priority = repo_opts.get('priority') if priority is not None: try: priority = int(priority) except ValueError: priority = None self.priority = priority sync = repo_opts.get('sync') if sync is not None: sync = sync.strip() self.sync = sync format = repo_opts.get('format') if format is not None: format = format.strip() self.format = format location = repo_opts.get('location') self.user_location = location if location is not None and location.strip(): if os.path.isdir(location): location = os.path.realpath(location) else: location = None self.location = location eapi = None missing = True if self.location is not None: eapi = read_corresponding_eapi_file(os.path.join(self.location, REPO_NAME_LOC)) name, missing = self._read_valid_repo_name(self.location) elif name == "DEFAULT": missing = False self.eapi = eapi self.name = name self.missing_repo_name = missing # sign_commit is disabled by default, since it requires Git >=1.7.9, # and key_id configured by `git config user.signingkey key_id` self.sign_commit = False self.sign_manifest = True self.thin_manifest = False self.allow_missing_manifest = False self.allow_provide_virtual = False self.create_manifest = True self.disable_manifest = False self.manifest_hashes = None self.update_changelog = False self.cache_formats = None self.portage1_profiles = True self.portage1_profiles_compat = False self.find_invalid_path_char = _find_invalid_path_char # Parse layout.conf. if self.location: layout_filename = os.path.join(self.location, "metadata", "layout.conf") layout_data = parse_layout_conf(self.location, self.name)[0] # layout.conf masters may be overridden here if we have a masters # setting from the user's repos.conf if self.masters is None: self.masters = layout_data['masters'] if layout_data['aliases']: aliases = self.aliases if aliases is None: aliases = () # repos.conf aliases come after layout.conf aliases, giving # them the ability to do incremental overrides self.aliases = layout_data['aliases'] + tuple(aliases) for value in ('allow-missing-manifest', 'allow-provide-virtual', 'cache-formats', 'create-manifest', 'disable-manifest', 'manifest-hashes', 'profile-formats', 'sign-commit', 'sign-manifest', 'thin-manifest', 'update-changelog'): setattr(self, value.lower().replace("-", "_"), layout_data[value]) self.portage1_profiles = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \ any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats']) self.portage1_profiles_compat = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \ layout_data['profile-formats'] == ('portage-1-compat',)
def parse_layout_conf(repo_location, repo_name=None): eapi = read_corresponding_eapi_file(os.path.join(repo_location, REPO_NAME_LOC)) layout_filename = os.path.join(repo_location, "metadata", "layout.conf") layout_file = KeyValuePairFileLoader(layout_filename, None, None) layout_data, layout_errors = layout_file.load() data = {} # None indicates abscence of a masters setting, which later code uses # to trigger a backward compatibility fallback that sets an implicit # master. In order to avoid this fallback behavior, layout.conf can # explicitly set masters to an empty value, which will result in an # empty tuple here instead of None. masters = layout_data.get('masters') if masters is not None: masters = tuple(masters.split()) data['masters'] = masters data['aliases'] = tuple(layout_data.get('aliases', '').split()) data['allow-provide-virtual'] = \ layout_data.get('allow-provide-virtuals', 'false').lower() == 'true' data['sign-commit'] = layout_data.get('sign-commits', 'false').lower() \ == 'true' data['sign-manifest'] = layout_data.get('sign-manifests', 'true').lower() \ == 'true' data['thin-manifest'] = layout_data.get('thin-manifests', 'false').lower() \ == 'true' manifest_policy = layout_data.get('use-manifests', 'strict').lower() data['allow-missing-manifest'] = manifest_policy != 'strict' data['create-manifest'] = manifest_policy != 'false' data['disable-manifest'] = manifest_policy == 'false' # for compatibility w/ PMS, fallback to pms; but also check if the # cache exists or not. cache_formats = layout_data.get('cache-formats', '').lower().split() if not cache_formats: # Auto-detect cache formats, and prefer md5-cache if available. # After this behavior is deployed in stable portage, the default # egencache format can be changed to md5-dict. cache_formats = [] if os.path.isdir(os.path.join(repo_location, 'metadata', 'md5-cache')): cache_formats.append('md5-dict') if os.path.isdir(os.path.join(repo_location, 'metadata', 'cache')): cache_formats.append('pms') data['cache-formats'] = tuple(cache_formats) manifest_hashes = layout_data.get('manifest-hashes') if manifest_hashes is not None: manifest_hashes = frozenset(manifest_hashes.upper().split()) if MANIFEST2_REQUIRED_HASH not in manifest_hashes: repo_name = _get_repo_name(repo_location, cached=repo_name) warnings.warn((_("Repository named '%(repo_name)s' has a " "'manifest-hashes' setting that does not contain " "the '%(hash)s' hash which is required by this " "portage version. You will have to upgrade portage " "if you want to generate valid manifests for this " "repository: %(layout_filename)s") % {"repo_name": repo_name or 'unspecified', "hash":MANIFEST2_REQUIRED_HASH, "layout_filename":layout_filename}), DeprecationWarning) unsupported_hashes = manifest_hashes.difference( MANIFEST2_HASH_FUNCTIONS) if unsupported_hashes: repo_name = _get_repo_name(repo_location, cached=repo_name) warnings.warn((_("Repository named '%(repo_name)s' has a " "'manifest-hashes' setting that contains one " "or more hash types '%(hashes)s' which are not supported by " "this portage version. You will have to upgrade " "portage if you want to generate valid manifests for " "this repository: %(layout_filename)s") % {"repo_name": repo_name or 'unspecified', "hashes":" ".join(sorted(unsupported_hashes)), "layout_filename":layout_filename}), DeprecationWarning) data['manifest-hashes'] = manifest_hashes data['update-changelog'] = layout_data.get('update-changelog', 'false').lower() \ == 'true' raw_formats = layout_data.get('profile-formats') if raw_formats is None: if eapi_allows_directories_on_profile_level_and_repository_level(eapi): raw_formats = ('portage-1',) else: raw_formats = ('portage-1-compat',) else: raw_formats = set(raw_formats.split()) unknown = raw_formats.difference(_valid_profile_formats) if unknown: repo_name = _get_repo_name(repo_location, cached=repo_name) warnings.warn((_("Repository named '%(repo_name)s' has unsupported " "profiles in use ('profile-formats = %(unknown_fmts)s' setting in " "'%(layout_filename)s; please upgrade portage.") % dict(repo_name=repo_name or 'unspecified', layout_filename=layout_filename, unknown_fmts=" ".join(unknown))), DeprecationWarning) raw_formats = tuple(raw_formats.intersection(_valid_profile_formats)) data['profile-formats'] = raw_formats return data, layout_errors
def parse_layout_conf(repo_location, repo_name=None): eapi = read_corresponding_eapi_file(os.path.join(repo_location, REPO_NAME_LOC)) layout_filename = os.path.join(repo_location, "metadata", "layout.conf") layout_file = KeyValuePairFileLoader(layout_filename, None, None) layout_data, layout_errors = layout_file.load() data = {} # None indicates abscence of a masters setting, which later code uses # to trigger a backward compatibility fallback that sets an implicit # master. In order to avoid this fallback behavior, layout.conf can # explicitly set masters to an empty value, which will result in an # empty tuple here instead of None. masters = layout_data.get("masters") if masters is not None: masters = tuple(masters.split()) data["masters"] = masters data["aliases"] = tuple(layout_data.get("aliases", "").split()) data["allow-provide-virtual"] = layout_data.get("allow-provide-virtuals", "false").lower() == "true" data["eapis-banned"] = tuple(layout_data.get("eapis-banned", "").split()) data["eapis-deprecated"] = tuple(layout_data.get("eapis-deprecated", "").split()) data["sign-commit"] = layout_data.get("sign-commits", "false").lower() == "true" data["sign-manifest"] = layout_data.get("sign-manifests", "true").lower() == "true" data["thin-manifest"] = layout_data.get("thin-manifests", "false").lower() == "true" data["repo-name"] = _gen_valid_repo(layout_data.get("repo-name", "")) manifest_policy = layout_data.get("use-manifests", "strict").lower() data["allow-missing-manifest"] = manifest_policy != "strict" data["create-manifest"] = manifest_policy != "false" data["disable-manifest"] = manifest_policy == "false" # for compatibility w/ PMS, fallback to pms; but also check if the # cache exists or not. cache_formats = layout_data.get("cache-formats", "").lower().split() if not cache_formats: # Auto-detect cache formats, and prefer md5-cache if available. # This behavior was deployed in portage-2.1.11.14, so that the # default egencache format could eventually be changed to md5-dict # in portage-2.1.11.32. WARNING: Versions prior to portage-2.1.11.14 # will NOT recognize md5-dict format unless it is explicitly # listed in layout.conf. cache_formats = [] if os.path.isdir(os.path.join(repo_location, "metadata", "md5-cache")): cache_formats.append("md5-dict") if os.path.isdir(os.path.join(repo_location, "metadata", "cache")): cache_formats.append("pms") data["cache-formats"] = tuple(cache_formats) manifest_hashes = layout_data.get("manifest-hashes") if manifest_hashes is not None: manifest_hashes = frozenset(manifest_hashes.upper().split()) if MANIFEST2_REQUIRED_HASH not in manifest_hashes: repo_name = _get_repo_name(repo_location, cached=repo_name) warnings.warn( ( _( "Repository named '%(repo_name)s' has a " "'manifest-hashes' setting that does not contain " "the '%(hash)s' hash which is required by this " "portage version. You will have to upgrade portage " "if you want to generate valid manifests for this " "repository: %(layout_filename)s" ) % { "repo_name": repo_name or "unspecified", "hash": MANIFEST2_REQUIRED_HASH, "layout_filename": layout_filename, } ), DeprecationWarning, ) unsupported_hashes = manifest_hashes.difference(MANIFEST2_HASH_FUNCTIONS) if unsupported_hashes: repo_name = _get_repo_name(repo_location, cached=repo_name) warnings.warn( ( _( "Repository named '%(repo_name)s' has a " "'manifest-hashes' setting that contains one " "or more hash types '%(hashes)s' which are not supported by " "this portage version. You will have to upgrade " "portage if you want to generate valid manifests for " "this repository: %(layout_filename)s" ) % { "repo_name": repo_name or "unspecified", "hashes": " ".join(sorted(unsupported_hashes)), "layout_filename": layout_filename, } ), DeprecationWarning, ) data["manifest-hashes"] = manifest_hashes data["update-changelog"] = layout_data.get("update-changelog", "false").lower() == "true" raw_formats = layout_data.get("profile-formats") if raw_formats is None: if eapi_allows_directories_on_profile_level_and_repository_level(eapi): raw_formats = ("portage-1",) else: raw_formats = ("portage-1-compat",) else: raw_formats = set(raw_formats.split()) unknown = raw_formats.difference(_valid_profile_formats) if unknown: repo_name = _get_repo_name(repo_location, cached=repo_name) warnings.warn( ( _( "Repository named '%(repo_name)s' has unsupported " "profiles in use ('profile-formats = %(unknown_fmts)s' setting in " "'%(layout_filename)s; please upgrade portage." ) % dict( repo_name=repo_name or "unspecified", layout_filename=layout_filename, unknown_fmts=" ".join(unknown), ) ), DeprecationWarning, ) raw_formats = tuple(raw_formats.intersection(_valid_profile_formats)) data["profile-formats"] = raw_formats try: eapi = layout_data["profile_eapi_when_unspecified"] except KeyError: pass else: if "profile-default-eapi" not in raw_formats: warnings.warn( ( _( "Repository named '%(repo_name)s' has " "profile_eapi_when_unspecified setting in " "'%(layout_filename)s', but 'profile-default-eapi' is " "not listed in the profile-formats field. Please " "report this issue to the repository maintainer." ) % dict(repo_name=repo_name or "unspecified", layout_filename=layout_filename) ), SyntaxWarning, ) elif not portage.eapi_is_supported(eapi): warnings.warn( ( _( "Repository named '%(repo_name)s' has " "unsupported EAPI '%(eapi)s' setting in " "'%(layout_filename)s'; please upgrade portage." ) % dict(repo_name=repo_name or "unspecified", eapi=eapi, layout_filename=layout_filename) ), SyntaxWarning, ) else: data["profile_eapi_when_unspecified"] = eapi return data, layout_errors
def _addProfile(self, currentPath, repositories, known_repos, previous_repos): current_abs_path = os.path.abspath(currentPath) allow_directories = True allow_parent_colon = True repo_loc = None compat_mode = False current_formats = () eapi = None intersecting_repos = tuple(x for x in known_repos if current_abs_path.startswith(x[0])) if intersecting_repos: # Handle nested repositories. The longest path # will be the correct one. repo_loc, layout_data = max(intersecting_repos, key=lambda x: len(x[0])) eapi = layout_data.get("profile_eapi_when_unspecified") eapi_file = os.path.join(currentPath, "eapi") eapi = eapi or "0" f = None try: f = io.open( _unicode_encode(eapi_file, encoding=_encodings["fs"], errors="strict"), mode="r", encoding=_encodings["content"], errors="replace", ) eapi = f.readline().strip() except IOError: pass else: if not eapi_is_supported(eapi): raise ParseError( _("Profile contains unsupported " "EAPI '%s': '%s'") % ( eapi, os.path.realpath(eapi_file), )) finally: if f is not None: f.close() if intersecting_repos: allow_directories = ( eapi_allows_directories_on_profile_level_and_repository_level( eapi) or any(x in _portage1_profiles_allow_directories for x in layout_data["profile-formats"])) compat_mode = ( not eapi_allows_directories_on_profile_level_and_repository_level( eapi) and layout_data["profile-formats"] == ("portage-1-compat", )) allow_parent_colon = any(x in _allow_parent_colon for x in layout_data["profile-formats"]) current_formats = tuple(layout_data["profile-formats"]) # According to PMS, a deprecated profile warning is not inherited. Since # the current profile node may have been inherited by a user profile # node, the deprecation warning may be relevant even if it is not a # top-level profile node. Therefore, consider the deprecated warning # to be irrelevant when the current profile node belongs to the same # repo as the previous profile node. show_deprecated_warning = tuple(x[0] for x in previous_repos) != tuple( x[0] for x in intersecting_repos) if compat_mode: offenders = _PORTAGE1_DIRECTORIES.intersection( os.listdir(currentPath)) offenders = sorted(x for x in offenders if os.path.isdir(os.path.join(currentPath, x))) if offenders: warnings.warn( _("\nThe selected profile is implicitly using the 'portage-1' format:\n" "\tprofile = %(profile_path)s\n" "But this repository is not using that format:\n" "\trepo = %(repo_name)s\n" "This will break in the future. Please convert these dirs to files:\n" "\t%(files)s\n" "Or, add this line to the repository's layout.conf:\n" "\tprofile-formats = portage-1") % dict( profile_path=currentPath, repo_name=repo_loc, files="\n\t".join(offenders), )) parentsFile = os.path.join(currentPath, "parent") if exists_raise_eaccess(parentsFile): parents = grabfile(parentsFile) if not parents: raise ParseError(_("Empty parent file: '%s'") % parentsFile) for parentPath in parents: abs_parent = parentPath[:1] == os.sep if not abs_parent and allow_parent_colon: parentPath = self._expand_parent_colon( parentsFile, parentPath, repo_loc, repositories) # NOTE: This os.path.join() call is intended to ignore # currentPath if parentPath is already absolute. parentPath = normalize_path( os.path.join(currentPath, parentPath)) if (abs_parent or repo_loc is None or not parentPath.startswith(repo_loc)): # It seems that this parent may point outside # of the current repo, so realpath it. parentPath = os.path.realpath(parentPath) if exists_raise_eaccess(parentPath): self._addProfile(parentPath, repositories, known_repos, intersecting_repos) else: raise ParseError( _("Parent '%s' not found: '%s'") % (parentPath, parentsFile)) self.profiles.append(currentPath) self.profiles_complex.append( _profile_node( currentPath, allow_directories, False, current_formats, eapi, "build-id" in current_formats, show_deprecated_warning=show_deprecated_warning, ))