blob: 2f193fb43c5654148d3fafeff849fabe4cda84e0 [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 Ward585df892000-03-01 14:40:15 +000014import os, string
Greg Ward2689e3d1999-03-22 14:52:19 +000015from distutils.errors import *
16
17
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)
41
Greg Ward96182d72000-03-03 03:00:02 +000042 if os.path.isdir (name) or name == '':
Greg Ward2689e3d1999-03-22 14:52:19 +000043 return
Greg Wardac1424a1999-09-21 18:37:51 +000044 if PATH_CREATED.get (name):
45 return
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)
73 except os.error, (errno, errstr):
Greg Ward96182d72000-03-03 03:00:02 +000074 raise DistutilsFileError, "'%s': %s" % (head, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +000075
Greg Wardac1424a1999-09-21 18:37:51 +000076 PATH_CREATED[head] = 1
77
Greg Ward2689e3d1999-03-22 14:52:19 +000078# mkpath ()
79
80
Greg Ward138ce651999-09-13 03:09:38 +000081def newer (source, target):
82 """Return true if 'source' exists and is more recently modified than
83 'target', or if 'source' exists and 'target' doesn't. Return
84 false if both exist and 'target' is the same age or younger than
85 'source'. Raise DistutilsFileError if 'source' does not
86 exist."""
Greg Ward2689e3d1999-03-22 14:52:19 +000087
Greg Ward138ce651999-09-13 03:09:38 +000088 if not os.path.exists (source):
89 raise DistutilsFileError, "file '%s' does not exist" % source
90 if not os.path.exists (target):
Greg Ward2689e3d1999-03-22 14:52:19 +000091 return 1
92
Greg Ward138ce651999-09-13 03:09:38 +000093 from stat import ST_MTIME
94 mtime1 = os.stat(source)[ST_MTIME]
95 mtime2 = os.stat(target)[ST_MTIME]
Greg Ward2689e3d1999-03-22 14:52:19 +000096
97 return mtime1 > mtime2
98
99# newer ()
100
101
Greg Ward138ce651999-09-13 03:09:38 +0000102def newer_pairwise (sources, targets):
Greg Ward95526652000-03-06 03:44:32 +0000103 """Walk two filename lists in parallel, testing if each source is newer
104 than its corresponding target. Return a pair of lists (sources,
105 targets) where source is newer than target, according to the
106 semantics of 'newer()'."""
Greg Ward138ce651999-09-13 03:09:38 +0000107
108 if len (sources) != len (targets):
109 raise ValueError, "'sources' and 'targets' must be same length"
110
Greg Ward95526652000-03-06 03:44:32 +0000111 # build a pair of lists (sources, targets) where source is newer
112 n_sources = []
113 n_targets = []
114 for i in range (len (sources)):
115 if newer (sources[i], targets[i]):
116 n_sources.append (sources[i])
117 n_targets.append (targets[i])
118
119 return (n_sources, n_targets)
Greg Ward138ce651999-09-13 03:09:38 +0000120
121# newer_pairwise ()
122
123
Greg Ward7b7679e2000-01-09 22:48:59 +0000124def newer_group (sources, target, missing='error'):
Greg Ward138ce651999-09-13 03:09:38 +0000125 """Return true if 'target' is out-of-date with respect to any
126 file listed in 'sources'. In other words, if 'target' exists and
127 is newer than every file in 'sources', return false; otherwise
Greg Ward7b7679e2000-01-09 22:48:59 +0000128 return true. 'missing' controls what we do when a source file is
129 missing; the default ("error") is to blow up with an OSError from
130 inside 'stat()'; if it is "ignore", we silently drop any missing
131 source files; if it is "newer", any missing source files make us
132 assume that 'target' is out-of-date (this is handy in "dry-run"
133 mode: it'll make you pretend to carry out commands that wouldn't
134 work because inputs are missing, but that doesn't matter because
135 you're not actually going to run the commands)."""
Greg Ward138ce651999-09-13 03:09:38 +0000136
137 # If the target doesn't even exist, then it's definitely out-of-date.
138 if not os.path.exists (target):
139 return 1
140
141 # Otherwise we have to find out the hard way: if *any* source file
142 # is more recent than 'target', then 'target' is out-of-date and
143 # we can immediately return true. If we fall through to the end
144 # of the loop, then 'target' is up-to-date and we return false.
145 from stat import ST_MTIME
146 target_mtime = os.stat (target)[ST_MTIME]
147 for source in sources:
Greg Ward7b7679e2000-01-09 22:48:59 +0000148 if not os.path.exists (source):
149 if missing == 'error': # blow up when we stat() the file
150 pass
151 elif missing == 'ignore': # missing source dropped from
152 continue # target's dependency list
153 elif missing == 'newer': # missing source means target is
154 return 1 # out-of-date
155
Greg Ward138ce651999-09-13 03:09:38 +0000156 source_mtime = os.stat(source)[ST_MTIME]
157 if source_mtime > target_mtime:
158 return 1
159 else:
160 return 0
161
162# newer_group ()
163
164
Greg Wardf3b997a1999-10-03 20:50:41 +0000165# XXX this isn't used anywhere, and worse, it has the same name as a method
166# in Command with subtly different semantics. (This one just has one
167# source -> one dest; that one has many sources -> one dest.) Nuke it?
Greg Ward2689e3d1999-03-22 14:52:19 +0000168def make_file (src, dst, func, args,
169 verbose=0, update_message=None, noupdate_message=None):
170 """Makes 'dst' from 'src' (both filenames) by calling 'func' with
171 'args', but only if it needs to: i.e. if 'dst' does not exist or
172 'src' is newer than 'dst'."""
173
174 if newer (src, dst):
175 if verbose and update_message:
176 print update_message
177 apply (func, args)
178 else:
179 if verbose and noupdate_message:
180 print noupdate_message
181
182# make_file ()
183
184
185def _copy_file_contents (src, dst, buffer_size=16*1024):
186 """Copy the file 'src' to 'dst'; both must be filenames. Any error
187 opening either file, reading from 'src', or writing to 'dst',
188 raises DistutilsFileError. Data is read/written in chunks of
189 'buffer_size' bytes (default 16k). No attempt is made to handle
190 anything apart from regular files."""
191
192 # Stolen from shutil module in the standard library, but with
193 # custom error-handling added.
194
195 fsrc = None
196 fdst = None
197 try:
198 try:
199 fsrc = open(src, 'rb')
200 except os.error, (errno, errstr):
Greg Ward96182d72000-03-03 03:00:02 +0000201 raise DistutilsFileError, \
202 "could not open '%s': %s" % (src, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000203
204 try:
205 fdst = open(dst, 'wb')
206 except os.error, (errno, errstr):
Greg Ward96182d72000-03-03 03:00:02 +0000207 raise DistutilsFileError, \
208 "could not create '%s': %s" % (dst, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000209
210 while 1:
211 try:
212 buf = fsrc.read (buffer_size)
213 except os.error, (errno, errstr):
214 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000215 "could not read from '%s': %s" % (src, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000216
217 if not buf:
218 break
219
220 try:
221 fdst.write(buf)
222 except os.error, (errno, errstr):
223 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000224 "could not write to '%s': %s" % (dst, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000225
226 finally:
227 if fdst:
228 fdst.close()
229 if fsrc:
230 fsrc.close()
231
232# _copy_file_contents()
233
234
235def copy_file (src, dst,
236 preserve_mode=1,
237 preserve_times=1,
238 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000239 verbose=0,
240 dry_run=0):
Greg Ward2689e3d1999-03-22 14:52:19 +0000241
242 """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src'
243 is copied there with the same name; otherwise, it must be a
244 filename. (If the file exists, it will be ruthlessly clobbered.)
245 If 'preserve_mode' is true (the default), the file's mode (type
246 and permission bits, or whatever is analogous on the current
247 platform) is copied. If 'preserve_times' is true (the default),
248 the last-modified and last-access times are copied as well. If
249 'update' is true, 'src' will only be copied if 'dst' does not
250 exist, or if 'dst' does exist but is older than 'src'. If
251 'verbose' is true, then a one-line summary of the copy will be
Greg Ward884df451999-05-02 21:42:05 +0000252 printed to stdout.
253
254 Return true if the file was copied (or would have been copied),
255 false otherwise (ie. 'update' was true and the destination is
256 up-to-date)."""
Greg Ward2689e3d1999-03-22 14:52:19 +0000257
258 # XXX doesn't copy Mac-specific metadata
259
Greg Ward2689e3d1999-03-22 14:52:19 +0000260 from stat import *
261
262 if not os.path.isfile (src):
263 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000264 "can't copy '%s': not a regular file" % src
Greg Ward2689e3d1999-03-22 14:52:19 +0000265
266 if os.path.isdir (dst):
267 dir = dst
268 dst = os.path.join (dst, os.path.basename (src))
269 else:
270 dir = os.path.dirname (dst)
271
272 if update and not newer (src, dst):
Greg Ward884df451999-05-02 21:42:05 +0000273 if verbose:
274 print "not copying %s (output up-to-date)" % src
275 return 0
Greg Ward2689e3d1999-03-22 14:52:19 +0000276
277 if verbose:
278 print "copying %s -> %s" % (src, dir)
279
Greg Warde765a3b1999-04-04 02:54:20 +0000280 if dry_run:
Greg Ward884df451999-05-02 21:42:05 +0000281 return 1
Greg Warde765a3b1999-04-04 02:54:20 +0000282
Greg Ward911d8662000-03-07 03:34:16 +0000283 # On a Mac, use the native file copy routine
284 if os.name == 'mac':
285 import macostools
286 try:
287 macostools.copy (src, dst, 0, preserve_times)
288 except OSError, exc:
289 raise DistutilsFileError, \
290 "could not copy '%s' to '%s': %s" % (src, dst, exc[-1])
291 return 1
292
293 # Otherwise use custom routine
Greg Warde765a3b1999-04-04 02:54:20 +0000294 _copy_file_contents (src, dst)
Greg Ward2689e3d1999-03-22 14:52:19 +0000295 if preserve_mode or preserve_times:
296 st = os.stat (src)
Greg Ward5116f901999-06-08 17:05:21 +0000297
298 # According to David Ascher <da@ski.org>, utime() should be done
299 # before chmod() (at least under NT).
Greg Ward2689e3d1999-03-22 14:52:19 +0000300 if preserve_times:
301 os.utime (dst, (st[ST_ATIME], st[ST_MTIME]))
Greg Ward5116f901999-06-08 17:05:21 +0000302 if preserve_mode:
303 os.chmod (dst, S_IMODE (st[ST_MODE]))
Greg Ward2689e3d1999-03-22 14:52:19 +0000304
Greg Ward884df451999-05-02 21:42:05 +0000305 return 1
306
Greg Ward2689e3d1999-03-22 14:52:19 +0000307# copy_file ()
308
309
310def copy_tree (src, dst,
311 preserve_mode=1,
312 preserve_times=1,
313 preserve_symlinks=0,
314 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000315 verbose=0,
316 dry_run=0):
317
Greg Ward2689e3d1999-03-22 14:52:19 +0000318
319 """Copy an entire directory tree 'src' to a new location 'dst'. Both
320 'src' and 'dst' must be directory names. If 'src' is not a
321 directory, raise DistutilsFileError. If 'dst' does not exist, it
Greg Wardf3b997a1999-10-03 20:50:41 +0000322 is created with 'mkpath()'. The end result of the copy is that
Greg Ward2689e3d1999-03-22 14:52:19 +0000323 every file in 'src' is copied to 'dst', and directories under
Greg Ward884df451999-05-02 21:42:05 +0000324 'src' are recursively copied to 'dst'. Return the list of files
325 copied (under their output names) -- note that if 'update' is true,
326 this might be less than the list of files considered. Return
327 value is not affected by 'dry_run'.
Greg Ward2689e3d1999-03-22 14:52:19 +0000328
329 'preserve_mode' and 'preserve_times' are the same as for
330 'copy_file'; note that they only apply to regular files, not to
331 directories. If 'preserve_symlinks' is true, symlinks will be
332 copied as symlinks (on platforms that support them!); otherwise
333 (the default), the destination of the symlink will be copied.
334 'update' and 'verbose' are the same as for 'copy_file'."""
335
Greg Ward138ce651999-09-13 03:09:38 +0000336 if not dry_run and not os.path.isdir (src):
Greg Ward2689e3d1999-03-22 14:52:19 +0000337 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000338 "cannot copy tree '%s': not a directory" % src
Greg Ward2689e3d1999-03-22 14:52:19 +0000339 try:
340 names = os.listdir (src)
341 except os.error, (errno, errstr):
Greg Ward138ce651999-09-13 03:09:38 +0000342 if dry_run:
343 names = []
344 else:
345 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000346 "error listing files in '%s': %s" % (src, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000347
Greg Warde765a3b1999-04-04 02:54:20 +0000348 if not dry_run:
349 mkpath (dst, verbose=verbose)
Greg Ward2689e3d1999-03-22 14:52:19 +0000350
Greg Ward884df451999-05-02 21:42:05 +0000351 outputs = []
352
Greg Ward2689e3d1999-03-22 14:52:19 +0000353 for n in names:
354 src_name = os.path.join (src, n)
355 dst_name = os.path.join (dst, n)
356
357 if preserve_symlinks and os.path.islink (src_name):
358 link_dest = os.readlink (src_name)
Greg Warde765a3b1999-04-04 02:54:20 +0000359 if verbose:
360 print "linking %s -> %s" % (dst_name, link_dest)
361 if not dry_run:
362 os.symlink (link_dest, dst_name)
Greg Ward884df451999-05-02 21:42:05 +0000363 outputs.append (dst_name)
364
Greg Ward2689e3d1999-03-22 14:52:19 +0000365 elif os.path.isdir (src_name):
Greg Warda002edc2000-01-30 19:57:48 +0000366 outputs.extend (
Greg Ward884df451999-05-02 21:42:05 +0000367 copy_tree (src_name, dst_name,
368 preserve_mode, preserve_times, preserve_symlinks,
Greg Warda002edc2000-01-30 19:57:48 +0000369 update, verbose, dry_run))
Greg Ward2689e3d1999-03-22 14:52:19 +0000370 else:
Greg Ward884df451999-05-02 21:42:05 +0000371 if (copy_file (src_name, dst_name,
372 preserve_mode, preserve_times,
373 update, verbose, dry_run)):
374 outputs.append (dst_name)
375
376 return outputs
Greg Ward2689e3d1999-03-22 14:52:19 +0000377
378# copy_tree ()
Greg Ward138ce651999-09-13 03:09:38 +0000379
380
381# XXX I suspect this is Unix-specific -- need porting help!
382def move_file (src, dst,
383 verbose=0,
384 dry_run=0):
385
386 """Move a file 'src' to 'dst'. If 'dst' is a directory, the file
387 will be moved into it with the same name; otherwise, 'src' is
388 just renamed to 'dst'. Return the new full name of the file.
389
390 Handles cross-device moves on Unix using
391 'copy_file()'. What about other systems???"""
392
393 from os.path import exists, isfile, isdir, basename, dirname
394
395 if verbose:
396 print "moving %s -> %s" % (src, dst)
397
398 if dry_run:
399 return dst
400
401 if not isfile (src):
402 raise DistutilsFileError, \
403 "can't move '%s': not a regular file" % src
404
405 if isdir (dst):
406 dst = os.path.join (dst, basename (src))
407 elif exists (dst):
408 raise DistutilsFileError, \
409 "can't move '%s': destination '%s' already exists" % \
410 (src, dst)
411
412 if not isdir (dirname (dst)):
413 raise DistutilsFileError, \
414 "can't move '%s': destination '%s' not a valid path" % \
415 (src, dst)
416
417 copy_it = 0
418 try:
419 os.rename (src, dst)
420 except os.error, (num, msg):
421 if num == errno.EXDEV:
422 copy_it = 1
423 else:
424 raise DistutilsFileError, \
425 "couldn't move '%s' to '%s': %s" % (src, dst, msg)
426
427 if copy_it:
428 copy_file (src, dst)
429 try:
430 os.unlink (src)
431 except os.error, (num, msg):
432 try:
433 os.unlink (dst)
434 except os.error:
435 pass
436 raise DistutilsFileError, \
437 ("couldn't move '%s' to '%s' by copy/delete: " +
438 "delete '%s' failed: %s") % \
439 (src, dst, src, msg)
440
441 return dst
442
443# move_file ()
Greg Wardac1424a1999-09-21 18:37:51 +0000444
445
446def write_file (filename, contents):
Greg Wardf3b997a1999-10-03 20:50:41 +0000447 """Create a file with the specified name and write 'contents' (a
Greg Wardac1424a1999-09-21 18:37:51 +0000448 sequence of strings without line terminators) to it."""
449
450 f = open (filename, "w")
451 for line in contents:
452 f.write (line + "\n")
453 f.close ()
Greg Ward585df892000-03-01 14:40:15 +0000454
455
456def get_platform ():
457 """Return a string (suitable for tacking onto directory names) that
458 identifies the current platform. Under Unix, identifies both the OS
459 and hardware architecture, e.g. "linux-i586", "solaris-sparc",
460 "irix-mips". For Windows and Mac OS, just returns 'sys.platform' --
461 i.e. "???" or "???"."""
462
463 if os.name == 'posix':
464 uname = os.uname()
465 OS = uname[0]
466 arch = uname[4]
467 return "%s-%s" % (string.lower (OS), string.lower (arch))
468 else:
469 return sys.platform
470
471# get_platform()
Greg Ward50919292000-03-07 03:27:08 +0000472
473
474def native_path (pathname):
475 """Return 'pathname' as a name that will work on the native
476 filesystem, i.e. split it on '/' and put it back together again
477 using the current directory separator. Needed because filenames in
478 the setup script are always supplied in Unix style, and have to be
479 converted to the local convention before we can actually use them in
480 the filesystem. Raises DistutilsValueError if 'pathname' is
481 absolute (starts with '/') or contains local directory separators
482 (unless the local separator is '/', of course)."""
483
484 if pathname[0] == '/':
485 raise DistutilsValueError, "path '%s' cannot be absolute" % pathname
486 if pathname[-1] == '/':
487 raise DistutilsValueError, "path '%s' cannot end with '/'" % pathname
488 if os.sep != '/':
489 if os.sep in pathname:
490 raise DistutilsValueError, \
491 "path '%s' cannot contain '%c' character" % \
492 (pathname, os.sep)
493
494 paths = string.split (pathname, '/')
495 return apply (os.path.join, paths)
496 else:
497 return pathname
498
499# native_path ()