def get_selections(config, options, iface_uri, select_only, download_only, test_callback): """Get selections for iface_uri, according to the options passed. Will switch to GUI mode if necessary. @param options: options from OptionParser @param iface_uri: canonical URI of the interface @param select_only: return immediately even if the selected versions aren't cached @param download_only: wait for stale feeds, and display GUI button as Download, not Run @return: the selected versions, or None if the user cancels @rtype: L{selections.Selections} | None """ if options.offline: config.network_use = model.network_offline iface_cache = config.iface_cache # Try to load it as a feed. If it is a feed, it'll get cached. If not, it's a # selections document and we return immediately. maybe_selections = iface_cache.get_feed(iface_uri, selections_ok=True) if isinstance(maybe_selections, selections.Selections): if not select_only: blocker = maybe_selections.download_missing(config) if blocker: logger.info( _("Waiting for selected implementations to be downloaded..." )) tasks.wait_for_blocker(blocker) return maybe_selections r = requirements.Requirements(iface_uri) r.parse_options(options) return get_selections_for(r, config, options, select_only, download_only, test_callback)
def get_requirements(self): import json from zeroinstall.injector import requirements r = requirements.Requirements(None) reqs_file = os.path.join(self.path, 'requirements.json') with open(reqs_file, 'rt') as stream: values = json.load(stream) for k, v in values.items(): setattr(r, k, v) return r
def handle(config, options, args): if len(args) != 2: raise UsageError() pet_name = args[0] iface_uri = model.canonical_iface_uri(args[1]) sels = select.get_selections(config, options, iface_uri, select_only = False, download_only = True, test_callback = None) if not sels: sys.exit(1) # Aborted by user r = requirements.Requirements(iface_uri) r.parse_options(options) app = config.app_mgr.create_app(pet_name, r) app.set_selections(sels) app.integrate_shell(pet_name)
def testCommands(self): iface = os.path.join(mydir, "Command.xml") driver = Driver(requirements=Requirements(iface), config=self.config) driver.need_download() assert driver.solver.ready impl = driver.solver.selections[self.config.iface_cache.get_interface( iface)] assert impl.id == 'c' assert impl.main == 'test-gui' dep_impl_uri = impl.commands['run'].requires[0].interface dep_impl = driver.solver.selections[ self.config.iface_cache.get_interface(dep_impl_uri)] assert dep_impl.id == 'sha1=256' s1 = driver.solver.selections assert s1.commands[0].path == 'test-gui' xml = s1.toDOM().toxml("utf-8") root = qdom.parse(BytesIO(xml)) s2 = selections.Selections(root) assert s2.commands[0].path == 'test-gui' impl = s2.selections[iface] assert impl.id == 'c' assert s2.commands[0].qdom.attrs['http://custom attr'] == 'namespaced' custom_element = s2.commands[0].qdom.childNodes[0] assert custom_element.name == 'child' dep_impl = s2.selections[dep_impl_uri] assert dep_impl.id == 'sha1=256' d = Driver(self.config, requirements.Requirements(runexec)) need_download = d.need_download() assert need_download == False xml = d.solver.selections.toDOM().toxml("utf-8") root = qdom.parse(BytesIO(xml)) s3 = selections.Selections(root) runnable_impl = s3.selections[runnable] assert 'foo' in runnable_impl.commands assert 'run' in runnable_impl.commands
def get_requirements(self): import json from zeroinstall.injector import requirements r = requirements.Requirements(None) reqs_file = os.path.join(self.path, 'requirements.json') with open(reqs_file, 'rt') as stream: values = json.load(stream) # Update old before/not-before values before = values.pop('before', None) not_before = values.pop('not_before', None) if before or not_before: assert not values.extra_restrictions expr = (not_before or '') + '..' if before: expr += '!' + before values['extra_restrictions'] = {values['interface_uri']: expr} for k, v in values.items(): setattr(r, k, v) return r
def handle(config, options, args): """@type args: [str]""" if len(args) != 2: raise UsageError() pet_name = args[0] iface_uri = model.canonical_iface_uri(args[1]) sels = select.get_selections(config, options, iface_uri, select_only = False, download_only = True, test_callback = None) if not sels: sys.exit(1) # Aborted by user root_feed = config.iface_cache.get_feed(iface_uri) if root_feed: target = root_feed.get_replaced_by() if target is not None: print(_("Warning: interface {old} has been replaced by {new}".format(old = iface_uri, new = target))) r = requirements.Requirements(iface_uri) r.parse_options(options) app = config.app_mgr.create_app(pet_name, r) app.set_selections(sels) app.integrate_shell(pet_name)
def handle(config, options, args): if len(args) != 1: raise UsageError() assert not options.offline old_gui = options.gui app = config.app_mgr.lookup_app(args[0], missing_ok=True) if app is not None: old_sels = app.get_selections() old_selections = old_sels.selections iface_uri = old_sels.interface r = app.get_requirements() r.parse_update_options(options) else: iface_uri = model.canonical_iface_uri(args[0]) r = requirements.Requirements(iface_uri) r.parse_options(options) # Select once in offline console mode to get the old values options.offline = True options.gui = False options.refresh = False try: old_sels = select.get_selections_for(r, config, options, select_only=True, download_only=False, test_callback=None) except SafeException: old_selections = {} else: if old_sels is None: old_selections = {} else: old_selections = old_sels.selections # Download in online mode to get the new values config.network_use = model.network_full options.offline = False options.gui = old_gui options.refresh = True sels = select.get_selections_for(r, config, options, select_only=False, download_only=True, test_callback=None) if not sels: sys.exit(1) # Aborted by user root_feed = config.iface_cache.get_feed(iface_uri) if root_feed: target = root_feed.get_replaced_by() if target is not None: print( _("Warning: interface {old} has been replaced by {new}".format( old=iface_uri, new=target))) from zeroinstall.cmd import whatchanged changes = whatchanged.show_changes(old_selections, sels.selections) root_sel = sels[iface_uri] if not changes: from zeroinstall.support import xmltools # No obvious changes, but check for more subtle updates. if not xmltools.nodes_equal(sels.toDOM(), old_sels.toDOM()): changes = True print( _("Updates to metadata found, but no change to version ({version})." ).format(version=root_sel.version)) root_iface = config.iface_cache.get_interface(iface_uri) # Force a reload, since we may have used the GUI to update it for feed in config.iface_cache.get_feeds(root_iface): config.iface_cache.get_feed(feed, force=True) root_impls = config.iface_cache.get_implementations(root_iface) latest = max((impl.version, impl) for impl in root_impls)[1] if latest.version > model.parse_version(sels[iface_uri].version): print( _("A later version ({name} {latest}) exists but was not selected. Using {version} instead." ).format(latest=latest.get_version(), name=root_iface.get_name(), version=root_sel.version)) if not config.help_with_testing and latest.get_stability( ) < model.stable: print( _('To select "testing" versions, use:\n0install config help_with_testing True' )) elif not changes: print( _("No updates found. Continuing with version {version}.").format( version=root_sel.version)) if app is not None: if changes: app.set_selections(sels) app.set_requirements(r)
def run_test_combinations(config, spec): r = requirements.Requirements(spec.test_iface) r.command = spec.command d = driver.Driver(config=config, requirements=r) solver = d.solver # Explore all combinations... tested_iface = config.iface_cache.get_interface(spec.test_iface) results = Results(spec) for combo in spec.get_combos(spec.test_ifaces): key = set() restrictions = {} selections = {} for (uri, version) in combo.iteritems(): iface = config.iface_cache.get_interface(uri) selections[iface] = version if version.startswith('%'): if version == '%nonlocal': restrictions[iface] = [NonlocalRestriction()] else: raise model.SafeException( "Unknown special '{special}'".format(special=version)) elif ',' in version: not_before, before = [ model.parse_version(v) if v != "" else None for v in version.split(',') ] if (not_before and before) and not_before >= before: raise model.SafeException( "Low version >= high version in %s!" % version) restrictions[iface] = [ model.VersionRangeRestriction(before, not_before) ] else: restrictions[iface] = [ model.VersionExpressionRestriction(version) ] key.add((uri, version)) solver.extra_restrictions = restrictions solve = d.solve_with_downloads() tasks.wait_for_blocker(solve) if not solver.ready: logging.info("Can't select combination %s: %s", combo, solver.get_failure_reason()) result = 'skipped' for uri, impl in solver.selections.iteritems(): if impl is None: selections[uri] = selections.get(uri, None) or '?' else: selections[uri] = impl.get_version() if not selections: selections = solver.get_failure_reason() else: selections = {} for iface, impl in solver.selections.iteritems(): if impl: version = impl.get_version() else: impl = None selections[iface] = version download = d.download_uncached_implementations() if download: config.handler.wait_for_blocker(download) print format_combo(selections) result = run_tests(config, tested_iface, solver.selections, spec) results.by_status[result].append(selections) results.by_combo[frozenset(key)] = (result, selections) return results
def recursive_build(self, iface_uri, source_impl_id=None): """Build an implementation of iface_uri and register it as a feed. @param source_impl_id: the version to build, or None to build any version @type source_impl_id: str """ r = requirements.Requirements(iface_uri) r.source = True r.command = 'compile' d = driver.Driver(self.config, r) iface = self.config.iface_cache.get_interface(iface_uri) d.solver.record_details = True if source_impl_id is not None: d.solver.extra_restrictions[iface] = [ ImplRestriction(source_impl_id) ] # For testing... #p.target_arch = arch.Architecture(os_ranks = {'FreeBSD': 0, None: 1}, machine_ranks = {'i386': 0, None: 1, 'newbuild': 2}) while True: self.heading(iface_uri) self.note("\nSelecting versions for %s..." % iface.get_name()) solved = d.solve_with_downloads() if solved: yield solved tasks.check(solved) if not d.solver.ready: self.print_details(d.solver) raise d.solver.get_failure_reason() self.note("Selection done.") self.note("\nPlan:\n") self.pretty_print_plan(d.solver, r.interface_uri) self.note('') needed = [] for dep_iface_uri, dep_sel in d.solver.selections.selections.iteritems( ): if dep_sel.id.startswith('0compile='): if not needed: self.note( "Build dependencies that need to be compiled first:\n" ) self.note("- {iface} {version}".format( iface=dep_iface_uri, version=dep_sel.version)) needed.append((dep_iface_uri, dep_sel)) if not needed: self.note( "No dependencies need compiling... compile %s itself..." % iface.get_name()) build = self.compile_and_register( d.solver.selections, # force the interface in the recursive case iface_uri if iface_uri != self.iface_uri else None) yield build tasks.check(build) return # Compile the first missing build dependency... dep_iface_uri, dep_sel = needed[0] self.note("") #details = d.solver.details[self.config.iface_cache.get_interface(dep_iface.uri)] #for de in details: # print de dep_source_id = dep_sel.id.split('=', 1)[1] seen_key = (dep_iface_uri, dep_source_id) if seen_key in self.seen: self.note_error( "BUG: Stuck in an auto-compile loop: already built {key}!". format(key=seen_key)) # Try to find out why the previous build couldn't be used... dep_iface = self.config.iface_cache.get_interface( dep_iface_uri) previous_build = self.seen[seen_key] previous_build_feed = os.path.join(previous_build, '0install', 'feed.xml') previous_feed = self.config.iface_cache.get_feed( previous_build_feed) previous_binary_impl = previous_feed.implementations.values( )[0] raise SafeException( "BUG: auto-compile loop: expected to select previously-build binary {binary}:\n\n{reason}" .format(binary=previous_binary_impl, reason=d.solver.justify_decision( r, dep_iface, previous_binary_impl))) build = self.recursive_build(dep_iface_uri, dep_source_id) yield build tasks.check(build) assert seen_key in self.seen, (seen_key, self.seen ) # Must have been built by now
def get_selections(config, options, iface_uri, select_only, download_only, test_callback): """Get selections for iface_uri, according to the options passed. Will switch to GUI mode if necessary. @param options: options from OptionParser @param iface_uri: canonical URI of the interface @param select_only: return immediately even if the selected versions aren't cached @param download_only: wait for stale feeds, and display GUI button as Download, not Run @return: the selected versions, or None if the user cancels @rtype: L{selections.Selections} | None """ if options.offline: config.network_use = model.network_offline iface_cache = config.iface_cache # Try to load it as a feed. If it is a feed, it'll get cached. If not, it's a # selections document and we return immediately. maybe_selections = iface_cache.get_feed(iface_uri, selections_ok = True) if isinstance(maybe_selections, selections.Selections): if not select_only: blocker = maybe_selections.download_missing(config) if blocker: logging.info(_("Waiting for selected implementations to be downloaded...")) tasks.wait_for_blocker(blocker) return maybe_selections r = requirements.Requirements(iface_uri) r.parse_options(options) policy = Policy(config = config, requirements = r) # Note that need_download() triggers a solve if options.refresh or options.gui: # We could run immediately, but the user asked us not to can_run_immediately = False else: if select_only: # --select-only: we only care that we've made a selection, not that we've cached the implementations policy.need_download() can_run_immediately = policy.ready else: can_run_immediately = not policy.need_download() stale_feeds = [feed for feed in policy.solver.feeds_used if not os.path.isabs(feed) and # Ignore local feeds (note: file might be missing too) not feed.startswith('distribution:') and # Ignore (memory-only) PackageKit feeds iface_cache.is_stale(iface_cache.get_feed(feed), config.freshness)] if download_only and stale_feeds: can_run_immediately = False if can_run_immediately: if stale_feeds: if policy.network_use == model.network_offline: logging.debug(_("No doing background update because we are in off-line mode.")) else: # There are feeds we should update, but we can run without them. # Do the update in the background while the program is running. from zeroinstall.injector import background background.spawn_background_update(policy, options.verbose > 0) return policy.solver.selections # If the user didn't say whether to use the GUI, choose for them. if options.gui is None and os.environ.get('DISPLAY', None): options.gui = True # If we need to download anything, we might as well # refresh all the feeds first. options.refresh = True logging.info(_("Switching to GUI mode... (use --console to disable)")) if options.gui: gui_args = policy.requirements.get_as_options() if download_only: # Just changes the button's label gui_args.append('--download-only') if options.refresh: gui_args.append('--refresh') if options.verbose: gui_args.insert(0, '--verbose') if options.verbose > 1: gui_args.insert(0, '--verbose') if options.with_store: for x in options.with_store: gui_args += ['--with-store', x] if select_only: gui_args.append('--select-only') from zeroinstall import helpers sels = helpers.get_selections_gui(iface_uri, gui_args, test_callback) if not sels: return None # Aborted else: # Note: --download-only also makes us stop and download stale feeds first. downloaded = policy.solve_and_download_impls(refresh = options.refresh or download_only or False, select_only = select_only) if downloaded: tasks.wait_for_blocker(downloaded) sels = selections.Selections(policy) return sels
def run_gui(args): parser = OptionParser(usage=_("usage: %prog [options] interface")) parser.add_option("", "--before", help=_("choose a version before this"), metavar='VERSION') parser.add_option("", "--cpu", help=_("target CPU type"), metavar='CPU') parser.add_option("", "--command", help=_("command to select"), metavar='COMMAND') parser.add_option("-d", "--download-only", help=_("fetch but don't run"), action='store_true') parser.add_option("", "--message", help=_("message to display when interacting with user")) parser.add_option("", "--not-before", help=_("minimum version to choose"), metavar='VERSION') parser.add_option("", "--os", help=_("target operation system type"), metavar='OS') parser.add_option("-r", "--refresh", help=_("check for updates of all interfaces"), action='store_true') parser.add_option("", "--select-only", help=_("only download the feeds"), action='store_true') parser.add_option("-s", "--source", help=_("select source code"), action='store_true') parser.add_option("", "--systray", help=_("download in the background"), action='store_true') parser.add_option("-v", "--verbose", help=_("more verbose output"), action='count') parser.add_option("-V", "--version", help=_("display version information"), action='store_true') parser.add_option("", "--with-store", help=_("add an implementation cache"), action='append', metavar='DIR') parser.disable_interspersed_args() (options, args) = parser.parse_args(args) if options.verbose: import logging logger = logging.getLogger() if options.verbose == 1: logger.setLevel(logging.INFO) else: logger.setLevel(logging.DEBUG) import gui if options.version: print "0launch-gui (zero-install) " + gui.version print "Copyright (C) 2010 Thomas Leonard" print _( "This program comes with ABSOLUTELY NO WARRANTY," "\nto the extent permitted by law." "\nYou may redistribute copies of this program" "\nunder the terms of the GNU Lesser General Public License." "\nFor more information about these matters, see the file named COPYING." ) sys.exit(0) import gtk if gtk.gdk.get_display() is None: print >> sys.stderr, "Failed to connect to display. Aborting." sys.exit(1) handler = gui.GUIHandler() config = load_config(handler) if options.with_store: from zeroinstall import zerostore for x in options.with_store: config.stores.stores.append(zerostore.Store(os.path.abspath(x))) if len(args) < 1: import preferences box = preferences.show_preferences(config) box.connect('destroy', gtk.main_quit) gtk.main() sys.exit(0) interface_uri = args[0] if len(args) > 1: parser.print_help() sys.exit(1) import mainwindow, dialog r = requirements.Requirements(interface_uri) r.parse_options(options) widgets = dialog.Template('main') policy = Policy(config=config, requirements=r) root_iface = config.iface_cache.get_interface(interface_uri) policy.solver.record_details = True window = mainwindow.MainWindow(policy, widgets, download_only=bool(options.download_only), select_only=bool(options.select_only)) handler.mainwindow = window if options.message: window.set_message(options.message) root = config.iface_cache.get_interface(policy.root) window.browser.set_root(root) window.window.connect('destroy', lambda w: handler.abort_all_downloads()) if options.systray: window.use_systray_icon() @tasks. async def main(): force_refresh = bool(options.refresh) while True: window.refresh_button.set_sensitive(False) window.browser.set_update_icons(force_refresh) solved = policy.solve_with_downloads(force=force_refresh, update_local=True) if not window.systray_icon: window.show() yield solved try: window.refresh_button.set_sensitive(True) window.browser.highlight_problems() tasks.check(solved) except Exception as ex: window.report_exception(ex) if window.systray_icon and window.systray_icon.get_visible() and \ window.systray_icon.is_embedded(): if policy.ready: window.systray_icon.set_tooltip( _('Downloading updates for %s') % root_iface.get_name()) window.run_button.set_active(True) else: # Should already be reporting an error, but # blink it again just in case window.systray_icon.set_blinking(True) refresh_clicked = dialog.ButtonClickedBlocker( window.refresh_button) yield refresh_clicked, _recalculate if refresh_clicked.happened: force_refresh = True tasks.wait_for_blocker(main())
def run_gui(args): parser = OptionParser(usage=_("usage: %prog [options] interface")) parser.add_option("", "--before", help=_("choose a version before this"), metavar='VERSION') parser.add_option("", "--cpu", help=_("target CPU type"), metavar='CPU') parser.add_option("", "--command", help=_("command to select"), metavar='COMMAND') parser.add_option("-d", "--download-only", help=_("fetch but don't run"), action='store_true') parser.add_option("-g", "--force-gui", help=_("display an error if there's no GUI"), action='store_true') parser.add_option("", "--message", help=_("message to display when interacting with user")) parser.add_option("", "--not-before", help=_("minimum version to choose"), metavar='VERSION') parser.add_option("", "--os", help=_("target operation system type"), metavar='OS') parser.add_option("-r", "--refresh", help=_("check for updates of all interfaces"), action='store_true') parser.add_option("", "--select-only", help=_("only download the feeds"), action='store_true') parser.add_option("-s", "--source", help=_("select source code"), action='store_true') parser.add_option("", "--systray", help=_("download in the background"), action='store_true') parser.add_option("-v", "--verbose", help=_("more verbose output"), action='count') parser.add_option("-V", "--version", help=_("display version information"), action='store_true') parser.add_option( "", "--version-for", help=_("set version constraints for a specific interface"), nargs=2, metavar='URI RANGE', action='append') parser.add_option("", "--with-store", help=_("add an implementation cache"), action='append', metavar='DIR') parser.disable_interspersed_args() (options, args) = parser.parse_args(args) if options.verbose: logger = logging.getLogger() if options.verbose == 1: logger.setLevel(logging.INFO) else: logger.setLevel(logging.DEBUG) if options.version: import gui print("0launch-gui (zero-install) " + gui.version) print("Copyright (C) 2010 Thomas Leonard") print( _("This program comes with ABSOLUTELY NO WARRANTY," "\nto the extent permitted by law." "\nYou may redistribute copies of this program" "\nunder the terms of the GNU Lesser General Public License." "\nFor more information about these matters, see the file named COPYING." )) sys.exit(0) def nogui(ex): if options.force_gui: fn = logging.warn else: fn = logging.info fn("No GUI available", exc_info=ex) sys.exit(100) with warnings.catch_warnings(): if not options.force_gui: warnings.filterwarnings("ignore") if sys.version_info[0] < 3: try: import pygtk pygtk.require('2.0') except ImportError as ex: nogui(ex) import gui try: if sys.version_info[0] > 2: from zeroinstall.gtkui import pygtkcompat pygtkcompat.enable() pygtkcompat.enable_gtk(version='3.0') import gtk except (ImportError, ValueError, RuntimeError) as ex: nogui(ex) if gtk.gdk.get_display() is None: try: raise SafeException("Failed to connect to display.") except SafeException as ex: nogui(ex) # logging needs this as a raised exception handler = gui.GUIHandler() config = load_config(handler) if options.with_store: from zeroinstall import zerostore for x in options.with_store: config.stores.stores.append(zerostore.Store(os.path.abspath(x))) if len(args) < 1: @tasks. async def prefs_main(): import preferences box = preferences.show_preferences(config) done = tasks.Blocker('close preferences') box.connect('destroy', lambda w: done.trigger()) yield done tasks.wait_for_blocker(prefs_main()) sys.exit(0) interface_uri = args[0] if len(args) > 1: parser.print_help() sys.exit(1) import mainwindow, dialog r = requirements.Requirements(interface_uri) r.parse_options(options) widgets = dialog.Template('main') driver = Driver(config=config, requirements=r) root_iface = config.iface_cache.get_interface(interface_uri) driver.solver.record_details = True window = mainwindow.MainWindow(driver, widgets, download_only=bool(options.download_only), select_only=bool(options.select_only)) handler.mainwindow = window if options.message: window.set_message(options.message) root = config.iface_cache.get_interface(r.interface_uri) window.browser.set_root(root) window.window.connect('destroy', lambda w: handler.abort_all_downloads()) if options.systray: window.use_systray_icon() @tasks. async def main(): force_refresh = bool(options.refresh) while True: window.refresh_button.set_sensitive(False) window.browser.set_update_icons(force_refresh) solved = driver.solve_with_downloads(force=force_refresh, update_local=True) if not window.systray_icon: window.show() yield solved try: window.refresh_button.set_sensitive(True) window.browser.highlight_problems() tasks.check(solved) except Exception as ex: window.report_exception(ex) if window.systray_icon and window.systray_icon.get_visible() and \ window.systray_icon.is_embedded(): if driver.solver.ready: window.systray_icon.set_tooltip( _('Downloading updates for %s') % root_iface.get_name()) window.run_button.set_active(True) else: # Should already be reporting an error, but # blink it again just in case window.systray_icon.set_blinking(True) refresh_clicked = dialog.ButtonClickedBlocker( window.refresh_button) yield refresh_clicked, _recalculate if refresh_clicked.happened: force_refresh = True tasks.wait_for_blocker(main())