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.
11 from zeroinstall
import _
12 from zeroinstall
.cmd
import UsageError
13 from zeroinstall
.injector
import model
, selections
, requirements
14 from zeroinstall
.injector
.policy
import Policy
15 from zeroinstall
.support
import tasks
19 def add_generic_select_options(parser
):
20 """All options for selecting."""
21 parser
.add_option("", "--before", help=_("choose a version before this"), metavar
='VERSION')
22 parser
.add_option("", "--command", help=_("command to select"), metavar
='COMMAND')
23 parser
.add_option("", "--cpu", help=_("target CPU type"), metavar
='CPU')
24 parser
.add_option("", "--message", help=_("message to display when interacting with user"))
25 parser
.add_option("", "--not-before", help=_("minimum version to choose"), metavar
='VERSION')
26 parser
.add_option("-o", "--offline", help=_("try to avoid using the network"), action
='store_true')
27 parser
.add_option("", "--os", help=_("target operation system type"), metavar
='OS')
28 parser
.add_option("-r", "--refresh", help=_("refresh all used interfaces"), action
='store_true')
29 parser
.add_option("-s", "--source", help=_("select source code"), action
='store_true')
31 def add_options(parser
):
32 """Options for 'select' and 'download' (but not 'run')"""
33 add_generic_select_options(parser
)
34 parser
.add_option("", "--xml", help=_("write selected versions as XML"), action
='store_true')
36 def get_selections(config
, options
, iface_uri
, select_only
, download_only
, test_callback
):
37 """Get selections for iface_uri, according to the options passed.
38 Will switch to GUI mode if necessary.
39 @param options: options from OptionParser
40 @param iface_uri: canonical URI of the interface
41 @param select_only: return immediately even if the selected versions aren't cached
42 @param download_only: wait for stale feeds, and display GUI button as Download, not Run
43 @return: the selected versions, or None if the user cancels
44 @rtype: L{selections.Selections} | None
47 config
.network_use
= model
.network_offline
49 iface_cache
= config
.iface_cache
51 # Try to load it as a feed. If it is a feed, it'll get cached. If not, it's a
52 # selections document and we return immediately.
53 maybe_selections
= iface_cache
.get_feed(iface_uri
, selections_ok
= True)
54 if isinstance(maybe_selections
, selections
.Selections
):
56 blocker
= maybe_selections
.download_missing(config
)
58 logging
.info(_("Waiting for selected implementations to be downloaded..."))
59 tasks
.wait_for_blocker(blocker
)
60 return maybe_selections
62 r
= requirements
.Requirements(iface_uri
)
63 r
.parse_options(options
)
65 policy
= Policy(config
= config
, requirements
= r
)
67 # Note that need_download() triggers a solve
68 if options
.refresh
or options
.gui
:
69 # We could run immediately, but the user asked us not to
70 can_run_immediately
= False
73 # --select-only: we only care that we've made a selection, not that we've cached the implementations
74 policy
.need_download()
75 can_run_immediately
= policy
.ready
77 can_run_immediately
= not policy
.need_download()
79 stale_feeds
= [feed
for feed
in policy
.solver
.feeds_used
if
80 not os
.path
.isabs(feed
) and # Ignore local feeds (note: file might be missing too)
81 not feed
.startswith('distribution:') and # Ignore (memory-only) PackageKit feeds
82 iface_cache
.is_stale(iface_cache
.get_feed(feed
), config
.freshness
)]
84 if download_only
and stale_feeds
:
85 can_run_immediately
= False
87 if can_run_immediately
:
89 if policy
.network_use
== model
.network_offline
:
90 logging
.debug(_("No doing background update because we are in off-line mode."))
92 # There are feeds we should update, but we can run without them.
93 # Do the update in the background while the program is running.
94 from zeroinstall
.injector
import background
95 background
.spawn_background_update(policy
, options
.verbose
> 0)
96 return policy
.solver
.selections
98 # If the user didn't say whether to use the GUI, choose for them.
99 if options
.gui
is None and os
.environ
.get('DISPLAY', None):
101 # If we need to download anything, we might as well
102 # refresh all the feeds first.
103 options
.refresh
= True
104 logging
.info(_("Switching to GUI mode... (use --console to disable)"))
107 gui_args
= policy
.requirements
.get_as_options()
109 # Just changes the button's label
110 gui_args
.append('--download-only')
112 gui_args
.append('--refresh')
114 gui_args
.insert(0, '--verbose')
115 if options
.verbose
> 1:
116 gui_args
.insert(0, '--verbose')
117 if options
.with_store
:
118 for x
in options
.with_store
:
119 gui_args
+= ['--with-store', x
]
121 gui_args
.append('--select-only')
123 from zeroinstall
import helpers
124 sels
= helpers
.get_selections_gui(iface_uri
, gui_args
, test_callback
)
127 return None # Aborted
129 # Note: --download-only also makes us stop and download stale feeds first.
130 downloaded
= policy
.solve_and_download_impls(refresh
= options
.refresh
or download_only
or False,
131 select_only
= select_only
)
133 tasks
.wait_for_blocker(downloaded
)
134 sels
= selections
.Selections(policy
)
138 def handle(config
, options
, args
):
141 iface_uri
= model
.canonical_iface_uri(args
[0])
143 sels
= get_selections(config
, options
, iface_uri
,
144 select_only
= True, download_only
= False, test_callback
= None)
146 sys
.exit(1) # Aborted by user
151 show_human(sels
, config
.stores
)
155 doc
.writexml(sys
.stdout
)
156 sys
.stdout
.write('\n')
158 def show_human(sels
, stores
):
159 from zeroinstall
import zerostore
160 done
= set() # detect cycles
161 def print_node(uri
, command
, indent
):
162 if uri
in done
: return
164 impl
= sels
.selections
.get(uri
, None)
165 print indent
+ "- URI:", uri
167 print indent
+ " Version:", impl
.version
169 if impl
.id.startswith('package:'):
170 path
= "(" + impl
.id + ")"
172 path
= impl
.local_path
or stores
.lookup_any(impl
.digests
)
173 except zerostore
.NotStored
:
174 path
= "(not cached)"
175 print indent
+ " Path:", path
177 deps
= impl
.dependencies
178 if command
is not None:
179 deps
+= sels
.commands
[command
].requires
181 if isinstance(child
, model
.InterfaceDependency
):
182 if child
.qdom
.name
== 'runner':
183 child_command
= command
+ 1
186 print_node(child
.interface
, child_command
, indent
)
188 print indent
+ " No selected version"
192 print_node(sels
.interface
, 0, "")
194 print_node(sels
.interface
, None, "")