Gentoo Archives: gentoo-commits

From: Zac Medico <zmedico@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/portage:master commit in: pym/portage/tests/dep/, pym/portage/dep/, pym/portage/tests/resolver/
Date: Sat, 03 Feb 2018 00:01:53
Message-Id: 1517572885.0ddfb5802d98c618bd20742be49a520c9a54b394.zmedico@gentoo
1 commit: 0ddfb5802d98c618bd20742be49a520c9a54b394
2 Author: Zac Medico <zmedico <AT> gentoo <DOT> org>
3 AuthorDate: Fri Feb 2 06:08:59 2018 +0000
4 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org>
5 CommitDate: Fri Feb 2 12:01:25 2018 +0000
6 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=0ddfb580
7
8 dep_zapdeps: sort by new_slot_count for DNF only (bug 645002)
9
10 Sorting of choices by new_slot_count causes the order of
11 choices specified in the ebuild to be discarded, and
12 new_slot_count may have variance related to the order that
13 packages are added to the graph. This variance can
14 contribute to outcomes that appear to be random, like when
15 catalyst stage1 builds sometimes pull in paludis to
16 satisfy perl-cleaner dependencies.
17
18 Meanwhile, the order specified in the ebuild has no
19 variance, and the preferences that it specifies can serve
20 as a crucial sources of guidance. Therefore, take advantage
21 of the order specified in the ebuild whenever possible, and
22 use new_slot_count only when it is needed to select optimal
23 choices from DNF (as in bug 632026).
24
25 This fixes random outcomes in the unit test for bug 645002.
26 Previously, the unit test pulled in paludis randomly unless
27 portage-utils was added to the arguments. With this patch,
28 the portage-utils argument is no longer needed for the unit
29 test to succeed consistently. The perl-cleaner dependencies
30 do not have any overlapping || deps, so the DNF conversion
31 and new_slot_count sorting do not apply, and the first
32 choice is preferred regardless of the number of slots that
33 it pulls in:
34
35 || (
36 ( sys-apps/portage app-portage/portage-utils )
37 sys-apps/pkgcore
38 sys-apps/paludis
39 )
40
41 The _overlap_dnf function now returns the argument object
42 when there is no overlap, so OverlapDNFTestCase has to be
43 adjusted to account for this.
44
45 Bug: https://bugs.gentoo.org/645002
46 Fixes: 9fdaf9bdbdf5 ("dep_check: use DNF to optimize overlapping virtual || deps (bug 632026)")
47
48 pym/portage/dep/dep_check.py | 49 ++++++++++++++++++----
49 pym/portage/tests/dep/test_overlap_dnf.py | 2 +-
50 .../resolver/test_virtual_minimize_children.py | 12 +++---
51 3 files changed, 47 insertions(+), 16 deletions(-)
52
53 diff --git a/pym/portage/dep/dep_check.py b/pym/portage/dep/dep_check.py
54 index 7e5a3186e..2896e2389 100644
55 --- a/pym/portage/dep/dep_check.py
56 +++ b/pym/portage/dep/dep_check.py
57 @@ -298,7 +298,8 @@ class _dep_choice(SlotObject):
58 __slots__ = ('atoms', 'slot_map', 'cp_map', 'all_available',
59 'all_installed_slots', 'new_slot_count')
60
61 -def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None):
62 +def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None,
63 + minimize_slots=False):
64 """
65 Takes an unreduced and reduced deplist and removes satisfied dependencies.
66 Returned deplist contains steps that must be taken to satisfy dependencies.
67 @@ -314,7 +315,8 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None):
68 for x, satisfied in zip(unreduced, reduced):
69 if isinstance(x, list):
70 unresolved += dep_zapdeps(x, satisfied, myroot,
71 - use_binaries=use_binaries, trees=trees)
72 + use_binaries=use_binaries, trees=trees,
73 + minimize_slots=minimize_slots)
74 elif not satisfied:
75 unresolved.append(x)
76 return unresolved
77 @@ -386,7 +388,8 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None):
78 for x, satisfied in zip(deps, satisfieds):
79 if isinstance(x, list):
80 atoms = dep_zapdeps(x, satisfied, myroot,
81 - use_binaries=use_binaries, trees=trees)
82 + use_binaries=use_binaries, trees=trees,
83 + minimize_slots=minimize_slots)
84 else:
85 atoms = [x]
86 if vardb is None:
87 @@ -663,9 +666,28 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None):
88 for choices in choice_bins:
89 if len(choices) < 2:
90 continue
91 - # Prefer choices with all_installed_slots for bug #480736, and
92 - # choices with a smaller number of new slots for bug #632026.
93 - choices.sort(key=lambda x: (not x.all_installed_slots, x.new_slot_count))
94 +
95 + sort_keys = []
96 + # Prefer choices with all_installed_slots for bug #480736.
97 + sort_keys.append(lambda x: not x.all_installed_slots)
98 +
99 + if minimize_slots:
100 + # Prefer choices having fewer new slots. When used with DNF form,
101 + # this can eliminate unecessary packages that depclean would
102 + # ultimately eliminate (see bug 632026). Only use this behavior
103 + # when deemed necessary by the caller, since this will discard the
104 + # order specified in the ebuild, and the preferences specified
105 + # there can serve as a crucial sources of guidance (see bug 645002).
106 +
107 + # NOTE: Under some conditions, new_slot_count value may have some
108 + # variance from one calculation to the next because it depends on
109 + # the order that packages are added to the graph. This variance can
110 + # contribute to outcomes that appear to be random. Meanwhile,
111 + # the order specified in the ebuild is without variance, so it
112 + # does not have this problem.
113 + sort_keys.append(lambda x: x.new_slot_count)
114 +
115 + choices.sort(key=lambda x: tuple(f(x) for f in sort_keys))
116 for choice_1 in choices[1:]:
117 cps = set(choice_1.cp_map)
118 for choice_2 in choices:
119 @@ -792,8 +814,11 @@ def dep_check(depstring, mydbapi, mysettings, use="yes", mode=None, myuse=None,
120 except ParseError as e:
121 return [0, "%s" % (e,)]
122
123 + dnf = False
124 if mysettings.local_config: # if not repoman
125 + orig_split = mysplit
126 mysplit = _overlap_dnf(mysplit)
127 + dnf = mysplit is not orig_split
128
129 mysplit2 = dep_wordreduce(mysplit,
130 mysettings, mydbapi, mode, use_cache=use_cache)
131 @@ -805,7 +830,7 @@ def dep_check(depstring, mydbapi, mysettings, use="yes", mode=None, myuse=None,
132 writemsg("mysplit2: %s\n" % (mysplit2), 1)
133
134 selected_atoms = dep_zapdeps(mysplit, mysplit2, myroot,
135 - use_binaries=use_binaries, trees=trees)
136 + use_binaries=use_binaries, trees=trees, minimize_slots=dnf)
137
138 return [1, selected_atoms]
139
140 @@ -817,6 +842,12 @@ def _overlap_dnf(dep_struct):
141 "|| ( foo bar ) || ( bar baz )" as in bug #632026. Non-overlapping
142 groups are excluded from the conversion, since DNF leads to exponential
143 explosion of the formula.
144 +
145 + When dep_struct does not contain any overlapping groups, no DNF
146 + conversion will be performed, and dep_struct will be returned as-is.
147 + Callers can detect this case by checking if the returned object has
148 + the same identity as dep_struct. If the identity is different, then
149 + DNF conversion was performed.
150 """
151 if not _contains_disjunction(dep_struct):
152 return dep_struct
153 @@ -847,6 +878,7 @@ def _overlap_dnf(dep_struct):
154
155 # group together disjunctions having atom.cp overlap
156 traversed = set()
157 + overlap = False
158 for cp in overlap_graph:
159 if cp in traversed:
160 continue
161 @@ -863,6 +895,7 @@ def _overlap_dnf(dep_struct):
162 stack.append(other_cp)
163
164 if len(disjunctions) > 1:
165 + overlap = True
166 # convert overlapping disjunctions to DNF
167 result.extend(_dnf_convert(
168 sorted(disjunctions.values(), key=order_key)))
169 @@ -870,7 +903,7 @@ def _overlap_dnf(dep_struct):
170 # pass through non-overlapping disjunctions
171 result.append(disjunctions.popitem()[1])
172
173 - return result
174 + return result if overlap else dep_struct
175
176
177 def _iter_flatten(dep_struct):
178
179 diff --git a/pym/portage/tests/dep/test_overlap_dnf.py b/pym/portage/tests/dep/test_overlap_dnf.py
180 index 79b3e8e46..ee48e5556 100644
181 --- a/pym/portage/tests/dep/test_overlap_dnf.py
182 +++ b/pym/portage/tests/dep/test_overlap_dnf.py
183 @@ -12,7 +12,7 @@ class OverlapDNFTestCase(TestCase):
184 test_cases = (
185 (
186 '|| ( cat/A cat/B ) cat/E || ( cat/C cat/D )',
187 - ['cat/E', ['||', 'cat/A', 'cat/B'], ['||', 'cat/C', 'cat/D']],
188 + [['||', 'cat/A', 'cat/B'], 'cat/E', ['||', 'cat/C', 'cat/D']],
189 ),
190 (
191 '|| ( cat/A cat/B ) cat/D || ( cat/B cat/C )',
192
193 diff --git a/pym/portage/tests/resolver/test_virtual_minimize_children.py b/pym/portage/tests/resolver/test_virtual_minimize_children.py
194 index 287445e58..b566cb592 100644
195 --- a/pym/portage/tests/resolver/test_virtual_minimize_children.py
196 +++ b/pym/portage/tests/resolver/test_virtual_minimize_children.py
197 @@ -168,17 +168,15 @@ class VirtualMinimizeChildrenTestCase(TestCase):
198 }
199
200 test_cases = (
201 - # Test bug 645002, where we want to prefer choices
202 - # based on the number of new slots rather than the total
203 - # number of slots. This is necessary so that perl-cleaner's
204 - # deps are satisfied by the ( portage portage-utils )
205 - # choice which has a larger total number of slots than the
206 - # paludis choice.
207 + # Test bug 645002, where paludis was selected to satisfy a
208 + # perl-cleaner dependency because that choice contained fewer
209 + # packages than the ( portage portage-utils ) choice which
210 + # should have been preferred according to the order of
211 + # choices specified in the ebuild.
212 ResolverPlaygroundTestCase(
213 [
214 'app-admin/perl-cleaner',
215 'virtual/package-manager',
216 - 'app-portage/portage-utils',
217 ],
218 all_permutations=True,
219 success=True,