blob: 5c1de789971992b5a10d4b959c9dc083b1a2d33f [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 Wardaebf7062000-04-04 02:05:59 +000014# for backwards compatibility:
15from distutils.file_util import *
16from distutils.dir_util import *
17from distutils.dep_util import *
18from distutils.archive_util import *
Greg Ward585df892000-03-01 14:40:15 +000019
20
Greg Wardaa458bc2000-04-22 15:14:58 +000021# Need to define 'abspath()', because it was new with Python 1.5.2
22if hasattr (os.path, 'abspath'):
23 abspath = os.path.abspath
24else:
25 def abspath(path):
26 if not os.path.isabs(path):
27 path = os.path.join(os.getcwd(), path)
28 return os.path.normpath(path)
29
30
31# More backwards compatability hacks
32def extend (list, new_list):
33 """Appends the list 'new_list' to 'list', just like the 'extend()'
34 list method does in Python 1.5.2 -- but this works on earlier
35 versions of Python too."""
36
37 if hasattr (list, 'extend'):
38 list.extend (new_list)
39 else:
40 list[len(list):] = new_list
41
42# extend ()
43
44
Greg Ward585df892000-03-01 14:40:15 +000045def get_platform ():
46 """Return a string (suitable for tacking onto directory names) that
Greg Wardb75c4852000-06-18 15:45:55 +000047 identifies the current platform. Currently, this is just
48 'sys.platform'.
49 """
50 return sys.platform
Greg Ward50919292000-03-07 03:27:08 +000051
52
Greg Wardd8dfb4c2000-05-31 02:32:10 +000053def convert_path (pathname):
Greg Ward50919292000-03-07 03:27:08 +000054 """Return 'pathname' as a name that will work on the native
55 filesystem, i.e. split it on '/' and put it back together again
56 using the current directory separator. Needed because filenames in
57 the setup script are always supplied in Unix style, and have to be
58 converted to the local convention before we can actually use them in
Greg Ward02a1a2b2000-04-15 22:15:07 +000059 the filesystem. Raises ValueError if 'pathname' is
Greg Ward50919292000-03-07 03:27:08 +000060 absolute (starts with '/') or contains local directory separators
61 (unless the local separator is '/', of course)."""
62
63 if pathname[0] == '/':
Greg Ward02a1a2b2000-04-15 22:15:07 +000064 raise ValueError, "path '%s' cannot be absolute" % pathname
Greg Ward50919292000-03-07 03:27:08 +000065 if pathname[-1] == '/':
Greg Ward02a1a2b2000-04-15 22:15:07 +000066 raise ValueError, "path '%s' cannot end with '/'" % pathname
Greg Ward464023f2000-04-25 01:33:11 +000067 if os.sep != '/':
Greg Wardd8dfb4c2000-05-31 02:32:10 +000068 paths = string.split (pathname, '/')
69 return apply (os.path.join, paths)
Greg Ward50919292000-03-07 03:27:08 +000070 else:
71 return pathname
72
Greg Wardd8dfb4c2000-05-31 02:32:10 +000073# convert_path ()
Greg Ward1b4ede52000-03-22 00:22:44 +000074
75
Greg Ward67f75d42000-04-27 01:53:46 +000076def change_root (new_root, pathname):
Greg Ward67f75d42000-04-27 01:53:46 +000077 """Return 'pathname' with 'new_root' prepended. If 'pathname' is
78 relative, this is equivalent to "os.path.join(new_root,pathname)".
79 Otherwise, it requires making 'pathname' relative and then joining the
Greg Ward4b46ef92000-05-31 02:14:32 +000080 two, which is tricky on DOS/Windows and Mac OS.
81 """
82 if os.name == 'posix':
83 if not os.path.isabs (pathname):
84 return os.path.join (new_root, pathname)
85 else:
86 return os.path.join (new_root, pathname[1:])
Greg Ward67f75d42000-04-27 01:53:46 +000087
88 elif os.name == 'nt':
Greg Ward67f75d42000-04-27 01:53:46 +000089 (drive, path) = os.path.splitdrive (pathname)
Greg Ward4b46ef92000-05-31 02:14:32 +000090 if path[0] == '\\':
91 path = path[1:]
92 return os.path.join (new_root, path)
Greg Ward67f75d42000-04-27 01:53:46 +000093
94 elif os.name == 'mac':
95 raise RuntimeError, "no clue how to do this on Mac OS"
96
97 else:
98 raise DistutilsPlatformError, \
99 "nothing known about platform '%s'" % os.name
100
101
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +0000102_environ_checked = 0
103def check_environ ():
Greg Ward1b4ede52000-03-22 00:22:44 +0000104 """Ensure that 'os.environ' has all the environment variables we
105 guarantee that users can use in config files, command-line
106 options, etc. Currently this includes:
107 HOME - user's home directory (Unix only)
108 PLAT - desription of the current platform, including hardware
109 and OS (see 'get_platform()')
110 """
111
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +0000112 global _environ_checked
113 if _environ_checked:
114 return
115
Greg Ward1b4ede52000-03-22 00:22:44 +0000116 if os.name == 'posix' and not os.environ.has_key('HOME'):
117 import pwd
118 os.environ['HOME'] = pwd.getpwuid (os.getuid())[5]
119
120 if not os.environ.has_key('PLAT'):
121 os.environ['PLAT'] = get_platform ()
122
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +0000123 _environ_checked = 1
124
Greg Ward1b4ede52000-03-22 00:22:44 +0000125
126def subst_vars (str, local_vars):
127 """Perform shell/Perl-style variable substitution on 'string'.
128 Every occurence of '$' followed by a name, or a name enclosed in
129 braces, is considered a variable. Every variable is substituted by
130 the value found in the 'local_vars' dictionary, or in 'os.environ'
131 if it's not in 'local_vars'. 'os.environ' is first checked/
132 augmented to guarantee that it contains certain values: see
133 '_check_environ()'. Raise ValueError for any variables not found in
134 either 'local_vars' or 'os.environ'."""
135
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +0000136 check_environ ()
Greg Ward1b4ede52000-03-22 00:22:44 +0000137 def _subst (match, local_vars=local_vars):
138 var_name = match.group(1)
139 if local_vars.has_key (var_name):
140 return str (local_vars[var_name])
141 else:
142 return os.environ[var_name]
143
144 return re.sub (r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, str)
145
146# subst_vars ()
Greg Ward7c1a6d42000-03-29 02:48:40 +0000147
148
Greg Warde9055132000-06-17 02:16:46 +0000149def grok_environment_error (exc, prefix="error: "):
150 """Generate a useful error message from an EnvironmentError (IOError or
151 OSError) exception object. Handles Python 1.5.1 and 1.5.2 styles, and
152 does what it can to deal with exception objects that don't have a
153 filename (which happens when the error is due to a two-file operation,
154 such as 'rename()' or 'link()'. Returns the error message as a string
155 prefixed with 'prefix'.
156 """
157 # check for Python 1.5.2-style {IO,OS}Error exception objects
158 if hasattr (exc, 'filename') and hasattr (exc, 'strerror'):
159 if exc.filename:
160 error = prefix + "%s: %s" % (exc.filename, exc.strerror)
161 else:
162 # two-argument functions in posix module don't
163 # include the filename in the exception object!
164 error = prefix + "%s" % exc.strerror
165 else:
166 error = prefix + str(exc[-1])
167
168 return error
Greg Ward6a2a3db2000-06-24 20:40:02 +0000169
170
171# Needed by 'split_quoted()'
172_wordchars_re = re.compile(r'[^\\\'\"\ ]*')
173_squote_re = re.compile(r"'(?:[^'\\]|\\.)*'")
174_dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"')
175
176def split_quoted (s):
177 """Split a string up according to Unix shell-like rules for quotes and
178 backslashes. In short: words are delimited by spaces, as long as those
179 spaces are not escaped by a backslash, or inside a quoted string.
180 Single and double quotes are equivalent, and the quote characters can
181 be backslash-escaped. The backslash is stripped from any two-character
182 escape sequence, leaving only the escaped character. The quote
183 characters are stripped from any quoted string. Returns a list of
184 words.
185 """
186
187 # This is a nice algorithm for splitting up a single string, since it
188 # doesn't require character-by-character examination. It was a little
189 # bit of a brain-bender to get it working right, though...
190
191 s = string.strip(s)
192 words = []
193 pos = 0
194
195 while s:
196 m = _wordchars_re.match(s, pos)
197 end = m.end()
198 if end == len(s):
199 words.append(s[:end])
200 break
201
202 if s[end] == ' ': # unescaped, unquoted space: now
203 words.append(s[:end]) # we definitely have a word delimiter
204 s = string.lstrip(s[end:])
205 pos = 0
206
207 elif s[end] == '\\': # preserve whatever is being escaped;
208 # will become part of the current word
209 s = s[:end] + s[end+1:]
210 pos = end+1
211
212 else:
213 if s[end] == "'": # slurp singly-quoted string
214 m = _squote_re.match(s, end)
215 elif s[end] == '"': # slurp doubly-quoted string
216 m = _dquote_re.match(s, end)
217 else:
218 raise RuntimeError, \
219 "this can't happen (bad char '%c')" % s[end]
220
221 if m is None:
222 raise ValueError, \
223 "bad string (mismatched %s quotes?)" % s[end]
224
225 (beg, end) = m.span()
226 s = s[:beg] + s[beg+1:end-1] + s[end:]
227 pos = m.end() - 2
228
229 if pos >= len(s):
230 words.append(s)
231 break
232
233 return words
234
235# split_quoted ()