Gentoo Archives: gentoo-portage-dev

From: Brian Dolbec <dolsen@g.o>
To: gentoo-portage-dev@l.g.o
Subject: Re: [gentoo-portage-dev] [PATCH 1/2] emerge: terminate backtracking early for autounmask changes (bug 615680)
Date: Sun, 14 May 2017 13:38:05
Message-Id: 20170514063804.7c077fcc.dolsen@gentoo.org
In Reply to: [gentoo-portage-dev] [PATCH 1/2] emerge: terminate backtracking early for autounmask changes (bug 615680) by Zac Medico
1 On Tue, 9 May 2017 20:52:34 -0700
2 Zac Medico <zmedico@g.o> wrote:
3
4 > Since autounmask changes are a strong indicator that backtracking
5 > will ultimately fail to produce a solution, terminate early for
6 > autounmask changes, and add a --autounmask-backtrack=<y|n> option
7 > to modify this behavior. The --autounmask-continue option implies
8 > --autounmask-backtrack=y behavior, for backward compatibility.
9 >
10 > When backtracking terminates early, the following warning message
11 > is displayed after the autounmask change(s):
12 >
13 > * In order to avoid wasting time, backtracking has terminated early
14 > * due to the above autounmask change(s). The --autounmask-backtrack=y
15 > * option can be used to force further backtracking, but there is no
16 > * guarantee that it will produce a solution.
17 >
18 > With this change, five of the existing cases fail unless
19 > --autounmask-backtrack=y is added to the options. For each of
20 > these cases, comments below the test case document how it behaves
21 > with and without --autounmask-backtrack=y enabled.
22 >
23 > X-Gentoo-bug: 615680
24 > X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=615680
25 > ---
26 > man/emerge.1 | 10 ++-
27 > pym/_emerge/depgraph.py | 80
28 > ++++++++++++++++++----
29 > pym/_emerge/main.py | 6 ++
30 > pym/portage/tests/resolver/test_autounmask.py | 57
31 > ++++++++++++++- .../tests/resolver/test_autounmask_use_breakage.py |
32 > 40 +++++++++++ .../test_slot_conflict_unsatisfied_deep_deps.py |
33 > 61 +++++++++++++++++ 6 files changed, 237 insertions(+), 17
34 > deletions(-)
35 >
36 > diff --git a/man/emerge.1 b/man/emerge.1
37 > index f1a9d4f..94edc90 100644
38 > --- a/man/emerge.1
39 > +++ b/man/emerge.1
40 > @@ -363,12 +363,20 @@ the specified configuration file(s), or enable
41 > the \fBEMERGE_DEFAULT_OPTS\fR variable may be used to
42 > disable this option by default in \fBmake.conf\fR(5).
43 > .TP
44 > +.BR "\-\-autounmask\-backtrack < y | n >"
45 > +Allow backtracking after autounmask has detected that
46 > +configuration changes are necessary. This option is not
47 > +recommended, since it can cause a large amount of time to
48 > +be wasted by backtracking calculations, even though there
49 > +is no guarantee that it will produce a solution. This
50 > +option is disabled by default.
51 > +.TP
52 > .BR "\-\-autounmask\-continue [ y | n ]"
53 > Automatically apply autounmask changes to configuration
54 > files, and continue to execute the specified command. If
55 > the dependency calculation is not entirely successful, then
56 > emerge will simply abort without modifying any configuration
57 > -files.
58 > +files. This option implies \fB\-\-autounmask\-backtrack=y\fR.
59 > \fBWARNING:\fR
60 > This option is intended to be used only with great caution,
61 > since it is possible for it to make nonsensical configuration
62 > diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
63 > index e1119af..53910dd 100644
64 > --- a/pym/_emerge/depgraph.py
65 > +++ b/pym/_emerge/depgraph.py
66 > @@ -444,6 +444,7 @@ class _dynamic_depgraph_config(object):
67 > self._autounmask =
68 > depgraph._frozen_config.myopts.get('--autounmask') != 'n'
69 > self._displayed_autounmask = False self._success_without_autounmask =
70 > False
71 > + self._autounmask_backtrack_disabled = False
72 > self._required_use_unsatisfied = False
73 > self._traverse_ignored_deps = False
74 > self._complete_mode = False
75 > @@ -1129,7 +1130,8 @@ class depgraph(object):
76 >
77 > self._show_merge_list()
78 >
79 > - self._dynamic_config._slot_conflict_handler =
80 > slot_conflict_handler(self)
81 > + if self._dynamic_config._slot_conflict_handler is
82 > None:
83 > + self._dynamic_config._slot_conflict_handler
84 > = slot_conflict_handler(self) handler =
85 > self._dynamic_config._slot_conflict_handler
86 > conflict = handler.get_conflict()
87 > @@ -4243,17 +4245,7 @@ class depgraph(object):
88 > # set below is reserved for cases where there are
89 > *zero* other # problems. For reference, see backtrack_depgraph, where
90 > it skips the # get_best_run() call when success_without_autounmask is
91 > True. -
92 > - digraph_nodes = self._dynamic_config.digraph.nodes
93 > -
94 > - if any(x in digraph_nodes for x in
95 > -
96 > self._dynamic_config._needed_unstable_keywords) or \
97 > - any(x in digraph_nodes for x in
98 > - self._dynamic_config._needed_p_mask_changes)
99 > or \
100 > - any(x in digraph_nodes for x in
101 > -
102 > self._dynamic_config._needed_use_config_changes) or \
103 > - any(x in digraph_nodes for x in
104 > -
105 > self._dynamic_config._needed_license_changes) :
106 > + if self._have_autounmask_changes():
107 > #We failed if the user needs to change the
108 > configuration self._dynamic_config._success_without_autounmask = True
109 > if
110 > (self._frozen_config.myopts.get("--autounmask-continue") is True and
111 > @@ -8564,6 +8556,17 @@ class depgraph(object): "experimental or
112 > unstable packages.\n", noiselevel=-1)
113 >
114 > + if
115 > self._dynamic_config._autounmask_backtrack_disabled:
116 > + msg = [
117 > + "In order to avoid wasting time,
118 > backtracking has terminated early",
119 > + "due to the above autounmask
120 > change(s). The --autounmask-backtrack=y",
121 > + "option can be used to force further
122 > backtracking, but there is no",
123 > + "guarantee that it will produce a
124 > solution.",
125 > + ]
126 > + writemsg("\n", noiselevel=-1)
127 > + for line in msg:
128 > + writemsg(" %s %s\n" %
129 > (colorize("WARN", "*"), line),
130 > + noiselevel=-1)
131 >
132 > def display_problems(self):
133 > """
134 > @@ -9072,8 +9075,57 @@ class depgraph(object):
135 > not self._dynamic_config._skip_restart
136 >
137 > def need_config_change(self):
138 > - return
139 > self._dynamic_config._success_without_autounmask or \
140 > -
141 > self._dynamic_config._required_use_unsatisfied
142 > + """
143 > + Returns true if backtracking should terminate due to
144 > a needed
145 > + configuration change.
146 > + """
147 > + if (self._dynamic_config._success_without_autounmask
148 > or
149 > +
150 > self._dynamic_config._required_use_unsatisfied):
151 > + return True
152 > +
153 > + if (self._dynamic_config._slot_conflict_handler is
154 > None and
155 > + not self._accept_blocker_conflicts() and
156 > +
157 > any(self._dynamic_config._package_tracker.slot_conflicts())):
158 > + self._dynamic_config._slot_conflict_handler
159 > = slot_conflict_handler(self)
160 > + if
161 > self._dynamic_config._slot_conflict_handler.changes:
162 > + # Terminate backtracking early if
163 > the slot conflict
164 > + # handler finds some changes to
165 > suggest. The case involving
166 > + # sci-libs/L and sci-libs/M in
167 > SlotCollisionTestCase will
168 > + # otherwise fail with
169 > --autounmask-backtrack=n, since
170 > + # backtracking will eventually lead
171 > to some autounmask
172 > + # changes. Changes suggested by the
173 > slot conflict handler
174 > + # are more likely to be useful.
175 > + return True
176 > +
177 > + if (self._dynamic_config._allow_backtracking and
178 > +
179 > self._frozen_config.myopts.get("--autounmask-backtrack") != 'y' and
180 > + self._have_autounmask_changes()):
181 > +
182 > + if
183 > (self._frozen_config.myopts.get("--autounmask-continue") is True and
184 > +
185 > self._frozen_config.myopts.get("--autounmask-backtrack") != 'n'):
186 > + # --autounmask-continue implies
187 > --autounmask-backtrack=y behavior,
188 > + # for backward compatibility.
189 > + return False
190 > +
191 > + # This disables backtracking when there are
192 > autounmask
193 > + # config changes. The display_problems
194 > method will notify
195 > + # the user that --autounmask-backtrack=y can
196 > be used to
197 > + # force backtracking in this case.
198 > +
199 > self._dynamic_config._autounmask_backtrack_disabled = True
200 > + return True
201 > +
202 > + return False
203 > +
204 > + def _have_autounmask_changes(self):
205 > + digraph_nodes = self._dynamic_config.digraph.nodes
206 > + return (any(x in digraph_nodes for x in
207 > +
208 > self._dynamic_config._needed_unstable_keywords) or
209 > + any(x in digraph_nodes for x in
210 > + self._dynamic_config._needed_p_mask_changes)
211 > or
212 > + any(x in digraph_nodes for x in
213 > +
214 > self._dynamic_config._needed_use_config_changes) or
215 > + any(x in digraph_nodes for x in
216 > +
217 > self._dynamic_config._needed_license_changes))
218 > def need_config_reload(self):
219 > return self._dynamic_config._need_config_reload
220 > diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py
221 > index 76e963a..8084967 100644
222 > --- a/pym/_emerge/main.py
223 > +++ b/pym/_emerge/main.py
224 > @@ -326,6 +326,12 @@ def parse_opts(tmpcmdline, silent=False):
225 > "choices" : true_y_or_n
226 > },
227 >
228 > + "--autounmask-backtrack": {
229 > + "help": ("continue backtracking when there
230 > are autounmask "
231 > + "configuration changes"),
232 > + "choices":("y", "n")
233 > + },
234 > +
235 > "--autounmask-continue": {
236 > "help" : "write autounmask changes and
237 > continue", "choices" : true_y_or_n
238 > diff --git a/pym/portage/tests/resolver/test_autounmask.py
239 > b/pym/portage/tests/resolver/test_autounmask.py index
240 > 75fb368..e2a7de0 100644 ---
241 > a/pym/portage/tests/resolver/test_autounmask.py +++
242 > b/pym/portage/tests/resolver/test_autounmask.py @@ -81,20 +81,73 @@
243 > class AutounmaskTestCase(TestCase): #Make sure we restart if needed.
244 > ResolverPlaygroundTestCase(
245 > ["dev-libs/A:1",
246 > "dev-libs/B"],
247 > - options={"--autounmask":
248 > True},
249 > + options={"--autounmask":
250 > True, "--autounmask-backtrack": "y"}, all_permutations=True,
251 > success=False,
252 > mergelist=["dev-libs/C-1",
253 > "dev-libs/B-1", "dev-libs/A-1"], use_changes={ "dev-libs/B-1":
254 > {"foo": True} }), +
255 > + # With --autounmask-backtrack=y:
256 > + #[ebuild N ] dev-libs/C-1
257 > + #[ebuild N ] dev-libs/B-1
258 > USE="foo -bar"
259 > + #[ebuild N ] dev-libs/A-1
260 > + #
261 > + #The following USE changes are
262 > necessary to proceed:
263 > + # (see "package.use" in the
264 > portage(5) man page for more details)
265 > + ## required by
266 > dev-libs/A-1::test_repo
267 > + ## required by dev-libs/A:1
268 > (argument)
269 > + #>=dev-libs/B-1 foo
270 > +
271 > + # Without --autounmask-backtrack=y:
272 > + #[ebuild N ] dev-libs/B-1
273 > USE="foo -bar"
274 > + #[ebuild N ] dev-libs/A-1
275 > + #
276 > + #The following USE changes are
277 > necessary to proceed:
278 > + # (see "package.use" in the
279 > portage(5) man page for more details)
280 > + ## required by
281 > dev-libs/A-1::test_repo
282 > + ## required by dev-libs/A:1
283 > (argument)
284 > + #>=dev-libs/B-1 foo
285 > +
286 > ResolverPlaygroundTestCase(
287 > ["dev-libs/A:1",
288 > "dev-libs/A:2", "dev-libs/B"],
289 > - options={"--autounmask":
290 > True},
291 > + options={"--autounmask":
292 > True, "--autounmask-backtrack": "y"}, all_permutations=True,
293 > success=False,
294 > mergelist=["dev-libs/D-1",
295 > "dev-libs/C-1", "dev-libs/B-1", "dev-libs/A-1", "dev-libs/A-2"],
296 > ignore_mergelist_order=True, use_changes={ "dev-libs/B-1": {"foo":
297 > True, "bar": True} }),
298 > + # With --autounmask-backtrack=y:
299 > + #[ebuild N ] dev-libs/C-1
300 > + #[ebuild N ] dev-libs/D-1
301 > + #[ebuild N ] dev-libs/B-1
302 > USE="bar foo"
303 > + #[ebuild N ] dev-libs/A-2
304 > + #[ebuild N ] dev-libs/A-1
305 > + #
306 > + #The following USE changes are
307 > necessary to proceed:
308 > + # (see "package.use" in the
309 > portage(5) man page for more details)
310 > + ## required by
311 > dev-libs/A-2::test_repo
312 > + ## required by dev-libs/A:2
313 > (argument)
314 > + #>=dev-libs/B-1 bar foo
315 > +
316 > + # Without --autounmask-backtrack=y:
317 > + #[ebuild N ] dev-libs/B-1
318 > USE="bar foo"
319 > + #[ebuild N ] dev-libs/A-1
320 > + #[ebuild N ] dev-libs/A-2
321 > + #
322 > + #The following USE changes are
323 > necessary to proceed:
324 > + # (see "package.use" in the
325 > portage(5) man page for more details)
326 > + ## required by
327 > dev-libs/A-1::test_repo
328 > + ## required by dev-libs/A:1
329 > (argument)
330 > + #>=dev-libs/B-1 foo bar
331 > +
332 > + # NOTE: The --autounmask-backtrack=n
333 > behavior is acceptable, but
334 > + # it would be nicer if it added the
335 > dev-libs/C-1 and dev-libs/D-1
336 > + # deps to the depgraph without
337 > backtracking. It could add two
338 > + # instances of dev-libs/B-1 to the
339 > graph with different USE flags,
340 > + # and then use
341 > _solve_non_slot_operator_slot_conflicts to eliminate
342 > + # the redundant instance.
343 > +
344 > #Test keywording.
345 > #The simple case.
346 >
347 > diff --git
348 > a/pym/portage/tests/resolver/test_autounmask_use_breakage.py
349 > b/pym/portage/tests/resolver/test_autounmask_use_breakage.py index
350 > 3654aa6..1739416 100644 ---
351 > a/pym/portage/tests/resolver/test_autounmask_use_breakage.py +++
352 > b/pym/portage/tests/resolver/test_autounmask_use_breakage.py @@
353 > -46,12 +46,52 @@ class AutounmaskUseBreakageTestCase(TestCase): # due
354 > to autounmask USE breakage.
355 > ResolverPlaygroundTestCase( ["app-misc/C", "app-misc/B",
356 > "app-misc/A"],
357 > + options={"--autounmask-backtrack":
358 > "y"}, all_permutations = True,
359 > success = False,
360 > ambiguous_slot_collision_solutions =
361 > True, slot_collision_solutions = [None, []]
362 > ),
363 >
364 > + # With --autounmask-backtrack=y:
365 > + #emerge: there are no ebuilds built with USE
366 > flags to satisfy "app-misc/D[foo]".
367 > + #!!! One of the following packages is
368 > required to complete your request:
369 > + #- app-misc/D-0::test_repo (Change USE: +foo)
370 > + #(dependency required by
371 > "app-misc/B-0::test_repo" [ebuild])
372 > + #(dependency required by
373 > "app-misc/B" [argument]) +
374 > + # Without --autounmask-backtrack=y:
375 > + #[ebuild N ] app-misc/D-0 USE="foo"
376 > + #[ebuild N ] app-misc/D-1 USE="-bar"
377 > + #[ebuild N ] app-misc/C-0
378 > + #[ebuild N ] app-misc/B-0
379 > + #[ebuild N ] app-misc/A-0
380 > + #
381 > + #!!! Multiple package instances within a
382 > single package slot have been pulled
383 > + #!!! into the dependency graph, resulting in
384 > a slot conflict:
385 > + #
386 > + #app-misc/D:0
387 > + #
388 > + # (app-misc/D-0:0/0::test_repo, ebuild
389 > scheduled for merge) pulled in by
390 > + # app-misc/D[-foo] required by
391 > (app-misc/A-0:0/0::test_repo, ebuild scheduled for merge)
392 > + # ^^^^
393 > + # app-misc/D[foo] required by
394 > (app-misc/B-0:0/0::test_repo, ebuild scheduled for merge)
395 > + # ^^^
396 > + #
397 > + # (app-misc/D-1:0/0::test_repo, ebuild
398 > scheduled for merge) pulled in by
399 > + # >=app-misc/D-1 required by
400 > (app-misc/C-0:0/0::test_repo, ebuild scheduled for merge)
401 > + # ^^ ^
402 > + #
403 > + #The following USE changes are necessary to
404 > proceed:
405 > + # (see "package.use" in the portage(5) man
406 > page for more details)
407 > + ## required by app-misc/B-0::test_repo
408 > + ## required by app-misc/B (argument)
409 > + #=app-misc/D-0 foo
410 > +
411 > + # NOTE: The --autounmask-backtrack=n output
412 > is preferable here,
413 > + # because it highlights the unsolvable
414 > dependency conflict.
415 > + # It would be better if it eliminated the
416 > autounmask suggestion,
417 > + # since that suggestion won't solve the
418 > conflict. )
419 >
420 > playground = ResolverPlayground(ebuilds=ebuilds,
421 > debug=False) diff --git
422 > a/pym/portage/tests/resolver/test_slot_conflict_unsatisfied_deep_deps.py
423 > b/pym/portage/tests/resolver/test_slot_conflict_unsatisfied_deep_deps.py
424 > index 13f7e67..846ba0e 100644 ---
425 > a/pym/portage/tests/resolver/test_slot_conflict_unsatisfied_deep_deps.py
426 > +++
427 > b/pym/portage/tests/resolver/test_slot_conflict_unsatisfied_deep_deps.py
428 > @@ -79,6 +79,7 @@ class
429 > SlotConflictUnsatisfiedDeepDepsTestCase(TestCase): ["@world"],
430 > options={ "--autounmask": "y",
431 > + "--autounmask-backtrack":
432 > "y", "--complete-graph": True,
433 > "--selective": True,
434 > "--deep": 1
435 > @@ -89,11 +90,63 @@ class
436 > SlotConflictUnsatisfiedDeepDepsTestCase(TestCase):
437 > unsatisfied_deps=["dev-libs/initially-unsatisfied"], success=False),
438 >
439 > + # With --autounmask-backtrack=y:
440 > + #[ebuild N ~] dev-libs/A-2
441 > + #[ebuild N ] dev-libs/C-1
442 > + #[ebuild N ] dev-libs/D-1
443 > + #[ebuild N ] dev-libs/B-1
444 > + #
445 > + #The following keyword changes are necessary
446 > to proceed:
447 > + # (see "package.accept_keywords" in the
448 > portage(5) man page for more details)
449 > + ## required by dev-libs/C-1::test_repo
450 > + ## required by @selected
451 > + ## required by @world (argument)
452 > + #=dev-libs/A-2 ~x86
453 > + #
454 > + #!!! Problems have been detected with your
455 > world file
456 > + #!!! Please run emaint --check world
457 > + #
458 > + #
459 > + #!!! Ebuilds for the following packages are
460 > either all
461 > + #!!! masked or don't exist:
462 > + #dev-libs/broken
463 > + #
464 > + #emerge: there are no ebuilds to satisfy
465 > "dev-libs/initially-unsatisfied".
466 > + #(dependency required by
467 > "dev-libs/broken-1::test_repo" [installed])
468 > + #(dependency required by "@selected" [set])
469 > + #(dependency required by "@world" [argument])
470 > +
471 > + # Without --autounmask-backtrack=y:
472 > + #!!! Multiple package instances within a
473 > single package slot have been pulled
474 > + #!!! into the dependency graph, resulting in
475 > a slot conflict:
476 > + #
477 > + #dev-libs/A:0
478 > + #
479 > + # (dev-libs/A-1:0/0::test_repo, ebuild
480 > scheduled for merge) pulled in by
481 > + # (no parents that aren't satisfied by
482 > other packages in this slot)
483 > + #
484 > + # (dev-libs/A-2:0/0::test_repo, ebuild
485 > scheduled for merge) pulled in by
486 > + # >=dev-libs/A-2 required by
487 > (dev-libs/C-1:0/0::test_repo, ebuild scheduled for merge)
488 > + # ^^ ^
489 > + #
490 > + #The following keyword changes are necessary
491 > to proceed:
492 > + # (see "package.accept_keywords" in the
493 > portage(5) man page for more details)
494 > + ## required by dev-libs/C-1::test_repo
495 > + ## required by @selected
496 > + ## required by @world (argument)
497 > + #=dev-libs/A-2 ~x86
498 > + #
499 > + #emerge: there are no ebuilds to satisfy
500 > "dev-libs/initially-unsatisfied".
501 > + #(dependency required by
502 > "dev-libs/broken-1::test_repo" [installed])
503 > + #(dependency required by "@selected" [set])
504 > + #(dependency required by "@world" [argument])
505 > +
506 > # Test --deep = True
507 > ResolverPlaygroundTestCase(
508 > ["@world"],
509 > options={
510 > "--autounmask": "y",
511 > + "--autounmask-backtrack":
512 > "y", "--complete-graph": True,
513 > "--selective": True,
514 > "--deep": True
515 > @@ -103,6 +156,14 @@ class
516 > SlotConflictUnsatisfiedDeepDepsTestCase(TestCase):
517 > unstable_keywords=["dev-libs/A-2"],
518 > unsatisfied_deps=["dev-libs/initially-unsatisfied"], success=False),
519 > +
520 > + # The effects of --autounmask-backtrack are
521 > the same as the previous test case.
522 > + # Both test cases can randomly succeed with
523 > --autounmask-backtrack=n, when
524 > + # "backtracking due to unsatisfied dep"
525 > randomly occurs before the autounmask
526 > + # unstable keyword change. It would be
527 > possible to eliminate backtracking here
528 > + # by recognizing that there are no
529 > alternatives to satisfy the dev-libs/broken
530 > + # atom in the world file. Then the test
531 > cases will consistently succeed with
532 > + # --autounmask-backtrack=n.
533 > )
534 >
535 > playground = ResolverPlayground(ebuilds=ebuilds,
536 > installed=installed,
537
538
539 Looks good, thank you :)
540
541 --
542 Brian Dolbec <dolsen>

Replies