[gentoo-portage-dev] [PATCH 00/10] First steps to get rid of backtracking

2014-01-29 Thread Sebastian Luther
Hi all,

as you may have noticed, emerge can in some cases take ages ( 5-10 minutes)
to resolve dependencies these days. This happens when lots of backtracking
is required to solve slot conflicts and/or to schedule slot operator rebuilds.
The problem is that the current backtracking implementation has to rebuild
the entire dependency graph from scratch each time it backtracks.

This series of patches is a first step into fixing this problem.

What these patches do:
Patch 1
---
Adds a new data structure called package_tracker. This data structure is
meant to replace several of the depgraph data structures.
It has some new features not present in the existing data structures.
1) It can properly deal with several packages in the same slot.
2) It supports removal of previously added packages.
3) It tracks package conflicts automatically.
4) It has a more general concept of conflicts.

Not all of the features are used for now, but they will make future
changes easier.

Patch 2-5
-
These patches replace the old data structures with the package tracker

Patch 6-8
-
These patches fix several issues with emerge output. 6 and 8 are 
somewhat
independent of the other patches in this series. Patch 7 introduces a 
new
function used in Patch 10.

Patch 9
---
New function for patch 10.

Patch 10

This patch builds on top of all the previous patches. It introduces two
new functions to the depgraph class. A function to remove packages that
have previously been pulled in and a function to solve simple slot
conflicts without backtracking.

This should resolve most no parents that aren't satisfied by other
packages in this slot slot conflicts.

You may find these patches on github here:
https://github.com/few/fews-portage-branch/tree/package_tracker

Some numbers


My system has quite a number of conflicts that give emerge a hard time
resolving dependencies.

-uDN world

Without the patches:
* 11 unsolved slot conflicts
* 2 unsolved blockers
* takes 5 backtracking steps and then fails

With the patches:
* no unsolved slot conflicts or blockers
* takes 7 backtracking steps and then succeeds

In this case it actually became slower, but was finally able to find a solution.

-e world

Without the patches:
* 5 unsolved slot conflicts
* 1 unsolved blocker
* takes 23 backtracking steps and then fails

With the patches:
* 1 unsolved slot conflict (From a quick look it looks like there really
is no solution.)
* takes 13 backtracking steps and then fails

In this case it's a lot faster, but still unacceptably slow.
The result is improved by solving 4 out of 5 conflicts.





[gentoo-portage-dev] [PATCH 03/10] Replace mydbapi with _package_tracker

2014-01-29 Thread Sebastian Luther
---
 pym/_emerge/depgraph.py | 211 +++-
 1 file changed, 101 insertions(+), 110 deletions(-)

diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
index fd59dda..9d234c2 100644
--- a/pym/_emerge/depgraph.py
+++ b/pym/_emerge/depgraph.py
@@ -344,7 +344,6 @@ class _dynamic_depgraph_config(object):
self._allow_backtracking = allow_backtracking
# Maps nodes to the reasons they were selected for 
reinstallation.
self._reinstall_nodes = {}
-   self.mydbapi = {}
# Contains a filtered view of preferred packages that are 
selected
# from available repositories.
self._filtered_trees = {}
@@ -440,7 +439,6 @@ class _dynamic_depgraph_config(object):
# have after new packages have been installed.
fakedb = PackageTrackerDbapiWrapper(myroot, 
self._package_tracker)
 
-   self.mydbapi[myroot] = fakedb
def graph_tree():
pass
graph_tree.dbapi = fakedb
@@ -558,8 +556,6 @@ class depgraph(object):
 
if preload_installed_pkgs:
vardb = fake_vartree.dbapi
-   fakedb = self._dynamic_config._graph_trees[
-   myroot][vartree].dbapi
 
if not dynamic_deps:
for pkg in vardb:
@@ -724,25 +720,23 @@ class depgraph(object):
 
for pkg in list(self._dynamic_config.ignored_binaries):
 
-   selected_pkg = self._dynamic_config.mydbapi[pkg.root
-   ].match_pkgs(pkg.slot_atom)
+   selected_pkg = list()
 
-   if not selected_pkg:
-   continue
+   for selected_pkg in 
self._dynamic_config._package_tracker.match(
+   pkg.root, pkg.slot_atom):
 
-   selected_pkg = selected_pkg[-1]
-   if selected_pkg  pkg:
-   self._dynamic_config.ignored_binaries.pop(pkg)
-   continue
+   if selected_pkg  pkg:
+   
self._dynamic_config.ignored_binaries.pop(pkg)
+   break
 
-   if selected_pkg.installed and \
-   selected_pkg.cpv == pkg.cpv and \
-   selected_pkg.build_time == pkg.build_time:
-   # We don't care about ignored binaries when an
-   # identical installed instance is selected to
-   # fill the slot.
-   self._dynamic_config.ignored_binaries.pop(pkg)
-   continue
+   if selected_pkg.installed and \
+   selected_pkg.cpv == pkg.cpv and \
+   selected_pkg.build_time == 
pkg.build_time:
+   # We don't care about ignored binaries 
when an
+   # identical installed instance is 
selected to
+   # fill the slot.
+   
self._dynamic_config.ignored_binaries.pop(pkg)
+   break
 
if not self._dynamic_config.ignored_binaries:
return
@@ -788,20 +782,25 @@ class depgraph(object):
# Exclude installed here since we only
# want to show available updates.
continue
-   chosen_pkg = self._dynamic_config.mydbapi[pkg.root
-   ].match_pkgs(pkg.slot_atom)
-   if not chosen_pkg or chosen_pkg[-1] = pkg:
-   continue
-   k = (pkg.root, pkg.slot_atom)
-   if k in missed_updates:
-   other_pkg, mask_type, parent_atoms = 
missed_updates[k]
-   if other_pkg  pkg:
-   continue
-   for mask_type, parent_atoms in mask_reasons.items():
-   if not parent_atoms:
-   continue
-   missed_updates[k] = (pkg, mask_type, 
parent_atoms)
-   break
+   missed_update = True
+   any_selected = False
+   for chosen_pkg in 
self._dynamic_config._package_tracker.match(
+   

[gentoo-portage-dev] [PATCH 10/10] Solve some slot conflicts without backtracking

2014-01-29 Thread Sebastian Luther
---
 pym/_emerge/depgraph.py| 345 -
 pym/portage/tests/resolver/test_backtracking.py|  13 +-
 pym/portage/tests/resolver/test_blocker.py |  48 +++
 pym/portage/tests/resolver/test_slot_collisions.py |  75 -
 4 files changed, 457 insertions(+), 24 deletions(-)
 create mode 100644 pym/portage/tests/resolver/test_blocker.py

diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
index 1bb086b..b5fecec 100644
--- a/pym/_emerge/depgraph.py
+++ b/pym/_emerge/depgraph.py
@@ -3,6 +3,7 @@
 
 from __future__ import print_function, unicode_literals
 
+import collections
 import errno
 import io
 import logging
@@ -78,7 +79,7 @@ from _emerge.resolver.backtracking import Backtracker, 
BacktrackParameter
 from _emerge.resolver.package_tracker import PackageTracker, 
PackageTrackerDbapiWrapper
 from _emerge.resolver.slot_collision import slot_conflict_handler
 from _emerge.resolver.circular_dependency import circular_dependency_handler
-from _emerge.resolver.output import Display
+from _emerge.resolver.output import Display, format_unmatched_atom
 
 if sys.hexversion = 0x300:
basestring = str
@@ -423,6 +424,8 @@ class _dynamic_depgraph_config(object):
self._complete_mode = False
self._slot_operator_deps = {}
self._package_tracker = PackageTracker()
+   # Track missed updates caused by solved conflicts.
+   self._conflict_missed_update = collections.defaultdict(dict)
 
for myroot in depgraph._frozen_config.trees:
self.sets[myroot] = _depgraph_sets()
@@ -769,7 +772,8 @@ class depgraph(object):
# missed update from each SLOT.
missed_updates = {}
for pkg, mask_reasons in \
-   self._dynamic_config._runtime_pkg_mask.items():
+   chain(self._dynamic_config._runtime_pkg_mask.items(),
+   
self._dynamic_config._conflict_missed_update.items()):
if pkg.installed:
# Exclude installed here since we only
# want to show available updates.
@@ -779,7 +783,8 @@ class depgraph(object):
for chosen_pkg in 
self._dynamic_config._package_tracker.match(
pkg.root, pkg.slot_atom):
any_selected = True
-   if chosen_pkg = pkg:
+   if chosen_pkg  pkg or (not 
chosen_pkg.installed and \
+   chosen_pkg.version == pkg.version):
missed_update = False
break
if any_selected and missed_update:
@@ -869,7 +874,7 @@ class depgraph(object):
 
self._show_merge_list()
msg = []
-   msg.append(\nWARNING: One or more updates have been  + \
+   msg.append(\nWARNING: One or more updates/rebuilds have been  
+ \
skipped due to a dependency conflict:\n\n)
 
indent =   
@@ -879,22 +884,29 @@ class depgraph(object):
msg.append( for %s % (pkg.root,))
msg.append(\n\n)
 
-   for parent, atom in parent_atoms:
-   msg.append(indent)
-   msg.append(str(pkg))
+   msg.append(indent)
+   msg.append(str(pkg))
+   msg.append( conflicts with\n)
 
-   msg.append( conflicts with\n)
-   msg.append(2*indent)
+   for parent, atom in parent_atoms:
if isinstance(parent,
(PackageArg, AtomArg)):
# For PackageArg and AtomArg types, it's
# redundant to display the atom 
attribute.
+   msg.append(2*indent)
msg.append(str(parent))
+   msg.append(\n)
else:
# Display the specific atom from SetArg 
or
# Package types.
-   msg.append(%s required by %s % (atom, 
parent))
-   msg.append(\n)
+   atom, marker = format_unmatched_atom(
+   pkg, atom, 
self._pkg_use_enabled)
+
+   msg.append(2*indent)
+   msg.append(%s required by %s\n % 
(atom, parent))
+   

[gentoo-portage-dev] [PATCH 04/10] Replace _slot_collision_info with _package_tracker

2014-01-29 Thread Sebastian Luther
---
 pym/_emerge/depgraph.py| 59 --
 pym/_emerge/resolver/slot_collision.py | 22 ++---
 2 files changed, 31 insertions(+), 50 deletions(-)

diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
index 9d234c2..484ac14 100644
--- a/pym/_emerge/depgraph.py
+++ b/pym/_emerge/depgraph.py
@@ -378,11 +378,6 @@ class _dynamic_depgraph_config(object):
# This use used to check if we have accounted for blockers
# relevant to a package.
self._traversed_pkg_deps = set()
-   # This should be ordered such that the backtracker will
-   # attempt to solve conflicts which occurred earlier first,
-   # since an earlier conflict can be the cause of a conflict
-   # which occurs later.
-   self._slot_collision_info = OrderedDict()
# Slot collision nodes are not allowed to block other packages 
since
# blocker validation is only able to account for one package 
per slot.
self._slot_collision_nodes = set()
@@ -915,7 +910,7 @@ class depgraph(object):
cases.

 
-   if not self._dynamic_config._slot_collision_info:
+   if not 
any(self._dynamic_config._package_tracker.slot_conflicts()):
return
 
self._show_merge_list()
@@ -971,16 +966,18 @@ class depgraph(object):
is called, so that all relevant reverse dependencies are
available for use in backtracking decisions.

-   for (slot_atom, root), slot_nodes in \
-   self._dynamic_config._slot_collision_info.items():
-   self._process_slot_conflict(root, slot_atom, slot_nodes)
+   for conflict in 
self._dynamic_config._package_tracker.slot_conflicts():
+   self._process_slot_conflict(conflict)
 
-   def _process_slot_conflict(self, root, slot_atom, slot_nodes):
+   def _process_slot_conflict(self, conflict):

Process slot conflict data to identify specific atoms which
lead to conflict. These atoms only match a subset of the
packages that have been pulled into a given slot.

+   root = conflict.root
+   slot_atom = conflict.atom
+   slot_nodes = conflict.pkgs
 
debug = --debug in self._frozen_config.myopts
 
@@ -1999,7 +1996,6 @@ class depgraph(object):
 
existing_node, existing_node_matches = \
self._check_slot_conflict(pkg, dep.atom)
-   slot_collision = False
if existing_node:
if existing_node_matches:
# The existing node can be reused.
@@ -2032,19 +2028,7 @@ class depgraph(object):

modified_use=self._pkg_use_enabled(existing_node))),
level=logging.DEBUG, 
noiselevel=-1)
 
-   slot_collision = True
-
-   if slot_collision:
-   # Now add this node to the graph so that 
self.display()
-   # can show use flags and --tree portage.output. 
 This node is
-   # only being partially added to the graph.  It 
must not be
-   # allowed to interfere with the other nodes 
that have been
-   # added.
-   # Even though the graph is now invalid, 
continue to process
-   # dependencies so that things like --fetchonly 
can still
-   # function despite collisions.
-   pass
-   elif not previously_added:
+   if not previously_added:

self._dynamic_config._package_tracker.add_pkg(pkg)

self._dynamic_config._filtered_trees[pkg.root][porttree].dbapi._clear_cache()
self._dynamic_config._highest_pkg_cache.clear()
@@ -2156,14 +2140,6 @@ class depgraph(object):
 
def _add_slot_conflict(self, pkg):
self._dynamic_config._slot_collision_nodes.add(pkg)
-   slot_key = (pkg.slot_atom, pkg.root)
-   slot_nodes = 
self._dynamic_config._slot_collision_info.get(slot_key)
-   if slot_nodes is None:
-   slot_nodes = set()
-   
slot_nodes.update(self._dynamic_config._package_tracker.match(
-   pkg.root, pkg.slot_atom, installed=False))
-   

[gentoo-portage-dev] [PATCH 08/10] Some small output fixes for the slot conflict handler

2014-01-29 Thread Sebastian Luther
* unmatched atom printing now uses ^^ markers
* unmatched atom printing properly supports sub-slots
* Fix spurious no parents message caused by AtomArg parents
---
 pym/_emerge/resolver/slot_collision.py | 119 ++---
 1 file changed, 96 insertions(+), 23 deletions(-)

diff --git a/pym/_emerge/resolver/slot_collision.py 
b/pym/_emerge/resolver/slot_collision.py
index ca3fb74..c5936ad 100644
--- a/pym/_emerge/resolver/slot_collision.py
+++ b/pym/_emerge/resolver/slot_collision.py
@@ -1,4 +1,4 @@
-# Copyright 2010-2013 Gentoo Foundation
+# Copyright 2010-2014 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 from __future__ import print_function, unicode_literals
@@ -273,12 +273,14 @@ class slot_conflict_handler(object):
for ppkg, atom in parent_atoms:
atom_set = 
InternalPackageSet(initial_atoms=(atom,))
atom_without_use_set = 
InternalPackageSet(initial_atoms=(atom.without_use,))
+   atom_without_use_and_slot_set = 
InternalPackageSet(initial_atoms=(
+   
atom.without_use.without_slot,))
 
for other_pkg in pkgs:
if other_pkg == pkg:
continue
 
-   if not 
atom_without_use_set.findAtomForPackage(other_pkg, \
+   if not 
atom_without_use_and_slot_set.findAtomForPackage(other_pkg, \

modified_use=_pkg_use_enabled(other_pkg)):
if 
atom.operator is not None:
# The 
version range does not match.
@@ -295,9 +297,11 @@ class slot_conflict_handler(object):

atoms.add((ppkg, atom, other_pkg))

num_all_specific_atoms += 1

collision_reasons[key] = atoms
-   else:
-   # The 
sub_slot does not match.
-   key = 
(sub-slot, atom.sub_slot)
+
+   elif not 
atom_without_use_set.findAtomForPackage(other_pkg, \
+   
modified_use=_pkg_use_enabled(other_pkg)):
+   # The 
slot and/or sub_slot does not match.
+   key = 
(slot, (atom.slot, atom.sub_slot, atom.slot_operator))
atoms = 
collision_reasons.get(key, set())

atoms.add((ppkg, atom, other_pkg))

num_all_specific_atoms += 1
@@ -342,6 +346,11 @@ class slot_conflict_handler(object):

atoms.add((ppkg, atom, other_pkg))

collision_reasons[(use, flag)] = atoms

num_all_specific_atoms += 1
+   elif isinstance(ppkg, 
AtomArg) and other_pkg.installed:
+   parent_atoms = 
collision_reasons.get((AtomArg, None), set())
+   
parent_atoms.add((ppkg, atom))
+   
collision_reasons[(AtomArg, None)] = parent_atoms
+   
num_all_specific_atoms += 1
 
msg.append( pulled in by\n)
 
@@ -372,9 +381,11 @@ class slot_conflict_handler(object):
if not verboseconflicts:

selected_for_display.update(

best_matches.values())
-   elif type == sub-slot:
+   

[gentoo-portage-dev] [PATCH 09/10] Add digraph.discard

2014-01-29 Thread Sebastian Luther
---
 pym/portage/util/digraph.py | 12 +++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/pym/portage/util/digraph.py b/pym/portage/util/digraph.py
index fc1fb86..4a9cb43 100644
--- a/pym/portage/util/digraph.py
+++ b/pym/portage/util/digraph.py
@@ -1,4 +1,4 @@
-# Copyright 2010-2013 Gentoo Foundation
+# Copyright 2010-2014 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 from __future__ import unicode_literals
@@ -47,6 +47,16 @@ class digraph(object):
priorities.append(priority)
priorities.sort()
 
+   def discard(self, node):
+   
+   Like remove(), except it doesn't raises KeyError if the
+   node doesn't exist.
+   
+   try:
+   self.remove(node)
+   except KeyError:
+   pass
+
def remove(self, node):
Removes the specified node from the digraph, also removing
and ties to other nodes in the digraph. Raises KeyError if the
-- 
1.8.3.2




[gentoo-portage-dev] [PATCH 07/10] format_unmatched_atom: Pretty printing for unmatched atoms

2014-01-29 Thread Sebastian Luther
This is a split out from the slot conflict handler to be
used in other places.
---
 pym/_emerge/resolver/output.py | 109 +++--
 1 file changed, 106 insertions(+), 3 deletions(-)

diff --git a/pym/_emerge/resolver/output.py b/pym/_emerge/resolver/output.py
index 3e8552f..5f550be 100644
--- a/pym/_emerge/resolver/output.py
+++ b/pym/_emerge/resolver/output.py
@@ -1,4 +1,4 @@
-# Copyright 2010-2013 Gentoo Foundation
+# Copyright 2010-2014 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 Resolver output display operation.
@@ -7,7 +7,7 @@
 from __future__ import unicode_literals
 
 __all__ = (
-   Display,
+   Display, format_unmatched_atom,
)
 
 import sys
@@ -23,8 +23,9 @@ from portage.package.ebuild._spawn_nofetch import 
spawn_nofetch
 from portage.output import ( blue, colorize, create_color_func,
darkblue, darkgreen, green, nc_len, teal)
 bad = create_color_func(BAD)
+from portage._sets.base import InternalPackageSet
 from portage.util import writemsg_stdout
-from portage.versions import best
+from portage.versions import best, cpv_getversion
 
 from _emerge.Blocker import Blocker
 from _emerge.create_world_atom import create_world_atom
@@ -916,3 +917,105 @@ class Display(object):
self.print_changelog()
 
return os.EX_OK
+
+
+def format_unmatched_atom(pkg, atom, pkg_use_enabled):
+   
+   Returns two strings. The first string contains the
+   'atom' with parts of the atom colored, which 'pkg'
+   doesn't match. The second string has the same number
+   of characters as the first one, but consists of only
+   white space or ^. The ^ characters have the same position
+   as the colored parts of the first string.
+   
+   # Things to check:
+   #   1. Version
+   #   2. cp
+   #   3. slot/sub_slot
+   #   4. repository
+   #   5. USE
+
+   highlight = set()
+
+   def perform_coloring():
+   atom_str = 
+   marker_str = 
+   for ii, x in enumerate(atom):
+   if ii in highlight:
+   atom_str += colorize(BAD, x)
+   marker_str += ^
+   else:
+   atom_str += x
+   marker_str +=  
+   return atom_str, marker_str
+
+   if atom.cp != pkg.cp:
+   # Highlight the cp part only.
+   ii = atom.find(atom.cp)
+   highlight.update(range(ii, ii + len(atom.cp)))
+   return perform_coloring()
+
+   version_atom = atom.without_repo.without_slot.without_use
+   version_atom_set = InternalPackageSet(initial_atoms=(version_atom,))
+   highlight_version = not bool(version_atom_set.findAtomForPackage(pkg,
+   modified_use=pkg_use_enabled(pkg)))
+
+   highlight_slot = False
+   if (atom.slot and atom.slot != pkg.slot) or \
+   (atom.sub_slot and atom.sub_slot != pkg.sub_slot):
+   highlight_slot = True
+
+   if highlight_version:
+   op = atom.operator
+   ver = None
+   if atom.cp != atom.cpv:
+   ver = cpv_getversion(atom.cpv)
+
+   if op == =*:
+   op = =
+   ver += *
+
+   if op is not None:
+   highlight.update(range(len(op)))
+
+   if ver is not None:
+   start = atom.rfind(ver)
+   end = start + len(ver)
+   highlight.update(range(start, end))
+
+   if highlight_slot:
+   slot_str = : + atom.slot
+   if atom.sub_slot:
+   slot_str += / + atom.sub_slot
+   if atom.slot_operator:
+   slot_str += atom.slot_operator
+   start = atom.find(slot_str)
+   end = start + len(slot_str)
+   highlight.update(range(start, end))
+
+   highlight_use = set()
+   if atom.use:
+   use_atom = %s[%s] % (atom.cp, str(atom.use))
+   use_atom_set = InternalPackageSet(initial_atoms=(use_atom,))
+   if not use_atom_set.findAtomForPackage(pkg, \
+   modified_use=pkg_use_enabled(pkg)):
+   missing_iuse = pkg.iuse.get_missing_iuse(
+   atom.unevaluated_atom.use.required)
+   if missing_iuse:
+   highlight_use = set(missing_iuse)
+   else:
+   #Use conditionals not met.
+   violated_atom = atom.violated_conditionals(
+   pkg_use_enabled(pkg), 
pkg.iuse.is_valid_flag)
+   if violated_atom.use is not None:
+

[gentoo-portage-dev] [PATCH 05/10] Replace _slot_collision_nodes with _package_tracker

2014-01-29 Thread Sebastian Luther
---
 pym/_emerge/depgraph.py | 16 +++-
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
index 484ac14..1bb086b 100644
--- a/pym/_emerge/depgraph.py
+++ b/pym/_emerge/depgraph.py
@@ -378,9 +378,6 @@ class _dynamic_depgraph_config(object):
# This use used to check if we have accounted for blockers
# relevant to a package.
self._traversed_pkg_deps = set()
-   # Slot collision nodes are not allowed to block other packages 
since
-   # blocker validation is only able to account for one package 
per slot.
-   self._slot_collision_nodes = set()
self._parent_atoms = {}
self._slot_conflict_handler = None
self._circular_dependency_handler = None
@@ -1799,11 +1796,16 @@ class depgraph(object):
buildpkgonly = --buildpkgonly in self._frozen_config.myopts
nodeps = --nodeps in self._frozen_config.myopts
if dep.blocker:
+
+   # Slot collision nodes are not allowed to block other 
packages since
+   # blocker validation is only able to account for one 
package per slot.
+   is_slot_conflict_parent = any(dep.parent in 
conflict.pkgs[1:] for conflict in \
+   
self._dynamic_config._package_tracker.slot_conflicts())
if not buildpkgonly and \
not nodeps and \
not dep.collapsed_priority.ignored and \
not dep.collapsed_priority.optional and \
-   dep.parent not in 
self._dynamic_config._slot_collision_nodes:
+   not is_slot_conflict_parent:
if dep.parent.onlydeps:
# It's safe to ignore blockers if the
# parent is an --onlydeps node.
@@ -2019,7 +2021,6 @@ class depgraph(object):

level=logging.DEBUG, noiselevel=-1)
 
else:
-   self._add_slot_conflict(pkg)
if debug:
writemsg_level(
%s%s %s\n % (Slot 
Conflict:.ljust(15),
@@ -2138,9 +2139,6 @@ class depgraph(object):
self._dynamic_config._slot_operator_deps[slot_key] = 
slot_info
slot_info.append(dep)
 
-   def _add_slot_conflict(self, pkg):
-   self._dynamic_config._slot_collision_nodes.add(pkg)
-
def _add_pkg_deps(self, pkg, allow_unsatisfied=False):
 
myroot = pkg.root
@@ -6019,7 +6017,7 @@ class depgraph(object):
 
if complete not in self._dynamic_config.myparams and \
self._dynamic_config._allow_backtracking and \
-   self._dynamic_config._slot_collision_nodes and \
+   
any(self._dynamic_config._package_tracker.slot_conflicts()) and \
not self._accept_blocker_conflicts():
self._dynamic_config.myparams[complete] = True
 
-- 
1.8.3.2




[gentoo-portage-dev] [PATCH 01/10] Add resolver/package_tracker

2014-01-29 Thread Sebastian Luther
---
 pym/_emerge/resolver/package_tracker.py | 310 
 1 file changed, 310 insertions(+)
 create mode 100644 pym/_emerge/resolver/package_tracker.py

diff --git a/pym/_emerge/resolver/package_tracker.py 
b/pym/_emerge/resolver/package_tracker.py
new file mode 100644
index 000..4aee4ea
--- /dev/null
+++ b/pym/_emerge/resolver/package_tracker.py
@@ -0,0 +1,310 @@
+# Copyright 2014 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+from __future__ import print_function
+
+import collections
+
+import portage
+portage.proxy.lazyimport.lazyimport(globals(),
+   'portage:OrderedDict',
+   'portage.dep:Atom,match_from_list',
+   'portage.util:cmp_sort_key',
+   'portage.versions:vercmp',
+)
+
+_PackageConflict = collections.namedtuple(_PackageConflict, [root, pkgs, 
atom, description])
+
+class PackageConflict(_PackageConflict):
+   
+   Class to track the reason for a conflict and the conflicting packages.
+   
+   def __iter__(self):
+   return iter(self.pkgs)
+
+   def __contains__(self, pkg):
+   return pkg in self.pkgs
+
+   def __len__(self):
+   return len(self.pkgs)
+
+
+class PackageTracker(object):
+   
+   This class tracks packages which are currently
+   installed and packages which have been pulled into
+   the dependency graph.
+
+   It automatically tracks conflicts between packages.
+
+   Possible conflicts:
+   1) Packages that share the same SLOT.
+   2) Packages with the same cpv.
+   Not yet implemented:
+   3) Packages that block each other.
+   
+
+   def __init__(self):
+   # Mapping from package keys to set of packages.
+   self._cp_pkg_map = collections.defaultdict(list)
+   self._cp_vdb_pkg_map = collections.defaultdict(list)
+   # List of package keys that may contain conflicts.
+   # The insetation order must be preserved.
+   self._multi_pkgs = []
+
+   # Cache for result of conflicts().
+   self._conflicts_cache = None
+
+   # Records for each pulled package which installed package
+   # are replaced.
+   self._replacing = collections.defaultdict(list)
+   # Records which pulled packages replace this package.
+   self._replaced_by = collections.defaultdict(list)
+
+   self._match_cache = collections.defaultdict(dict)
+
+   def add_pkg(self, pkg):
+   
+   Add a new package to the tracker. Records conflicts as 
necessary.
+   
+   cp_key = pkg.root, pkg.cp
+
+   try:
+   if any(other is pkg for other in 
self._cp_pkg_map[cp_key]):
+   return
+   except KeyError:
+   self._cp_pkg_map[cp_key] = [pkg]
+   else:
+   self._cp_pkg_map[cp_key].append(pkg)
+   if len(self._cp_pkg_map[cp_key]) == 2:
+   self._multi_pkgs.append(cp_key)
+   self._conflicts_cache = None
+
+   self._replacing[pkg] = []
+   for installed in self._cp_vdb_pkg_map[cp_key]:
+   if installed.slot_atom == pkg.slot_atom or \
+   installed.cpv == pkg.cpv:
+   self._replacing[pkg].append(installed)
+   self._replaced_by[installed].append(pkg)
+
+   self._match_cache.pop((pkg.root, pkg.cp))
+
+   def add_installed_pkg(self, installed):
+   
+   Add an installed package during vdb load. These packages
+   are not returned by matched_pull as long as add_pkg hasn't
+   been called with them. They are only returned by match_final.
+   
+   cp_key = installed.root, installed.cp
+   try:
+   if any(other is installed for other in 
self._cp_vdb_pkg_map[cp_key]):
+   return
+   except KeyError:
+   self._cp_vdb_pkg_map[cp_key] = [installed]
+   else:
+   self._cp_vdb_pkg_map[cp_key].append(installed)
+
+   for pkg in self._cp_pkg_map[cp_key]:
+   if installed.slot_atom == pkg.slot_atom or \
+   installed.cpv == pkg.cpv:
+   self._replacing[pkg].append(installed)
+   self._replaced_by[installed].append(pkg)
+
+   def remove_pkg(self, pkg):
+   
+   Removes the package from the tracker.
+   Raises KeyError if it isn't present.
+   
+   cp_key = pkg.root, pkg.cp
+   try:
+ 

Re: [gentoo-portage-dev] xattr wrapper for install, bug #465000

2014-01-29 Thread Francesco R.
Il 29/01/2014 17:33, Anthony G. Basile ha scritto:
 On 01/27/2014 09:02 AM, viv...@gmail.com wrote:
 On 01/26/14 23:53, Anthony G. Basile wrote:
 Hi everyone,

 A while back, I wrote a python wrapper for install to preserve xattrs.
 Its installed in LIBDIR/portage/bin/install.py.  It is *painfully*
 slow.  For a package like moodle with 16650 .php files, none of which
 probably need any xattr's set, it takes about 30 mins to install.  I
 rewrote the wrapper in C.  Replacing the python wrapper with the C
 wrapper, the same example reduces from about 30 mins to 2 mins.

 Mike and I did some back and forth about how best to write it.  The
 latest version is pretty much done at

 https://bugs.gentoo.org/show_bug.cgi?id=465000#c56

 We need to get that integrated into portage.  1) I'm not 100% sure how
 to do that.  2) We may want to install it at /usr/bin/install-xattr
 because I'm sure it will be useful for more than just portage.

 Comments?

 patch install from coreutils (and then upstream changes) is not an
 option?
 they already support selinux contexts

 anyway install-xattr could be useful and /usr/bin would be a good option
 IMHO


 Been there and I even had a patch ready.  Upstream answer was '\0'. 
 The only people who engaged the discussion were gentoo devs.

 Would patching coreutils have been the better approach?  Some people
 might argue install and cp and mv etc should just copy contents to
 keep these utilities as simple as possible.  Although, as you say,
 install can copy selinux contexts, and cp can copy xattr attributes. 
 So what's the problem with extending installs functionality to include
 arbitrary xattr attributes?

 Anyhow, seeing as upstream is uninterested, I prefer this wrapper to
 maintaining a local patch against coreutils.


thanks for answering, so yes the wrapper seem the only sensible thing,
good luck, since there are no other questions in 3 days the plan must be
perfect ;)

on a totally unrelated and ipotetical note kde is discussing a metadata
engine that use xattr instead of databases, so this could become
important even for desktops