blob: 1a1ec6d8582cb71f3d3cb3429678d502b1b23d4d [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 Warda7540bd2000-03-23 04:39:16 +000011import sys, os, string, re, shutil
Greg Ward2689e3d1999-03-22 14:52:19 +000012from distutils.errors import *
Greg Ward7c1a6d42000-03-29 02:48:40 +000013from distutils.spawn import spawn
Greg Ward2689e3d1999-03-22 14:52:19 +000014
Greg Wardaa458bc2000-04-22 15:14:58 +000015
Greg Ward585df892000-03-01 14:40:15 +000016def get_platform ():
Greg Ward59399bb2000-09-15 01:16:14 +000017 """Return a string that identifies the current platform. This is used
18 mainly to distinguish platform-specific build directories and
19 platform-specific built distributions. Typically includes the OS name
20 and version and the architecture (as supplied by 'os.uname()'),
21 although the exact information included depends on the OS; eg. for IRIX
22 the architecture isn't particularly important (IRIX only runs on SGI
23 hardware), but for Linux the kernel version isn't particularly
24 important.
25
26 Examples of returned values:
27 linux-i586
28 linux-alpha (?)
29 solaris-2.6-sun4u
30 irix-5.3
31 irix64-6.2
32
33 For non-POSIX platforms, currently just returns 'sys.platform'.
Greg Wardb75c4852000-06-18 15:45:55 +000034 """
Greg Wardec84c212000-09-30 17:09:39 +000035 if os.name != "posix" or not hasattr(os, 'uname'):
Greg Ward59399bb2000-09-15 01:16:14 +000036 # XXX what about the architecture? NT is Intel or Alpha,
37 # Mac OS is M68k or PPC, etc.
38 return sys.platform
39
40 # Try to distinguish various flavours of Unix
41
42 (osname, host, release, version, machine) = os.uname()
43 osname = string.lower(osname)
44 if osname[:5] == "linux":
45 # At least on Linux/Intel, 'machine' is the processor --
46 # i386, etc.
47 # XXX what about Alpha, SPARC, etc?
48 return "%s-%s" % (osname, machine)
49 elif osname[:5] == "sunos":
50 if release[0] >= "5": # SunOS 5 == Solaris 2
51 osname = "solaris"
52 release = "%d.%s" % (int(release[0]) - 3, release[2:])
53 # fall through to standard osname-release-machine representation
54 elif osname[:4] == "irix": # could be "irix64"!
55 return "%s-%s" % (osname, release)
56
57 return "%s-%s-%s" % (osname, release, machine)
58
59# get_platform ()
Greg Ward50919292000-03-07 03:27:08 +000060
61
Greg Wardd8dfb4c2000-05-31 02:32:10 +000062def convert_path (pathname):
Greg Wardb8b263b2000-09-30 18:40:42 +000063 """Return 'pathname' as a name that will work on the native filesystem,
64 i.e. split it on '/' and put it back together again using the current
65 directory separator. Needed because filenames in the setup script are
66 always supplied in Unix style, and have to be converted to the local
67 convention before we can actually use them in the filesystem. Raises
Greg Ward47527692000-09-30 18:49:14 +000068 ValueError on non-Unix-ish systems if 'pathname' either starts or
69 ends with a slash.
Greg Wardb8b263b2000-09-30 18:40:42 +000070 """
Greg Ward7ec05352000-09-22 01:05:43 +000071 if os.sep == '/':
72 return pathname
Greg Ward50919292000-03-07 03:27:08 +000073 if pathname[0] == '/':
Greg Ward02a1a2b2000-04-15 22:15:07 +000074 raise ValueError, "path '%s' cannot be absolute" % pathname
Greg Ward50919292000-03-07 03:27:08 +000075 if pathname[-1] == '/':
Greg Ward02a1a2b2000-04-15 22:15:07 +000076 raise ValueError, "path '%s' cannot end with '/'" % pathname
Greg Ward7ec05352000-09-22 01:05:43 +000077
78 paths = string.split(pathname, '/')
79 return apply(os.path.join, paths)
Greg Ward50919292000-03-07 03:27:08 +000080
Greg Wardd8dfb4c2000-05-31 02:32:10 +000081# convert_path ()
Greg Ward1b4ede52000-03-22 00:22:44 +000082
83
Greg Ward67f75d42000-04-27 01:53:46 +000084def change_root (new_root, pathname):
Greg Ward67f75d42000-04-27 01:53:46 +000085 """Return 'pathname' with 'new_root' prepended. If 'pathname' is
86 relative, this is equivalent to "os.path.join(new_root,pathname)".
87 Otherwise, it requires making 'pathname' relative and then joining the
Greg Ward4b46ef92000-05-31 02:14:32 +000088 two, which is tricky on DOS/Windows and Mac OS.
89 """
90 if os.name == 'posix':
Greg Wardbe86bde2000-09-26 01:56:15 +000091 if not os.path.isabs(pathname):
92 return os.path.join(new_root, pathname)
Greg Ward4b46ef92000-05-31 02:14:32 +000093 else:
Greg Wardbe86bde2000-09-26 01:56:15 +000094 return os.path.join(new_root, pathname[1:])
Greg Ward67f75d42000-04-27 01:53:46 +000095
96 elif os.name == 'nt':
Greg Wardbe86bde2000-09-26 01:56:15 +000097 (drive, path) = os.path.splitdrive(pathname)
Greg Ward4b46ef92000-05-31 02:14:32 +000098 if path[0] == '\\':
99 path = path[1:]
Greg Wardbe86bde2000-09-26 01:56:15 +0000100 return os.path.join(new_root, path)
Greg Ward67f75d42000-04-27 01:53:46 +0000101
102 elif os.name == 'mac':
Greg Wardf5855742000-09-21 01:23:35 +0000103 if not os.path.isabs(pathname):
104 return os.path.join(new_root, pathname)
105 else:
106 # Chop off volume name from start of path
107 elements = string.split(pathname, ":", 1)
108 pathname = ":" + elements[1]
109 return os.path.join(new_root, pathname)
Greg Ward67f75d42000-04-27 01:53:46 +0000110
111 else:
112 raise DistutilsPlatformError, \
113 "nothing known about platform '%s'" % os.name
114
115
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +0000116_environ_checked = 0
117def check_environ ():
Greg Ward1b4ede52000-03-22 00:22:44 +0000118 """Ensure that 'os.environ' has all the environment variables we
Greg Wardb8b263b2000-09-30 18:40:42 +0000119 guarantee that users can use in config files, command-line options,
120 etc. Currently this includes:
121 HOME - user's home directory (Unix only)
122 PLAT - description of the current platform, including hardware
123 and OS (see 'get_platform()')
Greg Ward1b4ede52000-03-22 00:22:44 +0000124 """
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +0000125 global _environ_checked
126 if _environ_checked:
127 return
128
Greg Ward1b4ede52000-03-22 00:22:44 +0000129 if os.name == 'posix' and not os.environ.has_key('HOME'):
130 import pwd
Greg Wardbe86bde2000-09-26 01:56:15 +0000131 os.environ['HOME'] = pwd.getpwuid(os.getuid())[5]
Greg Ward1b4ede52000-03-22 00:22:44 +0000132
133 if not os.environ.has_key('PLAT'):
Greg Wardbe86bde2000-09-26 01:56:15 +0000134 os.environ['PLAT'] = get_platform()
Greg Ward1b4ede52000-03-22 00:22:44 +0000135
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +0000136 _environ_checked = 1
137
Greg Ward1b4ede52000-03-22 00:22:44 +0000138
139def subst_vars (str, local_vars):
Greg Wardb8b263b2000-09-30 18:40:42 +0000140 """Perform shell/Perl-style variable substitution on 'string'. Every
Greg Ward47527692000-09-30 18:49:14 +0000141 occurrence of '$' followed by a name is considered a variable, and
142 variable is substituted by the value found in the 'local_vars'
143 dictionary, or in 'os.environ' if it's not in 'local_vars'.
144 'os.environ' is first checked/augmented to guarantee that it contains
145 certain values: see 'check_environ()'. Raise ValueError for any
146 variables not found in either 'local_vars' or 'os.environ'.
Greg Wardb8b263b2000-09-30 18:40:42 +0000147 """
Greg Wardbe86bde2000-09-26 01:56:15 +0000148 check_environ()
Greg Ward1b4ede52000-03-22 00:22:44 +0000149 def _subst (match, local_vars=local_vars):
150 var_name = match.group(1)
Greg Wardbe86bde2000-09-26 01:56:15 +0000151 if local_vars.has_key(var_name):
152 return str(local_vars[var_name])
Greg Ward1b4ede52000-03-22 00:22:44 +0000153 else:
154 return os.environ[var_name]
155
Greg Ward47527692000-09-30 18:49:14 +0000156 try:
157 return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, str)
158 except KeyError, var:
159 raise ValueError, "invalid variable '$%s'" % var
Greg Ward1b4ede52000-03-22 00:22:44 +0000160
161# subst_vars ()
Greg Ward7c1a6d42000-03-29 02:48:40 +0000162
163
Greg Warde9055132000-06-17 02:16:46 +0000164def grok_environment_error (exc, prefix="error: "):
165 """Generate a useful error message from an EnvironmentError (IOError or
166 OSError) exception object. Handles Python 1.5.1 and 1.5.2 styles, and
167 does what it can to deal with exception objects that don't have a
168 filename (which happens when the error is due to a two-file operation,
169 such as 'rename()' or 'link()'. Returns the error message as a string
170 prefixed with 'prefix'.
171 """
172 # check for Python 1.5.2-style {IO,OS}Error exception objects
Greg Wardbe86bde2000-09-26 01:56:15 +0000173 if hasattr(exc, 'filename') and hasattr(exc, 'strerror'):
Greg Warde9055132000-06-17 02:16:46 +0000174 if exc.filename:
175 error = prefix + "%s: %s" % (exc.filename, exc.strerror)
176 else:
177 # two-argument functions in posix module don't
178 # include the filename in the exception object!
179 error = prefix + "%s" % exc.strerror
180 else:
181 error = prefix + str(exc[-1])
182
183 return error
Greg Ward6a2a3db2000-06-24 20:40:02 +0000184
185
186# Needed by 'split_quoted()'
Greg Ward2b042de2000-08-08 14:38:13 +0000187_wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace)
Greg Ward6a2a3db2000-06-24 20:40:02 +0000188_squote_re = re.compile(r"'(?:[^'\\]|\\.)*'")
189_dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"')
190
191def split_quoted (s):
192 """Split a string up according to Unix shell-like rules for quotes and
193 backslashes. In short: words are delimited by spaces, as long as those
194 spaces are not escaped by a backslash, or inside a quoted string.
195 Single and double quotes are equivalent, and the quote characters can
196 be backslash-escaped. The backslash is stripped from any two-character
197 escape sequence, leaving only the escaped character. The quote
198 characters are stripped from any quoted string. Returns a list of
199 words.
200 """
201
202 # This is a nice algorithm for splitting up a single string, since it
203 # doesn't require character-by-character examination. It was a little
204 # bit of a brain-bender to get it working right, though...
205
206 s = string.strip(s)
207 words = []
208 pos = 0
209
210 while s:
211 m = _wordchars_re.match(s, pos)
212 end = m.end()
213 if end == len(s):
214 words.append(s[:end])
215 break
216
Greg Ward2b042de2000-08-08 14:38:13 +0000217 if s[end] in string.whitespace: # unescaped, unquoted whitespace: now
Greg Ward6a2a3db2000-06-24 20:40:02 +0000218 words.append(s[:end]) # we definitely have a word delimiter
219 s = string.lstrip(s[end:])
220 pos = 0
221
222 elif s[end] == '\\': # preserve whatever is being escaped;
223 # will become part of the current word
224 s = s[:end] + s[end+1:]
225 pos = end+1
226
227 else:
228 if s[end] == "'": # slurp singly-quoted string
229 m = _squote_re.match(s, end)
230 elif s[end] == '"': # slurp doubly-quoted string
231 m = _dquote_re.match(s, end)
232 else:
233 raise RuntimeError, \
234 "this can't happen (bad char '%c')" % s[end]
235
236 if m is None:
237 raise ValueError, \
238 "bad string (mismatched %s quotes?)" % s[end]
239
240 (beg, end) = m.span()
241 s = s[:beg] + s[beg+1:end-1] + s[end:]
242 pos = m.end() - 2
243
244 if pos >= len(s):
245 words.append(s)
246 break
247
248 return words
249
250# split_quoted ()
Greg Ward1c16ac32000-08-02 01:37:30 +0000251
252
253def execute (func, args, msg=None, verbose=0, dry_run=0):
254 """Perform some action that affects the outside world (eg. by writing
255 to the filesystem). Such actions are special because they are disabled
256 by the 'dry_run' flag, and announce themselves if 'verbose' is true.
257 This method takes care of all that bureaucracy for you; all you have to
258 do is supply the function to call and an argument tuple for it (to
259 embody the "external action" being performed), and an optional message
260 to print.
261 """
262 # Generate a message if we weren't passed one
263 if msg is None:
264 msg = "%s%s" % (func.__name__, `args`)
265 if msg[-2:] == ',)': # correct for singleton tuple
266 msg = msg[0:-2] + ')'
267
268 # Print it if verbosity level is high enough
269 if verbose:
270 print msg
271
272 # And do it, as long as we're not in dry-run mode
273 if not dry_run:
274 apply(func, args)
275
276# execute()
Greg Ward817dc092000-09-25 01:25:06 +0000277
278
279def strtobool (val):
280 """Convert a string representation of truth to true (1) or false (0).
281 True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
282 are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
283 'val' is anything else.
284 """
285 val = string.lower(val)
286 if val in ('y', 'yes', 't', 'true', 'on', '1'):
287 return 1
288 elif val in ('n', 'no', 'f', 'false', 'off', '0'):
289 return 0
290 else:
291 raise ValueError, "invalid truth value %s" % `val`