Gentoo Archives: gentoo-commits

From: "Zac Medico (zmedico)" <zmedico@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] portage r9965 - in main/trunk: doc/dependency_resolution pym/_emerge
Date: Fri, 25 Apr 2008 01:53:49
Message-Id: E1JpD8D-00069G-Q5@stork.gentoo.org
1 Author: zmedico
2 Date: 2008-04-25 01:53:44 +0000 (Fri, 25 Apr 2008)
3 New Revision: 9965
4
5 Modified:
6 main/trunk/doc/dependency_resolution/task_scheduling.docbook
7 main/trunk/pym/_emerge/__init__.py
8 Log:
9 Bug #172812 - If any Uninstall tasks need to be executed in order
10 to avoid a conflict, complete the graph with any dependencies that
11 may have been initially neglected (to ensure that unsafe Uninstall
12 tasks are properly identified and blocked from execution).
13
14
15 Modified: main/trunk/doc/dependency_resolution/task_scheduling.docbook
16 ===================================================================
17 --- main/trunk/doc/dependency_resolution/task_scheduling.docbook 2008-04-24 20:43:16 UTC (rev 9964)
18 +++ main/trunk/doc/dependency_resolution/task_scheduling.docbook 2008-04-25 01:53:44 UTC (rev 9965)
19 @@ -29,13 +29,14 @@
20 Installed packages that have been pulled into the current dependency
21 graph will not be uninstalled. Due to
22 <link linkend='dependency-resolution-package-modeling-dependency-neglection'>
23 - dependency neglection</link>, other checks may be necessary in order
24 + dependency neglection</link> and special properties of packages
25 + in the "system" set, other checks may be necessary in order
26 to protect inappropriate packages from being uninstalled.
27 </listitem>
28 <listitem>
29 An installed package that is matched by a dependency atom from the
30 "system" set will not be uninstalled in advance since it might not
31 - be safe. Such a package will be uninstalled through replacement.
32 + be safe. Such a package will be only uninstalled through replacement.
33 </listitem>
34 <listitem>
35 An installed package that is matched by a dependency atom from the
36
37 Modified: main/trunk/pym/_emerge/__init__.py
38 ===================================================================
39 --- main/trunk/pym/_emerge/__init__.py 2008-04-24 20:43:16 UTC (rev 9964)
40 +++ main/trunk/pym/_emerge/__init__.py 2008-04-25 01:53:44 UTC (rev 9965)
41 @@ -1711,7 +1711,7 @@
42 # Slot collision nodes are not allowed to block other packages since
43 # blocker validation is only able to account for one package per slot.
44 self._slot_collision_nodes = set()
45 - self._altlist_cache = {}
46 + self._serialized_tasks_cache = None
47 self._pprovided_args = []
48 self._missing_args = []
49 self._masked_installed = []
50 @@ -1858,7 +1858,6 @@
51 nodeps = "--nodeps" in self.myopts
52 empty = "empty" in self.myparams
53 deep = "deep" in self.myparams
54 - complete = "complete" in self.myparams
55 update = "--update" in self.myopts and dep.depth <= 1
56 if dep.blocker:
57 if not buildpkgonly and \
58 @@ -1902,8 +1901,7 @@
59 # should have been masked.
60 raise
61 if not myarg:
62 - if complete:
63 - self._ignored_deps.append(dep)
64 + self._ignored_deps.append(dep)
65 return 1
66
67 if not self._add_pkg(dep_pkg, dep.parent,
68 @@ -2050,8 +2048,6 @@
69 return 1
70 elif pkg.installed and \
71 "deep" not in self.myparams:
72 - if "complete" not in self.myparams:
73 - return 1
74 dep_stack = self._ignored_deps
75
76 self.spinner.update()
77 @@ -2567,7 +2563,7 @@
78
79 if not self.validate_blockers():
80 return False, myfavorites
81 -
82 +
83 # We're true here unless we are missing binaries.
84 return (not missing,myfavorites)
85
86 @@ -3233,9 +3229,17 @@
87 blocker, set()).add(parent)
88 if not self.blocker_parents[blocker]:
89 del self.blocker_parents[blocker]
90 - # Validate blockers that depend on merge order.
91 - if not self.blocker_digraph.empty():
92 +
93 + # This checks whether or not it's possible to resolve blocker
94 + # conflicts that depend on installation order or require
95 + # uninstallation of a currently installed package. Note that
96 + # this can lead to the current method being called recursively
97 + # if changes to the dependency graph are required.
98 + try:
99 self.altlist()
100 + except self._unknown_internal_error:
101 + return False
102 +
103 if self._slot_collision_info:
104 # The user is only notified of a slot collision if there are no
105 # unresolvable blocks.
106 @@ -3267,13 +3271,19 @@
107 mygraph.order.sort(cmp_merge_preference)
108
109 def altlist(self, reversed=False):
110 - if reversed in self._altlist_cache:
111 - return self._altlist_cache[reversed][:]
112 +
113 + while self._serialized_tasks_cache is None:
114 + try:
115 + self._serialized_tasks_cache = self._serialize_tasks()
116 + except self._serialize_tasks_retry:
117 + pass
118 +
119 + retlist = self._serialized_tasks_cache[:]
120 if reversed:
121 - retlist = self.altlist()
122 retlist.reverse()
123 - self._altlist_cache[reversed] = retlist[:]
124 - return retlist
125 + return retlist
126 +
127 + def _serialize_tasks(self):
128 mygraph=self.digraph.copy()
129 # Prune "nomerge" root nodes if nothing depends on them, since
130 # otherwise they slow down merge order calculation. Don't remove
131 @@ -3311,7 +3321,11 @@
132 # correspond to blocker conflicts that could not be
133 # resolved.
134 ignored_uninstall_tasks = set()
135 - blocker_deps = None
136 + have_uninstall_task = False
137 + complete = "complete" in self.myparams
138 + myblocker_parents = self.blocker_parents.copy()
139 + for k, v in myblocker_parents.iteritems():
140 + myblocker_parents[k] = v.copy()
141 asap_nodes = []
142 portage_node = None
143 def get_nodes(**kwargs):
144 @@ -3501,43 +3515,54 @@
145 inst_pkg = self._pkg_cache[
146 ("installed", task.root, task.cpv, "nomerge")]
147
148 - # For packages in the system set, don't take
149 - # any chances. If the conflict can't be resolved
150 - # by a normal upgrade operation then require
151 - # user intervention.
152 - skip = False
153 - try:
154 - for atom in root_config.sets[
155 - "system"].iterAtomsForPackage(task):
156 - skip = True
157 - break
158 - except portage.exception.InvalidDependString:
159 - skip = True
160 - if skip:
161 + if self.digraph.contains(inst_pkg):
162 continue
163
164 - # For packages in the world set, go ahead an uninstall
165 - # when necessary, as long as the atom will be satisfied
166 - # in the final state.
167 - graph_db = self.mydbapi[task.root]
168 - try:
169 - for atom in root_config.sets[
170 - "world"].iterAtomsForPackage(task):
171 - satisfied = False
172 - for cpv in graph_db.match(atom):
173 - if cpv == inst_pkg.cpv and \
174 - inst_pkg in graph_db:
175 - continue
176 - satisfied = True
177 - break
178 - if not satisfied:
179 + if "/" == task.root:
180 + # For packages in the system set, don't take
181 + # any chances. If the conflict can't be resolved
182 + # by a normal replacement operation then abort.
183 + skip = False
184 + try:
185 + for atom in root_config.sets[
186 + "system"].iterAtomsForPackage(task):
187 skip = True
188 break
189 - except portage.exception.InvalidDependString:
190 - skip = True
191 - if skip:
192 - continue
193 + except portage.exception.InvalidDependString:
194 + skip = True
195 + if skip:
196 + continue
197
198 + # Note that the world check isn't always
199 + # necessary since self._complete_graph() will
200 + # add all packages from the system and world sets to the
201 + # graph. This just allows unresolved conflicts to be
202 + # detected as early as possible, which makes it possible
203 + # to avoid calling self._complete_graph() when it is
204 + # unnecessary due to blockers triggering an abortion.
205 + if not complete:
206 + # For packages in the world set, go ahead an uninstall
207 + # when necessary, as long as the atom will be satisfied
208 + # in the final state.
209 + graph_db = self.mydbapi[task.root]
210 + try:
211 + for atom in root_config.sets[
212 + "world"].iterAtomsForPackage(task):
213 + satisfied = False
214 + for cpv in graph_db.match(atom):
215 + if cpv == inst_pkg.cpv and \
216 + inst_pkg in graph_db:
217 + continue
218 + satisfied = True
219 + break
220 + if not satisfied:
221 + skip = True
222 + break
223 + except portage.exception.InvalidDependString:
224 + skip = True
225 + if skip:
226 + continue
227 +
228 # Check the deps of parent nodes to ensure that
229 # the chosen task produces a leaf node. Maybe
230 # this can be optimized some more to make the
231 @@ -3629,6 +3654,7 @@
232 # and uninstallation tasks.
233 uninst_task = None
234 if isinstance(node, Uninstall):
235 + have_uninstall_task = True
236 uninst_task = node
237 else:
238 vardb = self.trees[node.root]["vartree"].dbapi
239 @@ -3654,24 +3680,32 @@
240 unresolved = \
241 self._unresolved_blocker_parents.get(blocker)
242 if unresolved:
243 - self.blocker_parents[blocker] = unresolved
244 + myblocker_parents[blocker] = unresolved
245 else:
246 - del self.blocker_parents[blocker]
247 + del myblocker_parents[blocker]
248
249 if node[-1] != "nomerge":
250 retlist.append(list(node))
251 mygraph.remove(node)
252
253 - if not reversed:
254 - """Blocker validation does not work with reverse mode,
255 - so self.altlist() should first be called with reverse disabled
256 - so that blockers are properly validated."""
257 - self.blocker_digraph = myblockers
258 + for blocker in myblocker_parents:
259 + retlist.append(list(blocker))
260
261 - """ Add any unresolved blocks so that they can be displayed."""
262 - for blocker in self.blocker_parents:
263 - retlist.append(list(blocker))
264 - self._altlist_cache[reversed] = retlist[:]
265 + # If any Uninstall tasks need to be executed in order
266 + # to avoid a conflict, complete the graph with any
267 + # dependencies that may have been initially
268 + # neglected (to ensure that unsafe Uninstall tasks
269 + # are properly identified and blocked from execution).
270 + if have_uninstall_task and \
271 + not complete and \
272 + not myblocker_parents:
273 + self.myparams.add("complete")
274 + if not self._complete_graph():
275 + raise self._unknown_internal_error("")
276 + if not self.validate_blockers():
277 + raise self._unknown_internal_error("")
278 + raise self._serialize_tasks_retry("")
279 +
280 return retlist
281
282 def display(self, mylist, favorites=[], verbosity=None):
283 @@ -4562,6 +4596,22 @@
284 fakedb[myroot].cpv_inject(pkg)
285 self.spinner.update()
286
287 + class _unknown_internal_error(portage.exception.PortageException):
288 + """
289 + Used by the depgraph internally to terminate graph creation.
290 + The specific reason for the failure should have been dumped
291 + to stderr, unfortunately, the exact reason for the failure
292 + may not be known.
293 + """
294 +
295 + class _serialize_tasks_retry(portage.exception.PortageException):
296 + """
297 + This is raised by the _serialize_tasks() method when it needs to
298 + be called again for some reason. The only case that it's currently
299 + used for is when neglected dependencies need to be added to the
300 + graph in order to avoid making a potentially unsafe decision.
301 + """
302 +
303 class _dep_check_composite_db(portage.dbapi):
304 """
305 A dbapi-like interface that is optimized for use in dep_check() calls.
306
307 --
308 gentoo-commits@l.g.o mailing list