def _parse_repository_usealiases(self, repositories): ret = {} for repo in repositories.repos_with_profiles(): file_name = os.path.join(repo.location, "profiles", "use.aliases") eapi = read_corresponding_eapi_file( file_name, default=repo.eapi) useflag_re = _get_useflag_re(eapi) raw_file_dict = grabdict(file_name, recursive=True) file_dict = {} for real_flag, aliases in raw_file_dict.items(): if useflag_re.match(real_flag) is None: writemsg(_("--- Invalid real USE flag in '%s': '%s'\n") % (file_name, real_flag), noiselevel=-1) else: for alias in aliases: if useflag_re.match(alias) is None: writemsg(_("--- Invalid USE flag alias for '%s' real USE flag in '%s': '%s'\n") % (real_flag, file_name, alias), noiselevel=-1) else: if any(alias in v for k, v in file_dict.items() if k != real_flag): writemsg(_("--- Duplicated USE flag alias in '%s': '%s'\n") % (file_name, alias), noiselevel=-1) else: file_dict.setdefault(real_flag, []).append(alias) ret[repo.name] = file_dict return ret
def _parse_file_to_dict(self, file_name, juststrings=False, recursive=True, eapi_filter=None): ret = {} location_dict = {} file_dict = grabdict_package(file_name, recursive=recursive, verify_eapi=True) eapi = read_corresponding_eapi_file(file_name) if eapi_filter is not None and not eapi_filter(eapi): if file_dict: writemsg(_("--- EAPI '%s' does not support '%s': '%s'\n") % (eapi, os.path.basename(file_name), file_name), noiselevel=-1) return ret useflag_re = _get_useflag_re(eapi) for k, v in file_dict.items(): useflags = [] for prefixed_useflag in v: if prefixed_useflag[:1] == "-": useflag = prefixed_useflag[1:] else: useflag = prefixed_useflag if useflag_re.match(useflag) is None: writemsg(_("--- Invalid USE flag for '%s' in '%s': '%s'\n") % (k, file_name, prefixed_useflag), noiselevel=-1) else: useflags.append(prefixed_useflag) location_dict.setdefault(k, []).extend(useflags) for k, v in location_dict.items(): if juststrings: v = " ".join(v) else: v = tuple(v) ret.setdefault(k.cp, {})[k] = v return ret
def _parse_repository_packageusealiases(self, repositories): ret = {} for repo in repositories.repos_with_profiles(): file_name = os.path.join(repo.location, "profiles", "package.use.aliases") eapi = read_corresponding_eapi_file(file_name, default=repo.eapi) useflag_re = _get_useflag_re(eapi) lines = grabfile(file_name, recursive=True) file_dict = {} for line in lines: elements = line.split() atom = elements[0] try: atom = Atom(atom, eapi=eapi) except InvalidAtom: writemsg( _("--- Invalid atom in '%s': '%s'\n") % (file_name, atom)) continue if len(elements) == 1: writemsg( _("--- Missing real USE flag for '%s' in '%s'\n") % (atom, file_name), noiselevel=-1) continue real_flag = elements[1] if useflag_re.match(real_flag) is None: writemsg( _("--- Invalid real USE flag for '%s' in '%s': '%s'\n") % (atom, file_name, real_flag), noiselevel=-1) else: for alias in elements[2:]: if useflag_re.match(alias) is None: writemsg(_( "--- Invalid USE flag alias for '%s' real USE flag for '%s' in '%s': '%s'\n" ) % (real_flag, atom, file_name, alias), noiselevel=-1) else: # Duplicated USE flag aliases in entries for different atoms # matching the same package version are detected in getUseAliases(). if any(alias in v for k, v in file_dict.get( atom.cp, {}).get(atom, {}).items() if k != real_flag): writemsg(_( "--- Duplicated USE flag alias for '%s' in '%s': '%s'\n" ) % (atom, file_name, alias), noiselevel=-1) else: file_dict.setdefault(atom.cp, {}).setdefault( atom, {}).setdefault(real_flag, []).append(alias) ret[repo.name] = file_dict return ret
def _parse_file_to_tuple(self, file_name, recursive=True, eapi_filter=None, eapi=None, eapi_default="0"): """ @param file_name: input file name @type file_name: str @param recursive: triggers recursion if the input file is a directory @type recursive: bool @param eapi_filter: a function that accepts a single eapi argument, and returns true if the current file type is supported by the given EAPI @type eapi_filter: callable @param eapi: the EAPI of the current profile node, which allows a call to read_corresponding_eapi_file to be skipped @type eapi: str @param eapi_default: the default EAPI which applies if the current profile node does not define a local EAPI @type eapi_default: str @rtype: tuple @return: collection of USE flags """ ret = [] lines = grabfile(file_name, recursive=recursive) if eapi is None: eapi = read_corresponding_eapi_file(file_name, default=eapi_default) if eapi_filter is not None and not eapi_filter(eapi): if lines: writemsg( _("--- EAPI '%s' does not support '%s': '%s'\n") % (eapi, os.path.basename(file_name), file_name), noiselevel=-1, ) return () useflag_re = _get_useflag_re(eapi) for prefixed_useflag in lines: if prefixed_useflag[:1] == "-": useflag = prefixed_useflag[1:] else: useflag = prefixed_useflag if useflag_re.match(useflag) is None: writemsg( _("--- Invalid USE flag in '%s': '%s'\n") % (file_name, prefixed_useflag), noiselevel=-1, ) else: ret.append(prefixed_useflag) return tuple(ret)
def _parse_repository_packageusealiases(self, repositories): ret = {} for repo in repositories.repos_with_profiles(): file_name = os.path.join(repo.location, "profiles", "package.use.aliases") eapi = read_corresponding_eapi_file(file_name) useflag_re = _get_useflag_re(eapi) lines = grabfile(file_name, recursive=True) file_dict = {} for line in lines: elements = line.split() atom = elements[0] try: atom = Atom(atom, eapi=eapi) except InvalidAtom: writemsg(_("--- Invalid atom in '%s': '%s'\n") % (file_name, atom)) continue if len(elements) == 1: writemsg(_("--- Missing real USE flag for '%s' in '%s'\n") % (atom, file_name), noiselevel=-1) continue real_flag = elements[1] if useflag_re.match(real_flag) is None: writemsg( _("--- Invalid real USE flag for '%s' in '%s': '%s'\n") % (atom, file_name, real_flag), noiselevel=-1, ) else: for alias in elements[2:]: if useflag_re.match(alias) is None: writemsg( _("--- Invalid USE flag alias for '%s' real USE flag for '%s' in '%s': '%s'\n") % (real_flag, atom, file_name, alias), noiselevel=-1, ) else: # Duplicated USE flag aliases in entries for different atoms # matching the same package version are detected in getUseAliases(). if any( alias in v for k, v in file_dict.get(atom.cp, {}).get(atom, {}).items() if k != real_flag ): writemsg( _("--- Duplicated USE flag alias for '%s' in '%s': '%s'\n") % (atom, file_name, alias), noiselevel=-1, ) else: file_dict.setdefault(atom.cp, {}).setdefault(atom, {}).setdefault(real_flag, []).append( alias ) ret[repo.name] = file_dict return ret
def _parse_file_to_dict(self, file_name, juststrings=False, recursive=True, eapi_filter=None, user_config=False): ret = {} location_dict = {} eapi = read_corresponding_eapi_file(file_name, default=None) if eapi is None and not user_config: eapi = "0" if eapi is None: ret = ExtendedAtomDict(dict) else: ret = {} file_dict = grabdict_package(file_name, recursive=recursive, allow_wildcard=(eapi is None), allow_repo=(eapi is None), verify_eapi=(eapi is not None)) if eapi is not None and eapi_filter is not None and not eapi_filter( eapi): if file_dict: writemsg(_("--- EAPI '%s' does not support '%s': '%s'\n") % (eapi, os.path.basename(file_name), file_name), noiselevel=-1) return ret useflag_re = _get_useflag_re(eapi) for k, v in file_dict.items(): useflags = [] for prefixed_useflag in v: if prefixed_useflag[:1] == "-": useflag = prefixed_useflag[1:] else: useflag = prefixed_useflag if useflag_re.match(useflag) is None: writemsg( _("--- Invalid USE flag for '%s' in '%s': '%s'\n") % (k, file_name, prefixed_useflag), noiselevel=-1) else: useflags.append(prefixed_useflag) location_dict.setdefault(k, []).extend(useflags) for k, v in location_dict.items(): if juststrings: v = " ".join(v) else: v = tuple(v) ret.setdefault(k.cp, {})[k] = v return ret
def _parse_file_to_tuple(self, file_name, recursive=True): ret = [] lines = grabfile(file_name, recursive=recursive) eapi = read_corresponding_eapi_file(file_name) useflag_re = _get_useflag_re(eapi) for prefixed_useflag in lines: if prefixed_useflag[:1] == "-": useflag = prefixed_useflag[1:] else: useflag = prefixed_useflag if useflag_re.match(useflag) is None: writemsg(_("--- Invalid USE flag in '%s': '%s'\n") % (file_name, prefixed_useflag), noiselevel=-1) else: ret.append(prefixed_useflag) return tuple(ret)
def _parse_file_to_tuple(self, file_name, recursive=True, eapi_filter=None, eapi=None, eapi_default="0"): """ @param file_name: input file name @type file_name: str @param recursive: triggers recursion if the input file is a directory @type recursive: bool @param eapi_filter: a function that accepts a single eapi argument, and returns true if the the current file type is supported by the given EAPI @type eapi_filter: callable @param eapi: the EAPI of the current profile node, which allows a call to read_corresponding_eapi_file to be skipped @type eapi: str @param eapi_default: the default EAPI which applies if the current profile node does not define a local EAPI @type eapi_default: str @rtype: tuple @return: collection of USE flags """ ret = [] lines = grabfile(file_name, recursive=recursive) if eapi is None: eapi = read_corresponding_eapi_file( file_name, default=eapi_default) if eapi_filter is not None and not eapi_filter(eapi): if lines: writemsg(_("--- EAPI '%s' does not support '%s': '%s'\n") % (eapi, os.path.basename(file_name), file_name), noiselevel=-1) return () useflag_re = _get_useflag_re(eapi) for prefixed_useflag in lines: if prefixed_useflag[:1] == "-": useflag = prefixed_useflag[1:] else: useflag = prefixed_useflag if useflag_re.match(useflag) is None: writemsg(_("--- Invalid USE flag in '%s': '%s'\n") % (file_name, prefixed_useflag), noiselevel=-1) else: ret.append(prefixed_useflag) return tuple(ret)
def _parse_file_to_tuple(self, file_name, recursive=True, eapi_filter=None): ret = [] lines = grabfile(file_name, recursive=recursive) eapi = read_corresponding_eapi_file(file_name) if eapi_filter is not None and not eapi_filter(eapi): if lines: writemsg(_("--- EAPI '%s' does not support '%s': '%s'\n") % (eapi, os.path.basename(file_name), file_name), noiselevel=-1) return () useflag_re = _get_useflag_re(eapi) for prefixed_useflag in lines: if prefixed_useflag[:1] == "-": useflag = prefixed_useflag[1:] else: useflag = prefixed_useflag if useflag_re.match(useflag) is None: writemsg(_("--- Invalid USE flag in '%s': '%s'\n") % (file_name, prefixed_useflag), noiselevel=-1) else: ret.append(prefixed_useflag) return tuple(ret)
def _parse_repository_usealiases(self, repositories): ret = {} for repo in repositories.repos_with_profiles(): file_name = os.path.join(repo.location, "profiles", "use.aliases") eapi = read_corresponding_eapi_file(file_name, default=repo.eapi) useflag_re = _get_useflag_re(eapi) raw_file_dict = grabdict(file_name, recursive=True) file_dict = {} for real_flag, aliases in raw_file_dict.items(): if useflag_re.match(real_flag) is None: writemsg( _("--- Invalid real USE flag in '%s': '%s'\n") % (file_name, real_flag), noiselevel=-1, ) else: for alias in aliases: if useflag_re.match(alias) is None: writemsg( _("--- Invalid USE flag alias for '%s' real USE flag in '%s': '%s'\n" ) % (real_flag, file_name, alias), noiselevel=-1, ) else: if any(alias in v for k, v in file_dict.items() if k != real_flag): writemsg( _("--- Duplicated USE flag alias in '%s': '%s'\n" ) % (file_name, alias), noiselevel=-1, ) else: file_dict.setdefault(real_flag, []).append(alias) ret[repo.name] = file_dict return ret
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 load_profiles(self, repositories, known_repository_paths): known_repository_paths = set( os.path.realpath(x) for x in known_repository_paths) known_repos = [] for x in known_repository_paths: try: repo = repositories.get_repo_for_location(x) except KeyError: layout_data = parse_layout_conf(x)[0] else: layout_data = { "profile-formats": repo.profile_formats, "profile_eapi_when_unspecified": repo.eapi } # force a trailing '/' for ease of doing startswith checks known_repos.append((x + '/', layout_data)) known_repos = tuple(known_repos) if self.config_profile_path is None: deprecated_profile_path = os.path.join(self.config_root, 'etc', 'make.profile') self.config_profile_path = \ os.path.join(self.config_root, PROFILE_PATH) if isdir_raise_eaccess(self.config_profile_path): self.profile_path = self.config_profile_path if isdir_raise_eaccess(deprecated_profile_path) and not \ os.path.samefile(self.profile_path, deprecated_profile_path): # Don't warn if they refer to the same path, since # that can be used for backward compatibility with # old software. writemsg("!!! %s\n" % _("Found 2 make.profile dirs: " "using '%s', ignoring '%s'") % (self.profile_path, deprecated_profile_path), noiselevel=-1) else: self.config_profile_path = deprecated_profile_path if isdir_raise_eaccess(self.config_profile_path): self.profile_path = self.config_profile_path else: self.profile_path = None else: # NOTE: repoman may pass in an empty string # here, in order to create an empty profile # for checking dependencies of packages with # empty KEYWORDS. self.profile_path = self.config_profile_path # The symlink might not exist or might not be a symlink. self.profiles = [] self.profiles_complex = [] if self.profile_path: try: self._addProfile(os.path.realpath(self.profile_path), repositories, known_repos) except ParseError as e: if not portage._sync_mode: writemsg(_("!!! Unable to parse profile: '%s'\n") % self.profile_path, noiselevel=-1) writemsg("!!! ParseError: %s\n" % str(e), noiselevel=-1) self.profiles = [] self.profiles_complex = [] if self._user_config and self.profiles: custom_prof = os.path.join(self.config_root, CUSTOM_PROFILE_PATH) if os.path.exists(custom_prof): # For read_corresponding_eapi_file, specify default=None # in order to allow things like wildcard atoms when # is no explicit EAPI setting. self.user_profile_dir = custom_prof self.profiles.append(custom_prof) self.profiles_complex.append( _profile_node( custom_prof, True, True, ('profile-bashrcs', 'profile-set'), read_corresponding_eapi_file(custom_prof + os.sep, default=None), True)) del custom_prof self.profiles = tuple(self.profiles) self.profiles_complex = tuple(self.profiles_complex)
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, 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 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 load_profiles(self, repositories, known_repository_paths): known_repository_paths = set(os.path.realpath(x) for x in known_repository_paths) known_repos = [] for x in known_repository_paths: try: repo = repositories.get_repo_for_location(x) except KeyError: layout_data = parse_layout_conf(x)[0] else: layout_data = { "profile-formats": repo.profile_formats, "profile_eapi_when_unspecified": repo.eapi } # force a trailing '/' for ease of doing startswith checks known_repos.append((x + '/', layout_data)) known_repos = tuple(known_repos) if self.config_profile_path is None: deprecated_profile_path = os.path.join( self.config_root, 'etc', 'make.profile') self.config_profile_path = \ os.path.join(self.config_root, PROFILE_PATH) if isdir_raise_eaccess(self.config_profile_path): self.profile_path = self.config_profile_path if isdir_raise_eaccess(deprecated_profile_path) and not \ os.path.samefile(self.profile_path, deprecated_profile_path): # Don't warn if they refer to the same path, since # that can be used for backward compatibility with # old software. writemsg("!!! %s\n" % _("Found 2 make.profile dirs: " "using '%s', ignoring '%s'") % (self.profile_path, deprecated_profile_path), noiselevel=-1) else: self.config_profile_path = deprecated_profile_path if isdir_raise_eaccess(self.config_profile_path): self.profile_path = self.config_profile_path else: self.profile_path = None else: # NOTE: repoman may pass in an empty string # here, in order to create an empty profile # for checking dependencies of packages with # empty KEYWORDS. self.profile_path = self.config_profile_path # The symlink might not exist or might not be a symlink. self.profiles = [] self.profiles_complex = [] if self.profile_path: try: self._addProfile(os.path.realpath(self.profile_path), repositories, known_repos) except ParseError as e: if not portage._sync_mode: writemsg(_("!!! Unable to parse profile: '%s'\n") % self.profile_path, noiselevel=-1) writemsg("!!! ParseError: %s\n" % str(e), noiselevel=-1) self.profiles = [] self.profiles_complex = [] if self._user_config and self.profiles: custom_prof = os.path.join( self.config_root, CUSTOM_PROFILE_PATH) if os.path.exists(custom_prof): # For read_corresponding_eapi_file, specify default=None # in order to allow things like wildcard atoms when # is no explicit EAPI setting. self.user_profile_dir = custom_prof self.profiles.append(custom_prof) self.profiles_complex.append( _profile_node(custom_prof, True, True, ('profile-bashrcs', 'profile-set'), read_corresponding_eapi_file( custom_prof + os.sep, default=None))) del custom_prof self.profiles = tuple(self.profiles) self.profiles_complex = tuple(self.profiles_complex)
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', 'pms').lower().split() if 'pms' in cache_formats and not os.path.isdir( os.path.join(repo_location, 'metadata', 'cache')): cache_formats.remove('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 in ('4-python',): raw_formats = ('portage-1',) else: raw_formats = ('portage-1-compat',) else: raw_formats = set(raw_formats.split()) unknown = raw_formats.difference(['pms', 'portage-1']) 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(['pms', 'portage-1'])) data['profile-formats'] = raw_formats 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): """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_file_to_dict(self, file_name, juststrings=False, recursive=True, eapi_filter=None, user_config=False, eapi=None, eapi_default="0"): """ @param file_name: input file name @type file_name: str @param juststrings: store dict values as space-delimited strings instead of tuples @type juststrings: bool @param recursive: triggers recursion if the input file is a directory @type recursive: bool @param eapi_filter: a function that accepts a single eapi argument, and returns true if the the current file type is supported by the given EAPI @type eapi_filter: callable @param user_config: current file is part of the local configuration (not repository content) @type user_config: bool @param eapi: the EAPI of the current profile node, which allows a call to read_corresponding_eapi_file to be skipped @type eapi: str @param eapi_default: the default EAPI which applies if the current profile node does not define a local EAPI @type eapi_default: str @rtype: tuple @return: collection of USE flags """ ret = {} location_dict = {} if eapi is None: eapi = read_corresponding_eapi_file(file_name, default=eapi_default) extended_syntax = eapi is None and user_config if extended_syntax: ret = ExtendedAtomDict(dict) else: ret = {} file_dict = grabdict_package(file_name, recursive=recursive, allow_wildcard=extended_syntax, allow_repo=extended_syntax, verify_eapi=(not extended_syntax), eapi=eapi, eapi_default=eapi_default) if eapi is not None and eapi_filter is not None and not eapi_filter(eapi): if file_dict: writemsg(_("--- EAPI '%s' does not support '%s': '%s'\n") % (eapi, os.path.basename(file_name), file_name), noiselevel=-1) return ret useflag_re = _get_useflag_re(eapi) for k, v in file_dict.items(): useflags = [] for prefixed_useflag in v: if prefixed_useflag[:1] == "-": useflag = prefixed_useflag[1:] else: useflag = prefixed_useflag if useflag_re.match(useflag) is None: writemsg(_("--- Invalid USE flag for '%s' in '%s': '%s'\n") % (k, file_name, prefixed_useflag), noiselevel=-1) else: useflags.append(prefixed_useflag) location_dict.setdefault(k, []).extend(useflags) for k, v in location_dict.items(): if juststrings: v = " ".join(v) else: v = tuple(v) ret.setdefault(k.cp, {})[k] = v return ret
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 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_file_to_dict(self, file_name, juststrings=False, recursive=True, eapi_filter=None, user_config=False, eapi=None, eapi_default="0", allow_build_id=False): """ @param file_name: input file name @type file_name: str @param juststrings: store dict values as space-delimited strings instead of tuples @type juststrings: bool @param recursive: triggers recursion if the input file is a directory @type recursive: bool @param eapi_filter: a function that accepts a single eapi argument, and returns true if the the current file type is supported by the given EAPI @type eapi_filter: callable @param user_config: current file is part of the local configuration (not repository content) @type user_config: bool @param eapi: the EAPI of the current profile node, which allows a call to read_corresponding_eapi_file to be skipped @type eapi: str @param eapi_default: the default EAPI which applies if the current profile node does not define a local EAPI @type eapi_default: str @param allow_build_id: allow atoms to specify a particular build-id @type allow_build_id: bool @rtype: tuple @return: collection of USE flags """ ret = {} location_dict = {} if eapi is None: eapi = read_corresponding_eapi_file(file_name, default=eapi_default) extended_syntax = eapi is None and user_config if extended_syntax: ret = ExtendedAtomDict(dict) else: ret = {} file_dict = grabdict_package(file_name, recursive=recursive, allow_wildcard=extended_syntax, allow_repo=extended_syntax, verify_eapi=(not extended_syntax), eapi=eapi, eapi_default=eapi_default, allow_build_id=allow_build_id, allow_use=False) if eapi is not None and eapi_filter is not None and not eapi_filter( eapi): if file_dict: writemsg(_("--- EAPI '%s' does not support '%s': '%s'\n") % (eapi, os.path.basename(file_name), file_name), noiselevel=-1) return ret useflag_re = _get_useflag_re(eapi) for k, v in file_dict.items(): useflags = [] use_expand_prefix = '' for prefixed_useflag in v: if extended_syntax and prefixed_useflag == "\n": use_expand_prefix = "" continue if extended_syntax and prefixed_useflag[-1] == ":": use_expand_prefix = prefixed_useflag[:-1].lower() + "_" continue if prefixed_useflag[:1] == "-": useflag = use_expand_prefix + prefixed_useflag[1:] prefixed_useflag = "-" + useflag else: useflag = use_expand_prefix + prefixed_useflag prefixed_useflag = useflag if useflag_re.match(useflag) is None: writemsg( _("--- Invalid USE flag for '%s' in '%s': '%s'\n") % (k, file_name, prefixed_useflag), noiselevel=-1) else: useflags.append(prefixed_useflag) location_dict.setdefault(k, []).extend(useflags) for k, v in location_dict.items(): if juststrings: v = " ".join(v) else: v = tuple(v) ret.setdefault(k.cp, {})[k] = v return ret