blob: 37cd4b554ef4ee6dc8bc21b7f84ec39476deb879 [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
Jeremy Hyltona05e2932000-06-28 14:48:01 +000021# More backwards compatibility hacks
Greg Wardaa458bc2000-04-22 15:14:58 +000022def extend (list, new_list):
23 """Appends the list 'new_list' to 'list', just like the 'extend()'
24 list method does in Python 1.5.2 -- but this works on earlier
25 versions of Python too."""
26
27 if hasattr (list, 'extend'):
28 list.extend (new_list)
29 else:
30 list[len(list):] = new_list
31
32# extend ()
33
34
Greg Ward585df892000-03-01 14:40:15 +000035def get_platform ():
36 """Return a string (suitable for tacking onto directory names) that
Greg Wardb75c4852000-06-18 15:45:55 +000037 identifies the current platform. Currently, this is just
38 'sys.platform'.
39 """
40 return sys.platform
Greg Ward50919292000-03-07 03:27:08 +000041
42
Greg Wardd8dfb4c2000-05-31 02:32:10 +000043def convert_path (pathname):
Greg Ward50919292000-03-07 03:27:08 +000044 """Return 'pathname' as a name that will work on the native
45 filesystem, i.e. split it on '/' and put it back together again
46 using the current directory separator. Needed because filenames in
47 the setup script are always supplied in Unix style, and have to be
48 converted to the local convention before we can actually use them in
Greg Ward02a1a2b2000-04-15 22:15:07 +000049 the filesystem. Raises ValueError if 'pathname' is
Greg Ward50919292000-03-07 03:27:08 +000050 absolute (starts with '/') or contains local directory separators
51 (unless the local separator is '/', of course)."""
52
53 if pathname[0] == '/':
Greg Ward02a1a2b2000-04-15 22:15:07 +000054 raise ValueError, "path '%s' cannot be absolute" % pathname
Greg Ward50919292000-03-07 03:27:08 +000055 if pathname[-1] == '/':
Greg Ward02a1a2b2000-04-15 22:15:07 +000056 raise ValueError, "path '%s' cannot end with '/'" % pathname
Greg Ward464023f2000-04-25 01:33:11 +000057 if os.sep != '/':
Greg Wardd8dfb4c2000-05-31 02:32:10 +000058 paths = string.split (pathname, '/')
59 return apply (os.path.join, paths)
Greg Ward50919292000-03-07 03:27:08 +000060 else:
61 return pathname
62
Greg Wardd8dfb4c2000-05-31 02:32:10 +000063# convert_path ()
Greg Ward1b4ede52000-03-22 00:22:44 +000064
65
Greg Ward67f75d42000-04-27 01:53:46 +000066def change_root (new_root, pathname):
Greg Ward67f75d42000-04-27 01:53:46 +000067 """Return 'pathname' with 'new_root' prepended. If 'pathname' is
68 relative, this is equivalent to "os.path.join(new_root,pathname)".
69 Otherwise, it requires making 'pathname' relative and then joining the
Greg Ward4b46ef92000-05-31 02:14:32 +000070 two, which is tricky on DOS/Windows and Mac OS.
71 """
72 if os.name == 'posix':
73 if not os.path.isabs (pathname):
74 return os.path.join (new_root, pathname)
75 else:
76 return os.path.join (new_root, pathname[1:])
Greg Ward67f75d42000-04-27 01:53:46 +000077
78 elif os.name == 'nt':
Greg Ward67f75d42000-04-27 01:53:46 +000079 (drive, path) = os.path.splitdrive (pathname)
Greg Ward4b46ef92000-05-31 02:14:32 +000080 if path[0] == '\\':
81 path = path[1:]
82 return os.path.join (new_root, path)
Greg Ward67f75d42000-04-27 01:53:46 +000083
84 elif os.name == 'mac':
85 raise RuntimeError, "no clue how to do this on Mac OS"
86
87 else:
88 raise DistutilsPlatformError, \
89 "nothing known about platform '%s'" % os.name
90
91
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +000092_environ_checked = 0
93def check_environ ():
Greg Ward1b4ede52000-03-22 00:22:44 +000094 """Ensure that 'os.environ' has all the environment variables we
95 guarantee that users can use in config files, command-line
96 options, etc. Currently this includes:
97 HOME - user's home directory (Unix only)
Greg Ward612eb9f2000-07-27 02:13:20 +000098 PLAT - description of the current platform, including hardware
Greg Ward1b4ede52000-03-22 00:22:44 +000099 and OS (see 'get_platform()')
100 """
101
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +0000102 global _environ_checked
103 if _environ_checked:
104 return
105
Greg Ward1b4ede52000-03-22 00:22:44 +0000106 if os.name == 'posix' and not os.environ.has_key('HOME'):
107 import pwd
108 os.environ['HOME'] = pwd.getpwuid (os.getuid())[5]
109
110 if not os.environ.has_key('PLAT'):
111 os.environ['PLAT'] = get_platform ()
112
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +0000113 _environ_checked = 1
114
Greg Ward1b4ede52000-03-22 00:22:44 +0000115
116def subst_vars (str, local_vars):
117 """Perform shell/Perl-style variable substitution on 'string'.
Greg Ward612eb9f2000-07-27 02:13:20 +0000118 Every occurrence of '$' followed by a name, or a name enclosed in
Greg Ward1b4ede52000-03-22 00:22:44 +0000119 braces, is considered a variable. Every variable is substituted by
120 the value found in the 'local_vars' dictionary, or in 'os.environ'
121 if it's not in 'local_vars'. 'os.environ' is first checked/
122 augmented to guarantee that it contains certain values: see
123 '_check_environ()'. Raise ValueError for any variables not found in
124 either 'local_vars' or 'os.environ'."""
125
Gregory P. Smithe7e35ac2000-05-12 00:40:00 +0000126 check_environ ()
Greg Ward1b4ede52000-03-22 00:22:44 +0000127 def _subst (match, local_vars=local_vars):
128 var_name = match.group(1)
129 if local_vars.has_key (var_name):
130 return str (local_vars[var_name])
131 else:
132 return os.environ[var_name]
133
134 return re.sub (r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, str)
135
136# subst_vars ()
Greg Ward7c1a6d42000-03-29 02:48:40 +0000137
138
Greg Warde9055132000-06-17 02:16:46 +0000139def grok_environment_error (exc, prefix="error: "):
140 """Generate a useful error message from an EnvironmentError (IOError or
141 OSError) exception object. Handles Python 1.5.1 and 1.5.2 styles, and
142 does what it can to deal with exception objects that don't have a
143 filename (which happens when the error is due to a two-file operation,
144 such as 'rename()' or 'link()'. Returns the error message as a string
145 prefixed with 'prefix'.
146 """
147 # check for Python 1.5.2-style {IO,OS}Error exception objects
148 if hasattr (exc, 'filename') and hasattr (exc, 'strerror'):
149 if exc.filename:
150 error = prefix + "%s: %s" % (exc.filename, exc.strerror)
151 else:
152 # two-argument functions in posix module don't
153 # include the filename in the exception object!
154 error = prefix + "%s" % exc.strerror
155 else:
156 error = prefix + str(exc[-1])
157
158 return error
Greg Ward6a2a3db2000-06-24 20:40:02 +0000159
160
161# Needed by 'split_quoted()'
162_wordchars_re = re.compile(r'[^\\\'\"\ ]*')
163_squote_re = re.compile(r"'(?:[^'\\]|\\.)*'")
164_dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"')
165
166def split_quoted (s):
167 """Split a string up according to Unix shell-like rules for quotes and
168 backslashes. In short: words are delimited by spaces, as long as those
169 spaces are not escaped by a backslash, or inside a quoted string.
170 Single and double quotes are equivalent, and the quote characters can
171 be backslash-escaped. The backslash is stripped from any two-character
172 escape sequence, leaving only the escaped character. The quote
173 characters are stripped from any quoted string. Returns a list of
174 words.
175 """
176
177 # This is a nice algorithm for splitting up a single string, since it
178 # doesn't require character-by-character examination. It was a little
179 # bit of a brain-bender to get it working right, though...
180
181 s = string.strip(s)
182 words = []
183 pos = 0
184
185 while s:
186 m = _wordchars_re.match(s, pos)
187 end = m.end()
188 if end == len(s):
189 words.append(s[:end])
190 break
191
192 if s[end] == ' ': # unescaped, unquoted space: now
193 words.append(s[:end]) # we definitely have a word delimiter
194 s = string.lstrip(s[end:])
195 pos = 0
196
197 elif s[end] == '\\': # preserve whatever is being escaped;
198 # will become part of the current word
199 s = s[:end] + s[end+1:]
200 pos = end+1
201
202 else:
203 if s[end] == "'": # slurp singly-quoted string
204 m = _squote_re.match(s, end)
205 elif s[end] == '"': # slurp doubly-quoted string
206 m = _dquote_re.match(s, end)
207 else:
208 raise RuntimeError, \
209 "this can't happen (bad char '%c')" % s[end]
210
211 if m is None:
212 raise ValueError, \
213 "bad string (mismatched %s quotes?)" % s[end]
214
215 (beg, end) = m.span()
216 s = s[:beg] + s[beg+1:end-1] + s[end:]
217 pos = m.end() - 2
218
219 if pos >= len(s):
220 words.append(s)
221 break
222
223 return words
224
225# split_quoted ()
Greg Ward1c16ac32000-08-02 01:37:30 +0000226
227
228def execute (func, args, msg=None, verbose=0, dry_run=0):
229 """Perform some action that affects the outside world (eg. by writing
230 to the filesystem). Such actions are special because they are disabled
231 by the 'dry_run' flag, and announce themselves if 'verbose' is true.
232 This method takes care of all that bureaucracy for you; all you have to
233 do is supply the function to call and an argument tuple for it (to
234 embody the "external action" being performed), and an optional message
235 to print.
236 """
237 # Generate a message if we weren't passed one
238 if msg is None:
239 msg = "%s%s" % (func.__name__, `args`)
240 if msg[-2:] == ',)': # correct for singleton tuple
241 msg = msg[0:-2] + ')'
242
243 # Print it if verbosity level is high enough
244 if verbose:
245 print msg
246
247 # And do it, as long as we're not in dry-run mode
248 if not dry_run:
249 apply(func, args)
250
251# execute()