Converted print statements to functions
[zeroinstall.git] / zeroinstall / cmd / select.py
blobbd5fa8da56b801fab420f9ac496a0729a4b543d0
1 """
2 The B{0install select} command-line interface.
3 """
5 # Copyright (C) 2011, Thomas Leonard
6 # See the README file for details, or visit http://0install.net.
8 from __future__ import print_function
10 import os, sys
11 import logging
13 from zeroinstall import _
14 from zeroinstall.cmd import UsageError
15 from zeroinstall.injector import model, selections, requirements
16 from zeroinstall.injector.policy import Policy
17 from zeroinstall.support import tasks
19 syntax = "URI"
21 def add_generic_select_options(parser):
22 """All options for selecting."""
23 parser.add_option("", "--before", help=_("choose a version before this"), metavar='VERSION')
24 parser.add_option("", "--command", help=_("command to select"), metavar='COMMAND')
25 parser.add_option("", "--cpu", help=_("target CPU type"), metavar='CPU')
26 parser.add_option("", "--message", help=_("message to display when interacting with user"))
27 parser.add_option("", "--not-before", help=_("minimum version to choose"), metavar='VERSION')
28 parser.add_option("-o", "--offline", help=_("try to avoid using the network"), action='store_true')
29 parser.add_option("", "--os", help=_("target operation system type"), metavar='OS')
30 parser.add_option("-r", "--refresh", help=_("refresh all used interfaces"), action='store_true')
31 parser.add_option("-s", "--source", help=_("select source code"), action='store_true')
33 def add_options(parser):
34 """Options for 'select' and 'download' (but not 'run')"""
35 add_generic_select_options(parser)
36 parser.add_option("", "--xml", help=_("write selected versions as XML"), action='store_true')
38 def get_selections(config, options, iface_uri, select_only, download_only, test_callback):
39 """Get selections for iface_uri, according to the options passed.
40 Will switch to GUI mode if necessary.
41 @param options: options from OptionParser
42 @param iface_uri: canonical URI of the interface
43 @param select_only: return immediately even if the selected versions aren't cached
44 @param download_only: wait for stale feeds, and display GUI button as Download, not Run
45 @return: the selected versions, or None if the user cancels
46 @rtype: L{selections.Selections} | None
47 """
48 if options.offline:
49 config.network_use = model.network_offline
51 iface_cache = config.iface_cache
53 # Try to load it as a feed. If it is a feed, it'll get cached. If not, it's a
54 # selections document and we return immediately.
55 maybe_selections = iface_cache.get_feed(iface_uri, selections_ok = True)
56 if isinstance(maybe_selections, selections.Selections):
57 if not select_only:
58 blocker = maybe_selections.download_missing(config)
59 if blocker:
60 logging.info(_("Waiting for selected implementations to be downloaded..."))
61 tasks.wait_for_blocker(blocker)
62 return maybe_selections
64 r = requirements.Requirements(iface_uri)
65 r.parse_options(options)
67 policy = Policy(config = config, requirements = r)
69 # Note that need_download() triggers a solve
70 if options.refresh or options.gui:
71 # We could run immediately, but the user asked us not to
72 can_run_immediately = False
73 else:
74 if select_only:
75 # --select-only: we only care that we've made a selection, not that we've cached the implementations
76 policy.need_download()
77 can_run_immediately = policy.ready
78 else:
79 can_run_immediately = not policy.need_download()
81 stale_feeds = [feed for feed in policy.solver.feeds_used if
82 not os.path.isabs(feed) and # Ignore local feeds (note: file might be missing too)
83 not feed.startswith('distribution:') and # Ignore (memory-only) PackageKit feeds
84 iface_cache.is_stale(iface_cache.get_feed(feed), config.freshness)]
86 if download_only and stale_feeds:
87 can_run_immediately = False
89 if can_run_immediately:
90 if stale_feeds:
91 if policy.network_use == model.network_offline:
92 logging.debug(_("No doing background update because we are in off-line mode."))
93 else:
94 # There are feeds we should update, but we can run without them.
95 # Do the update in the background while the program is running.
96 from zeroinstall.injector import background
97 background.spawn_background_update(policy, options.verbose > 0)
98 return policy.solver.selections
100 # If the user didn't say whether to use the GUI, choose for them.
101 if options.gui is None and os.environ.get('DISPLAY', None):
102 options.gui = True
103 # If we need to download anything, we might as well
104 # refresh all the feeds first.
105 options.refresh = True
106 logging.info(_("Switching to GUI mode... (use --console to disable)"))
108 if options.gui:
109 gui_args = policy.requirements.get_as_options()
110 if download_only:
111 # Just changes the button's label
112 gui_args.append('--download-only')
113 if options.refresh:
114 gui_args.append('--refresh')
115 if options.verbose:
116 gui_args.insert(0, '--verbose')
117 if options.verbose > 1:
118 gui_args.insert(0, '--verbose')
119 if options.with_store:
120 for x in options.with_store:
121 gui_args += ['--with-store', x]
122 if select_only:
123 gui_args.append('--select-only')
125 from zeroinstall import helpers
126 sels = helpers.get_selections_gui(iface_uri, gui_args, test_callback)
128 if not sels:
129 return None # Aborted
130 else:
131 # Note: --download-only also makes us stop and download stale feeds first.
132 downloaded = policy.solve_and_download_impls(refresh = options.refresh or download_only or False,
133 select_only = select_only)
134 if downloaded:
135 tasks.wait_for_blocker(downloaded)
136 sels = policy.solver.selections
138 return sels
140 def handle(config, options, args):
141 if len(args) != 1:
142 raise UsageError()
143 iface_uri = model.canonical_iface_uri(args[0])
145 sels = get_selections(config, options, iface_uri,
146 select_only = True, download_only = False, test_callback = None)
147 if not sels:
148 sys.exit(1) # Aborted by user
150 if options.xml:
151 show_xml(sels)
152 else:
153 show_human(sels, config.stores)
155 def show_xml(sels):
156 doc = sels.toDOM()
157 doc.writexml(sys.stdout)
158 sys.stdout.write('\n')
160 def show_human(sels, stores):
161 from zeroinstall import zerostore
162 done = set() # detect cycles
163 def print_node(uri, commands, indent):
164 if uri in done: return
165 done.add(uri)
166 impl = sels.selections.get(uri, None)
167 print(indent + "- URI:", uri)
168 if impl:
169 print(indent + " Version:", impl.version)
170 #print indent + " Command:", command
171 try:
172 if impl.id.startswith('package:'):
173 path = "(" + impl.id + ")"
174 else:
175 path = impl.local_path or stores.lookup_any(impl.digests)
176 except zerostore.NotStored:
177 path = "(not cached)"
178 print(indent + " Path:", path)
179 indent += " "
181 deps = impl.dependencies
182 for c in commands:
183 deps += impl.get_command(c).requires
184 for child in deps:
185 print_node(child.interface, child.get_required_commands(), indent)
186 else:
187 print(indent + " No selected version")
190 if sels.command:
191 print_node(sels.interface, [sels.command], "")
192 else:
193 print_node(sels.interface, [], "")