| # Copyright 2010-2012 Gentoo Foundation |
| # Distributed under the terms of the GNU General Public License v2 |
| |
| __all__ = ( |
| 'KeywordsManager', |
| ) |
| |
| from _emerge.Package import Package |
| from portage import os |
| from portage.dep import ExtendedAtomDict, _repo_separator, _slot_separator |
| from portage.localization import _ |
| from portage.package.ebuild._config.helper import ordered_by_atom_specificity |
| from portage.util import grabdict_package, stack_lists, writemsg |
| from portage.versions import _pkg_str |
| |
| class KeywordsManager(object): |
| """Manager class to handle keywords processing and validation""" |
| |
| def __init__(self, profiles, abs_user_config, user_config=True, |
| global_accept_keywords=""): |
| self._pkeywords_list = [] |
| rawpkeywords = [grabdict_package( |
| os.path.join(x.location, "package.keywords"), |
| recursive=x.portage1_directories, |
| verify_eapi=True) \ |
| for x in profiles] |
| for pkeyworddict in rawpkeywords: |
| if not pkeyworddict: |
| # Omit non-existent files from the stack. |
| continue |
| cpdict = {} |
| for k, v in pkeyworddict.items(): |
| cpdict.setdefault(k.cp, {})[k] = v |
| self._pkeywords_list.append(cpdict) |
| self._pkeywords_list = tuple(self._pkeywords_list) |
| |
| self._p_accept_keywords = [] |
| raw_p_accept_keywords = [grabdict_package( |
| os.path.join(x.location, "package.accept_keywords"), |
| recursive=x.portage1_directories, |
| verify_eapi=True) \ |
| for x in profiles] |
| for d in raw_p_accept_keywords: |
| if not d: |
| # Omit non-existent files from the stack. |
| continue |
| cpdict = {} |
| for k, v in d.items(): |
| cpdict.setdefault(k.cp, {})[k] = tuple(v) |
| self._p_accept_keywords.append(cpdict) |
| self._p_accept_keywords = tuple(self._p_accept_keywords) |
| |
| self.pkeywordsdict = ExtendedAtomDict(dict) |
| |
| if user_config: |
| pkgdict = grabdict_package( |
| os.path.join(abs_user_config, "package.keywords"), |
| recursive=1, allow_wildcard=True, allow_repo=True, |
| verify_eapi=False) |
| |
| for k, v in grabdict_package( |
| os.path.join(abs_user_config, "package.accept_keywords"), |
| recursive=1, allow_wildcard=True, allow_repo=True, |
| verify_eapi=False).items(): |
| pkgdict.setdefault(k, []).extend(v) |
| |
| accept_keywords_defaults = global_accept_keywords.split() |
| accept_keywords_defaults = tuple('~' + keyword for keyword in \ |
| accept_keywords_defaults if keyword[:1] not in "~-") |
| for k, v in pkgdict.items(): |
| # default to ~arch if no specific keyword is given |
| if not v: |
| v = accept_keywords_defaults |
| else: |
| v = tuple(v) |
| self.pkeywordsdict.setdefault(k.cp, {})[k] = v |
| |
| |
| def getKeywords(self, cpv, slot, keywords, repo): |
| try: |
| cpv.slot |
| except AttributeError: |
| pkg = _pkg_str(cpv, slot=slot, repo=repo) |
| else: |
| pkg = cpv |
| cp = pkg.cp |
| keywords = [[x for x in keywords.split() if x != "-*"]] |
| for pkeywords_dict in self._pkeywords_list: |
| cpdict = pkeywords_dict.get(cp) |
| if cpdict: |
| pkg_keywords = ordered_by_atom_specificity(cpdict, pkg) |
| if pkg_keywords: |
| keywords.extend(pkg_keywords) |
| return stack_lists(keywords, incremental=True) |
| |
| def isStable(self, pkg, global_accept_keywords, backuped_accept_keywords): |
| mygroups = self.getKeywords(pkg, None, pkg._metadata["KEYWORDS"], None) |
| pgroups = global_accept_keywords.split() |
| |
| unmaskgroups = self.getPKeywords(pkg, None, None, |
| global_accept_keywords) |
| pgroups.extend(unmaskgroups) |
| |
| egroups = backuped_accept_keywords.split() |
| |
| if unmaskgroups or egroups: |
| pgroups = self._getEgroups(egroups, pgroups) |
| else: |
| pgroups = set(pgroups) |
| |
| if self._getMissingKeywords(pkg, pgroups, mygroups): |
| return False |
| |
| if pkg.cpv._settings.local_config: |
| # If replacing all keywords with unstable variants would mask the |
| # package, then it's considered stable. |
| unstable = [] |
| for kw in mygroups: |
| if kw[:1] != "~": |
| kw = "~" + kw |
| unstable.append(kw) |
| |
| return bool(self._getMissingKeywords(pkg, pgroups, set(unstable))) |
| else: |
| # For repoman, if the package has an effective stable keyword that |
| # intersects with the effective ACCEPT_KEYWORDS for the current |
| # profile, then consider it stable. |
| for kw in pgroups: |
| if kw[:1] != "~": |
| if kw in mygroups or '*' in mygroups: |
| return True |
| if kw == '*': |
| for x in mygroups: |
| if x[:1] != "~": |
| return True |
| return False |
| |
| def getMissingKeywords(self, |
| cpv, |
| slot, |
| keywords, |
| repo, |
| global_accept_keywords, |
| backuped_accept_keywords): |
| """ |
| Take a package and return a list of any KEYWORDS that the user may |
| need to accept for the given package. If the KEYWORDS are empty |
| and the the ** keyword has not been accepted, the returned list will |
| contain ** alone (in order to distinguish from the case of "none |
| missing"). |
| |
| @param cpv: The package name (for package.keywords support) |
| @type cpv: String |
| @param slot: The 'SLOT' key from the raw package metadata |
| @type slot: String |
| @param keywords: The 'KEYWORDS' key from the raw package metadata |
| @type keywords: String |
| @param global_accept_keywords: The current value of ACCEPT_KEYWORDS |
| @type global_accept_keywords: String |
| @param backuped_accept_keywords: ACCEPT_KEYWORDS from the backup env |
| @type backuped_accept_keywords: String |
| @rtype: List |
| @return: A list of KEYWORDS that have not been accepted. |
| """ |
| |
| mygroups = self.getKeywords(cpv, slot, keywords, repo) |
| # Repoman may modify this attribute as necessary. |
| pgroups = global_accept_keywords.split() |
| |
| unmaskgroups = self.getPKeywords(cpv, slot, repo, |
| global_accept_keywords) |
| pgroups.extend(unmaskgroups) |
| |
| # Hack: Need to check the env directly here as otherwise stacking |
| # doesn't work properly as negative values are lost in the config |
| # object (bug #139600) |
| egroups = backuped_accept_keywords.split() |
| |
| if unmaskgroups or egroups: |
| pgroups = self._getEgroups(egroups, pgroups) |
| else: |
| pgroups = set(pgroups) |
| |
| return self._getMissingKeywords(cpv, pgroups, mygroups) |
| |
| |
| def getRawMissingKeywords(self, |
| cpv, |
| slot, |
| keywords, |
| repo, |
| global_accept_keywords): |
| """ |
| Take a package and return a list of any KEYWORDS that the user may |
| need to accept for the given package. If the KEYWORDS are empty, |
| the returned list will contain ** alone (in order to distinguish |
| from the case of "none missing"). This DOES NOT apply any user config |
| package.accept_keywords acceptance. |
| |
| @param cpv: The package name (for package.keywords support) |
| @type cpv: String |
| @param slot: The 'SLOT' key from the raw package metadata |
| @type slot: String |
| @param keywords: The 'KEYWORDS' key from the raw package metadata |
| @type keywords: String |
| @param global_accept_keywords: The current value of ACCEPT_KEYWORDS |
| @type global_accept_keywords: String |
| @rtype: List |
| @return: lists of KEYWORDS that have not been accepted |
| and the keywords it looked for. |
| """ |
| |
| mygroups = self.getKeywords(cpv, slot, keywords, repo) |
| pgroups = global_accept_keywords.split() |
| pgroups = set(pgroups) |
| return self._getMissingKeywords(cpv, pgroups, mygroups) |
| |
| |
| @staticmethod |
| def _getEgroups(egroups, mygroups): |
| """gets any keywords defined in the environment |
| |
| @param backuped_accept_keywords: ACCEPT_KEYWORDS from the backup env |
| @type backuped_accept_keywords: String |
| @rtype: List |
| @return: list of KEYWORDS that have been accepted |
| """ |
| mygroups = list(mygroups) |
| mygroups.extend(egroups) |
| inc_pgroups = set() |
| for x in mygroups: |
| if x[:1] == "-": |
| if x == "-*": |
| inc_pgroups.clear() |
| else: |
| inc_pgroups.discard(x[1:]) |
| else: |
| inc_pgroups.add(x) |
| return inc_pgroups |
| |
| |
| @staticmethod |
| def _getMissingKeywords(cpv, pgroups, mygroups): |
| """Determines the missing keywords |
| |
| @param pgroups: The pkg keywords accepted |
| @type pgroups: list |
| @param mygroups: The ebuild keywords |
| @type mygroups: list |
| """ |
| match = False |
| hasstable = False |
| hastesting = False |
| for gp in mygroups: |
| if gp == "*": |
| match = True |
| break |
| elif gp == "~*": |
| hastesting = True |
| for x in pgroups: |
| if x[:1] == "~": |
| match = True |
| break |
| if match: |
| break |
| elif gp in pgroups: |
| match = True |
| break |
| elif gp.startswith("~"): |
| hastesting = True |
| elif not gp.startswith("-"): |
| hasstable = True |
| if not match and \ |
| ((hastesting and "~*" in pgroups) or \ |
| (hasstable and "*" in pgroups) or "**" in pgroups): |
| match = True |
| if match: |
| missing = [] |
| else: |
| if not mygroups: |
| # If KEYWORDS is empty then we still have to return something |
| # in order to distinguish from the case of "none missing". |
| mygroups = ["**"] |
| missing = mygroups |
| return missing |
| |
| |
| def getPKeywords(self, cpv, slot, repo, global_accept_keywords): |
| """Gets any package.keywords settings for cp for the given |
| cpv, slot and repo |
| |
| @param cpv: The package name (for package.keywords support) |
| @type cpv: String |
| @param slot: The 'SLOT' key from the raw package metadata |
| @type slot: String |
| @param keywords: The 'KEYWORDS' key from the raw package metadata |
| @type keywords: String |
| @param global_accept_keywords: The current value of ACCEPT_KEYWORDS |
| @type global_accept_keywords: String |
| @param backuped_accept_keywords: ACCEPT_KEYWORDS from the backup env |
| @type backuped_accept_keywords: String |
| @rtype: List |
| @return: list of KEYWORDS that have been accepted |
| """ |
| |
| pgroups = global_accept_keywords.split() |
| try: |
| cpv.slot |
| except AttributeError: |
| cpv = _pkg_str(cpv, slot=slot, repo=repo) |
| cp = cpv.cp |
| |
| unmaskgroups = [] |
| if self._p_accept_keywords: |
| accept_keywords_defaults = tuple('~' + keyword for keyword in \ |
| pgroups if keyword[:1] not in "~-") |
| for d in self._p_accept_keywords: |
| cpdict = d.get(cp) |
| if cpdict: |
| pkg_accept_keywords = \ |
| ordered_by_atom_specificity(cpdict, cpv) |
| if pkg_accept_keywords: |
| for x in pkg_accept_keywords: |
| if not x: |
| x = accept_keywords_defaults |
| unmaskgroups.extend(x) |
| |
| pkgdict = self.pkeywordsdict.get(cp) |
| if pkgdict: |
| pkg_accept_keywords = \ |
| ordered_by_atom_specificity(pkgdict, cpv) |
| if pkg_accept_keywords: |
| for x in pkg_accept_keywords: |
| unmaskgroups.extend(x) |
| return unmaskgroups |