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