예제 #1
0
		def process_group(group, group_attrs, base_depends, base_bindings, base_commands):
			for item in group.childNodes:
				if item.uri != XMLNS_IFACE: continue

				if item.name not in ('group', 'implementation', 'package-implementation'):
					continue

				# We've found a group or implementation. Scan for dependencies,
				# bindings and commands. Doing this here means that:
				# - We can share the code for groups and implementations here.
				# - The order doesn't matter, because these get processed first.
				# A side-effect is that the document root cannot contain
				# these.

				depends = base_depends[:]
				bindings = base_bindings[:]
				commands = base_commands.copy()

				for attr, command in [('main', 'run'),
						      ('self-test', 'test')]:
					value = item.attrs.get(attr, None)
					if value is not None:
						commands[command] = Command(qdom.Element(XMLNS_IFACE, 'command', {'name': command, 'path': value}), None)

				for child in item.childNodes:
					if child.uri != XMLNS_IFACE: continue
					if child.name == 'requires':
						dep = process_depends(child, local_dir)
						if dep is not None:
							depends.append(dep)
					elif child.name == 'command':
						command_name = child.attrs.get('name', None)
						if not command_name:
							raise InvalidInterface('Missing name for <command>')
						commands[command_name] = Command(child, local_dir)
					elif child.name in binding_names:
						bindings.append(process_binding(child))

				compile_command = item.attrs.get('http://zero-install.sourceforge.net/2006/namespaces/0compile command')
				if compile_command is not None:
					commands['compile'] = Command(qdom.Element(XMLNS_IFACE, 'command', {'name': 'compile', 'shell-command': compile_command}), None)

				item_attrs = _merge_attrs(group_attrs, item)

				if item.name == 'group':
					process_group(item, item_attrs, depends, bindings, commands)
				elif item.name == 'implementation':
					process_impl(item, item_attrs, depends, bindings, commands)
				elif item.name == 'package-implementation':
					if depends:
						warn("A <package-implementation> with dependencies in %s!", self.url)
					self._package_implementations.append((item, item_attrs))
				else:
					assert 0
예제 #2
0
    def build_feed(self):
        def child(parent, name, attrs=None):
            new = qdom.Element(XMLNS_IFACE, name, attrs or {})
            parent.childNodes.append(new)
            return new

        root = qdom.Element(XMLNS_IFACE, 'interface',
                            {'uri': uri_prefix + self.name})
        child(root, 'name').content = self.name
        child(root, 'summary').content = self.name

        i = 0
        for version in self.versions.values():
            attrs = {
                'id': str(i),
                'version': str(version.n),
                'main': 'dummy',
            }
            if version.arch:
                attrs['arch'] = version.arch
            impl = child(root, 'implementation', attrs)
            child(impl, 'manifest-digest', {'sha1new': '1234'})
            for lib, min_v, max_v in version.requires:
                req = child(impl, 'requires', {'interface': uri_prefix + lib})
                child(req, 'version', {
                    'before': str(int(max_v) + 1),
                    'not-before': min_v
                })
            i += 1

        feed = model.ZeroInstallFeed(root)
        feed.last_modified = 1
        return feed
예제 #3
0
	def _set_main(self, path):
		""""@deprecated: use commands["run"] instead"""
		if path is None:
			if "run" in self.commands:
				del self.commands["run"]
		else:
			self.commands["run"] = Command(qdom.Element(XMLNS_IFACE, 'command', {'path': path, 'name': 'run'}), None)
예제 #4
0
    def get_feed(self, url, force=False):
        feed = iface_cache.IfaceCache.get_feed(self, url, force)
        if not feed: return None

        if feed not in self.done:
            self.done.add(feed)

            # For each source impl, add a corresponding binary
            # (the binary has no dependencies as we can't predict them here,
            # but they're not the same as the source's dependencies)

            srcs = [
                x for x in feed.implementations.itervalues()
                if x.arch and x.arch.endswith('-src')
            ]
            for x in srcs:
                new_id = '0compile=' + x.id
                if not new_id in feed.implementations:
                    new = NewBuildImplementation(feed, new_id, None)
                    feed.implementations[new_id] = new
                    new.set_arch(host_arch)
                    new.version = x.version

                    # Give it some dummy commands in case we're using it as a <runner>, etc (otherwise it can't be selected)
                    for cmd_name in get_commands(x):
                        cmd = qdom.Element(namespaces.XMLNS_IFACE, 'command', {
                            'path': 'new-build',
                            'name': cmd_name
                        })
                        new.commands[cmd_name] = model.Command(cmd, None)

                    # Find the <command name='compile'/>
                    add_binary_deps(x, new)

        return feed
예제 #5
0
    def installed_fixup(self, impl):
        # OpenSUSE uses _, Fedora uses .
        impl_id = impl.id.replace('_', '.')

        # Hack: If we added any Java implementations, find the corresponding JAVA_HOME...

        if impl_id.startswith('package:rpm:java-1.6.0-openjdk:'):
            java_version = '1.6.0-openjdk'
        elif impl_id.startswith('package:rpm:java-1.7.0-openjdk:'):
            java_version = '1.7.0-openjdk'
        else:
            return

        # On Fedora, unlike Debian, the arch is x86_64, not amd64

        java_bin = '/usr/lib/jvm/jre-%s.%s/bin/java' % (java_version,
                                                        impl.machine)
        if not os.path.exists(java_bin):
            # Try without the arch...
            java_bin = '/usr/lib/jvm/jre-%s/bin/java' % java_version
            if not os.path.exists(java_bin):
                logger.info("Java binary not found (%s)", java_bin)
                if impl.main is None:
                    java_bin = '/usr/bin/java'
                else:
                    return

        impl.commands["run"] = model.Command(
            qdom.Element(namespaces.XMLNS_IFACE, 'command', {
                'path': java_bin,
                'name': 'run'
            }), None)
예제 #6
0
    def installed_fixup(self, impl):
        # Hack: If we added any Java implementations, find the corresponding JAVA_HOME...
        if impl.id.startswith('package:deb:openjdk-6-jre:'):
            java_version = '6-openjdk'
        elif impl.id.startswith('package:deb:openjdk-7-jre:'):
            java_version = '7-openjdk'
        else:
            return

        if impl.machine == 'x86_64':
            java_arch = 'amd64'
        else:
            java_arch = impl.machine

        java_bin = '/usr/lib/jvm/java-%s-%s/jre/bin/java' % (java_version,
                                                             java_arch)
        if not os.path.exists(java_bin):
            # Try without the arch...
            java_bin = '/usr/lib/jvm/java-%s/jre/bin/java' % java_version
            if not os.path.exists(java_bin):
                logger.info("Java binary not found (%s)", java_bin)
                if impl.main is None:
                    java_bin = '/usr/bin/java'
                else:
                    return

        impl.commands["run"] = model.Command(
            qdom.Element(namespaces.XMLNS_IFACE, 'command', {
                'path': java_bin,
                'name': 'run'
            }), None)
예제 #7
0
 def testDep(self):
     b = model.InterfaceDependency('http://foo',
                                   element=qdom.Element(
                                       namespaces.XMLNS_IFACE, 'requires',
                                       {}))
     assert not b.restrictions
     assert not b.bindings
     str(b)
예제 #8
0
 def clone_command_for(command, arch):
     # This is a bit messy. We need to make a copy of the command, without the
     # unnecessary <requires> elements.
     all_dep_elems = set(dep.qdom for dep in command.requires)
     required_dep_elems = set(dep.qdom
                              for dep in deps_in_use(command, arch))
     if all_dep_elems == required_dep_elems:
         return command  # No change
     dep_elems_to_remove = all_dep_elems - required_dep_elems
     old_root = command.qdom
     new_qdom = qdom.Element(old_root.uri, old_root.name,
                             old_root.attrs)
     new_qdom.childNodes = [
         node for node in command.qdom.childNodes
         if node not in dep_elems_to_remove
     ]
     return model.Command(new_qdom, command._local_dir)
예제 #9
0
    def testLocalPath(self):
        # 0launch --get-selections Local.xml
        iface = os.path.join(mydir, "Local.xml")
        driver = Driver(requirements=Requirements(iface), config=self.config)
        driver.need_download()
        assert driver.solver.ready
        s1 = driver.solver.selections
        xml = s1.toDOM().toxml("utf-8")

        # Reload selections and check they're the same
        root = qdom.parse(BytesIO(xml))
        s2 = selections.Selections(root)
        local_path = s2.selections[iface].local_path
        assert os.path.isdir(local_path), local_path
        assert not s2.selections[iface].digests, s2.selections[iface].digests

        # Add a newer implementation and try again
        feed = self.config.iface_cache.get_feed(iface)
        impl = model.ZeroInstallImplementation(feed,
                                               "foo bar=123",
                                               local_path=None)
        impl.version = model.parse_version('1.0')
        impl.commands["run"] = model.Command(
            qdom.Element(namespaces.XMLNS_IFACE, 'command', {
                'path': 'dummy',
                'name': 'run'
            }), None)
        impl.add_download_source('http://localhost/bar.tgz', 1000, None)
        feed.implementations = {impl.id: impl}
        assert driver.need_download()
        assert driver.solver.ready, driver.solver.get_failure_reason()
        s1 = driver.solver.selections
        xml = s1.toDOM().toxml("utf-8")
        root = qdom.parse(BytesIO(xml))
        s2 = selections.Selections(root)
        xml = s2.toDOM().toxml("utf-8")
        qdom.parse(BytesIO(xml))
        assert s2.selections[iface].local_path is None
        assert not s2.selections[iface].digests, s2.selections[iface].digests
        assert s2.selections[iface].id == 'foo bar=123'
예제 #10
0
 def child(parent, name, attrs=None):
     new = qdom.Element(XMLNS_IFACE, name, attrs or {})
     parent.childNodes.append(new)
     return new
예제 #11
0
	def __init__(self, feed_element, local_path = None, distro = None):
		"""Create a feed object from a DOM.
		@param feed_element: the root element of a feed file
		@type feed_element: L{qdom.Element}
		@param local_path: the pathname of this local feed, or None for remote feeds"""
		self.local_path = local_path
		self.implementations = {}
		self.name = None
		self.summaries = {}	# { lang: str }
		self.first_summary = None
		self.descriptions = {}	# { lang: str }
		self.first_description = None
		self.last_modified = None
		self.feeds = []
		self.feed_for = set()
		self.metadata = []
		self.last_checked = None
		self._package_implementations = []

		if distro is not None:
			import warnings
			warnings.warn("distro argument is now ignored", DeprecationWarning, 2)

		if feed_element is None:
			return			# XXX subclass?

		assert feed_element.name in ('interface', 'feed'), "Root element should be <interface>, not %s" % feed_element
		assert feed_element.uri == XMLNS_IFACE, "Wrong namespace on root element: %s" % feed_element.uri

		main = feed_element.getAttribute('main')
		#if main: warn("Setting 'main' on the root element is deprecated. Put it on a <group> instead")

		if local_path:
			self.url = local_path
			local_dir = os.path.dirname(local_path)
		else:
			assert local_path is None
			self.url = feed_element.getAttribute('uri')
			if not self.url:
				raise InvalidInterface(_("<interface> uri attribute missing"))
			local_dir = None	# Can't have relative paths

		min_injector_version = feed_element.getAttribute('min-injector-version')
		if min_injector_version:
			if parse_version(min_injector_version) > parse_version(version):
				raise InvalidInterface(_("This feed requires version %(min_version)s or later of "
							"Zero Install, but I am only version %(version)s. "
							"You can get a newer version from http://0install.net") %
							{'min_version': min_injector_version, 'version': version})

		for x in feed_element.childNodes:
			if x.uri != XMLNS_IFACE:
				self.metadata.append(x)
				continue
			if x.name == 'name':
				self.name = x.content
			elif x.name == 'description':
				if self.first_description == None:
					self.first_description = x.content
				self.descriptions[x.attrs.get("http://www.w3.org/XML/1998/namespace lang", 'en')] = x.content
			elif x.name == 'summary':
				if self.first_summary == None:
					self.first_summary = x.content
				self.summaries[x.attrs.get("http://www.w3.org/XML/1998/namespace lang", 'en')] = x.content
			elif x.name == 'feed-for':
				feed_iface = x.getAttribute('interface')
				if not feed_iface:
					raise InvalidInterface(_('Missing "interface" attribute in <feed-for>'))
				self.feed_for.add(feed_iface)
				# Bug report from a Debian/stable user that --feed gets the wrong value.
				# Can't reproduce (even in a Debian/stable chroot), but add some logging here
				# in case it happens again.
				logger.debug(_("Is feed-for %s"), feed_iface)
			elif x.name == 'feed':
				feed_src = x.getAttribute('src')
				if not feed_src:
					raise InvalidInterface(_('Missing "src" attribute in <feed>'))
				if feed_src.startswith('http:') or feed_src.startswith('https:') or local_path:
					if feed_src.startswith('.'):
						feed_src = os.path.abspath(os.path.join(local_dir, feed_src))

					langs = x.getAttribute('langs')
					if langs: langs = langs.replace('_', '-')
					self.feeds.append(Feed(feed_src, x.getAttribute('arch'), False, langs = langs))
				else:
					raise InvalidInterface(_("Invalid feed URL '%s'") % feed_src)
			else:
				self.metadata.append(x)

		if not self.name:
			raise InvalidInterface(_("Missing <name> in feed"))
		if not self.summary:
			raise InvalidInterface(_("Missing <summary> in feed"))

		def process_group(group, group_attrs, base_depends, base_bindings, base_commands):
			for item in group.childNodes:
				if item.uri != XMLNS_IFACE: continue

				if item.name not in ('group', 'implementation', 'package-implementation'):
					continue

				# We've found a group or implementation. Scan for dependencies,
				# bindings and commands. Doing this here means that:
				# - We can share the code for groups and implementations here.
				# - The order doesn't matter, because these get processed first.
				# A side-effect is that the document root cannot contain
				# these.

				depends = base_depends[:]
				bindings = base_bindings[:]
				commands = base_commands.copy()

				for attr, command in [('main', 'run'),
						      ('self-test', 'test')]:
					value = item.attrs.get(attr, None)
					if value is not None:
						commands[command] = Command(qdom.Element(XMLNS_IFACE, 'command', {'name': command, 'path': value}), None)

				for child in item.childNodes:
					if child.uri != XMLNS_IFACE: continue
					if child.name in _dependency_names:
						dep = process_depends(child, local_dir)
						depends.append(dep)
					elif child.name == 'command':
						command_name = child.attrs.get('name', None)
						if not command_name:
							raise InvalidInterface('Missing name for <command>')
						commands[command_name] = Command(child, local_dir)
					elif child.name in binding_names:
						bindings.append(process_binding(child))

				compile_command = item.attrs.get('http://zero-install.sourceforge.net/2006/namespaces/0compile command')
				if compile_command is not None:
					commands['compile'] = Command(qdom.Element(XMLNS_IFACE, 'command', {'name': 'compile', 'shell-command': compile_command}), None)

				item_attrs = _merge_attrs(group_attrs, item)

				if item.name == 'group':
					process_group(item, item_attrs, depends, bindings, commands)
				elif item.name == 'implementation':
					process_impl(item, item_attrs, depends, bindings, commands)
				elif item.name == 'package-implementation':
					self._package_implementations.append((item, item_attrs, depends))
				else:
					assert 0

		def process_impl(item, item_attrs, depends, bindings, commands):
			id = item.getAttribute('id')
			if id is None:
				raise InvalidInterface(_("Missing 'id' attribute on %s") % item)
			local_path = item_attrs.get('local-path')
			if local_dir and local_path:
				abs_local_path = os.path.abspath(os.path.join(local_dir, local_path))
				impl = ZeroInstallImplementation(self, id, abs_local_path)
			elif local_dir and (id.startswith('/') or id.startswith('.')):
				# For old feeds
				id = os.path.abspath(os.path.join(local_dir, id))
				impl = ZeroInstallImplementation(self, id, id)
			else:
				impl = ZeroInstallImplementation(self, id, None)
				if '=' in id:
					# In older feeds, the ID was the (single) digest
					impl.digests.append(id)
			if id in self.implementations:
				logger.warn(_("Duplicate ID '%(id)s' in feed '%(feed)s'"), {'id': id, 'feed': self})
			self.implementations[id] = impl

			impl.metadata = item_attrs
			try:
				version_mod = item_attrs.get('version-modifier', None)
				if version_mod:
					item_attrs['version'] += version_mod
					del item_attrs['version-modifier']
				version = item_attrs['version']
			except KeyError:
				raise InvalidInterface(_("Missing version attribute"))
			impl.version = parse_version(version)

			impl.commands = commands

			impl.released = item_attrs.get('released', None)
			impl.langs = item_attrs.get('langs', '').replace('_', '-')

			size = item.getAttribute('size')
			if size:
				impl.size = int(size)
			impl.arch = item_attrs.get('arch', None)
			try:
				stability = stability_levels[str(item_attrs['stability'])]
			except KeyError:
				stab = str(item_attrs['stability'])
				if stab != stab.lower():
					raise InvalidInterface(_('Stability "%s" invalid - use lower case!') % item_attrs.stability)
				raise InvalidInterface(_('Stability "%s" invalid') % item_attrs['stability'])
			if stability >= preferred:
				raise InvalidInterface(_("Upstream can't set stability to preferred!"))
			impl.upstream_stability = stability

			impl.bindings = bindings
			impl.requires = depends

			for elem in item.childNodes:
				if elem.uri != XMLNS_IFACE: continue
				if elem.name == 'archive':
					url = elem.getAttribute('href')
					if not url:
						raise InvalidInterface(_("Missing href attribute on <archive>"))
					size = elem.getAttribute('size')
					if not size:
						raise InvalidInterface(_("Missing size attribute on <archive>"))
					impl.add_download_source(url = url, size = int(size),
							extract = elem.getAttribute('extract'),
							start_offset = _get_long(elem, 'start-offset'),
							type = elem.getAttribute('type'))
				elif elem.name == 'manifest-digest':
					for aname, avalue in elem.attrs.items():
						if ' ' not in aname:
							impl.digests.append(zerostore.format_algorithm_digest_pair(aname, avalue))
				elif elem.name == 'recipe':
					recipe = Recipe()
					for recipe_step in elem.childNodes:
						if recipe_step.uri == XMLNS_IFACE and recipe_step.name == 'archive':
							url = recipe_step.getAttribute('href')
							if not url:
								raise InvalidInterface(_("Missing href attribute on <archive>"))
							size = recipe_step.getAttribute('size')
							if not size:
								raise InvalidInterface(_("Missing size attribute on <archive>"))
							recipe.steps.append(DownloadSource(None, url = url, size = int(size),
									extract = recipe_step.getAttribute('extract'),
									start_offset = _get_long(recipe_step, 'start-offset'),
									type = recipe_step.getAttribute('type')))
						elif recipe_step.uri == XMLNS_IFACE and recipe_step.name == 'rename':
							source = recipe_step.getAttribute('source')
							if not source:
								raise InvalidInterface(_("Missing source attribute on <rename>"))
							dest = recipe_step.getAttribute('dest')
							if not dest:
								raise InvalidInterface(_("Missing dest attribute on <rename>"))
							recipe.steps.append(RenameStep(source=source, dest=dest))
						else:
							logger.info(_("Unknown step '%s' in recipe; skipping recipe"), recipe_step.name)
							break
					else:
						impl.download_sources.append(recipe)

		root_attrs = {'stability': 'testing'}
		root_commands = {}
		if main:
			logger.info("Note: @main on document element is deprecated in %s", self)
			root_commands['run'] = Command(qdom.Element(XMLNS_IFACE, 'command', {'path': main, 'name': 'run'}), None)
		process_group(feed_element, root_attrs, [], [], root_commands)
예제 #12
0
def execute_selections(selections,
                       prog_args,
                       dry_run=False,
                       main=None,
                       wrapper=None,
                       stores=None):
    """Execute program. On success, doesn't return. On failure, raises an Exception.
	Returns normally only for a successful dry run.
	@param selections: the selected versions
	@type selections: L{selections.Selections}
	@param prog_args: arguments to pass to the program
	@type prog_args: [str]
	@param dry_run: if True, just print a message about what would have happened
	@type dry_run: bool
	@param main: the name of the binary to run, or None to use the default
	@type main: str
	@param wrapper: a command to use to actually run the binary, or None to run the binary directly
	@type wrapper: str
	@since: 0.27
	@precondition: All implementations are in the cache.
	"""
    #assert stores is not None
    if stores is None:
        from zeroinstall import zerostore
        stores = zerostore.Stores()

    setup = Setup(stores, selections)

    commands = selections.commands
    if main is not None:
        # Replace first command with user's input
        if main.startswith('/'):
            main = main[
                1:]  # User specified a path relative to the package root
        else:
            old_path = commands[0].path if commands else None
            if not old_path:
                raise SafeException(
                    _("Can't use a relative replacement main when there is no original one!"
                      ))
            main = os.path.join(
                os.path.dirname(old_path),
                main)  # User main is relative to command's name
        # Copy all child nodes (e.g. <runner>) except for the arguments
        user_command_element = qdom.Element(namespaces.XMLNS_IFACE, 'command',
                                            {'path': main})
        if commands:
            for child in commands[0].qdom.childNodes:
                if child.uri == namespaces.XMLNS_IFACE and child.name == 'arg':
                    continue
                user_command_element.childNodes.append(child)
        user_command = Command(user_command_element, None)
    else:
        user_command = None

    setup.prepare_env()
    prog_args = setup.build_command(selections.interface, selections.command,
                                    user_command) + prog_args

    if wrapper:
        prog_args = ['/bin/sh', '-c', wrapper + ' "$@"', '-'] + list(prog_args)

    if dry_run:
        print(_("Would execute: %s") % ' '.join(prog_args))
    else:
        logger.info(_("Executing: %s"), prog_args)
        sys.stdout.flush()
        sys.stderr.flush()
        try:
            env = os.environ.copy()
            for x in ['0install-runenv-ZEROINSTALL_GPG', 'ZEROINSTALL_GPG']:
                if x in env:
                    del env[x]

            os.execve(prog_args[0], prog_args, env)
        except OSError as ex:
            raise SafeException(
                _("Failed to run '%(program_path)s': %(exception)s") % {
                    'program_path': prog_args[0],
                    'exception': str(ex)
                })