blob: 8242e10fc1b939e56585dfad0d1d03db12960820 [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
Greg Ward739d0662000-03-29 02:56:34 +000027 directory already exists (or if 'name' is the empty string, which
28 means the current directory, which of course exists), then do
29 nothing. Raise DistutilsFileError if unable to create some
30 directory along the way (eg. some sub-path exists, but is a file
31 rather than a directory). If 'verbose' is true, print a one-line
32 summary of each mkdir to stdout. Return the list of directories
33 actually created."""
Greg Ward2689e3d1999-03-22 14:52:19 +000034
Greg Wardac1424a1999-09-21 18:37:51 +000035 global PATH_CREATED
36
Greg Ward2689e3d1999-03-22 14:52:19 +000037 # XXX what's the better way to handle verbosity? print as we create
38 # each directory in the path (the current behaviour), or only announce
Greg Wardac1424a1999-09-21 18:37:51 +000039 # the creation of the whole path? (quite easy to do the latter since
40 # we're not using a recursive algorithm)
Greg Ward2689e3d1999-03-22 14:52:19 +000041
Greg Wardf3b997a1999-10-03 20:50:41 +000042 name = os.path.normpath (name)
Greg Wardda4d1ae2000-03-29 02:53:02 +000043 created_dirs = []
Greg Ward96182d72000-03-03 03:00:02 +000044 if os.path.isdir (name) or name == '':
Greg Wardda4d1ae2000-03-29 02:53:02 +000045 return created_dirs
Greg Wardac1424a1999-09-21 18:37:51 +000046 if PATH_CREATED.get (name):
Greg Wardda4d1ae2000-03-29 02:53:02 +000047 return created_dirs
Greg Ward2689e3d1999-03-22 14:52:19 +000048
49 (head, tail) = os.path.split (name)
50 tails = [tail] # stack of lone dirs to create
51
52 while head and tail and not os.path.isdir (head):
53 #print "splitting '%s': " % head,
54 (head, tail) = os.path.split (head)
55 #print "to ('%s','%s')" % (head, tail)
56 tails.insert (0, tail) # push next higher dir onto stack
57
58 #print "stack of tails:", tails
59
Greg Warde765a3b1999-04-04 02:54:20 +000060 # now 'head' contains the deepest directory that already exists
61 # (that is, the child of 'head' in 'name' is the highest directory
62 # that does *not* exist)
Greg Ward2689e3d1999-03-22 14:52:19 +000063 for d in tails:
64 #print "head = %s, d = %s: " % (head, d),
65 head = os.path.join (head, d)
Greg Wardcd1486f1999-09-29 12:14:16 +000066 if PATH_CREATED.get (head):
67 continue
68
Greg Ward2689e3d1999-03-22 14:52:19 +000069 if verbose:
70 print "creating", head
Greg Warde765a3b1999-04-04 02:54:20 +000071
72 if not dry_run:
73 try:
74 os.mkdir (head)
Greg Wardda4d1ae2000-03-29 02:53:02 +000075 created_dirs.append(head)
Greg Ward739d0662000-03-29 02:56:34 +000076 except OSError, exc:
Greg Ward1b4ede52000-03-22 00:22:44 +000077 raise DistutilsFileError, \
Greg Ward739d0662000-03-29 02:56:34 +000078 "could not create '%s': %s" % (head, exc[-1])
Greg Ward2689e3d1999-03-22 14:52:19 +000079
Greg Wardac1424a1999-09-21 18:37:51 +000080 PATH_CREATED[head] = 1
Greg Wardda4d1ae2000-03-29 02:53:02 +000081 return created_dirs
Greg Wardac1424a1999-09-21 18:37:51 +000082
Greg Ward2689e3d1999-03-22 14:52:19 +000083# mkpath ()
84
85
Greg Ward138ce651999-09-13 03:09:38 +000086def newer (source, target):
87 """Return true if 'source' exists and is more recently modified than
88 'target', or if 'source' exists and 'target' doesn't. Return
89 false if both exist and 'target' is the same age or younger than
90 'source'. Raise DistutilsFileError if 'source' does not
91 exist."""
Greg Ward2689e3d1999-03-22 14:52:19 +000092
Greg Ward138ce651999-09-13 03:09:38 +000093 if not os.path.exists (source):
94 raise DistutilsFileError, "file '%s' does not exist" % source
95 if not os.path.exists (target):
Greg Ward2689e3d1999-03-22 14:52:19 +000096 return 1
97
Greg Ward138ce651999-09-13 03:09:38 +000098 from stat import ST_MTIME
99 mtime1 = os.stat(source)[ST_MTIME]
100 mtime2 = os.stat(target)[ST_MTIME]
Greg Ward2689e3d1999-03-22 14:52:19 +0000101
102 return mtime1 > mtime2
103
104# newer ()
105
106
Greg Ward138ce651999-09-13 03:09:38 +0000107def newer_pairwise (sources, targets):
Greg Ward95526652000-03-06 03:44:32 +0000108 """Walk two filename lists in parallel, testing if each source is newer
109 than its corresponding target. Return a pair of lists (sources,
110 targets) where source is newer than target, according to the
111 semantics of 'newer()'."""
Greg Ward138ce651999-09-13 03:09:38 +0000112
113 if len (sources) != len (targets):
114 raise ValueError, "'sources' and 'targets' must be same length"
115
Greg Ward95526652000-03-06 03:44:32 +0000116 # build a pair of lists (sources, targets) where source is newer
117 n_sources = []
118 n_targets = []
119 for i in range (len (sources)):
120 if newer (sources[i], targets[i]):
121 n_sources.append (sources[i])
122 n_targets.append (targets[i])
123
124 return (n_sources, n_targets)
Greg Ward138ce651999-09-13 03:09:38 +0000125
126# newer_pairwise ()
127
128
Greg Ward7b7679e2000-01-09 22:48:59 +0000129def newer_group (sources, target, missing='error'):
Greg Ward138ce651999-09-13 03:09:38 +0000130 """Return true if 'target' is out-of-date with respect to any
131 file listed in 'sources'. In other words, if 'target' exists and
132 is newer than every file in 'sources', return false; otherwise
Greg Ward7b7679e2000-01-09 22:48:59 +0000133 return true. 'missing' controls what we do when a source file is
134 missing; the default ("error") is to blow up with an OSError from
135 inside 'stat()'; if it is "ignore", we silently drop any missing
136 source files; if it is "newer", any missing source files make us
137 assume that 'target' is out-of-date (this is handy in "dry-run"
138 mode: it'll make you pretend to carry out commands that wouldn't
139 work because inputs are missing, but that doesn't matter because
140 you're not actually going to run the commands)."""
Greg Ward138ce651999-09-13 03:09:38 +0000141
142 # If the target doesn't even exist, then it's definitely out-of-date.
143 if not os.path.exists (target):
144 return 1
145
146 # Otherwise we have to find out the hard way: if *any* source file
147 # is more recent than 'target', then 'target' is out-of-date and
148 # we can immediately return true. If we fall through to the end
149 # of the loop, then 'target' is up-to-date and we return false.
150 from stat import ST_MTIME
151 target_mtime = os.stat (target)[ST_MTIME]
152 for source in sources:
Greg Ward7b7679e2000-01-09 22:48:59 +0000153 if not os.path.exists (source):
154 if missing == 'error': # blow up when we stat() the file
155 pass
156 elif missing == 'ignore': # missing source dropped from
157 continue # target's dependency list
158 elif missing == 'newer': # missing source means target is
159 return 1 # out-of-date
160
Greg Ward138ce651999-09-13 03:09:38 +0000161 source_mtime = os.stat(source)[ST_MTIME]
162 if source_mtime > target_mtime:
163 return 1
164 else:
165 return 0
166
167# newer_group ()
168
169
Greg Wardf3b997a1999-10-03 20:50:41 +0000170# XXX this isn't used anywhere, and worse, it has the same name as a method
171# in Command with subtly different semantics. (This one just has one
172# source -> one dest; that one has many sources -> one dest.) Nuke it?
Greg Ward2689e3d1999-03-22 14:52:19 +0000173def make_file (src, dst, func, args,
174 verbose=0, update_message=None, noupdate_message=None):
175 """Makes 'dst' from 'src' (both filenames) by calling 'func' with
176 'args', but only if it needs to: i.e. if 'dst' does not exist or
177 'src' is newer than 'dst'."""
178
179 if newer (src, dst):
180 if verbose and update_message:
181 print update_message
182 apply (func, args)
183 else:
184 if verbose and noupdate_message:
185 print noupdate_message
186
187# make_file ()
188
189
190def _copy_file_contents (src, dst, buffer_size=16*1024):
191 """Copy the file 'src' to 'dst'; both must be filenames. Any error
192 opening either file, reading from 'src', or writing to 'dst',
193 raises DistutilsFileError. Data is read/written in chunks of
194 'buffer_size' bytes (default 16k). No attempt is made to handle
195 anything apart from regular files."""
196
197 # Stolen from shutil module in the standard library, but with
198 # custom error-handling added.
199
200 fsrc = None
201 fdst = None
202 try:
203 try:
204 fsrc = open(src, 'rb')
205 except os.error, (errno, errstr):
Greg Ward96182d72000-03-03 03:00:02 +0000206 raise DistutilsFileError, \
207 "could not open '%s': %s" % (src, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000208
209 try:
210 fdst = open(dst, 'wb')
211 except os.error, (errno, errstr):
Greg Ward96182d72000-03-03 03:00:02 +0000212 raise DistutilsFileError, \
213 "could not create '%s': %s" % (dst, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000214
215 while 1:
216 try:
217 buf = fsrc.read (buffer_size)
218 except os.error, (errno, errstr):
219 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000220 "could not read from '%s': %s" % (src, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000221
222 if not buf:
223 break
224
225 try:
226 fdst.write(buf)
227 except os.error, (errno, errstr):
228 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000229 "could not write to '%s': %s" % (dst, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000230
231 finally:
232 if fdst:
233 fdst.close()
234 if fsrc:
235 fsrc.close()
236
237# _copy_file_contents()
238
239
240def copy_file (src, dst,
241 preserve_mode=1,
242 preserve_times=1,
243 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000244 verbose=0,
245 dry_run=0):
Greg Ward2689e3d1999-03-22 14:52:19 +0000246
247 """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src'
248 is copied there with the same name; otherwise, it must be a
249 filename. (If the file exists, it will be ruthlessly clobbered.)
250 If 'preserve_mode' is true (the default), the file's mode (type
251 and permission bits, or whatever is analogous on the current
252 platform) is copied. If 'preserve_times' is true (the default),
253 the last-modified and last-access times are copied as well. If
254 'update' is true, 'src' will only be copied if 'dst' does not
255 exist, or if 'dst' does exist but is older than 'src'. If
256 'verbose' is true, then a one-line summary of the copy will be
Greg Ward884df451999-05-02 21:42:05 +0000257 printed to stdout.
258
259 Return true if the file was copied (or would have been copied),
260 false otherwise (ie. 'update' was true and the destination is
261 up-to-date)."""
Greg Ward2689e3d1999-03-22 14:52:19 +0000262
263 # XXX doesn't copy Mac-specific metadata
264
Greg Ward2689e3d1999-03-22 14:52:19 +0000265 from stat import *
266
267 if not os.path.isfile (src):
268 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000269 "can't copy '%s': not a regular file" % src
Greg Ward2689e3d1999-03-22 14:52:19 +0000270
271 if os.path.isdir (dst):
272 dir = dst
273 dst = os.path.join (dst, os.path.basename (src))
274 else:
275 dir = os.path.dirname (dst)
276
277 if update and not newer (src, dst):
Greg Ward884df451999-05-02 21:42:05 +0000278 if verbose:
279 print "not copying %s (output up-to-date)" % src
280 return 0
Greg Ward2689e3d1999-03-22 14:52:19 +0000281
282 if verbose:
283 print "copying %s -> %s" % (src, dir)
284
Greg Warde765a3b1999-04-04 02:54:20 +0000285 if dry_run:
Greg Ward884df451999-05-02 21:42:05 +0000286 return 1
Greg Warde765a3b1999-04-04 02:54:20 +0000287
Greg Ward911d8662000-03-07 03:34:16 +0000288 # On a Mac, use the native file copy routine
289 if os.name == 'mac':
290 import macostools
291 try:
292 macostools.copy (src, dst, 0, preserve_times)
293 except OSError, exc:
294 raise DistutilsFileError, \
295 "could not copy '%s' to '%s': %s" % (src, dst, exc[-1])
296 return 1
297
298 # Otherwise use custom routine
Greg Warde765a3b1999-04-04 02:54:20 +0000299 _copy_file_contents (src, dst)
Greg Ward2689e3d1999-03-22 14:52:19 +0000300 if preserve_mode or preserve_times:
301 st = os.stat (src)
Greg Ward5116f901999-06-08 17:05:21 +0000302
303 # According to David Ascher <da@ski.org>, utime() should be done
304 # before chmod() (at least under NT).
Greg Ward2689e3d1999-03-22 14:52:19 +0000305 if preserve_times:
306 os.utime (dst, (st[ST_ATIME], st[ST_MTIME]))
Greg Ward5116f901999-06-08 17:05:21 +0000307 if preserve_mode:
308 os.chmod (dst, S_IMODE (st[ST_MODE]))
Greg Ward2689e3d1999-03-22 14:52:19 +0000309
Greg Ward884df451999-05-02 21:42:05 +0000310 return 1
311
Greg Ward2689e3d1999-03-22 14:52:19 +0000312# copy_file ()
313
314
315def copy_tree (src, dst,
316 preserve_mode=1,
317 preserve_times=1,
318 preserve_symlinks=0,
319 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000320 verbose=0,
321 dry_run=0):
322
Greg Ward2689e3d1999-03-22 14:52:19 +0000323 """Copy an entire directory tree 'src' to a new location 'dst'. Both
324 'src' and 'dst' must be directory names. If 'src' is not a
325 directory, raise DistutilsFileError. If 'dst' does not exist, it
Greg Wardf3b997a1999-10-03 20:50:41 +0000326 is created with 'mkpath()'. The end result of the copy is that
Greg Ward2689e3d1999-03-22 14:52:19 +0000327 every file in 'src' is copied to 'dst', and directories under
Greg Ward884df451999-05-02 21:42:05 +0000328 'src' are recursively copied to 'dst'. Return the list of files
329 copied (under their output names) -- note that if 'update' is true,
330 this might be less than the list of files considered. Return
331 value is not affected by 'dry_run'.
Greg Ward2689e3d1999-03-22 14:52:19 +0000332
333 'preserve_mode' and 'preserve_times' are the same as for
334 'copy_file'; note that they only apply to regular files, not to
335 directories. If 'preserve_symlinks' is true, symlinks will be
336 copied as symlinks (on platforms that support them!); otherwise
337 (the default), the destination of the symlink will be copied.
338 'update' and 'verbose' are the same as for 'copy_file'."""
339
Greg Ward138ce651999-09-13 03:09:38 +0000340 if not dry_run and not os.path.isdir (src):
Greg Ward2689e3d1999-03-22 14:52:19 +0000341 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000342 "cannot copy tree '%s': not a directory" % src
Greg Ward2689e3d1999-03-22 14:52:19 +0000343 try:
344 names = os.listdir (src)
345 except os.error, (errno, errstr):
Greg Ward138ce651999-09-13 03:09:38 +0000346 if dry_run:
347 names = []
348 else:
349 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000350 "error listing files in '%s': %s" % (src, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000351
Greg Warde765a3b1999-04-04 02:54:20 +0000352 if not dry_run:
353 mkpath (dst, verbose=verbose)
Greg Ward2689e3d1999-03-22 14:52:19 +0000354
Greg Ward884df451999-05-02 21:42:05 +0000355 outputs = []
356
Greg Ward2689e3d1999-03-22 14:52:19 +0000357 for n in names:
358 src_name = os.path.join (src, n)
359 dst_name = os.path.join (dst, n)
360
361 if preserve_symlinks and os.path.islink (src_name):
362 link_dest = os.readlink (src_name)
Greg Warde765a3b1999-04-04 02:54:20 +0000363 if verbose:
364 print "linking %s -> %s" % (dst_name, link_dest)
365 if not dry_run:
366 os.symlink (link_dest, dst_name)
Greg Ward884df451999-05-02 21:42:05 +0000367 outputs.append (dst_name)
368
Greg Ward2689e3d1999-03-22 14:52:19 +0000369 elif os.path.isdir (src_name):
Greg Warda002edc2000-01-30 19:57:48 +0000370 outputs.extend (
Greg Ward884df451999-05-02 21:42:05 +0000371 copy_tree (src_name, dst_name,
372 preserve_mode, preserve_times, preserve_symlinks,
Greg Warda002edc2000-01-30 19:57:48 +0000373 update, verbose, dry_run))
Greg Ward2689e3d1999-03-22 14:52:19 +0000374 else:
Greg Ward884df451999-05-02 21:42:05 +0000375 if (copy_file (src_name, dst_name,
376 preserve_mode, preserve_times,
377 update, verbose, dry_run)):
378 outputs.append (dst_name)
379
380 return outputs
Greg Ward2689e3d1999-03-22 14:52:19 +0000381
382# copy_tree ()
Greg Ward138ce651999-09-13 03:09:38 +0000383
384
Greg Wardb98fe362000-03-18 15:42:22 +0000385def remove_tree (directory, verbose=0, dry_run=0):
386 """Recursively remove an entire directory tree. Any errors are ignored
387 (apart from being reported to stdout if 'verbose' is true)."""
388
389 if verbose:
390 print "removing '%s' (and everything under it)" % directory
391 if dry_run:
392 return
393 try:
394 shutil.rmtree(directory,1)
395 except (IOError, OSError), exc:
396 if verbose:
397 if exc.filename:
398 print "error removing %s: %s (%s)" % \
399 (directory, exc.strerror, exc.filename)
400 else:
401 print "error removing %s: %s" % (directory, exc.strerror)
402
403
Greg Ward138ce651999-09-13 03:09:38 +0000404# XXX I suspect this is Unix-specific -- need porting help!
405def move_file (src, dst,
406 verbose=0,
407 dry_run=0):
408
409 """Move a file 'src' to 'dst'. If 'dst' is a directory, the file
410 will be moved into it with the same name; otherwise, 'src' is
411 just renamed to 'dst'. Return the new full name of the file.
412
413 Handles cross-device moves on Unix using
414 'copy_file()'. What about other systems???"""
415
416 from os.path import exists, isfile, isdir, basename, dirname
417
418 if verbose:
419 print "moving %s -> %s" % (src, dst)
420
421 if dry_run:
422 return dst
423
424 if not isfile (src):
425 raise DistutilsFileError, \
426 "can't move '%s': not a regular file" % src
427
428 if isdir (dst):
429 dst = os.path.join (dst, basename (src))
430 elif exists (dst):
431 raise DistutilsFileError, \
432 "can't move '%s': destination '%s' already exists" % \
433 (src, dst)
434
435 if not isdir (dirname (dst)):
436 raise DistutilsFileError, \
437 "can't move '%s': destination '%s' not a valid path" % \
438 (src, dst)
439
440 copy_it = 0
441 try:
442 os.rename (src, dst)
443 except os.error, (num, msg):
444 if num == errno.EXDEV:
445 copy_it = 1
446 else:
447 raise DistutilsFileError, \
448 "couldn't move '%s' to '%s': %s" % (src, dst, msg)
449
450 if copy_it:
451 copy_file (src, dst)
452 try:
453 os.unlink (src)
454 except os.error, (num, msg):
455 try:
456 os.unlink (dst)
457 except os.error:
458 pass
459 raise DistutilsFileError, \
460 ("couldn't move '%s' to '%s' by copy/delete: " +
461 "delete '%s' failed: %s") % \
462 (src, dst, src, msg)
463
464 return dst
465
466# move_file ()
Greg Wardac1424a1999-09-21 18:37:51 +0000467
468
469def write_file (filename, contents):
Greg Wardf3b997a1999-10-03 20:50:41 +0000470 """Create a file with the specified name and write 'contents' (a
Greg Wardac1424a1999-09-21 18:37:51 +0000471 sequence of strings without line terminators) to it."""
472
473 f = open (filename, "w")
474 for line in contents:
475 f.write (line + "\n")
476 f.close ()
Greg Ward585df892000-03-01 14:40:15 +0000477
478
479def get_platform ():
480 """Return a string (suitable for tacking onto directory names) that
481 identifies the current platform. Under Unix, identifies both the OS
482 and hardware architecture, e.g. "linux-i586", "solaris-sparc",
483 "irix-mips". For Windows and Mac OS, just returns 'sys.platform' --
484 i.e. "???" or "???"."""
485
486 if os.name == 'posix':
487 uname = os.uname()
488 OS = uname[0]
489 arch = uname[4]
490 return "%s-%s" % (string.lower (OS), string.lower (arch))
491 else:
492 return sys.platform
493
494# get_platform()
Greg Ward50919292000-03-07 03:27:08 +0000495
496
497def native_path (pathname):
498 """Return 'pathname' as a name that will work on the native
499 filesystem, i.e. split it on '/' and put it back together again
500 using the current directory separator. Needed because filenames in
501 the setup script are always supplied in Unix style, and have to be
502 converted to the local convention before we can actually use them in
503 the filesystem. Raises DistutilsValueError if 'pathname' is
504 absolute (starts with '/') or contains local directory separators
505 (unless the local separator is '/', of course)."""
506
507 if pathname[0] == '/':
508 raise DistutilsValueError, "path '%s' cannot be absolute" % pathname
509 if pathname[-1] == '/':
510 raise DistutilsValueError, "path '%s' cannot end with '/'" % pathname
Greg Ward1b4ede52000-03-22 00:22:44 +0000511 if os.sep != '/' and os.sep in pathname:
512 raise DistutilsValueError, \
513 "path '%s' cannot contain '%c' character" % \
514 (pathname, os.sep)
Greg Ward50919292000-03-07 03:27:08 +0000515
516 paths = string.split (pathname, '/')
517 return apply (os.path.join, paths)
518 else:
519 return pathname
520
521# native_path ()
Greg Ward1b4ede52000-03-22 00:22:44 +0000522
523
524def _check_environ ():
525 """Ensure that 'os.environ' has all the environment variables we
526 guarantee that users can use in config files, command-line
527 options, etc. Currently this includes:
528 HOME - user's home directory (Unix only)
529 PLAT - desription of the current platform, including hardware
530 and OS (see 'get_platform()')
531 """
532
533 if os.name == 'posix' and not os.environ.has_key('HOME'):
534 import pwd
535 os.environ['HOME'] = pwd.getpwuid (os.getuid())[5]
536
537 if not os.environ.has_key('PLAT'):
538 os.environ['PLAT'] = get_platform ()
539
540
541def subst_vars (str, local_vars):
542 """Perform shell/Perl-style variable substitution on 'string'.
543 Every occurence of '$' followed by a name, or a name enclosed in
544 braces, is considered a variable. Every variable is substituted by
545 the value found in the 'local_vars' dictionary, or in 'os.environ'
546 if it's not in 'local_vars'. 'os.environ' is first checked/
547 augmented to guarantee that it contains certain values: see
548 '_check_environ()'. Raise ValueError for any variables not found in
549 either 'local_vars' or 'os.environ'."""
550
551 _check_environ ()
552 def _subst (match, local_vars=local_vars):
553 var_name = match.group(1)
554 if local_vars.has_key (var_name):
555 return str (local_vars[var_name])
556 else:
557 return os.environ[var_name]
558
559 return re.sub (r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, str)
560
561# subst_vars ()
Greg Ward7c1a6d42000-03-29 02:48:40 +0000562
563
564def make_tarball (base_dir, compress="gzip", verbose=0, dry_run=0):
565 """Create a (possibly compressed) tar file from all the files under
566 'base_dir'. 'compress' must be "gzip" (the default), "compress", or
567 None. Both "tar" and the compression utility named by 'compress'
568 must be on the default program search path, so this is probably
569 Unix-specific. The output tar file will be named 'base_dir' +
570 ".tar", possibly plus the appropriate compression extension
571 (".gz" or ".Z"). Return the output filename."""
572
573 # XXX GNU tar 1.13 has a nifty option to add a prefix directory.
574 # It's pretty new, though, so we certainly can't require it --
575 # but it would be nice to take advantage of it to skip the
576 # "create a tree of hardlinks" step! (Would also be nice to
577 # detect GNU tar to use its 'z' option and save a step.)
578
579 compress_ext = { 'gzip': ".gz",
580 'compress': ".Z" }
581
582 if compress is not None and compress not in ('gzip', 'compress'):
583 raise ValueError, \
584 "bad value for 'compress': must be None, 'gzip', or 'compress'"
585
586 archive_name = base_dir + ".tar"
587 cmd = ["tar", "-cf", archive_name, base_dir]
588 spawn (cmd, verbose=verbose, dry_run=dry_run)
589
590 if compress:
591 spawn ([compress, archive_name], verbose=verbose, dry_run=dry_run)
592 return archive_name + compress_ext[compress]
593 else:
594 return archive_name
595
596# make_tarball ()
597
598
599def make_zipfile (base_dir, verbose=0, dry_run=0):
600 """Create a ZIP file from all the files under 'base_dir'. The
601 output ZIP file will be named 'base_dir' + ".zip". Uses either the
602 InfoZIP "zip" utility (if installed and found on the default search
603 path) or the "zipfile" Python module (if available). If neither
604 tool is available, raises DistutilsExecError. Returns the name
605 of the output ZIP file."""
606
607 # This initially assumed the Unix 'zip' utility -- but
608 # apparently InfoZIP's zip.exe works the same under Windows, so
609 # no changes needed!
610
611 zip_filename = base_dir + ".zip"
612 try:
613 spawn (["zip", "-r", zip_filename, base_dir],
614 verbose=verbose, dry_run=dry_run)
615 except DistutilsExecError:
616
617 # XXX really should distinguish between "couldn't find
618 # external 'zip' command" and "zip failed" -- shouldn't try
619 # again in the latter case. (I think fixing this will
620 # require some cooperation from the spawn module -- perhaps
621 # a utility function to search the path, so we can fallback
622 # on zipfile.py without the failed spawn.)
623 try:
624 import zipfile
625 except ImportError:
626 raise DistutilsExecError, \
627 ("unable to create zip file '%s': " +
628 "could neither find a standalone zip utility nor " +
629 "import the 'zipfile' module") % zip_filename
630
631 if verbose:
632 print "creating '%s' and adding '%s' to it" % \
633 (zip_filename, base_dir)
634
635 def visit (z, dirname, names):
636 for name in names:
637 path = os.path.join (dirname, name)
638 if os.path.isfile (path):
639 z.write (path, path)
640
641 if not dry_run:
642 z = zipfile.ZipFile (zip_filename, "wb",
643 compression=zipfile.ZIP_DEFLATED)
644
645 os.path.walk (base_dir, visit, z)
646 z.close()
647
648 return zip_filename
649
650# make_zipfile ()