Package yum :: Module depsolve
[hide private]
[frames] | no frames]

Source Code for Module yum.depsolve

   1  #!/usr/bin/python -t 
   2  # This program is free software; you can redistribute it and/or modify 
   3  # it under the terms of the GNU General Public License as published by 
   4  # the Free Software Foundation; either version 2 of the License, or 
   5  # (at your option) any later version. 
   6  # 
   7  # This program is distributed in the hope that it will be useful, 
   8  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
   9  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  10  # GNU Library General Public License for more details. 
  11  # 
  12  # You should have received a copy of the GNU General Public License 
  13  # along with this program; if not, write to the Free Software 
  14  # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
  15  # Copyright 2005 Duke University  
  16   
  17  """ 
  18  Dependency resolution module for yum. 
  19  """ 
  20   
  21  import os.path 
  22  import types 
  23  import logging 
  24   
  25  import rpmUtils.transaction 
  26  import rpmUtils.miscutils 
  27  from rpmUtils.arch import archDifference, canCoinstall 
  28  import misc 
  29  from misc import unique, version_tuple_to_string 
  30  import rpm 
  31   
  32  from packageSack import ListPackageSack 
  33  from constants import * 
  34  import packages 
  35  import logginglevels 
  36  import Errors 
  37  import warnings 
  38  warnings.simplefilter("ignore", Errors.YumFutureDeprecationWarning) 
  39   
  40  from yum import _ 
  41   
  42  try: 
  43      assert max(2, 4) == 4 
  44  except: 
45 # Python-2.4.x doesn't have min/max ... *sigh* 46 - def min(x, *args):
47 for y in args: 48 if x > y: x = y 49 return x
50 - def max(x, *args):
51 for y in args: 52 if x < y: x = y 53 return x
54 flags = {"GT": rpm.RPMSENSE_GREATER, 55 "GE": rpm.RPMSENSE_EQUAL | rpm.RPMSENSE_GREATER, 56 "LT": rpm.RPMSENSE_LESS, 57 "LE": rpm.RPMSENSE_LESS | rpm.RPMSENSE_EQUAL, 58 "EQ": rpm.RPMSENSE_EQUAL, 59 None: 0 }
60 61 -class Depsolve(object):
62 63 """ 64 Dependency resolving class. 65 """ 66
67 - def __init__(self):
68 packages.base = self 69 self._ts = None 70 self._tsInfo = None 71 self.dsCallback = None 72 self.logger = logging.getLogger("yum.Depsolve") 73 self.verbose_logger = logging.getLogger("yum.verbose.Depsolve") 74 75 self.path = [] 76 self.loops = [] 77 78 self.installedFileRequires = None 79 self.installedUnresolvedFileRequires = None
80
81 - def doTsSetup(self):
82 warnings.warn(_('doTsSetup() will go away in a future version of Yum.\n'), 83 Errors.YumFutureDeprecationWarning, stacklevel=2) 84 return self._getTs()
85
86 - def _getTs(self, remove_only=False):
87 """setup all the transaction set storage items we'll need 88 This can't happen in __init__ b/c we don't know our installroot 89 yet""" 90 91 if self._tsInfo != None and self._ts != None: 92 if not remove_only and self._tsInfo.pkgSack is None: 93 self._tsInfo.setDatabases(self.rpmdb, self.pkgSack) 94 return 95 96 if not self.conf.installroot: 97 raise Errors.YumBaseError, _('Setting up TransactionSets before config class is up') 98 99 self._getTsInfo(remove_only) 100 self.initActionTs()
101
102 - def _getTsInfo(self, remove_only=False):
103 """ remove_only param. says if we are going to do _only_ remove(s) in 104 the transaction. If so we don't need to setup the remote repos. """ 105 if self._tsInfo is None: 106 self._tsInfo = self._transactionDataFactory() 107 if remove_only: 108 pkgSack = None 109 else: 110 pkgSack = self.pkgSack 111 self._tsInfo.setDatabases(self.rpmdb, pkgSack) 112 self._tsInfo.installonlypkgs = self.conf.installonlypkgs # this kinda sucks 113 # this REALLY sucks, sadly (needed for group conditionals) 114 self._tsInfo.install_method = self.install 115 self._tsInfo.update_method = self.update 116 self._tsInfo.remove_method = self.remove 117 return self._tsInfo
118
119 - def _setTsInfo(self, value):
120 self._tsInfo = value
121
122 - def _delTsInfo(self):
123 self._tsInfo = None
124
125 - def _getActionTs(self):
126 if not self._ts: 127 self.initActionTs() 128 return self._ts
129 130
131 - def initActionTs(self):
132 """sets up the ts we'll use for all the work""" 133 134 self._ts = rpmUtils.transaction.TransactionWrapper(self.conf.installroot) 135 ts_flags_to_rpm = { 'noscripts': rpm.RPMTRANS_FLAG_NOSCRIPTS, 136 'notriggers': rpm.RPMTRANS_FLAG_NOTRIGGERS, 137 'nodocs': rpm.RPMTRANS_FLAG_NODOCS, 138 'test': rpm.RPMTRANS_FLAG_TEST, 139 'justdb': rpm.RPMTRANS_FLAG_JUSTDB, 140 'repackage': rpm.RPMTRANS_FLAG_REPACKAGE} 141 142 self._ts.setFlags(0) # reset everything. 143 144 for flag in self.conf.tsflags: 145 if flag in ts_flags_to_rpm: 146 self._ts.addTsFlag(ts_flags_to_rpm[flag]) 147 else: 148 self.logger.critical(_('Invalid tsflag in config file: %s'), flag) 149 150 probfilter = 0 151 for flag in self.tsInfo.probFilterFlags: 152 probfilter |= flag 153 self._ts.setProbFilter(probfilter)
154
155 - def whatProvides(self, name, flags, version):
156 """searches the packageSacks for what provides the arguments 157 returns a ListPackageSack of providing packages, possibly empty""" 158 159 self.verbose_logger.log(logginglevels.DEBUG_1, _('Searching pkgSack for dep: %s'), 160 name) 161 defSack = ListPackageSack(self.pkgSack.searchProvides((name, flags, version))) 162 return defSack
163
164 - def allowedMultipleInstalls(self, po):
165 """takes a packageObject, returns 1 or 0 depending on if the package 166 should/can be installed multiple times with different vers 167 like kernels and kernel modules, for example""" 168 169 iopkgs = set(self.conf.installonlypkgs) 170 if po.name in iopkgs: 171 return True 172 173 for prov in po.provides_names: 174 if prov in iopkgs: 175 return True 176 177 return False
178
179 - def populateTs(self, test=0, keepold=1):
180 """take transactionData class and populate transaction set""" 181 182 if self.dsCallback: self.dsCallback.transactionPopulation() 183 ts_elem = {} 184 185 if self.ts.ts is None: 186 self.initActionTs() 187 188 if keepold: 189 for te in self.ts: 190 epoch = te.E() 191 if epoch is None: 192 epoch = '0' 193 pkginfo = (te.N(), te.A(), epoch, te.V(), te.R()) 194 if te.Type() == 1: 195 mode = 'i' 196 elif te.Type() == 2: 197 mode = 'e' 198 199 ts_elem[(pkginfo, mode)] = 1 200 201 for txmbr in self.tsInfo.getMembers(): 202 self.verbose_logger.log(logginglevels.DEBUG_3, _('Member: %s'), txmbr) 203 if txmbr.ts_state in ['u', 'i']: 204 if (txmbr.pkgtup, 'i') in ts_elem: 205 continue 206 rpmfile = txmbr.po.localPkg() 207 if os.path.exists(rpmfile): 208 hdr = txmbr.po.returnHeaderFromPackage() 209 else: 210 self.downloadHeader(txmbr.po) 211 hdr = txmbr.po.returnLocalHeader() 212 213 if txmbr.ts_state == 'u': 214 if self.allowedMultipleInstalls(txmbr.po): 215 self.verbose_logger.log(logginglevels.DEBUG_2, 216 _('%s converted to install'), txmbr.po) 217 txmbr.ts_state = 'i' 218 txmbr.output_state = TS_INSTALL 219 220 221 self.ts.addInstall(hdr, (hdr, rpmfile), txmbr.ts_state) 222 self.verbose_logger.log(logginglevels.DEBUG_1, 223 _('Adding Package %s in mode %s'), txmbr.po, txmbr.ts_state) 224 if self.dsCallback: 225 self.dsCallback.pkgAdded(txmbr.pkgtup, txmbr.ts_state) 226 227 elif txmbr.ts_state in ['e']: 228 if (txmbr.pkgtup, txmbr.ts_state) in ts_elem: 229 continue 230 self.ts.addErase(txmbr.po.idx) 231 if self.dsCallback: self.dsCallback.pkgAdded(txmbr.pkgtup, 'e') 232 self.verbose_logger.log(logginglevels.DEBUG_1, 233 _('Removing Package %s'), txmbr.po)
234
235 - def _dscb_procReq(self, po, niceformatneed):
236 """ Call the callback for processing requires, call the nicest one 237 available. """ 238 if not self.dsCallback: 239 return 240 241 if hasattr(self.dsCallback, 'procReqPo'): 242 self.dsCallback.procReqPo(po, niceformatneed) 243 else: 244 self.dsCallback.procReq(po.name, niceformatneed)
245
246 - def _processReq(self, po, requirement):
247 """processes a Requires dep from the resolveDeps functions, returns a tuple 248 of (CheckDeps, missingdep, conflicts, errors) the last item is an array 249 of error messages""" 250 251 errormsgs = [] 252 253 needname, flags, needversion = requirement 254 niceformatneed = rpmUtils.miscutils.formatRequire(needname, needversion, flags) 255 self.verbose_logger.log(logginglevels.DEBUG_1, _('%s requires: %s'), po, niceformatneed) 256 self._dscb_procReq(po, niceformatneed) 257 258 try: 259 if po.repo.id != "installed": 260 CheckDeps, missingdep = self._requiringFromTransaction(po, requirement, errormsgs) 261 else: 262 CheckDeps, missingdep = self._requiringFromInstalled(po, requirement, errormsgs) 263 264 # Check packages with problems 265 if missingdep: 266 self.po_with_problems.add((po,self._working_po,errormsgs[-1])) 267 268 269 except Errors.DepError,e: 270 # FIXME: This is a hack, it don't solve the problem 271 # of tries to update to a package the have been removed from the 272 # pkgSack because of dep problems. 273 # The real solution is to remove the package from the updates, when 274 # it is remove from the pkgSack 275 self.po_with_problems.add((po,self._working_po,str(e))) 276 CheckDeps = 1 277 missingdep = 0 278 279 return (CheckDeps, missingdep, errormsgs)
280 281 @staticmethod
282 - def _prco_req_nfv2req(rn, rf, rv):
283 return (rn, flags[rf], version_tuple_to_string(rv))
284
285 - def _prco_req2req(self, req):
286 return self._prco_req_nfv2req(req[0], req[1], req[2])
287
288 - def _err_missing_requires(self, reqPo, reqTup):
289 if hasattr(self.dsCallback, 'format_missing_requires'): 290 msg = self.dsCallback.format_missing_requires(reqPo, reqTup) 291 if msg is not None: # PK 292 return self.dsCallback.format_missing_requires(reqPo, reqTup) 293 (needname, needflags, needversion) = reqTup 294 ui_req = rpmUtils.miscutils.formatRequire(needname, needversion, 295 needflags) 296 return _('%s requires %s') % (reqPo, ui_req)
297
298 - def _requiringFromInstalled(self, requiringPo, requirement, errorlist):
299 """processes the dependency resolution for a dep where the requiring 300 package is installed""" 301 302 checkdeps = 0 303 missingdep = 0 304 305 if self.tsInfo.getMembersWithState(requiringPo.pkgtup, TS_REMOVE_STATES): 306 return checkdeps, missingdep 307 308 name, arch, epoch, ver, rel = requiringPo.pkgtup 309 310 needname, needflags, needversion = requirement 311 niceformatneed = rpmUtils.miscutils.formatRequire(needname, needversion, needflags) 312 313 314 # we must first find out why the requirement is no longer there 315 # we must find out what provides/provided it from the rpmdb (if anything) 316 # then check to see if that thing is being acted upon by the transaction set 317 # if it is then we need to find out what is being done to it and act accordingly 318 needmode = None # mode in the transaction of the needed pkg (if any) 319 needpo = None 320 providers = [] 321 322 if (needname, needflags, needversion) in self.cheaterlookup: 323 self.verbose_logger.log(logginglevels.DEBUG_2, _('Needed Require has already been looked up, cheating')) 324 cheater_po = self.cheaterlookup[(needname, needflags, needversion)] 325 providers = [cheater_po] 326 327 elif self.rpmdb.contains(name=needname): 328 txmbrs = self.tsInfo.matchNaevr(name=needname) 329 for txmbr in txmbrs: 330 providers.append(txmbr.po) 331 332 else: 333 self.verbose_logger.log(logginglevels.DEBUG_2, _('Needed Require is not a package name. Looking up: %s'), niceformatneed) 334 providers = self.rpmdb.getProvides(needname, needflags, needversion) 335 336 for inst_po in providers: 337 inst_str = '%s.%s %s:%s-%s' % inst_po.pkgtup 338 (i_n, i_a, i_e, i_v, i_r) = inst_po.pkgtup 339 self.verbose_logger.log(logginglevels.DEBUG_2, 340 _('Potential Provider: %s'), inst_str) 341 thismode = self.tsInfo.getMode(name=i_n, arch=i_a, 342 epoch=i_e, ver=i_v, rel=i_r) 343 344 if thismode is None and i_n in self.conf.exactarchlist: 345 # check for mode by the same name+arch 346 thismode = self.tsInfo.getMode(name=i_n, arch=i_a) 347 348 if thismode is None and i_n not in self.conf.exactarchlist: 349 # check for mode by just the name 350 thismode = self.tsInfo.getMode(name=i_n) 351 352 # if this package is being obsoleted, it's just like if it's 353 # being upgraded as far as checking for other providers 354 if thismode is None: 355 if filter(lambda x: x.obsoleted_by, 356 self.tsInfo.matchNaevr(i_n, i_a, i_e, i_v, i_r)): 357 thismode = 'u' 358 359 if thismode is not None: 360 needmode = thismode 361 362 self.cheaterlookup[(needname, needflags, needversion)] = inst_po 363 self.verbose_logger.log(logginglevels.DEBUG_2, _('Mode is %s for provider of %s: %s'), 364 needmode, niceformatneed, inst_str) 365 break 366 367 self.verbose_logger.log(logginglevels.DEBUG_2, _('Mode for pkg providing %s: %s'), 368 niceformatneed, needmode) 369 370 if needmode in ['e']: 371 self.verbose_logger.log(logginglevels.DEBUG_2, _('TSINFO: %s package requiring %s marked as erase'), 372 requiringPo, needname) 373 txmbr = self.tsInfo.addErase(requiringPo) 374 txmbr.setAsDep(po=inst_po) 375 checkdeps = 1 376 377 if needmode in ['i', 'u']: 378 length = len(self.tsInfo) 379 self.update(name=name, epoch=epoch, version=ver, release=rel, 380 requiringPo=requiringPo) 381 txmbrs = self.tsInfo.getMembersWithState(requiringPo.pkgtup, TS_REMOVE_STATES) 382 if len(self.tsInfo) != length and txmbrs: 383 if txmbrs[0].output_state == TS_OBSOLETED: 384 self.verbose_logger.log(logginglevels.DEBUG_2, _('TSINFO: Obsoleting %s with %s to resolve dep.'), 385 requiringPo, txmbrs[0].obsoleted_by[0]) 386 else: 387 self.verbose_logger.log(logginglevels.DEBUG_2, _('TSINFO: Updating %s to resolve dep.'), requiringPo) 388 # If the requirement is still there, try and solve it again 389 # so we don't lose it 390 for pkg in txmbrs[0].updated_by: 391 if requirement in map(self._prco_req2req, pkg.returnPrco('requires')): 392 return True, missingdep + self._requiringFromTransaction(pkg, requirement, errorlist)[1] 393 checkdeps = True 394 return checkdeps, missingdep 395 self.verbose_logger.log(logginglevels.DEBUG_2, _('Cannot find an update path for dep for: %s'), niceformatneed) 396 return self._requiringFromTransaction(requiringPo, requirement, errorlist) 397 398 399 if needmode is None: 400 reqpkg = (name, ver, rel, None) 401 if self.pkgSack is None: 402 return self._requiringFromTransaction(requiringPo, requirement, errorlist) 403 else: 404 msg = self._err_missing_requires(requiringPo, requirement) 405 self.verbose_logger.log(logginglevels.DEBUG_2, msg) 406 checkdeps = 0 407 missingdep = 1 408 errorlist.append(msg) 409 410 return checkdeps, missingdep
411
412 - def _quickWhatProvides(self, name, flags, version):
413 if self._last_req is None: 414 return False 415 416 if flags == 0: 417 flags = None 418 if type(version) in (types.StringType, types.NoneType, types.UnicodeType): 419 (r_e, r_v, r_r) = rpmUtils.miscutils.stringToVersion(version) 420 elif type(version) in (types.TupleType, types.ListType): # would this ever be a ListType? 421 (r_e, r_v, r_r) = version 422 423 # Quick lookup, lots of reqs for one pkg: 424 po = self._last_req 425 if po.checkPrco('provides', (name, flags, (r_e, r_v, r_r))): 426 self.verbose_logger.debug(_('Quick matched %s to require for %s'), po, name) 427 return True 428 return False
429
430 - def _requiringFromTransaction(self, requiringPo, requirement, errorlist):
431 """processes the dependency resolution for a dep where requiring 432 package is in the transaction set""" 433 434 (name, arch, epoch, version, release) = requiringPo.pkgtup 435 (needname, needflags, needversion) = requirement 436 checkdeps = 0 437 missingdep = 0 438 upgraded = {} 439 440 #~ - if it's not available from some repository: 441 #~ - mark as unresolveable. 442 # 443 #~ - if it's available from some repo: 444 #~ - if there is an another version of the package currently installed then 445 # - if the other version is marked in the transaction set 446 # - if it's marked as erase 447 # - mark the dep as unresolveable 448 449 # - if it's marked as update or install 450 # - check if the version for this requirement: 451 # - if it is higher 452 # - mark this version to be updated/installed 453 # - remove the other version from the transaction set 454 # - tell the transaction set to be rebuilt 455 # - if it is lower 456 # - mark the dep as unresolveable 457 # - if they are the same 458 # - be confused but continue 459 460 if self._quickWhatProvides(needname, needflags, needversion): 461 return checkdeps, missingdep 462 463 provSack = self.whatProvides(needname, needflags, needversion) 464 # get rid of things that are already in the rpmdb - b/c it's pointless to use them here 465 466 for pkg in provSack.returnPackages(): 467 if self.rpmdb.contains(po=pkg): # is it already installed? 468 self.verbose_logger.log(logginglevels.DEBUG_2, _('%s is in providing packages but it is already installed, removing.'), pkg) 469 provSack.delPackage(pkg) 470 continue 471 472 # we need to check to see, if we have anything similar to it (name-wise) 473 # installed or in the ts, and this isn't a package that allows multiple installs 474 # then if it's newer, fine - continue on, if not, then we're unresolveable 475 # cite it and exit 476 477 tspkgs = [] 478 if not self.allowedMultipleInstalls(pkg): 479 # from ts 480 tspkgs = self.tsInfo.matchNaevr(name=pkg.name) 481 for tspkg in tspkgs: 482 if not canCoinstall(pkg.arch, tspkg.po.arch): # a comparable arch 483 if tspkg.po.verGT(pkg): 484 msg = _('Potential resolving package %s has newer instance in ts.') % pkg 485 self.verbose_logger.log(logginglevels.DEBUG_2, msg) 486 provSack.delPackage(pkg) 487 continue 488 elif tspkg.po.verLT(pkg): 489 upgraded.setdefault(pkg.pkgtup, []).append(tspkg.pkgtup) 490 491 # from rpmdb 492 dbpkgs = self.rpmdb.searchNevra(name=pkg.name) 493 for dbpkg in dbpkgs: 494 if dbpkg.verGT(pkg) and not canCoinstall(pkg.arch, dbpkg.arch): 495 msg = _('Potential resolving package %s has newer instance installed.') % pkg 496 self.verbose_logger.log(logginglevels.DEBUG_2, msg) 497 provSack.delPackage(pkg) 498 continue 499 500 if len(provSack) == 0: # unresolveable 501 missingdep = 1 502 msg = self._err_missing_requires(requiringPo, requirement) 503 errorlist.append(msg) 504 return checkdeps, missingdep 505 506 # iterate the provSack briefly, if we find the package is already in the 507 # tsInfo then just skip this run 508 for pkg in provSack.returnPackages(): 509 (n,a,e,v,r) = pkg.pkgtup 510 pkgmode = self.tsInfo.getMode(name=n, arch=a, epoch=e, ver=v, rel=r) 511 if pkgmode in ['i', 'u']: 512 self.verbose_logger.log(logginglevels.DEBUG_2, 513 _('%s already in ts, skipping this one'), pkg) 514 # FIXME: Remove this line, if it is not needed ? 515 # checkdeps = 1 516 self._last_req = pkg 517 return checkdeps, missingdep 518 519 # find the best one 520 521 # try updating the already install pkgs 522 for pkg in provSack.returnNewestByName(): 523 results = self.update(requiringPo=requiringPo, name=pkg.name, 524 epoch=pkg.epoch, version=pkg.version, 525 rel=pkg.rel) 526 for txmbr in results: 527 if pkg == txmbr.po: 528 checkdeps = True 529 self._last_req = pkg 530 return checkdeps, missingdep 531 532 pkgs = provSack.returnPackages() 533 if len(pkgs) == 1: # Minor opt. 534 best = pkgs[0] 535 else: 536 # Always do compare providers for multiple pkgs, it deals with 537 # newest etc. ... so no need to do NewestNameArch() ... and it 538 # stops compare_providers from being clever. 539 pkgresults = self._compare_providers(pkgs, requiringPo) 540 best = pkgresults[0][0] 541 542 if self.rpmdb.contains(po=best): # is it already installed? 543 missingdep = 1 544 checkdeps = 0 545 msg = self._err_missing_requires(requiringPo, requirement) 546 errorlist.append(msg) 547 return checkdeps, missingdep 548 549 550 551 # FIXME - why can't we look up in the transaction set for the requiringPkg 552 # and know what needs it that way and provide a more sensible dep structure in the txmbr 553 inst = self.rpmdb.searchNevra(name=best.name, arch=best.arch) 554 if len(inst) > 0: 555 self.verbose_logger.debug(_('TSINFO: Marking %s as update for %s') %(best, 556 requiringPo)) 557 # FIXME: we should probably handle updating multiple packages... 558 txmbr = self.tsInfo.addUpdate(best, inst[0]) 559 txmbr.setAsDep(po=requiringPo) 560 txmbr.reason = "dep" 561 self._last_req = best 562 else: 563 self.verbose_logger.debug(_('TSINFO: Marking %s as install for %s'), best, 564 requiringPo) 565 # FIXME: Don't we want .install() here, so obsoletes get done? 566 txmbr = self.tsInfo.addInstall(best) 567 txmbr.setAsDep(po=requiringPo) 568 txmbr.reason = "dep" 569 self._last_req = best 570 571 # if we had other packages with this name.arch that we found 572 # before, they're not going to be installed anymore, so we 573 # should mark them to be re-checked 574 if best.pkgtup in upgraded: 575 map(self.tsInfo.remove, upgraded[best.pkgtup]) 576 577 checkdeps = 1 578 579 return checkdeps, missingdep
580
581 - def _dscb_procConflict(self, po, niceformatneed):
582 """ Call the callback for processing requires, call the nicest one 583 available. """ 584 if not self.dsCallback: 585 return 586 587 if hasattr(self.dsCallback, 'procConflictPo'): 588 self.dsCallback.procConflictPo(po, niceformatneed) 589 else: 590 self.dsCallback.procConflict(po.name, niceformatneed)
591
592 - def _processConflict(self, po, conflict, conflicting_po):
593 """processes a Conflict dep from the resolveDeps() method""" 594 595 CheckDeps = True 596 errormsgs = [] 597 598 needname, flags, needversion = conflict 599 (name, arch, epoch, ver, rel) = po.pkgtup 600 601 niceformatneed = rpmUtils.miscutils.formatRequire(needname, needversion, flags) 602 self._dscb_procConflict(po, niceformatneed) 603 604 length = len(self.tsInfo) 605 if flags & rpm.RPMSENSE_LESS: 606 self.update(name=conflicting_po.name) 607 txmbrs = self.tsInfo.getMembersWithState(conflicting_po.pkgtup, TS_REMOVE_STATES) 608 if len(self.tsInfo) != length and txmbrs: 609 return CheckDeps, errormsgs 610 elif flags & rpm.RPMSENSE_GREATER: 611 self.update(name=name) 612 txmbrs = self.tsInfo.getMembersWithState(po.pkgtup, TS_REMOVE_STATES) 613 if len(self.tsInfo) != length and txmbrs: 614 return CheckDeps, errormsgs 615 616 self.update(name=conflicting_po.name) 617 txmbrs = self.tsInfo.getMembersWithState(conflicting_po.pkgtup, TS_REMOVE_STATES) 618 if len(self.tsInfo) != length and txmbrs: 619 return CheckDeps, errormsgs 620 self.update(name=name) 621 txmbrs = self.tsInfo.getMembersWithState(po.pkgtup, TS_REMOVE_STATES) 622 if len(self.tsInfo) != length and txmbrs: 623 return CheckDeps, errormsgs 624 625 msg = '%s conflicts with %s' % (name, conflicting_po.name) 626 errormsgs.append(msg) 627 self.verbose_logger.log(logginglevels.DEBUG_1, msg) 628 CheckDeps = False 629 self.po_with_problems.add((po,None,errormsgs[-1])) 630 return CheckDeps, errormsgs
631
632 - def _undoDepInstalls(self):
633 # clean up after ourselves in the case of failures 634 for txmbr in self.tsInfo: 635 if txmbr.isDep: 636 self.tsInfo.remove(txmbr.pkgtup)
637
638 - def prof_resolveDeps(self):
639 fn = "anaconda.prof.0" 640 import hotshot, hotshot.stats 641 prof = hotshot.Profile(fn) 642 rc = prof.runcall(self.resolveDeps) 643 prof.close() 644 print "done running depcheck" 645 stats = hotshot.stats.load(fn) 646 stats.strip_dirs() 647 stats.sort_stats('time', 'calls') 648 stats.print_stats(20) 649 return rc
650
651 - def cprof_resolveDeps(self):
652 import cProfile, pstats 653 prof = cProfile.Profile() 654 rc = prof.runcall(self.resolveDeps) 655 prof.dump_stats("yumprof") 656 print "done running depcheck" 657 658 p = pstats.Stats('yumprof') 659 p.strip_dirs() 660 p.sort_stats('time') 661 p.print_stats(20) 662 return rc
663
664 - def resolveDeps(self, full_check=True):
665 666 if not len(self.tsInfo): 667 return (0, [_('Success - empty transaction')]) 668 669 self.po_with_problems = set() 670 self._working_po = None 671 self._last_req = None 672 self.tsInfo.resetResolved(hard=False) 673 674 CheckDeps = True 675 CheckRemoves = full_check 676 CheckInstalls = full_check 677 678 missingdep = 0 679 errors = [] 680 681 if self.dsCallback: self.dsCallback.start() 682 683 while True: 684 685 CheckDeps = True 686 687 # check Requires 688 while CheckDeps: 689 self.cheaterlookup = {} 690 if self.dsCallback: self.dsCallback.tscheck() 691 CheckDeps, checkinstalls, checkremoves, missing = self._resolveRequires(errors) 692 CheckInstalls |= checkinstalls 693 CheckRemoves |= checkremoves 694 695 696 # check global FileRequires 697 if CheckRemoves: 698 CheckRemoves = False 699 for po, dep in self._checkFileRequires(): 700 (checkdep, missing, errormsgs) = self._processReq(po, dep) 701 CheckDeps |= checkdep 702 errors += errormsgs 703 704 if CheckDeps: 705 if self.dsCallback: self.dsCallback.restartLoop() 706 self.verbose_logger.log(logginglevels.DEBUG_1, _('Restarting Loop')) 707 continue 708 709 # check Conflicts 710 if CheckInstalls: 711 CheckInstalls = False 712 for conflict in self._checkConflicts(): 713 (checkdep, errormsgs) = self._processConflict(*conflict) 714 CheckDeps |= checkdep 715 errors += errormsgs 716 if checkdep: 717 break # The next conflict might be the same pkg 718 719 if CheckDeps: 720 if self.dsCallback: self.dsCallback.restartLoop() 721 self.verbose_logger.log(logginglevels.DEBUG_1, _('Restarting Loop')) 722 continue 723 724 break 725 726 # FIXME: this doesn't belong here at all... 727 for txmbr in self.tsInfo.getMembers(): 728 if self.allowedMultipleInstalls(txmbr.po) and \ 729 txmbr.ts_state == 'u': 730 self.verbose_logger.log(logginglevels.DEBUG_2, 731 _('%s converted to install'), 732 txmbr.po) 733 txmbr.ts_state = 'i' 734 txmbr.output_state = TS_INSTALL 735 736 if self.dsCallback: self.dsCallback.end() 737 self.verbose_logger.log(logginglevels.DEBUG_1, _('Dependency Process ending')) 738 739 self.tsInfo.changed = False 740 if len(errors) > 0: 741 errors = unique(errors) 742 # We immediately display this in cli, so don't show it twice. 743 # Plus skip-broken can get here N times. Might be worth keeping 744 # around for debugging? 745 done = set() # Same as the unique above 746 for po,wpo,err in self.po_with_problems: 747 if (po,err) in done: 748 continue 749 done.add((po, err)) 750 self.verbose_logger.log(logginglevels.DEBUG_4, 751 _("%s from %s has depsolving problems") % (po, po.repoid)) 752 err = err.replace('\n', '\n --> ') 753 self.verbose_logger.log(logginglevels.DEBUG_4," --> %s" % err) 754 return (1, errors) 755 756 if len(self.tsInfo) > 0: 757 if not len(self.tsInfo): 758 return (0, [_('Success - empty transaction')]) 759 return (2, [_('Success - deps resolved')])
760
761 - def _resolveRequires(self, errors):
762 any_missing = False 763 CheckDeps = False 764 CheckInstalls = False 765 CheckRemoves = False 766 # we need to check the opposite of install and remove for regular 767 # tsInfo members vs removed members 768 for txmbr in self.tsInfo.getUnresolvedMembers(): 769 770 if self.dsCallback and txmbr.ts_state: 771 self.dsCallback.pkgAdded(txmbr.pkgtup, txmbr.ts_state) 772 self.verbose_logger.log(logginglevels.DEBUG_2, 773 _("Checking deps for %s") %(txmbr,)) 774 775 # store the primary po we currently are working on 776 # so we can store it in self.po_with_problems. 777 # it is useful when an update is breaking an require of an installed package 778 # then we want to know who is causing the problem, not just who is having the problem. 779 if not txmbr.updates and txmbr.relatedto: 780 self._working_po = txmbr.relatedto[0][0] 781 else: 782 self._working_po = txmbr.po 783 784 if (txmbr.output_state in TS_INSTALL_STATES) == (txmbr.po.state != None): 785 thisneeds = self._checkInstall(txmbr) 786 CheckInstalls = True 787 else: 788 thisneeds = self._checkRemove(txmbr) 789 CheckRemoves = True 790 791 missing_in_pkg = False 792 for po, dep in thisneeds: 793 (checkdep, missing, errormsgs) = self._processReq(po, dep) 794 CheckDeps |= checkdep 795 errors += errormsgs 796 missing_in_pkg |= missing 797 798 if not missing_in_pkg: 799 self.tsInfo.markAsResolved(txmbr) 800 801 any_missing |= missing_in_pkg 802 803 return CheckDeps, CheckInstalls, CheckRemoves, any_missing
804 805 @staticmethod
806 - def _sort_req_key(pkgtup):
807 """ Get a sort key for a package requires from most "narrow" to least, 808 this tries to ensure that if we have two reqs like 809 "libfoo = 1.2.3-4" and "foo-api" (which is also provided by 810 libxyz-foo) that we'll get just libfoo. 811 There are other similar cases this "handles".""" 812 813 mapper = {'EQ' : 1, 'LT' : 2, 'LE' : 3, 'GT' : 4, 'GE' : 5, None : 99} 814 flagscore = mapper.get(pkgtup[1], 10) 815 816 # This is pretty magic, basically we want an explicit: 817 # 818 # Requires: foo 819 # 820 # ...to happen before the implicit: 821 # 822 # Requires: libfoo.so.0() 823 # 824 # ...because sometimes the libfoo.so.0() is provided by multiple 825 # packages. Do we need more magic for other implicit deps. here? 826 827 namescore = 0 828 if pkgtup[0].startswith("lib") and \ 829 (pkgtup[0].endswith("()") or pkgtup[0].endswith("()(64bit)")): 830 namescore = 99 # Processes these last 831 832 return (flagscore, namescore)
833
834 - def _checkInstall(self, txmbr):
835 txmbr_reqs = txmbr.po.returnPrco('requires') 836 837 # if this is an update, we should check what the old 838 # requires were to make things faster 839 oldreqs = [] 840 for oldpo in txmbr.updates: 841 oldreqs.extend(oldpo.returnPrco('requires')) 842 oldreqs = set(oldreqs) 843 844 ret = [] 845 for req in sorted(txmbr_reqs, key=self._sort_req_key): 846 if req[0].startswith('rpmlib('): 847 continue 848 if req in oldreqs and self.rpmdb.getProvides(*req): 849 continue 850 851 self.verbose_logger.log(logginglevels.DEBUG_2, _("looking for %s as a requirement of %s"), req, txmbr) 852 provs = self.tsInfo.getProvides(*req) 853 if not provs: 854 ret.append( (txmbr.po, self._prco_req2req(req)) ) 855 continue 856 857 #Add relationship 858 for po in provs: 859 if txmbr.name == po.name: 860 continue 861 for member in self.tsInfo.getMembersWithState( 862 pkgtup=po.pkgtup, output_states=TS_INSTALL_STATES): 863 member.relatedto.append((txmbr.po, 'dependson')) 864 865 return ret
866
867 - def _checkRemove(self, txmbr):
868 po = txmbr.po 869 provs = po.returnPrco('provides') 870 871 # if this is an update, we should check what the new package 872 # provides to make things faster 873 newpoprovs = {} 874 for newpo in txmbr.updated_by + txmbr.obsoleted_by: 875 for p in newpo.provides: 876 newpoprovs[p] = 1 877 ret = [] 878 879 # iterate over the provides of the package being removed 880 # and see what's actually going away 881 for prov in provs: 882 if prov[0].startswith('rpmlib('): # ignore rpmlib() provides 883 continue 884 if newpoprovs.has_key(prov): 885 continue 886 # FIXME: This is probably the best place to fix the postfix rename 887 # problem long term (post .21) ... see compare_providers. 888 for pkg, hits in self.tsInfo.getRequires(*prov).iteritems(): 889 for hit in hits: 890 # See if the update solves the problem... 891 found = False 892 for newpo in txmbr.updated_by: 893 if newpo.checkPrco('provides', hit): 894 found = True 895 break 896 if found: continue 897 for newpo in txmbr.obsoleted_by: 898 if newpo.checkPrco('provides', hit): 899 found = True 900 break 901 if found: continue 902 903 # It doesn't, so see what else might... 904 rn, rf, rv = hit 905 if not self.tsInfo.getProvides(rn, rf, rv): 906 ret.append( (pkg, self._prco_req_nfv2req(rn, rf, rv)) ) 907 return ret
908
909 - def _checkFileRequires(self):
910 fileRequires = set() 911 nfileRequires = set() # These need to be looked up in the rpmdb. 912 reverselookup = {} 913 ret = [] 914 915 # generate list of file requirement in rpmdb 916 if self.installedFileRequires is None: 917 self.installedFileRequires, \ 918 self.installedUnresolvedFileRequires, \ 919 self.installedFileProviders = self.rpmdb.fileRequiresData() 920 921 # get file requirements from packages not deleted 922 todel = [] 923 for pkgtup, files in self.installedFileRequires.iteritems(): 924 if self._tsInfo.getMembersWithState(pkgtup, output_states=TS_REMOVE_STATES): 925 todel.append(pkgtup) 926 else: 927 fileRequires.update(files) 928 for filename in files: 929 reverselookup.setdefault(filename, []).append(pkgtup) 930 for pkgtup in todel: 931 del self.installedFileRequires[pkgtup] 932 933 fileRequires -= self.installedUnresolvedFileRequires 934 935 # get file requirements from new packages 936 for txmbr in self._tsInfo.getMembersWithState(output_states=TS_INSTALL_STATES): 937 for name, flag, evr in txmbr.po.requires: 938 if name.startswith('/'): 939 pt = txmbr.po.pkgtup 940 self.installedFileRequires.setdefault(pt, []).append(name) 941 # check if file requires was already unresolved in update 942 if name in self.installedUnresolvedFileRequires: 943 already_broken = False 944 for oldpo in txmbr.updates: 945 if oldpo.checkPrco('requires', (name, None, (None, None, None))): 946 already_broken = True 947 break 948 if already_broken: 949 continue 950 if name not in fileRequires: 951 nfileRequires.add(name) 952 fileRequires.add(name) 953 reverselookup.setdefault(name, []).append(txmbr.po.pkgtup) 954 955 todel = [] 956 for fname in self.installedFileProviders: 957 niFP_fname = [] 958 for pkgtup in self.installedFileProviders[fname]: 959 if self._tsInfo.getMembersWithState(pkgtup, output_states=TS_REMOVE_STATES): 960 continue 961 niFP_fname.append(pkgtup) 962 963 if not niFP_fname: 964 todel.append(fname) 965 continue 966 967 self.installedFileProviders[fname] = niFP_fname 968 for fname in todel: 969 del self.installedFileProviders[fname] 970 971 # check the file requires 972 iFP = self.installedFileProviders 973 for filename in fileRequires: 974 # In theory we need this to be: 975 # 976 # nprov, filename in iFP (or new), oprov 977 # 978 # ...this keeps the cache exactly the same as the non-cached data. 979 # However that also means that we'll always need the filelists, so 980 # we do: 981 # 982 # filename in iFP (if found return), oprov (if found return), 983 # nprov 984 # 985 # ...this means we'll always get the same _result_ (as we only need 986 # to know if _something_ provides), but our cache will be off on 987 # what does/doesn't provide the file. 988 if filename in self.installedFileProviders: 989 continue 990 991 oprov = self.tsInfo.getOldProvides(filename) 992 if oprov: 993 iFP.setdefault(filename, []).extend([po.pkgtup for po in oprov]) 994 continue 995 996 nprov = self.tsInfo.getNewProvides(filename) 997 if nprov: 998 iFP.setdefault(filename, []).extend([po.pkgtup for po in nprov]) 999 continue 1000 1001 for pkgtup in reverselookup[filename]: 1002 po = self.tsInfo.getMembersWithState(pkgtup, TS_INSTALL_STATES) 1003 if po: 1004 po = po[0].po # Should only have one 1005 else: 1006 po = self.getInstalledPackageObject(pkgtup) 1007 ret.append( (po, (filename, 0, '')) ) 1008 1009 self.rpmdb.transactionCacheFileRequires(self.installedFileRequires, 1010 self.installedUnresolvedFileRequires, 1011 self.installedFileProviders, 1012 ret) 1013 1014 return ret
1015
1016 - def _checkConflicts(self):
1017 ret = [ ] 1018 cpkgs = [] 1019 for po in self.rpmdb.returnConflictPackages(): 1020 if self.tsInfo.getMembersWithState(po.pkgtup, output_states=TS_REMOVE_STATES): 1021 continue 1022 cpkgs.append(po) 1023 for conflict in po.returnPrco('conflicts'): 1024 (r, f, v) = conflict 1025 for conflicting_po in self.tsInfo.getNewProvides(r, f, v): 1026 if conflicting_po.pkgtup[0] == po.pkgtup[0] and conflicting_po.pkgtup[2:] == po.pkgtup[2:]: 1027 continue 1028 ret.append( (po, self._prco_req_nfv2req(r, f, v), 1029 conflicting_po) ) 1030 for txmbr in self.tsInfo.getMembersWithState(output_states=TS_INSTALL_STATES): 1031 po = txmbr.po 1032 done = False 1033 for conflict in txmbr.po.returnPrco('conflicts'): 1034 if not done: 1035 cpkgs.append(txmbr.po) 1036 done = True 1037 (r, f, v) = conflict 1038 for conflicting_po in self.tsInfo.getProvides(r, f, v): 1039 if conflicting_po.pkgtup[0] == po.pkgtup[0] and conflicting_po.pkgtup[2:] == po.pkgtup[2:]: 1040 continue 1041 ret.append( (po, self._prco_req_nfv2req(r, f, v), 1042 conflicting_po) ) 1043 self.rpmdb.transactionCacheConflictPackages(cpkgs) 1044 return ret
1045
1046 - def isPackageInstalled(self, pkgname):
1047 lst = self.tsInfo.matchNaevr(name = pkgname) 1048 for txmbr in lst: 1049 if txmbr.output_state in TS_INSTALL_STATES: 1050 return True 1051 1052 if len(lst) > 0: 1053 # if we get here then it's in the tsInfo for an erase or obsoleted 1054 # --> not going to be installed 1055 return False 1056 1057 if not self.rpmdb.contains(name=pkgname): 1058 return False 1059 1060 return True
1061 _isPackageInstalled = isPackageInstalled 1062
1063 - def _compare_providers(self, pkgs, reqpo):
1064 """take the list of pkgs and score them based on the requesting package 1065 return a dictionary of po=score""" 1066 self.verbose_logger.log(logginglevels.DEBUG_4, 1067 _("Running compare_providers() for %s") %(str(pkgs))) 1068 1069 def _common_prefix_len(x, y, minlen=2): 1070 num = min(len(x), len(y)) 1071 for off in range(num): 1072 if x[off] != y[off]: 1073 return max(off, minlen) 1074 return max(num, minlen)
1075 1076 def _common_sourcerpm(x, y): 1077 if not hasattr(x, 'sourcerpm'): 1078 return False 1079 if not hasattr(y, 'sourcerpm'): 1080 return False 1081 return x.sourcerpm == y.sourcerpm
1082 1083 def _compare_arch_distance(x, y, req_compare_arch): 1084 # take X and Y package objects 1085 # determine which has a closer archdistance to compare_arch 1086 # if they are equal to compare_arch, compare which is closer to the 1087 # running arch 1088 # return the package which is closer or None for equal, or equally useless 1089 1090 x_dist = archDifference(req_compare_arch, x.arch) 1091 if self.arch.multilib: # only go to the next one if we're multilib - 1092 if x_dist == 0: # can't really use best's arch anyway... 1093 self.verbose_logger.log(logginglevels.DEBUG_4, 1094 _("better arch in po %s") %(y)) 1095 return y # just try the next one - can't be much worse 1096 1097 y_dist = archDifference(req_compare_arch, y.arch) 1098 if y_dist > 0 and x_dist > y_dist: 1099 self.verbose_logger.log(logginglevels.DEBUG_4, 1100 _("better arch in po %s") %(y)) 1101 1102 return y 1103 if y_dist == x_dist: 1104 return None 1105 return x 1106 1107 # Actual start of _compare_providers(). 1108 1109 # Do a NameArch filtering, based on repo. __cmp__ 1110 unique_nevra_pkgs = {} 1111 for pkg in pkgs: 1112 if (pkg.pkgtup in unique_nevra_pkgs and 1113 unique_nevra_pkgs[pkg.pkgtup].repo <= pkg.repo): 1114 continue 1115 unique_nevra_pkgs[pkg.pkgtup] = pkg 1116 pkgs = unique_nevra_pkgs.values() 1117 1118 pkgresults = {} 1119 ipkgresults = {} 1120 1121 for pkg in pkgs: 1122 pkgresults[pkg] = 0 1123 1124 rpmdbpkgs = self.rpmdb.searchNevra(name=pkg.name) 1125 if rpmdbpkgs: 1126 # We only want to count things as "installed" if they are 1127 # older than what we are comparing, because this then an update 1128 # so we give preference. If they are newer then obsoletes/etc. 1129 # could play a part ... this probably needs a better fix. 1130 newest = sorted(rpmdbpkgs)[-1] 1131 if newest.verLT(pkg): 1132 # give pkgs which are updates just a SLIGHT edge 1133 # we should also make sure that any pkg 1134 # we are giving an edge to is not obsoleted by 1135 # something else in the transaction. :( 1136 # there are many ways I hate this - this is but one 1137 ipkgresults[pkg] = 5 1138 else: 1139 # just b/c they're not installed pkgs doesn't mean they should 1140 # be ignored entirely. Just not preferred 1141 ipkgresults[pkg] = 0 1142 1143 # This is probably only for "renames". What happens is that pkgA-1 gets 1144 # obsoleted by pkgB but pkgB requires pkgA-2, now _if_ the pkgA txmbr 1145 # gets processed before pkgB then we'll process the "checkRemove" of 1146 # pkgA ... so any deps. on pkgA-1 will look for a new provider, one of 1147 # which is pkgA-2 in that case we want to choose that pkg over any 1148 # others. This works for multiple cases too, but who'd do that right?:) 1149 # FIXME: A good long term. fix here is to just not get into this 1150 # problem, but that's too much for .21. This is much safer. 1151 if ipkgresults: 1152 pkgresults = ipkgresults 1153 pkgs = ipkgresults.keys() 1154 1155 # go through each pkg and compare to others 1156 # if it is same skip it 1157 # if the pkg is obsoleted by any other of the packages 1158 # then add -1024 to its score 1159 # don't need to look for mutual obsoletes b/c each package 1160 # is evaluated against all the others, so mutually obsoleting 1161 # packages will have their scores diminished equally 1162 1163 # compare the arch vs each other pkg 1164 # give each time it returns with a better arch a +5 1165 1166 # look for common source vs the reqpo - give a +10 if it has it 1167 1168 # look for common_prefix_len - add the length*2 to the score 1169 1170 # add the negative of the length of the name to the score 1171 1172 1173 lpos = {} 1174 for po in pkgs: 1175 for nextpo in pkgs: 1176 if po == nextpo: 1177 continue 1178 1179 # If this package isn't the latest version of said package, 1180 # treat it like it's obsoleted. The problem here is X-1 1181 # accidentally provides FOO, so you release X-2 without the 1182 # provide, but X-1 is still picked over a real provider. 1183 if po.name not in lpos: 1184 lpos[po.name] = self.pkgSack.returnNewestByName(po.name)[:1] 1185 if not lpos[po.name] or not po.verEQ(lpos[po.name][0]): 1186 pkgresults[po] -= 1024 1187 1188 obsoleted = False 1189 poprovtup = (po.name, 'EQ', (po.epoch, po.ver, po.release)) 1190 if nextpo.inPrcoRange('obsoletes', poprovtup): 1191 obsoleted = True 1192 pkgresults[po] -= 1024 1193 1194 self.verbose_logger.log(logginglevels.DEBUG_4, 1195 _("%s obsoletes %s") % (nextpo, po)) 1196 1197 if reqpo: 1198 arches = (reqpo.arch, self.arch.bestarch) 1199 else: 1200 arches = (self.arch.bestarch,) 1201 1202 for thisarch in arches: 1203 res = _compare_arch_distance(po, nextpo, thisarch) 1204 if not res: 1205 continue 1206 self.verbose_logger.log(logginglevels.DEBUG_4, 1207 _('archdist compared %s to %s on %s\n Winner: %s' % (po, nextpo, thisarch, res))) 1208 1209 if res == po: 1210 pkgresults[po] += 5 1211 1212 if _common_sourcerpm(po, reqpo): 1213 self.verbose_logger.log(logginglevels.DEBUG_4, 1214 _('common sourcerpm %s and %s' % (po, reqpo))) 1215 pkgresults[po] += 20 1216 if self.isPackageInstalled(po.base_package_name): 1217 self.verbose_logger.log(logginglevels.DEBUG_4, 1218 _('base package %s is installed for %s' % (po.base_package_name, po))) 1219 pkgresults[po] += 5 # Same as ipkgresults above. 1220 if reqpo: 1221 cpl = _common_prefix_len(po.name, reqpo.name) 1222 if cpl > 2: 1223 self.verbose_logger.log(logginglevels.DEBUG_4, 1224 _('common prefix of %s between %s and %s' % (cpl, po, reqpo))) 1225 1226 pkgresults[po] += cpl*2 1227 1228 pkgresults[po] += (len(po.name)*-1) 1229 1230 bestorder = sorted(pkgresults.items(), 1231 key=lambda x: (x[1], x[0]), reverse=True) 1232 self.verbose_logger.log(logginglevels.DEBUG_4, 1233 _('Best Order: %s' % str(bestorder))) 1234 1235 return bestorder 1236
1237 1238 1239 1240 -class DepCheck(object):
1241 """object that YumDepsolver uses to see what things are needed to close 1242 the transaction set. attributes: requires, conflicts are a list of 1243 requires are conflicts in the current transaction set. Each item in the 1244 lists are a requires or conflicts object"""
1245 - def __init__(self):
1246 self.requires = [] 1247 self.conflicts = []
1248
1249 - def addRequires(self, po, req_tuple_list):
1250 # fixme - do checking for duplicates or additions in here to zip things along 1251 reqobj = Requires(po, req_tuple_list) 1252 self.requires.append(reqobj)
1253
1254 - def addConflicts(self, conflict_po_list, conflict_item):
1255 confobj = Conflicts(conflict_po_list, conflict_item) 1256 self.conflicts.append(confobj)
1257
1258 -class Requires(object):
1259 1260 """ 1261 A pure data class for holding a package and the list of things it 1262 requires. 1263 """ 1264
1265 - def __init__(self, pkg,requires):
1266 self.pkg = pkg # po of requiring pkg 1267 self.requires = requires # list of things it requires that are un-closed in the ts
1268
1269 1270 -class Conflicts(object):
1271 1272 """ 1273 A pure data class for holding a package and the list of things it 1274 conflicts. 1275 """ 1276
1277 - def __init__(self, pkglist, conflict):
1278 self.pkglist = pkglist # list of conflicting package objects 1279 self.conflict = conflict # what the conflict was between them
1280