blob: 468887127fd4d9c7a198174204daa104eac647dc [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
4one of the other *util.py modules."""
Greg Ward2689e3d1999-03-22 14:52:19 +00005
6# created 1999/03/08, Greg Ward
7
Greg Ward3ce77fd2000-03-02 01:49:45 +00008__revision__ = "$Id$"
Greg Ward2689e3d1999-03-22 14:52:19 +00009
Greg Warda7540bd2000-03-23 04:39:16 +000010import sys, os, string, re, shutil
Greg Ward2689e3d1999-03-22 14:52:19 +000011from distutils.errors import *
Greg Ward7c1a6d42000-03-29 02:48:40 +000012from distutils.spawn import spawn
Greg Ward2689e3d1999-03-22 14:52:19 +000013
Greg Wardaa458bc2000-04-22 15:14:58 +000014
Greg Ward585df892000-03-01 14:40:15 +000015def get_platform ():
Greg Ward59399bb2000-09-15 01:16:14 +000016 """Return a string that identifies the current platform. This is used
17 mainly to distinguish platform-specific build directories and
18 platform-specific built distributions. Typically includes the OS name
19 and version and the architecture (as supplied by 'os.uname()'),
20 although the exact information included depends on the OS; eg. for IRIX
21 the architecture isn't particularly important (IRIX only runs on SGI
22 hardware), but for Linux the kernel version isn't particularly
23 important.
24
25 Examples of returned values:
26 linux-i586
27 linux-alpha (?)
28 solaris-2.6-sun4u
29 irix-5.3
30 irix64-6.2
31
32 For non-POSIX platforms, currently just returns 'sys.platform'.
Greg Wardb75c4852000-06-18 15:45:55 +000033 """
Greg Ward59399bb2000-09-15 01:16:14 +000034 if os.name != "posix":
35 # XXX what about the architecture? NT is Intel or Alpha,
36 # Mac OS is M68k or PPC, etc.
37 return sys.platform
38
39 # Try to distinguish various flavours of Unix
40
41 (osname, host, release, version, machine) = os.uname()
42 osname = string.lower(osname)
43 if osname[:5] == "linux":
44 # At least on Linux/Intel, 'machine' is the processor --
45 # i386, etc.
46 # XXX what about Alpha, SPARC, etc?
47 return "%s-%s" % (osname, machine)
48 elif osname[:5] == "sunos":
49 if release[0] >= "5": # SunOS 5 == Solaris 2
50 osname = "solaris"
51 release = "%d.%s" % (int(release[0]) - 3, release[2:])
52 # fall through to standard osname-release-machine representation
53 elif osname[:4] == "irix": # could be "irix64"!
54 return "%s-%s" % (osname, release)
55
56 return "%s-%s-%s" % (osname, release, machine)
57
58# get_platform ()
Greg Ward50919292000-03-07 03:27:08 +000059
60
Greg Wardd8dfb4c2000-05-31 02:32:10 +000061def convert_path (pathname):
Greg Ward50919292000-03-07 03:27:08 +000062 """Return 'pathname' as a name that will work on the native
63 filesystem, i.e. split it on '/' and put it back together again
64 using the current directory separator. Needed because filenames in
65 the setup script are always supplied in Unix style, and have to be
66 converted to the local convention before we can actually use them in
Greg Ward02a1a2b2000-04-15 22:15:07 +000067 the filesystem. Raises ValueError if 'pathname' is
Greg Ward50919292000-03-07 03:27:08 +000068 absolute (starts with '/') or contains local directory separators
69 (unless the local separator is '/', of course)."""
70
71 if pathname[0] == '/':
Greg Ward02a1a2b2000-04-15 22:15:07 +000072 raise ValueError, "path '%s' cannot be absolute" % pathname
Greg Ward50919292000-03-07 03:27:08 +000073 if pathname[-1] == '/':
Greg Ward02a1a2b2000-04-15 22:15:07 +000074 raise ValueError, "path '%s' cannot end with '/'" % pathname
Greg Ward464023f2000-04-25 01:33:11 +000075 if os.sep != '/':
Greg Wardd8dfb4c2000-05-31 02:32:10 +000076 paths = string.split (pathname, '/')
77 return apply (os.path.join, paths)
Greg Ward50919292000-03-07 03:27:08 +000078 else:
79 return pathname
80
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':
91 if not os.path.isabs (pathname):
92 return os.path.join (new_root, pathname)
93 else:
94 return os.path.join (new_root, pathname[1:])
Greg Ward67f75d42000-04-27 01:53:46 +000095
96 elif os.name == 'nt':
Greg Ward67f75d42000-04-27 01:53:46 +000097 (drive, path) = os.path.splitdrive (pathname)
Greg Ward4b46ef92000-05-31 02:14:32 +000098 if path[0] == '\\':
99 path = path[1:]
100 return os.path.join (new_root, path)
Greg Ward67f75d42000-04-27 01:53:46 +0000101
102 elif os.name == 'mac':
103 raise RuntimeError, "no clue how to do this on Mac OS"
104
105 else:
106 raise DistutilsPlatformError, \
107 "nothing known about platform '%s'" % os.name
108
109
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +0000110_environ_checked = 0
111def check_environ ():
Greg Ward1b4ede52000-03-22 00:22:44 +0000112 """Ensure that 'os.environ' has all the environment variables we
113 guarantee that users can use in config files, command-line
114 options, etc. Currently this includes:
115 HOME - user's home directory (Unix only)
Greg Ward612eb9f2000-07-27 02:13:20 +0000116 PLAT - description of the current platform, including hardware
Greg Ward1b4ede52000-03-22 00:22:44 +0000117 and OS (see 'get_platform()')
118 """
119
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +0000120 global _environ_checked
121 if _environ_checked:
122 return
123
Greg Ward1b4ede52000-03-22 00:22:44 +0000124 if os.name == 'posix' and not os.environ.has_key('HOME'):
125 import pwd
126 os.environ['HOME'] = pwd.getpwuid (os.getuid())[5]
127
128 if not os.environ.has_key('PLAT'):
129 os.environ['PLAT'] = get_platform ()
130
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +0000131 _environ_checked = 1
132
Greg Ward1b4ede52000-03-22 00:22:44 +0000133
134def subst_vars (str, local_vars):
135 """Perform shell/Perl-style variable substitution on 'string'.
Greg Ward612eb9f2000-07-27 02:13:20 +0000136 Every occurrence of '$' followed by a name, or a name enclosed in
Greg Ward1b4ede52000-03-22 00:22:44 +0000137 braces, is considered a variable. Every variable is substituted by
138 the value found in the 'local_vars' dictionary, or in 'os.environ'
139 if it's not in 'local_vars'. 'os.environ' is first checked/
140 augmented to guarantee that it contains certain values: see
141 '_check_environ()'. Raise ValueError for any variables not found in
142 either 'local_vars' or 'os.environ'."""
143
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +0000144 check_environ ()
Greg Ward1b4ede52000-03-22 00:22:44 +0000145 def _subst (match, local_vars=local_vars):
146 var_name = match.group(1)
147 if local_vars.has_key (var_name):
148 return str (local_vars[var_name])
149 else:
150 return os.environ[var_name]
151
152 return re.sub (r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, str)
153
154# subst_vars ()
Greg Ward7c1a6d42000-03-29 02:48:40 +0000155
156
Greg Warde9055132000-06-17 02:16:46 +0000157def grok_environment_error (exc, prefix="error: "):
158 """Generate a useful error message from an EnvironmentError (IOError or
159 OSError) exception object. Handles Python 1.5.1 and 1.5.2 styles, and
160 does what it can to deal with exception objects that don't have a
161 filename (which happens when the error is due to a two-file operation,
162 such as 'rename()' or 'link()'. Returns the error message as a string
163 prefixed with 'prefix'.
164 """
165 # check for Python 1.5.2-style {IO,OS}Error exception objects
166 if hasattr (exc, 'filename') and hasattr (exc, 'strerror'):
167 if exc.filename:
168 error = prefix + "%s: %s" % (exc.filename, exc.strerror)
169 else:
170 # two-argument functions in posix module don't
171 # include the filename in the exception object!
172 error = prefix + "%s" % exc.strerror
173 else:
174 error = prefix + str(exc[-1])
175
176 return error
Greg Ward6a2a3db2000-06-24 20:40:02 +0000177
178
179# Needed by 'split_quoted()'
Greg Ward2b042de2000-08-08 14:38:13 +0000180_wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace)
Greg Ward6a2a3db2000-06-24 20:40:02 +0000181_squote_re = re.compile(r"'(?:[^'\\]|\\.)*'")
182_dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"')
183
184def split_quoted (s):
185 """Split a string up according to Unix shell-like rules for quotes and
186 backslashes. In short: words are delimited by spaces, as long as those
187 spaces are not escaped by a backslash, or inside a quoted string.
188 Single and double quotes are equivalent, and the quote characters can
189 be backslash-escaped. The backslash is stripped from any two-character
190 escape sequence, leaving only the escaped character. The quote
191 characters are stripped from any quoted string. Returns a list of
192 words.
193 """
194
195 # This is a nice algorithm for splitting up a single string, since it
196 # doesn't require character-by-character examination. It was a little
197 # bit of a brain-bender to get it working right, though...
198
199 s = string.strip(s)
200 words = []
201 pos = 0
202
203 while s:
204 m = _wordchars_re.match(s, pos)
205 end = m.end()
206 if end == len(s):
207 words.append(s[:end])
208 break
209
Greg Ward2b042de2000-08-08 14:38:13 +0000210 if s[end] in string.whitespace: # unescaped, unquoted whitespace: now
Greg Ward6a2a3db2000-06-24 20:40:02 +0000211 words.append(s[:end]) # we definitely have a word delimiter
212 s = string.lstrip(s[end:])
213 pos = 0
214
215 elif s[end] == '\\': # preserve whatever is being escaped;
216 # will become part of the current word
217 s = s[:end] + s[end+1:]
218 pos = end+1
219
220 else:
221 if s[end] == "'": # slurp singly-quoted string
222 m = _squote_re.match(s, end)
223 elif s[end] == '"': # slurp doubly-quoted string
224 m = _dquote_re.match(s, end)
225 else:
226 raise RuntimeError, \
227 "this can't happen (bad char '%c')" % s[end]
228
229 if m is None:
230 raise ValueError, \
231 "bad string (mismatched %s quotes?)" % s[end]
232
233 (beg, end) = m.span()
234 s = s[:beg] + s[beg+1:end-1] + s[end:]
235 pos = m.end() - 2
236
237 if pos >= len(s):
238 words.append(s)
239 break
240
241 return words
242
243# split_quoted ()
Greg Ward1c16ac32000-08-02 01:37:30 +0000244
245
246def execute (func, args, msg=None, verbose=0, dry_run=0):
247 """Perform some action that affects the outside world (eg. by writing
248 to the filesystem). Such actions are special because they are disabled
249 by the 'dry_run' flag, and announce themselves if 'verbose' is true.
250 This method takes care of all that bureaucracy for you; all you have to
251 do is supply the function to call and an argument tuple for it (to
252 embody the "external action" being performed), and an optional message
253 to print.
254 """
255 # Generate a message if we weren't passed one
256 if msg is None:
257 msg = "%s%s" % (func.__name__, `args`)
258 if msg[-2:] == ',)': # correct for singleton tuple
259 msg = msg[0:-2] + ')'
260
261 # Print it if verbosity level is high enough
262 if verbose:
263 print msg
264
265 # And do it, as long as we're not in dry-run mode
266 if not dry_run:
267 apply(func, args)
268
269# execute()