예제 #1
0
    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
예제 #2
0
	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
예제 #3
0
    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)]
예제 #4
0
    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
예제 #5
0
	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)
예제 #6
0
	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
예제 #7
0
	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]
예제 #8
0
    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