Add rubygems to the list of natively supported file formats (since it is basically...
[zeroinstall/solver.git] / 0alias
blob32aef2f308b559cdefbf9b83969a28bbb4dd2d93
1 #!/usr/bin/env python
2 # Copyright (C) 2010, Thomas Leonard
3 # See the README file for details, or visit http://0install.net.
5 import locale
6 from logging import warn
7 try:
8 locale.setlocale(locale.LC_ALL, '')
9 except locale.Error:
10 warn('Error setting locale (eg. Invalid locale)')
12 import os, sys
14 ## PATH ##
16 from optparse import OptionParser
18 from zeroinstall.injector import reader, model
19 from zeroinstall import support, alias, helpers
20 from zeroinstall.support import basedir
22 def export(name, value):
23 """Try to guess the command to set an environment variable."""
24 shell = os.environ.get('SHELL', '?')
25 if 'csh' in shell:
26 return "setenv %s %s" % (name, value)
27 return "export %s=%s" % (name, value)
29 def find_path(paths):
30 """Find the first writable path in : separated list."""
31 for path in paths:
32 if os.path.realpath(path).startswith(basedir.xdg_cache_home):
33 pass # print "Skipping cache", first_path
34 elif not os.access(path, os.W_OK):
35 pass # print "No access", first_path
36 else:
37 break
38 else:
39 return None
41 return path
43 # Do this here so we can include it in the help message.
44 # But, don't abort if there isn't one because we might
45 # be doing something else (e.g. --manpage)
46 first_path = find_path(os.environ['PATH'].split(':'))
47 in_path = first_path is not None
48 if not in_path:
49 first_path = os.path.expanduser('~/bin/')
51 parser = OptionParser(usage="usage: %%prog [options] alias [interface [main]]\n\n"
52 "Creates a script to run 'interface' (will be created in\n"
53 "%s unless overridden by --dir).\n"
54 "If no interface is given, edits the policy for an existing alias.\n"
55 "For interfaces providing more than one executable, the desired\n"
56 "'main' binary may also be given." % first_path)
57 parser.add_option("-m", "--manpage", help="show the manual page for an existing alias", action='store_true')
58 parser.add_option("-r", "--resolve", help="show the URI for an alias", action='store_true')
59 parser.add_option("-V", "--version", help="display version information", action='store_true')
60 parser.add_option("-d", "--dir", help="install in DIR", dest="user_path", metavar="DIR")
61 parser.disable_interspersed_args()
63 (options, args) = parser.parse_args()
65 if options.version:
66 import zeroinstall
67 print "0alias (zero-install) " + zeroinstall.version
68 print "Copyright (C) 2010 Thomas Leonard"
69 print "This program comes with ABSOLUTELY NO WARRANTY,"
70 print "to the extent permitted by law."
71 print "You may redistribute copies of this program"
72 print "under the terms of the GNU Lesser General Public License."
73 print "For more information about these matters, see the file named COPYING."
74 sys.exit(0)
76 if options.manpage:
77 if len(args) != 1:
78 os.execlp('man', 'man', *args)
79 sys.exit(1)
81 if len(args) < 1 or len(args) > 3:
82 parser.print_help()
83 sys.exit(1)
84 alias_prog, interface_uri, main = (list(args) + [None, None])[:3]
86 if options.resolve or options.manpage:
87 if interface_uri is not None:
88 parser.print_help()
89 sys.exit(1)
91 if options.user_path:
92 first_path = options.user_path
94 if interface_uri is None:
95 try:
96 if not os.path.isabs(alias_prog):
97 full_path = support.find_in_path(alias_prog)
98 if not full_path:
99 raise alias.NotAnAliasScript("Not found in $PATH: " + alias_prog)
100 else:
101 full_path = alias_prog
103 interface_uri, main = alias.parse_script(full_path)
104 except alias.NotAnAliasScript, ex:
105 if options.manpage:
106 os.execlp('man', 'man', *args)
107 print >>sys.stderr, str(ex)
108 sys.exit(1)
110 interface_uri = model.canonical_iface_uri(interface_uri)
112 if options.resolve:
113 print interface_uri
114 sys.exit(0)
116 if options.manpage:
117 sels = helpers.ensure_cached(interface_uri)
118 if not sels:
119 # Cancelled by user
120 sys.exit(1)
122 if sels.commands:
123 selected_command = sels.commands[0]
124 else:
125 print >>sys.stderr, "No <command> in selections!"
126 selected_impl = sels.selections[interface_uri]
128 from zeroinstall.injector.iface_cache import iface_cache
129 impl_path = selected_impl.local_path or iface_cache.stores.lookup_any(selected_impl.digests)
131 if main is None:
132 main = selected_command.path
133 if main is None:
134 print >>sys.stderr, "No main program for interface '%s'" % interface_uri
135 sys.exit(1)
137 prog_name = os.path.basename(main)
138 alias_name = os.path.basename(args[0])
140 assert impl_path
142 # TODO: the feed should say where the man-pages are, but for now we'll accept
143 # a directory called man in some common locations...
144 for mandir in ['man', 'share/man', 'usr/man', 'usr/share/man']:
145 manpath = os.path.join(impl_path, mandir)
146 if os.path.isdir(manpath):
147 # Note: unlike "man -M", this also copes with LANG settings...
148 os.environ['MANPATH'] = manpath
149 os.execlp('man', 'man', prog_name)
150 sys.exit(1)
152 # No man directory given or found, so try searching for man files
154 manpages = []
155 for root, dirs, files in os.walk(impl_path):
156 for f in files:
157 if f.endswith('.gz'):
158 manpage_file = f[:-3]
159 else:
160 manpage_file = f
161 if manpage_file.endswith('.1') or \
162 manpage_file.endswith('.6') or \
163 manpage_file.endswith('.8'):
164 manpage_prog = manpage_file[:-2]
165 if manpage_prog == prog_name or manpage_prog == alias_name:
166 os.execlp('man', 'man', os.path.join(root, f))
167 sys.exit(1)
168 else:
169 manpages.append((root, f))
171 print "No matching manpage was found for '%s' (%s)" % (alias_name, interface_uri)
172 if manpages:
173 print "These non-matching man-pages were found, however:"
174 for root, file in manpages:
175 print os.path.join(root, file)
176 sys.exit(1)
178 if not os.path.isdir(first_path):
179 print "(creating directory %s)" % first_path
180 os.makedirs(first_path)
182 if len(args) == 1:
183 os.execlp('0launch', '0launch', '-gd', '--', interface_uri)
184 sys.exit(1)
186 try:
187 interface = model.Interface(interface_uri)
188 if not reader.update_from_cache(interface):
189 print >>sys.stderr, "Interface '%s' not currently in cache. Fetching..." % interface_uri
190 if os.spawnlp(os.P_WAIT, '0launch', '0launch', '-d', interface_uri):
191 raise model.SafeException("0launch failed")
192 if not reader.update_from_cache(interface):
193 raise model.SafeException("Interface still not in cache. Aborting.")
195 script = os.path.join(first_path, alias_prog)
196 if os.path.exists(script):
197 raise model.SafeException("File '%s' already exists. Delete it first." % script)
198 sys.exit(1)
199 except model.SafeException, ex:
200 print >>sys.stderr, ex
201 sys.exit(1)
203 wrapper = file(script, 'w')
204 alias.write_script(wrapper, interface_uri, main)
206 # Make new script executable
207 os.chmod(script, 0111 | os.fstat(wrapper.fileno()).st_mode)
208 wrapper.close()
210 #print "Created script '%s'." % script
211 #print "To edit policy: 0alias %s" % alias_prog
212 if options.user_path:
213 pass # Assume user knows what they're doing
214 elif not in_path:
215 print >>sys.stderr, 'Warning: %s is not in $PATH. Add it with:\n%s' % (first_path, export('PATH', first_path + ':$PATH'))
216 else:
217 shell = os.environ.get('SHELL', '?')
218 if not shell.endswith('/zsh'):
219 print "(note: some shells require you to type 'rehash' now)"