blob: 80e4814389c00e9c8506b8d1facb5ec306a39ebc [file] [log] [blame]
Greg Ward2689e3d1999-03-22 14:52:19 +00001"""distutils.util
2
Greg Wardaebf7062000-04-04 02:05:59 +00003Miscellaneous utility functions -- anything that doesn't fit into
Greg Ward47527692000-09-30 18:49:14 +00004one of the other *util.py modules.
5"""
Greg Ward2689e3d1999-03-22 14:52:19 +00006
7# created 1999/03/08, Greg Ward
8
Greg Ward3ce77fd2000-03-02 01:49:45 +00009__revision__ = "$Id$"
Greg Ward2689e3d1999-03-22 14:52:19 +000010
Greg Ward1297b5c2000-09-30 20:37:56 +000011import sys, os, string, re
12from distutils.errors import DistutilsPlatformError
13from distutils.dep_util import newer
Greg Ward7c1a6d42000-03-29 02:48:40 +000014from distutils.spawn import spawn
Greg Ward2689e3d1999-03-22 14:52:19 +000015
Greg Wardaa458bc2000-04-22 15:14:58 +000016
Greg Ward585df892000-03-01 14:40:15 +000017def get_platform ():
Greg Ward59399bb2000-09-15 01:16:14 +000018 """Return a string that identifies the current platform. This is used
19 mainly to distinguish platform-specific build directories and
20 platform-specific built distributions. Typically includes the OS name
21 and version and the architecture (as supplied by 'os.uname()'),
22 although the exact information included depends on the OS; eg. for IRIX
23 the architecture isn't particularly important (IRIX only runs on SGI
24 hardware), but for Linux the kernel version isn't particularly
25 important.
26
27 Examples of returned values:
28 linux-i586
29 linux-alpha (?)
30 solaris-2.6-sun4u
31 irix-5.3
32 irix64-6.2
33
34 For non-POSIX platforms, currently just returns 'sys.platform'.
Greg Wardb75c4852000-06-18 15:45:55 +000035 """
Greg Wardec84c212000-09-30 17:09:39 +000036 if os.name != "posix" or not hasattr(os, 'uname'):
Greg Ward59399bb2000-09-15 01:16:14 +000037 # XXX what about the architecture? NT is Intel or Alpha,
38 # Mac OS is M68k or PPC, etc.
39 return sys.platform
40
41 # Try to distinguish various flavours of Unix
42
43 (osname, host, release, version, machine) = os.uname()
44 osname = string.lower(osname)
45 if osname[:5] == "linux":
46 # At least on Linux/Intel, 'machine' is the processor --
47 # i386, etc.
48 # XXX what about Alpha, SPARC, etc?
49 return "%s-%s" % (osname, machine)
50 elif osname[:5] == "sunos":
51 if release[0] >= "5": # SunOS 5 == Solaris 2
52 osname = "solaris"
53 release = "%d.%s" % (int(release[0]) - 3, release[2:])
54 # fall through to standard osname-release-machine representation
55 elif osname[:4] == "irix": # could be "irix64"!
56 return "%s-%s" % (osname, release)
Andrew M. Kuchling989835c2001-01-19 16:26:12 +000057 elif osname[:6] == "cygwin":
58 rel_re = re.compile (r'[\d.]+')
59 m = rel_re.match(release)
60 if m:
61 release = m.group()
Greg Ward59399bb2000-09-15 01:16:14 +000062
63 return "%s-%s-%s" % (osname, release, machine)
64
65# get_platform ()
Greg Ward50919292000-03-07 03:27:08 +000066
67
Greg Wardd8dfb4c2000-05-31 02:32:10 +000068def convert_path (pathname):
Greg Wardb8b263b2000-09-30 18:40:42 +000069 """Return 'pathname' as a name that will work on the native filesystem,
70 i.e. split it on '/' and put it back together again using the current
71 directory separator. Needed because filenames in the setup script are
72 always supplied in Unix style, and have to be converted to the local
73 convention before we can actually use them in the filesystem. Raises
Greg Ward47527692000-09-30 18:49:14 +000074 ValueError on non-Unix-ish systems if 'pathname' either starts or
75 ends with a slash.
Greg Wardb8b263b2000-09-30 18:40:42 +000076 """
Greg Ward7ec05352000-09-22 01:05:43 +000077 if os.sep == '/':
78 return pathname
Greg Ward50919292000-03-07 03:27:08 +000079 if pathname[0] == '/':
Greg Ward02a1a2b2000-04-15 22:15:07 +000080 raise ValueError, "path '%s' cannot be absolute" % pathname
Greg Ward50919292000-03-07 03:27:08 +000081 if pathname[-1] == '/':
Greg Ward02a1a2b2000-04-15 22:15:07 +000082 raise ValueError, "path '%s' cannot end with '/'" % pathname
Greg Ward7ec05352000-09-22 01:05:43 +000083
84 paths = string.split(pathname, '/')
85 return apply(os.path.join, paths)
Greg Ward50919292000-03-07 03:27:08 +000086
Greg Wardd8dfb4c2000-05-31 02:32:10 +000087# convert_path ()
Greg Ward1b4ede52000-03-22 00:22:44 +000088
89
Greg Ward67f75d42000-04-27 01:53:46 +000090def change_root (new_root, pathname):
Greg Ward67f75d42000-04-27 01:53:46 +000091 """Return 'pathname' with 'new_root' prepended. If 'pathname' is
92 relative, this is equivalent to "os.path.join(new_root,pathname)".
93 Otherwise, it requires making 'pathname' relative and then joining the
Greg Ward4b46ef92000-05-31 02:14:32 +000094 two, which is tricky on DOS/Windows and Mac OS.
95 """
96 if os.name == 'posix':
Greg Wardbe86bde2000-09-26 01:56:15 +000097 if not os.path.isabs(pathname):
98 return os.path.join(new_root, pathname)
Greg Ward4b46ef92000-05-31 02:14:32 +000099 else:
Greg Wardbe86bde2000-09-26 01:56:15 +0000100 return os.path.join(new_root, pathname[1:])
Greg Ward67f75d42000-04-27 01:53:46 +0000101
102 elif os.name == 'nt':
Greg Wardbe86bde2000-09-26 01:56:15 +0000103 (drive, path) = os.path.splitdrive(pathname)
Greg Ward4b46ef92000-05-31 02:14:32 +0000104 if path[0] == '\\':
105 path = path[1:]
Greg Wardbe86bde2000-09-26 01:56:15 +0000106 return os.path.join(new_root, path)
Greg Ward67f75d42000-04-27 01:53:46 +0000107
108 elif os.name == 'mac':
Greg Wardf5855742000-09-21 01:23:35 +0000109 if not os.path.isabs(pathname):
110 return os.path.join(new_root, pathname)
111 else:
112 # Chop off volume name from start of path
113 elements = string.split(pathname, ":", 1)
114 pathname = ":" + elements[1]
115 return os.path.join(new_root, pathname)
Greg Ward67f75d42000-04-27 01:53:46 +0000116
117 else:
118 raise DistutilsPlatformError, \
119 "nothing known about platform '%s'" % os.name
120
121
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +0000122_environ_checked = 0
123def check_environ ():
Greg Ward1b4ede52000-03-22 00:22:44 +0000124 """Ensure that 'os.environ' has all the environment variables we
Greg Wardb8b263b2000-09-30 18:40:42 +0000125 guarantee that users can use in config files, command-line options,
126 etc. Currently this includes:
127 HOME - user's home directory (Unix only)
128 PLAT - description of the current platform, including hardware
129 and OS (see 'get_platform()')
Greg Ward1b4ede52000-03-22 00:22:44 +0000130 """
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +0000131 global _environ_checked
132 if _environ_checked:
133 return
134
Greg Ward1b4ede52000-03-22 00:22:44 +0000135 if os.name == 'posix' and not os.environ.has_key('HOME'):
136 import pwd
Greg Wardbe86bde2000-09-26 01:56:15 +0000137 os.environ['HOME'] = pwd.getpwuid(os.getuid())[5]
Greg Ward1b4ede52000-03-22 00:22:44 +0000138
139 if not os.environ.has_key('PLAT'):
Greg Wardbe86bde2000-09-26 01:56:15 +0000140 os.environ['PLAT'] = get_platform()
Greg Ward1b4ede52000-03-22 00:22:44 +0000141
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +0000142 _environ_checked = 1
143
Greg Ward1b4ede52000-03-22 00:22:44 +0000144
145def subst_vars (str, local_vars):
Greg Wardb8b263b2000-09-30 18:40:42 +0000146 """Perform shell/Perl-style variable substitution on 'string'. Every
Greg Ward47527692000-09-30 18:49:14 +0000147 occurrence of '$' followed by a name is considered a variable, and
148 variable is substituted by the value found in the 'local_vars'
149 dictionary, or in 'os.environ' if it's not in 'local_vars'.
150 'os.environ' is first checked/augmented to guarantee that it contains
151 certain values: see 'check_environ()'. Raise ValueError for any
152 variables not found in either 'local_vars' or 'os.environ'.
Greg Wardb8b263b2000-09-30 18:40:42 +0000153 """
Greg Wardbe86bde2000-09-26 01:56:15 +0000154 check_environ()
Greg Ward1b4ede52000-03-22 00:22:44 +0000155 def _subst (match, local_vars=local_vars):
156 var_name = match.group(1)
Greg Wardbe86bde2000-09-26 01:56:15 +0000157 if local_vars.has_key(var_name):
158 return str(local_vars[var_name])
Greg Ward1b4ede52000-03-22 00:22:44 +0000159 else:
160 return os.environ[var_name]
161
Greg Ward47527692000-09-30 18:49:14 +0000162 try:
163 return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, str)
164 except KeyError, var:
165 raise ValueError, "invalid variable '$%s'" % var
Greg Ward1b4ede52000-03-22 00:22:44 +0000166
167# subst_vars ()
Greg Ward7c1a6d42000-03-29 02:48:40 +0000168
169
Greg Warde9055132000-06-17 02:16:46 +0000170def grok_environment_error (exc, prefix="error: "):
171 """Generate a useful error message from an EnvironmentError (IOError or
172 OSError) exception object. Handles Python 1.5.1 and 1.5.2 styles, and
173 does what it can to deal with exception objects that don't have a
174 filename (which happens when the error is due to a two-file operation,
175 such as 'rename()' or 'link()'. Returns the error message as a string
176 prefixed with 'prefix'.
177 """
178 # check for Python 1.5.2-style {IO,OS}Error exception objects
Greg Wardbe86bde2000-09-26 01:56:15 +0000179 if hasattr(exc, 'filename') and hasattr(exc, 'strerror'):
Greg Warde9055132000-06-17 02:16:46 +0000180 if exc.filename:
181 error = prefix + "%s: %s" % (exc.filename, exc.strerror)
182 else:
183 # two-argument functions in posix module don't
184 # include the filename in the exception object!
185 error = prefix + "%s" % exc.strerror
186 else:
187 error = prefix + str(exc[-1])
188
189 return error
Greg Ward6a2a3db2000-06-24 20:40:02 +0000190
191
192# Needed by 'split_quoted()'
Greg Ward2b042de2000-08-08 14:38:13 +0000193_wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace)
Greg Ward6a2a3db2000-06-24 20:40:02 +0000194_squote_re = re.compile(r"'(?:[^'\\]|\\.)*'")
195_dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"')
196
197def split_quoted (s):
198 """Split a string up according to Unix shell-like rules for quotes and
199 backslashes. In short: words are delimited by spaces, as long as those
200 spaces are not escaped by a backslash, or inside a quoted string.
201 Single and double quotes are equivalent, and the quote characters can
202 be backslash-escaped. The backslash is stripped from any two-character
203 escape sequence, leaving only the escaped character. The quote
204 characters are stripped from any quoted string. Returns a list of
205 words.
206 """
207
208 # This is a nice algorithm for splitting up a single string, since it
209 # doesn't require character-by-character examination. It was a little
210 # bit of a brain-bender to get it working right, though...
211
212 s = string.strip(s)
213 words = []
214 pos = 0
215
216 while s:
217 m = _wordchars_re.match(s, pos)
218 end = m.end()
219 if end == len(s):
220 words.append(s[:end])
221 break
222
Greg Ward2b042de2000-08-08 14:38:13 +0000223 if s[end] in string.whitespace: # unescaped, unquoted whitespace: now
Greg Ward6a2a3db2000-06-24 20:40:02 +0000224 words.append(s[:end]) # we definitely have a word delimiter
225 s = string.lstrip(s[end:])
226 pos = 0
227
228 elif s[end] == '\\': # preserve whatever is being escaped;
229 # will become part of the current word
230 s = s[:end] + s[end+1:]
231 pos = end+1
232
233 else:
234 if s[end] == "'": # slurp singly-quoted string
235 m = _squote_re.match(s, end)
236 elif s[end] == '"': # slurp doubly-quoted string
237 m = _dquote_re.match(s, end)
238 else:
239 raise RuntimeError, \
240 "this can't happen (bad char '%c')" % s[end]
241
242 if m is None:
243 raise ValueError, \
244 "bad string (mismatched %s quotes?)" % s[end]
245
246 (beg, end) = m.span()
247 s = s[:beg] + s[beg+1:end-1] + s[end:]
248 pos = m.end() - 2
249
250 if pos >= len(s):
251 words.append(s)
252 break
253
254 return words
255
256# split_quoted ()
Greg Ward1c16ac32000-08-02 01:37:30 +0000257
258
259def execute (func, args, msg=None, verbose=0, dry_run=0):
260 """Perform some action that affects the outside world (eg. by writing
261 to the filesystem). Such actions are special because they are disabled
262 by the 'dry_run' flag, and announce themselves if 'verbose' is true.
263 This method takes care of all that bureaucracy for you; all you have to
264 do is supply the function to call and an argument tuple for it (to
265 embody the "external action" being performed), and an optional message
266 to print.
267 """
268 # Generate a message if we weren't passed one
269 if msg is None:
270 msg = "%s%s" % (func.__name__, `args`)
271 if msg[-2:] == ',)': # correct for singleton tuple
272 msg = msg[0:-2] + ')'
273
274 # Print it if verbosity level is high enough
275 if verbose:
276 print msg
277
278 # And do it, as long as we're not in dry-run mode
279 if not dry_run:
280 apply(func, args)
281
282# execute()
Greg Ward817dc092000-09-25 01:25:06 +0000283
284
285def strtobool (val):
286 """Convert a string representation of truth to true (1) or false (0).
287 True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
288 are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
289 'val' is anything else.
290 """
291 val = string.lower(val)
292 if val in ('y', 'yes', 't', 'true', 'on', '1'):
293 return 1
294 elif val in ('n', 'no', 'f', 'false', 'off', '0'):
295 return 0
296 else:
297 raise ValueError, "invalid truth value %s" % `val`
Greg Ward1297b5c2000-09-30 20:37:56 +0000298
299
300def byte_compile (py_files,
301 optimize=0, force=0,
302 prefix=None, base_dir=None,
303 verbose=1, dry_run=0,
304 direct=None):
Greg Wardf217e212000-10-01 23:49:30 +0000305 """Byte-compile a collection of Python source files to either .pyc
306 or .pyo files in the same directory. 'py_files' is a list of files
307 to compile; any files that don't end in ".py" are silently skipped.
308 'optimize' must be one of the following:
Greg Ward1297b5c2000-09-30 20:37:56 +0000309 0 - don't optimize (generate .pyc)
310 1 - normal optimization (like "python -O")
311 2 - extra optimization (like "python -OO")
312 If 'force' is true, all files are recompiled regardless of
313 timestamps.
314
315 The source filename encoded in each bytecode file defaults to the
316 filenames listed in 'py_files'; you can modify these with 'prefix' and
317 'basedir'. 'prefix' is a string that will be stripped off of each
318 source filename, and 'base_dir' is a directory name that will be
319 prepended (after 'prefix' is stripped). You can supply either or both
320 (or neither) of 'prefix' and 'base_dir', as you wish.
321
322 If 'verbose' is true, prints out a report of each file. If 'dry_run'
323 is true, doesn't actually do anything that would affect the filesystem.
324
325 Byte-compilation is either done directly in this interpreter process
326 with the standard py_compile module, or indirectly by writing a
327 temporary script and executing it. Normally, you should let
328 'byte_compile()' figure out to use direct compilation or not (see
329 the source for details). The 'direct' flag is used by the script
330 generated in indirect mode; unless you know what you're doing, leave
331 it set to None.
332 """
333
334 # First, if the caller didn't force us into direct or indirect mode,
335 # figure out which mode we should be in. We take a conservative
336 # approach: choose direct mode *only* if the current interpreter is
337 # in debug mode and optimize is 0. If we're not in debug mode (-O
338 # or -OO), we don't know which level of optimization this
339 # interpreter is running with, so we can't do direct
340 # byte-compilation and be certain that it's the right thing. Thus,
341 # always compile indirectly if the current interpreter is in either
342 # optimize mode, or if either optimization level was requested by
343 # the caller.
344 if direct is None:
345 direct = (__debug__ and optimize == 0)
346
347 # "Indirect" byte-compilation: write a temporary script and then
348 # run it with the appropriate flags.
349 if not direct:
350 from tempfile import mktemp
351 script_name = mktemp(".py")
352 if verbose:
353 print "writing byte-compilation script '%s'" % script_name
354 if not dry_run:
355 script = open(script_name, "w")
356
357 script.write("""\
358from distutils.util import byte_compile
359files = [
360""")
Greg Ward9216cfe2000-10-03 03:31:05 +0000361
362 # XXX would be nice to write absolute filenames, just for
363 # safety's sake (script should be more robust in the face of
364 # chdir'ing before running it). But this requires abspath'ing
365 # 'prefix' as well, and that breaks the hack in build_lib's
366 # 'byte_compile()' method that carefully tacks on a trailing
367 # slash (os.sep really) to make sure the prefix here is "just
368 # right". This whole prefix business is rather delicate -- the
369 # problem is that it's really a directory, but I'm treating it
370 # as a dumb string, so trailing slashes and so forth matter.
371
372 #py_files = map(os.path.abspath, py_files)
373 #if prefix:
374 # prefix = os.path.abspath(prefix)
375
Greg Ward1297b5c2000-09-30 20:37:56 +0000376 script.write(string.join(map(repr, py_files), ",\n") + "]\n")
377 script.write("""
378byte_compile(files, optimize=%s, force=%s,
379 prefix=%s, base_dir=%s,
380 verbose=%s, dry_run=0,
381 direct=1)
382""" % (`optimize`, `force`, `prefix`, `base_dir`, `verbose`))
383
384 script.close()
385
386 cmd = [sys.executable, script_name]
387 if optimize == 1:
388 cmd.insert(1, "-O")
389 elif optimize == 2:
390 cmd.insert(1, "-OO")
391 spawn(cmd, verbose=verbose, dry_run=dry_run)
Greg Ward9216cfe2000-10-03 03:31:05 +0000392 execute(os.remove, (script_name,), "removing %s" % script_name,
393 verbose=verbose, dry_run=dry_run)
Greg Ward1297b5c2000-09-30 20:37:56 +0000394
395 # "Direct" byte-compilation: use the py_compile module to compile
396 # right here, right now. Note that the script generated in indirect
397 # mode simply calls 'byte_compile()' in direct mode, a weird sort of
398 # cross-process recursion. Hey, it works!
399 else:
400 from py_compile import compile
401
402 for file in py_files:
403 if file[-3:] != ".py":
Greg Wardf217e212000-10-01 23:49:30 +0000404 # This lets us be lazy and not filter filenames in
405 # the "install_lib" command.
406 continue
Greg Ward1297b5c2000-09-30 20:37:56 +0000407
408 # Terminology from the py_compile module:
409 # cfile - byte-compiled file
410 # dfile - purported source filename (same as 'file' by default)
411 cfile = file + (__debug__ and "c" or "o")
412 dfile = file
413 if prefix:
414 if file[:len(prefix)] != prefix:
415 raise ValueError, \
416 ("invalid prefix: filename %s doesn't start with %s"
417 % (`file`, `prefix`))
418 dfile = dfile[len(prefix):]
419 if base_dir:
420 dfile = os.path.join(base_dir, dfile)
421
422 cfile_base = os.path.basename(cfile)
423 if direct:
424 if force or newer(file, cfile):
425 if verbose:
426 print "byte-compiling %s to %s" % (file, cfile_base)
427 if not dry_run:
428 compile(file, cfile, dfile)
429 else:
430 if verbose:
431 print "skipping byte-compilation of %s to %s" % \
432 (file, cfile_base)
433
434# byte_compile ()