Include Darwin packages in MacPortsDistribution
[zeroinstall.git] / 0alias
blobfaac6b08cb49e6cb951e2ee71c01c2dd63d34a72
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 import logging
9 from logging import warn
10 try:
11 locale.setlocale(locale.LC_ALL, '')
12 except locale.Error:
13 warn('Error setting locale (eg. Invalid locale)')
15 import os, sys
17 ## PATH ##
19 from optparse import OptionParser
21 from zeroinstall.injector import reader, model
22 from zeroinstall.injector.config import load_config
23 from zeroinstall import support, alias, helpers, apps, _
24 from zeroinstall.support import basedir
26 config = load_config()
28 def export(name, value):
29 """Try to guess the command to set an environment variable."""
30 shell = os.environ.get('SHELL', '?')
31 if 'csh' in shell:
32 return "setenv %s %s" % (name, value)
33 return "export %s=%s" % (name, value)
35 # Do this here so we can include it in the help message.
36 # But, don't abort if there isn't one because we might
37 # be doing something else (e.g. --manpage)
38 first_path = apps.find_bin_dir()
39 in_path = first_path is not None
40 if not in_path:
41 first_path = os.path.expanduser('~/bin/')
43 parser = OptionParser(usage="usage: %%prog [options] alias [interface [main]]\n\n"
44 "Creates a script to run 'interface' (will be created in\n"
45 "%s unless overridden by --dir).\n\n"
46 "If no interface is given, edits the policy for an existing alias.\n"
47 "For interfaces providing more than one executable, the desired\n"
48 "'main' binary or command may also be given." % first_path)
49 parser.add_option("-c", "--command", help="the command the alias will run (default 'run')", metavar='COMMNAD')
50 parser.add_option("-m", "--manpage", help="show the manual page for an existing alias", action='store_true')
51 parser.add_option("-r", "--resolve", help="show the URI for an alias", action='store_true')
52 parser.add_option("-v", "--verbose", help="more verbose output", action='count')
53 parser.add_option("-V", "--version", help="display version information", action='store_true')
54 parser.add_option("-d", "--dir", help="install in DIR", dest="user_path", metavar="DIR")
56 (options, args) = parser.parse_args()
58 if options.verbose:
59 logger = logging.getLogger()
60 if options.verbose == 1:
61 logger.setLevel(logging.INFO)
62 else:
63 logger.setLevel(logging.DEBUG)
64 hdlr = logging.StreamHandler()
65 fmt = logging.Formatter("%(levelname)s:%(message)s")
66 hdlr.setFormatter(fmt)
67 logger.addHandler(hdlr)
69 if options.version:
70 import zeroinstall
71 print("0alias (zero-install) " + zeroinstall.version)
72 print("Copyright (C) 2010 Thomas Leonard")
73 print("This program comes with ABSOLUTELY NO WARRANTY,")
74 print("to the extent permitted by law.")
75 print("You may redistribute copies of this program")
76 print("under the terms of the GNU Lesser General Public License.")
77 print("For more information about these matters, see the file named COPYING.")
78 sys.exit(0)
80 if options.manpage:
81 if len(args) != 1:
82 os.execlp('man', 'man', *args)
83 sys.exit(1)
85 if len(args) < 1 or len(args) > 3:
86 parser.print_help()
87 sys.exit(1)
88 alias_prog, interface_uri, main = (list(args) + [None, None])[:3]
89 command = options.command
91 if options.resolve or options.manpage:
92 if interface_uri is not None:
93 parser.print_help()
94 sys.exit(1)
96 if options.user_path:
97 first_path = options.user_path
99 if interface_uri is None:
100 if options.command:
101 print("Can't use --command when editing an existing alias", file=sys.stderr)
102 sys.exit(1)
103 try:
104 if not os.path.isabs(alias_prog):
105 full_path = support.find_in_path(alias_prog)
106 if not full_path:
107 raise alias.NotAnAliasScript("Not found in $PATH: " + alias_prog)
108 else:
109 full_path = alias_prog
111 alias_info = alias.parse_script(full_path)
112 interface_uri = alias_info.uri
113 main = alias_info.main
114 command = alias_info.command
115 except (alias.NotAnAliasScript, IOError) as ex:
116 # (we get IOError if e.g. the script isn't readable)
117 if options.manpage:
118 logging.debug("not a 0alias script '%s': %s", alias_prog, ex)
119 os.execlp('man', 'man', *args)
120 print(str(ex), file=sys.stderr)
121 sys.exit(1)
123 interface_uri = model.canonical_iface_uri(interface_uri)
125 if options.resolve:
126 print(interface_uri)
127 sys.exit(0)
129 if options.manpage:
130 sels = helpers.ensure_cached(interface_uri, command = command or 'run')
131 if not sels:
132 # Cancelled by user
133 sys.exit(1)
135 if sels.commands:
136 selected_command = sels.commands[0]
137 else:
138 print("No <command> in selections!", file=sys.stderr)
139 selected_impl = sels.selections[interface_uri]
141 impl_path = selected_impl.local_path or config.iface_cache.stores.lookup_any(selected_impl.digests)
143 if main is None:
144 main = selected_command.path
145 if main is None:
146 print("No main program for interface '%s'" % interface_uri, file=sys.stderr)
147 sys.exit(1)
149 prog_name = os.path.basename(main)
150 alias_name = os.path.basename(args[0])
152 assert impl_path
154 # TODO: the feed should say where the man-pages are, but for now we'll accept
155 # a directory called man in some common locations...
156 for mandir in ['man', 'share/man', 'usr/man', 'usr/share/man']:
157 manpath = os.path.join(impl_path, mandir)
158 if os.path.isdir(manpath):
159 # Note: unlike "man -M", this also copes with LANG settings...
160 os.environ['MANPATH'] = manpath
161 os.execlp('man', 'man', prog_name)
162 sys.exit(1)
164 # No man directory given or found, so try searching for man files
166 manpages = []
167 for root, dirs, files in os.walk(impl_path):
168 for f in files:
169 if f.endswith('.gz'):
170 manpage_file = f[:-3]
171 else:
172 manpage_file = f
173 if manpage_file.endswith('.1') or \
174 manpage_file.endswith('.6') or \
175 manpage_file.endswith('.8'):
176 manpage_prog = manpage_file[:-2]
177 if manpage_prog == prog_name or manpage_prog == alias_name:
178 os.execlp('man', 'man', os.path.join(root, f))
179 sys.exit(1)
180 else:
181 manpages.append((root, f))
183 print("No matching manpage was found for '%s' (%s)" % (alias_name, interface_uri))
184 if manpages:
185 print("These non-matching man-pages were found, however:")
186 for root, file in manpages:
187 print(os.path.join(root, file))
188 sys.exit(1)
190 if not os.path.isdir(first_path):
191 print("(creating directory %s)" % first_path)
192 os.makedirs(first_path)
194 if len(args) == 1:
195 os.execlp('0launch', '0launch', '-gd', '--', interface_uri)
196 sys.exit(1)
198 try:
199 if not options.user_path:
200 if alias_prog == '0launch':
201 raise model.SafeException(_('Refusing to create an alias named "0launch" (to avoid an infinite loop)'))
203 interface = model.Interface(interface_uri)
204 if not reader.update_from_cache(interface):
205 print("Interface '%s' not currently in cache. Fetching..." % interface_uri, file=sys.stderr)
206 if os.spawnlp(os.P_WAIT, '0launch', '0launch', '-d', interface_uri):
207 raise model.SafeException("0launch failed")
208 if not reader.update_from_cache(interface):
209 raise model.SafeException("Interface still not in cache. Aborting.")
211 script = os.path.join(first_path, alias_prog)
212 if os.path.exists(script):
213 raise model.SafeException("File '%s' already exists. Delete it first." % script)
214 sys.exit(1)
215 except model.SafeException as ex:
216 print(ex, file=sys.stderr)
217 sys.exit(1)
219 seen = set([interface_uri])
220 while True:
221 feed = config.iface_cache.get_feed(interface_uri)
222 replacement_uri = feed.get_replaced_by()
223 if replacement_uri is None:
224 break
225 print(_("(interface {old} has been replaced by {new}; using that instead)").format(old = interface_uri, new = replacement_uri))
226 assert replacement_uri not in seen, "loop detected!"
227 seen.add(replacement_uri)
228 interface_uri = replacement_uri
230 wrapper = open(script, 'w')
231 alias.write_script(wrapper, interface_uri, main, command = command)
233 # Make new script executable
234 os.chmod(script, 0o111 | os.fstat(wrapper.fileno()).st_mode)
235 wrapper.close()
237 #print "Created script '%s'." % script
238 #print "To edit policy: 0alias %s" % alias_prog
239 if options.user_path:
240 pass # Assume user knows what they're doing
241 elif not in_path:
242 print('Warning: %s is not in $PATH. Add it with:\n%s' % (first_path, export('PATH', first_path + ':$PATH')), file=sys.stderr)
243 else:
244 shell = os.environ.get('SHELL', '?')
245 if not (shell.endswith('/zsh') or shell.endswith('/bash')):
246 print("(note: some shells require you to type 'rehash' now)")