blob: 6351bc7edc8f760a1e0347beca9bf07da4eff980 [file] [log] [blame]
Greg Ward2689e3d1999-03-22 14:52:19 +00001"""distutils.util
2
3General-purpose utility functions used throughout the Distutils
4(especially in command classes). Mostly filesystem manipulation, but
5not limited to that. The functions in this module generally raise
6DistutilsFileError when they have problems with the filesystem, because
7os.error in pre-1.5.2 Python only gives the error message and not the
8file causing it."""
9
10# created 1999/03/08, Greg Ward
11
Greg Ward3ce77fd2000-03-02 01:49:45 +000012__revision__ = "$Id$"
Greg Ward2689e3d1999-03-22 14:52:19 +000013
Greg Warda7540bd2000-03-23 04:39:16 +000014import sys, os, string, re, shutil
Greg Ward2689e3d1999-03-22 14:52:19 +000015from distutils.errors import *
Greg Ward7c1a6d42000-03-29 02:48:40 +000016from distutils.spawn import spawn
Greg Ward2689e3d1999-03-22 14:52:19 +000017
Greg Wardac1424a1999-09-21 18:37:51 +000018# cache for by mkpath() -- in addition to cheapening redundant calls,
19# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode
20PATH_CREATED = {}
21
Greg Ward2689e3d1999-03-22 14:52:19 +000022# I don't use os.makedirs because a) it's new to Python 1.5.2, and
23# b) it blows up if the directory already exists (I want to silently
24# succeed in that case).
Greg Warde765a3b1999-04-04 02:54:20 +000025def mkpath (name, mode=0777, verbose=0, dry_run=0):
Greg Ward2689e3d1999-03-22 14:52:19 +000026 """Create a directory and any missing ancestor directories. If the
27 directory already exists, return silently. Raise
28 DistutilsFileError if unable to create some directory along the
29 way (eg. some sub-path exists, but is a file rather than a
30 directory). If 'verbose' is true, print a one-line summary of
31 each mkdir to stdout."""
32
Greg Wardac1424a1999-09-21 18:37:51 +000033 global PATH_CREATED
34
Greg Ward2689e3d1999-03-22 14:52:19 +000035 # XXX what's the better way to handle verbosity? print as we create
36 # each directory in the path (the current behaviour), or only announce
Greg Wardac1424a1999-09-21 18:37:51 +000037 # the creation of the whole path? (quite easy to do the latter since
38 # we're not using a recursive algorithm)
Greg Ward2689e3d1999-03-22 14:52:19 +000039
Greg Wardf3b997a1999-10-03 20:50:41 +000040 name = os.path.normpath (name)
Greg Wardda4d1ae2000-03-29 02:53:02 +000041 created_dirs = []
Greg Ward96182d72000-03-03 03:00:02 +000042 if os.path.isdir (name) or name == '':
Greg Wardda4d1ae2000-03-29 02:53:02 +000043 return created_dirs
Greg Wardac1424a1999-09-21 18:37:51 +000044 if PATH_CREATED.get (name):
Greg Wardda4d1ae2000-03-29 02:53:02 +000045 return created_dirs
Greg Ward2689e3d1999-03-22 14:52:19 +000046
47 (head, tail) = os.path.split (name)
48 tails = [tail] # stack of lone dirs to create
49
50 while head and tail and not os.path.isdir (head):
51 #print "splitting '%s': " % head,
52 (head, tail) = os.path.split (head)
53 #print "to ('%s','%s')" % (head, tail)
54 tails.insert (0, tail) # push next higher dir onto stack
55
56 #print "stack of tails:", tails
57
Greg Warde765a3b1999-04-04 02:54:20 +000058 # now 'head' contains the deepest directory that already exists
59 # (that is, the child of 'head' in 'name' is the highest directory
60 # that does *not* exist)
Greg Ward2689e3d1999-03-22 14:52:19 +000061 for d in tails:
62 #print "head = %s, d = %s: " % (head, d),
63 head = os.path.join (head, d)
Greg Wardcd1486f1999-09-29 12:14:16 +000064 if PATH_CREATED.get (head):
65 continue
66
Greg Ward2689e3d1999-03-22 14:52:19 +000067 if verbose:
68 print "creating", head
Greg Warde765a3b1999-04-04 02:54:20 +000069
70 if not dry_run:
71 try:
72 os.mkdir (head)
Greg Wardda4d1ae2000-03-29 02:53:02 +000073 created_dirs.append(head)
Greg Warde765a3b1999-04-04 02:54:20 +000074 except os.error, (errno, errstr):
Greg Ward1b4ede52000-03-22 00:22:44 +000075 raise DistutilsFileError, \
76 "could not create '%s': %s" % (head, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +000077
Greg Wardac1424a1999-09-21 18:37:51 +000078 PATH_CREATED[head] = 1
Greg Wardda4d1ae2000-03-29 02:53:02 +000079 return created_dirs
Greg Wardac1424a1999-09-21 18:37:51 +000080
Greg Ward2689e3d1999-03-22 14:52:19 +000081# mkpath ()
82
83
Greg Ward138ce651999-09-13 03:09:38 +000084def newer (source, target):
85 """Return true if 'source' exists and is more recently modified than
86 'target', or if 'source' exists and 'target' doesn't. Return
87 false if both exist and 'target' is the same age or younger than
88 'source'. Raise DistutilsFileError if 'source' does not
89 exist."""
Greg Ward2689e3d1999-03-22 14:52:19 +000090
Greg Ward138ce651999-09-13 03:09:38 +000091 if not os.path.exists (source):
92 raise DistutilsFileError, "file '%s' does not exist" % source
93 if not os.path.exists (target):
Greg Ward2689e3d1999-03-22 14:52:19 +000094 return 1
95
Greg Ward138ce651999-09-13 03:09:38 +000096 from stat import ST_MTIME
97 mtime1 = os.stat(source)[ST_MTIME]
98 mtime2 = os.stat(target)[ST_MTIME]
Greg Ward2689e3d1999-03-22 14:52:19 +000099
100 return mtime1 > mtime2
101
102# newer ()
103
104
Greg Ward138ce651999-09-13 03:09:38 +0000105def newer_pairwise (sources, targets):
Greg Ward95526652000-03-06 03:44:32 +0000106 """Walk two filename lists in parallel, testing if each source is newer
107 than its corresponding target. Return a pair of lists (sources,
108 targets) where source is newer than target, according to the
109 semantics of 'newer()'."""
Greg Ward138ce651999-09-13 03:09:38 +0000110
111 if len (sources) != len (targets):
112 raise ValueError, "'sources' and 'targets' must be same length"
113
Greg Ward95526652000-03-06 03:44:32 +0000114 # build a pair of lists (sources, targets) where source is newer
115 n_sources = []
116 n_targets = []
117 for i in range (len (sources)):
118 if newer (sources[i], targets[i]):
119 n_sources.append (sources[i])
120 n_targets.append (targets[i])
121
122 return (n_sources, n_targets)
Greg Ward138ce651999-09-13 03:09:38 +0000123
124# newer_pairwise ()
125
126
Greg Ward7b7679e2000-01-09 22:48:59 +0000127def newer_group (sources, target, missing='error'):
Greg Ward138ce651999-09-13 03:09:38 +0000128 """Return true if 'target' is out-of-date with respect to any
129 file listed in 'sources'. In other words, if 'target' exists and
130 is newer than every file in 'sources', return false; otherwise
Greg Ward7b7679e2000-01-09 22:48:59 +0000131 return true. 'missing' controls what we do when a source file is
132 missing; the default ("error") is to blow up with an OSError from
133 inside 'stat()'; if it is "ignore", we silently drop any missing
134 source files; if it is "newer", any missing source files make us
135 assume that 'target' is out-of-date (this is handy in "dry-run"
136 mode: it'll make you pretend to carry out commands that wouldn't
137 work because inputs are missing, but that doesn't matter because
138 you're not actually going to run the commands)."""
Greg Ward138ce651999-09-13 03:09:38 +0000139
140 # If the target doesn't even exist, then it's definitely out-of-date.
141 if not os.path.exists (target):
142 return 1
143
144 # Otherwise we have to find out the hard way: if *any* source file
145 # is more recent than 'target', then 'target' is out-of-date and
146 # we can immediately return true. If we fall through to the end
147 # of the loop, then 'target' is up-to-date and we return false.
148 from stat import ST_MTIME
149 target_mtime = os.stat (target)[ST_MTIME]
150 for source in sources:
Greg Ward7b7679e2000-01-09 22:48:59 +0000151 if not os.path.exists (source):
152 if missing == 'error': # blow up when we stat() the file
153 pass
154 elif missing == 'ignore': # missing source dropped from
155 continue # target's dependency list
156 elif missing == 'newer': # missing source means target is
157 return 1 # out-of-date
158
Greg Ward138ce651999-09-13 03:09:38 +0000159 source_mtime = os.stat(source)[ST_MTIME]
160 if source_mtime > target_mtime:
161 return 1
162 else:
163 return 0
164
165# newer_group ()
166
167
Greg Wardf3b997a1999-10-03 20:50:41 +0000168# XXX this isn't used anywhere, and worse, it has the same name as a method
169# in Command with subtly different semantics. (This one just has one
170# source -> one dest; that one has many sources -> one dest.) Nuke it?
Greg Ward2689e3d1999-03-22 14:52:19 +0000171def make_file (src, dst, func, args,
172 verbose=0, update_message=None, noupdate_message=None):
173 """Makes 'dst' from 'src' (both filenames) by calling 'func' with
174 'args', but only if it needs to: i.e. if 'dst' does not exist or
175 'src' is newer than 'dst'."""
176
177 if newer (src, dst):
178 if verbose and update_message:
179 print update_message
180 apply (func, args)
181 else:
182 if verbose and noupdate_message:
183 print noupdate_message
184
185# make_file ()
186
187
188def _copy_file_contents (src, dst, buffer_size=16*1024):
189 """Copy the file 'src' to 'dst'; both must be filenames. Any error
190 opening either file, reading from 'src', or writing to 'dst',
191 raises DistutilsFileError. Data is read/written in chunks of
192 'buffer_size' bytes (default 16k). No attempt is made to handle
193 anything apart from regular files."""
194
195 # Stolen from shutil module in the standard library, but with
196 # custom error-handling added.
197
198 fsrc = None
199 fdst = None
200 try:
201 try:
202 fsrc = open(src, 'rb')
203 except os.error, (errno, errstr):
Greg Ward96182d72000-03-03 03:00:02 +0000204 raise DistutilsFileError, \
205 "could not open '%s': %s" % (src, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000206
207 try:
208 fdst = open(dst, 'wb')
209 except os.error, (errno, errstr):
Greg Ward96182d72000-03-03 03:00:02 +0000210 raise DistutilsFileError, \
211 "could not create '%s': %s" % (dst, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000212
213 while 1:
214 try:
215 buf = fsrc.read (buffer_size)
216 except os.error, (errno, errstr):
217 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000218 "could not read from '%s': %s" % (src, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000219
220 if not buf:
221 break
222
223 try:
224 fdst.write(buf)
225 except os.error, (errno, errstr):
226 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000227 "could not write to '%s': %s" % (dst, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000228
229 finally:
230 if fdst:
231 fdst.close()
232 if fsrc:
233 fsrc.close()
234
235# _copy_file_contents()
236
237
238def copy_file (src, dst,
239 preserve_mode=1,
240 preserve_times=1,
241 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000242 verbose=0,
243 dry_run=0):
Greg Ward2689e3d1999-03-22 14:52:19 +0000244
245 """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src'
246 is copied there with the same name; otherwise, it must be a
247 filename. (If the file exists, it will be ruthlessly clobbered.)
248 If 'preserve_mode' is true (the default), the file's mode (type
249 and permission bits, or whatever is analogous on the current
250 platform) is copied. If 'preserve_times' is true (the default),
251 the last-modified and last-access times are copied as well. If
252 'update' is true, 'src' will only be copied if 'dst' does not
253 exist, or if 'dst' does exist but is older than 'src'. If
254 'verbose' is true, then a one-line summary of the copy will be
Greg Ward884df451999-05-02 21:42:05 +0000255 printed to stdout.
256
257 Return true if the file was copied (or would have been copied),
258 false otherwise (ie. 'update' was true and the destination is
259 up-to-date)."""
Greg Ward2689e3d1999-03-22 14:52:19 +0000260
261 # XXX doesn't copy Mac-specific metadata
262
Greg Ward2689e3d1999-03-22 14:52:19 +0000263 from stat import *
264
265 if not os.path.isfile (src):
266 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000267 "can't copy '%s': not a regular file" % src
Greg Ward2689e3d1999-03-22 14:52:19 +0000268
269 if os.path.isdir (dst):
270 dir = dst
271 dst = os.path.join (dst, os.path.basename (src))
272 else:
273 dir = os.path.dirname (dst)
274
275 if update and not newer (src, dst):
Greg Ward884df451999-05-02 21:42:05 +0000276 if verbose:
277 print "not copying %s (output up-to-date)" % src
278 return 0
Greg Ward2689e3d1999-03-22 14:52:19 +0000279
280 if verbose:
281 print "copying %s -> %s" % (src, dir)
282
Greg Warde765a3b1999-04-04 02:54:20 +0000283 if dry_run:
Greg Ward884df451999-05-02 21:42:05 +0000284 return 1
Greg Warde765a3b1999-04-04 02:54:20 +0000285
Greg Ward911d8662000-03-07 03:34:16 +0000286 # On a Mac, use the native file copy routine
287 if os.name == 'mac':
288 import macostools
289 try:
290 macostools.copy (src, dst, 0, preserve_times)
291 except OSError, exc:
292 raise DistutilsFileError, \
293 "could not copy '%s' to '%s': %s" % (src, dst, exc[-1])
294 return 1
295
296 # Otherwise use custom routine
Greg Warde765a3b1999-04-04 02:54:20 +0000297 _copy_file_contents (src, dst)
Greg Ward2689e3d1999-03-22 14:52:19 +0000298 if preserve_mode or preserve_times:
299 st = os.stat (src)
Greg Ward5116f901999-06-08 17:05:21 +0000300
301 # According to David Ascher <da@ski.org>, utime() should be done
302 # before chmod() (at least under NT).
Greg Ward2689e3d1999-03-22 14:52:19 +0000303 if preserve_times:
304 os.utime (dst, (st[ST_ATIME], st[ST_MTIME]))
Greg Ward5116f901999-06-08 17:05:21 +0000305 if preserve_mode:
306 os.chmod (dst, S_IMODE (st[ST_MODE]))
Greg Ward2689e3d1999-03-22 14:52:19 +0000307
Greg Ward884df451999-05-02 21:42:05 +0000308 return 1
309
Greg Ward2689e3d1999-03-22 14:52:19 +0000310# copy_file ()
311
312
313def copy_tree (src, dst,
314 preserve_mode=1,
315 preserve_times=1,
316 preserve_symlinks=0,
317 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000318 verbose=0,
319 dry_run=0):
320
Greg Ward2689e3d1999-03-22 14:52:19 +0000321 """Copy an entire directory tree 'src' to a new location 'dst'. Both
322 'src' and 'dst' must be directory names. If 'src' is not a
323 directory, raise DistutilsFileError. If 'dst' does not exist, it
Greg Wardf3b997a1999-10-03 20:50:41 +0000324 is created with 'mkpath()'. The end result of the copy is that
Greg Ward2689e3d1999-03-22 14:52:19 +0000325 every file in 'src' is copied to 'dst', and directories under
Greg Ward884df451999-05-02 21:42:05 +0000326 'src' are recursively copied to 'dst'. Return the list of files
327 copied (under their output names) -- note that if 'update' is true,
328 this might be less than the list of files considered. Return
329 value is not affected by 'dry_run'.
Greg Ward2689e3d1999-03-22 14:52:19 +0000330
331 'preserve_mode' and 'preserve_times' are the same as for
332 'copy_file'; note that they only apply to regular files, not to
333 directories. If 'preserve_symlinks' is true, symlinks will be
334 copied as symlinks (on platforms that support them!); otherwise
335 (the default), the destination of the symlink will be copied.
336 'update' and 'verbose' are the same as for 'copy_file'."""
337
Greg Ward138ce651999-09-13 03:09:38 +0000338 if not dry_run and not os.path.isdir (src):
Greg Ward2689e3d1999-03-22 14:52:19 +0000339 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000340 "cannot copy tree '%s': not a directory" % src
Greg Ward2689e3d1999-03-22 14:52:19 +0000341 try:
342 names = os.listdir (src)
343 except os.error, (errno, errstr):
Greg Ward138ce651999-09-13 03:09:38 +0000344 if dry_run:
345 names = []
346 else:
347 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000348 "error listing files in '%s': %s" % (src, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000349
Greg Warde765a3b1999-04-04 02:54:20 +0000350 if not dry_run:
351 mkpath (dst, verbose=verbose)
Greg Ward2689e3d1999-03-22 14:52:19 +0000352
Greg Ward884df451999-05-02 21:42:05 +0000353 outputs = []
354
Greg Ward2689e3d1999-03-22 14:52:19 +0000355 for n in names:
356 src_name = os.path.join (src, n)
357 dst_name = os.path.join (dst, n)
358
359 if preserve_symlinks and os.path.islink (src_name):
360 link_dest = os.readlink (src_name)
Greg Warde765a3b1999-04-04 02:54:20 +0000361 if verbose:
362 print "linking %s -> %s" % (dst_name, link_dest)
363 if not dry_run:
364 os.symlink (link_dest, dst_name)
Greg Ward884df451999-05-02 21:42:05 +0000365 outputs.append (dst_name)
366
Greg Ward2689e3d1999-03-22 14:52:19 +0000367 elif os.path.isdir (src_name):
Greg Warda002edc2000-01-30 19:57:48 +0000368 outputs.extend (
Greg Ward884df451999-05-02 21:42:05 +0000369 copy_tree (src_name, dst_name,
370 preserve_mode, preserve_times, preserve_symlinks,
Greg Warda002edc2000-01-30 19:57:48 +0000371 update, verbose, dry_run))
Greg Ward2689e3d1999-03-22 14:52:19 +0000372 else:
Greg Ward884df451999-05-02 21:42:05 +0000373 if (copy_file (src_name, dst_name,
374 preserve_mode, preserve_times,
375 update, verbose, dry_run)):
376 outputs.append (dst_name)
377
378 return outputs
Greg Ward2689e3d1999-03-22 14:52:19 +0000379
380# copy_tree ()
Greg Ward138ce651999-09-13 03:09:38 +0000381
382
Greg Wardb98fe362000-03-18 15:42:22 +0000383def remove_tree (directory, verbose=0, dry_run=0):
384 """Recursively remove an entire directory tree. Any errors are ignored
385 (apart from being reported to stdout if 'verbose' is true)."""
386
387 if verbose:
388 print "removing '%s' (and everything under it)" % directory
389 if dry_run:
390 return
391 try:
392 shutil.rmtree(directory,1)
393 except (IOError, OSError), exc:
394 if verbose:
395 if exc.filename:
396 print "error removing %s: %s (%s)" % \
397 (directory, exc.strerror, exc.filename)
398 else:
399 print "error removing %s: %s" % (directory, exc.strerror)
400
401
Greg Ward138ce651999-09-13 03:09:38 +0000402# XXX I suspect this is Unix-specific -- need porting help!
403def move_file (src, dst,
404 verbose=0,
405 dry_run=0):
406
407 """Move a file 'src' to 'dst'. If 'dst' is a directory, the file
408 will be moved into it with the same name; otherwise, 'src' is
409 just renamed to 'dst'. Return the new full name of the file.
410
411 Handles cross-device moves on Unix using
412 'copy_file()'. What about other systems???"""
413
414 from os.path import exists, isfile, isdir, basename, dirname
415
416 if verbose:
417 print "moving %s -> %s" % (src, dst)
418
419 if dry_run:
420 return dst
421
422 if not isfile (src):
423 raise DistutilsFileError, \
424 "can't move '%s': not a regular file" % src
425
426 if isdir (dst):
427 dst = os.path.join (dst, basename (src))
428 elif exists (dst):
429 raise DistutilsFileError, \
430 "can't move '%s': destination '%s' already exists" % \
431 (src, dst)
432
433 if not isdir (dirname (dst)):
434 raise DistutilsFileError, \
435 "can't move '%s': destination '%s' not a valid path" % \
436 (src, dst)
437
438 copy_it = 0
439 try:
440 os.rename (src, dst)
441 except os.error, (num, msg):
442 if num == errno.EXDEV:
443 copy_it = 1
444 else:
445 raise DistutilsFileError, \
446 "couldn't move '%s' to '%s': %s" % (src, dst, msg)
447
448 if copy_it:
449 copy_file (src, dst)
450 try:
451 os.unlink (src)
452 except os.error, (num, msg):
453 try:
454 os.unlink (dst)
455 except os.error:
456 pass
457 raise DistutilsFileError, \
458 ("couldn't move '%s' to '%s' by copy/delete: " +
459 "delete '%s' failed: %s") % \
460 (src, dst, src, msg)
461
462 return dst
463
464# move_file ()
Greg Wardac1424a1999-09-21 18:37:51 +0000465
466
467def write_file (filename, contents):
Greg Wardf3b997a1999-10-03 20:50:41 +0000468 """Create a file with the specified name and write 'contents' (a
Greg Wardac1424a1999-09-21 18:37:51 +0000469 sequence of strings without line terminators) to it."""
470
471 f = open (filename, "w")
472 for line in contents:
473 f.write (line + "\n")
474 f.close ()
Greg Ward585df892000-03-01 14:40:15 +0000475
476
477def get_platform ():
478 """Return a string (suitable for tacking onto directory names) that
479 identifies the current platform. Under Unix, identifies both the OS
480 and hardware architecture, e.g. "linux-i586", "solaris-sparc",
481 "irix-mips". For Windows and Mac OS, just returns 'sys.platform' --
482 i.e. "???" or "???"."""
483
484 if os.name == 'posix':
485 uname = os.uname()
486 OS = uname[0]
487 arch = uname[4]
488 return "%s-%s" % (string.lower (OS), string.lower (arch))
489 else:
490 return sys.platform
491
492# get_platform()
Greg Ward50919292000-03-07 03:27:08 +0000493
494
495def native_path (pathname):
496 """Return 'pathname' as a name that will work on the native
497 filesystem, i.e. split it on '/' and put it back together again
498 using the current directory separator. Needed because filenames in
499 the setup script are always supplied in Unix style, and have to be
500 converted to the local convention before we can actually use them in
501 the filesystem. Raises DistutilsValueError if 'pathname' is
502 absolute (starts with '/') or contains local directory separators
503 (unless the local separator is '/', of course)."""
504
505 if pathname[0] == '/':
506 raise DistutilsValueError, "path '%s' cannot be absolute" % pathname
507 if pathname[-1] == '/':
508 raise DistutilsValueError, "path '%s' cannot end with '/'" % pathname
Greg Ward1b4ede52000-03-22 00:22:44 +0000509 if os.sep != '/' and os.sep in pathname:
510 raise DistutilsValueError, \
511 "path '%s' cannot contain '%c' character" % \
512 (pathname, os.sep)
Greg Ward50919292000-03-07 03:27:08 +0000513
514 paths = string.split (pathname, '/')
515 return apply (os.path.join, paths)
516 else:
517 return pathname
518
519# native_path ()
Greg Ward1b4ede52000-03-22 00:22:44 +0000520
521
522def _check_environ ():
523 """Ensure that 'os.environ' has all the environment variables we
524 guarantee that users can use in config files, command-line
525 options, etc. Currently this includes:
526 HOME - user's home directory (Unix only)
527 PLAT - desription of the current platform, including hardware
528 and OS (see 'get_platform()')
529 """
530
531 if os.name == 'posix' and not os.environ.has_key('HOME'):
532 import pwd
533 os.environ['HOME'] = pwd.getpwuid (os.getuid())[5]
534
535 if not os.environ.has_key('PLAT'):
536 os.environ['PLAT'] = get_platform ()
537
538
539def subst_vars (str, local_vars):
540 """Perform shell/Perl-style variable substitution on 'string'.
541 Every occurence of '$' followed by a name, or a name enclosed in
542 braces, is considered a variable. Every variable is substituted by
543 the value found in the 'local_vars' dictionary, or in 'os.environ'
544 if it's not in 'local_vars'. 'os.environ' is first checked/
545 augmented to guarantee that it contains certain values: see
546 '_check_environ()'. Raise ValueError for any variables not found in
547 either 'local_vars' or 'os.environ'."""
548
549 _check_environ ()
550 def _subst (match, local_vars=local_vars):
551 var_name = match.group(1)
552 if local_vars.has_key (var_name):
553 return str (local_vars[var_name])
554 else:
555 return os.environ[var_name]
556
557 return re.sub (r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, str)
558
559# subst_vars ()
Greg Ward7c1a6d42000-03-29 02:48:40 +0000560
561
562def make_tarball (base_dir, compress="gzip", verbose=0, dry_run=0):
563 """Create a (possibly compressed) tar file from all the files under
564 'base_dir'. 'compress' must be "gzip" (the default), "compress", or
565 None. Both "tar" and the compression utility named by 'compress'
566 must be on the default program search path, so this is probably
567 Unix-specific. The output tar file will be named 'base_dir' +
568 ".tar", possibly plus the appropriate compression extension
569 (".gz" or ".Z"). Return the output filename."""
570
571 # XXX GNU tar 1.13 has a nifty option to add a prefix directory.
572 # It's pretty new, though, so we certainly can't require it --
573 # but it would be nice to take advantage of it to skip the
574 # "create a tree of hardlinks" step! (Would also be nice to
575 # detect GNU tar to use its 'z' option and save a step.)
576
577 compress_ext = { 'gzip': ".gz",
578 'compress': ".Z" }
579
580 if compress is not None and compress not in ('gzip', 'compress'):
581 raise ValueError, \
582 "bad value for 'compress': must be None, 'gzip', or 'compress'"
583
584 archive_name = base_dir + ".tar"
585 cmd = ["tar", "-cf", archive_name, base_dir]
586 spawn (cmd, verbose=verbose, dry_run=dry_run)
587
588 if compress:
589 spawn ([compress, archive_name], verbose=verbose, dry_run=dry_run)
590 return archive_name + compress_ext[compress]
591 else:
592 return archive_name
593
594# make_tarball ()
595
596
597def make_zipfile (base_dir, verbose=0, dry_run=0):
598 """Create a ZIP file from all the files under 'base_dir'. The
599 output ZIP file will be named 'base_dir' + ".zip". Uses either the
600 InfoZIP "zip" utility (if installed and found on the default search
601 path) or the "zipfile" Python module (if available). If neither
602 tool is available, raises DistutilsExecError. Returns the name
603 of the output ZIP file."""
604
605 # This initially assumed the Unix 'zip' utility -- but
606 # apparently InfoZIP's zip.exe works the same under Windows, so
607 # no changes needed!
608
609 zip_filename = base_dir + ".zip"
610 try:
611 spawn (["zip", "-r", zip_filename, base_dir],
612 verbose=verbose, dry_run=dry_run)
613 except DistutilsExecError:
614
615 # XXX really should distinguish between "couldn't find
616 # external 'zip' command" and "zip failed" -- shouldn't try
617 # again in the latter case. (I think fixing this will
618 # require some cooperation from the spawn module -- perhaps
619 # a utility function to search the path, so we can fallback
620 # on zipfile.py without the failed spawn.)
621 try:
622 import zipfile
623 except ImportError:
624 raise DistutilsExecError, \
625 ("unable to create zip file '%s': " +
626 "could neither find a standalone zip utility nor " +
627 "import the 'zipfile' module") % zip_filename
628
629 if verbose:
630 print "creating '%s' and adding '%s' to it" % \
631 (zip_filename, base_dir)
632
633 def visit (z, dirname, names):
634 for name in names:
635 path = os.path.join (dirname, name)
636 if os.path.isfile (path):
637 z.write (path, path)
638
639 if not dry_run:
640 z = zipfile.ZipFile (zip_filename, "wb",
641 compression=zipfile.ZIP_DEFLATED)
642
643 os.path.walk (base_dir, visit, z)
644 z.close()
645
646 return zip_filename
647
648# make_zipfile ()