def testSimple(self): iface_cache = self.config.iface_cache s = solver.DefaultSolver(self.config) foo = iface_cache.get_interface('http://foo/Binary.xml') self.import_feed(foo.uri, 'Binary.xml') foo_src = iface_cache.get_interface('http://foo/Source.xml') self.import_feed(foo_src.uri, 'Source.xml') compiler = iface_cache.get_interface('http://foo/Compiler.xml') self.import_feed(compiler.uri, 'Compiler.xml') binary_arch = arch.Architecture({None: 1}, {None: 1}) assert str(binary_arch).startswith("<Arch") s.solve('http://foo/Binary.xml', binary_arch) assert s.ready assert s.feeds_used == set([foo.uri]), s.feeds_used assert s.selections[foo].id == 'sha1=123' # Now ask for source instead s.solve('http://foo/Binary.xml', arch.SourceArchitecture(binary_arch), command_name='compile') assert s.ready, s.get_failure_reason() assert s.feeds_used == set([foo.uri, foo_src.uri, compiler.uri]), s.feeds_used assert s.selections[foo].id == 'sha1=234' # The source assert s.selections[ compiler].id == 'sha1=345' # A binary needed to compile it assert not s.details
def recalculate(self, fetch_stale_interfaces = True): """@deprecated: see L{solve_with_downloads} """ self.stale_feeds = set() host_arch = self.target_arch if self.src: host_arch = arch.SourceArchitecture(host_arch) self.solver.solve(self.root, host_arch) if self.network_use == network_offline: fetch_stale_interfaces = False blockers = [] for f in self.solver.feeds_used: if f.startswith('/'): continue feed = iface_cache.get_feed(f) if feed is None or feed.last_modified is None: self.download_and_import_feed_if_online(f) # Will start a download elif self.is_stale(feed): debug(_("Adding %s to stale set"), f) self.stale_feeds.add(iface_cache.get_interface(f)) # Legacy API if fetch_stale_interfaces: self.download_and_import_feed_if_online(f) # Will start a download for w in self.watchers: w() return blockers
def testDetails(self): iface_cache = self.config.iface_cache s = solver.DefaultSolver(self.config) foo_binary_uri = 'http://foo/Binary.xml' foo = iface_cache.get_interface(foo_binary_uri) self.import_feed(foo_binary_uri, 'Binary.xml') foo_src = iface_cache.get_interface('http://foo/Source.xml') self.import_feed(foo_src.uri, 'Source.xml') compiler = iface_cache.get_interface('http://foo/Compiler.xml') self.import_feed(compiler.uri, 'Compiler.xml') binary_arch = arch.Architecture({None: 1}, {None: 1}) s.record_details = True s.solve('http://foo/Binary.xml', arch.SourceArchitecture(binary_arch), command_name='compile') assert s.ready, s.get_failure_reason() foo_src_impls = iface_cache.get_feed(foo_src.uri).implementations foo_impls = iface_cache.get_feed(foo.uri).implementations compiler_impls = iface_cache.get_feed(compiler.uri).implementations assert len(s.details) == 2 self.assertEquals( [(foo_src_impls['sha1=234'], None), (foo_impls['sha1=123'], 'Unsupported machine type')], sorted(s.details[foo])) assert s.details[compiler] == [(compiler_impls['sha1=345'], None)]
def get_arch_for(self, requirements, interface=None): """Return the Architecture we would use when solving for this interface. Normally, this architecture is constructed from the OS and CPU type in the requirements, using the host platform's settings if these are not given. If interface is the root, then we wrap this in a SourceArchitecture if looking for source code and (for backwards compatibility) we enable use="testing" dependencies if the command is "test". @param requirements: the overall requirements for the solve @type requirements: L{requirements.Requirements} @param interface: the interface of interest @type interface: L{model.Interface} @return: the architecture that would be used @rtype: L{architecture.Architecture} @since: 1.9""" root_arch = arch.get_architecture(requirements.os, requirements.cpu) if interface is None or interface.uri == requirements.interface_uri: if requirements.source: root_arch = arch.SourceArchitecture(root_arch) if requirements.command == 'test': # This is for old feeds that have use='testing' instead of the newer # 'test' command for giving test-only dependencies. root_arch = arch.Architecture(root_arch.os_ranks, root_arch.machine_ranks) root_arch.use = frozenset([None, "testing"]) return root_arch # Assume we use the same arch for all descendants return root_arch.child_arch
def solve_with_downloads(self, force = False): """Run the solver, then download any feeds that are missing or that need to be updated. Each time a new feed is imported into the cache, the solver is run again, possibly adding new downloads. @param force: whether to download even if we're already ready to run.""" downloads_finished = set() # Successful or otherwise downloads_in_progress = {} # URL -> Download host_arch = self.target_arch if self.src: host_arch = arch.SourceArchitecture(host_arch) while True: postponed = self.solver.solve(self.root, host_arch, return_postponed=True) if postponed: while postponed: yield postponed postponed = [i for i in postponed if not i.happened] continue for w in self.watchers: w() if self.solver.ready and not force: break else: if self.network_use == network_offline and not force: info(_("Can't choose versions and in off-line mode, so aborting")) break # Once we've starting downloading some things, # we might as well get them all. force = True for f in self.solver.feeds_used: if f in downloads_finished or f in downloads_in_progress: continue if f.startswith('/'): continue feed = iface_cache.get_interface(f) downloads_in_progress[f] = self.fetcher.download_and_import_feed(f, iface_cache) if not downloads_in_progress: break blockers = downloads_in_progress.values() yield blockers tasks.check(blockers, self.handler.report_error) for f in downloads_in_progress.keys(): if downloads_in_progress[f].happened: del downloads_in_progress[f] downloads_finished.add(f)
def need_download(self): """Decide whether we need to download anything (but don't do it!) @return: true if we MUST download something (feeds or implementations) @rtype: bool""" host_arch = self.target_arch if self.src: host_arch = arch.SourceArchitecture(host_arch) self.solver.solve(self.root, host_arch) for w in self.watchers: w() if not self.solver.ready: return True # Maybe a newer version will work? if self.get_uncached_implementations(): return True return False
def solve_with_downloads(self, force = False, update_local = False): """Run the solver, then download any feeds that are missing or that need to be updated. Each time a new feed is imported into the cache, the solver is run again, possibly adding new downloads. @param force: whether to download even if we're already ready to run. @param update_local: fetch PackageKit feeds even if we're ready to run.""" downloads_finished = set() # Successful or otherwise downloads_in_progress = {} # URL -> Download host_arch = self.target_arch if self.requirements.source: host_arch = arch.SourceArchitecture(host_arch) # There are three cases: # 1. We want to run immediately if possible. If not, download all the information we can. # (force = False, update_local = False) # 2. We're in no hurry, but don't want to use the network unnecessarily. # We should still update local information (from PackageKit). # (force = False, update_local = True) # 3. The user explicitly asked us to refresh everything. # (force = True) try_quick_exit = not (force or update_local) while True: self.solver.solve(self.requirements.interface_uri, host_arch, command_name = self.requirements.command) for w in self.watchers: w() if try_quick_exit and self.solver.ready: break try_quick_exit = False if not self.solver.ready: force = True for f in self.solver.feeds_used: if f in downloads_finished or f in downloads_in_progress: continue if os.path.isabs(f): if force: self.config.iface_cache.get_feed(f, force = True) downloads_in_progress[f] = tasks.IdleBlocker('Refresh local feed') continue elif f.startswith('distribution:'): if force or update_local: downloads_in_progress[f] = self.config.fetcher.download_and_import_feed(f, self.config.iface_cache) elif force and self.config.network_use != network_offline: downloads_in_progress[f] = self.config.fetcher.download_and_import_feed(f, self.config.iface_cache) # Once we've starting downloading some things, # we might as well get them all. force = True if not downloads_in_progress: if self.config.network_use == network_offline: info(_("Can't choose versions and in off-line mode, so aborting")) break # Wait for at least one download to finish blockers = downloads_in_progress.values() yield blockers tasks.check(blockers, self.config.handler.report_error) for f in downloads_in_progress.keys(): if f in downloads_in_progress and downloads_in_progress[f].happened: del downloads_in_progress[f] downloads_finished.add(f) # Need to refetch any "distribution" feed that # depends on this one distro_feed_url = 'distribution:' + f if distro_feed_url in downloads_finished: downloads_finished.remove(distro_feed_url) if distro_feed_url in downloads_in_progress: del downloads_in_progress[distro_feed_url]
def justify_decision(self, requirements, iface, impl): """Run a solve with impl_id forced to be selected, and explain why it wasn't (or was) selected in the normal case.""" assert isinstance(iface, model.Interface), iface restrictions = self.extra_restrictions.copy() restrictions[iface] = restrictions.get(iface, []) + [_ForceImpl(impl)] s = SATSolver(self.config, restrictions) s.record_details = True s.solve_for(requirements) wanted = "{iface} {version}".format(iface=iface.get_name(), version=impl.get_version()) # Could a selection involving impl even be valid? if not s.ready or iface.uri not in s.selections.selections: reasons = s.details.get(iface, []) for (rid, rstr) in reasons: if rid.id == impl.id and rstr is not None: return _( "{wanted} cannot be used (regardless of other components): {reason}" ).format(wanted=wanted, reason=rstr) if not s.ready: return _( "There is no possible selection using {wanted}.\n{reason}" ).format(wanted=wanted, reason=s.get_failure_reason()) actual_selection = self.selections.get(iface, None) if actual_selection is not None: # Was impl actually selected anyway? if actual_selection.id == impl.id: return _("{wanted} was selected as the preferred version." ).format(wanted=wanted) # Was impl ranked below the selected version? iface_arch = arch.get_architecture(requirements.os, requirements.cpu) if requirements.source and iface.uri == requirements.interface_uri: iface_arch = arch.SourceArchitecture(iface_arch) wanted_rating = self.get_rating(iface, impl, arch) selected_rating = self.get_rating(iface, actual_selection, arch) if wanted_rating < selected_rating: _ranking_component_reason = [ _("natural languages we understand are preferred"), _("preferred versions come first"), _("locally-available versions are preferred when network use is limited" ), _("packages that don't require admin access to install are preferred" ), _("more stable versions preferred"), _("newer versions are preferred"), _("native packages are preferred"), _("newer versions are preferred"), _("better OS match"), _("better CPU match"), _("better locale match"), _("is locally available"), _("better ID (tie-breaker)"), ] actual = actual_selection.get_version() if impl.get_version() == actual: def detail(i): if len(i.id) < 18: return " (" + i.id + ")" else: return " (" + i.id[:16] + "...)" wanted += detail(impl) actual += detail(actual_selection) for i in range(len(wanted_rating)): if wanted_rating[i] < selected_rating[i]: return _( "{wanted} is ranked lower than {actual}: {why}" ).format(wanted=wanted, actual=actual, why=_ranking_component_reason[i]) used_impl = iface.uri in s.selections.selections # Impl is selectable and ranked higher than the selected version. Selecting it would cause # a problem elsewhere. changes = [] for old_iface, old_sel in self.selections.selections.items(): if old_iface == iface.uri and used_impl: continue new_sel = s.selections.selections.get(old_iface, None) if new_sel is None: changes.append( _("{interface}: no longer used").format( interface=old_iface)) elif old_sel.version != new_sel.version: changes.append( _("{interface}: {old} to {new}").format( interface=old_iface, old=old_sel.version, new=new_sel.version)) elif old_sel.id != new_sel.id: changes.append( _("{interface}: {old} to {new}").format( interface=old_iface, old=old_sel.id, new=new_sel.id)) if changes: changes_text = '\n\n' + _( 'The changes would be:') + '\n\n' + '\n'.join(changes) else: changes_text = '' if used_impl: return _( "{wanted} is selectable, but using it would produce a less optimal solution overall." ).format(wanted=wanted) + changes_text else: return _( "If {wanted} were the only option, the best available solution wouldn't use it." ).format(wanted=wanted) + changes_text