2 The B{0install select} command-line interface.
5 # Copyright (C) 2011, Thomas Leonard
6 # See the README file for details, or visit http://0install.net.
8 from __future__
import print_function
13 from zeroinstall
import _
14 from zeroinstall
.cmd
import UsageError
15 from zeroinstall
.injector
import model
, selections
, requirements
16 from zeroinstall
.injector
.driver
import Driver
17 from zeroinstall
.support
import tasks
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
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
):
58 blocker
= maybe_selections
.download_missing(config
)
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 return get_selections_for(r
, config
, options
, select_only
, download_only
, test_callback
)
69 def get_selections_for(requirements
, config
, options
, select_only
, download_only
, test_callback
):
70 """Get selections for given requirements.
73 config
.network_use
= model
.network_offline
75 iface_cache
= config
.iface_cache
77 driver
= Driver(config
= config
, requirements
= requirements
)
79 # Note that need_download() triggers a solve
80 if options
.refresh
or options
.gui
:
81 # We could run immediately, but the user asked us not to
82 can_run_immediately
= False
85 # --select-only: we only care that we've made a selection, not that we've cached the implementations
86 driver
.need_download()
87 can_run_immediately
= driver
.solver
.ready
89 can_run_immediately
= not driver
.need_download()
91 stale_feeds
= [feed
for feed
in driver
.solver
.feeds_used
if
92 not feed
.startswith('distribution:') and # Ignore (memory-only) PackageKit feeds
93 iface_cache
.is_stale(feed
, config
.freshness
)]
95 if download_only
and stale_feeds
:
96 can_run_immediately
= False
98 if can_run_immediately
:
100 if config
.network_use
== model
.network_offline
:
101 logging
.debug(_("No doing background update because we are in off-line mode."))
103 # There are feeds we should update, but we can run without them.
104 # Do the update in the background while the program is running.
105 from zeroinstall
.injector
import background
106 background
.spawn_background_update(driver
, options
.verbose
)
107 return driver
.solver
.selections
109 # If we need to download anything, we might as well
110 # refresh all the feeds first.
111 options
.refresh
= True
113 if options
.gui
!= False:
114 # If the user didn't say whether to use the GUI, choose for them.
115 gui_args
= driver
.requirements
.get_as_options()
117 # Just changes the button's label
118 gui_args
.append('--download-only')
120 gui_args
.append('--refresh')
122 gui_args
.insert(0, '--verbose')
123 if options
.verbose
> 1:
124 gui_args
.insert(0, '--verbose')
125 if options
.with_store
:
126 for x
in options
.with_store
:
127 gui_args
+= ['--with-store', x
]
129 gui_args
.append('--select-only')
131 from zeroinstall
import helpers
132 sels
= helpers
.get_selections_gui(requirements
.interface_uri
, gui_args
, test_callback
, use_gui
= options
.gui
)
135 return None # Aborted
136 elif sels
is helpers
.DontUseGUI
:
142 # Note: --download-only also makes us stop and download stale feeds first.
143 downloaded
= driver
.solve_and_download_impls(refresh
= options
.refresh
or download_only
or False,
144 select_only
= select_only
)
146 tasks
.wait_for_blocker(downloaded
)
147 sels
= driver
.solver
.selections
151 def handle(config
, options
, args
):
155 app
= config
.app_mgr
.lookup_app(args
[0], missing_ok
= True)
157 sels
= app
.get_selections()
159 r
= app
.get_requirements()
160 do_select
= r
.parse_update_options(options
)
161 iface_uri
= sels
.interface
163 iface_uri
= model
.canonical_iface_uri(args
[0])
166 if do_select
or options
.gui
:
167 sels
= get_selections(config
, options
, iface_uri
,
168 select_only
= True, download_only
= False, test_callback
= None)
170 sys
.exit(1) # Aborted by user
175 show_human(sels
, config
.stores
)
176 if app
is not None and do_select
:
177 print(_("(use '0install update' to save the new parameters)"))
181 doc
.writexml(sys
.stdout
)
182 sys
.stdout
.write('\n')
184 def show_human(sels
, stores
):
185 done
= set() # detect cycles
186 def print_node(uri
, commands
, indent
):
187 if uri
in done
: return
189 impl
= sels
.selections
.get(uri
, None)
190 print(indent
+ "- URI:", uri
)
192 print(indent
+ " Version:", impl
.version
)
193 #print indent + " Command:", command
194 if impl
.id.startswith('package:'):
195 path
= "(" + impl
.id + ")"
197 path
= impl
.get_path(stores
, missing_ok
= True) or _("(not cached)")
198 print(indent
+ " Path:", path
)
201 deps
= impl
.dependencies
203 deps
+= impl
.get_command(c
).requires
205 print_node(child
.interface
, child
.get_required_commands(), indent
)
207 print(indent
+ " No selected version")
211 print_node(sels
.interface
, [sels
.command
], "")
213 print_node(sels
.interface
, [], "")