From b583812101f1156c553385effcd9dbee0b751087 Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Mon, 26 Jul 2010 21:16:58 +0200 Subject: repoman: Check if the prefix.eclass is inherited if eprefixify is used. Thanks to Jeremy Olexa (darkside) for the initial patch. --- bin/repoman | 1 + man/repoman.1 | 3 +++ pym/repoman/checks.py | 20 +++++++++++++++++++- pym/repoman/errors.py | 1 + 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/bin/repoman b/bin/repoman index 53b9ad0c..fc184e2f 100755 --- a/bin/repoman +++ b/bin/repoman @@ -339,6 +339,7 @@ qahelp={ "ebuild.majorsyn":"This ebuild has a major syntax error that may cause the ebuild to fail partially or fully", "ebuild.minorsyn":"This ebuild has a minor syntax error that contravenes gentoo coding style", "ebuild.badheader":"This ebuild has a malformed header", + "eprefixify.defined":"The ebuild uses eprefixify, but does not inherit the prefix eclass", "manifest.bad":"Manifest has missing or incorrect digests", "metadata.missing":"Missing metadata.xml files", "metadata.bad":"Bad metadata.xml files", diff --git a/man/repoman.1 b/man/repoman.1 index 58165bb7..ce9d0ba5 100644 --- a/man/repoman.1 +++ b/man/repoman.1 @@ -282,6 +282,9 @@ PATCHES variable should be a bash array to ensure white space safety Error generating cache entry for ebuild; typically caused by ebuild syntax error or digest verification failure. .TP +.B eprefixify.defined +The ebuild uses eprefixify, but does not inherit the prefix eclass +.TP .B file.UTF8 File is not UTF8 compliant .TP diff --git a/pym/repoman/checks.py b/pym/repoman/checks.py index d403044b..5588fa86 100644 --- a/pym/repoman/checks.py +++ b/pym/repoman/checks.py @@ -306,6 +306,24 @@ class EbuildQuotedA(LineCheck): if match: return "Quoted \"${A}\" on line: %d" +class EprefixifyDefined(LineCheck): + """ Check that prefix.eclass is inherited if needed""" + + repoman_check_name = 'eprefixify.defined' + + _eprefixify_re = re.compile(r'\beprefixify\b') + _inherit_prefix_re = re.compile(r'^\s*inherit\s(.*\s)?prefix\b') + + def new(self, pkg): + self._prefix_inherited = False + + def check(self, num, line): + if self._eprefixify_re.search(line) is not None: + if not self._prefix_inherited: + return errors.EPREFIXIFY_MISSING_INHERIT + elif self._inherit_prefix_re.search(line) is not None: + self._prefix_inherited = True + class InheritAutotools(LineCheck): """ Make sure appropriate functions are called in @@ -493,7 +511,7 @@ _constant_checks = tuple((c() for c in ( EbuildHeader, EbuildWhitespace, EbuildBlankLine, EbuildQuote, EbuildAssignment, Eapi3EbuildAssignment, EbuildUselessDodoc, EbuildUselessCdS, EbuildNestedDie, - EbuildPatches, EbuildQuotedA, EapiDefinition, + EbuildPatches, EbuildQuotedA, EapiDefinition, EprefixifyDefined, IUseUndefined, InheritAutotools, EMakeParallelDisabled, EMakeParallelDisabledViaMAKEOPTS, NoAsNeeded, DeprecatedBindnowFlags, SrcUnpackPatches, WantAutoDefaultValue, diff --git a/pym/repoman/errors.py b/pym/repoman/errors.py index 97bd2829..8a28d4fd 100644 --- a/pym/repoman/errors.py +++ b/pym/repoman/errors.py @@ -19,3 +19,4 @@ EAPI_DEFINED_AFTER_INHERIT = 'EAPI defined after inherit on line: %d' NO_AS_NEEDED = 'Upstream asneeded linking bug (no-as-needed on line: %d)' PRESERVE_OLD_LIB = 'Upstream ABI change workaround on line: %d' BUILT_WITH_USE = 'built_with_use on line: %d' +EPREFIXIFY_MISSING_INHERIT = "prefix.eclass is not inherited, but eprefixify is used on line: %d" -- cgit v1.2.3-65-gdbad From 3b9488a32d6f170387b6dfbf1985e87db556f998 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Wed, 4 Aug 2010 22:06:59 -0700 Subject: Bug #285191 - Add back the RDEPEND.implicit warning to detect the cases where DEPEND is set and RDEPEND is unset in the ebuild, since this triggers implicit RDEPEND=$DEPEND assignment (prior to EAPI 4) and is forbidden by the QA team. --- bin/repoman | 2 +- man/repoman.1 | 4 ++++ pym/repoman/checks.py | 35 ++++++++++++++++++++++++++++++++++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/bin/repoman b/bin/repoman index fc184e2f..9f64701b 100755 --- a/bin/repoman +++ b/bin/repoman @@ -328,7 +328,7 @@ qahelp={ "IUSE.undefined":"This ebuild does not define IUSE (style guideline says to define IUSE even when empty)", "LICENSE.invalid":"This ebuild is listing a license that doesnt exist in portages license/ dir.", "KEYWORDS.invalid":"This ebuild contains KEYWORDS that are not listed in profiles/arch.list or for which no valid profile was found", - "RDEPEND.implicit":"RDEPEND is unset in the ebuild which triggers implicit RDEPEND=$DEPEND assignment", + "RDEPEND.implicit":"RDEPEND is unset in the ebuild which triggers implicit RDEPEND=$DEPEND assignment (prior to EAPI 4)", "RDEPEND.suspect":"RDEPEND contains a package that usually only belongs in DEPEND.", "RESTRICT.invalid":"This ebuild contains invalid RESTRICT values.", "digest.assumed":"Existing digest must be assumed correct (Package level only)", diff --git a/man/repoman.1 b/man/repoman.1 index ce9d0ba5..ad4c74cb 100644 --- a/man/repoman.1 +++ b/man/repoman.1 @@ -210,6 +210,10 @@ Masked ebuilds with RDEPEND settings (matched against *all* ebuilds) in developi .B RDEPEND.badtilde RDEPEND uses the ~ dep operator with a non-zero revision part, which is useless (the revision is ignored) .TP +.B RDEPEND.implicit +RDEPEND is unset in the ebuild which triggers implicit RDEPEND=$DEPEND +assignment (prior to EAPI 4) +.TP .B RDEPEND.suspect RDEPEND contains a package that usually only belongs in DEPEND .TP diff --git a/pym/repoman/checks.py b/pym/repoman/checks.py index 5588fa86..7e76bf78 100644 --- a/pym/repoman/checks.py +++ b/pym/repoman/checks.py @@ -324,6 +324,39 @@ class EprefixifyDefined(LineCheck): elif self._inherit_prefix_re.search(line) is not None: self._prefix_inherited = True +class ImplicitRuntimeDeps(LineCheck): + """ + Detect the case where DEPEND is set and RDEPEND is unset in the ebuild, + since this triggers implicit RDEPEND=$DEPEND assignment (prior to EAPI 4). + """ + + repoman_check_name = 'RDEPEND.implicit' + _assignment_re = re.compile(r'^\s*(R?DEPEND)=') + + def new(self, pkg): + self._rdepend = False + self._depend = False + + def check_eapi(self, eapi): + # Beginning with EAPI 4, there is no + # implicit RDEPEND=$DEPEND assignment + # to be concerned with. + return eapi in ('0', '1', '2', '3') + + def check(self, num, line): + if not self._rdepend: + m = self._assignment_re.match(line) + if m is None: + pass + elif m.group(1) == "RDEPEND": + self._rdepend = True + elif m.group(1) == "DEPEND": + self._depend = True + + def end(self): + if self._depend and not self._rdepend: + yield 'RDEPEND is not explicitly assigned' + class InheritAutotools(LineCheck): """ Make sure appropriate functions are called in @@ -512,7 +545,7 @@ _constant_checks = tuple((c() for c in ( EbuildAssignment, Eapi3EbuildAssignment, EbuildUselessDodoc, EbuildUselessCdS, EbuildNestedDie, EbuildPatches, EbuildQuotedA, EapiDefinition, EprefixifyDefined, - IUseUndefined, InheritAutotools, + ImplicitRuntimeDeps, InheritAutotools, IUseUndefined, EMakeParallelDisabled, EMakeParallelDisabledViaMAKEOPTS, NoAsNeeded, DeprecatedBindnowFlags, SrcUnpackPatches, WantAutoDefaultValue, SrcCompileEconf, Eapi3DeprecatedFuncs, -- cgit v1.2.3-65-gdbad From 8ed07e3ebab02e760dc499aff4ff95e62f70c208 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Wed, 4 Aug 2010 22:22:44 -0700 Subject: Bug #330179 - Fix depgraph._show_unsatisfied_dep() to show a masked package when possible, instead of 'Missing IUSE' message for an unmasked package. --- pym/_emerge/depgraph.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 1c40088a..22f9b1f1 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -1,4 +1,4 @@ -# Copyright 1999-2009 Gentoo Foundation +# Copyright 1999-2010 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from __future__ import print_function @@ -2296,7 +2296,7 @@ class depgraph(object): break elif unmasked_iuse_reasons: - if missing_use_reasons: + if masked_packages: # All packages with required IUSE are masked, # so display a normal masking message. pass -- cgit v1.2.3-65-gdbad From 23703fd2216045d0d4a0897e2993f2a8c3916eed Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Wed, 4 Aug 2010 22:32:52 -0700 Subject: Bug #330179 - Ensure that depgraph._show_unsatisfied_dep() only shows masked packages if one of them satisfies required IUSE. --- pym/_emerge/depgraph.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 22f9b1f1..0fa30a7d 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -2296,11 +2296,14 @@ class depgraph(object): break elif unmasked_iuse_reasons: - if masked_packages: - # All packages with required IUSE are masked, - # so display a normal masking message. - pass - else: + masked_with_iuse = False + for pkg in masked_pkg_instances: + if not pkg.iuse.get_missing_iuse(atom.use.required): + # Package(s) with required IUSE are masked, + # so display a normal masking message. + masked_with_iuse = True + break + if not masked_with_iuse: show_missing_use = unmasked_iuse_reasons mask_docs = False -- cgit v1.2.3-65-gdbad From b1e8c0e266cf875787cf44ee22458008845d22a4 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Wed, 4 Aug 2010 23:18:52 -0700 Subject: Bug #308835 - Make econf() use sed to substitute $CONFIG_SHELL in the shebang of configure scripts when appropriate. --- bin/ebuild.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bin/ebuild.sh b/bin/ebuild.sh index 9c599c01..a68b9731 100755 --- a/bin/ebuild.sh +++ b/bin/ebuild.sh @@ -479,6 +479,11 @@ econf() { : ${ECONF_SOURCE:=.} if [ -x "${ECONF_SOURCE}/configure" ]; then + if [[ -n $CONFIG_SHELL && \ + "$(head -n1 "$ECONF_SOURCE/configure")" =~ ^'#!'[[:space:]]*/bin/sh[[:space:]]*$ ]] ; then + sed -e "1s:.*:#!$CONFIG_SHELL" -i "$ECONF_SOURCE/configure" || \ + die "Substition of shebang in '$ECONF_SOURCE/configure' failed" + fi if [ -e /usr/share/gnuconfig/ ]; then find "${WORKDIR}" -type f '(' \ -name config.guess -o -name config.sub ')' -print0 | \ -- cgit v1.2.3-65-gdbad From 320a9800e157756f79f1774654774cc460ada5b1 Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Wed, 4 Aug 2010 23:31:21 +0200 Subject: Automatically take packages masked by ~arch if needed and inform the user --- pym/_emerge/depgraph.py | 93 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 4 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 0fa30a7d..82d85ba0 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -168,6 +168,7 @@ class _dynamic_depgraph_config(object): self._missing_args = [] self._masked_installed = set() self._masked_license_updates = set() + self._needed_user_config_changes = {} self._unsatisfied_deps_for_display = [] self._unsatisfied_blockers_for_display = None self._circular_deps_for_display = None @@ -1971,6 +1972,11 @@ class depgraph(object): except self._unknown_internal_error: return False, myfavorites + if set(self._dynamic_config.digraph.nodes.keys()).intersection( \ + set(self._dynamic_config._needed_user_config_changes.keys())): + #We failed if the user needs to change the configuration + return False, myfavorites + # We're true here unless we are missing binaries. return (not missing,myfavorites) @@ -2471,6 +2477,31 @@ class depgraph(object): return ret def _select_pkg_highest_available_imp(self, root, atom, onlydeps=False): + pkg, existing = self._wrapped_select_pkg_highest_available_imp(root, atom, onlydeps=onlydeps) + + if pkg is None: + pkg, existing = self._wrapped_select_pkg_highest_available_imp(root, atom, \ + onlydeps=onlydeps, allow_missing_keywords=True) + + if pkg is not None and not pkg.visible: + self._dynamic_config._needed_user_config_changes.setdefault(pkg, set()).add("unstable keyword") + + return pkg, existing + + def _pkg_visibility_check(self, pkg, root, allow_missing_keywords=False): + pkgsettings = self._frozen_config.pkgsettings[root] + root_config = self._frozen_config.roots[root] + if pkg.visible: + return True + if not allow_missing_keywords: + return False + mreasons = get_masking_status(pkg, pkgsettings, root_config) + if len(mreasons) == 1 and mreasons[0].startswith("~") and mreasons[0].endswith("keyword"): + return True + else: + return False + + def _wrapped_select_pkg_highest_available_imp(self, root, atom, onlydeps=False, allow_missing_keywords=False): root_config = self._frozen_config.roots[root] pkgsettings = self._frozen_config.pkgsettings[root] dbs = self._dynamic_config._filtered_trees[root]["dbs"] @@ -2567,7 +2598,7 @@ class depgraph(object): # were installed can be automatically downgraded # to an unmasked version. - if not pkg.visible: + if not self._pkg_visibility_check(pkg, root, allow_missing_keywords): continue # Enable upgrade or downgrade to a version @@ -2601,7 +2632,7 @@ class depgraph(object): except portage.exception.PackageNotFound: continue else: - if not pkg_eb.visible: + if not self._pkg_visibility_check(pkg_eb, root, allow_missing_keywords): continue # Calculation of USE for unbuilt ebuilds is relatively @@ -2785,11 +2816,11 @@ class depgraph(object): if avoid_update: for pkg in matched_packages: - if pkg.installed and pkg.visible: + if pkg.installed and self._pkg_visibility_check(pkg, root, allow_missing_keywords): return pkg, existing_node bestmatch = portage.best( - [pkg.cpv for pkg in matched_packages if pkg.visible]) + [pkg.cpv for pkg in matched_packages if self._pkg_visibility_check(pkg, root, allow_missing_keywords)]) if not bestmatch: # all are masked, so ignore visibility bestmatch = portage.best( @@ -5046,6 +5077,60 @@ class depgraph(object): else: self._show_missed_update() + def get_dep_chain(pkg): + traversed_nodes = set() + msg = "#" + node = pkg + first = True + while node is not None: + traversed_nodes.add(node) + if node is not pkg: + if first: + first = False + else: + msg += ", " + msg += 'required by =%s' % node.cpv + + if node not in self._dynamic_config.digraph: + # The parent is not in the graph due to backtracking. + break + + # When traversing to parents, prefer arguments over packages + # since arguments are root nodes. Never traverse the same + # package twice, in order to prevent an infinite loop. + selected_parent = None + for parent in self._dynamic_config.digraph.parent_nodes(node): + if isinstance(parent, DependencyArg): + if first: + first = False + else: + msg += ", " + msg += 'required by %s (argument)' % str(parent) + selected_parent = None + break + if parent not in traversed_nodes: + selected_parent = parent + node = selected_parent + msg += "\n" + return msg + + unstable_keyword_msg = [] + for pkg, changes in self._dynamic_config._needed_user_config_changes.items(): + self._show_merge_list() + if pkg in self._dynamic_config.digraph.nodes.keys(): + for change in changes: + if change == "unstable keyword": + pkgsettings = self._frozen_config.pkgsettings[pkg.root] + unstable_keyword_msg.append(get_dep_chain(pkg)) + unstable_keyword_msg.append("=%s ~%s\n" % (pkg.cpv, pkgsettings["ACCEPT_KEYWORDS"])) + else: + raise NotImplementedError() + + if unstable_keyword_msg: + writemsg_stdout("\nThe following " + colorize("BAD", "keyword changes") + \ + " are necessary to proceed:\n", noiselevel=-1) + writemsg_stdout("".join(unstable_keyword_msg), noiselevel=-1) + # TODO: Add generic support for "set problem" handlers so that # the below warnings aren't special cases for world only. -- cgit v1.2.3-65-gdbad From 65f2723805697c89269d8f02e550c16291f7e484 Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Thu, 5 Aug 2010 08:52:27 +0200 Subject: _emerge.depgraph: Use _pkg_visibility_check() everywhere --- pym/_emerge/depgraph.py | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 82d85ba0..5924d7b5 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -1283,7 +1283,7 @@ class depgraph(object): inst_pkgs = vardb.match_pkgs(atom) if inst_pkgs: for inst_pkg in inst_pkgs: - if inst_pkg.visible: + if self._pkg_visibility_check(inst_pkg): # highest visible mypriority.satisfied = inst_pkg break @@ -1329,7 +1329,7 @@ class depgraph(object): inst_pkgs = vardb.match_pkgs(atom) if inst_pkgs: for inst_pkg in inst_pkgs: - if inst_pkg.visible: + if self._pkg_visibility_check(inst_pkg): # highest visible mypriority.satisfied = inst_pkg break @@ -2471,7 +2471,7 @@ class depgraph(object): pkg, existing = ret if pkg is not None: settings = pkg.root_config.settings - if pkg.visible and not (pkg.installed and \ + if self._pkg_visibility_check(pkg) and not (pkg.installed and \ settings._getMissingKeywords(pkg.cpv, pkg.metadata)): self._dynamic_config._visible_pkgs[pkg.root].cpv_inject(pkg) return ret @@ -2488,11 +2488,17 @@ class depgraph(object): return pkg, existing - def _pkg_visibility_check(self, pkg, root, allow_missing_keywords=False): - pkgsettings = self._frozen_config.pkgsettings[root] - root_config = self._frozen_config.roots[root] + def _pkg_visibility_check(self, pkg, allow_missing_keywords=False): + pkgsettings = self._frozen_config.pkgsettings[pkg.root] + root_config = self._frozen_config.roots[pkg.root] if pkg.visible: return True + + pending_keyword_change = self._dynamic_config._needed_user_config_changes.get(pkg) + if pending_keyword_change is not None and \ + "unstable keyword" in pending_keyword_change: + return True + if not allow_missing_keywords: return False mreasons = get_masking_status(pkg, pkgsettings, root_config) @@ -2598,7 +2604,7 @@ class depgraph(object): # were installed can be automatically downgraded # to an unmasked version. - if not self._pkg_visibility_check(pkg, root, allow_missing_keywords): + if not self._pkg_visibility_check(pkg, allow_missing_keywords=allow_missing_keywords): continue # Enable upgrade or downgrade to a version @@ -2632,7 +2638,8 @@ class depgraph(object): except portage.exception.PackageNotFound: continue else: - if not self._pkg_visibility_check(pkg_eb, root, allow_missing_keywords): + if not self._pkg_visibility_check(pkg_eb, \ + allow_missing_keywords=allow_missing_keywords): continue # Calculation of USE for unbuilt ebuilds is relatively @@ -2816,11 +2823,13 @@ class depgraph(object): if avoid_update: for pkg in matched_packages: - if pkg.installed and self._pkg_visibility_check(pkg, root, allow_missing_keywords): + if pkg.installed and self._pkg_visibility_check(pkg, \ + allow_missing_keywords=allow_missing_keywords): return pkg, existing_node bestmatch = portage.best( - [pkg.cpv for pkg in matched_packages if self._pkg_visibility_check(pkg, root, allow_missing_keywords)]) + [pkg.cpv for pkg in matched_packages \ + if self._pkg_visibility_check(pkg, allow_missing_keywords=allow_missing_keywords)]) if not bestmatch: # all are masked, so ignore visibility bestmatch = portage.best( @@ -2986,7 +2995,7 @@ class depgraph(object): root_config=root_config, type_name=type_name) self._frozen_config._pkg_cache[pkg] = pkg - if not pkg.visible and \ + if not self._pkg_visibility_check(pkg) and \ 'LICENSE' in pkg.masks and len(pkg.masks) == 1: slot_key = (pkg.root, pkg.slot_atom) other_pkg = self._frozen_config._highest_license_masked.get(slot_key) @@ -3042,7 +3051,7 @@ class depgraph(object): # packages masked by license, since the user likely wants # to adjust ACCEPT_LICENSE. if pkg in final_db: - if not pkg.visible and \ + if not self._pkg_visibility_check(pkg) and \ (pkg_in_graph or 'LICENSE' in pkg.masks): self._dynamic_config._masked_installed.add(pkg) else: @@ -5329,7 +5338,7 @@ class depgraph(object): self._frozen_config.excluded_pkgs.findAtomForPackage(pkg): continue - if "merge" == pkg.operation and not pkg.visible: + if "merge" == pkg.operation and not self._pkg_visibility_check(pkg): if skip_masked: masked_tasks.append(Dependency(root=pkg.root, parent=pkg)) else: -- cgit v1.2.3-65-gdbad From 3e7009f85fc4bb5a15accda35e8f6b900d4a132d Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Thu, 5 Aug 2010 09:23:32 +0200 Subject: _emerge.depgraph: Keep _needed_user_config_changes during backtracking --- pym/_emerge/depgraph.py | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 5924d7b5..7072e18d 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -106,7 +106,7 @@ class _frozen_depgraph_config(object): class _dynamic_depgraph_config(object): def __init__(self, depgraph, myparams, allow_backtracking, - runtime_pkg_mask): + runtime_pkg_mask, needed_user_config_changes): self.myparams = myparams.copy() self._vdb_loaded = False self._allow_backtracking = allow_backtracking @@ -168,7 +168,6 @@ class _dynamic_depgraph_config(object): self._missing_args = [] self._masked_installed = set() self._masked_license_updates = set() - self._needed_user_config_changes = {} self._unsatisfied_deps_for_display = [] self._unsatisfied_blockers_for_display = None self._circular_deps_for_display = None @@ -183,6 +182,14 @@ class _dynamic_depgraph_config(object): else: runtime_pkg_mask = dict((k, v.copy()) for (k, v) in \ runtime_pkg_mask.items()) + + if needed_user_config_changes is None: + self._needed_user_config_changes = {} + else: + self._needed_user_config_changes = \ + dict((k.copy(), v.copy()) for (k, v) in \ + needed_user_config_changes.items()) + self._runtime_pkg_mask = runtime_pkg_mask self._need_restart = False @@ -253,13 +260,13 @@ class depgraph(object): _dep_keys = ["DEPEND", "RDEPEND", "PDEPEND"] def __init__(self, settings, trees, myopts, myparams, spinner, - frozen_config=None, runtime_pkg_mask=None, allow_backtracking=False): + frozen_config=None, runtime_pkg_mask=None, needed_user_config_changes=None, allow_backtracking=False): if frozen_config is None: frozen_config = _frozen_depgraph_config(settings, trees, myopts, spinner) self._frozen_config = frozen_config self._dynamic_config = _dynamic_depgraph_config(self, myparams, - allow_backtracking, runtime_pkg_mask) + allow_backtracking, runtime_pkg_mask, needed_user_config_changes) self._select_atoms = self._select_atoms_highest_available self._select_package = self._select_pkg_highest_available @@ -5531,8 +5538,10 @@ class depgraph(object): def need_restart(self): return self._dynamic_config._need_restart - def get_runtime_pkg_mask(self): - return self._dynamic_config._runtime_pkg_mask.copy() + def get_backtrack_parameter(self): + return self._dynamic_config._needed_user_config_changes.copy(), \ + self._dynamic_config._runtime_pkg_mask.copy() + class _dep_check_composite_db(dbapi): """ @@ -5764,6 +5773,7 @@ def _backtrack_depgraph(settings, trees, myopts, myparams, backtrack_max = myopts.get('--backtrack', 5) runtime_pkg_mask = None + needed_user_config_changes = None allow_backtracking = backtrack_max > 0 backtracked = 0 frozen_config = _frozen_depgraph_config(settings, trees, @@ -5772,11 +5782,13 @@ def _backtrack_depgraph(settings, trees, myopts, myparams, mydepgraph = depgraph(settings, trees, myopts, myparams, spinner, frozen_config=frozen_config, allow_backtracking=allow_backtracking, + needed_user_config_changes=needed_user_config_changes, runtime_pkg_mask=runtime_pkg_mask) success, favorites = mydepgraph.select_files(myfiles) if not success: if mydepgraph.need_restart() and backtracked < backtrack_max: - runtime_pkg_mask = mydepgraph.get_runtime_pkg_mask() + needed_user_config_changes, runtime_pkg_mask = \ + mydepgraph.get_backtrack_parameter() backtracked += 1 elif backtracked and allow_backtracking: if "--debug" in myopts: @@ -5786,6 +5798,9 @@ def _backtrack_depgraph(settings, trees, myopts, myparams, # Backtracking failed, so disable it and do # a plain dep calculation + error message. allow_backtracking = False + #Don't reset needed_user_config_changes here, since we don't want to + #send the user through a "one step at a time" unmasking session for + #no good reason. runtime_pkg_mask = None else: break -- cgit v1.2.3-65-gdbad From f27e8b3aa5e5feec5095ee199f04b61367841979 Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Thu, 5 Aug 2010 09:39:44 +0200 Subject: _emerge.depgraph.get_backtrack_parameters(): Return a dict instead of a tuple --- pym/_emerge/depgraph.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 7072e18d..056c7bdb 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -5538,9 +5538,13 @@ class depgraph(object): def need_restart(self): return self._dynamic_config._need_restart - def get_backtrack_parameter(self): - return self._dynamic_config._needed_user_config_changes.copy(), \ - self._dynamic_config._runtime_pkg_mask.copy() + def get_backtrack_parameters(self): + return { + "needed_user_config_changes": + self._dynamic_config._needed_user_config_changes.copy(), \ + "runtime_pkg_mask": + self._dynamic_config._runtime_pkg_mask.copy() + } class _dep_check_composite_db(dbapi): @@ -5787,8 +5791,9 @@ def _backtrack_depgraph(settings, trees, myopts, myparams, success, favorites = mydepgraph.select_files(myfiles) if not success: if mydepgraph.need_restart() and backtracked < backtrack_max: - needed_user_config_changes, runtime_pkg_mask = \ - mydepgraph.get_backtrack_parameter() + backtrack_parameters = mydepgraph.get_backtrack_parameter() + needed_user_config_changes = backtrack_parameters["needed_user_config_changes"] + runtime_pkg_mask = backtrack_parameters["runtime_pkg_mask"] backtracked += 1 elif backtracked and allow_backtracking: if "--debug" in myopts: -- cgit v1.2.3-65-gdbad From 03a201256ab9a8557862e30732ecc9b7de19a885 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Thu, 5 Aug 2010 02:45:29 -0700 Subject: * Add a --autounmask[=n] option and for now leave it disable by default in order to minimize the impact of any bugs. * If _wrapped_select_pkg_highest_available_imp returns an installed package when the user has not explicitly requested for this package to be replaced (typically via an atom on the command line), reject the installed package and try to unmask one. --- man/emerge.1 | 9 +++++++++ pym/_emerge/depgraph.py | 37 +++++++++++++++++++++++++++++++------ pym/_emerge/help.py | 11 +++++++++++ pym/_emerge/main.py | 11 +++++++++++ 4 files changed, 62 insertions(+), 6 deletions(-) diff --git a/man/emerge.1 b/man/emerge.1 index 572002d3..73c90d3f 100644 --- a/man/emerge.1 +++ b/man/emerge.1 @@ -284,6 +284,15 @@ acceptance of the first choice. This option is intended to be set in the \fBmake.conf\fR(5) \fBEMERGE_DEFAULT_OPTS\fR variable. .TP +.BR "\-\-autounmask[=n]" +Automatically unmask packages. If any configuration +changes are required, then they will be displayed +after the merge list and emerge will immediately +abort. If the displayed configuration changes are +satisfactory, you should copy and paste them into +the specified configuration file(s). Currently, +this only works for unstable KEYWORDS masks. +.TP .BR \-\-backtrack=COUNT Specifies an integer number of times to backtrack if dependency calculation fails due to a conflict or an diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 056c7bdb..05c10859 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -2483,15 +2483,40 @@ class depgraph(object): self._dynamic_config._visible_pkgs[pkg.root].cpv_inject(pkg) return ret + def _want_installed_pkg(self, pkg): + """ + Given an installed package returned from select_pkg, return + True if the user has not explicitly requested for this package + to be replaced (typically via an atom on the command line). + """ + if "selective" not in self._dynamic_config.myparams and \ + pkg.root == self._frozen_config.target_root: + try: + next(self._iter_atoms_for_pkg(pkg)) + except StopIteration: + pass + except portage.exception.InvalidDependString: + pass + else: + return False + return True + def _select_pkg_highest_available_imp(self, root, atom, onlydeps=False): pkg, existing = self._wrapped_select_pkg_highest_available_imp(root, atom, onlydeps=onlydeps) - if pkg is None: - pkg, existing = self._wrapped_select_pkg_highest_available_imp(root, atom, \ - onlydeps=onlydeps, allow_missing_keywords=True) - - if pkg is not None and not pkg.visible: - self._dynamic_config._needed_user_config_changes.setdefault(pkg, set()).add("unstable keyword") + if self._frozen_config.myopts.get('--autounmask', 'n') is True: + if pkg is not None and \ + pkg.installed and \ + not self._want_installed_pkg(pkg): + pkg = None + if pkg is None: + pkg, existing = \ + self._wrapped_select_pkg_highest_available_imp( + root, atom, onlydeps=onlydeps, + allow_missing_keywords=True) + + if pkg is not None and not pkg.visible: + self._dynamic_config._needed_user_config_changes.setdefault(pkg, set()).add("unstable keyword") return pkg, existing diff --git a/pym/_emerge/help.py b/pym/_emerge/help.py index 43e9794e..72bb56d4 100644 --- a/pym/_emerge/help.py +++ b/pym/_emerge/help.py @@ -291,6 +291,17 @@ def help(myopts, havecolor=1): "EMERGE_DEFAULT_OPTS variable." for line in wrap(desc, desc_width): print(desc_indent + line) + print() + print(" " + green("--autounmask") + "[=%s]" % turquoise("n")) + desc = "Automatically unmask packages. If any configuration " + \ + "changes are required, then they will be displayed " + \ + "after the merge list and emerge will immediately " + \ + "abort. If the displayed configuration changes are " + \ + "satisfactory, you should copy and paste them into " + \ + "the specified configuration file(s). Currently, " + \ + "this only works for unstable KEYWORDS masks." + for line in wrap(desc, desc_width): + print(desc_indent + line) print() print(" " + green("--backtrack") + " " + turquoise("COUNT")) desc = "Specifies an integer number of times to backtrack if " + \ diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py index b2bb362f..92df3bfb 100644 --- a/pym/_emerge/main.py +++ b/pym/_emerge/main.py @@ -388,6 +388,7 @@ def insert_optional_args(args): new_args = [] default_arg_opts = { + '--autounmask' : ('n',), '--complete-graph' : ('n',), '--deep' : valid_integers, '--depclean-lib-check' : ('n',), @@ -515,6 +516,13 @@ def parse_opts(tmpcmdline, silent=False): longopt_aliases = {"--cols":"--columns", "--skip-first":"--skipfirst"} argument_options = { + + "--autounmask": { + "help" : "automatically unmask packages", + "type" : "choice", + "choices" : ("True", "n") + }, + "--accept-properties": { "help":"temporarily override ACCEPT_PROPERTIES", "action":"store" @@ -735,6 +743,9 @@ def parse_opts(tmpcmdline, silent=False): myoptions, myargs = parser.parse_args(args=tmpcmdline) + if myoptions.autounmask in ("True",): + myoptions.autounmask = True + if myoptions.changed_use is not False: myoptions.reinstall = "changed-use" myoptions.changed_use = False -- cgit v1.2.3-65-gdbad From 293bd0e85326a22effb5f10d1489014ecc617e59 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Thu, 5 Aug 2010 03:46:20 -0700 Subject: Simplify passing of the backtracking parameters from one graph to the next. --- pym/_emerge/depgraph.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 05c10859..aeaee0fc 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -5801,7 +5801,7 @@ def _backtrack_depgraph(settings, trees, myopts, myparams, myaction, myfiles, spinner): backtrack_max = myopts.get('--backtrack', 5) - runtime_pkg_mask = None + backtrack_parameters = {} needed_user_config_changes = None allow_backtracking = backtrack_max > 0 backtracked = 0 @@ -5811,14 +5811,11 @@ def _backtrack_depgraph(settings, trees, myopts, myparams, mydepgraph = depgraph(settings, trees, myopts, myparams, spinner, frozen_config=frozen_config, allow_backtracking=allow_backtracking, - needed_user_config_changes=needed_user_config_changes, - runtime_pkg_mask=runtime_pkg_mask) + **backtrack_parameters) success, favorites = mydepgraph.select_files(myfiles) if not success: if mydepgraph.need_restart() and backtracked < backtrack_max: - backtrack_parameters = mydepgraph.get_backtrack_parameter() - needed_user_config_changes = backtrack_parameters["needed_user_config_changes"] - runtime_pkg_mask = backtrack_parameters["runtime_pkg_mask"] + backtrack_parameters = mydepgraph.get_backtrack_parameters() backtracked += 1 elif backtracked and allow_backtracking: if "--debug" in myopts: @@ -5831,7 +5828,7 @@ def _backtrack_depgraph(settings, trees, myopts, myparams, #Don't reset needed_user_config_changes here, since we don't want to #send the user through a "one step at a time" unmasking session for #no good reason. - runtime_pkg_mask = None + backtrack_parameters.pop('runtime_pkg_mask', None) else: break else: -- cgit v1.2.3-65-gdbad From eb863416cde2c7b7a7ab8f70b5bac4b4fc4d8aee Mon Sep 17 00:00:00 2001 From: Arfrever Frehtes Taifersar Arahesis Date: Thu, 5 Aug 2010 15:02:20 +0200 Subject: Bug #330937: Handle IOError raised by remaining calls to array.fromfile(). --- pym/_emerge/PipeReader.py | 3 ++- pym/_emerge/SpawnProcess.py | 3 ++- pym/portage/package/ebuild/doebuild.py | 3 ++- pym/portage/tests/ebuild/test_array_fromfile_eof.py | 3 ++- pym/portage/util/_pty.py | 6 ++---- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/pym/_emerge/PipeReader.py b/pym/_emerge/PipeReader.py index be958bcb..2c3495c3 100644 --- a/pym/_emerge/PipeReader.py +++ b/pym/_emerge/PipeReader.py @@ -71,7 +71,8 @@ class PipeReader(AbstractPollTask): buf = array.array('B') try: buf.fromfile(f, self._bufsize) - except EOFError: + # EOFError was raised in Python <2.6.6 and <2.7.1. + except (EOFError, IOError): pass if buf: diff --git a/pym/_emerge/SpawnProcess.py b/pym/_emerge/SpawnProcess.py index bacbc2f3..490b111f 100644 --- a/pym/_emerge/SpawnProcess.py +++ b/pym/_emerge/SpawnProcess.py @@ -210,7 +210,8 @@ class SpawnProcess(SubProcess): buf = array.array('B') try: buf.fromfile(self._files.process, self._bufsize) - except EOFError: + # EOFError was raised in Python <2.6.6 and <2.7.1. + except (EOFError, IOError): pass if buf: diff --git a/pym/portage/package/ebuild/doebuild.py b/pym/portage/package/ebuild/doebuild.py index b9cbc3e0..c8ec3fc2 100644 --- a/pym/portage/package/ebuild/doebuild.py +++ b/pym/portage/package/ebuild/doebuild.py @@ -1268,7 +1268,8 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, fakero buf = array.array('B') try: buf.fromfile(f, buffsize) - except EOFError: + # EOFError was raised in Python <2.6.6 and <2.7.1. + except (EOFError, IOError): pass if not buf: eof = True diff --git a/pym/portage/tests/ebuild/test_array_fromfile_eof.py b/pym/portage/tests/ebuild/test_array_fromfile_eof.py index 3f2a6c7c..c32a15cb 100644 --- a/pym/portage/tests/ebuild/test_array_fromfile_eof.py +++ b/pym/portage/tests/ebuild/test_array_fromfile_eof.py @@ -28,7 +28,8 @@ class ArrayFromfileEofTestCase(TestCase): a = array.array('B') try: a.fromfile(f, len(input_bytes) + 1) - except EOFError: + # EOFError was raised in Python <2.6.6 and <2.7.1. + except (EOFError, IOError): # python-3.0 lost data here eof = True diff --git a/pym/portage/util/_pty.py b/pym/portage/util/_pty.py index 7fba0e2b..a4910ec7 100644 --- a/pym/portage/util/_pty.py +++ b/pym/portage/util/_pty.py @@ -95,10 +95,8 @@ def _test_pty_eof(): buf = array.array('B') try: buf.fromfile(master_file, 1024) - except EOFError: - eof = True - except IOError: - # This is where data loss occurs. + # EOFError was raised in Python <2.6.6 and <2.7.1. + except (EOFError, IOError): eof = True if not buf: -- cgit v1.2.3-65-gdbad From dee1323cd34ed867b16f898f1ea7732af6d235da Mon Sep 17 00:00:00 2001 From: Arfrever Frehtes Taifersar Arahesis Date: Thu, 5 Aug 2010 16:51:34 +0200 Subject: EOFError can still be raised by array.fromfile() in Python >=2.6.6 and >=2.7.1. --- pym/_emerge/PipeReader.py | 1 - pym/_emerge/SpawnProcess.py | 2 -- pym/portage/package/ebuild/doebuild.py | 1 - pym/portage/tests/ebuild/test_array_fromfile_eof.py | 1 - pym/portage/util/_pty.py | 1 - 5 files changed, 6 deletions(-) diff --git a/pym/_emerge/PipeReader.py b/pym/_emerge/PipeReader.py index 2c3495c3..604fbf29 100644 --- a/pym/_emerge/PipeReader.py +++ b/pym/_emerge/PipeReader.py @@ -71,7 +71,6 @@ class PipeReader(AbstractPollTask): buf = array.array('B') try: buf.fromfile(f, self._bufsize) - # EOFError was raised in Python <2.6.6 and <2.7.1. except (EOFError, IOError): pass diff --git a/pym/_emerge/SpawnProcess.py b/pym/_emerge/SpawnProcess.py index 490b111f..51cd04fc 100644 --- a/pym/_emerge/SpawnProcess.py +++ b/pym/_emerge/SpawnProcess.py @@ -148,7 +148,6 @@ class SpawnProcess(SubProcess): buf = array.array('B') try: buf.fromfile(files.process, self._bufsize) - # EOFError was raised in Python <2.6.6 and <2.7.1. except (EOFError, IOError): pass @@ -210,7 +209,6 @@ class SpawnProcess(SubProcess): buf = array.array('B') try: buf.fromfile(self._files.process, self._bufsize) - # EOFError was raised in Python <2.6.6 and <2.7.1. except (EOFError, IOError): pass diff --git a/pym/portage/package/ebuild/doebuild.py b/pym/portage/package/ebuild/doebuild.py index c8ec3fc2..9f4fe2f4 100644 --- a/pym/portage/package/ebuild/doebuild.py +++ b/pym/portage/package/ebuild/doebuild.py @@ -1268,7 +1268,6 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, fakero buf = array.array('B') try: buf.fromfile(f, buffsize) - # EOFError was raised in Python <2.6.6 and <2.7.1. except (EOFError, IOError): pass if not buf: diff --git a/pym/portage/tests/ebuild/test_array_fromfile_eof.py b/pym/portage/tests/ebuild/test_array_fromfile_eof.py index c32a15cb..d8277f27 100644 --- a/pym/portage/tests/ebuild/test_array_fromfile_eof.py +++ b/pym/portage/tests/ebuild/test_array_fromfile_eof.py @@ -28,7 +28,6 @@ class ArrayFromfileEofTestCase(TestCase): a = array.array('B') try: a.fromfile(f, len(input_bytes) + 1) - # EOFError was raised in Python <2.6.6 and <2.7.1. except (EOFError, IOError): # python-3.0 lost data here eof = True diff --git a/pym/portage/util/_pty.py b/pym/portage/util/_pty.py index a4910ec7..877430e9 100644 --- a/pym/portage/util/_pty.py +++ b/pym/portage/util/_pty.py @@ -95,7 +95,6 @@ def _test_pty_eof(): buf = array.array('B') try: buf.fromfile(master_file, 1024) - # EOFError was raised in Python <2.6.6 and <2.7.1. except (EOFError, IOError): eof = True -- cgit v1.2.3-65-gdbad From 6403211fa4be5b0c6d47ea87c6be06b62184d33b Mon Sep 17 00:00:00 2001 From: Arfrever Frehtes Taifersar Arahesis Date: Thu, 5 Aug 2010 21:28:25 +0200 Subject: Bug #301915: Add QA check for byte-compiled Python modules. --- bin/misc-functions.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/bin/misc-functions.sh b/bin/misc-functions.sh index 7726b9f2..5552f69e 100755 --- a/bin/misc-functions.sh +++ b/bin/misc-functions.sh @@ -532,13 +532,12 @@ install_qa_check() { fi fi - # Compiled python objects do not belong in /usr/share (FHS violation) - # and can be a pain when upgrading python - f=$([ -d "${D}"/usr/share ] && \ - find "${D}"usr/share -name '*.py[co]' | sed "s:${D}:/:") + f=$(find "${D}" -name '*.py[co]' | sed "s:${D}:/:") if [[ -n ${f} ]] ; then vecho -ne '\a\n' - eqawarn "QA Notice: Precompiled python object files do not belong in /usr/share" + eqawarn "QA Notice: Byte-compiled Python modules have been found. python_mod_optimize()" + eqawarn " and python_mod_cleanup() functions python.eclass should be used to" + eqawarn " handle byte-compiled Python modules." eqawarn "${f}" vecho -ne '\a\n' fi -- cgit v1.2.3-65-gdbad From 9f90face9b64a08b002ba6870081f21289af2900 Mon Sep 17 00:00:00 2001 From: Arfrever Frehtes Taifersar Arahesis Date: Thu, 5 Aug 2010 22:06:02 +0200 Subject: Make some variables local. --- bin/misc-functions.sh | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/bin/misc-functions.sh b/bin/misc-functions.sh index 5552f69e..10d19312 100755 --- a/bin/misc-functions.sh +++ b/bin/misc-functions.sh @@ -41,6 +41,8 @@ install_symlink_html_docs() { } install_qa_check() { + local f + cd "${D}" || die "cd failed" export STRIP_MASK @@ -49,6 +51,7 @@ install_qa_check() { ecompress --dequeue # Now we look for all world writable files. + local i for i in $(find "${D}/" -type f -perm -2); do vecho -ne '\a' vecho "QA Security Notice:" @@ -60,7 +63,7 @@ install_qa_check() { if type -P scanelf > /dev/null && ! hasq binchecks ${RESTRICT}; then local qa_var insecure_rpath=0 tmp_quiet=${PORTAGE_QUIET} - local f x + local x # display warnings when using stricter because we die afterwards if has stricter ${FEATURES} ; then @@ -331,6 +334,7 @@ install_qa_check() { fi # Sanity check syntax errors in init.d scripts + local d for d in /etc/conf.d /etc/init.d ; do [[ -d ${D}/${d} ]] || continue for i in "${D}"/${d}/* ; do @@ -343,6 +347,7 @@ install_qa_check() { # this should help to ensure that all (most?) shared libraries are executable # and that all libtool scripts / static libraries are not executable + local j for i in "${D}"opt/*/lib{,32,64} \ "${D}"lib{,32,64} \ "${D}"usr/lib{,32,64} \ @@ -384,6 +389,7 @@ install_qa_check() { # the static library, or gcc will utilize the static lib when linking :(. # http://bugs.gentoo.org/4411 abort="no" + local a s for a in "${D}"usr/lib*/*.a ; do s=${a%.a}.so if [[ ! -e ${s} ]] ; then @@ -549,7 +555,7 @@ install_qa_check() { [[ -x /usr/bin/file && -x /usr/bin/find ]] && \ [[ -n ${MULTILIB_STRICT_DIRS} && -n ${MULTILIB_STRICT_DENY} ]] then - local abort=no firstrun=yes + local abort=no dir file firstrun=yes MULTILIB_STRICT_EXEMPT=$(echo ${MULTILIB_STRICT_EXEMPT} | sed -e 's:\([(|)]\):\\\1:g') for dir in ${MULTILIB_STRICT_DIRS} ; do [[ -d ${D}/${dir} ]] || continue @@ -577,6 +583,7 @@ install_mask() { # we don't want globbing for initial expansion, but afterwards, we do local shopts=$- set -o noglob + local no_inst for no_inst in ${install_mask}; do set +o noglob quiet_mode || einfo "Removing ${no_inst}" @@ -619,6 +626,7 @@ preinst_mask() { cd "${T}" # remove man pages, info pages, docs if requested + local f for f in man info doc; do if hasq no${f} $FEATURES; then INSTALL_MASK="${INSTALL_MASK} /usr/share/${f}" @@ -675,7 +683,7 @@ preinst_suid_scan() { fi # total suid control. if hasq suidctl $FEATURES; then - local sfconf + local i sfconf x sfconf=${PORTAGE_CONFIGROOT}etc/portage/suidctl.conf # sandbox prevents us from writing directly # to files outside of the sandbox, but this @@ -844,6 +852,7 @@ if [ -n "${MISC_FUNCTIONS_ARGS}" ]; then for x in ${MISC_FUNCTIONS_ARGS}; do ${x} done + unset x fi [ -n "${EBUILD_EXIT_STATUS_FILE}" ] && \ -- cgit v1.2.3-65-gdbad From d1779db3525d9c1783e67c2f31a884dae35a60a6 Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Thu, 5 Aug 2010 15:45:22 +0200 Subject: _emerge.depgraph: If "/" not in trees, treat target_root as running_root (needed for tests) --- pym/_emerge/depgraph.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index aeaee0fc..15004821 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -66,7 +66,10 @@ class _frozen_depgraph_config(object): if settings.get("PORTAGE_DEBUG", "") == "1": self.edebug = 1 self.spinner = spinner - self._running_root = trees["/"]["root_config"] + if "/" in trees: + self._running_root = trees["/"]["root_config"] + else: + self._running_root = trees[self.target_root]["root_config"] self._opts_no_restart = frozenset(["--buildpkgonly", "--fetchonly", "--fetch-all-uri", "--pretend"]) self.pkgsettings = {} -- cgit v1.2.3-65-gdbad From 099fc66a93ae7bb8a4f3e5b2caeb08426365b923 Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Thu, 5 Aug 2010 15:47:00 +0200 Subject: Tests: Add resolver/test_simple and the resolver test infrastructure --- pym/portage/tests/resolver/ResolverPlayground.py | 190 +++++++++++++++++++++++ pym/portage/tests/resolver/__init__.py | 2 + pym/portage/tests/resolver/__test__ | 0 pym/portage/tests/resolver/test_simple.py | 32 ++++ 4 files changed, 224 insertions(+) create mode 100644 pym/portage/tests/resolver/ResolverPlayground.py create mode 100644 pym/portage/tests/resolver/__init__.py create mode 100644 pym/portage/tests/resolver/__test__ create mode 100644 pym/portage/tests/resolver/test_simple.py diff --git a/pym/portage/tests/resolver/ResolverPlayground.py b/pym/portage/tests/resolver/ResolverPlayground.py new file mode 100644 index 00000000..56cdb5f4 --- /dev/null +++ b/pym/portage/tests/resolver/ResolverPlayground.py @@ -0,0 +1,190 @@ +# Copyright 2010 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +from itertools import chain +import tempfile +import portage +from portage import os +from portage.dbapi.vartree import vartree +from portage.dbapi.porttree import portagetree +from portage.dbapi.bintree import binarytree +from portage.dep import Atom +from portage.package.ebuild.config import config +from portage.sets import load_default_config +from portage.versions import catsplit + +from _emerge.create_depgraph_params import create_depgraph_params +from _emerge.depgraph import backtrack_depgraph +from _emerge.RootConfig import RootConfig + +class ResolverPlayground(object): + """ + This class help to create the necessary files on disk and + the needed settings instances, etc. for the resolver to do + it's work. + """ + + def __init__(self, ebuilds={}, installed={}, profile={}): + """ + ebuilds: cpv -> metadata mapping simulating avaiable ebuilds. + installed: cpv -> metadata mapping simulating installed packages. + If a metadata key is missing, it gets a default value. + profile: settings defined by the profile. + """ + self.root = tempfile.mkdtemp() + os.path.sep + self.portdir = os.path.join(self.root, "usr/portage") + self.vdbdir = os.path.join(self.root, "var/db/pkg") + os.makedirs(self.portdir) + os.makedirs(self.vdbdir) + + self._create_ebuilds(ebuilds) + self._create_installed(installed) + self._create_profile(ebuilds, installed, profile) + + self.settings, self.trees = self._load_config() + + self._create_ebuild_manifests(ebuilds) + + def _create_ebuilds(self, ebuilds): + for cpv in ebuilds: + a = Atom("=" + cpv) + ebuild_dir = os.path.join(self.portdir, a.cp) + ebuild_path = os.path.join(ebuild_dir, a.cpv.split("/")[1] + ".ebuild") + try: + os.makedirs(ebuild_dir) + except os.error: + pass + + metadata = ebuilds[cpv] + eapi = metadata.get("EAPI", 0) + slot = metadata.get("SLOT", 0) + keywords = metadata.get("KEYWORDS", "x86") + iuse = metadata.get("IUSE", "") + depend = metadata.get("DEPEND", "") + rdepend = metadata.get("RDEPEND", None) + pdepend = metadata.get("PDEPEND", None) + + f = open(ebuild_path, "w") + f.write('EAPI="' + str(eapi) + '"\n') + f.write('SLOT="' + str(slot) + '"\n') + f.write('KEYWORDS="' + str(keywords) + '"\n') + f.write('IUSE="' + str(iuse) + '"\n') + f.write('DEPEND="' + str(depend) + '"\n') + if rdepend is not None: + f.write('RDEPEND="' + str(rdepend) + '"\n') + if rdepend is not None: + f.write('PDEPEND="' + str(pdepend) + '"\n') + f.close() + + def _create_ebuild_manifests(self, ebuilds): + for cpv in ebuilds: + a = Atom("=" + cpv) + ebuild_dir = os.path.join(self.portdir, a.cp) + ebuild_path = os.path.join(ebuild_dir, a.cpv.split("/")[1] + ".ebuild") + + portage.util.noiselimit = -1 + tmpsettings = config(clone=self.settings) + portdb = self.trees[self.root]["porttree"].dbapi + portage.doebuild(ebuild_path, "digest", self.root, tmpsettings, + tree="porttree", mydbapi=portdb) + portage.util.noiselimit = 0 + + def _create_installed(self, installed): + for cpv in installed: + a = Atom("=" + cpv) + vdb_pkg_dir = os.path.join(self.vdbdir, a.cpv) + try: + os.makedirs(vdb_pkg_dir) + except os.error: + pass + + metadata = installed[cpv] + eapi = metadata.get("EAPI", 0) + slot = metadata.get("SLOT", 0) + keywords = metadata.get("KEYWORDS", "~x86") + iuse = metadata.get("IUSE", "") + use = metadata.get("USE", "") + depend = metadata.get("DEPEND", "") + rdepend = metadata.get("RDEPEND", None) + pdepend = metadata.get("PDEPEND", None) + + def write_key(key, value): + f = open(os.path.join(vdb_pkg_dir, key), "w") + f.write(str(value) + "\n") + f.close() + + write_key("EAPI", eapi) + write_key("SLOT", slot) + write_key("KEYWORDS", keywords) + write_key("IUSE", iuse) + write_key("USE", use) + write_key("DEPEND", depend) + if rdepend is not None: + write_key("RDEPEND", rdepend) + if rdepend is not None: + write_key("PDEPEND", pdepend) + + def _create_profile(self, ebuilds, installed, profile): + #Create $PORTDIR/profiles/categories + categories = set() + for cpv in chain(ebuilds.keys(), installed.keys()): + categories.add(catsplit(cpv)[0]) + + profile_dir = os.path.join(self.portdir, "profiles") + try: + os.makedirs(profile_dir) + except os.error: + pass + + categories_file = os.path.join(profile_dir, "categories") + + f = open(categories_file, "w") + for cat in categories: + f.write(cat + "\n") + f.close() + + #Create $PORTDIR/eclass (we fail to digest the ebuilds if it's not there) + os.makedirs(os.path.join(self.portdir, "eclass")) + + if profile: + #This is meant to allow the consumer to set up his own profile, + #with package.mask and what not. + raise NotImplentedError() + + def _load_config(self): + env = { "PORTDIR": self.portdir, "ROOT": self.root, "ACCEPT_KEYWORDS": "x86"} + settings = config(config_root=self.root, target_root=self.root, local_config=False, env=env) + settings.lock() + + trees = { + self.root: { + "virtuals": settings.getvirtuals(), + "vartree": vartree(self.root, categories=settings.categories, settings=settings), + "porttree": portagetree(self.root, settings=settings), + "bintree": binarytree(self.root, os.path.join(self.root, "usr/portage/packages"), settings=settings) + } + } + + for root, root_trees in trees.items(): + settings = root_trees["vartree"].settings + settings._init_dirs() + setconfig = load_default_config(settings, root_trees) + root_trees["root_config"] = RootConfig(settings, root_trees, setconfig) + + return settings, trees + + def run(self, myfiles, myopts={}, myaction=None): + myopts["--pretend"] = True + myopts["--quiet"] = True + myopts["--root"] = self.root + myopts["--config-root"] = self.root + myparams = create_depgraph_params(myopts, myaction) + success, mydepgraph, favorites = backtrack_depgraph( + self.settings, self.trees, myopts, myparams, myaction, myfiles, None) + + if success: + mergelist = [x.cpv for x in mydepgraph._dynamic_config._serialized_tasks_cache] + return True, mergelist + else: + #TODO: Use mydepgraph.display_problems() to return a useful error message + return False, None diff --git a/pym/portage/tests/resolver/__init__.py b/pym/portage/tests/resolver/__init__.py new file mode 100644 index 00000000..21a391ae --- /dev/null +++ b/pym/portage/tests/resolver/__init__.py @@ -0,0 +1,2 @@ +# Copyright 2010 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 diff --git a/pym/portage/tests/resolver/__test__ b/pym/portage/tests/resolver/__test__ new file mode 100644 index 00000000..e69de29b diff --git a/pym/portage/tests/resolver/test_simple.py b/pym/portage/tests/resolver/test_simple.py new file mode 100644 index 00000000..d178e7fb --- /dev/null +++ b/pym/portage/tests/resolver/test_simple.py @@ -0,0 +1,32 @@ +# Copyright 2010 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +from portage.tests import TestCase +from portage.tests.resolver.ResolverPlayground import ResolverPlayground + +class SimpleResolverTestCase(TestCase): + + def testSimple(self): + ebuilds = { + "dev-libs/A-1": {}, + "dev-libs/A-2": { "KEYWORDS": "~x86" }, + "dev-libs/B-1.2": {}, + } + installed = { + "dev-libs/B-1.1": {}, + } + + requests = ( + (["dev-libs/A"], {}, None, True, ["dev-libs/A-1"]), + (["=dev-libs/A-2"], {}, None, False, None), + (["dev-libs/B"], {"--noreplace": True}, None, True, []), + (["dev-libs/B"], {"--update": True}, None, True, ["dev-libs/B-1.2"]), + ) + + playground = ResolverPlayground(ebuilds=ebuilds, installed=installed) + + for atoms, options, action, expected_result, expected_mergelist in requests: + success, mergelist = playground.run(atoms, options, action) + self.assertEqual(success, expected_result) + if success: + self.assertEqual(mergelist, expected_mergelist) -- cgit v1.2.3-65-gdbad From cafa8cf69be8eaaccf51c7befab814f6fd8eb89a Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Thu, 5 Aug 2010 16:29:31 +0200 Subject: Tests: Add resolver/test_eapi (2 tests disabled) The resolver accepts EAPI-0 ebuilds with IUSE defaults and slot dependencies. --- pym/portage/tests/resolver/ResolverPlayground.py | 4 + pym/portage/tests/resolver/test_eapi.py | 98 ++++++++++++++++++++++++ pym/portage/tests/resolver/test_simple.py | 4 +- 3 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 pym/portage/tests/resolver/test_eapi.py diff --git a/pym/portage/tests/resolver/ResolverPlayground.py b/pym/portage/tests/resolver/ResolverPlayground.py index 56cdb5f4..643a3b61 100644 --- a/pym/portage/tests/resolver/ResolverPlayground.py +++ b/pym/portage/tests/resolver/ResolverPlayground.py @@ -178,9 +178,13 @@ class ResolverPlayground(object): myopts["--quiet"] = True myopts["--root"] = self.root myopts["--config-root"] = self.root + myopts["--root-deps"] = "rdeps" + + portage.util.noiselimit = -2 myparams = create_depgraph_params(myopts, myaction) success, mydepgraph, favorites = backtrack_depgraph( self.settings, self.trees, myopts, myparams, myaction, myfiles, None) + portage.util.noiselimit = 0 if success: mergelist = [x.cpv for x in mydepgraph._dynamic_config._serialized_tasks_cache] diff --git a/pym/portage/tests/resolver/test_eapi.py b/pym/portage/tests/resolver/test_eapi.py new file mode 100644 index 00000000..f2eff44d --- /dev/null +++ b/pym/portage/tests/resolver/test_eapi.py @@ -0,0 +1,98 @@ +# Copyright 2010 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +from portage.tests import TestCase +from portage.tests.resolver.ResolverPlayground import ResolverPlayground + +class SimpleResolverTestCase(TestCase): + + def testEAPI(self): + ebuilds = { + #EAPI-1: IUSE-defaults + "dev-libs/A-1.0": { "EAPI": 0, "IUSE": "+foo" }, + "dev-libs/A-1.1": { "EAPI": 1, "IUSE": "+foo" }, + "dev-libs/A-1.2": { "EAPI": 2, "IUSE": "+foo" }, + "dev-libs/A-1.3": { "EAPI": 3, "IUSE": "+foo" }, + #~ "dev-libs/A-1.4": { "EAPI": 4, "IUSE": "+foo" }, + + #EAPI-1: slot deps + "dev-libs/A-2.0": { "EAPI": 0, "DEPEND": "dev-libs/B:0" }, + "dev-libs/A-2.1": { "EAPI": 1, "DEPEND": "dev-libs/B:0" }, + "dev-libs/A-2.2": { "EAPI": 2, "DEPEND": "dev-libs/B:0" }, + "dev-libs/A-2.3": { "EAPI": 3, "DEPEND": "dev-libs/B:0" }, + #~ "dev-libs/A-2.4": { "EAPI": 4, "DEPEND": "dev-libs/B:0" }, + + #EAPI-2: use deps + "dev-libs/A-3.0": { "EAPI": 0, "DEPEND": "dev-libs/B[foo]" }, + "dev-libs/A-3.1": { "EAPI": 1, "DEPEND": "dev-libs/B[foo]" }, + "dev-libs/A-3.2": { "EAPI": 2, "DEPEND": "dev-libs/B[foo]" }, + "dev-libs/A-3.3": { "EAPI": 3, "DEPEND": "dev-libs/B[foo]" }, + #~ "dev-libs/A-3.4": { "EAPI": 4, "DEPEND": "dev-libs/B[foo]" }, + + #EAPI-2: strong blocks + "dev-libs/A-4.0": { "EAPI": 0, "DEPEND": "!!dev-libs/B" }, + "dev-libs/A-4.1": { "EAPI": 1, "DEPEND": "!!dev-libs/B" }, + "dev-libs/A-4.2": { "EAPI": 2, "DEPEND": "!!dev-libs/B" }, + "dev-libs/A-4.3": { "EAPI": 3, "DEPEND": "!!dev-libs/B" }, + #~ "dev-libs/A-4.4": { "EAPI": 4, "DEPEND": "!!dev-libs/B" }, + + #EAPI-4: slot operator deps + #~ "dev-libs/A-5.0": { "EAPI": 0, "DEPEND": "dev-libs/B:*" }, + #~ "dev-libs/A-5.1": { "EAPI": 1, "DEPEND": "dev-libs/B:*" }, + #~ "dev-libs/A-5.2": { "EAPI": 2, "DEPEND": "dev-libs/B:*" }, + #~ "dev-libs/A-5.3": { "EAPI": 3, "DEPEND": "dev-libs/B:*" }, + #~ "dev-libs/A-5.4": { "EAPI": 4, "DEPEND": "dev-libs/B:*" }, + + #EAPI-4: slot operator deps + #~ "dev-libs/A-6.0": { "EAPI": 0, "DEPEND": "dev-libs/B[bar(+)]" }, + #~ "dev-libs/A-6.1": { "EAPI": 1, "DEPEND": "dev-libs/B[bar(+)]" }, + #~ "dev-libs/A-6.2": { "EAPI": 2, "DEPEND": "dev-libs/B[bar(+)]" }, + #~ "dev-libs/A-6.3": { "EAPI": 3, "DEPEND": "dev-libs/B[bar(+)]" }, + #~ "dev-libs/A-6.4": { "EAPI": 4, "DEPEND": "dev-libs/B[bar(+)]" }, + + "dev-libs/B-1": {"EAPI": 1, "IUSE": "+foo"}, + } + + requests = ( + #~ (["=dev-libs/A-1.0"], {}, None, False, None), + (["=dev-libs/A-1.1"], {}, None, True, ["dev-libs/A-1.1"]), + (["=dev-libs/A-1.2"], {}, None, True, ["dev-libs/A-1.2"]), + (["=dev-libs/A-1.3"], {}, None, True, ["dev-libs/A-1.3"]), + #~ (["=dev-libs/A-1.4"], {}, None, True, ["dev-libs/A-1.4"]), + + #~ (["=dev-libs/A-2.0"], {}, None, False, None), + (["=dev-libs/A-2.1"], {}, None, True, ["dev-libs/B-1", "dev-libs/A-2.1"]), + (["=dev-libs/A-2.2"], {}, None, True, ["dev-libs/B-1", "dev-libs/A-2.2"]), + (["=dev-libs/A-2.3"], {}, None, True, ["dev-libs/B-1", "dev-libs/A-2.3"]), + #~ (["=dev-libs/A-2.4"], {}, None, True, ["dev-libs/B-1", "dev-libs/A-2.4"]), + + (["=dev-libs/A-3.0"], {}, None, False, None), + (["=dev-libs/A-3.1"], {}, None, False, None), + (["=dev-libs/A-3.2"], {}, None, True, ["dev-libs/B-1", "dev-libs/A-3.2"]), + (["=dev-libs/A-3.3"], {}, None, True, ["dev-libs/B-1", "dev-libs/A-3.3"]), + #~ (["=dev-libs/A-3.4"], {}, None, True, ["dev-libs/B-1", "dev-libs/A-3.4"]), + + (["=dev-libs/A-4.0"], {}, None, False, None), + (["=dev-libs/A-4.1"], {}, None, False, None), + (["=dev-libs/A-4.2"], {}, None, True, ["dev-libs/A-4.2"]), + (["=dev-libs/A-4.3"], {}, None, True, ["dev-libs/A-4.3"]), + #~ (["=dev-libs/A-4.4"], {}, None, True, ["dev-libs/A-4.4"]), + + #~ (["=dev-libs/A-5.0"], {}, None, False, None), + #~ (["=dev-libs/A-5.1"], {}, None, False, None), + #~ (["=dev-libs/A-5.2"], {}, None, False, None), + #~ (["=dev-libs/A-5.3"], {}, None, False, None), + #~ (["=dev-libs/A-5.4"], {}, None, True, ["dev-libs/B-1", "dev-libs/A-5.4"]), + + #~ (["=dev-libs/A-6.0"], {}, None, False, None), + #~ (["=dev-libs/A-6.1"], {}, None, False, None), + #~ (["=dev-libs/A-6.2"], {}, None, False, None), + #~ (["=dev-libs/A-6.3"], {}, None, False, None), + #~ (["=dev-libs/A-6.4"], {}, None, True, ["dev-libs/B-1", "dev-libs/A-6.4"]), + ) + + playground = ResolverPlayground(ebuilds=ebuilds) + + for atoms, options, action, expected_result, expected_mergelist in requests: + success, mergelist = playground.run(atoms, options, action) + self.assertEqual((success, mergelist), (expected_result, expected_mergelist)) diff --git a/pym/portage/tests/resolver/test_simple.py b/pym/portage/tests/resolver/test_simple.py index d178e7fb..c092129b 100644 --- a/pym/portage/tests/resolver/test_simple.py +++ b/pym/portage/tests/resolver/test_simple.py @@ -27,6 +27,4 @@ class SimpleResolverTestCase(TestCase): for atoms, options, action, expected_result, expected_mergelist in requests: success, mergelist = playground.run(atoms, options, action) - self.assertEqual(success, expected_result) - if success: - self.assertEqual(mergelist, expected_mergelist) + self.assertEqual((success, mergelist), (expected_result, expected_mergelist)) -- cgit v1.2.3-65-gdbad From a082e60d36af81d908e1fa185032c5da1e270375 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Thu, 5 Aug 2010 13:33:38 -0700 Subject: Use a ResolverPlayground.cleanup() method to cleanup temporary directories. --- pym/portage/tests/resolver/ResolverPlayground.py | 4 ++++ pym/portage/tests/resolver/test_eapi.py | 12 ++++++++---- pym/portage/tests/resolver/test_simple.py | 12 ++++++++---- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/pym/portage/tests/resolver/ResolverPlayground.py b/pym/portage/tests/resolver/ResolverPlayground.py index 643a3b61..9034deb8 100644 --- a/pym/portage/tests/resolver/ResolverPlayground.py +++ b/pym/portage/tests/resolver/ResolverPlayground.py @@ -2,6 +2,7 @@ # Distributed under the terms of the GNU General Public License v2 from itertools import chain +import shutil import tempfile import portage from portage import os @@ -192,3 +193,6 @@ class ResolverPlayground(object): else: #TODO: Use mydepgraph.display_problems() to return a useful error message return False, None + + def cleanup(self): + shutil.rmtree(self.root) diff --git a/pym/portage/tests/resolver/test_eapi.py b/pym/portage/tests/resolver/test_eapi.py index f2eff44d..19c7b219 100644 --- a/pym/portage/tests/resolver/test_eapi.py +++ b/pym/portage/tests/resolver/test_eapi.py @@ -92,7 +92,11 @@ class SimpleResolverTestCase(TestCase): ) playground = ResolverPlayground(ebuilds=ebuilds) - - for atoms, options, action, expected_result, expected_mergelist in requests: - success, mergelist = playground.run(atoms, options, action) - self.assertEqual((success, mergelist), (expected_result, expected_mergelist)) + try: + for atoms, options, action, \ + expected_result, expected_mergelist in requests: + success, mergelist = playground.run(atoms, options, action) + self.assertEqual((success, mergelist), + (expected_result, expected_mergelist)) + finally: + playground.cleanup() diff --git a/pym/portage/tests/resolver/test_simple.py b/pym/portage/tests/resolver/test_simple.py index c092129b..0e77c9e6 100644 --- a/pym/portage/tests/resolver/test_simple.py +++ b/pym/portage/tests/resolver/test_simple.py @@ -24,7 +24,11 @@ class SimpleResolverTestCase(TestCase): ) playground = ResolverPlayground(ebuilds=ebuilds, installed=installed) - - for atoms, options, action, expected_result, expected_mergelist in requests: - success, mergelist = playground.run(atoms, options, action) - self.assertEqual((success, mergelist), (expected_result, expected_mergelist)) + try: + for atoms, options, action, \ + expected_result, expected_mergelist in requests: + success, mergelist = playground.run(atoms, options, action) + self.assertEqual((success, mergelist), + (expected_result, expected_mergelist)) + finally: + playground.cleanup() -- cgit v1.2.3-65-gdbad From 81821e3626dba0319c37527c823a99b5254b7847 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Thu, 5 Aug 2010 13:34:47 -0700 Subject: Add a fake _test_ option to myopts that can be used for conditional test code. --- pym/_emerge/depgraph.py | 6 +++--- pym/portage/tests/resolver/ResolverPlayground.py | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 15004821..cc697ccf 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -66,10 +66,10 @@ class _frozen_depgraph_config(object): if settings.get("PORTAGE_DEBUG", "") == "1": self.edebug = 1 self.spinner = spinner - if "/" in trees: - self._running_root = trees["/"]["root_config"] - else: + if "_test_" in myopts and "/" not in trees: self._running_root = trees[self.target_root]["root_config"] + else: + self._running_root = trees["/"]["root_config"] self._opts_no_restart = frozenset(["--buildpkgonly", "--fetchonly", "--fetch-all-uri", "--pretend"]) self.pkgsettings = {} diff --git a/pym/portage/tests/resolver/ResolverPlayground.py b/pym/portage/tests/resolver/ResolverPlayground.py index 9034deb8..a9954fef 100644 --- a/pym/portage/tests/resolver/ResolverPlayground.py +++ b/pym/portage/tests/resolver/ResolverPlayground.py @@ -180,6 +180,9 @@ class ResolverPlayground(object): myopts["--root"] = self.root myopts["--config-root"] = self.root myopts["--root-deps"] = "rdeps" + # Add a fake _test_ option that can be used for + # conditional test code. + myopts["_test_"] = True portage.util.noiselimit = -2 myparams = create_depgraph_params(myopts, myaction) -- cgit v1.2.3-65-gdbad From aa05f70c6053e0e106a53c1af1c69ec39735807b Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Thu, 5 Aug 2010 15:11:24 -0700 Subject: Add a test case for lazyimport._preload_portage_submodules(). --- pym/portage/tests/lazyimport/__init__.py | 0 pym/portage/tests/lazyimport/__test__ | 0 .../tests/lazyimport/test_preload_portage_submodules.py | 16 ++++++++++++++++ 3 files changed, 16 insertions(+) create mode 100644 pym/portage/tests/lazyimport/__init__.py create mode 100644 pym/portage/tests/lazyimport/__test__ create mode 100644 pym/portage/tests/lazyimport/test_preload_portage_submodules.py diff --git a/pym/portage/tests/lazyimport/__init__.py b/pym/portage/tests/lazyimport/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pym/portage/tests/lazyimport/__test__ b/pym/portage/tests/lazyimport/__test__ new file mode 100644 index 00000000..e69de29b diff --git a/pym/portage/tests/lazyimport/test_preload_portage_submodules.py b/pym/portage/tests/lazyimport/test_preload_portage_submodules.py new file mode 100644 index 00000000..9d20ebac --- /dev/null +++ b/pym/portage/tests/lazyimport/test_preload_portage_submodules.py @@ -0,0 +1,16 @@ +# Copyright 2010 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +import portage +from portage.tests import TestCase + +class PreloadPortageSubmodulesTestCase(TestCase): + + def testPreloadPortageSubmodules(self): + """ + Verify that _preload_portage_submodules() doesn't leave any + remaining proxies that refer to the portage.* namespace. + """ + portage.proxy.lazyimport._preload_portage_submodules() + for name in portage.proxy.lazyimport._module_proxies: + self.assertEqual(name.startswith('portage.'), False) -- cgit v1.2.3-65-gdbad From 7606baaf099a9ea209507a677fcc60077d390052 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Thu, 5 Aug 2010 15:22:53 -0700 Subject: Use ValueError.args since direct indexing of ValueError is not supported in python3, as reported in bug 241132, commment #4. --- pym/_emerge/actions.py | 2 +- pym/_emerge/main.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py index bbab613b..e8c6e3ad 100644 --- a/pym/_emerge/actions.py +++ b/pym/_emerge/actions.py @@ -2354,7 +2354,7 @@ def action_uninstall(settings, trees, ldpath_mtimes, for line in textwrap.wrap(msg, 70): writemsg_level("!!! %s\n" % (line,), level=logging.ERROR, noiselevel=-1) - for i in e[0]: + for i in e.args[0]: writemsg_level(" %s\n" % colorize("INFORM", i), level=logging.ERROR, noiselevel=-1) writemsg_level("\n", level=logging.ERROR, noiselevel=-1) diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py index 92df3bfb..aa5891f9 100644 --- a/pym/_emerge/main.py +++ b/pym/_emerge/main.py @@ -1607,7 +1607,7 @@ def emerge_main(): for line in textwrap.wrap(msg, 70): writemsg_level("!!! %s\n" % (line,), level=logging.ERROR, noiselevel=-1) - for i in e[0]: + for i in e.args[0]: writemsg_level(" %s\n" % colorize("INFORM", i), level=logging.ERROR, noiselevel=-1) writemsg_level("\n", level=logging.ERROR, noiselevel=-1) -- cgit v1.2.3-65-gdbad From 7d92c18b49fabc98f75ca55c51acd2b849c39f5c Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Thu, 5 Aug 2010 16:02:25 -0700 Subject: Bug #331271 - Fix USE_EXPAND wildcards so that the USE="linguas_en_US -linguas_*" case is handled correctly. --- pym/portage/package/ebuild/config.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py index 7266a636..bfff1cc4 100644 --- a/pym/portage/package/ebuild/config.py +++ b/pym/portage/package/ebuild/config.py @@ -2292,6 +2292,12 @@ class config(object): continue if x[0] == "-": + if x[-2:] == '_*': + prefix = x[1:-1] + prefix_len = len(prefix) + myflags.difference_update( + [y for y in myflags if \ + y[:prefix_len] == prefix]) myflags.discard(x[1:]) continue -- cgit v1.2.3-65-gdbad From 7c38e75dd8460f7a1ddafce7bd9d3f9a87d0c4e9 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Thu, 5 Aug 2010 19:35:37 -0700 Subject: Bug #331271 - Fix USE_EXPAND wildcards so that the USE="linguas_* -linguas_en_US" case is handled correctly. --- pym/portage/package/ebuild/config.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py index bfff1cc4..ba3e91c9 100644 --- a/pym/portage/package/ebuild/config.py +++ b/pym/portage/package/ebuild/config.py @@ -2273,6 +2273,9 @@ class config(object): # For optimal performance, use slice # comparison instead of startswith(). + iuse = self.configdict["pkg"].get("IUSE") + if iuse is not None: + iuse = [x.lstrip("+-") for x in iuse.split()] myflags = set() for curdb in self.uvlist: cur_use_expand = [x for x in use_expand if x in curdb] @@ -2301,7 +2304,24 @@ class config(object): myflags.discard(x[1:]) continue - myflags.add(x) + if iuse is not None and x[-2:] == '_*': + # Expand wildcards here, so that cases like + # USE="linguas_* -linguas_en_US" work correctly. + prefix = x[:-1] + prefix_len = len(prefix) + has_iuse = False + for y in iuse: + if y[:prefix_len] == prefix: + has_iuse = True + myflags.add(y) + if not has_iuse: + # There are no matching IUSE, so allow the + # wildcard to pass through. This allows + # linguas_* to trigger unset LINGUAS in + # cases when no linguas_ flags are in IUSE. + myflags.add(x) + else: + myflags.add(x) for var in cur_use_expand: var_lower = var.lower() -- cgit v1.2.3-65-gdbad From 88f0bd14c7bf43af8732b1f31f479aa9837c04be Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Thu, 5 Aug 2010 23:35:33 -0700 Subject: Fix scope issue that could cause 'retval' variable to be undefined. --- pym/_emerge/PackageUninstall.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pym/_emerge/PackageUninstall.py b/pym/_emerge/PackageUninstall.py index 58a1717a..6f528663 100644 --- a/pym/_emerge/PackageUninstall.py +++ b/pym/_emerge/PackageUninstall.py @@ -27,8 +27,8 @@ class PackageUninstall(AsynchronousTask): else: self.returncode = os.EX_OK - if retval == 1: - self.world_atom(self.pkg) + if retval == 1: + self.world_atom(self.pkg) self.wait() -- cgit v1.2.3-65-gdbad From e7c68e7d1c53b5ece656fffae66be5a6f7c13e25 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 00:24:16 -0700 Subject: Pass more parameters to the Package constructor. --- pym/_emerge/unmerge.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pym/_emerge/unmerge.py b/pym/_emerge/unmerge.py index c8ad7a2e..5b466e2d 100644 --- a/pym/_emerge/unmerge.py +++ b/pym/_emerge/unmerge.py @@ -40,9 +40,9 @@ def unmerge(root_config, myopts, unmerge_action, def _pkg(cpv): pkg = pkg_cache.get(cpv) if pkg is None: - pkg = Package(cpv=cpv, installed=True, + pkg = Package(built=True, cpv=cpv, installed=True, metadata=zip(db_keys, vartree.dbapi.aux_get(cpv, db_keys)), - root_config=root_config, + operation="uninstall", root_config=root_config, type_name="installed") pkg_cache[cpv] = pkg return pkg -- cgit v1.2.3-65-gdbad From b9226939dc1b50d7b77e8ac99112d468b2140b52 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 01:21:47 -0700 Subject: In action_unmerge(), create a Schuduler instance for calls to unmerge(), in order to cause redirection of ebuild phase output to logs as required for options such as --quiet. --- pym/_emerge/Scheduler.py | 1 + pym/_emerge/actions.py | 23 +++++++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/pym/_emerge/Scheduler.py b/pym/_emerge/Scheduler.py index 94391fc8..c6af547f 100644 --- a/pym/_emerge/Scheduler.py +++ b/pym/_emerge/Scheduler.py @@ -345,6 +345,7 @@ class Scheduler(PollScheduler): def _set_digraph(self, digraph): if "--nodeps" in self.myopts or \ + digraph is None or \ (self._max_jobs is not True and self._max_jobs < 2): # save some memory self._digraph = None diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py index e8c6e3ad..9de45f44 100644 --- a/pym/_emerge/actions.py +++ b/pym/_emerge/actions.py @@ -515,7 +515,7 @@ def action_config(settings, trees, myopts, myfiles): print() def action_depclean(settings, trees, ldpath_mtimes, - myopts, action, myfiles, spinner): + myopts, action, myfiles, spinner, scheduler=None): # Kill packages that aren't explicitly merged or are required as a # dependency of another package. World file is explicit. @@ -576,7 +576,8 @@ def action_depclean(settings, trees, ldpath_mtimes, if cleanlist: unmerge(root_config, myopts, "unmerge", - cleanlist, ldpath_mtimes, ordered=ordered) + cleanlist, ldpath_mtimes, ordered=ordered, + scheduler=scheduler) if action == "prune": return @@ -2427,18 +2428,28 @@ def action_uninstall(settings, trees, ldpath_mtimes, for line in textwrap.wrap(msg, 72): out.ewarn(line) + if action == 'deselect': + return action_deselect(settings, trees, opts, valid_atoms) + + # Create a Schuduler for calls to unmerge(), in order to cause + # redirection of ebuild phase output to logs as required for + # options such as --quiet. + sched = Scheduler(settings, trees, None, opts, + spinner, [], [], None) + sched._background = sched._background_mode() + sched._status_display.quiet = True + if action in ('clean', 'unmerge') or \ (action == 'prune' and "--nodeps" in opts): # When given a list of atoms, unmerge them in the order given. ordered = action == 'unmerge' unmerge(trees[settings["ROOT"]]['root_config'], opts, action, - valid_atoms, ldpath_mtimes, ordered=ordered) + valid_atoms, ldpath_mtimes, ordered=ordered, + scheduler=sched._sched_iface) rval = os.EX_OK - elif action == 'deselect': - rval = action_deselect(settings, trees, opts, valid_atoms) else: rval = action_depclean(settings, trees, ldpath_mtimes, - opts, action, valid_atoms, spinner) + opts, action, valid_atoms, spinner, scheduler=sched._sched_iface) return rval -- cgit v1.2.3-65-gdbad From 7295411a389c89418b460c059e2a222475dd1ec4 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 03:48:26 -0700 Subject: Use a regular expression to simplify dblink.getcontents(). --- pym/portage/dbapi/vartree.py | 110 ++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 65 deletions(-) diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py index bda96929..db76e045 100644 --- a/pym/portage/dbapi/vartree.py +++ b/pym/portage/dbapi/vartree.py @@ -1878,14 +1878,14 @@ class dblink(object): """ import re - _normalize_needed = re.compile(r'.*//.*|^[^/]|.+/$|(^|.*/)\.\.?(/.*|$)') - _contents_split_counts = { - "dev": 2, - "dir": 2, - "fif": 2, - "obj": 4, - "sym": 5 - } + _normalize_needed = re.compile(r'//|^[^/]|./$|(^|/)\.\.?(/|$)') + + _contents_re = re.compile(r'^(' + \ + r'(?P(dev|dir|fif) (.+))|' + \ + r'(?P(obj) (.+) (\S+) (\d+))|' + \ + r'(?P(sym) (.+) -> (.+) (\d+))' + \ + r')$' + ) # When looping over files for merge/unmerge, temporarily yield to the # scheduler each time this many files are processed. @@ -2033,7 +2033,10 @@ class dblink(object): myc.close() null_byte = "\0" normalize_needed = self._normalize_needed - contents_split_counts = self._contents_split_counts + contents_re = self._contents_re + obj_index = contents_re.groupindex['obj'] + dir_index = contents_re.groupindex['dir'] + sym_index = contents_re.groupindex['sym'] myroot = self.myroot if myroot == os.path.sep: myroot = None @@ -2045,63 +2048,40 @@ class dblink(object): errors.append((pos + 1, _("Null byte found in CONTENTS entry"))) continue line = line.rstrip("\n") - # Split on " " so that even file paths that - # end with spaces can be handled. - mydat = line.split(" ") - entry_type = mydat[0] # empty string if line is empty - correct_split_count = contents_split_counts.get(entry_type) - if correct_split_count and len(mydat) > correct_split_count: - # Apparently file paths contain spaces, so reassemble - # the split have the correct_split_count. - newsplit = [entry_type] - spaces_total = len(mydat) - correct_split_count - if entry_type == "sym": - try: - splitter = mydat.index("->", 2, len(mydat) - 2) - except ValueError: - errors.append((pos + 1, _("Unrecognized CONTENTS entry"))) - continue - spaces_in_path = splitter - 2 - spaces_in_target = spaces_total - spaces_in_path - newsplit.append(" ".join(mydat[1:splitter])) - newsplit.append("->") - target_end = splitter + spaces_in_target + 2 - newsplit.append(" ".join(mydat[splitter + 1:target_end])) - newsplit.extend(mydat[target_end:]) - else: - path_end = spaces_total + 2 - newsplit.append(" ".join(mydat[1:path_end])) - newsplit.extend(mydat[path_end:]) - mydat = newsplit - - # we do this so we can remove from non-root filesystems - # (use the ROOT var to allow maintenance on other partitions) - try: - if normalize_needed.match(mydat[1]): - mydat[1] = normalize_path(mydat[1]) - if not mydat[1].startswith(os.path.sep): - mydat[1] = os.path.sep + mydat[1] - if myroot: - mydat[1] = os.path.join(myroot, mydat[1].lstrip(os.path.sep)) - if mydat[0] == "obj": - #format: type, mtime, md5sum - pkgfiles[mydat[1]] = [mydat[0], mydat[3], mydat[2]] - elif mydat[0] == "dir": - #format: type - pkgfiles[mydat[1]] = [mydat[0]] - elif mydat[0] == "sym": - #format: type, mtime, dest - pkgfiles[mydat[1]] = [mydat[0], mydat[4], mydat[3]] - elif mydat[0] == "dev": - #format: type - pkgfiles[mydat[1]] = [mydat[0]] - elif mydat[0]=="fif": - #format: type - pkgfiles[mydat[1]] = [mydat[0]] - else: - errors.append((pos + 1, _("Unrecognized CONTENTS entry"))) - except (KeyError, IndexError): + m = contents_re.match(line) + if m is None: errors.append((pos + 1, _("Unrecognized CONTENTS entry"))) + continue + + if m.group(obj_index) is not None: + base = obj_index + #format: type, mtime, md5sum + data = (m.group(base+1), m.group(base+4), m.group(base+3)) + elif m.group(dir_index) is not None: + base = dir_index + #format: type + data = (m.group(base+1),) + elif m.group(sym_index) is not None: + base = sym_index + #format: type, mtime, dest + data = (m.group(base+1), m.group(base+4), m.group(base+3)) + else: + # This won't happen as long the regular expression + # is written to only match valid entries. + raise AssertionError(_("required group not found " + \ + "in CONTENTS entry: '%s'") % line) + + path = m.group(base+2) + if normalize_needed.search(path) is not None: + path = normalize_path(path) + if not path.startswith(os.path.sep): + path = os.path.sep + path + + if myroot is not None: + path = os.path.join(myroot, path.lstrip(os.path.sep)) + + pkgfiles[path] = data + if errors: writemsg(_("!!! Parse error in '%s'\n") % contents_file, noiselevel=-1) for pos, e in errors: -- cgit v1.2.3-65-gdbad From ec1507373773c320a823524292b24c0d90473ec7 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 04:57:05 -0700 Subject: Split out a _getmaskingstatus() method that returns categorized message objects instead of plain strings. --- pym/portage/package/ebuild/getmaskingstatus.py | 33 ++++++++++++++++++-------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/pym/portage/package/ebuild/getmaskingstatus.py b/pym/portage/package/ebuild/getmaskingstatus.py index 7211a02d..f1ccd6d9 100644 --- a/pym/portage/package/ebuild/getmaskingstatus.py +++ b/pym/portage/package/ebuild/getmaskingstatus.py @@ -15,12 +15,25 @@ from portage.versions import catpkgsplit, cpv_getkey if sys.hexversion >= 0x3000000: basestring = str +class _MaskReason(object): + + __slots__ = ('category', 'message') + + def __init__(self, category, message): + self.category = category + self.message = message + def getmaskingstatus(mycpv, settings=None, portdb=None): if settings is None: settings = config(clone=portage.settings) if portdb is None: portdb = portage.portdb + return [mreason.message for \ + mreason in _getmaskingstatus(mycpv, settings, portdb)] + +def _getmaskingstatus(mycpv, settings, portdb): + metadata = None installed = False if not isinstance(mycpv, basestring): @@ -40,7 +53,7 @@ def getmaskingstatus(mycpv, settings=None, portdb=None): except KeyError: if not portdb.cpv_exists(mycpv): raise - return ["corruption"] + return [_MaskReason("corruption", "corruption")] if "?" in metadata["LICENSE"]: settings.setcpv(mycpv, mydb=metadata) metadata["USE"] = settings["PORTAGE_USE"] @@ -51,11 +64,11 @@ def getmaskingstatus(mycpv, settings=None, portdb=None): # profile checking if settings._getProfileMaskAtom(mycpv, metadata): - rValue.append("profile") + rValue.append(_MaskReason("profile", "profile")) # package.mask checking if settings._getMaskAtom(mycpv, metadata): - rValue.append("package.mask") + rValue.append(_MaskReason("package.mask", "package.mask")) # keywords checking eapi = metadata["EAPI"] @@ -65,9 +78,9 @@ def getmaskingstatus(mycpv, settings=None, portdb=None): if eapi.startswith("-"): eapi = eapi[1:] if not eapi_is_supported(eapi): - return ["EAPI %s" % eapi] + return [_MaskReason("EAPI", "EAPI %s" % eapi)] elif _eapi_is_deprecated(eapi) and not installed: - return ["EAPI %s" % eapi] + return [_MaskReason("EAPI", "EAPI %s" % eapi)] egroups = settings.configdict["backupenv"].get( "ACCEPT_KEYWORDS", "").split() pgroups = settings["ACCEPT_KEYWORDS"].split() @@ -132,9 +145,9 @@ def getmaskingstatus(mycpv, settings=None, portdb=None): if x in allowed_tokens] msg = license_split[:] msg.append("license(s)") - rValue.append(" ".join(msg)) + rValue.append(_MaskReason("LICENSE", " ".join(msg))) except portage.exception.InvalidDependString as e: - rValue.append("LICENSE: "+str(e)) + rValue.append(_MaskReason("LICENSE", "LICENSE: "+str(e))) try: missing_properties = settings._getMissingProperties(mycpv, metadata) @@ -146,13 +159,13 @@ def getmaskingstatus(mycpv, settings=None, portdb=None): if x in allowed_tokens] msg = properties_split[:] msg.append("properties") - rValue.append(" ".join(msg)) + rValue.append(_MaskReason("PROPERTIES", " ".join(msg))) except portage.exception.InvalidDependString as e: - rValue.append("PROPERTIES: "+str(e)) + rValue.append(_MaskReason("PROPERTIES", "PROPERTIES: "+str(e))) # Only show KEYWORDS masks for installed packages # if they're not masked for any other reason. if kmask and (not installed or not rValue): - rValue.append(kmask+" keyword") + rValue.append(_MaskReason("KEYWORDS", kmask + " keyword")) return rValue -- cgit v1.2.3-65-gdbad From 6d935d3362d9bff8da88e2e7a02062a879e7017e Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 05:09:30 -0700 Subject: Bug #331413 - Clarify valid $VERSION parts that may be used in conjunction with the =$CATEGORY/$PN-$VERSION* atom operator. --- man/ebuild.5 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/man/ebuild.5 b/man/ebuild.5 index 5c71cbea..e3936653 100644 --- a/man/ebuild.5 +++ b/man/ebuild.5 @@ -288,7 +288,10 @@ beginning with \fBEAPI 2\fR. .br \fI*\fR means match any version of the package so long as the specified base is matched. So with a version of '2*', we can match '2.1', '2.2', '2.2.1', -etc... and not match version '1.0', '3.0', '4.1', etc... +etc... and not match version '1.0', '3.0', '4.1', etc... The version part +that comes before the '*' must be a valid version in the absence of the '*'. +For example, '2' is a valid version and '2.' is not. Therefore, '2*' is +allowed and '2.*' is not. .br \fI~\fR means match any revision of the base version specified. So in the above example, we would match versions '1.0.2a', '1.0.2a\-r1', '1.0.2a\-r2', -- cgit v1.2.3-65-gdbad From 0d529b7829b19b3f55dc05747dc8299ae32498e1 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 05:30:42 -0700 Subject: * Split out a _get_masking_status() method that returns categorized message objects instead of plain strings. * Move masks to the "invalid" category. --- pym/_emerge/depgraph.py | 18 +++++++++++++----- pym/portage/package/ebuild/getmaskingstatus.py | 4 ++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index cc697ccf..4addbd44 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -20,6 +20,8 @@ from portage.dep import Atom from portage.output import bold, blue, colorize, create_color_func, darkblue, \ darkgreen, green, nc_len, red, teal, turquoise, yellow bad = create_color_func("BAD") +from portage.package.ebuild.getmaskingstatus import \ + _getmaskingstatus, _MaskReason from portage.sets import SETPREFIX from portage.sets.base import InternalPackageSet from portage.util import cmp_sort_key, writemsg, writemsg_stdout @@ -6020,21 +6022,27 @@ def show_blocker_docs_link(): print() def get_masking_status(pkg, pkgsettings, root_config): + return [mreason.message for \ + mreason in _get_masking_status(pkg, pkgsettings, root_config)] - mreasons = portage.getmaskingstatus( +def _get_masking_status(pkg, pkgsettings, root_config): + + mreasons = _getmaskingstatus( pkg, settings=pkgsettings, portdb=root_config.trees["porttree"].dbapi) if not pkg.installed: if not pkgsettings._accept_chost(pkg.cpv, pkg.metadata): - mreasons.append("CHOST: %s" % \ - pkg.metadata["CHOST"]) + mreasons.append(_MaskReason("CHOST", "CHOST: %s" % \ + pkg.metadata["CHOST"])) if pkg.invalid: for msg_type, msgs in pkg.invalid.items(): for msg in msgs: - mreasons.append("invalid: %s" % (msg,)) + mreasons.append( + _MaskReason("invalid", "invalid: %s" % (msg,))) if not pkg.metadata["SLOT"]: - mreasons.append("invalid: SLOT is undefined") + mreasons.append( + _MaskReason("invalid", "SLOT: undefined")) return mreasons diff --git a/pym/portage/package/ebuild/getmaskingstatus.py b/pym/portage/package/ebuild/getmaskingstatus.py index f1ccd6d9..61a06e76 100644 --- a/pym/portage/package/ebuild/getmaskingstatus.py +++ b/pym/portage/package/ebuild/getmaskingstatus.py @@ -147,7 +147,7 @@ def _getmaskingstatus(mycpv, settings, portdb): msg.append("license(s)") rValue.append(_MaskReason("LICENSE", " ".join(msg))) except portage.exception.InvalidDependString as e: - rValue.append(_MaskReason("LICENSE", "LICENSE: "+str(e))) + rValue.append(_MaskReason("invalid", "LICENSE: "+str(e))) try: missing_properties = settings._getMissingProperties(mycpv, metadata) @@ -161,7 +161,7 @@ def _getmaskingstatus(mycpv, settings, portdb): msg.append("properties") rValue.append(_MaskReason("PROPERTIES", " ".join(msg))) except portage.exception.InvalidDependString as e: - rValue.append(_MaskReason("PROPERTIES", "PROPERTIES: "+str(e))) + rValue.append(_MaskReason("invalid", "PROPERTIES: "+str(e))) # Only show KEYWORDS masks for installed packages # if they're not masked for any other reason. -- cgit v1.2.3-65-gdbad From 82a1ed5044a54438392a4e604caf95777d11e0db Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 05:42:07 -0700 Subject: Use _get_masking_status() for the --autounmaks KEYWORDS check. --- pym/_emerge/depgraph.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 4addbd44..4dd3c3f1 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -2538,8 +2538,10 @@ class depgraph(object): if not allow_missing_keywords: return False - mreasons = get_masking_status(pkg, pkgsettings, root_config) - if len(mreasons) == 1 and mreasons[0].startswith("~") and mreasons[0].endswith("keyword"): + mreasons = _get_masking_status(pkg, pkgsettings, root_config) + if len(mreasons) == 1 and \ + mreasons[0].category == 'KEYWORDS' and \ + mreasons[0].message.startswith("~"): return True else: return False -- cgit v1.2.3-65-gdbad From 73dcdcd67c9c3fd7abf4782aaeab0d1ee126ad33 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 05:49:28 -0700 Subject: Add a _MaskReason.hint attribute that the --autounmask code can use to simplify the 'unstable keyword' check. --- pym/_emerge/depgraph.py | 3 +-- pym/portage/package/ebuild/getmaskingstatus.py | 10 +++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 4dd3c3f1..b7bbbd4b 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -2540,8 +2540,7 @@ class depgraph(object): return False mreasons = _get_masking_status(pkg, pkgsettings, root_config) if len(mreasons) == 1 and \ - mreasons[0].category == 'KEYWORDS' and \ - mreasons[0].message.startswith("~"): + mreasons[0].hint == 'unstable keyword': return True else: return False diff --git a/pym/portage/package/ebuild/getmaskingstatus.py b/pym/portage/package/ebuild/getmaskingstatus.py index 61a06e76..5b209018 100644 --- a/pym/portage/package/ebuild/getmaskingstatus.py +++ b/pym/portage/package/ebuild/getmaskingstatus.py @@ -17,11 +17,12 @@ if sys.hexversion >= 0x3000000: class _MaskReason(object): - __slots__ = ('category', 'message') + __slots__ = ('category', 'message', 'hint') - def __init__(self, category, message): + def __init__(self, category, message, hint=None): self.category = category self.message = message + self.hint = hint def getmaskingstatus(mycpv, settings=None, portdb=None): if settings is None: @@ -114,6 +115,7 @@ def _getmaskingstatus(mycpv, settings, portdb): del inc_pgroups kmask = "missing" + kmask_hint = None if '**' in pgroups: kmask = None @@ -133,6 +135,7 @@ def _getmaskingstatus(mycpv, settings, portdb): break elif gp=="~"+myarch and myarch in pgroups: kmask="~"+myarch + kmask_hint = "unstable keyword" break try: @@ -166,6 +169,7 @@ def _getmaskingstatus(mycpv, settings, portdb): # Only show KEYWORDS masks for installed packages # if they're not masked for any other reason. if kmask and (not installed or not rValue): - rValue.append(_MaskReason("KEYWORDS", kmask + " keyword")) + rValue.append(_MaskReason("KEYWORDS", + kmask + " keyword", hint=kmask_hint)) return rValue -- cgit v1.2.3-65-gdbad From bc67368537e26a8e2df53a1674366dcbfc6b2763 Mon Sep 17 00:00:00 2001 From: Arfrever Frehtes Taifersar Arahesis Date: Fri, 6 Aug 2010 18:37:43 +0200 Subject: Fix typo in comment. --- pym/_emerge/actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py index 9de45f44..aeae5e75 100644 --- a/pym/_emerge/actions.py +++ b/pym/_emerge/actions.py @@ -2431,7 +2431,7 @@ def action_uninstall(settings, trees, ldpath_mtimes, if action == 'deselect': return action_deselect(settings, trees, opts, valid_atoms) - # Create a Schuduler for calls to unmerge(), in order to cause + # Create a Scheduler for calls to unmerge(), in order to cause # redirection of ebuild phase output to logs as required for # options such as --quiet. sched = Scheduler(settings, trees, None, opts, -- cgit v1.2.3-65-gdbad From b14a48bd91262033c8cdfdceb296dc6db6aaf266 Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Fri, 6 Aug 2010 21:14:21 +0200 Subject: autounmask: Add ability to adjust USE --- pym/_emerge/depgraph.py | 156 +++++++++++++++++++---- pym/portage/tests/resolver/ResolverPlayground.py | 26 +++- pym/portage/tests/resolver/test_autounmask.py | 131 +++++++++++++++++++ pym/portage/tests/resolver/test_eapi.py | 6 +- pym/portage/tests/resolver/test_simple.py | 4 +- 5 files changed, 288 insertions(+), 35 deletions(-) create mode 100644 pym/portage/tests/resolver/test_autounmask.py diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index b7bbbd4b..691a7560 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -111,7 +111,7 @@ class _frozen_depgraph_config(object): class _dynamic_depgraph_config(object): def __init__(self, depgraph, myparams, allow_backtracking, - runtime_pkg_mask, needed_user_config_changes): + runtime_pkg_mask, needed_user_config_changes, needed_use_config_changes): self.myparams = myparams.copy() self._vdb_loaded = False self._allow_backtracking = allow_backtracking @@ -195,6 +195,15 @@ class _dynamic_depgraph_config(object): dict((k.copy(), v.copy()) for (k, v) in \ needed_user_config_changes.items()) + if needed_use_config_changes is None: + self._needed_use_config_changes = {} + else: + self._needed_use_config_changes = \ + dict((k.copy(), (v[0].copy(), v[1].copy())) for (k, v) in \ + needed_use_config_changes.items()) + + self._autounmask = depgraph._frozen_config.myopts.get('--autounmask', 'n') == True + self._runtime_pkg_mask = runtime_pkg_mask self._need_restart = False @@ -265,13 +274,14 @@ class depgraph(object): _dep_keys = ["DEPEND", "RDEPEND", "PDEPEND"] def __init__(self, settings, trees, myopts, myparams, spinner, - frozen_config=None, runtime_pkg_mask=None, needed_user_config_changes=None, allow_backtracking=False): + frozen_config=None, runtime_pkg_mask=None, needed_user_config_changes=None, \ + needed_use_config_changes=None, allow_backtracking=False): if frozen_config is None: frozen_config = _frozen_depgraph_config(settings, trees, myopts, spinner) self._frozen_config = frozen_config self._dynamic_config = _dynamic_depgraph_config(self, myparams, - allow_backtracking, runtime_pkg_mask, needed_user_config_changes) + allow_backtracking, runtime_pkg_mask, needed_user_config_changes, needed_use_config_changes) self._select_atoms = self._select_atoms_highest_available self._select_package = self._select_pkg_highest_available @@ -1122,7 +1132,7 @@ class depgraph(object): myroot = pkg.root mykey = pkg.cpv metadata = pkg.metadata - myuse = pkg.use.enabled + myuse = self._pkg_use_enabled(pkg) jbigkey = pkg depth = pkg.depth + 1 removal_action = "remove" in self._dynamic_config.myparams @@ -1203,7 +1213,7 @@ class depgraph(object): dep_string = portage.dep.paren_normalize( portage.dep.use_reduce( portage.dep.paren_reduce(dep_string), - uselist=pkg.use.enabled)) + uselist=self._pkg_use_enabled(pkg))) dep_string = list(self._queue_disjunctive_deps( pkg, dep_root, dep_priority, dep_string)) @@ -1265,7 +1275,7 @@ class depgraph(object): try: selected_atoms = self._select_atoms(dep_root, - dep_string, myuse=pkg.use.enabled, parent=pkg, + dep_string, myuse=self._pkg_use_enabled(pkg), parent=pkg, strict=strict, priority=dep_priority) except portage.exception.InvalidDependString as e: show_invalid_depstring_notice(pkg, dep_string, str(e)) @@ -1985,7 +1995,9 @@ class depgraph(object): return False, myfavorites if set(self._dynamic_config.digraph.nodes.keys()).intersection( \ - set(self._dynamic_config._needed_user_config_changes.keys())): + set(self._dynamic_config._needed_user_config_changes.keys())) or \ + set(self._dynamic_config.digraph.nodes.keys()).intersection( \ + set(self._dynamic_config._needed_use_config_changes.keys())) : #We failed if the user needs to change the configuration return False, myfavorites @@ -2070,7 +2082,7 @@ class depgraph(object): dep_str = " ".join(pkg.metadata[k] for k in blocker_dep_keys) try: selected_atoms = self._select_atoms( - pkg.root, dep_str, pkg.use.enabled, + pkg.root, dep_str, self._pkg_use_enabled(pkg), parent=pkg, strict=True) except portage.exception.InvalidDependString: continue @@ -2125,6 +2137,9 @@ class depgraph(object): myuse=None, parent=None, strict=True, trees=None, priority=None): """This will raise InvalidDependString if necessary. If trees is None then self._dynamic_config._filtered_trees is used.""" + + _autounmask_backup = self._dynamic_config._autounmask + self._dynamic_config._autounmask = False pkgsettings = self._frozen_config.pkgsettings[root] if trees is None: trees = self._dynamic_config._filtered_trees @@ -2175,6 +2190,7 @@ class depgraph(object): selected_atoms[pkg] = [atom for atom in \ atom_graph.child_nodes(node) if atom in chosen_atoms] + self._dynamic_config._autounmask = _autounmask_backup return selected_atoms def _show_unsatisfied_dep(self, root, atom, myparent=None, arg=None, @@ -2239,7 +2255,7 @@ class depgraph(object): masked_pkg_instances.add(pkg) if atom.unevaluated_atom.use: if not pkg.iuse.is_valid_flag(atom.unevaluated_atom.use.required) \ - or atom.violated_conditionals(pkg.use.enabled).use: + or atom.violated_conditionals(self._pkg_use_enabled(pkg)).use: missing_use.append(pkg) if not mreasons: continue @@ -2257,7 +2273,7 @@ class depgraph(object): missing_use_reasons = [] missing_iuse_reasons = [] for pkg in missing_use: - use = pkg.use.enabled + use = self._pkg_use_enabled(pkg) missing_iuse = pkg.iuse.get_missing_iuse(atom.use.required) mreasons = [] if missing_iuse: @@ -2279,7 +2295,7 @@ class depgraph(object): # Lets see if the violated use deps are conditional. # If so, suggest to change them on the parent. mreasons = [] - violated_atom = atom.unevaluated_atom.violated_conditionals(pkg.use.enabled, myparent.use.enabled) + violated_atom = atom.unevaluated_atom.violated_conditionals(self._pkg_use_enabled(pkg), myparent.use.enabled) if not (violated_atom.use.enabled or violated_atom.use.disabled): #all violated use deps are conditional changes = [] @@ -2509,19 +2525,26 @@ class depgraph(object): def _select_pkg_highest_available_imp(self, root, atom, onlydeps=False): pkg, existing = self._wrapped_select_pkg_highest_available_imp(root, atom, onlydeps=onlydeps) - if self._frozen_config.myopts.get('--autounmask', 'n') is True: + if self._dynamic_config._autounmask is True: if pkg is not None and \ pkg.installed and \ not self._want_installed_pkg(pkg): pkg = None - if pkg is None: + + for allow_missing_keywords in False, True: + if pkg is not None: + break + pkg, existing = \ self._wrapped_select_pkg_highest_available_imp( root, atom, onlydeps=onlydeps, - allow_missing_keywords=True) + allow_use_changes=True, allow_missing_keywords=allow_missing_keywords) if pkg is not None and not pkg.visible: self._dynamic_config._needed_user_config_changes.setdefault(pkg, set()).add("unstable keyword") + + if self._dynamic_config._need_restart: + return None, None return pkg, existing @@ -2545,7 +2568,64 @@ class depgraph(object): else: return False - def _wrapped_select_pkg_highest_available_imp(self, root, atom, onlydeps=False, allow_missing_keywords=False): + def _pkg_use_enabled(self, pkg, target_use=None): + """ + If target_use is None, returns pkg.use.enabled + changes in _needed_use_config_changes. + If target_use is given, the need changes are computed to make the package useable. + Example: target_use = { "foo": True, "bar": False } + The flags target_use must be in the pkg's IUSE. + """ + needed_use_config_change = self._dynamic_config._needed_use_config_changes.get(pkg) + + if target_use is None: + if needed_use_config_change is None: + return pkg.use.enabled + else: + return needed_use_config_change[0] + + if needed_use_config_change is not None: + old_use = needed_use_config_change[0] + new_use = set() + old_changes = needed_use_config_change[1] + new_changes = old_changes.copy() + else: + old_use = pkg.use.enabled + new_use = set() + old_changes = {} + new_changes = {} + + for flag, state in target_use.items(): + if state: + if flag not in old_use: + if new_changes.get(flag) == False: + return old_use + new_changes[flag] = True + new_use.add(flag) + else: + if flag in old_use: + if new_changes.get(flag) == True: + return old_use + new_changes[flag] = False + new_use.update(old_use.difference(target_use.keys())) + + def want_restart_for_use_change(pkg): + if pkg not in self._dynamic_config.digraph.nodes: + return False + #TODO: We can be more clever here. No need to restart if + # 1) we don't have a parent that can't work with our + # new use config + # and + # 2) none of pkg's *DEPEND vars changed + return True + + if new_changes != old_changes: + self._dynamic_config._needed_use_config_changes[pkg] = (new_use, new_changes) + if want_restart_for_use_change(pkg): + self._dynamic_config._need_restart = True + return new_use + + def _wrapped_select_pkg_highest_available_imp(self, root, atom, onlydeps=False, \ + allow_use_changes=False, allow_missing_keywords=False): root_config = self._frozen_config.roots[root] pkgsettings = self._frozen_config.pkgsettings[root] dbs = self._dynamic_config._filtered_trees[root]["dbs"] @@ -2704,11 +2784,21 @@ class depgraph(object): # since IUSE cannot be adjusted by the user. continue - if atom.use.enabled.difference(pkg.use.enabled): + if allow_use_changes: + target_use = {} + for flag in atom.use.enabled: + target_use[flag] = True + for flag in atom.use.disabled: + target_use[flag] = False + use = self._pkg_use_enabled(pkg, target_use) + else: + use = self._pkg_use_enabled(pkg) + + if atom.use.enabled.difference(use): if not pkg.built: packages_with_invalid_use_config.append(pkg) continue - if atom.use.disabled.intersection(pkg.use.enabled): + if atom.use.disabled.intersection(use): if not pkg.built: packages_with_invalid_use_config.append(pkg) continue @@ -2749,7 +2839,7 @@ class depgraph(object): "--reinstall" in self._frozen_config.myopts or \ "--binpkg-respect-use" in self._frozen_config.myopts): iuses = pkg.iuse.all - old_use = pkg.use.enabled + old_use = self._pkg_use_enabled(pkg) if myeb: pkgsettings.setcpv(myeb) else: @@ -2778,7 +2868,7 @@ class depgraph(object): old_use = vardb.aux_get(cpv, ["USE"])[0].split() old_iuse = set(filter_iuse_defaults( vardb.aux_get(cpv, ["IUSE"])[0].split())) - cur_use = pkg.use.enabled + cur_use = self._pkg_use_enabled(pkg) cur_iuse = pkg.iuse.all reinstall_for_flags = \ self._reinstall_for_flags( @@ -3158,7 +3248,7 @@ class depgraph(object): portage.dep._dep_check_strict = False try: success, atoms = portage.dep_check(depstr, - final_db, pkgsettings, myuse=pkg.use.enabled, + final_db, pkgsettings, myuse=self._pkg_use_enabled(pkg), trees=self._dynamic_config._graph_trees, myroot=myroot) except Exception as e: if isinstance(e, SystemExit): @@ -4469,7 +4559,7 @@ class depgraph(object): os.path.dirname(ebuild_path))) else: repo_path_real = portdb.getRepositoryPath(repo_name) - pkg_use = list(pkg.use.enabled) + pkg_use = list(self._pkg_use_enabled(pkg)) if not pkg.built and pkg.operation == 'merge' and \ 'fetch' in pkg.metadata.restrict: fetch = red("F") @@ -4560,7 +4650,7 @@ class depgraph(object): forced_flags.update(pkgsettings.useforce) forced_flags.update(pkgsettings.usemask) - cur_use = [flag for flag in pkg.use.enabled \ + cur_use = [flag for flag in self._pkg_use_enabled(pkg) \ if flag in pkg.iuse.all] cur_iuse = sorted(pkg.iuse.all) @@ -5173,11 +5263,29 @@ class depgraph(object): else: raise NotImplementedError() + use_changes_msg = [] + for pkg, needed_use_config_change in self._dynamic_config._needed_use_config_changes.items(): + self._show_merge_list() + if pkg in self._dynamic_config.digraph.nodes.keys(): + changes = needed_use_config_change[1] + adjustments = [] + for flag, state in changes.items(): + if state: + adjustments.append(flag) + else: + adjustments.append("-" + flag) + use_changes_msg.append("=%s %s\n" % (pkg.cpv, " ".join(adjustments))) + if unstable_keyword_msg: writemsg_stdout("\nThe following " + colorize("BAD", "keyword changes") + \ " are necessary to proceed:\n", noiselevel=-1) writemsg_stdout("".join(unstable_keyword_msg), noiselevel=-1) + if use_changes_msg: + writemsg_stdout("\nThe following " + colorize("BAD", "USE changes") + \ + " are necessary to proceed:\n", noiselevel=-1) + writemsg_stdout("".join(use_changes_msg), noiselevel=-1) + # TODO: Add generic support for "set problem" handlers so that # the below warnings aren't special cases for world only. @@ -5574,7 +5682,9 @@ class depgraph(object): "needed_user_config_changes": self._dynamic_config._needed_user_config_changes.copy(), \ "runtime_pkg_mask": - self._dynamic_config._runtime_pkg_mask.copy() + self._dynamic_config._runtime_pkg_mask.copy(), + "needed_use_config_changes": + self._dynamic_config._needed_use_config_changes.copy() } diff --git a/pym/portage/tests/resolver/ResolverPlayground.py b/pym/portage/tests/resolver/ResolverPlayground.py index a9954fef..3740f831 100644 --- a/pym/portage/tests/resolver/ResolverPlayground.py +++ b/pym/portage/tests/resolver/ResolverPlayground.py @@ -14,6 +14,7 @@ from portage.package.ebuild.config import config from portage.sets import load_default_config from portage.versions import catsplit +from _emerge.Blocker import Blocker from _emerge.create_depgraph_params import create_depgraph_params from _emerge.depgraph import backtrack_depgraph from _emerge.RootConfig import RootConfig @@ -183,19 +184,30 @@ class ResolverPlayground(object): # Add a fake _test_ option that can be used for # conditional test code. myopts["_test_"] = True - + portage.util.noiselimit = -2 myparams = create_depgraph_params(myopts, myaction) success, mydepgraph, favorites = backtrack_depgraph( self.settings, self.trees, myopts, myparams, myaction, myfiles, None) + result = ResolverPlaygroundResult(success, mydepgraph, favorites) portage.util.noiselimit = 0 - if success: - mergelist = [x.cpv for x in mydepgraph._dynamic_config._serialized_tasks_cache] - return True, mergelist - else: - #TODO: Use mydepgraph.display_problems() to return a useful error message - return False, None + return result def cleanup(self): shutil.rmtree(self.root) + +class ResolverPlaygroundResult(object): + def __init__(self, success, mydepgraph, favorites): + self.success = success + self.depgraph = mydepgraph + self.favorites = favorites + self.mergelist = None + + if self.depgraph._dynamic_config._serialized_tasks_cache is not None: + self.mergelist = [] + for x in self.depgraph._dynamic_config._serialized_tasks_cache: + if isinstance(x, Blocker): + self.mergelist.append(x.atom) + else: + self.mergelist.append(x.cpv) diff --git a/pym/portage/tests/resolver/test_autounmask.py b/pym/portage/tests/resolver/test_autounmask.py new file mode 100644 index 00000000..944e5485 --- /dev/null +++ b/pym/portage/tests/resolver/test_autounmask.py @@ -0,0 +1,131 @@ +# Copyright 2010 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +from portage.tests import TestCase +from portage.tests.resolver.ResolverPlayground import ResolverPlayground + +class AutounmaskTestCase(TestCase): + + def testAutounmask(self): + ebuilds = { + #ebuilds to test use changes + "dev-libs/A-1": { "SLOT": 1, "DEPEND": "dev-libs/B[foo]", "EAPI": 2}, + "dev-libs/A-2": { "SLOT": 2, "DEPEND": "dev-libs/B[bar]", "EAPI": 2}, + "dev-libs/B-1": { "DEPEND": "foo? ( dev-libs/C ) bar? ( dev-libs/D )", "IUSE": "foo bar"}, + "dev-libs/C-1": {}, + "dev-libs/D-1": {}, + + #ebuilds to test keyword changes + "app-misc/Z-1": { "KEYWORDS": "~x86", "DEPEND": "app-misc/Y" }, + "app-misc/Y-1": { "KEYWORDS": "~x86" }, + "app-misc/W-1": {}, + "app-misc/W-2": { "KEYWORDS": "~x86" }, + "app-misc/V-1": { "KEYWORDS": "~x86", "DEPEND": ">=app-misc/W-2"}, + + #ebuilds for mixed test for || dep handling + "sci-libs/K-1": { "DEPEND": " || ( sci-libs/L[bar] || ( sci-libs/M sci-libs/P ) )", "EAPI": 2}, + "sci-libs/K-2": { "DEPEND": " || ( sci-libs/L[bar] || ( sci-libs/P sci-libs/M ) )", "EAPI": 2}, + "sci-libs/K-3": { "DEPEND": " || ( sci-libs/M || ( sci-libs/L[bar] sci-libs/P ) )", "EAPI": 2}, + "sci-libs/K-4": { "DEPEND": " || ( sci-libs/M || ( sci-libs/P sci-libs/L[bar] ) )", "EAPI": 2}, + "sci-libs/K-5": { "DEPEND": " || ( sci-libs/P || ( sci-libs/L[bar] sci-libs/M ) )", "EAPI": 2}, + "sci-libs/K-6": { "DEPEND": " || ( sci-libs/P || ( sci-libs/M sci-libs/L[bar] ) )", "EAPI": 2}, + "sci-libs/K-7": { "DEPEND": " || ( sci-libs/M sci-libs/L[bar] )", "EAPI": 2}, + "sci-libs/K-8": { "DEPEND": " || ( sci-libs/L[bar] sci-libs/M )", "EAPI": 2}, + + "sci-libs/L-1": { "IUSE": "bar" }, + "sci-libs/M-1": { "KEYWORDS": "~x86" }, + "sci-libs/P-1": { }, + + #ebuilds to conflicting use changes + "net-www/G-1": { "DEPEND": "net-www/I[foo]", "EAPI": 2 }, + "net-www/H-1": { "DEPEND": "net-www/I[-foo]", "EAPI": 2 }, + "net-www/I-1": { "IUSE": "foo" }, + "net-www/J-1": { "DEPEND": "|| ( net-www/G net-www/H )" }, + "net-www/J-2": { "DEPEND": "|| ( net-www/H net-www/G )" }, + "net-www/K-1": { "DEPEND": "!!net-www/G", "EAPI": 2 }, + } + + requests = ( + #Test USE changes. + #The simple case. + + (["dev-libs/A:1"], {"--autounmask": "n"}, None, False, None), + (["dev-libs/A:1"], {"--autounmask": True}, None, False, \ + ["dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1"]), + + #Make sure we restart if needed. + + (["dev-libs/B", "dev-libs/A:1"], {"--autounmask": True}, None, False, \ + ["dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1"]), + (["dev-libs/A:1", "dev-libs/B"], {"--autounmask": True}, None, False, \ + ["dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1"]), + (["dev-libs/A:1", "dev-libs/A:2"], {"--autounmask": True}, None, False, \ + ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"]), + (["dev-libs/B", "dev-libs/A:1", "dev-libs/A:2"], {"--autounmask": True}, None, False, \ + ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"]), + (["dev-libs/A:1", "dev-libs/B", "dev-libs/A:2"], {"--autounmask": True}, None, False, \ + ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"]), + (["dev-libs/A:1", "dev-libs/A:2", "dev-libs/B"], {"--autounmask": True}, None, False, \ + ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"]), + + #Test keywording. + #The simple case. + + (["app-misc/Z"], {"--autounmask": "n"}, None, False, None), + (["app-misc/Z"], {"--autounmask": True}, None, False, \ + ["app-misc/Y-1", "app-misc/Z-1"]), + + #Make sure that the backtracking for slot conflicts handles our mess. + + (["=app-misc/V-1", "app-misc/W"], {"--autounmask": True}, None, False, \ + ["app-misc/W-2", "app-misc/V-1"]), + (["app-misc/W", "=app-misc/V-1"], {"--autounmask": True}, None, False, \ + ["app-misc/W-2", "app-misc/V-1"]), + + #Mixed testing + #Make sure we don't change use for something in a || dep if there is another choice + #that needs no change. + + (["=sci-libs/K-1"], {"--autounmask": True}, None, True, \ + ["sci-libs/P-1", "sci-libs/K-1"]), + (["=sci-libs/K-2"], {"--autounmask": True}, None, True, \ + ["sci-libs/P-1", "sci-libs/K-2"]), + (["=sci-libs/K-3"], {"--autounmask": True}, None, True, \ + ["sci-libs/P-1", "sci-libs/K-3"]), + (["=sci-libs/K-4"], {"--autounmask": True}, None, True, \ + ["sci-libs/P-1", "sci-libs/K-4"]), + (["=sci-libs/K-5"], {"--autounmask": True}, None, True, \ + ["sci-libs/P-1", "sci-libs/K-5"]), + (["=sci-libs/K-6"], {"--autounmask": True}, None, True, \ + ["sci-libs/P-1", "sci-libs/K-6"]), + + #Make sure we prefer use changes over keyword changes. + (["=sci-libs/K-7"], {"--autounmask": True}, None, False, \ + ["sci-libs/L-1", "sci-libs/K-7"]), + (["=sci-libs/K-8"], {"--autounmask": True}, None, False, \ + ["sci-libs/L-1", "sci-libs/K-8"]), + + #Testing conflict bahviour + (["=net-www/G-1", "=net-www/H-1"], {"--autounmask": True}, None, False, None), + #Some of the following tests don't work because we are not able to take back + #changes that later on turn out to be not necessary, because the package + #that induced the change gets masked. + #~ (["=net-www/J-1", "=net-www/K-1"], {"--autounmask": True}, None, True, \ + #~ ["net-www/I-1", "net-www/H-1", "net-www/J-1", "net-www/K-1"] ), + (["=net-www/J-2", "=net-www/K-1"], {"--autounmask": True}, None, True, \ + ["net-www/I-1", "net-www/K-1", "net-www/H-1", "net-www/J-2", ] ), + #~ (["=net-www/K-1", "=net-www/J-1"], {"--autounmask": True}, None, True, \ + #~ ["net-www/I-1", "net-www/H-1", "net-www/J-1", "net-www/K-1"] ), + (["=net-www/K-1", "=net-www/J-2"], {"--autounmask": True}, None, True, \ + ["net-www/I-1", "net-www/K-1", "net-www/H-1", "net-www/J-2", ] ), + ) + + playground = ResolverPlayground(ebuilds=ebuilds) + try: + for atoms, options, action, \ + expected_result, expected_mergelist in requests: + result = playground.run(atoms, options, action) + self.assertEqual((result.success, result.mergelist), + (expected_result, expected_mergelist)) + finally: + playground.cleanup() diff --git a/pym/portage/tests/resolver/test_eapi.py b/pym/portage/tests/resolver/test_eapi.py index 19c7b219..d9f34f43 100644 --- a/pym/portage/tests/resolver/test_eapi.py +++ b/pym/portage/tests/resolver/test_eapi.py @@ -4,7 +4,7 @@ from portage.tests import TestCase from portage.tests.resolver.ResolverPlayground import ResolverPlayground -class SimpleResolverTestCase(TestCase): +class EAPITestCase(TestCase): def testEAPI(self): ebuilds = { @@ -95,8 +95,8 @@ class SimpleResolverTestCase(TestCase): try: for atoms, options, action, \ expected_result, expected_mergelist in requests: - success, mergelist = playground.run(atoms, options, action) - self.assertEqual((success, mergelist), + result = playground.run(atoms, options, action) + self.assertEqual((result.success, result.mergelist), (expected_result, expected_mergelist)) finally: playground.cleanup() diff --git a/pym/portage/tests/resolver/test_simple.py b/pym/portage/tests/resolver/test_simple.py index 0e77c9e6..ef19abe1 100644 --- a/pym/portage/tests/resolver/test_simple.py +++ b/pym/portage/tests/resolver/test_simple.py @@ -27,8 +27,8 @@ class SimpleResolverTestCase(TestCase): try: for atoms, options, action, \ expected_result, expected_mergelist in requests: - success, mergelist = playground.run(atoms, options, action) - self.assertEqual((success, mergelist), + result = playground.run(atoms, options, action) + self.assertEqual((result.success, result.mergelist), (expected_result, expected_mergelist)) finally: playground.cleanup() -- cgit v1.2.3-65-gdbad From 233dd1cad9ce4886210bd4183047f319bbfd12da Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 15:53:26 -0700 Subject: Use finally block for autounmask handling inside _select_atoms_highest_available. --- pym/_emerge/depgraph.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 691a7560..82388bb4 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -2138,13 +2138,15 @@ class depgraph(object): """This will raise InvalidDependString if necessary. If trees is None then self._dynamic_config._filtered_trees is used.""" - _autounmask_backup = self._dynamic_config._autounmask - self._dynamic_config._autounmask = False pkgsettings = self._frozen_config.pkgsettings[root] if trees is None: trees = self._dynamic_config._filtered_trees atom_graph = digraph() if True: + # Temporarily disable autounmask so that || preferences + # account for masking and USE settings. + _autounmask_backup = self._dynamic_config._autounmask + self._dynamic_config._autounmask = False try: if parent is not None: trees[root]["parent"] = parent @@ -2157,6 +2159,7 @@ class depgraph(object): pkgsettings, myuse=myuse, myroot=root, trees=trees) finally: + self._dynamic_config._autounmask = _autounmask_backup if parent is not None: trees[root].pop("parent") trees[root].pop("atom_graph") @@ -2190,7 +2193,6 @@ class depgraph(object): selected_atoms[pkg] = [atom for atom in \ atom_graph.child_nodes(node) if atom in chosen_atoms] - self._dynamic_config._autounmask = _autounmask_backup return selected_atoms def _show_unsatisfied_dep(self, root, atom, myparent=None, arg=None, -- cgit v1.2.3-65-gdbad From d306ffaab50cc43a287a57bb96cd40a7efbf4f95 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 15:38:21 -0700 Subject: Make dep_check use depgraph._pkg_use_enabled to query USE settings of new-style virtuals when appropriate. --- pym/_emerge/depgraph.py | 3 +++ pym/portage/dep/dep_check.py | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 82388bb4..d765055b 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -2141,12 +2141,14 @@ class depgraph(object): pkgsettings = self._frozen_config.pkgsettings[root] if trees is None: trees = self._dynamic_config._filtered_trees + mytrees = trees[root] atom_graph = digraph() if True: # Temporarily disable autounmask so that || preferences # account for masking and USE settings. _autounmask_backup = self._dynamic_config._autounmask self._dynamic_config._autounmask = False + mytrees["pkg_use_enabled"] = self._pkg_use_enabled try: if parent is not None: trees[root]["parent"] = parent @@ -2160,6 +2162,7 @@ class depgraph(object): myroot=root, trees=trees) finally: self._dynamic_config._autounmask = _autounmask_backup + del mytrees["pkg_use_enabled"] if parent is not None: trees[root].pop("parent") trees[root].pop("atom_graph") diff --git a/pym/portage/dep/dep_check.py b/pym/portage/dep/dep_check.py index f4a44611..8747bb14 100644 --- a/pym/portage/dep/dep_check.py +++ b/pym/portage/dep/dep_check.py @@ -28,6 +28,7 @@ def _expand_new_virtuals(mysplit, edebug, mydbapi, mysettings, myroot="/", newsplit = [] mytrees = trees[myroot] portdb = mytrees["porttree"].dbapi + pkg_use_enabled = mytrees.get("pkg_use_enabled") atom_graph = mytrees.get("atom_graph") parent = mytrees.get("parent") virt_parent = mytrees.get("virt_parent") @@ -100,7 +101,8 @@ def _expand_new_virtuals(mysplit, edebug, mydbapi, mysettings, myroot="/", atom_graph.add(x, graph_parent) continue - if repoman or not hasattr(portdb, 'match_pkgs'): + if repoman or not hasattr(portdb, 'match_pkgs') or \ + pkg_use_enabled is None: if portdb.cp_list(x.cp): newsplit.append(x) else: @@ -149,7 +151,7 @@ def _expand_new_virtuals(mysplit, edebug, mydbapi, mysettings, myroot="/", # should enforce this. depstring = pkg.metadata['RDEPEND'] pkg_kwargs = kwargs.copy() - pkg_kwargs["myuse"] = pkg.use.enabled + pkg_kwargs["myuse"] = pkg_use_enabled(pkg) if edebug: writemsg_level(_("Virtual Parent: %s\n") \ % (pkg,), noiselevel=-1, level=logging.DEBUG) -- cgit v1.2.3-65-gdbad From 1c4935f8cdd27736ea2d6c82609a4d041419b70e Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 16:01:06 -0700 Subject: Delay variable definitions in _pkg_visibility_check until they are needed. --- pym/_emerge/depgraph.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index d765055b..31f984d5 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -2554,8 +2554,7 @@ class depgraph(object): return pkg, existing def _pkg_visibility_check(self, pkg, allow_missing_keywords=False): - pkgsettings = self._frozen_config.pkgsettings[pkg.root] - root_config = self._frozen_config.roots[pkg.root] + if pkg.visible: return True @@ -2566,6 +2565,9 @@ class depgraph(object): if not allow_missing_keywords: return False + + pkgsettings = self._frozen_config.pkgsettings[pkg.root] + root_config = self._frozen_config.roots[pkg.root] mreasons = _get_masking_status(pkg, pkgsettings, root_config) if len(mreasons) == 1 and \ mreasons[0].hint == 'unstable keyword': -- cgit v1.2.3-65-gdbad From e422f604c7156f2c401c64cf00ca2a0cfc10f207 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 16:15:15 -0700 Subject: Rename allow_missing_keywords to allow_unstable_keywords since that's what it does. --- pym/_emerge/depgraph.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 31f984d5..3b604f04 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -2536,14 +2536,14 @@ class depgraph(object): not self._want_installed_pkg(pkg): pkg = None - for allow_missing_keywords in False, True: + for allow_unstable_keywords in False, True: if pkg is not None: break pkg, existing = \ self._wrapped_select_pkg_highest_available_imp( root, atom, onlydeps=onlydeps, - allow_use_changes=True, allow_missing_keywords=allow_missing_keywords) + allow_use_changes=True, allow_unstable_keywords=allow_unstable_keywords) if pkg is not None and not pkg.visible: self._dynamic_config._needed_user_config_changes.setdefault(pkg, set()).add("unstable keyword") @@ -2553,7 +2553,7 @@ class depgraph(object): return pkg, existing - def _pkg_visibility_check(self, pkg, allow_missing_keywords=False): + def _pkg_visibility_check(self, pkg, allow_unstable_keywords=False): if pkg.visible: return True @@ -2563,7 +2563,7 @@ class depgraph(object): "unstable keyword" in pending_keyword_change: return True - if not allow_missing_keywords: + if not allow_unstable_keywords: return False pkgsettings = self._frozen_config.pkgsettings[pkg.root] @@ -2632,7 +2632,7 @@ class depgraph(object): return new_use def _wrapped_select_pkg_highest_available_imp(self, root, atom, onlydeps=False, \ - allow_use_changes=False, allow_missing_keywords=False): + allow_use_changes=False, allow_unstable_keywords=False): root_config = self._frozen_config.roots[root] pkgsettings = self._frozen_config.pkgsettings[root] dbs = self._dynamic_config._filtered_trees[root]["dbs"] @@ -2729,7 +2729,7 @@ class depgraph(object): # were installed can be automatically downgraded # to an unmasked version. - if not self._pkg_visibility_check(pkg, allow_missing_keywords=allow_missing_keywords): + if not self._pkg_visibility_check(pkg, allow_unstable_keywords=allow_unstable_keywords): continue # Enable upgrade or downgrade to a version @@ -2764,7 +2764,7 @@ class depgraph(object): continue else: if not self._pkg_visibility_check(pkg_eb, \ - allow_missing_keywords=allow_missing_keywords): + allow_unstable_keywords=allow_unstable_keywords): continue # Calculation of USE for unbuilt ebuilds is relatively @@ -2959,12 +2959,12 @@ class depgraph(object): if avoid_update: for pkg in matched_packages: if pkg.installed and self._pkg_visibility_check(pkg, \ - allow_missing_keywords=allow_missing_keywords): + allow_unstable_keywords=allow_unstable_keywords): return pkg, existing_node bestmatch = portage.best( [pkg.cpv for pkg in matched_packages \ - if self._pkg_visibility_check(pkg, allow_missing_keywords=allow_missing_keywords)]) + if self._pkg_visibility_check(pkg, allow_unstable_keywords=allow_unstable_keywords)]) if not bestmatch: # all are masked, so ignore visibility bestmatch = portage.best( -- cgit v1.2.3-65-gdbad From a32038ffb00db6e65cdee26ca7cd08ada4b33208 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 16:27:26 -0700 Subject: Fix _select_pkg_highest_available_imp to reject installed packages returned from _wrapped_select_pkg_highest_available_imp when necessary. --- pym/_emerge/depgraph.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 3b604f04..c0567956 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -2545,6 +2545,11 @@ class depgraph(object): root, atom, onlydeps=onlydeps, allow_use_changes=True, allow_unstable_keywords=allow_unstable_keywords) + if pkg is not None and \ + pkg.installed and \ + not self._want_installed_pkg(pkg): + pkg = None + if pkg is not None and not pkg.visible: self._dynamic_config._needed_user_config_changes.setdefault(pkg, set()).add("unstable keyword") -- cgit v1.2.3-65-gdbad From 29775f8cfeaff844ec4ed220dbf6da144fe37cf2 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 17:01:12 -0700 Subject: Implement ExtendedAtomDict.__iter__(). --- pym/portage/dep/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pym/portage/dep/__init__.py b/pym/portage/dep/__init__.py index 0ba96bde..e0a0fffb 100644 --- a/pym/portage/dep/__init__.py +++ b/pym/portage/dep/__init__.py @@ -785,6 +785,15 @@ class ExtendedAtomDict(portage.cache.mappings.MutableMapping): self._normal = {} self._value_class = value_class + def __iter__(self): + for k in self._normal: + yield k + for k in self._extended: + yield k + + if sys.hexversion >= 0x3000000: + keys = __iter__ + def setdefault(self, cp, default=None): if "*" in cp: return self._extended.setdefault(cp, default) -- cgit v1.2.3-65-gdbad From 0395c698afd27f5ae5ea2dbc20c61979097bc1dd Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 17:16:09 -0700 Subject: Implement ExtendedAtomDict.__len__(). --- pym/portage/dep/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pym/portage/dep/__init__.py b/pym/portage/dep/__init__.py index e0a0fffb..0ee52218 100644 --- a/pym/portage/dep/__init__.py +++ b/pym/portage/dep/__init__.py @@ -794,6 +794,9 @@ class ExtendedAtomDict(portage.cache.mappings.MutableMapping): if sys.hexversion >= 0x3000000: keys = __iter__ + def __len__(self): + return len(self._normal) + len(self._extended) + def setdefault(self, cp, default=None): if "*" in cp: return self._extended.setdefault(cp, default) -- cgit v1.2.3-65-gdbad From ac6ee1217d2309cbc2ab4af893f57fc5b76eaeb2 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 17:25:41 -0700 Subject: Bug #331429 - Fix breakage in package.properties and package.license handling due to behavior of ExtendedAtomDict.get(). We need to use setdefault() when updating ExtendedAtomDict like this. --- pym/portage/package/ebuild/config.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py index ba3e91c9..ff9894d8 100644 --- a/pym/portage/package/ebuild/config.py +++ b/pym/portage/package/ebuild/config.py @@ -868,12 +868,8 @@ class config(object): else: self.configdict["conf"]["ACCEPT_LICENSE"] = " ".join(v) for k, v in licdict.items(): - cp = k.cp - cp_dict = self._plicensedict.get(cp) - if not cp_dict: - cp_dict = {} - self._plicensedict[cp] = cp_dict - cp_dict[k] = self.expandLicenseTokens(v) + self._plicensedict.setdefault(k.cp, {})[k] = \ + self.expandLicenseTokens(v) #package.properties propdict = grabdict_package(os.path.join( @@ -885,12 +881,7 @@ class config(object): else: self.configdict["conf"]["ACCEPT_PROPERTIES"] = " ".join(v) for k, v in propdict.items(): - cp = k.cp - cp_dict = self._ppropertiesdict.get(cp) - if not cp_dict: - cp_dict = {} - self._ppropertiesdict[cp] = cp_dict - cp_dict[k] = v + self._ppropertiesdict.setdefault(k.cp, {})[k] = v self._local_repo_configs = {} self._local_repo_conf_path = \ -- cgit v1.2.3-65-gdbad From 67797d2ed2914b5eb805e096904fd34ace78f74f Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 17:34:09 -0700 Subject: Use ResolverPlayground for it's config instance, so that we don't instantiate portage.settings. --- pym/portage/tests/ebuild/test_spawn.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pym/portage/tests/ebuild/test_spawn.py b/pym/portage/tests/ebuild/test_spawn.py index f37a0c5a..bb9fb262 100644 --- a/pym/portage/tests/ebuild/test_spawn.py +++ b/pym/portage/tests/ebuild/test_spawn.py @@ -4,16 +4,19 @@ import codecs import errno import sys +from tempfile import mkstemp from portage import os from portage import _encodings from portage import _unicode_encode +from portage import spawn from portage.tests import TestCase +from portage.tests.resolver.ResolverPlayground import ResolverPlayground class SpawnTestCase(TestCase): def testLogfile(self): - from portage import settings, spawn - from tempfile import mkstemp + playground = ResolverPlayground() + settings = playground.settings logfile = None try: fd, logfile = mkstemp() @@ -42,6 +45,7 @@ class SpawnTestCase(TestCase): # may occur. self.assertEqual(test_string, log_content) finally: + playground.cleanup() if logfile: try: os.unlink(logfile) -- cgit v1.2.3-65-gdbad From 2b1eceaf79ac667eecdf1ef9585592a235c8d93f Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 17:45:45 -0700 Subject: Bug #331413 - Clarify the behavior of the =$CATEGORY/$PN-$VERSION* atom operator some more. Thanks to Dennis Schridde . --- man/ebuild.5 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/man/ebuild.5 b/man/ebuild.5 index e3936653..94f49d70 100644 --- a/man/ebuild.5 +++ b/man/ebuild.5 @@ -286,9 +286,12 @@ and explicitly disallow them from being temporarily installed simultaneously during a series of upgrades. This syntax is supported beginning with \fBEAPI 2\fR. .br -\fI*\fR means match any version of the package so long as the specified base -is matched. So with a version of '2*', we can match '2.1', '2.2', '2.2.1', -etc... and not match version '1.0', '3.0', '4.1', etc... The version part +\fI*\fR means match any version of the package so long +as the specified string prefix is matched. So with a +version of '2*', we can match '2.1', '2.2', '2.2.1', +etc... and not match version '1.0', '3.0', '4.1', etc... +Beware that, due to the string matching nature, '20' +will also be matched by '2*'. The version part that comes before the '*' must be a valid version in the absence of the '*'. For example, '2' is a valid version and '2.' is not. Therefore, '2*' is allowed and '2.*' is not. -- cgit v1.2.3-65-gdbad From aa30da809919b9d9c6b75ef6e9d65076d19f47c9 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 18:00:30 -0700 Subject: Fix _getMissingLicenses() package.license handling so that settings from more specific atoms override those of less specific atoms. --- pym/portage/package/ebuild/config.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py index ff9894d8..34481054 100644 --- a/pym/portage/package/ebuild/config.py +++ b/pym/portage/package/ebuild/config.py @@ -1874,10 +1874,22 @@ class config(object): cp = cpv_getkey(cpv) cpdict = self._plicensedict.get(cp) if cpdict: - accept_license = list(self._accept_license) cpv_slot = "%s:%s" % (cpv, metadata["SLOT"]) - for atom in match_to_list(cpv_slot, list(cpdict)): - accept_license.extend(cpdict[atom]) + keys = list(cpdict) + plicence_list = [] + while keys: + bestmatch = best_match_to_list(cpv_slot, keys) + if bestmatch: + keys.remove(bestmatch) + plicence_list.append(cpdict[bestmatch]) + else: + break + if plicence_list: + # reverse, so the most specific atoms come last + plicence_list.reverse() + accept_license = list(self._accept_license) + for x in plicence_list: + accept_license.extend(x) licenses = set(flatten(use_reduce(paren_reduce( metadata["LICENSE"]), matchall=1))) -- cgit v1.2.3-65-gdbad From 03e6dd20e5c1a30540e1a17eabcd038a89254730 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 18:06:59 -0700 Subject: Fix _getMissingProperties() package.properties handling so that settings from more specific atoms override those of less specific atoms. --- pym/portage/package/ebuild/config.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py index 34481054..0540151a 100644 --- a/pym/portage/package/ebuild/config.py +++ b/pym/portage/package/ebuild/config.py @@ -1966,10 +1966,22 @@ class config(object): cp = cpv_getkey(cpv) cpdict = self._ppropertiesdict.get(cp) if cpdict: - accept_properties = list(self._accept_properties) cpv_slot = "%s:%s" % (cpv, metadata["SLOT"]) - for atom in match_to_list(cpv_slot, list(cpdict)): - accept_properties.extend(cpdict[atom]) + keys = list(cpdict) + pproperties_list = [] + while keys: + bestmatch = best_match_to_list(cpv_slot, keys) + if bestmatch: + keys.remove(bestmatch) + pproperties_list.append(cpdict[bestmatch]) + else: + break + if pproperties_list: + # reverse, so the most specific atoms come last + pproperties_list.reverse() + accept_properties = list(self._accept_properties) + for x in pproperties_list: + accept_properties.extend(x) properties = set(flatten(use_reduce(paren_reduce( metadata["PROPERTIES"]), matchall=1))) -- cgit v1.2.3-65-gdbad From eabb5944a6e4f540b3c1a74485e15cd42789ff94 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 6 Aug 2010 22:04:25 -0700 Subject: Ensure that _select_pkg_highest_available_imp() falls back to an installed package if the autounmask path fails to select something. --- pym/_emerge/depgraph.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index c0567956..e0e215cf 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -2530,6 +2530,8 @@ class depgraph(object): def _select_pkg_highest_available_imp(self, root, atom, onlydeps=False): pkg, existing = self._wrapped_select_pkg_highest_available_imp(root, atom, onlydeps=onlydeps) + default_selection = (pkg, existing) + if self._dynamic_config._autounmask is True: if pkg is not None and \ pkg.installed and \ @@ -2556,6 +2558,11 @@ class depgraph(object): if self._dynamic_config._need_restart: return None, None + if pkg is None: + # This ensures that we can fall back to an installed package + # that may have been rejected in the autounmask path above. + return default_selection + return pkg, existing def _pkg_visibility_check(self, pkg, allow_unstable_keywords=False): -- cgit v1.2.3-65-gdbad From 6dcb8b35d6bda91556ab5bb05d0666f71d207fc9 Mon Sep 17 00:00:00 2001 From: Arfrever Frehtes Taifersar Arahesis Date: Sat, 7 Aug 2010 19:38:59 +0200 Subject: Bug #308835: Support options in shebang and terminate 's' option of sed. --- bin/ebuild.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/ebuild.sh b/bin/ebuild.sh index a68b9731..2affc923 100755 --- a/bin/ebuild.sh +++ b/bin/ebuild.sh @@ -480,8 +480,8 @@ econf() { : ${ECONF_SOURCE:=.} if [ -x "${ECONF_SOURCE}/configure" ]; then if [[ -n $CONFIG_SHELL && \ - "$(head -n1 "$ECONF_SOURCE/configure")" =~ ^'#!'[[:space:]]*/bin/sh[[:space:]]*$ ]] ; then - sed -e "1s:.*:#!$CONFIG_SHELL" -i "$ECONF_SOURCE/configure" || \ + "$(head -n1 "$ECONF_SOURCE/configure")" =~ ^'#!'[[:space:]]*/bin/sh([[:space:]]|$) ]] ; then + sed -e "1s:^#![[:space:]]*/bin/sh:#!$CONFIG_SHELL:" -i "$ECONF_SOURCE/configure" || \ die "Substition of shebang in '$ECONF_SOURCE/configure' failed" fi if [ -e /usr/share/gnuconfig/ ]; then -- cgit v1.2.3-65-gdbad From 23225a062ac87a53c1a206a0c1978698458f7ce4 Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Sat, 7 Aug 2010 10:07:13 +0200 Subject: autounmask: Restart less often for use changes --- pym/_emerge/depgraph.py | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index e0e215cf..1d78a0cb 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -2627,19 +2627,37 @@ class depgraph(object): new_changes[flag] = False new_use.update(old_use.difference(target_use.keys())) - def want_restart_for_use_change(pkg): + def want_restart_for_use_change(pkg, new_use): if pkg not in self._dynamic_config.digraph.nodes: return False - #TODO: We can be more clever here. No need to restart if - # 1) we don't have a parent that can't work with our - # new use config - # and - # 2) none of pkg's *DEPEND vars changed - return True + + for key in "DEPEND", "RDEPEND", "PDEPEND", "LICENSE": + dep = pkg.metadata[key] + old_val = set(portage.dep.paren_normalize( \ + portage.dep.use_reduce(portage.dep.paren_reduce(dep), pkg.use.enabled))) + new_val = set(portage.dep.paren_normalize( \ + portage.dep.use_reduce(portage.dep.paren_reduce(dep), new_use))) + + if old_val != new_val: + return True + + parent_atoms = self._dynamic_config._parent_atoms.get(pkg) + if not parent_atoms: + return False + + new_use, changes = self._dynamic_config._needed_use_config_changes.get(pkg) + for ppkg, atom in parent_atoms: + if not atom.use or \ + not atom.use.required.intersection(changes.keys()): + continue + else: + return True + + return False if new_changes != old_changes: self._dynamic_config._needed_use_config_changes[pkg] = (new_use, new_changes) - if want_restart_for_use_change(pkg): + if want_restart_for_use_change(pkg, new_use): self._dynamic_config._need_restart = True return new_use -- cgit v1.2.3-65-gdbad From 44ade54c617432cc8836e5bf862705c347410cc9 Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Sat, 7 Aug 2010 10:07:38 +0200 Subject: Tests: resolver/test_autounmask: Remove test that don't test what they were supposed to --- pym/portage/tests/resolver/ResolverPlayground.py | 1 + pym/portage/tests/resolver/test_autounmask.py | 22 ---------------------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/pym/portage/tests/resolver/ResolverPlayground.py b/pym/portage/tests/resolver/ResolverPlayground.py index 3740f831..5d6d9d12 100644 --- a/pym/portage/tests/resolver/ResolverPlayground.py +++ b/pym/portage/tests/resolver/ResolverPlayground.py @@ -203,6 +203,7 @@ class ResolverPlaygroundResult(object): self.depgraph = mydepgraph self.favorites = favorites self.mergelist = None + self.use_changes = self.depgraph._dynamic_config._needed_use_config_changes if self.depgraph._dynamic_config._serialized_tasks_cache is not None: self.mergelist = [] diff --git a/pym/portage/tests/resolver/test_autounmask.py b/pym/portage/tests/resolver/test_autounmask.py index 944e5485..19d91484 100644 --- a/pym/portage/tests/resolver/test_autounmask.py +++ b/pym/portage/tests/resolver/test_autounmask.py @@ -35,14 +35,6 @@ class AutounmaskTestCase(TestCase): "sci-libs/L-1": { "IUSE": "bar" }, "sci-libs/M-1": { "KEYWORDS": "~x86" }, "sci-libs/P-1": { }, - - #ebuilds to conflicting use changes - "net-www/G-1": { "DEPEND": "net-www/I[foo]", "EAPI": 2 }, - "net-www/H-1": { "DEPEND": "net-www/I[-foo]", "EAPI": 2 }, - "net-www/I-1": { "IUSE": "foo" }, - "net-www/J-1": { "DEPEND": "|| ( net-www/G net-www/H )" }, - "net-www/J-2": { "DEPEND": "|| ( net-www/H net-www/G )" }, - "net-www/K-1": { "DEPEND": "!!net-www/G", "EAPI": 2 }, } requests = ( @@ -104,20 +96,6 @@ class AutounmaskTestCase(TestCase): ["sci-libs/L-1", "sci-libs/K-7"]), (["=sci-libs/K-8"], {"--autounmask": True}, None, False, \ ["sci-libs/L-1", "sci-libs/K-8"]), - - #Testing conflict bahviour - (["=net-www/G-1", "=net-www/H-1"], {"--autounmask": True}, None, False, None), - #Some of the following tests don't work because we are not able to take back - #changes that later on turn out to be not necessary, because the package - #that induced the change gets masked. - #~ (["=net-www/J-1", "=net-www/K-1"], {"--autounmask": True}, None, True, \ - #~ ["net-www/I-1", "net-www/H-1", "net-www/J-1", "net-www/K-1"] ), - (["=net-www/J-2", "=net-www/K-1"], {"--autounmask": True}, None, True, \ - ["net-www/I-1", "net-www/K-1", "net-www/H-1", "net-www/J-2", ] ), - #~ (["=net-www/K-1", "=net-www/J-1"], {"--autounmask": True}, None, True, \ - #~ ["net-www/I-1", "net-www/H-1", "net-www/J-1", "net-www/K-1"] ), - (["=net-www/K-1", "=net-www/J-2"], {"--autounmask": True}, None, True, \ - ["net-www/I-1", "net-www/K-1", "net-www/H-1", "net-www/J-2", ] ), ) playground = ResolverPlayground(ebuilds=ebuilds) -- cgit v1.2.3-65-gdbad From 7a0227049551d919d3bb766a57834d7dc3ccc9c8 Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Sat, 7 Aug 2010 10:22:46 +0200 Subject: Test: resolver/test_autounmask: Check the suggested use changes --- pym/portage/tests/resolver/ResolverPlayground.py | 9 ++++- pym/portage/tests/resolver/test_autounmask.py | 47 ++++++++++++------------ 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/pym/portage/tests/resolver/ResolverPlayground.py b/pym/portage/tests/resolver/ResolverPlayground.py index 5d6d9d12..6f658d8c 100644 --- a/pym/portage/tests/resolver/ResolverPlayground.py +++ b/pym/portage/tests/resolver/ResolverPlayground.py @@ -203,7 +203,7 @@ class ResolverPlaygroundResult(object): self.depgraph = mydepgraph self.favorites = favorites self.mergelist = None - self.use_changes = self.depgraph._dynamic_config._needed_use_config_changes + self.use_changes = None if self.depgraph._dynamic_config._serialized_tasks_cache is not None: self.mergelist = [] @@ -212,3 +212,10 @@ class ResolverPlaygroundResult(object): self.mergelist.append(x.atom) else: self.mergelist.append(x.cpv) + + if self.depgraph._dynamic_config._needed_use_config_changes: + self.use_changes = {} + for pkg, needed_use_config_changes in \ + self.depgraph._dynamic_config._needed_use_config_changes.items(): + new_use, changes = needed_use_config_changes + self.use_changes[pkg.cpv] = changes diff --git a/pym/portage/tests/resolver/test_autounmask.py b/pym/portage/tests/resolver/test_autounmask.py index 19d91484..74cd98be 100644 --- a/pym/portage/tests/resolver/test_autounmask.py +++ b/pym/portage/tests/resolver/test_autounmask.py @@ -41,69 +41,68 @@ class AutounmaskTestCase(TestCase): #Test USE changes. #The simple case. - (["dev-libs/A:1"], {"--autounmask": "n"}, None, False, None), + (["dev-libs/A:1"], {"--autounmask": "n"}, None, False, None, None), (["dev-libs/A:1"], {"--autounmask": True}, None, False, \ - ["dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1"]), + ["dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1"], { "dev-libs/B-1": {"foo": True} }), #Make sure we restart if needed. - (["dev-libs/B", "dev-libs/A:1"], {"--autounmask": True}, None, False, \ - ["dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1"]), + ["dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1"], { "dev-libs/B-1": {"foo": True} }), (["dev-libs/A:1", "dev-libs/B"], {"--autounmask": True}, None, False, \ - ["dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1"]), + ["dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1"], { "dev-libs/B-1": {"foo": True} }), (["dev-libs/A:1", "dev-libs/A:2"], {"--autounmask": True}, None, False, \ - ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"]), + ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"], { "dev-libs/B-1": {"foo": True, "bar": True} }), (["dev-libs/B", "dev-libs/A:1", "dev-libs/A:2"], {"--autounmask": True}, None, False, \ - ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"]), + ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"], { "dev-libs/B-1": {"foo": True, "bar": True} }), (["dev-libs/A:1", "dev-libs/B", "dev-libs/A:2"], {"--autounmask": True}, None, False, \ - ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"]), + ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"], { "dev-libs/B-1": {"foo": True, "bar": True} }), (["dev-libs/A:1", "dev-libs/A:2", "dev-libs/B"], {"--autounmask": True}, None, False, \ - ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"]), + ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"], { "dev-libs/B-1": {"foo": True, "bar": True} }), #Test keywording. #The simple case. - (["app-misc/Z"], {"--autounmask": "n"}, None, False, None), + (["app-misc/Z"], {"--autounmask": "n"}, None, False, None, None), (["app-misc/Z"], {"--autounmask": True}, None, False, \ - ["app-misc/Y-1", "app-misc/Z-1"]), + ["app-misc/Y-1", "app-misc/Z-1"], None), #Make sure that the backtracking for slot conflicts handles our mess. (["=app-misc/V-1", "app-misc/W"], {"--autounmask": True}, None, False, \ - ["app-misc/W-2", "app-misc/V-1"]), + ["app-misc/W-2", "app-misc/V-1"], None), (["app-misc/W", "=app-misc/V-1"], {"--autounmask": True}, None, False, \ - ["app-misc/W-2", "app-misc/V-1"]), + ["app-misc/W-2", "app-misc/V-1"], None), #Mixed testing #Make sure we don't change use for something in a || dep if there is another choice #that needs no change. (["=sci-libs/K-1"], {"--autounmask": True}, None, True, \ - ["sci-libs/P-1", "sci-libs/K-1"]), + ["sci-libs/P-1", "sci-libs/K-1"], None), (["=sci-libs/K-2"], {"--autounmask": True}, None, True, \ - ["sci-libs/P-1", "sci-libs/K-2"]), + ["sci-libs/P-1", "sci-libs/K-2"], None), (["=sci-libs/K-3"], {"--autounmask": True}, None, True, \ - ["sci-libs/P-1", "sci-libs/K-3"]), + ["sci-libs/P-1", "sci-libs/K-3"], None), (["=sci-libs/K-4"], {"--autounmask": True}, None, True, \ - ["sci-libs/P-1", "sci-libs/K-4"]), + ["sci-libs/P-1", "sci-libs/K-4"], None), (["=sci-libs/K-5"], {"--autounmask": True}, None, True, \ - ["sci-libs/P-1", "sci-libs/K-5"]), + ["sci-libs/P-1", "sci-libs/K-5"], None), (["=sci-libs/K-6"], {"--autounmask": True}, None, True, \ - ["sci-libs/P-1", "sci-libs/K-6"]), + ["sci-libs/P-1", "sci-libs/K-6"], None), #Make sure we prefer use changes over keyword changes. (["=sci-libs/K-7"], {"--autounmask": True}, None, False, \ - ["sci-libs/L-1", "sci-libs/K-7"]), + ["sci-libs/L-1", "sci-libs/K-7"], { "sci-libs/L-1": { "bar": True } }), (["=sci-libs/K-8"], {"--autounmask": True}, None, False, \ - ["sci-libs/L-1", "sci-libs/K-8"]), + ["sci-libs/L-1", "sci-libs/K-8"], { "sci-libs/L-1": { "bar": True } }), ) playground = ResolverPlayground(ebuilds=ebuilds) try: for atoms, options, action, \ - expected_result, expected_mergelist in requests: + expected_result, expected_mergelist, expected_use_changes in requests: result = playground.run(atoms, options, action) - self.assertEqual((result.success, result.mergelist), - (expected_result, expected_mergelist)) + self.assertEqual((result.success, result.mergelist, result.use_changes), + (expected_result, expected_mergelist, expected_use_changes)) finally: playground.cleanup() -- cgit v1.2.3-65-gdbad From 026f630247c7ec77f4bb46e01091b9f0c2b74201 Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Sat, 7 Aug 2010 10:51:51 +0200 Subject: autounmask: rename _needed_user_config_changes in _needed_unstable_keywords. Let the tests check for it. --- pym/_emerge/depgraph.py | 43 +++++++----------- pym/portage/tests/resolver/ResolverPlayground.py | 10 ++++- pym/portage/tests/resolver/test_autounmask.py | 56 ++++++++++++++---------- 3 files changed, 57 insertions(+), 52 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 1d78a0cb..91b3a154 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -111,7 +111,7 @@ class _frozen_depgraph_config(object): class _dynamic_depgraph_config(object): def __init__(self, depgraph, myparams, allow_backtracking, - runtime_pkg_mask, needed_user_config_changes, needed_use_config_changes): + runtime_pkg_mask, needed_unstable_keywords, needed_use_config_changes): self.myparams = myparams.copy() self._vdb_loaded = False self._allow_backtracking = allow_backtracking @@ -188,12 +188,10 @@ class _dynamic_depgraph_config(object): runtime_pkg_mask = dict((k, v.copy()) for (k, v) in \ runtime_pkg_mask.items()) - if needed_user_config_changes is None: - self._needed_user_config_changes = {} + if needed_unstable_keywords is None: + self._needed_unstable_keywords = set() else: - self._needed_user_config_changes = \ - dict((k.copy(), v.copy()) for (k, v) in \ - needed_user_config_changes.items()) + self._needed_unstable_keywords = needed_unstable_keywords.copy() if needed_use_config_changes is None: self._needed_use_config_changes = {} @@ -274,14 +272,14 @@ class depgraph(object): _dep_keys = ["DEPEND", "RDEPEND", "PDEPEND"] def __init__(self, settings, trees, myopts, myparams, spinner, - frozen_config=None, runtime_pkg_mask=None, needed_user_config_changes=None, \ + frozen_config=None, runtime_pkg_mask=None, needed_unstable_keywords=None, \ needed_use_config_changes=None, allow_backtracking=False): if frozen_config is None: frozen_config = _frozen_depgraph_config(settings, trees, myopts, spinner) self._frozen_config = frozen_config self._dynamic_config = _dynamic_depgraph_config(self, myparams, - allow_backtracking, runtime_pkg_mask, needed_user_config_changes, needed_use_config_changes) + allow_backtracking, runtime_pkg_mask, needed_unstable_keywords, needed_use_config_changes) self._select_atoms = self._select_atoms_highest_available self._select_package = self._select_pkg_highest_available @@ -1995,7 +1993,7 @@ class depgraph(object): return False, myfavorites if set(self._dynamic_config.digraph.nodes.keys()).intersection( \ - set(self._dynamic_config._needed_user_config_changes.keys())) or \ + set(self._dynamic_config._needed_unstable_keywords)) or \ set(self._dynamic_config.digraph.nodes.keys()).intersection( \ set(self._dynamic_config._needed_use_config_changes.keys())) : #We failed if the user needs to change the configuration @@ -2553,7 +2551,7 @@ class depgraph(object): pkg = None if pkg is not None and not pkg.visible: - self._dynamic_config._needed_user_config_changes.setdefault(pkg, set()).add("unstable keyword") + self._dynamic_config._needed_unstable_keywords.add(pkg) if self._dynamic_config._need_restart: return None, None @@ -2566,13 +2564,10 @@ class depgraph(object): return pkg, existing def _pkg_visibility_check(self, pkg, allow_unstable_keywords=False): - if pkg.visible: return True - pending_keyword_change = self._dynamic_config._needed_user_config_changes.get(pkg) - if pending_keyword_change is not None and \ - "unstable keyword" in pending_keyword_change: + if pkg in self._dynamic_config._needed_unstable_keywords: return True if not allow_unstable_keywords: @@ -5289,16 +5284,12 @@ class depgraph(object): return msg unstable_keyword_msg = [] - for pkg, changes in self._dynamic_config._needed_user_config_changes.items(): + for pkg in self._dynamic_config._needed_unstable_keywords: self._show_merge_list() if pkg in self._dynamic_config.digraph.nodes.keys(): - for change in changes: - if change == "unstable keyword": - pkgsettings = self._frozen_config.pkgsettings[pkg.root] - unstable_keyword_msg.append(get_dep_chain(pkg)) - unstable_keyword_msg.append("=%s ~%s\n" % (pkg.cpv, pkgsettings["ACCEPT_KEYWORDS"])) - else: - raise NotImplementedError() + pkgsettings = self._frozen_config.pkgsettings[pkg.root] + unstable_keyword_msg.append(get_dep_chain(pkg)) + unstable_keyword_msg.append("=%s ~%s\n" % (pkg.cpv, pkgsettings["ACCEPT_KEYWORDS"])) use_changes_msg = [] for pkg, needed_use_config_change in self._dynamic_config._needed_use_config_changes.items(): @@ -5716,8 +5707,8 @@ class depgraph(object): def get_backtrack_parameters(self): return { - "needed_user_config_changes": - self._dynamic_config._needed_user_config_changes.copy(), \ + "needed_unstable_keywords": + self._dynamic_config._needed_unstable_keywords.copy(), \ "runtime_pkg_mask": self._dynamic_config._runtime_pkg_mask.copy(), "needed_use_config_changes": @@ -5955,7 +5946,7 @@ def _backtrack_depgraph(settings, trees, myopts, myparams, backtrack_max = myopts.get('--backtrack', 5) backtrack_parameters = {} - needed_user_config_changes = None + needed_unstable_keywords = None allow_backtracking = backtrack_max > 0 backtracked = 0 frozen_config = _frozen_depgraph_config(settings, trees, @@ -5978,7 +5969,7 @@ def _backtrack_depgraph(settings, trees, myopts, myparams, # Backtracking failed, so disable it and do # a plain dep calculation + error message. allow_backtracking = False - #Don't reset needed_user_config_changes here, since we don't want to + #Don't reset needed_unstable_keywords here, since we don't want to #send the user through a "one step at a time" unmasking session for #no good reason. backtrack_parameters.pop('runtime_pkg_mask', None) diff --git a/pym/portage/tests/resolver/ResolverPlayground.py b/pym/portage/tests/resolver/ResolverPlayground.py index 6f658d8c..b4ff4180 100644 --- a/pym/portage/tests/resolver/ResolverPlayground.py +++ b/pym/portage/tests/resolver/ResolverPlayground.py @@ -203,7 +203,8 @@ class ResolverPlaygroundResult(object): self.depgraph = mydepgraph self.favorites = favorites self.mergelist = None - self.use_changes = None + self.use_changes = None + self.unstable_keywords = None if self.depgraph._dynamic_config._serialized_tasks_cache is not None: self.mergelist = [] @@ -212,10 +213,15 @@ class ResolverPlaygroundResult(object): self.mergelist.append(x.atom) else: self.mergelist.append(x.cpv) - + if self.depgraph._dynamic_config._needed_use_config_changes: self.use_changes = {} for pkg, needed_use_config_changes in \ self.depgraph._dynamic_config._needed_use_config_changes.items(): new_use, changes = needed_use_config_changes self.use_changes[pkg.cpv] = changes + + if self.depgraph._dynamic_config._needed_unstable_keywords: + self.unstable_keywords = set() + for pkg in self.depgraph._dynamic_config._needed_unstable_keywords: + self.unstable_keywords.add(pkg.cpv) diff --git a/pym/portage/tests/resolver/test_autounmask.py b/pym/portage/tests/resolver/test_autounmask.py index 74cd98be..d528f907 100644 --- a/pym/portage/tests/resolver/test_autounmask.py +++ b/pym/portage/tests/resolver/test_autounmask.py @@ -41,68 +41,76 @@ class AutounmaskTestCase(TestCase): #Test USE changes. #The simple case. - (["dev-libs/A:1"], {"--autounmask": "n"}, None, False, None, None), + (["dev-libs/A:1"], {"--autounmask": "n"}, None, False, None, None, None), (["dev-libs/A:1"], {"--autounmask": True}, None, False, \ - ["dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1"], { "dev-libs/B-1": {"foo": True} }), + ["dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1"], { "dev-libs/B-1": {"foo": True} }, None), #Make sure we restart if needed. (["dev-libs/B", "dev-libs/A:1"], {"--autounmask": True}, None, False, \ - ["dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1"], { "dev-libs/B-1": {"foo": True} }), + ["dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1"], { "dev-libs/B-1": {"foo": True} }, None), (["dev-libs/A:1", "dev-libs/B"], {"--autounmask": True}, None, False, \ - ["dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1"], { "dev-libs/B-1": {"foo": True} }), + ["dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1"], { "dev-libs/B-1": {"foo": True} }, None), (["dev-libs/A:1", "dev-libs/A:2"], {"--autounmask": True}, None, False, \ - ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"], { "dev-libs/B-1": {"foo": True, "bar": True} }), + ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"], \ + { "dev-libs/B-1": {"foo": True, "bar": True} }, None), (["dev-libs/B", "dev-libs/A:1", "dev-libs/A:2"], {"--autounmask": True}, None, False, \ - ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"], { "dev-libs/B-1": {"foo": True, "bar": True} }), + ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"], \ + { "dev-libs/B-1": {"foo": True, "bar": True} }, None), (["dev-libs/A:1", "dev-libs/B", "dev-libs/A:2"], {"--autounmask": True}, None, False, \ - ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"], { "dev-libs/B-1": {"foo": True, "bar": True} }), + ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"], \ + { "dev-libs/B-1": {"foo": True, "bar": True} }, None), (["dev-libs/A:1", "dev-libs/A:2", "dev-libs/B"], {"--autounmask": True}, None, False, \ - ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"], { "dev-libs/B-1": {"foo": True, "bar": True} }), + ["dev-libs/D-1", "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"], \ + { "dev-libs/B-1": {"foo": True, "bar": True} }, None), #Test keywording. #The simple case. - (["app-misc/Z"], {"--autounmask": "n"}, None, False, None, None), + (["app-misc/Z"], {"--autounmask": "n"}, None, False, None, None, None), (["app-misc/Z"], {"--autounmask": True}, None, False, \ - ["app-misc/Y-1", "app-misc/Z-1"], None), + ["app-misc/Y-1", "app-misc/Z-1"], None, ["app-misc/Y-1", "app-misc/Z-1"]), #Make sure that the backtracking for slot conflicts handles our mess. (["=app-misc/V-1", "app-misc/W"], {"--autounmask": True}, None, False, \ - ["app-misc/W-2", "app-misc/V-1"], None), + ["app-misc/W-2", "app-misc/V-1"], None, ["app-misc/W-2", "app-misc/V-1"]), (["app-misc/W", "=app-misc/V-1"], {"--autounmask": True}, None, False, \ - ["app-misc/W-2", "app-misc/V-1"], None), + ["app-misc/W-2", "app-misc/V-1"], None, ["app-misc/W-2", "app-misc/V-1"]), #Mixed testing #Make sure we don't change use for something in a || dep if there is another choice #that needs no change. (["=sci-libs/K-1"], {"--autounmask": True}, None, True, \ - ["sci-libs/P-1", "sci-libs/K-1"], None), + ["sci-libs/P-1", "sci-libs/K-1"], None, None), (["=sci-libs/K-2"], {"--autounmask": True}, None, True, \ - ["sci-libs/P-1", "sci-libs/K-2"], None), + ["sci-libs/P-1", "sci-libs/K-2"], None, None), (["=sci-libs/K-3"], {"--autounmask": True}, None, True, \ - ["sci-libs/P-1", "sci-libs/K-3"], None), + ["sci-libs/P-1", "sci-libs/K-3"], None, None), (["=sci-libs/K-4"], {"--autounmask": True}, None, True, \ - ["sci-libs/P-1", "sci-libs/K-4"], None), + ["sci-libs/P-1", "sci-libs/K-4"], None, None), (["=sci-libs/K-5"], {"--autounmask": True}, None, True, \ - ["sci-libs/P-1", "sci-libs/K-5"], None), + ["sci-libs/P-1", "sci-libs/K-5"], None, None), (["=sci-libs/K-6"], {"--autounmask": True}, None, True, \ - ["sci-libs/P-1", "sci-libs/K-6"], None), + ["sci-libs/P-1", "sci-libs/K-6"], None, None), #Make sure we prefer use changes over keyword changes. (["=sci-libs/K-7"], {"--autounmask": True}, None, False, \ - ["sci-libs/L-1", "sci-libs/K-7"], { "sci-libs/L-1": { "bar": True } }), + ["sci-libs/L-1", "sci-libs/K-7"], { "sci-libs/L-1": { "bar": True } }, None), (["=sci-libs/K-8"], {"--autounmask": True}, None, False, \ - ["sci-libs/L-1", "sci-libs/K-8"], { "sci-libs/L-1": { "bar": True } }), + ["sci-libs/L-1", "sci-libs/K-8"], { "sci-libs/L-1": { "bar": True } }, None), ) playground = ResolverPlayground(ebuilds=ebuilds) try: - for atoms, options, action, \ - expected_result, expected_mergelist, expected_use_changes in requests: + for atoms, options, action, expected_result, expected_mergelist, \ + expected_use_changes, expected_unstable_keywords in requests: result = playground.run(atoms, options, action) - self.assertEqual((result.success, result.mergelist, result.use_changes), - (expected_result, expected_mergelist, expected_use_changes)) + if expected_unstable_keywords is not None: + expected_unstable_keywords = set(expected_unstable_keywords) + self.assertEqual( + (result.success, result.mergelist, result.use_changes, result.unstable_keywords), + (expected_result, expected_mergelist, expected_use_changes, expected_unstable_keywords) + ) finally: playground.cleanup() -- cgit v1.2.3-65-gdbad From fef6bc0af3f527ded24fd82465383f363865e4b6 Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Sun, 8 Aug 2010 22:52:50 +0200 Subject: Tests: Let ./runTests take files as argument to run only the test in these files --- pym/portage/tests/__init__.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pym/portage/tests/__init__.py b/pym/portage/tests/__init__.py index 393ecf78..bd41f1ee 100644 --- a/pym/portage/tests/__init__.py +++ b/pym/portage/tests/__init__.py @@ -26,6 +26,10 @@ def main(): basedir = os.path.dirname(os.path.realpath(__file__)) testDirs = [] + if len(sys.argv) > 1: + suite.addTests(getTestFromCommandLine(sys.argv[1:], basedir)) + return TextTestRunner(verbosity=2).run(suite) + # the os.walk help mentions relative paths as being quirky # I was tired of adding dirs to the list, so now we add __test__ # to each dir we want tested. @@ -52,6 +56,29 @@ def my_import(name): mod = getattr(mod, comp) return mod +def getTestFromCommandLine(args, base_path): + ret = [] + for arg in args: + realpath = os.path.realpath(arg) + path = os.path.dirname(realpath) + f = realpath[len(path)+1:] + + if not f.startswith("test") or not f.endswith(".py"): + raise Exception("Invalid argument: '%s'" % arg) + + mymodule = f[:-3] + + parent_path = path[len(base_path)+1:] + parent_module = ".".join(("portage", "tests", parent_path)) + parent_module = parent_module.replace('/', '.') + result = [] + + # Make the trailing / a . for module importing + modname = ".".join((parent_module, mymodule)) + mod = my_import(modname) + ret.append(unittest.TestLoader().loadTestsFromModule(mod)) + return ret + def getTests(path, base_path): """ -- cgit v1.2.3-65-gdbad From bd33d7cf8ceea75a9a7635a326735d309ffe4613 Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Sun, 8 Aug 2010 21:10:22 +0200 Subject: portage.dep.Atom.violated_conditionals(): Fix "not / is None" bug --- pym/portage/dep/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pym/portage/dep/__init__.py b/pym/portage/dep/__init__.py index 0ee52218..542c1768 100644 --- a/pym/portage/dep/__init__.py +++ b/pym/portage/dep/__init__.py @@ -519,9 +519,9 @@ class _use_dep(object): tokens.extend(x for x in self.enabled if x not in other_use) tokens.extend("-" + x for x in self.disabled if x in other_use) if conditional: - if not parent_use: + if parent_use is None: raise InvalidAtom("violated_conditionals needs 'parent_use'" + \ - " parameter for conditional flags: '%s'" % (token,)) + " parameter for conditional flags.") tokens.extend(x + "?" for x in conditional.enabled if x in parent_use and not x in other_use) tokens.extend("!" + x + "?" for x in conditional.disabled if x not in parent_use and x in other_use) tokens.extend(x + "=" for x in conditional.equal if x in parent_use and x not in other_use) -- cgit v1.2.3-65-gdbad From 2b37a784ac960d8a0ebe77cba37c1e8ca9f10c54 Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Sun, 8 Aug 2010 21:55:51 +0200 Subject: Tests: dep/testAtom: add test for Atom.violated_conditionals() --- pym/portage/tests/dep/testAtom.py | 50 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/pym/portage/tests/dep/testAtom.py b/pym/portage/tests/dep/testAtom.py index a4972905..35f8e5f2 100644 --- a/pym/portage/tests/dep/testAtom.py +++ b/pym/portage/tests/dep/testAtom.py @@ -87,3 +87,53 @@ class TestAtom(TestCase): for atom, allow_wildcard in tests_xfail: self.assertRaisesMsg(atom, portage.exception.InvalidAtom, Atom, atom) + + def test_violated_conditionals(self): + test_cases = ( + ("dev-libs/A", ["foo"], None, "dev-libs/A"), + ("dev-libs/A[foo]", [], None, "dev-libs/A[foo]"), + ("dev-libs/A[foo]", ["foo"], None, "dev-libs/A"), + ("dev-libs/A[foo]", [], [], "dev-libs/A[foo]"), + ("dev-libs/A[foo]", ["foo"], [], "dev-libs/A"), + + ("dev-libs/A:0[foo]", ["foo"], [], "dev-libs/A:0"), + + ("dev-libs/A[foo,-bar]", [], None, "dev-libs/A[foo]"), + ("dev-libs/A[-foo,bar]", [], None, "dev-libs/A[bar]"), + + ("dev-libs/A[a,b=,!c=,d?,!e?,-f]", [], [], "dev-libs/A[a,!c=]"), + + ("dev-libs/A[a,b=,!c=,d?,!e?,-f]", ["a"], [], "dev-libs/A[!c=]"), + ("dev-libs/A[a,b=,!c=,d?,!e?,-f]", ["b"], [], "dev-libs/A[a,b=,!c=]"), + ("dev-libs/A[a,b=,!c=,d?,!e?,-f]", ["c"], [], "dev-libs/A[a]"), + ("dev-libs/A[a,b=,!c=,d?,!e?,-f]", ["d"], [], "dev-libs/A[a,!c=]"), + ("dev-libs/A[a,b=,!c=,d?,!e?,-f]", ["e"], [], "dev-libs/A[a,!e?,!c=]"), + ("dev-libs/A[a,b=,!c=,d?,!e?,-f]", ["f"], [], "dev-libs/A[a,-f,!c=]"), + + ("dev-libs/A[a,b=,!c=,d?,!e?,-f]", ["a"], ["a"], "dev-libs/A[!c=]"), + ("dev-libs/A[a,b=,!c=,d?,!e?,-f]", ["b"], ["b"], "dev-libs/A[a,!c=]"), + ("dev-libs/A[a,b=,!c=,d?,!e?,-f]", ["c"], ["c"], "dev-libs/A[a,!c=]"), + ("dev-libs/A[a,b=,!c=,d?,!e?,-f]", ["d"], ["d"], "dev-libs/A[a,!c=]"), + ("dev-libs/A[a,b=,!c=,d?,!e?,-f]", ["e"], ["e"], "dev-libs/A[a,!c=]"), + ("dev-libs/A[a,b=,!c=,d?,!e?,-f]", ["f"], ["f"], "dev-libs/A[a,-f,!c=]"), + ) + + test_cases_xfail = ( + ("dev-libs/A[a,b=,!c=,d?,!e?,-f]", [], None), + ) + + for atom, other_use, parent_use, expected_violated_atom in test_cases: + a = Atom(atom) + violated_atom = a.violated_conditionals(other_use, parent_use) + if parent_use is None: + fail_msg = "Atom: %s, other_use: %s, parent_use: %s, got: %s, expected: %s" % \ + (atom, " ".join(other_use), "None", str(violated_atom), expected_violated_atom) + else: + fail_msg = "Atom: %s, other_use: %s, parent_use: %s, got: %s, expected: %s" % \ + (atom, " ".join(other_use), " ".join(parent_use), str(violated_atom), expected_violated_atom) + self.assertEqual(str(violated_atom), expected_violated_atom, fail_msg) + + for atom, other_use, parent_use in test_cases_xfail: + a = Atom(atom) + self.assertRaisesMsg(atom, portage.exception.InvalidAtom, \ + a.violated_conditionals, other_use, parent_use) -- cgit v1.2.3-65-gdbad From 14ffb5c29f748114dd529d0c20e3c5fe6d953b5f Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Sun, 8 Aug 2010 20:43:16 -0700 Subject: Detect if python xml support is enabled and bail out with an appropriate when necessary. --- bin/repoman | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/bin/repoman b/bin/repoman index 9f64701b..0115c288 100755 --- a/bin/repoman +++ b/bin/repoman @@ -21,8 +21,6 @@ import sys import tempfile import time import platform -import xml.etree.ElementTree -from xml.parsers.expat import ExpatError try: from urllib.request import urlopen as urllib_request_urlopen @@ -32,9 +30,6 @@ except ImportError: from itertools import chain from stat import S_ISDIR -if not hasattr(__builtins__, "set"): - from sets import Set as set - try: import portage except ImportError: @@ -42,6 +37,18 @@ except ImportError: sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")) import portage portage._disable_legacy_globals() + +try: + import xml.etree.ElementTree + from xml.parsers.expat import ExpatError +except ImportError: + msg = ["Please enable python's \"xml\" USE flag in order to use repoman."] + from portage.output import EOutput + out = EOutput() + for line in msg: + out.eerror(line) + sys.exit(1) + from portage import os from portage import subprocess_getstatusoutput from portage import _encodings -- cgit v1.2.3-65-gdbad From aeaceebd2f4a93056533d3bb340bc2c55cd5a691 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Mon, 9 Aug 2010 01:57:41 -0700 Subject: Call portage._disable_legacy_globals() in order to ensure that we don't instantiate portage.settings, so that tests should work the same regardless of global configuration file state/existence. --- pym/portage/tests/runTests | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pym/portage/tests/runTests b/pym/portage/tests/runTests index 8e8a3a44..a6f3b8f1 100755 --- a/pym/portage/tests/runTests +++ b/pym/portage/tests/runTests @@ -10,6 +10,12 @@ import os.path as osp # This line courtesy of Marienz and Pkgcore ;) sys.path.insert(0, osp.dirname(osp.dirname(osp.dirname(osp.abspath(__file__))))) +import portage + +# Ensure that we don't instantiate portage.settings, so that tests should +# work the same regardless of global configuration file state/existence. +portage._disable_legacy_globals() + import portage.tests as tests from portage.const import PORTAGE_BIN_PATH path = os.environ.get("PATH", "").split(":") @@ -19,6 +25,7 @@ if not path or not os.path.samefile(path[0], PORTAGE_BIN_PATH): os.environ["PATH"] = ":".join(path) del path + if __name__ == "__main__": result = tests.main() if not result.wasSuccessful(): -- cgit v1.2.3-65-gdbad From e413d6cdb0ca36919155a839927370350a8448c5 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Mon, 9 Aug 2010 03:05:00 -0700 Subject: Tweak the profile package.* atom specificity code to use list.append() and finally list.reverse(), instead of a bunch of list.insert() calls. This should be easier to read. --- pym/portage/package/ebuild/config.py | 51 +++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py index 0540151a..74a158f4 100644 --- a/pym/portage/package/ebuild/config.py +++ b/pym/portage/package/ebuild/config.py @@ -1476,22 +1476,24 @@ class config(object): has_changed = True defaults = [] - pos = 0 for i, pkgprofileuse_dict in enumerate(self.pkgprofileuse): + if self.make_defaults_use[i]: + defaults.append(self.make_defaults_use[i]) cpdict = pkgprofileuse_dict.get(cp) if cpdict: + pkg_defaults = [] keys = list(cpdict) while keys: bestmatch = best_match_to_list(cpv_slot, keys) if bestmatch: keys.remove(bestmatch) - defaults.insert(pos, cpdict[bestmatch]) + pkg_defaults.append(cpdict[bestmatch]) else: break - del keys - if self.make_defaults_use[i]: - defaults.insert(pos, self.make_defaults_use[i]) - pos = len(defaults) + if pkg_defaults: + # reverse, so the most specific atoms come last + pkg_defaults.reverse() + defaults.extend(pkg_defaults) defaults = " ".join(defaults) if defaults != self.configdict["defaults"].get("USE",""): self.configdict["defaults"]["USE"] = defaults @@ -1665,22 +1667,24 @@ class config(object): if cp is None: cp = cpv_getkey(remove_slot(pkg)) usemask = [] - pos = 0 for i, pusemask_dict in enumerate(self.pusemask_list): + if self.usemask_list[i]: + usemask.append(self.usemask_list[i]) cpdict = pusemask_dict.get(cp) if cpdict: + pkg_usemask = [] keys = list(cpdict) while keys: best_match = best_match_to_list(pkg, keys) if best_match: keys.remove(best_match) - usemask.insert(pos, cpdict[best_match]) + pkg_usemask.append(cpdict[best_match]) else: break - del keys - if self.usemask_list[i]: - usemask.insert(pos, self.usemask_list[i]) - pos = len(usemask) + if pkg_usemask: + # reverse, so the most specific atoms come last + pkg_usemask.reverse() + usemask.extend(pkg_usemask) return set(stack_lists(usemask, incremental=True)) def _getUseForce(self, pkg): @@ -1688,22 +1692,24 @@ class config(object): if cp is None: cp = cpv_getkey(remove_slot(pkg)) useforce = [] - pos = 0 for i, puseforce_dict in enumerate(self.puseforce_list): + if self.useforce_list[i]: + useforce.append(self.useforce_list[i]) cpdict = puseforce_dict.get(cp) if cpdict: + pkg_useforce = [] keys = list(cpdict) while keys: best_match = best_match_to_list(pkg, keys) if best_match: keys.remove(best_match) - useforce.insert(pos, cpdict[best_match]) + pkg_useforce.append(cpdict[best_match]) else: break - del keys - if self.useforce_list[i]: - useforce.insert(pos, self.useforce_list[i]) - pos = len(useforce) + if pkg_useforce: + # reverse, so the most specific atoms come last + pkg_useforce.reverse() + useforce.extend(pkg_useforce) return set(stack_lists(useforce, incremental=True)) def _getMaskAtom(self, cpv, metadata): @@ -1764,19 +1770,22 @@ class config(object): cp = cpv_getkey(cpv) pkg = "%s:%s" % (cpv, metadata["SLOT"]) keywords = [[x for x in metadata["KEYWORDS"].split() if x != "-*"]] - pos = len(keywords) for pkeywords_dict in self._pkeywords_list: cpdict = pkeywords_dict.get(cp) if cpdict: + pkg_keywords = [] keys = list(cpdict) while keys: best_match = best_match_to_list(pkg, keys) if best_match: keys.remove(best_match) - keywords.insert(pos, cpdict[best_match]) + pkg_keywords.append(cpdict[best_match]) else: break - pos = len(keywords) + if pkg_keywords: + # reverse, so the most specific atoms come last + pkg_keywords.reverse() + keywords.extend(pkg_keywords) return stack_lists(keywords, incremental=True) def _getMissingKeywords(self, cpv, metadata): -- cgit v1.2.3-65-gdbad From 0432135c5213c710ecbc6cdc9e50adfc0d8daa22 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Mon, 9 Aug 2010 03:35:45 -0700 Subject: Fix /etc/portage/package.keywords handling so that more specific atoms override less specific atoms. --- pym/portage/package/ebuild/config.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py index 74a158f4..23d7f219 100644 --- a/pym/portage/package/ebuild/config.py +++ b/pym/portage/package/ebuild/config.py @@ -1817,11 +1817,23 @@ class config(object): pkgdict = self.pkeywordsdict.get(cp) matches = False if pkgdict: - cpv_slot_list = ["%s:%s" % (cpv, metadata["SLOT"])] - for atom, pkgkeywords in pkgdict.items(): - if match_from_list(atom, cpv_slot_list): - matches = True - pgroups.extend(pkgkeywords) + cpv_slot = "%s:%s" % (cpv, metadata["SLOT"]) + pkg_accept_keywords = [] + keys = list(pkgdict) + while keys: + best_match = best_match_to_list(cpv_slot, keys) + if best_match: + keys.remove(best_match) + pkg_accept_keywords.append(pkgdict[best_match]) + else: + break + if pkg_accept_keywords: + # reverse, so the most specific atoms come last + pkg_accept_keywords.reverse() + for x in pkg_accept_keywords: + pgroups.extend(x) + matches = True + if matches or egroups: pgroups.extend(egroups) inc_pgroups = set() -- cgit v1.2.3-65-gdbad