diff options
Diffstat (limited to 'lib/portage/package/ebuild/_config/UseManager.py')
-rw-r--r-- | lib/portage/package/ebuild/_config/UseManager.py | 579 |
1 files changed, 579 insertions, 0 deletions
diff --git a/lib/portage/package/ebuild/_config/UseManager.py b/lib/portage/package/ebuild/_config/UseManager.py new file mode 100644 index 000000000..7302876ab --- /dev/null +++ b/lib/portage/package/ebuild/_config/UseManager.py @@ -0,0 +1,579 @@ +# Copyright 2010-2014 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +__all__ = ( + 'UseManager', +) + +from _emerge.Package import Package +from portage import os +from portage.dep import Atom, dep_getrepo, dep_getslot, ExtendedAtomDict, remove_slot, _get_useflag_re, _repo_separator +from portage.eapi import eapi_has_use_aliases, eapi_supports_stable_use_forcing_and_masking +from portage.exception import InvalidAtom +from portage.localization import _ +from portage.util import grabfile, grabdict, grabdict_package, read_corresponding_eapi_file, stack_lists, writemsg +from portage.versions import _pkg_str + +from portage.package.ebuild._config.helper import ordered_by_atom_specificity + +class UseManager(object): + + def __init__(self, repositories, profiles, abs_user_config, is_stable, + user_config=True): + # file variable + #-------------------------------- + # repositories + #-------------------------------- + # use.mask _repo_usemask_dict + # use.stable.mask _repo_usestablemask_dict + # use.force _repo_useforce_dict + # use.stable.force _repo_usestableforce_dict + # use.aliases _repo_usealiases_dict + # package.use.mask _repo_pusemask_dict + # package.use.stable.mask _repo_pusestablemask_dict + # package.use.force _repo_puseforce_dict + # package.use.stable.force _repo_pusestableforce_dict + # package.use.aliases _repo_pusealiases_dict + #-------------------------------- + # profiles + #-------------------------------- + # use.mask _usemask_list + # use.stable.mask _usestablemask_list + # use.force _useforce_list + # use.stable.force _usestableforce_list + # package.use.mask _pusemask_list + # package.use.stable.mask _pusestablemask_list + # package.use _pkgprofileuse + # package.use.force _puseforce_list + # package.use.stable.force _pusestableforce_list + #-------------------------------- + # user config + #-------------------------------- + # package.use _pusedict + + # Dynamic variables tracked by the config class + #-------------------------------- + # profiles + #-------------------------------- + # usemask + # useforce + #-------------------------------- + # user config + #-------------------------------- + # puse + + self._user_config = user_config + self._is_stable = is_stable + self._repo_usemask_dict = self._parse_repository_files_to_dict_of_tuples("use.mask", repositories) + self._repo_usestablemask_dict = \ + self._parse_repository_files_to_dict_of_tuples("use.stable.mask", + repositories, eapi_filter=eapi_supports_stable_use_forcing_and_masking) + self._repo_useforce_dict = self._parse_repository_files_to_dict_of_tuples("use.force", repositories) + self._repo_usestableforce_dict = \ + self._parse_repository_files_to_dict_of_tuples("use.stable.force", + repositories, eapi_filter=eapi_supports_stable_use_forcing_and_masking) + self._repo_pusemask_dict = self._parse_repository_files_to_dict_of_dicts("package.use.mask", repositories) + self._repo_pusestablemask_dict = \ + self._parse_repository_files_to_dict_of_dicts("package.use.stable.mask", + repositories, eapi_filter=eapi_supports_stable_use_forcing_and_masking) + self._repo_puseforce_dict = self._parse_repository_files_to_dict_of_dicts("package.use.force", repositories) + self._repo_pusestableforce_dict = \ + self._parse_repository_files_to_dict_of_dicts("package.use.stable.force", + repositories, eapi_filter=eapi_supports_stable_use_forcing_and_masking) + self._repo_puse_dict = self._parse_repository_files_to_dict_of_dicts("package.use", repositories) + + self._usemask_list = self._parse_profile_files_to_tuple_of_tuples("use.mask", profiles) + self._usestablemask_list = \ + self._parse_profile_files_to_tuple_of_tuples("use.stable.mask", + profiles, eapi_filter=eapi_supports_stable_use_forcing_and_masking) + self._useforce_list = self._parse_profile_files_to_tuple_of_tuples("use.force", profiles) + self._usestableforce_list = \ + self._parse_profile_files_to_tuple_of_tuples("use.stable.force", + profiles, eapi_filter=eapi_supports_stable_use_forcing_and_masking) + self._pusemask_list = self._parse_profile_files_to_tuple_of_dicts("package.use.mask", profiles) + self._pusestablemask_list = \ + self._parse_profile_files_to_tuple_of_dicts("package.use.stable.mask", + profiles, eapi_filter=eapi_supports_stable_use_forcing_and_masking) + self._pkgprofileuse = self._parse_profile_files_to_tuple_of_dicts("package.use", profiles, juststrings=True) + self._puseforce_list = self._parse_profile_files_to_tuple_of_dicts("package.use.force", profiles) + self._pusestableforce_list = \ + self._parse_profile_files_to_tuple_of_dicts("package.use.stable.force", + profiles, eapi_filter=eapi_supports_stable_use_forcing_and_masking) + + self._pusedict = self._parse_user_files_to_extatomdict("package.use", abs_user_config, user_config) + + self._repo_usealiases_dict = self._parse_repository_usealiases(repositories) + self._repo_pusealiases_dict = self._parse_repository_packageusealiases(repositories) + + self.repositories = repositories + + 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_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 + + def _parse_user_files_to_extatomdict(self, file_name, location, user_config): + ret = ExtendedAtomDict(dict) + if user_config: + pusedict = grabdict_package( + os.path.join(location, file_name), + recursive=1, newlines=1, allow_wildcard=True, + allow_repo=True, verify_eapi=False, + allow_build_id=True, allow_use=False) + for k, v in pusedict.items(): + l = [] + use_expand_prefix = '' + for flag in v: + if flag == "\n": + use_expand_prefix = "" + continue + if flag[-1] == ":": + use_expand_prefix = flag[:-1].lower() + "_" + continue + if flag[0] == "-": + nv = "-" + use_expand_prefix + flag[1:] + else: + nv = use_expand_prefix + flag + l.append(nv) + ret.setdefault(k.cp, {})[k] = tuple(l) + + return ret + + def _parse_repository_files_to_dict_of_tuples(self, file_name, repositories, eapi_filter=None): + ret = {} + for repo in repositories.repos_with_profiles(): + ret[repo.name] = self._parse_file_to_tuple( + os.path.join(repo.location, "profiles", file_name), + eapi_filter=eapi_filter, eapi_default=repo.eapi) + return ret + + def _parse_repository_files_to_dict_of_dicts(self, file_name, repositories, eapi_filter=None): + ret = {} + for repo in repositories.repos_with_profiles(): + ret[repo.name] = self._parse_file_to_dict( + os.path.join(repo.location, "profiles", file_name), + eapi_filter=eapi_filter, eapi_default=repo.eapi, + allow_build_id=("build-id" in repo.profile_formats)) + return ret + + def _parse_profile_files_to_tuple_of_tuples(self, file_name, locations, + eapi_filter=None): + return tuple(self._parse_file_to_tuple( + os.path.join(profile.location, file_name), + recursive=profile.portage1_directories, + eapi_filter=eapi_filter, eapi=profile.eapi, + eapi_default=None) for profile in locations) + + def _parse_profile_files_to_tuple_of_dicts(self, file_name, locations, + juststrings=False, eapi_filter=None): + return tuple(self._parse_file_to_dict( + os.path.join(profile.location, file_name), juststrings, + recursive=profile.portage1_directories, eapi_filter=eapi_filter, + user_config=profile.user_config, eapi=profile.eapi, + eapi_default=None, allow_build_id=profile.allow_build_id) + for profile in locations) + + 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_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 _isStable(self, pkg): + if self._user_config: + try: + return pkg.stable + except AttributeError: + # KEYWORDS is unavailable (prior to "depend" phase) + return False + + try: + pkg._metadata + except AttributeError: + # KEYWORDS is unavailable (prior to "depend" phase) + return False + + # Since repoman uses different config instances for + # different profiles, we have to be careful to do the + # stable check against the correct profile here. + return self._is_stable(pkg) + + def getUseMask(self, pkg=None, stable=None): + if pkg is None: + return frozenset(stack_lists( + self._usemask_list, incremental=True)) + + slot = None + cp = getattr(pkg, "cp", None) + if cp is None: + slot = dep_getslot(pkg) + repo = dep_getrepo(pkg) + pkg = _pkg_str(remove_slot(pkg), slot=slot, repo=repo) + cp = pkg.cp + + if stable is None: + stable = self._isStable(pkg) + + usemask = [] + + if hasattr(pkg, "repo") and pkg.repo != Package.UNKNOWN_REPO: + repos = [] + try: + repos.extend(repo.name for repo in + self.repositories[pkg.repo].masters) + except KeyError: + pass + repos.append(pkg.repo) + for repo in repos: + usemask.append(self._repo_usemask_dict.get(repo, {})) + if stable: + usemask.append(self._repo_usestablemask_dict.get(repo, {})) + cpdict = self._repo_pusemask_dict.get(repo, {}).get(cp) + if cpdict: + pkg_usemask = ordered_by_atom_specificity(cpdict, pkg) + if pkg_usemask: + usemask.extend(pkg_usemask) + if stable: + cpdict = self._repo_pusestablemask_dict.get(repo, {}).get(cp) + if cpdict: + pkg_usemask = ordered_by_atom_specificity(cpdict, pkg) + if pkg_usemask: + usemask.extend(pkg_usemask) + + for i, pusemask_dict in enumerate(self._pusemask_list): + if self._usemask_list[i]: + usemask.append(self._usemask_list[i]) + if stable and self._usestablemask_list[i]: + usemask.append(self._usestablemask_list[i]) + cpdict = pusemask_dict.get(cp) + if cpdict: + pkg_usemask = ordered_by_atom_specificity(cpdict, pkg) + if pkg_usemask: + usemask.extend(pkg_usemask) + if stable: + cpdict = self._pusestablemask_list[i].get(cp) + if cpdict: + pkg_usemask = ordered_by_atom_specificity(cpdict, pkg) + if pkg_usemask: + usemask.extend(pkg_usemask) + + return frozenset(stack_lists(usemask, incremental=True)) + + def getUseForce(self, pkg=None, stable=None): + if pkg is None: + return frozenset(stack_lists( + self._useforce_list, incremental=True)) + + cp = getattr(pkg, "cp", None) + if cp is None: + slot = dep_getslot(pkg) + repo = dep_getrepo(pkg) + pkg = _pkg_str(remove_slot(pkg), slot=slot, repo=repo) + cp = pkg.cp + + if stable is None: + stable = self._isStable(pkg) + + useforce = [] + + if hasattr(pkg, "repo") and pkg.repo != Package.UNKNOWN_REPO: + repos = [] + try: + repos.extend(repo.name for repo in + self.repositories[pkg.repo].masters) + except KeyError: + pass + repos.append(pkg.repo) + for repo in repos: + useforce.append(self._repo_useforce_dict.get(repo, {})) + if stable: + useforce.append(self._repo_usestableforce_dict.get(repo, {})) + cpdict = self._repo_puseforce_dict.get(repo, {}).get(cp) + if cpdict: + pkg_useforce = ordered_by_atom_specificity(cpdict, pkg) + if pkg_useforce: + useforce.extend(pkg_useforce) + if stable: + cpdict = self._repo_pusestableforce_dict.get(repo, {}).get(cp) + if cpdict: + pkg_useforce = ordered_by_atom_specificity(cpdict, pkg) + if pkg_useforce: + useforce.extend(pkg_useforce) + + for i, puseforce_dict in enumerate(self._puseforce_list): + if self._useforce_list[i]: + useforce.append(self._useforce_list[i]) + if stable and self._usestableforce_list[i]: + useforce.append(self._usestableforce_list[i]) + cpdict = puseforce_dict.get(cp) + if cpdict: + pkg_useforce = ordered_by_atom_specificity(cpdict, pkg) + if pkg_useforce: + useforce.extend(pkg_useforce) + if stable: + cpdict = self._pusestableforce_list[i].get(cp) + if cpdict: + pkg_useforce = ordered_by_atom_specificity(cpdict, pkg) + if pkg_useforce: + useforce.extend(pkg_useforce) + + return frozenset(stack_lists(useforce, incremental=True)) + + def getUseAliases(self, pkg): + if hasattr(pkg, "eapi") and not eapi_has_use_aliases(pkg.eapi): + return {} + + cp = getattr(pkg, "cp", None) + if cp is None: + slot = dep_getslot(pkg) + repo = dep_getrepo(pkg) + pkg = _pkg_str(remove_slot(pkg), slot=slot, repo=repo) + cp = pkg.cp + + usealiases = {} + + if hasattr(pkg, "repo") and pkg.repo != Package.UNKNOWN_REPO: + repos = [] + try: + repos.extend(repo.name for repo in + self.repositories[pkg.repo].masters) + except KeyError: + pass + repos.append(pkg.repo) + for repo in repos: + usealiases_dict = self._repo_usealiases_dict.get(repo, {}) + for real_flag, aliases in usealiases_dict.items(): + for alias in aliases: + if any(alias in v for k, v in usealiases.items() if k != real_flag): + writemsg(_("--- Duplicated USE flag alias for '%s%s%s': '%s'\n") % + (pkg.cpv, _repo_separator, pkg.repo, alias), noiselevel=-1) + else: + usealiases.setdefault(real_flag, []).append(alias) + cp_usealiases_dict = self._repo_pusealiases_dict.get(repo, {}).get(cp) + if cp_usealiases_dict: + usealiases_dict_list = ordered_by_atom_specificity(cp_usealiases_dict, pkg) + for usealiases_dict in usealiases_dict_list: + for real_flag, aliases in usealiases_dict.items(): + for alias in aliases: + if any(alias in v for k, v in usealiases.items() if k != real_flag): + writemsg(_("--- Duplicated USE flag alias for '%s%s%s': '%s'\n") % + (pkg.cpv, _repo_separator, pkg.repo, alias), noiselevel=-1) + else: + usealiases.setdefault(real_flag, []).append(alias) + + return usealiases + + def getPUSE(self, pkg): + cp = getattr(pkg, "cp", None) + if cp is None: + slot = dep_getslot(pkg) + repo = dep_getrepo(pkg) + pkg = _pkg_str(remove_slot(pkg), slot=slot, repo=repo) + cp = pkg.cp + ret = "" + cpdict = self._pusedict.get(cp) + if cpdict: + puse_matches = ordered_by_atom_specificity(cpdict, pkg) + if puse_matches: + puse_list = [] + for x in puse_matches: + puse_list.extend(x) + ret = " ".join(puse_list) + return ret + + def extract_global_USE_changes(self, old=""): + ret = old + cpdict = self._pusedict.get("*/*") + if cpdict is not None: + v = cpdict.pop("*/*", None) + if v is not None: + ret = " ".join(v) + if old: + ret = old + " " + ret + if not cpdict: + #No tokens left in atom_license_map, remove it. + del self._pusedict["*/*"] + return ret |