Package entropy :: Package client :: Package interfaces :: Module dep

Source Code for Module entropy.client.interfaces.dep

   1  # -*- coding: utf-8 -*- 
   2  """ 
   3   
   4      @author: Fabio Erculiani <lxnay@sabayonlinux.org> 
   5      @contact: lxnay@sabayonlinux.org 
   6      @copyright: Fabio Erculiani 
   7      @license: GPL-2 
   8   
   9      B{Entropy Package Manager Client Dependency handling Interface}. 
  10   
  11  """ 
  12  from entropy.const import * 
  13  from entropy.exceptions import * 
  14  from entropy.graph import Graph 
  15  from entropy.misc import Lifo 
  16  from entropy.output import bold, darkgreen, darkred, blue, red, purple 
  17  from entropy.i18n import _ 
  18   
19 -class CalculatorsMixin:
20
21 - def dependencies_test(self, dbconn = None):
22 23 if dbconn == None: 24 dbconn = self.clientDbconn 25 # get all the installed packages 26 installed_packages = dbconn.listAllIdpackages() 27 28 pdepend_id = etpConst['dependency_type_ids']['pdepend_id'] 29 deps_not_matched = set() 30 # now look 31 length = len(installed_packages) 32 count = 0 33 for idpackage in installed_packages: 34 count += 1 35 36 if (count%150 == 0) or (count == length) or (count == 1): 37 atom = dbconn.retrieveAtom(idpackage) 38 self.updateProgress( 39 darkgreen(_("Checking %s") % (bold(atom),)), 40 importance = 0, 41 type = "info", 42 back = True, 43 count = (count, length), 44 header = darkred(" @@ ") 45 ) 46 47 xdeps = dbconn.retrieveDependencies(idpackage, 48 exclude_deptypes = (pdepend_id,)) 49 needed_deps = [(x, dbconn.atomMatch(x),) for x in xdeps] 50 deps_not_matched |= set([x for x, (y, z,) in needed_deps if y == -1]) 51 52 return deps_not_matched
53
54 - def find_belonging_dependency(self, matched_atoms):
55 crying_atoms = set() 56 for atom in matched_atoms: 57 for repo in self.validRepositories: 58 rdbconn = self.open_repository(repo) 59 riddep = rdbconn.searchDependency(atom) 60 if riddep != -1: 61 ridpackages = rdbconn.searchIdpackageFromIddependency(riddep) 62 for i in ridpackages: 63 i, r = rdbconn.idpackageValidator(i) 64 if i == -1: 65 continue 66 iatom = rdbconn.retrieveAtom(i) 67 crying_atoms.add((iatom, repo)) 68 return crying_atoms
69
70 - def __handle_multi_repo_matches(self, results, extended_results, valid_repos, server_inst):
71 72 packageInformation = {} 73 versionInformation = {} 74 # .tbz2 repos have always the precedence, so if we find them, 75 # we should second what user wants, installing his tbz2 76 tbz2repos = [x for x in results if x.endswith(etpConst['packagesext'])] 77 if tbz2repos: 78 del tbz2repos 79 newrepos = results.copy() 80 for x in newrepos: 81 if x.endswith(etpConst['packagesext']): 82 continue 83 del results[x] 84 85 version_duplicates = set() 86 versions = set() 87 for repo in results: 88 packageInformation[repo] = {} 89 if extended_results: 90 version = results[repo][1] 91 packageInformation[repo]['versiontag'] = results[repo][2] 92 packageInformation[repo]['revision'] = results[repo][3] 93 else: 94 dbconn = self.__atom_match_open_db(repo, server_inst) 95 packageInformation[repo]['versiontag'] = dbconn.retrieveVersionTag(results[repo]) 96 packageInformation[repo]['revision'] = dbconn.retrieveRevision(results[repo]) 97 version = dbconn.retrieveVersion(results[repo]) 98 packageInformation[repo]['version'] = version 99 versionInformation[version] = repo 100 if version in versions: 101 version_duplicates.add(version) 102 versions.add(version) 103 104 newerVersion = self.entropyTools.get_newer_version(list(versions))[0] 105 # if no duplicates are found or newer version is not in duplicates we're done 106 if (not version_duplicates) or (newerVersion not in version_duplicates): 107 reponame = versionInformation.get(newerVersion) 108 return (results[reponame], reponame) 109 110 # we have two repositories with >two packages with the same version 111 # check package tag 112 113 conflictingEntries = {} 114 tags_duplicates = set() 115 tags = set() 116 tagsInfo = {} 117 for repo in packageInformation: 118 if packageInformation[repo]['version'] != newerVersion: 119 continue 120 conflictingEntries[repo] = {} 121 versiontag = packageInformation[repo]['versiontag'] 122 if versiontag in tags: 123 tags_duplicates.add(versiontag) 124 tags.add(versiontag) 125 tagsInfo[versiontag] = repo 126 conflictingEntries[repo]['versiontag'] = versiontag 127 conflictingEntries[repo]['revision'] = packageInformation[repo]['revision'] 128 129 # tags will always be != [] 130 newerTag = sorted(tags, reverse = True)[0] 131 if newerTag not in tags_duplicates: 132 reponame = tagsInfo.get(newerTag) 133 return (results[reponame], reponame) 134 135 # in this case, we have >two packages with the same version and tag 136 # check package revision 137 138 conflictingRevisions = {} 139 revisions = set() 140 revisions_duplicates = set() 141 revisionInfo = {} 142 for repo in conflictingEntries: 143 if conflictingEntries[repo]['versiontag'] == newerTag: 144 conflictingRevisions[repo] = {} 145 versionrev = conflictingEntries[repo]['revision'] 146 if versionrev in revisions: 147 revisions_duplicates.add(versionrev) 148 revisions.add(versionrev) 149 revisionInfo[versionrev] = repo 150 conflictingRevisions[repo]['revision'] = versionrev 151 152 newerRevision = max(revisions) 153 if newerRevision not in revisions_duplicates: 154 reponame = revisionInfo.get(newerRevision) 155 return (results[reponame], reponame) 156 157 # final step, in this case we have >two packages with 158 # the same version, tag and revision 159 # get the repository with the biggest priority 160 for reponame in valid_repos: 161 if reponame in conflictingRevisions: 162 return (results[reponame], reponame)
163
164 - def __validate_atom_match_cache(self, cached_obj, multiMatch, 165 extendedResults, multiRepo, server_inst):
166 167 data, rc = cached_obj 168 if rc == 1: 169 return cached_obj 170 171 if multiRepo or multiMatch: 172 # set([(14789, 'sabayonlinux.org'), (14479, 'sabayonlinux.org')]) 173 matches = data 174 if extendedResults: 175 # set([((14789, '3.3.8b', '', 0), 'sabayonlinux.org')]) 176 matches = [(x[0][0], x[1],) for x in data] 177 for m_id, m_repo in matches: 178 m_db = self.__atom_match_open_db(m_repo, server_inst) 179 if not m_db.isIdpackageAvailable(m_id): 180 return None 181 else: 182 # (14479, 'sabayonlinux.org') 183 m_id, m_repo = cached_obj 184 if extendedResults: 185 # ((14479, '4.4.2', '', 0), 'sabayonlinux.org') 186 m_id, m_repo = cached_obj[0][0], cached_obj[1] 187 m_db = self.__atom_match_open_db(m_repo, server_inst) 188 if not m_db.isIdpackageAvailable(m_id): 189 return None 190 191 return cached_obj
192
193 - def __atom_match_open_db(self, repoid, server_inst):
194 if server_inst is not None: 195 dbconn = server_inst.open_server_repository(just_reading = True, 196 repo = repoid, do_treeupdates = False) 197 else: 198 dbconn = self.open_repository(repoid) 199 return dbconn
200
201 - def atom_match(self, atom, caseSensitive = True, matchSlot = None, 202 matchTag = None, packagesFilter = True, 203 multiMatch = False, multiRepo = False, matchRevision = None, 204 matchRepo = None, server_repos = [], serverInstance = None, 205 extendedResults = False, useCache = True):
206 207 # support match in repository from shell 208 # atom@repo1,repo2,repo3 209 atom, repos = self.entropyTools.dep_get_match_in_repos(atom) 210 if (matchRepo == None) and (repos is not None): 211 matchRepo = repos 212 213 u_hash = "" 214 k_ms = "//" 215 k_mt = "@#@" 216 k_mr = "-1" 217 if isinstance(matchRepo, (list, tuple, set)): 218 u_hash = hash(frozenset(matchRepo)) 219 if const_isstring(matchSlot): 220 k_ms = matchSlot 221 if const_isstring(matchTag): 222 k_mt = matchTag 223 if const_isstring(matchRevision): 224 k_mr = matchRevision 225 repos_ck = self.all_repositories_checksum() 226 227 c_hash = "%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" % ( 228 repos_ck, 229 atom, k_ms, k_mt, hash(packagesFilter), 230 hash(frozenset(self.validRepositories)), 231 hash(frozenset(self.SystemSettings['repositories']['available'])), 232 hash(multiMatch), hash(multiRepo), hash(caseSensitive), 233 k_mr, hash(extendedResults), 234 u_hash, 235 ) 236 c_hash = "%s%s" % (self.atomMatchCacheKey, hash(c_hash),) 237 238 if self.xcache and useCache: 239 cached = self.Cacher.pop(c_hash) 240 if cached is not None: 241 try: 242 cached = self.__validate_atom_match_cache(cached, 243 multiMatch, extendedResults, multiRepo, serverInstance) 244 except (TypeError, ValueError, IndexError, KeyError,): 245 cached = None 246 if cached is not None: 247 return cached 248 249 if server_repos: 250 if not serverInstance: 251 t = _("server_repos needs serverInstance") 252 raise IncorrectParameter("IncorrectParameter: %s" % (t,)) 253 valid_repos = server_repos[:] 254 else: 255 valid_repos = self.validRepositories 256 if matchRepo and (type(matchRepo) in (list, tuple, set)): 257 valid_repos = list(matchRepo) 258 259 repoResults = {} 260 for repo in valid_repos: 261 262 # search 263 dbconn = self.__atom_match_open_db(repo, serverInstance) 264 use_cache = useCache 265 while True: 266 try: 267 query_data, query_rc = dbconn.atomMatch( 268 atom, 269 caseSensitive = caseSensitive, 270 matchSlot = matchSlot, 271 matchTag = matchTag, 272 packagesFilter = packagesFilter, 273 matchRevision = matchRevision, 274 extendedResults = extendedResults, 275 useCache = use_cache 276 ) 277 if query_rc == 0: 278 # package found, add to our dictionary 279 if extendedResults: 280 repoResults[repo] = (query_data[0], query_data[2], 281 query_data[3], query_data[4]) 282 else: 283 repoResults[repo] = query_data 284 except TypeError: 285 if not use_cache: 286 raise 287 use_cache = False 288 continue 289 except dbconn.dbapi2.OperationalError: 290 # repository fooked, skip! 291 break 292 break 293 294 dbpkginfo = (-1, 1) 295 if extendedResults: 296 dbpkginfo = ((-1, None, None, None), 1) 297 298 if multiRepo and repoResults: 299 300 data = set() 301 for repoid in repoResults: 302 data.add((repoResults[repoid], repoid)) 303 dbpkginfo = (data, 0) 304 305 elif len(repoResults) == 1: 306 # one result found 307 repo = list(repoResults.keys())[0] 308 dbpkginfo = (repoResults[repo], repo) 309 310 elif len(repoResults) > 1: 311 312 # we have to decide which version should be taken 313 mypkginfo = self.__handle_multi_repo_matches(repoResults, 314 extendedResults, valid_repos, serverInstance) 315 if mypkginfo is not None: 316 dbpkginfo = mypkginfo 317 318 # multimatch support 319 if multiMatch: 320 321 if dbpkginfo[1] == 1: 322 dbpkginfo = (set(), 1) 323 else: # can be "0" or a string, but 1 means failure 324 if multiRepo: 325 data = set() 326 for q_id, q_repo in dbpkginfo[0]: 327 dbconn = self.__atom_match_open_db(q_repo, serverInstance) 328 query_data, query_rc = dbconn.atomMatch( 329 atom, 330 caseSensitive = caseSensitive, 331 matchSlot = matchSlot, 332 matchTag = matchTag, 333 packagesFilter = packagesFilter, 334 multiMatch = True, 335 extendedResults = extendedResults 336 ) 337 if extendedResults: 338 for item in query_data: 339 data.add(((item[0], item[2], item[3], item[4]), q_repo)) 340 else: 341 for x in query_data: data.add((x, q_repo)) 342 dbpkginfo = (data, 0) 343 else: 344 dbconn = self.__atom_match_open_db(dbpkginfo[1], serverInstance) 345 query_data, query_rc = dbconn.atomMatch( 346 atom, 347 caseSensitive = caseSensitive, 348 matchSlot = matchSlot, 349 matchTag = matchTag, 350 packagesFilter = packagesFilter, 351 multiMatch = True, 352 extendedResults = extendedResults 353 ) 354 if extendedResults: 355 dbpkginfo = (set([((x[0], x[2], x[3], x[4]), dbpkginfo[1]) \ 356 for x in query_data]), 0) 357 else: 358 dbpkginfo = (set([(x, dbpkginfo[1]) for x in query_data]), 0) 359 360 if self.xcache and useCache: 361 self.Cacher.push(c_hash, dbpkginfo) 362 363 return dbpkginfo
364 365 # expands package sets, and in future something more perhaps
366 - def packages_expand(self, packages):
367 new_packages = [] 368 369 for pkg_id in range(len(packages)): 370 package = packages[pkg_id] 371 372 # expand package sets 373 if package.startswith(etpConst['packagesetprefix']): 374 set_pkgs = sorted(self.package_set_expand(package, raise_exceptions = False)) 375 new_packages.extend([x for x in set_pkgs if x not in packages]) # atomMatch below will filter dupies 376 else: 377 new_packages.append(package) 378 379 return new_packages
380
381 - def package_set_expand(self, package_set, raise_exceptions = True):
382 383 max_recursion_level = 50 384 recursion_level = 0 385 386 def do_expand(myset, recursion_level, max_recursion_level): 387 recursion_level += 1 388 if recursion_level > max_recursion_level: 389 raise InvalidPackageSet('InvalidPackageSet: corrupted, too many recursions: %s' % (myset,)) 390 set_data, set_rc = self.package_set_match(myset[len(etpConst['packagesetprefix']):]) 391 if not set_rc: 392 raise InvalidPackageSet('InvalidPackageSet: not found: %s' % (myset,)) 393 (set_from, package_set, mydata,) = set_data 394 395 mypkgs = set() 396 for fset in mydata: # recursively 397 if fset.startswith(etpConst['packagesetprefix']): 398 mypkgs |= do_expand(fset, recursion_level, max_recursion_level) 399 else: 400 mypkgs.add(fset) 401 402 return mypkgs
403 404 if not package_set.startswith(etpConst['packagesetprefix']): 405 package_set = "%s%s" % (etpConst['packagesetprefix'], package_set,) 406 407 try: 408 mylist = do_expand(package_set, recursion_level, max_recursion_level) 409 except InvalidPackageSet: 410 if raise_exceptions: raise 411 mylist = set() 412 413 return mylist
414
415 - def package_set_list(self, server_repos = [], serverInstance = None, matchRepo = None):
416 return self.package_set_match('', matchRepo = matchRepo, server_repos = server_repos, serverInstance = serverInstance, search = True)[0]
417
418 - def package_set_search(self, package_set, server_repos = [], serverInstance = None, matchRepo = None):
419 # search support 420 if package_set == '*': package_set = '' 421 return self.package_set_match(package_set, matchRepo = matchRepo, server_repos = server_repos, serverInstance = serverInstance, search = True)[0]
422
423 - def __package_set_match_open_db(self, repoid, server_inst):
424 if server_inst is not None: 425 dbconn = server_inst.open_server_repository(just_reading = True, repo = repoid) 426 else: 427 dbconn = self.open_repository(repoid) 428 return dbconn
429
430 - def package_set_match(self, package_set, multiMatch = False, 431 matchRepo = None, server_repos = [], serverInstance = None, 432 search = False):
433 434 # support match in repository from shell 435 # set@repo1,repo2,repo3 436 package_set, repos = self.entropyTools.dep_get_match_in_repos( 437 package_set) 438 if (matchRepo == None) and (repos is not None): 439 matchRepo = repos 440 441 if server_repos: 442 if not serverInstance: 443 t = _("server_repos needs serverInstance") 444 raise IncorrectParameter("IncorrectParameter: %s" % (t,)) 445 valid_repos = server_repos[:] 446 else: 447 valid_repos = self.validRepositories 448 449 if matchRepo and (type(matchRepo) in (list, tuple, set)): 450 valid_repos = list(matchRepo) 451 452 # if we search, we return all the matches available 453 if search: multiMatch = True 454 455 set_data = [] 456 457 while True: 458 459 # check inside SystemSettings 460 if not server_repos: 461 sys_pkgsets = self.SystemSettings['system_package_sets'] 462 if search: 463 mysets = [x for x in list(sys_pkgsets.keys()) if \ 464 (x.find(package_set) != -1)] 465 for myset in mysets: 466 mydata = sys_pkgsets.get(myset) 467 set_data.append((etpConst['userpackagesetsid'], 468 const_convert_to_unicode(myset), mydata.copy(),)) 469 else: 470 mydata = sys_pkgsets.get(package_set) 471 if mydata is not None: 472 set_data.append((etpConst['userpackagesetsid'], 473 const_convert_to_unicode(package_set), mydata,)) 474 if not multiMatch: 475 break 476 477 for repoid in valid_repos: 478 dbconn = self.__package_set_match_open_db(repoid, 479 serverInstance) 480 if search: 481 mysets = dbconn.searchSets(package_set) 482 for myset in mysets: 483 mydata = dbconn.retrievePackageSet(myset) 484 set_data.append((repoid, myset, mydata.copy(),)) 485 else: 486 mydata = dbconn.retrievePackageSet(package_set) 487 if mydata: 488 set_data.append((repoid, package_set, mydata,)) 489 if not multiMatch: 490 break 491 492 break 493 494 if not set_data: 495 return (), False 496 497 if multiMatch: 498 return set_data, True 499 500 return set_data.pop(0), True
501
502 - def _get_unsatisfied_dependencies(self, dependencies, deep_deps = False, 503 relaxed_deps = False, depcache = None):
504 505 # NOTE: to avoid user complaints, the magic toy is "relaxed_deps" 506 507 if self.xcache: 508 c_data = sorted(dependencies) 509 client_checksum = self.clientDbconn.checksum() 510 c_hash = hash("%s|%s|%s|%s" % (c_data, deep_deps, 511 client_checksum, relaxed_deps,)) 512 c_hash = "%s%s" % (etpCache['filter_satisfied_deps'], c_hash,) 513 cached = self.Cacher.pop(c_hash) 514 if cached is not None: 515 return cached 516 517 const_debug_write(__name__, 518 "_get_unsatisfied_dependencies (not cached, deep: %s) for => %s" % ( 519 deep_deps, dependencies,)) 520 521 # satisfied dependencies filter support 522 # package.satisfied file support 523 satisfied_kw = '__%s__satisfied_ids' % (__name__,) 524 satisfied_data = self.SystemSettings.get(satisfied_kw) 525 if satisfied_data is None: 526 satisfied_list = self.SystemSettings['satisfied'] 527 tmp_satisfied_data = set() 528 for atom in satisfied_list: 529 matches, m_res = self.atom_match(atom, multiMatch = True, 530 packagesFilter = False, multiRepo = True) 531 if m_res == 0: 532 tmp_satisfied_data |= matches 533 satisfied_data = tmp_satisfied_data 534 self.SystemSettings[satisfied_kw] = satisfied_data 535 536 cdb_am = self.clientDbconn.atomMatch 537 intf_error = self.dbapi2.InterfaceError 538 cdb_getversioning = self.clientDbconn.getVersioningData 539 cdb_retrieveBranch = self.clientDbconn.retrieveBranch 540 cdb_retrieveDigest = self.clientDbconn.retrieveDigest 541 etp_cmp = self.entropyTools.entropy_compare_versions 542 etp_get_rev = self.entropyTools.dep_get_entropy_revision 543 544 if depcache is None: 545 depcache = {} 546 547 def push_to_cache(dependency, is_unsat): 548 # push to cache 549 depcache[dependency] = is_unsat
550 551 unsatisfied = set() 552 for dependency in dependencies: 553 554 if dependency in depcache: 555 # already analized ? 556 is_unsat = depcache[dependency] 557 if is_unsat: 558 unsatisfied.add(dependency) 559 const_debug_write(__name__, 560 "_get_unsatisfied_dependencies control cached for => %s" % ( 561 dependency,)) 562 const_debug_write(__name__, "...") 563 continue 564 565 ### conflict 566 if dependency.startswith("!"): 567 idpackage, rc = cdb_am(dependency[1:]) 568 if idpackage != -1: 569 const_debug_write(__name__, 570 "_get_unsatisfied_dependencies conflict not found on system for => %s" % ( 571 dependency,)) 572 const_debug_write(__name__, "...") 573 unsatisfied.add(dependency) 574 push_to_cache(dependency, True) 575 continue 576 577 const_debug_write(__name__, "...") 578 push_to_cache(dependency, False) 579 continue 580 581 c_ids, c_rc = cdb_am(dependency, multiMatch = True) 582 if c_rc != 0: 583 const_debug_write(__name__, 584 "_get_unsatisfied_dependencies not satisfied on system for => %s" % ( 585 dependency,)) 586 const_debug_write(__name__, "...") 587 unsatisfied.add(dependency) 588 push_to_cache(dependency, True) 589 continue 590 591 # support for app-foo/foo-123~-1 592 # -1 revision means, always pull the latest 593 do_rev_deep = False 594 if not deep_deps: 595 string_rev = etp_get_rev(dependency) 596 if string_rev == -1: 597 do_rev_deep = True 598 599 # force_unsatisfied is another way to see "deep_deps". 600 # in this case, we are going to consider valid any dep that 601 # matches something in installed packages repo. 602 if c_ids and (not deep_deps) and (not do_rev_deep) and (relaxed_deps): 603 const_debug_write(__name__, 604 "_get_unsatisfied_dependencies (force unsat) SATISFIED => %s" % ( 605 dependency,)) 606 const_debug_write(__name__, "...") 607 push_to_cache(dependency, False) 608 continue 609 610 r_id, r_repo = self.atom_match(dependency) 611 if r_id == -1: 612 const_debug_write(__name__, 613 "_get_unsatisfied_dependencies repository match not found for => %s" % ( 614 dependency,)) 615 const_debug_write(__name__, "...") 616 unsatisfied.add(dependency) 617 push_to_cache(dependency, True) 618 continue 619 620 # satisfied dependencies filter support 621 # package.satisfied file support 622 if (r_id, r_repo,) in satisfied_data: 623 push_to_cache(dependency, False) 624 continue # satisfied 625 626 dbconn = self.open_repository(r_repo) 627 try: 628 repo_pkgver, repo_pkgtag, repo_pkgrev = dbconn.getVersioningData(r_id) 629 # note: read rationale below 630 repo_digest = dbconn.retrieveDigest(r_id) 631 except (intf_error, TypeError,): 632 # package entry is broken 633 const_debug_write(__name__, 634 "_get_unsatisfied_dependencies repository entry broken for match => %s" % ( 635 (r_id, r_repo),)) 636 const_debug_write(__name__, "...") 637 unsatisfied.add(dependency) 638 push_to_cache(dependency, True) 639 continue 640 641 client_data = set() 642 for c_id in c_ids: 643 try: 644 installedVer, installedTag, installedRev = cdb_getversioning(c_id) 645 # note: read rationale below 646 installedDigest = cdb_retrieveDigest(c_id) 647 except TypeError: # corrupted entry? 648 installedVer = "0" 649 installedTag = '' 650 installedRev = 0 651 installedDigest = None 652 client_data.add((installedVer, installedTag, installedRev, 653 installedDigest,)) 654 655 # this is required for multi-slotted packages (like python) 656 # and when people mix Entropy and Portage 657 do_cont = False 658 for installedVer, installedTag, installedRev, cdigest in client_data: 659 660 vcmp = etp_cmp((repo_pkgver, repo_pkgtag, repo_pkgrev,), 661 (installedVer, installedTag, installedRev,)) 662 663 # check if both pkgs share the same branch and digest, this must 664 # be done to avoid system inconsistencies across branch upgrades 665 if (vcmp == 0) and (cdigest != repo_digest): 666 vcmp = 1 667 668 if vcmp == 0: 669 const_debug_write(__name__, 670 "_get_unsatisfied_dependencies SATISFIED equals " + \ 671 "(not cached, deep: %s) => %s" % ( 672 deep_deps, dependency,)) 673 const_debug_write(__name__, "...") 674 do_cont = True 675 push_to_cache(dependency, False) 676 break 677 678 ver_tag_repo = (repo_pkgver, repo_pkgtag,) 679 ver_tag_inst = (installedVer, installedTag,) 680 rev_match = repo_pkgrev != installedRev 681 682 if do_rev_deep and rev_match and (ver_tag_repo == ver_tag_inst): 683 # this is unsatisfied then, need to continue to exit from 684 # for cycle and add it to unsatisfied 685 continue 686 687 if deep_deps: 688 # also this is clearly unsatisfied if deep is enabled 689 continue 690 691 if (ver_tag_repo == ver_tag_inst) and rev_match: 692 const_debug_write(__name__, 693 "_get_unsatisfied_dependencies SATISFIED " + \ 694 "w/o rev (not cached, deep: %s) => %s" % ( 695 deep_deps, dependency,)) 696 const_debug_write(__name__, "...") 697 do_cont = True 698 push_to_cache(dependency, False) 699 break 700 701 if do_cont: 702 continue 703 704 # if we get here it means that there are no matching packages 705 const_debug_write(__name__, 706 "_get_unsatisfied_dependencies NOT SATISFIED (not cached, deep: %s) => %s" % ( 707 deep_deps, dependency,)) 708 709 const_debug_write(__name__, "...") 710 unsatisfied.add(dependency) 711 push_to_cache(dependency, True) 712 713 if self.xcache: 714 self.Cacher.push(c_hash, unsatisfied) 715 716 return unsatisfied 717
718 - def get_masked_packages_tree(self, match, atoms = False, flat = False, 719 matchfilter = None):
720 721 if not isinstance(matchfilter, set): 722 matchfilter = set() 723 724 maskedtree = {} 725 mybuffer = Lifo() 726 depcache = set() 727 treelevel = -1 728 729 match_id, match_repo = match 730 731 mydbconn = self.open_repository(match_repo) 732 myatom = mydbconn.retrieveAtom(match_id) 733 idpackage, idreason = mydbconn.idpackageValidator(match_id) 734 if idpackage == -1: 735 treelevel += 1 736 if atoms: 737 mydict = {myatom: idreason,} 738 else: 739 mydict = {match: idreason,} 740 if flat: 741 maskedtree.update(mydict) 742 else: 743 maskedtree[treelevel] = mydict 744 745 mydeps = mydbconn.retrieveDependencies(match_id) 746 for mydep in mydeps: mybuffer.push(mydep) 747 try: 748 mydep = mybuffer.pop() 749 except ValueError: 750 mydep = None # stack empty 751 752 open_db = self.open_repository 753 am = self.atom_match 754 while mydep: 755 756 if mydep in depcache: 757 try: 758 mydep = mybuffer.pop() 759 except ValueError: 760 break # stack empty 761 continue 762 depcache.add(mydep) 763 764 idpackage, repoid = am(mydep) 765 if (idpackage, repoid) in matchfilter: 766 try: 767 mydep = mybuffer.pop() 768 except ValueError: 769 break # stack empty 770 continue 771 772 if idpackage != -1: 773 # doing even here because atomMatch with packagesFilter = False can pull 774 # something different 775 matchfilter.add((idpackage, repoid)) 776 777 # collect masked 778 if idpackage == -1: 779 idpackage, repoid = am(mydep, packagesFilter = False) 780 if idpackage != -1: 781 treelevel += 1 782 if treelevel not in maskedtree and not flat: 783 maskedtree[treelevel] = {} 784 dbconn = open_db(repoid) 785 vidpackage, idreason = dbconn.idpackageValidator(idpackage) 786 if atoms: 787 mydict = {dbconn.retrieveAtom(idpackage): idreason} 788 else: 789 mydict = {(idpackage, repoid): idreason} 790 if flat: maskedtree.update(mydict) 791 else: maskedtree[treelevel].update(mydict) 792 793 # push its dep into the buffer 794 if idpackage != -1: 795 matchfilter.add((idpackage, repoid)) 796 dbconn = open_db(repoid) 797 owndeps = dbconn.retrieveDependencies(idpackage) 798 for owndep in owndeps: 799 mybuffer.push(owndep) 800 801 try: 802 mydep = mybuffer.pop() 803 except ValueError: 804 break # stack empty 805 806 return maskedtree
807
808 - def __generate_dependency_tree_inst_hooks(self, installed_match, pkg_match, 809 stack):
810 811 broken_children_matches = self._lookup_library_drops(pkg_match, 812 installed_match) 813 814 broken_matches = self._lookup_library_breakages(pkg_match, 815 installed_match) 816 817 inverse_deps = self._lookup_inverse_dependencies(pkg_match, 818 installed_match) 819 for inv_match in inverse_deps: 820 stack.push(inv_match) 821 822 # broken children atoms can be added to broken atoms 823 # and pulled into dep calculation 824 broken_matches |= broken_children_matches 825 for br_match in broken_matches: 826 stack.push(br_match)
827
828 - def __generate_dependency_tree_analyze_conflict(self, conflict_str, 829 conflicts, stack, deep_deps):
830 831 conflict_atom = conflict_str[1:] 832 c_idpackage, xst = self.clientDbconn.atomMatch(conflict_atom) 833 if c_idpackage == -1: 834 return # conflicting pkg is not installed 835 836 confl_replacement = self._lookup_conflict_replacement( 837 conflict_atom, c_idpackage, deep_deps = deep_deps) 838 839 const_debug_write(__name__, 840 "__generate_dependency_tree_analyze_conflict " 841 "replacement => %s" % (confl_replacement,)) 842 843 if confl_replacement is not None: 844 stack.push(confl_replacement) 845 return 846 847 # conflict is installed, we need to record it 848 conflicts.add(c_idpackage)
849
850 - def __generate_dependency_tree_analyze_deplist(self, pkg_match, repo_db, 851 stack, deps_not_found, conflicts, unsat_cache, relaxed_deps, deep_deps, 852 empty_deps):
853 854 pkg_id, repo_id = pkg_match 855 myundeps = repo_db.retrieveDependenciesList(pkg_id) 856 857 # check conflicts 858 my_conflicts = set([x for x in myundeps if x.startswith("!")]) 859 if my_conflicts: 860 myundeps -= my_conflicts 861 for my_conflict in my_conflicts: 862 self.__generate_dependency_tree_analyze_conflict(my_conflict, 863 conflicts, stack, deep_deps) 864 865 const_debug_write(__name__, 866 "__generate_dependency_tree_analyze_deplist filtered " 867 "dependency list => %s" % (myundeps,)) 868 869 if not empty_deps: 870 871 myundeps = self._get_unsatisfied_dependencies(myundeps, 872 deep_deps = deep_deps, relaxed_deps = relaxed_deps, 873 depcache = unsat_cache) 874 875 const_debug_write(__name__, 876 "__generate_dependency_tree_analyze_deplist " + \ 877 "filtered UNSATISFIED dependencies => %s" % (myundeps,)) 878 879 post_deps = [] 880 # PDEPENDs support 881 if myundeps: 882 myundeps, post_deps = self._lookup_post_dependencies(repo_db, 883 pkg_id, myundeps) 884 885 const_debug_write(__name__, 886 "generate_dependency_tree POST dependencies ADDED => %s" % ( 887 post_deps,)) 888 889 deps = set() 890 for unsat_dep in myundeps: 891 match_pkg_id, match_repo_id = self.atom_match(unsat_dep) 892 if match_pkg_id == -1: 893 # dependency not found ! 894 deps_not_found.add(unsat_dep) 895 continue 896 deps.add((match_pkg_id, match_repo_id)) 897 stack.push((match_pkg_id, match_repo_id)) 898 899 post_deps_matches = set() 900 for post_dep in post_deps: 901 match_pkg_id, match_repo_id = self.atom_match(post_dep) 902 # if post dependency is not found, we can happily ignore the fact 903 if match_pkg_id == -1: 904 # not adding to deps_not_found 905 continue 906 post_deps_matches.add((match_pkg_id, match_repo_id)) 907 stack.push((match_pkg_id, match_repo_id)) 908 909 return deps, post_deps_matches
910
911 - def _generate_dependency_tree(self, matched_atom, graph, 912 empty_deps = False, relaxed_deps = False, deep_deps = False, 913 unsatisfied_deps_cache = None, elements_cache = None):
914 915 # this cache avoids adding the same element to graph 916 # several times, when it is supposed to be already handled 917 if elements_cache is None: 918 elements_cache = set() 919 if unsatisfied_deps_cache is None: 920 unsatisfied_deps_cache = {} 921 deps_not_found = set() 922 conflicts = set() 923 first_element = True 924 925 stack = Lifo() 926 stack.push(matched_atom) 927 928 929 while stack.is_filled(): 930 931 # get item from stack 932 pkg_id, repo_id = stack.pop() 933 pkg_match = (pkg_id, repo_id) 934 935 if pkg_match in elements_cache: 936 # already pushed to graph 937 continue 938 elements_cache.add(pkg_match) 939 940 # now we are ready to open repository 941 repo_db = self.open_repository(repo_id) 942 943 ## first element checks 944 if first_element: 945 first_element = False 946 # we need to check if first element is masked because of 947 # course, we don't trust function caller. 948 mask_pkg_id, idreason = repo_db.idpackageValidator(pkg_id) 949 if mask_pkg_id == -1: 950 mask_atom = repo_db.retrieveAtom(pkg_id) 951 if mask_atom is None: 952 mask_atom = 'N/A' # wtf? 953 deps_not_found.add(mask_atom) 954 continue # back to while 955 956 # search inside installed packages repository if there's something 957 # in the same slot, if so, do some extra checks first. 958 pkg_key, pkg_slot = repo_db.retrieveKeySlot(pkg_id) 959 cm_idpackage, cm_result = self.clientDbconn.atomMatch(pkg_key, 960 matchSlot = pkg_slot) 961 962 if cm_idpackage != -1: 963 # this method does: 964 # - broken libraries detection 965 # - inverse dependencies check 966 # - broken "dropped" libraries check (see _lookup_library_drops) 967 self.__generate_dependency_tree_inst_hooks( 968 (cm_idpackage, cm_result), pkg_match, stack) 969 970 dep_matches, post_dep_matches = \ 971 self.__generate_dependency_tree_analyze_deplist( 972 pkg_match, repo_db, stack, deps_not_found, 973 conflicts, unsatisfied_deps_cache, 974 relaxed_deps, deep_deps, empty_deps) 975 976 # eventually add our package match to depgraph 977 graph.add(pkg_match, dep_matches) 978 for post_dep_match in post_dep_matches: 979 graph.add(post_dep_match, set([pkg_match])) 980 981 982 # if deps not found, we won't do dep-sorting at all 983 if deps_not_found: 984 del stack 985 raise DependenciesNotFound(deps_not_found) 986 987 return graph, conflicts
988
989 - def _lookup_post_dependencies(self, repo_db, repo_idpackage, 990 unsatisfied_deps):
991 992 post_deps = [x for x in \ 993 repo_db.retrievePostDependencies(repo_idpackage) if x \ 994 in unsatisfied_deps] 995 996 const_debug_write(__name__, 997 "_lookup_post_dependencies POST dependencies => %s" % ( 998 post_deps,)) 999 1000 if post_deps: 1001 1002 # do some filtering 1003 # it is correct to not use my_dep_filter here 1004 unsatisfied_deps = [x for x in unsatisfied_deps \ 1005 if x not in post_deps] 1006 1007 return unsatisfied_deps, post_deps
1008 1009
1010 - def _lookup_system_mask_repository_deps(self):
1011 1012 client_settings = self.SystemSettings[self.sys_settings_client_plugin_id] 1013 data = client_settings['repositories']['system_mask'] 1014 1015 if not data: 1016 return [] 1017 mydata = [] 1018 cached_items = set() 1019 for atom in data: 1020 mymatch = self.atom_match(atom) 1021 if mymatch[0] == -1: # ignore missing ones intentionally 1022 continue 1023 if mymatch in cached_items: 1024 continue 1025 if mymatch not in mydata: 1026 # check if not found 1027 myaction = self.get_package_action(mymatch) 1028 # only if the package is not installed 1029 if myaction == 1: 1030 mydata.append(mymatch) 1031 cached_items.add(mymatch) 1032 return mydata
1033
1034 - def _lookup_conflict_replacement(self, conflict_atom, client_idpackage, deep_deps):
1035 if self.entropyTools.isjustname(conflict_atom): 1036 return 1037 1038 conflict_match = self.atom_match(conflict_atom) 1039 mykey, myslot = self.clientDbconn.retrieveKeySlot(client_idpackage) 1040 new_match = self.atom_match(mykey, matchSlot = myslot) 1041 if (conflict_match == new_match) or (new_match[1] == 1): 1042 return 1043 1044 action = self.get_package_action(new_match) 1045 if (action == 0) and (not deep_deps): 1046 return 1047 1048 return new_match
1049
1050 - def _lookup_inverse_dependencies(self, match, clientmatch):
1051 1052 cmpstat = self.get_package_action(match) 1053 if cmpstat == 0: 1054 return set() 1055 1056 keyslots_cache = set() 1057 match_cache = {} 1058 results = set() 1059 1060 cdb_rdeps = self.clientDbconn.retrieveDependencies 1061 cdb_rks = self.clientDbconn.retrieveKeySlot 1062 gpa = self.get_package_action 1063 mydepends = \ 1064 self.clientDbconn.retrieveReverseDependencies(clientmatch[0]) 1065 1066 for idpackage in mydepends: 1067 try: 1068 key, slot = cdb_rks(idpackage) 1069 except TypeError: 1070 continue 1071 1072 if (key, slot) in keyslots_cache: 1073 continue 1074 keyslots_cache.add((key, slot)) 1075 1076 # grab its deps 1077 mydeps = cdb_rdeps(idpackage) 1078 found = False 1079 for mydep in mydeps: 1080 mymatch = match_cache.get(mydep, 0) 1081 if mymatch == 0: 1082 mymatch = self.atom_match(mydep) 1083 match_cache[mydep] = mymatch 1084 if mymatch == match: 1085 found = True 1086 break 1087 1088 if not found: 1089 mymatch = self.atom_match(key, matchSlot = slot) 1090 if mymatch[0] == -1: 1091 continue 1092 cmpstat = gpa(mymatch) 1093 if cmpstat == 0: 1094 continue 1095 results.add(mymatch) 1096 1097 return results
1098
1099 - def _lookup_library_drops(self, match, client_match):
1100 1101 match_id, match_repo = match 1102 match_db = self.open_repository(match_repo) 1103 repo_libs = match_db.retrieveProvidedLibraries(match_id) 1104 1105 client_libs = self.clientDbconn.retrieveProvidedLibraries( 1106 client_match[0]) 1107 removed_libs = set([x for x in client_libs if x not in repo_libs]) 1108 1109 idpackages = set() 1110 for lib, path, elf in removed_libs: 1111 idpackages |= self.clientDbconn.searchNeeded(lib, elfclass = elf) 1112 1113 broken_matches = set() 1114 for c_idpackage in idpackages: 1115 1116 keyslot = self.clientDbconn.retrieveKeySlotAggregated(c_idpackage) 1117 if keyslot is None: 1118 continue 1119 idpackage, repo = self.atom_match(keyslot) 1120 if idpackage == -1: 1121 continue 1122 1123 cmpstat = self.get_package_action((idpackage, repo)) 1124 if cmpstat == 0: 1125 continue 1126 1127 broken_matches.add((idpackage, repo)) 1128 1129 return broken_matches
1130 1131
1132 - def __get_lib_breaks_client_and_repo_side(self, match_db, match_idpackage, 1133 client_idpackage):
1134 1135 soname = ".so" 1136 repo_needed = match_db.retrieveNeeded(match_idpackage, 1137 extended = True, format = True) 1138 client_needed = self.clientDbconn.retrieveNeeded(client_idpackage, 1139 extended = True, format = True) 1140 1141 repo_split = [x.split(soname)[0] for x in repo_needed] 1142 client_split = [x.split(soname)[0] for x in client_needed] 1143 1144 client_lib_dumps = set() # was client_side 1145 repo_lib_dumps = set() # was repo_side 1146 # ^^ library dumps using repository NEEDED metadata 1147 lib_removes = set() 1148 1149 for lib in client_needed: 1150 if lib in repo_needed: 1151 continue 1152 lib_name = lib.split(soname)[0] 1153 if lib_name in repo_split: 1154 client_lib_dumps.add(lib) 1155 else: 1156 lib_removes.add(lib) 1157 1158 for lib in repo_needed: 1159 if lib in client_needed: 1160 continue 1161 lib_name = lib.split(soname)[0] 1162 if lib_name in client_split: 1163 repo_lib_dumps.add(lib) 1164 1165 return repo_needed, lib_removes, client_lib_dumps, repo_lib_dumps
1166
1167 - def _lookup_library_breakages(self, match, clientmatch):
1168 1169 # there is no need to update this cache when "match" 1170 # will be installed, because at that point 1171 # clientmatch[0] will differ. 1172 c_hash = "%s|%s" % ( 1173 match, 1174 clientmatch, 1175 ) 1176 c_hash = "%s%s" % (etpCache['library_breakage'], hash(c_hash),) 1177 if self.xcache: 1178 cached = self.Cacher.pop(c_hash) 1179 if cached is not None: 1180 return cached 1181 1182 matchdb = self.open_repository(match[1]) 1183 reponeeded, lib_removes, client_side, repo_side = \ 1184 self.__get_lib_breaks_client_and_repo_side(matchdb, 1185 match[0], clientmatch[0]) 1186 1187 # all the packages in client_side should be pulled in and updated 1188 client_idpackages = set() 1189 for needed in client_side: 1190 client_idpackages |= self.clientDbconn.searchNeeded(needed) 1191 1192 client_keyslots = set() 1193 def mymf(idpackage): 1194 if idpackage == clientmatch[0]: 1195 return 0 1196 ks = self.clientDbconn.retrieveKeySlot(idpackage) 1197 if ks is None: 1198 return 0 1199 return ks
1200 client_keyslots = set([x for x in map(mymf, client_idpackages) \ 1201 if x != 0]) 1202 1203 # all the packages in repo_side should be pulled in too 1204 repodata = {} 1205 for needed in repo_side: 1206 repodata[needed] = reponeeded[needed] 1207 del repo_side, reponeeded 1208 1209 repo_dependencies = matchdb.retrieveDependencies(match[0]) 1210 matched_deps = set() 1211 matched_repos = set() 1212 for dependency in repo_dependencies: 1213 depmatch = self.atom_match(dependency) 1214 if depmatch[0] == -1: 1215 continue 1216 matched_repos.add(depmatch[1]) 1217 matched_deps.add(depmatch) 1218 1219 matched_repos = [x for x in self.SystemSettings['repositories']['order'] \ 1220 if x in matched_repos] 1221 found_matches = set() 1222 for needed in repodata: 1223 for myrepo in matched_repos: 1224 mydbc = self.open_repository(myrepo) 1225 solved_needed = mydbc.resolveNeeded(needed, 1226 repodata[needed]) 1227 found = False 1228 for idpackage in solved_needed: 1229 x = (idpackage, myrepo) 1230 if x in matched_deps: 1231 found_matches.add(x) 1232 found = True 1233 break 1234 if found: 1235 break 1236 1237 # these should be pulled in before 1238 repo_matches = set() 1239 # these can be pulled in after 1240 client_matches = set() 1241 1242 for idpackage, repo in found_matches: 1243 cmpstat = self.get_package_action((idpackage, repo)) 1244 if cmpstat == 0: 1245 continue 1246 repo_matches.add((idpackage, repo)) 1247 1248 for key, slot in client_keyslots: 1249 idpackage, repo = self.atom_match(key, matchSlot = slot) 1250 if idpackage == -1: 1251 continue 1252 cmpstat = self.get_package_action((idpackage, repo)) 1253 if cmpstat == 0: 1254 continue 1255 client_matches.add((idpackage, repo)) 1256 1257 client_matches |= repo_matches 1258 1259 if self.xcache: 1260 self.Cacher.push(c_hash, client_matches) 1261 1262 return client_matches 1263
1264 - def get_required_packages(self, matched_atoms, empty_deps = False, 1265 deep_deps = False, relaxed_deps = False, quiet = False):
1266 1267 c_hash = "%s%s" % ( 1268 etpCache['dep_tree'], 1269 hash("%s|%s|%s|%s|%s|%s" % ( 1270 hash(frozenset(sorted(matched_atoms))), 1271 empty_deps, 1272 deep_deps, 1273 relaxed_deps, 1274 self.clientDbconn.checksum(), 1275 # needed when users do bogus things like editing config files 1276 # manually (branch setting) 1277 self.SystemSettings['repositories']['branch'], 1278 )),) 1279 if self.xcache: 1280 cached = self.Cacher.pop(c_hash) 1281 if cached is not None: 1282 return cached 1283 1284 graph = Graph() 1285 deptree_conflicts = set() 1286 atomlen = len(matched_atoms); count = 0 1287 error_generated = 0 1288 error_tree = set() 1289 1290 # check if there are repositories needing some mandatory packages 1291 forced_matches = self._lookup_system_mask_repository_deps() 1292 if forced_matches: 1293 if isinstance(matched_atoms, list): 1294 matched_atoms = forced_matches + [x for x in matched_atoms \ 1295 if x not in forced_matches] 1296 1297 elif isinstance(matched_atoms, set): 1298 # we cannot do anything about the order here 1299 matched_atoms |= set(forced_matches) 1300 1301 sort_dep_text = _("Sorting dependencies") 1302 unsat_deps_cache = {} 1303 elements_cache = set() 1304 matchfilter = set() 1305 for matched_atom in matched_atoms: 1306 1307 const_debug_write(__name__, 1308 "get_required_packages matched_atom => %s" % (matched_atom,)) 1309 1310 if not quiet: 1311 count += 1 1312 if (count%10 == 0) or (count == atomlen) or (count == 1): 1313 self.updateProgress(sort_dep_text, importance = 0, 1314 type = "info", back = True, header = ":: ", 1315 footer = " ::", percent = True, 1316 count = (count, atomlen) 1317 ) 1318 1319 if matched_atom in matchfilter: 1320 continue 1321 1322 try: 1323 mygraph, conflicts = self._generate_dependency_tree( 1324 matched_atom, graph, empty_deps = empty_deps, 1325 deep_deps = deep_deps, relaxed_deps = relaxed_deps, 1326 unsatisfied_deps_cache = unsat_deps_cache, 1327 elements_cache = elements_cache 1328 ) 1329 except DependenciesNotFound as err: 1330 error_generated = -2 1331 error_tree |= err.value 1332 conflicts = set() 1333 1334 deptree_conflicts |= conflicts 1335 1336 if error_generated != 0: 1337 del graph 1338 return error_tree, error_generated 1339 1340 # solve depgraph and append conflicts 1341 deptree = graph.solve() 1342 if 0 in deptree: 1343 del graph 1344 raise KeyError("Graph contains a dep_level == 0") 1345 1346 # reverse ketys in deptree, this allows correct order (not inverse) 1347 level_count = 0 1348 reverse_tree = {} 1349 for key in sorted(deptree.keys(), reverse = True): 1350 level_count += 1 1351 reverse_tree[level_count] = deptree[key] 1352 1353 del deptree, graph 1354 reverse_tree[0] = deptree_conflicts 1355 1356 if self.xcache: 1357 self.Cacher.push(c_hash, (reverse_tree, 0)) 1358 1359 return reverse_tree, 0
1360
1361 - def _filter_depends_multimatched_atoms(self, idpackage, depends):
1362 1363 remove_depends = set() 1364 for d_idpackage in depends: 1365 mydeps = self.clientDbconn.retrieveDependencies(d_idpackage) 1366 for mydep in mydeps: 1367 1368 matches, rslt = self.clientDbconn.atomMatch(mydep, 1369 multiMatch = True) 1370 if rslt == 1: 1371 continue 1372 1373 if idpackage in matches and len(matches) > 1: 1374 # are all in depends? 1375 for mymatch in matches: 1376 if mymatch not in depends: 1377 remove_depends.add(d_idpackage) 1378 break 1379 1380 depends -= remove_depends 1381 return depends
1382
1383 - def generate_depends_tree(self, idpackages, deep = False):
1384 1385 c_hash = "%s%s" % ( 1386 etpCache['depends_tree'], 1387 hash("%s|%s" % ( 1388 tuple(sorted(idpackages)), 1389 deep, 1390 ), 1391 ), 1392 ) 1393 if self.xcache: 1394 cached = self.Cacher.pop(c_hash) 1395 # XXX drop old cache object format 1396 if not isinstance(cached, dict): 1397 cached = None 1398 if cached is not None: 1399 return cached 1400 1401 count = 0 1402 match_cache = set() 1403 stack = Lifo() 1404 graph = Graph() 1405 1406 # post-dependencies won't be pulled in 1407 pdepend_id = etpConst['dependency_type_ids']['pdepend_id'] 1408 rem_dep_text = _("Calculating inverse dependencies for") 1409 for idpackage in idpackages: 1410 stack.push(idpackage) 1411 1412 while stack.is_filled(): 1413 1414 idpackage = stack.pop() 1415 if idpackage in match_cache: 1416 # already analyzed 1417 continue 1418 match_cache.add(idpackage) 1419 1420 system_pkg = not self.validate_package_removal(idpackage) 1421 if system_pkg: 1422 # this is a system package, removal forbidden 1423 continue 1424 1425 count += 1 1426 p_atom = self.clientDbconn.retrieveAtom(idpackage) 1427 self.updateProgress( 1428 blue(rem_dep_text + " %s" % (purple(p_atom),)), 1429 importance = 0, 1430 type = "info", 1431 back = True, 1432 header = '|/-\\'[count%4]+" " 1433 ) 1434 1435 # obtain its inverse deps 1436 reverse_deps = self.clientDbconn.retrieveReverseDependencies( 1437 idpackage, exclude_deptypes = (pdepend_id,)) 1438 if reverse_deps: 1439 reverse_deps = self._filter_depends_multimatched_atoms( 1440 idpackage, reverse_deps) 1441 1442 if deep: 1443 1444 mydeps = set() 1445 for x in self.clientDbconn.retrieveDependencies(idpackage): 1446 match = self.clientDbconn.atomMatch(x) 1447 if match[0] != -1: 1448 mydeps.add(match[0]) 1449 1450 # now filter them 1451 mydeps = [x for x in mydeps if not \ 1452 (self.clientDbconn.isSystemPackage(x) or \ 1453 self.is_installed_idpackage_in_system_mask(x) )] 1454 1455 for x in mydeps: 1456 mydepends = self.clientDbconn.retrieveReverseDependencies(x) 1457 if not mydepends: 1458 reverse_deps.add(x) 1459 1460 for rev_dep in reverse_deps: 1461 stack.push(rev_dep) 1462 graph.add(idpackage, reverse_deps) 1463 1464 1465 del stack 1466 deptree = graph.solve() 1467 del graph 1468 1469 if self.xcache: 1470 self.Cacher.push(c_hash, deptree) 1471 return deptree
1472
1473 - def calculate_available_packages(self, use_cache = True):
1474 1475 c_hash = self.get_available_packages_chash() 1476 if use_cache and self.xcache: 1477 cached = self.get_available_packages_cache(myhash = c_hash) 1478 if cached is not None: 1479 return cached 1480 1481 available = [] 1482 self.setTotalCycles(len(self.validRepositories)) 1483 avail_dep_text = _("Calculating available packages for") 1484 for repo in self.validRepositories: 1485 try: 1486 dbconn = self.open_repository(repo) 1487 dbconn.validateDatabase() 1488 except (RepositoryError, SystemDatabaseError): 1489 self.cycleDone() 1490 continue 1491 try: 1492 # db may be corrupted, we cannot deal with it here 1493 idpackages = [x for x in dbconn.listAllIdpackages( 1494 order_by = 'atom') if dbconn.idpackageValidator(x)[0] != -1] 1495 except dbconn.dbapi2.OperationalError: 1496 self.cycleDone() 1497 continue 1498 count = 0 1499 maxlen = len(idpackages) 1500 myavailable = [] 1501 do_break = False 1502 for idpackage in idpackages: 1503 if do_break: 1504 break 1505 count += 1 1506 if (count % 10 == 0) or (count == 1) or (count == maxlen): 1507 self.updateProgress( 1508 avail_dep_text + " %s" % (repo,), 1509 importance = 0, 1510 type = "info", 1511 back = True, 1512 header = "::", 1513 count = (count, maxlen), 1514 percent = True, 1515 footer = " ::" 1516 ) 1517 # get key + slot 1518 try: 1519 key, slot = dbconn.retrieveKeySlot(idpackage) 1520 matches = self.clientDbconn.searchKeySlot(key, slot) 1521 except (self.dbapi2.DatabaseError, self.dbapi2.IntegrityError, self.dbapi2.OperationalError,): 1522 self.cycleDone() 1523 do_break = True 1524 continue 1525 if not matches: myavailable.append((idpackage, repo)) 1526 available += myavailable[:] 1527 self.cycleDone() 1528 1529 if self.xcache: 1530 self.Cacher.push("%s%s" % ( 1531 etpCache['world_available'], c_hash), available) 1532 return available
1533
1534 - def calculate_critical_updates(self, use_cache = True):
1535 1536 # check if we are branch migrating 1537 # in this case, critical pkgs feature is disabled 1538 in_branch_upgrade = etpConst['etp_in_branch_upgrade_file'] 1539 if os.access(in_branch_upgrade, os.R_OK) and \ 1540 os.path.isfile(in_branch_upgrade): 1541 return set(), [] 1542 1543 db_digest = self.all_repositories_checksum() 1544 if use_cache and self.xcache: 1545 cached = self.get_critical_updates_cache(db_digest = db_digest) 1546 if cached is not None: 1547 return cached 1548 1549 client_settings = self.SystemSettings[self.sys_settings_client_plugin_id] 1550 critical_data = client_settings['repositories']['critical_updates'] 1551 1552 atoms = set() 1553 atom_matches = {} 1554 for repoid in critical_data: 1555 for atom in critical_data[repoid]: 1556 match_id, match_repo = self.atom_match(atom) 1557 if match_repo == 1: 1558 continue 1559 atom_matches[atom] = (match_id, match_repo,) 1560 atoms.add(atom) 1561 1562 atoms = self._get_unsatisfied_dependencies(atoms) 1563 matches = [atom_matches.get(atom) for atom in atoms] 1564 data = (atoms, matches) 1565 1566 if self.xcache: 1567 c_hash = self.get_critical_update_cache_hash(db_digest) 1568 self.Cacher.push("%s%s" % (etpCache['critical_update'], c_hash,), 1569 data, async = False) 1570 1571 return data
1572 1573
1574 - def calculate_world_updates(self, empty_deps = False, use_cache = True, 1575 critical_updates = True):
1576 1577 cl_settings = self.SystemSettings[self.sys_settings_client_plugin_id] 1578 misc_settings = cl_settings['misc'] 1579 update = [] 1580 remove = [] 1581 fine = [] 1582 spm_fine = [] 1583 1584 # critical updates hook, if enabled 1585 # this will force callers to receive only critical updates 1586 if misc_settings.get('forcedupdates') and critical_updates: 1587 upd_atoms, upd_matches = self.calculate_critical_updates( 1588 use_cache = use_cache) 1589 if upd_atoms: 1590 return upd_matches, remove, fine, spm_fine 1591 1592 db_digest = self.all_repositories_checksum() 1593 if use_cache and self.xcache: 1594 cached = self.get_world_update_cache(empty_deps = empty_deps, 1595 db_digest = db_digest) 1596 if cached is not None: 1597 return cached 1598 1599 1600 ignore_spm_downgrades = misc_settings['ignore_spm_downgrades'] 1601 1602 # get all the installed packages 1603 try: 1604 idpackages = self.clientDbconn.listAllIdpackages(order_by = 'atom') 1605 except self.dbapi2.OperationalError: 1606 # client db is broken! 1607 raise SystemDatabaseError("installed packages database is broken") 1608 1609 maxlen = len(idpackages) 1610 count = 0 1611 mytxt = _("Calculating world packages") 1612 for idpackage in idpackages: 1613 1614 count += 1 1615 if (count%10 == 0) or (count == maxlen) or (count == 1): 1616 self.updateProgress( 1617 mytxt, 1618 importance = 0, 1619 type = "info", 1620 back = True, 1621 header = ":: ", 1622 count = (count, maxlen), 1623 percent = True, 1624 footer = " ::" 1625 ) 1626 1627 try: 1628 cl_pkgkey, cl_slot, cl_version, \ 1629 cl_tag, cl_revision, \ 1630 cl_atom = self.clientDbconn.getStrictData(idpackage) 1631 except TypeError: 1632 # check against broken entries, or removed during iteration 1633 continue 1634 use_match_cache = True 1635 do_continue = False 1636 while True: 1637 try: 1638 match = self.atom_match( 1639 cl_pkgkey, 1640 matchSlot = cl_slot, 1641 extendedResults = True, 1642 useCache = use_match_cache 1643 ) 1644 except self.dbapi2.OperationalError: 1645 # ouch, but don't crash here 1646 do_continue = True 1647 break 1648 try: 1649 m_idpackage = match[0][0] 1650 except TypeError: 1651 if not use_match_cache: raise 1652 use_match_cache = False 1653 continue 1654 break 1655 if do_continue: continue 1656 # now compare 1657 # version: cl_version 1658 # tag: cl_tag 1659 # revision: cl_revision 1660 if (m_idpackage != -1): 1661 repoid = match[1] 1662 version = match[0][1] 1663 tag = match[0][2] 1664 revision = match[0][3] 1665 if empty_deps: 1666 if (m_idpackage, repoid) not in update: 1667 update.append((m_idpackage, repoid)) 1668 continue 1669 elif (cl_revision != revision): 1670 # different revision 1671 if cl_revision == 9999 and ignore_spm_downgrades: 1672 # no difference, we're ignoring revision 9999 1673 fine.append(cl_atom) 1674 if (m_idpackage, repoid) not in update: 1675 spm_fine.append((m_idpackage, repoid)) 1676 continue 1677 else: 1678 if (m_idpackage, repoid) not in update: 1679 update.append((m_idpackage, repoid)) 1680 continue 1681 elif (cl_version != version): 1682 # different versions 1683 if (m_idpackage, repoid) not in update: 1684 update.append((m_idpackage, repoid)) 1685 continue 1686 elif (cl_tag != tag): 1687 # different tags 1688 if (m_idpackage, repoid) not in update: 1689 update.append((m_idpackage, repoid)) 1690 continue 1691 else: 1692 1693 # Note: this is a bugfix to improve branch migration 1694 # and really check if pkg has been repackaged 1695 # first check branch 1696 if idpackage is not None: 1697 1698 c_repodb = self.open_repository(repoid) 1699 c_digest = self.clientDbconn.retrieveDigest(idpackage) 1700 r_digest = c_repodb.retrieveDigest(m_idpackage) 1701 1702 if (r_digest != c_digest) and (r_digest is not None) \ 1703 and (c_digest is not None): 1704 if (m_idpackage, repoid) not in update: 1705 update.append((m_idpackage, repoid)) 1706 continue 1707 1708 # no difference 1709 fine.append(cl_atom) 1710 continue 1711 1712 # don't take action if it's just masked 1713 maskedresults = self.atom_match(cl_pkgkey, matchSlot = cl_slot, 1714 packagesFilter = False) 1715 if maskedresults[0] == -1: 1716 remove.append(idpackage) 1717 # look for packages that would match key 1718 # with any slot (for eg: gcc, kernel updates) 1719 matchresults = self.atom_match(cl_pkgkey) 1720 if matchresults[0] != -1: 1721 m_action = self.get_package_action(matchresults) 1722 if m_action > 0 and (matchresults not in update): 1723 update.append(matchresults) 1724 1725 if self.xcache: 1726 c_hash = self.get_world_update_cache_hash(db_digest, empty_deps, 1727 ignore_spm_downgrades) 1728 data = (update, remove, fine, spm_fine,) 1729 self.Cacher.push("%s%s" % (etpCache['world_update'], c_hash,), 1730 data, async = False) 1731 1732 if not update: 1733 # delete branch upgrade file if exists, since there are 1734 # no updates, this file does not deserve to be saved anyway 1735 br_path = etpConst['etp_in_branch_upgrade_file'] 1736 if os.access(br_path, os.W_OK) and os.path.isfile(br_path): 1737 os.remove(br_path) 1738 1739 return update, remove, fine, spm_fine
1740
1741 - def check_package_update(self, atom, deep = False):
1742 1743 c_hash = "%s%s" % (etpCache['check_package_update'], 1744 hash("%s%s" % (atom, deep,) 1745 ), 1746 ) 1747 if self.xcache: 1748 cached = self.Cacher.pop(c_hash) 1749 if cached is not None: 1750 return cached 1751 1752 found = False 1753 match = self.clientDbconn.atomMatch(atom) 1754 matched = None 1755 if match[0] != -1: 1756 myatom = self.clientDbconn.retrieveAtom(match[0]) 1757 mytag = self.entropyTools.dep_gettag(myatom) 1758 myatom = self.entropyTools.remove_tag(myatom) 1759 myrev = self.clientDbconn.retrieveRevision(match[0]) 1760 pkg_match = "="+myatom+"~"+str(myrev) 1761 if mytag is not None: 1762 pkg_match += "#%s" % (mytag,) 1763 pkg_unsatisfied = self._get_unsatisfied_dependencies([pkg_match], 1764 deep_deps = deep) 1765 if pkg_unsatisfied: 1766 # does it really exist on current repos? 1767 pkg_key = self.entropyTools.dep_getkey(myatom) 1768 pkg_id, pkg_repo = self.atom_match(pkg_key) 1769 if pkg_id != -1: 1770 found = True 1771 del pkg_unsatisfied 1772 matched = self.atom_match(pkg_match) 1773 del match 1774 1775 if self.xcache: 1776 self.Cacher.push(c_hash, (found, matched)) 1777 return found, matched
1778
1779 - def validate_package_removal(self, idpackage):
1780 1781 pkgatom = self.clientDbconn.retrieveAtom(idpackage) 1782 pkgkey = self.entropyTools.dep_getkey(pkgatom) 1783 client_settings = self.SystemSettings[self.sys_settings_client_plugin_id] 1784 mask_installed_keys = client_settings['system_mask']['repos_installed_keys'] 1785 1786 if self.is_installed_idpackage_in_system_mask(idpackage): 1787 idpackages = mask_installed_keys.get(pkgkey) 1788 if not idpackages: return False 1789 if len(idpackages) > 1: 1790 return True 1791 return False # sorry! 1792 1793 # did we store the bastard in the db? 1794 system_pkg = self.clientDbconn.isSystemPackage(idpackage) 1795 if not system_pkg: return True 1796 # check if the package is slotted and exist more than one installed first 1797 matches, rc = self.clientDbconn.atomMatch(pkgkey, multiMatch = True) 1798 if len(matches) < 2: 1799 return False 1800 return True
1801 1802
1803 - def get_removal_queue(self, idpackages, deep = False):
1804 queue = [] 1805 if not idpackages: 1806 return queue 1807 treeview = self.generate_depends_tree(idpackages, deep = deep) 1808 for x in sorted(treeview, reverse = True): 1809 queue.extend(treeview[x]) 1810 return queue
1811
1812 - def get_install_queue(self, matched_atoms, empty_deps, deep_deps, 1813 relaxed_deps = False, quiet = False):
1814 1815 install = [] 1816 removal = [] 1817 treepackages, result = self.get_required_packages(matched_atoms, 1818 empty_deps = empty_deps, deep_deps = deep_deps, 1819 relaxed_deps = relaxed_deps, quiet = quiet) 1820 1821 if result == -2: 1822 return treepackages, removal, result 1823 1824 # format 1825 removal = treepackages.pop(0, set()) 1826 for dep_level in sorted(treepackages): 1827 install.extend(treepackages[dep_level]) 1828 1829 # filter out packages that are in actionQueue comparing key + slot 1830 if install and removal: 1831 myremmatch = {} 1832 for rm_idpackage in removal: 1833 keyslot = self.clientDbconn.retrieveKeySlot(rm_idpackage) 1834 # check if users removed idpackage while this 1835 # whole instance is running 1836 if keyslot is None: 1837 continue 1838 myremmatch[keyslot] = rm_idpackage 1839 1840 for pkg_id, pkg_repo in install: 1841 dbconn = self.open_repository(pkg_repo) 1842 testtuple = dbconn.retrieveKeySlot(pkg_id) 1843 removal.discard(myremmatch.get(testtuple)) 1844 1845 return install, sorted(removal), 0
1846