2 The B{0launch} command-line interface.
4 This code is here, rather than in B{0launch} itself, simply so that it gets byte-compiled at
9 from optparse
import OptionParser
12 from zeroinstall
.injector
import model
, download
, autopolicy
, namespaces
14 #def program_log(msg): os.access('MARK: 0launch: ' + msg, os.F_OK)
16 #__main__.__builtins__.program_log = program_log
17 #program_log('0launch ' + ' '.join((sys.argv[1:])))
19 def _list_interfaces(args
):
20 from zeroinstall
.injector
.iface_cache
import iface_cache
22 matches
= iface_cache
.list_all_interfaces()
24 match
= args
[0].lower()
25 matches
= [i
for i
in iface_cache
.list_all_interfaces() if match
in i
.lower()]
33 def _import_interface(args
):
34 from zeroinstall
.injector
import gpg
, handler
, trust
35 from zeroinstall
.injector
.iface_cache
import iface_cache
, PendingFeed
36 from xml
.dom
import minidom
38 if not os
.path
.isfile(x
):
39 raise model
.SafeException("File '%s' does not exist" % x
)
40 logging
.info("Importing from file '%s'", x
)
42 data
, sigs
= gpg
.check_stream(signed_data
)
43 doc
= minidom
.parseString(data
.read())
44 uri
= doc
.documentElement
.getAttribute('uri')
46 raise model
.SafeException("Missing 'uri' attribute on root element in '%s'" % x
)
47 iface
= iface_cache
.get_interface(uri
)
48 logging
.info("Importing information about interface %s", iface
)
52 if not iface_cache
.update_interface_if_trusted(iface
, pending
.sigs
, pending
.new_xml
):
53 handler
.confirm_trust_keys(iface
, pending
.sigs
, pending
.new_xml
)
54 trust
.trust_db
.watchers
.append(lambda: keys_ready())
56 pending
= PendingFeed(uri
, signed_data
)
57 iface_cache
.add_pending(pending
)
59 handler
= handler
.Handler()
60 pending
.begin_key_downloads(handler
, keys_ready
)
61 handler
.wait_for_downloads()
63 def _manage_feeds(options
, args
):
64 from zeroinstall
.injector
import iface_cache
, writer
65 from xml
.dom
import minidom
66 if not args
: raise UsageError()
68 print "Feed '%s':\n" % x
69 x
= model
.canonical_iface_uri(x
)
70 policy
= autopolicy
.AutoPolicy(x
, download_only
= True, dry_run
= options
.dry_run
)
72 policy
.network_use
= model
.network_offline
73 policy
.recalculate_with_dl()
74 interfaces
= policy
.get_feed_targets(policy
.root
)
75 for i
in range(len(interfaces
)):
76 feed
= interfaces
[i
].get_feed(x
)
78 print "%d) Remove as feed for '%s'" % (i
+ 1, interfaces
[i
].uri
)
80 print "%d) Add as feed for '%s'" % (i
+ 1, interfaces
[i
].uri
)
84 i
= raw_input('Enter a number, or CTRL-C to cancel [1]: ').strip()
85 except KeyboardInterrupt:
87 raise model
.SafeException("Aborted at user request.")
95 if i
> 0 and i
<= len(interfaces
):
97 print "Invalid number. Try again. (1 to %d)" % len(interfaces
)
98 iface
= interfaces
[i
- 1]
99 feed
= iface
.get_feed(x
)
101 iface
.extra_feeds
.remove(feed
)
103 iface
.extra_feeds
.append(model
.Feed(x
, arch
= None, user_override
= True))
104 writer
.save_interface(iface
)
105 print "\nFeed list for interface '%s' is now:" % iface
.get_name()
107 for f
in iface
.feeds
:
112 def _normal_mode(options
, args
):
114 # You can use -g on its own to edit the GUI's own policy
115 # Otherwise, failing to give an interface is an error
117 args
= [namespaces
.injector_gui_uri
]
118 options
.download_only
= True
122 iface_uri
= model
.canonical_iface_uri(args
[0])
124 policy
= autopolicy
.AutoPolicy(iface_uri
,
125 download_only
= bool(options
.download_only
),
126 dry_run
= options
.dry_run
,
127 src
= options
.source
)
129 if options
.before
or options
.not_before
:
130 policy
.root_restrictions
.append(model
.Restriction(model
.parse_version(options
.before
),
131 model
.parse_version(options
.not_before
)))
134 policy
.network_use
= model
.network_offline
136 if options
.get_selections
:
138 raise model
.SafeException("Can't use arguments with --get-selections")
140 raise model
.SafeException("Can't use --main with --get-selections")
142 # Note that need_download() triggers a recalculate()
143 if options
.refresh
or options
.gui
:
144 # We could run immediately, but the user asked us not to
145 can_run_immediately
= False
147 can_run_immediately
= (not policy
.need_download()) and policy
.ready
149 if options
.download_only
and policy
.stale_feeds
:
150 can_run_immediately
= False
152 if can_run_immediately
:
153 if policy
.stale_feeds
:
154 if policy
.network_use
== model
.network_offline
:
155 logging
.debug("No doing background update because we are in off-line mode.")
157 # There are feeds we should update, but we can run without them.
158 # Do the update in the background while the program is running.
160 background
.spawn_background_update(policy
, options
.verbose
> 0)
161 if options
.get_selections
:
162 _get_selections(policy
)
164 policy
.execute(args
[1:], main
= options
.main
, wrapper
= options
.wrapper
)
165 assert options
.dry_run
or options
.download_only
168 # If the user didn't say whether to use the GUI, choose for them.
169 if options
.gui
is None and os
.environ
.get('DISPLAY', None):
171 # If we need to download anything, we might as well
172 # refresh all the interfaces first. Also, this triggers
173 # the 'checking for updates' box, which is non-interactive
174 # when there are no changes to the selection.
175 options
.refresh
= True
176 logging
.info("Switching to GUI mode... (use --console to disable)")
182 from zeroinstall
.injector
import run
184 if options
.download_only
:
185 # Just changes the button's label
186 gui_args
.append('--download-only')
188 gui_args
.append('--refresh')
189 if options
.not_before
:
190 gui_args
.insert(0, options
.not_before
)
191 gui_args
.insert(0, '--not-before')
193 gui_args
.insert(0, options
.before
)
194 gui_args
.insert(0, '--before')
196 gui_args
.insert(0, '--source')
197 sels
= _fork_gui(iface_uri
, gui_args
, prog_args
, options
)
199 sys
.exit(1) # Aborted
200 if options
.get_selections
:
202 doc
.writexml(sys
.stdout
)
203 sys
.stdout
.write('\n')
204 elif not options
.download_only
:
205 run
.execute_selections(sels
, prog_args
, options
.dry_run
, options
.main
, options
.wrapper
)
207 #program_log('download_and_execute ' + iface_uri)
208 policy
.download_and_execute(prog_args
, refresh
= bool(options
.refresh
), main
= options
.main
)
209 except autopolicy
.NeedDownload
, ex
:
210 # This only happens for dry runs
213 def _fork_gui(iface_uri
, gui_args
, prog_args
, options
= None):
214 """Run the GUI to get the selections.
215 prog_args and options are used only if the GUI requests a test.
217 from zeroinstall
import helpers
218 def test_callback(sels
):
219 from zeroinstall
.injector
import run
220 return run
.test_selections(sels
, prog_args
,
221 bool(options
and options
.dry_run
),
222 options
and options
.main
)
223 return helpers
.get_selections_gui(iface_uri
, gui_args
, test_callback
)
225 def _get_selections(policy
):
227 doc
= selections
.Selections(policy
).toDOM()
228 doc
.writexml(sys
.stdout
)
229 sys
.stdout
.write('\n')
231 class UsageError(Exception): pass
233 def main(command_args
):
234 """Act as if 0launch was run with the given arguments.
235 @arg command_args: array of arguments (e.g. C{sys.argv[1:]})
236 @type command_args: [str]
238 # Ensure stdin, stdout and stderr FDs exist, to avoid confusion
239 for std
in (0, 1, 2):
243 fd
= os
.open('/dev/null', os
.O_RDONLY
)
248 parser
= OptionParser(usage
="usage: %prog [options] interface [args]\n"
249 " %prog --list [search-term]\n"
250 " %prog --import [signed-interface-files]\n"
251 " %prog --feed [interface]")
252 parser
.add_option("", "--before", help="choose a version before this", metavar
='VERSION')
253 parser
.add_option("-c", "--console", help="never use GUI", action
='store_false', dest
='gui')
254 parser
.add_option("-d", "--download-only", help="fetch but don't run", action
='store_true')
255 parser
.add_option("-D", "--dry-run", help="just print actions", action
='store_true')
256 parser
.add_option("-f", "--feed", help="add or remove a feed", action
='store_true')
257 parser
.add_option("", "--get-selections", help="write selected versions as XML", action
='store_true')
258 parser
.add_option("-g", "--gui", help="show graphical policy editor", action
='store_true')
259 parser
.add_option("-i", "--import", help="import from files, not from the network", action
='store_true')
260 parser
.add_option("-l", "--list", help="list all known interfaces", action
='store_true')
261 parser
.add_option("-m", "--main", help="name of the file to execute")
262 parser
.add_option("", "--not-before", help="minimum version to choose", metavar
='VERSION')
263 parser
.add_option("-o", "--offline", help="try to avoid using the network", action
='store_true')
264 parser
.add_option("-r", "--refresh", help="refresh all used interfaces", action
='store_true')
265 parser
.add_option("", "--set-selections", help="run versions specified in XML file", metavar
='FILE')
266 parser
.add_option("-s", "--source", help="select source code", action
='store_true')
267 parser
.add_option("-v", "--verbose", help="more verbose output", action
='count')
268 parser
.add_option("-V", "--version", help="display version information", action
='store_true')
269 parser
.add_option("-w", "--wrapper", help="execute program using a debugger, etc", metavar
='COMMAND')
270 parser
.disable_interspersed_args()
272 (options
, args
) = parser
.parse_args(command_args
)
275 logger
= logging
.getLogger()
276 if options
.verbose
== 1:
277 logger
.setLevel(logging
.INFO
)
279 logger
.setLevel(logging
.DEBUG
)
281 logging
.info("Running 0launch %s %s; Python %s", zeroinstall
.version
, repr(args
), sys
.version
)
285 _list_interfaces(args
)
286 elif options
.version
:
288 print "0launch (zero-install) " + zeroinstall
.version
289 print "Copyright (C) 2007 Thomas Leonard"
290 print "This program comes with ABSOLUTELY NO WARRANTY,"
291 print "to the extent permitted by law."
292 print "You may redistribute copies of this program"
293 print "under the terms of the GNU Lesser General Public License."
294 print "For more information about these matters, see the file named COPYING."
295 elif options
.set_selections
:
296 from zeroinstall
.injector
import selections
, qdom
, run
297 sels
= selections
.Selections(qdom
.parse(file(options
.set_selections
)))
298 run
.execute_selections(sels
, args
, options
.dry_run
, options
.main
, options
.wrapper
)
299 elif getattr(options
, 'import'):
300 _import_interface(args
)
302 _manage_feeds(options
, args
)
304 _normal_mode(options
, args
)
308 except model
.SafeException
, ex
:
309 if options
.verbose
: raise
310 print >>sys
.stderr
, ex