1
2
3
4
5
6
7
8
9
10
11
12
13
14 import itertools
15
16 from conary import errors
17 from conary.lib import log
18 from conary.repository import searchsource
19 from conary.repository.resolvemethod import DepResolutionByTroveList, \
20 ResolutionStack, BasicResolutionMethod, DepResolutionByLabelPath, \
21 DepResolutionMethod
22
24
25 - def __init__(self, client, cfg, repos, db):
26 self.client = client
27 self.cfg = cfg
28 self.repos = repos
29 self.db = db
30
37
38 - def resolveDependencies(self, uJob, jobSet, split = False,
39 resolveDeps = True, useRepos = True,
40 resolveSource = None, keepRequired = True,
41 criticalUpdateInfo = None):
42 """
43 Determine and possibly resolve dependency problems.
44 @param uJob: update job we are resolving dependencies for
45 @type uJob: local.database.UpdateJob
46 @param jobSet: jobs that are to be applied
47 @type jobSet: list of job tuples
48 @param split: if True, find an ordering for the jobs in
49 jobSet plus any resolved jobs.
50 @type split: bool
51 @param resolveDeps: If True, try to resolve any dependency problems
52 by modifying the given job.
53 @type resolveDeps: bool
54 @param useRepos: If True, search for dependency solutions in the
55 repository after searching the update job search source.
56 @param keepRequired: If True, the resolver will attempt to remove
57 erase jobs from the job set to resolve dependency problems
58 created by the erasure. If False, those problems will be reported
59 instead.
60 @type keepRequired: bool
61 """
62 searchSource = uJob.getSearchSource()
63 if not isinstance(searchSource, searchsource.AbstractSearchSource):
64 searchSource = searchsource.SearchSource(searchSource,
65 self.cfg.flavor, self.db)
66 if useRepos:
67 defaultSearchSource = self.client.getSearchSource()
68 searchSource = searchsource.stack(searchSource,
69 defaultSearchSource)
70 troveSource = searchSource.getTroveSource()
71
72 ineligible = set()
73
74 check = self.db.getDepStateClass(uJob.getTroveSource())
75
76 (depList, cannotResolve, changeSetList, keepList, ineligible,
77 criticalUpdates) = self.checkDeps(uJob, jobSet, troveSource,
78 findOrdering = split,
79 resolveDeps = resolveDeps,
80 ineligible=ineligible,
81 keepRequired = keepRequired,
82 criticalUpdateInfo = criticalUpdateInfo,
83 check = check)
84
85 if not resolveDeps:
86
87
88 depList = []
89 cannotResolve = []
90
91 if resolveSource is None:
92 resolveSource = searchSource.getResolveMethod()
93 resolveSource.searchLeavesFirst()
94 else:
95 resolveSource.setTroveSource(troveSource)
96
97 suggMap = {}
98
99 if not hasattr(resolveSource, 'filterDependencies'):
100
101 resolveSource.filterDependencies = lambda x: x
102 depList = resolveSource.filterDependencies(depList)
103 while resolveSource.prepareForResolution(depList):
104
105 sugg = resolveSource.resolveDependencies()
106 newTroves = resolveSource.filterSuggestions(depList, sugg, suggMap)
107
108 if not newTroves:
109 continue
110
111 changedJob = self.addUpdates(newTroves, uJob, jobSet, ineligible,
112 keepList, troveSource, resolveSource)
113 if not changedJob:
114 continue
115
116 (depList, cannotResolve, changeSetList, newKeepList,
117 ineligible, criticalUpdates) = self.checkDeps(uJob, jobSet,
118 uJob.getTroveSource(),
119 findOrdering = True,
120 resolveDeps = True,
121 ineligible = ineligible,
122 keepRequired = keepRequired,
123 criticalUpdateInfo = criticalUpdateInfo,
124 check = check)
125 keepList.extend(newKeepList)
126 depList = resolveSource.filterDependencies(depList)
127
128 check.done()
129
130 if criticalUpdateInfo is None:
131
132 return (depList, suggMap, cannotResolve, changeSetList, keepList)
133 return (depList, suggMap, cannotResolve, changeSetList, keepList,
134 criticalUpdates)
135
136 - def addUpdates(self, troves, uJob, jobSet, ineligible, keepList,
137 troveSource, resolveSource):
138 """
139 Add the given dep resolution solutions to the current jobSet.
140 """
141
142
143
144 beingRemoved = set((x[0], x[1][0], x[1][1]) for x in
145 jobSet if x[1][0] is not None )
146 beingInstalled = set((x[0], x[2][0], x[2][1]) for x in
147 jobSet if x[2][0] is not None )
148
149
150
151
152 if self.cfg.autoResolvePackages:
153 packageJobs = self.addPackagesForComponents(troves,
154 troveSource,
155 beingInstalled)
156 troves.update(packageJobs)
157
158 newJob = self.client._updateChangeSet(troves, uJob,
159 keepExisting = False,
160 recurse = False,
161 ineligible = beingRemoved,
162 checkPrimaryPins = True)
163 assert(not (newJob & jobSet))
164 newJob = resolveSource.filterResolutionsPostUpdate(self.db, newJob,
165 troveSource)
166 if not newJob:
167
168
169
170 return False
171
172 jobSet.update(newJob)
173 return True
174
175 - def checkDeps(self, uJob, jobSet, trvSrc, findOrdering,
176 resolveDeps, ineligible, keepRequired = True,
177 criticalUpdateInfo = None, check = None):
178 """
179 Given a jobSet, use its dependencies to determine an
180 ordering, resolve problems with jobs that have difficult
181 dependency problems immediately,
182 and determine any missing dependencies.
183 """
184 assert(check)
185
186 keepList = []
187
188 while True:
189 linkedJobs = self.client._findOverlappingJobs(jobSet,
190 uJob.getTroveSource())
191 criticalJobs, finalJobs, criticalOnly = self._findCriticalJobInfo(
192 jobSet,
193 criticalUpdateInfo)
194 (depList, cannotResolve, changeSetList, criticalUpdates) = \
195 check.depCheck(jobSet,
196 findOrdering = findOrdering,
197 linkedJobs = linkedJobs,
198 criticalJobs = criticalJobs,
199 finalJobs = finalJobs,
200 criticalOnly = criticalOnly)
201
202 if not resolveDeps or not cannotResolve:
203 break
204
205
206
207
208
209
210
211
212 changeMade = False
213
214
215
216 log.debug('Update breaks dependencies!')
217 for reqInfo, depSet, provInfo in sorted(cannotResolve,
218 key=lambda x:x[0][0]):
219 depSet = '\n '.join(str(depSet).split('\n'))
220 msg = (
221 'Broken dependency: (dep needed by the system but being removed):\n'
222 ' %s=%s\n'
223 ' Requires:\n'
224 ' %s\n'
225 ' Provided by removed or updated packages: %s')
226 args = (reqInfo[0], reqInfo[1].trailingRevision(), depSet,
227 ', '.join(x[0] for x in provInfo))
228 log.debug(msg, *args)
229 if self.cfg.resolveLevel > 1:
230 cannotResolve, newJobSet = self.resolveEraseByUpdating(
231 trvSrc,
232 cannotResolve,
233 uJob, jobSet,
234 ineligible,
235 check)
236 if newJobSet:
237 jobSet |= newJobSet
238 changeMade = True
239
240 if not changeMade and cannotResolve and keepRequired:
241
242 cannotResolve, newKeepList = self.resolveEraseByKeeping(
243 trvSrc,
244 cannotResolve,
245 uJob, jobSet)
246 if newKeepList:
247 changeMade = True
248 keepList += newKeepList
249
250 if not changeMade:
251 break
252
253 return (depList, cannotResolve, changeSetList, keepList, ineligible,
254 criticalUpdates)
255
258 """
259 Attempt to resolve broken erase dependencies by updating the
260 package that has the dependency on the trove that is being
261 erased.
262 @param ineligible: Ineligible troves are troves that were
263 involved in resolveEraseByUpdating before.
264 """
265
266 oldIdx = {}
267 newIdx = {}
268 for job in jobSet:
269 if job[1][0] is not None:
270 oldIdx[(job[0], job[1][0], job[1][1])] = job
271 if job[2][0] is not None:
272 newIdx[(job[0], job[2][0], job[2][1])] = job
273
274 potentialUpdateList = []
275
276 for resolveInfo in cannotResolve:
277 (reqInfo, depSet, provInfoList) = resolveInfo
278
279
280
281 if reqInfo in newIdx:
282
283
284 continue
285
286 found = False
287 for provInfo in provInfoList:
288 if provInfo in ineligible:
289
290
291
292
293
294 found = True
295 break
296 if found:
297 continue
298
299 for provInfo in provInfoList:
300 if provInfo not in oldIdx:
301 continue
302
303 job = oldIdx[provInfo]
304
305 if not job[2][0]:
306
307
308 continue
309
310
311
312
313 ineligible.add(reqInfo)
314 potentialUpdateList.append((reqInfo, depSet, provInfoList))
315 break
316
317 if not potentialUpdateList:
318 return cannotResolve, set()
319
320 log.debug('Attempting to update the following packages in order to '
321 'remove their dependency on something being erased:\n %s', ('\n '.join(sorted(x[0][0].split(':')[0] for x in potentialUpdateList))))
322
323
324
325 updateJobs = [ (x[0][0].split(':')[0],
326 (None,