More detailed bug reports about why no version could be selected
[zeroinstall/zeroinstall-rsl.git] / 0alias
blobaa3ca3073c60cacdf2e5cedc5eb90e8ce4ece650
1 #!/usr/bin/env python
2 import os, sys
3 from optparse import OptionParser
5 from zeroinstall.injector import reader, model
6 from zeroinstall import support, alias, helpers
7 from zeroinstall.support import basedir
9 def export(name, value):
10 """Try to guess the command to set an environment variable."""
11 shell = os.environ.get('SHELL', '?')
12 if 'csh' in shell:
13 return "setenv %s %s" % (name, value)
14 return "export %s=%s" % (name, value)
16 def find_path(paths):
17 """Find the first writable path in : separated list."""
18 for path in paths:
19 if os.path.realpath(path).startswith(basedir.xdg_cache_home):
20 pass # print "Skipping cache", first_path
21 elif not os.access(path, os.W_OK):
22 pass # print "No access", first_path
23 else:
24 break
25 else:
26 return None
28 return path
30 # Do this here so we can include it in the help message.
31 # But, don't abort if there isn't one because we might
32 # be doing something else (e.g. --manpage)
33 first_path = find_path(os.environ['PATH'].split(':'))
35 parser = OptionParser(usage="usage: %%prog [options] alias [interface [command]]\n\n"
36 "Creates a script in the first usable directory in $PATH\n"
37 "(%s) to run 'interface' unless overridden by --dir.\n"
38 "If no interface is given, edits the policy for an existing alias.\n"
39 "For interfaces providing more than one command, the desired command\n"
40 "may also be given." % first_path)
41 parser.add_option("-m", "--manpage", help="show the manual page for an existing alias", action='store_true')
42 parser.add_option("-r", "--resolve", help="show the URI for an alias", action='store_true')
43 parser.add_option("-V", "--version", help="display version information", action='store_true')
44 parser.add_option("-d", "--dir", help="install in DIR", dest="user_path", metavar="DIR")
45 parser.disable_interspersed_args()
47 (options, args) = parser.parse_args()
49 if options.version:
50 import zeroinstall
51 print "0alias (zero-install) " + zeroinstall.version
52 print "Copyright (C) 2007 Thomas Leonard"
53 print "This program comes with ABSOLUTELY NO WARRANTY,"
54 print "to the extent permitted by law."
55 print "You may redistribute copies of this program"
56 print "under the terms of the GNU Lesser General Public License."
57 print "For more information about these matters, see the file named COPYING."
58 sys.exit(0)
60 if options.manpage:
61 if len(args) != 1:
62 os.execlp('man', 'man', *args)
63 sys.exit(1)
65 if len(args) < 1 or len(args) > 3:
66 parser.print_help()
67 sys.exit(1)
68 alias_prog, interface_uri, main = (list(args) + [None, None])[:3]
70 if options.resolve or options.manpage:
71 if interface_uri is not None:
72 parser.print_help()
73 sys.exit(1)
75 if options.user_path:
76 first_path = options.user_path
77 if not os.path.isdir(first_path):
78 print >>sys.stderr, "Directory '%s' does not exist." % first_path
79 sys.exit(1)
81 if interface_uri is None:
82 try:
83 if not os.path.isabs(alias_prog):
84 full_path = support.find_in_path(alias_prog)
85 if not full_path:
86 raise alias.NotAnAliasScript("Not found in $PATH: " + alias_prog)
87 else:
88 full_path = alias_prog
90 interface_uri, main = alias.parse_script(full_path)
91 except alias.NotAnAliasScript, ex:
92 if options.manpage:
93 os.execlp('man', 'man', *args)
94 print >>sys.stderr, str(ex)
95 sys.exit(1)
97 interface_uri = model.canonical_iface_uri(interface_uri)
99 if options.resolve:
100 print interface_uri
101 sys.exit(0)
103 if options.manpage:
104 sels = helpers.ensure_cached(interface_uri)
105 if not sels:
106 # Cancelled by user
107 sys.exit(1)
109 selected_impl = sels.selections[interface_uri]
110 if selected_impl.id.startswith('/'):
111 impl_path = selected_impl.id
112 else:
113 from zeroinstall.injector.iface_cache import iface_cache
114 impl_path = iface_cache.stores.lookup(selected_impl.id)
116 if main is None:
117 main = selected_impl.main
118 if main is None:
119 print >>sys.stderr, "No main program for interface '%s'" % interface_uri
120 sys.exit(1)
122 prog_name = os.path.basename(main)
123 alias_name = os.path.basename(args[0])
125 assert impl_path
127 # TODO: the feed should say where the man-pages are, but for now we'll accept
128 # a directory called man in some common locations...
129 for mandir in ['man', 'share/man', 'usr/man', 'usr/share/man']:
130 manpath = os.path.join(impl_path, mandir)
131 if os.path.isdir(manpath):
132 # Note: unlike "man -M", this also copes with LANG settings...
133 os.environ['MANPATH'] = manpath
134 os.execlp('man', 'man', prog_name)
135 sys.exit(1)
137 # No man directory given or found, so try searching for man files
139 manpages = []
140 for root, dirs, files in os.walk(impl_path):
141 for f in files:
142 if f.endswith('.gz'):
143 manpage_file = f[:-3]
144 else:
145 manpage_file = f
146 if manpage_file.endswith('.1') or \
147 manpage_file.endswith('.6') or \
148 manpage_file.endswith('.8'):
149 manpage_prog = manpage_file[:-2]
150 if manpage_prog == prog_name or manpage_prog == alias_name:
151 os.execlp('man', 'man', os.path.join(root, f))
152 sys.exit(1)
153 else:
154 manpages.append((root, f))
156 print "No matching manpage was found for '%s' (%s)" % (alias_name, interface_uri)
157 if manpages:
158 print "These non-matching man-pages were found, however:"
159 for root, file in manpages:
160 print os.path.join(root, file)
161 sys.exit(1)
163 if first_path is None:
164 print >>sys.stderr, ("No writable non-cache directory in $PATH, which currently contains:\n\n%s\n\n"
165 "To create a directory for your scripts, use these commands:\n"
166 "$ mkdir ~/bin\n"
167 "$ %s\n"
168 "or specify a writable path with --path"
169 % ('\n'.join(os.environ['PATH'].split(':')), export('PATH', '$HOME/bin:$PATH')))
170 sys.exit(1)
172 if len(args) == 1:
173 os.execlp('0launch', '0launch', '-gd', '--', interface_uri)
174 sys.exit(1)
176 try:
177 interface = model.Interface(interface_uri)
178 if not reader.update_from_cache(interface):
179 print >>sys.stderr, "Interface '%s' not currently in cache. Fetching..." % interface_uri
180 if os.spawnlp(os.P_WAIT, '0launch', '0launch', '-d', interface_uri):
181 raise model.SafeException("0launch failed")
182 if not reader.update_from_cache(interface):
183 raise model.SafeException("Interface still not in cache. Aborting.")
185 script = os.path.join(first_path, alias_prog)
186 if os.path.exists(script):
187 raise model.SafeException("File '%s' already exists. Delete it first." % script)
188 sys.exit(1)
189 except model.SafeException, ex:
190 print >>sys.stderr, ex
191 sys.exit(1)
193 wrapper = file(script, 'w')
194 alias.write_script(wrapper, interface_uri, main)
196 # Make new script executable
197 os.chmod(script, 0111 | os.fstat(wrapper.fileno()).st_mode)
198 wrapper.close()
200 print "Created script '%s'." % script
201 print "To edit policy: 0alias %s" % alias_prog
202 print "(note: some shells require you to type 'rehash' now)"