From 0eceab815ef09d385d8cdb4c2554ae6029a02b04 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Sat, 7 Apr 2007 13:55:46 +0000 Subject: [PATCH] The GUI now returns an XML selections document in all cases, rather than running the program itself. git-svn-id: file:///home/talex/Backups/sf.net/Subversion/zero-install/trunk/0launch@1652 9f8c893c-44ee-0310-b757-c8ca8341c71e --- zeroinstall/0launch-gui/ZeroInstall-GUI.xml | 2 +- zeroinstall/0launch-gui/download_box.py | 28 ++++--- zeroinstall/0launch-gui/gui.py | 4 +- zeroinstall/0launch-gui/mainwindow.py | 4 +- zeroinstall/injector/cli.py | 114 ++++++++++++++++++++-------- 5 files changed, 97 insertions(+), 55 deletions(-) diff --git a/zeroinstall/0launch-gui/ZeroInstall-GUI.xml b/zeroinstall/0launch-gui/ZeroInstall-GUI.xml index 92fe836..1736ec8 100644 --- a/zeroinstall/0launch-gui/ZeroInstall-GUI.xml +++ b/zeroinstall/0launch-gui/ZeroInstall-GUI.xml @@ -7,7 +7,7 @@ The Zero Install GUI displays a window showing which versions of various components will be used when running a program, and allows you to alter various policy settings to affect the selection process. You can use this to mark particular versions of libraries as preferred, bugggy, etc. http://0install.net/injector.html - + diff --git a/zeroinstall/0launch-gui/download_box.py b/zeroinstall/0launch-gui/download_box.py index 8ed29a5..d7840b5 100644 --- a/zeroinstall/0launch-gui/download_box.py +++ b/zeroinstall/0launch-gui/download_box.py @@ -3,19 +3,19 @@ import os, sys import sets # Note: for Python 2.3; frozenset is only in Python 2.4 from zeroinstall.injector.model import SafeException -from zeroinstall.injector import download, run +from zeroinstall.injector import download, writer from gui import policy, pretty_size from dialog import Dialog import warnings warnings.filterwarnings('ignore', category = DeprecationWarning, module='download_box') -def download_with_gui(mainwindow, prog_args, run_afterwards, main = None): - """If all downloads are ready, runs the program. Otherwise, - hides mainwindow, shows the download progress box and then runs - it. On error, mainwindow is re-shown and returns False. - Before starting, any current downloads (interfaces) are aborted. - On success, doesn't return (calls exec, or sys.exit(0) if nothing to exec).""" +def download_with_gui(mainwindow): + """If all downloads are ready, prints the selected versions and exits. Otherwise, + hides mainwindow, shows the download progress box and then prints them. + On error, mainwindow is re-shown and returns False. Before starting, + any current downloads (interfaces) are aborted. + On success, calls sys.exit(0).""" try: policy.abort_all_downloads() @@ -37,14 +37,12 @@ def download_with_gui(mainwindow, prog_args, run_afterwards, main = None): policy.monitor_download(dl) def run_it(): policy.abort_all_downloads() - if not run_afterwards: - mainwindow.destroy() - sys.exit(0) # Success - if main is None: - run.execute(policy, prog_args) # Don't break older versions - else: - run.execute(policy, prog_args, main = main) - # Not reached, unless this is a dry run + + from zeroinstall.injector import selections + sels = selections.Selections(policy) + doc = sels.toDOM() + doc.writexml(sys.stdout) + sys.stdout.write('\n') mainwindow.destroy() sys.exit(0) # Success if sets.ImmutableSet(policy.monitored_downloads) - existing_downloads: diff --git a/zeroinstall/0launch-gui/gui.py b/zeroinstall/0launch-gui/gui.py index 8fabb5d..2a45a30 100644 --- a/zeroinstall/0launch-gui/gui.py +++ b/zeroinstall/0launch-gui/gui.py @@ -190,9 +190,7 @@ class GUIPolicy(Policy): gtk.main() else: import download_box - download_box.download_with_gui(self.window, - self.prog_args, main = self.main_exec, - run_afterwards = not self.download_only) + download_box.download_with_gui(self.window) else: self.window.show() gtk.main() diff --git a/zeroinstall/0launch-gui/mainwindow.py b/zeroinstall/0launch-gui/mainwindow.py index 965da4f..fcaac9a 100644 --- a/zeroinstall/0launch-gui/mainwindow.py +++ b/zeroinstall/0launch-gui/mainwindow.py @@ -86,9 +86,7 @@ class MainWindow(Dialog): self.destroy() sys.exit(1) elif resp == gtk.RESPONSE_OK: - download_box.download_with_gui(self, - prog_args, main = policy.main_exec, - run_afterwards = not download_only) + download_box.download_with_gui(self) elif resp == gtk.RESPONSE_HELP: gui_help.display() elif resp == SHOW_PREFERENCES: diff --git a/zeroinstall/injector/cli.py b/zeroinstall/injector/cli.py index dd67379..8e402d4 100755 --- a/zeroinstall/injector/cli.py +++ b/zeroinstall/injector/cli.py @@ -111,6 +111,8 @@ def _manage_feeds(options, args): def _normal_mode(options, args): if len(args) < 1: + # You can use -g on its own to edit the GUI's own policy + # Otherwise, failing to give an interface is an error if options.gui: args = [namespaces.injector_gui_uri] options.download_only = True @@ -174,44 +176,90 @@ def _normal_mode(options, args): options.refresh = True logging.info("Switching to GUI mode... (use --console to disable)") - if options.gui: - policy.set_root(namespaces.injector_gui_uri) - policy.src = False - - # Try to start the GUI without using the network. - # The GUI can refresh itself if it wants to. - policy.freshness = 0 - policy.network_use = model.network_offline - - prog_args = [iface_uri] + args[1:] - # Options apply to actual program, not GUI - if options.download_only: - policy.download_only = False - prog_args.insert(0, '--download-only') - if options.refresh: - options.refresh = False - prog_args.insert(0, '--refresh') - if options.not_before: - prog_args.insert(0, options.not_before) - prog_args.insert(0, '--not-before') - if options.before: - prog_args.insert(0, options.before) - prog_args.insert(0, '--before') - if options.source: - prog_args.insert(0, '--source') - if options.main: - prog_args = ['--main', options.main] + prog_args - options.main = None - del policy.root_restrictions[:] - else: - prog_args = args[1:] + prog_args = args[1:] try: - #program_log('download_and_execute ' + iface_uri) - policy.download_and_execute(prog_args, refresh = bool(options.refresh), main = options.main) + if options.gui: + from zeroinstall.injector import run + gui_args = [] + if options.download_only: + # Just changes the button's label + gui_args.append('--download-only') + if options.refresh: + # Just changes the button's label + gui_args.append('--refresh') + if options.not_before: + gui_args.insert(0, options.not_before) + gui_args.insert(0, '--not-before') + if options.before: + gui_args.insert(0, options.before) + gui_args.insert(0, '--before') + if options.source: + gui_args.insert(0, '--source') + sels = _fork_gui(iface_uri, gui_args) + if not sels: return # Aborted + if options.get_selections: + doc = sels.toDOM() + doc.writexml(sys.stdout) + sys.stdout.write('\n') + elif not options.download_only: + run.execute_selections(sels, prog_args, options.dry_run, options.main) + else: + #program_log('download_and_execute ' + iface_uri) + policy.download_and_execute(prog_args, refresh = bool(options.refresh), main = options.main) except autopolicy.NeedDownload, ex: # This only happens for dry runs print ex + +def _fork_gui(iface_uri, gui_args): + gui_policy = autopolicy.AutoPolicy(namespaces.injector_gui_uri) + # Try to start the GUI without using the network. + # The GUI can refresh itself if it wants to. + gui_policy.freshness = 0 + gui_policy.network_use = model.network_offline + gui_policy.recalculate_with_dl() + assert gui_policy.ready # Should always be some version available + gui_policy.start_downloading_impls() + gui_policy.handler.wait_for_downloads() + + from zeroinstall.injector import run + import socket + cli, gui = socket.socketpair() + try: + child = os.fork() + if child == 0: + # We are the child + try: + try: + cli.close() + os.dup2(gui.fileno(), 1) + run.execute(gui_policy, gui_args + ['--', iface_uri]) + except: + import traceback + traceback.print_exc() + finally: + os._exit(1) + gui.close() + gui = None + + xml = "" + while True: + got = cli.recv(256) + if not got: break + xml += got + pid, status = os.waitpid(child, 0) + assert pid == child + if status == 1 << 8: + return None # Aborted + if status != 0: + raise Exception("Error from GUI: code = %d" % status) + finally: + if cli is not None: cli.close() + if gui is not None: gui.close() + + from StringIO import StringIO + from zeroinstall.injector import qdom, selections + return selections.Selections(qdom.parse(StringIO(xml))) def _get_selections(policy): import selections -- 2.11.4.GIT