"""
The B{0launch} command-line interface.
This code is here, rather than in B{0launch} itself, simply so that it gets byte-compiled at
install time.
"""
import os, sys
from optparse import OptionParser
import logging
from zeroinstall.injector import model, download, autopolicy, namespaces
#def program_log(msg): os.access('MARK: 0launch: ' + msg, os.F_OK)
#import __main__
#__main__.__builtins__.program_log = program_log
#program_log('0launch ' + ' '.join((sys.argv[1:])))
def _list_interfaces(args):
from zeroinstall.injector.iface_cache import iface_cache
if len(args) == 0:
matches = iface_cache.list_all_interfaces()
elif len(args) == 1:
match = args[0].lower()
matches = [i for i in iface_cache.list_all_interfaces() if match in i.lower()]
else:
raise UsageError()
matches.sort()
for i in matches:
print i
def _import_interface(args):
from zeroinstall.injector import gpg, handler, trust
from zeroinstall.injector.iface_cache import iface_cache, PendingFeed
from xml.dom import minidom
for x in args:
if not os.path.isfile(x):
raise model.SafeException("File '%s' does not exist" % x)
logging.info("Importing from file '%s'", x)
signed_data = file(x)
data, sigs = gpg.check_stream(signed_data)
doc = minidom.parseString(data.read())
uri = doc.documentElement.getAttribute('uri')
if not uri:
raise model.SafeException("Missing 'uri' attribute on root element in '%s'" % x)
iface = iface_cache.get_interface(uri)
logging.info("Importing information about interface %s", iface)
signed_data.seek(0)
def keys_ready():
if not iface_cache.update_interface_if_trusted(iface, pending.sigs, pending.new_xml):
handler.confirm_trust_keys(iface, pending.sigs, pending.new_xml)
trust.trust_db.watchers.append(lambda: keys_ready())
pending = PendingFeed(uri, signed_data)
iface_cache.add_pending(pending)
handler = handler.Handler()
pending.begin_key_downloads(handler, keys_ready)
handler.wait_for_downloads()
def _manage_feeds(options, args):
from zeroinstall.injector import iface_cache, writer
from xml.dom import minidom
if not args: raise UsageError()
for x in args:
print "Feed '%s':\n" % x
x = model.canonical_iface_uri(x)
policy = autopolicy.AutoPolicy(x, download_only = True, dry_run = options.dry_run)
if options.offline:
policy.network_use = model.network_offline
policy.recalculate_with_dl()
interfaces = policy.get_feed_targets(policy.root)
for i in range(len(interfaces)):
feed = interfaces[i].get_feed(x)
if feed:
print "%d) Remove as feed for '%s'" % (i + 1, interfaces[i].uri)
else:
print "%d) Add as feed for '%s'" % (i + 1, interfaces[i].uri)
print
while True:
try:
i = raw_input('Enter a number, or CTRL-C to cancel [1]: ').strip()
except KeyboardInterrupt:
print
raise model.SafeException("Aborted at user request.")
if i == '':
i = 1
else:
try:
i = int(i)
except ValueError:
i = 0
if i > 0 and i <= len(interfaces):
break
print "Invalid number. Try again. (1 to %d)" % len(interfaces)
iface = interfaces[i - 1]
feed = iface.get_feed(x)
if feed:
iface.feeds.remove(feed)
else:
iface.feeds.append(model.Feed(x, arch = None, user_override = True))
writer.save_interface(iface)
print "\nFeed list for interface '%s' is now:" % iface.get_name()
if iface.feeds:
for f in iface.feeds:
print "- " + f.uri
else:
print "(no feeds)"
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
else:
raise UsageError()
iface_uri = model.canonical_iface_uri(args[0])
policy = autopolicy.AutoPolicy(iface_uri,
download_only = bool(options.download_only),
dry_run = options.dry_run,
src = options.source)
if options.before or options.not_before:
policy.root_restrictions.append(model.Restriction(model.parse_version(options.before),
model.parse_version(options.not_before)))
if options.offline:
policy.network_use = model.network_offline
if options.get_selections:
if len(args) > 1:
raise model.SafeException("Can't use arguments with --get-selections")
if options.main:
raise model.SafeException("Can't use --main with --get-selections")
# Note that need_download() triggers a recalculate()
if options.refresh or options.gui:
# We could run immediately, but the user asked us not to
can_run_immediately = False
else:
can_run_immediately = (not policy.need_download()) and policy.ready
if options.download_only and policy.stale_feeds:
can_run_immediately = False
if can_run_immediately:
if policy.stale_feeds:
if policy.network_use == model.network_offline:
logging.debug("No doing background update because we are in off-line mode.")
else:
# There are feeds we should update, but we can run without them.
# Do the update in the background while the program is running.
import background
background.spawn_background_update(policy, options.verbose > 0)
if options.get_selections:
_get_selections(policy)
else:
policy.execute(args[1:], main = options.main, wrapper = options.wrapper)
assert options.dry_run or options.download_only
return
# If the user didn't say whether to use the GUI, choose for them.
if options.gui is None and os.environ.get('DISPLAY', None):
options.gui = True
# If we need to download anything, we might as well
# refresh all the interfaces first. Also, this triggers
# the 'checking for updates' box, which is non-interactive
# when there are no changes to the selection.
options.refresh = True
logging.info("Switching to GUI mode... (use --console to disable)")
prog_args = args[1:]
try:
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:
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, prog_args, options)
if not sels:
sys.exit(1) # 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, options.wrapper)
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, prog_args, options = None):
"""Run the GUI to get the selections.
prog_args and options are used only if the GUI requests a test.
"""
from zeroinstall import helpers
def test_callback(sels):
from zeroinstall.injector import run
return run.test_selections(sels, prog_args,
bool(options and options.dry_run),
options and options.main)
return helpers.get_selections_gui(iface_uri, gui_args, test_callback)
def _get_selections(policy):
import selections
doc = selections.Selections(policy).toDOM()
doc.writexml(sys.stdout)
sys.stdout.write('\n')
class UsageError(Exception): pass
def main(command_args):
"""Act as if 0launch was run with the given arguments.
@arg command_args: array of arguments (e.g. C{sys.argv[1:]})
@type command_args: [str]
"""
# Ensure stdin, stdout and stderr FDs exist, to avoid confusion
for std in (0, 1, 2):
try:
os.fstat(std)
except OSError:
fd = os.open('/dev/null', os.O_RDONLY)
if fd != std:
os.dup2(fd, std)
os.close(fd)
parser = OptionParser(usage="usage: %prog [options] interface [args]\n"
" %prog --list [search-term]\n"
" %prog --import [signed-interface-files]\n"
" %prog --feed [interface]")
parser.add_option("", "--before", help="choose a version before this", metavar='VERSION')
parser.add_option("-c", "--console", help="never use GUI", action='store_false', dest='gui')
parser.add_option("-d", "--download-only", help="fetch but don't run", action='store_true')
parser.add_option("-D", "--dry-run", help="just print actions", action='store_true')
parser.add_option("-f", "--feed", help="add or remove a feed", action='store_true')
parser.add_option("", "--get-selections", help="write selected versions as XML", action='store_true')
parser.add_option("-g", "--gui", help="show graphical policy editor", action='store_true')
parser.add_option("-i", "--import", help="import from files, not from the network", action='store_true')
parser.add_option("-l", "--list", help="list all known interfaces", action='store_true')
parser.add_option("-m", "--main", help="name of the file to execute")
parser.add_option("", "--not-before", help="minimum version to choose", metavar='VERSION')
parser.add_option("-o", "--offline", help="try to avoid using the network", action='store_true')
parser.add_option("-r", "--refresh", help="refresh all used interfaces", action='store_true')
parser.add_option("", "--set-selections", help="run versions specified in XML file", metavar='FILE')
parser.add_option("-s", "--source", help="select source code", action='store_true')
parser.add_option("-v", "--verbose", help="more verbose output", action='count')
parser.add_option("-V", "--version", help="display version information", action='store_true')
parser.add_option("-w", "--wrapper", help="execute program using a debugger, etc", metavar='COMMAND')
parser.disable_interspersed_args()
(options, args) = parser.parse_args(command_args)
if options.verbose:
logger = logging.getLogger()
if options.verbose == 1:
logger.setLevel(logging.INFO)
else:
logger.setLevel(logging.DEBUG)
import zeroinstall
logging.info("Running 0launch %s %s; Python %s", zeroinstall.version, repr(args), sys.version)
try:
if options.list:
_list_interfaces(args)
elif options.version:
import zeroinstall
print "0launch (zero-install) " + zeroinstall.version
print "Copyright (C) 2007 Thomas Leonard"
print "This program comes with ABSOLUTELY NO WARRANTY,"
print "to the extent permitted by law."
print "You may redistribute copies of this program"
print "under the terms of the GNU Lesser General Public License."
print "For more information about these matters, see the file named COPYING."
elif options.set_selections:
from zeroinstall.injector import selections, qdom, run
sels = selections.Selections(qdom.parse(file(options.set_selections)))
run.execute_selections(sels, args, options.dry_run, options.main, options.wrapper)
elif getattr(options, 'import'):
_import_interface(args)
elif options.feed:
_manage_feeds(options, args)
else:
_normal_mode(options, args)
except UsageError:
parser.print_help()
sys.exit(1)
except model.SafeException, ex:
if options.verbose: raise
print >>sys.stderr, ex
sys.exit(1)
syntax highlighted by Code2HTML, v. 0.9.1