blob: bb790af43815eca6387889f978b4c2b4d33eea25 [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
12__rcsid__ = "$Id$"
13
14import os
15from distutils.errors import *
16
17
18# I don't use os.makedirs because a) it's new to Python 1.5.2, and
19# b) it blows up if the directory already exists (I want to silently
20# succeed in that case).
Greg Warde765a3b1999-04-04 02:54:20 +000021def mkpath (name, mode=0777, verbose=0, dry_run=0):
Greg Ward2689e3d1999-03-22 14:52:19 +000022 """Create a directory and any missing ancestor directories. If the
23 directory already exists, return silently. Raise
24 DistutilsFileError if unable to create some directory along the
25 way (eg. some sub-path exists, but is a file rather than a
26 directory). If 'verbose' is true, print a one-line summary of
27 each mkdir to stdout."""
28
29 # XXX what's the better way to handle verbosity? print as we create
30 # each directory in the path (the current behaviour), or only announce
31 # the creation of the whole path, and force verbose=0 on all sub-calls?
32
33 if os.path.isdir (name):
34 return
35
36 (head, tail) = os.path.split (name)
37 tails = [tail] # stack of lone dirs to create
38
39 while head and tail and not os.path.isdir (head):
40 #print "splitting '%s': " % head,
41 (head, tail) = os.path.split (head)
42 #print "to ('%s','%s')" % (head, tail)
43 tails.insert (0, tail) # push next higher dir onto stack
44
45 #print "stack of tails:", tails
46
Greg Warde765a3b1999-04-04 02:54:20 +000047 # now 'head' contains the deepest directory that already exists
48 # (that is, the child of 'head' in 'name' is the highest directory
49 # that does *not* exist)
Greg Ward2689e3d1999-03-22 14:52:19 +000050 for d in tails:
51 #print "head = %s, d = %s: " % (head, d),
52 head = os.path.join (head, d)
53 if verbose:
54 print "creating", head
Greg Warde765a3b1999-04-04 02:54:20 +000055
56 if not dry_run:
57 try:
58 os.mkdir (head)
59 except os.error, (errno, errstr):
60 raise DistutilsFileError, "%s: %s" % (head, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +000061
62# mkpath ()
63
64
Greg Ward138ce651999-09-13 03:09:38 +000065def newer (source, target):
66 """Return true if 'source' exists and is more recently modified than
67 'target', or if 'source' exists and 'target' doesn't. Return
68 false if both exist and 'target' is the same age or younger than
69 'source'. Raise DistutilsFileError if 'source' does not
70 exist."""
Greg Ward2689e3d1999-03-22 14:52:19 +000071
Greg Ward138ce651999-09-13 03:09:38 +000072 if not os.path.exists (source):
73 raise DistutilsFileError, "file '%s' does not exist" % source
74 if not os.path.exists (target):
Greg Ward2689e3d1999-03-22 14:52:19 +000075 return 1
76
Greg Ward138ce651999-09-13 03:09:38 +000077 from stat import ST_MTIME
78 mtime1 = os.stat(source)[ST_MTIME]
79 mtime2 = os.stat(target)[ST_MTIME]
Greg Ward2689e3d1999-03-22 14:52:19 +000080
81 return mtime1 > mtime2
82
83# newer ()
84
85
Greg Ward138ce651999-09-13 03:09:38 +000086def newer_pairwise (sources, targets):
87
88 """Walk two filename lists in parallel, testing if each 'target' is
89 up-to-date relative to its corresponding 'source'. If so, both
90 are deleted from their respective lists. Return a list of tuples
91 containing the deleted (source,target) pairs."""
92
93 if len (sources) != len (targets):
94 raise ValueError, "'sources' and 'targets' must be same length"
95
96 goners = []
97 for i in range (len (sources)-1, -1, -1):
98 if not newer (sources[i], targets[i]):
99 goners.append ((sources[i], targets[i]))
100 del sources[i]
101 del targets[i]
102 goners.reverse()
103 return goners
104
105# newer_pairwise ()
106
107
108def newer_group (sources, target):
109 """Return true if 'target' is out-of-date with respect to any
110 file listed in 'sources'. In other words, if 'target' exists and
111 is newer than every file in 'sources', return false; otherwise
112 return true."""
113
114 # If the target doesn't even exist, then it's definitely out-of-date.
115 if not os.path.exists (target):
116 return 1
117
118 # Otherwise we have to find out the hard way: if *any* source file
119 # is more recent than 'target', then 'target' is out-of-date and
120 # we can immediately return true. If we fall through to the end
121 # of the loop, then 'target' is up-to-date and we return false.
122 from stat import ST_MTIME
123 target_mtime = os.stat (target)[ST_MTIME]
124 for source in sources:
125 source_mtime = os.stat(source)[ST_MTIME]
126 if source_mtime > target_mtime:
127 return 1
128 else:
129 return 0
130
131# newer_group ()
132
133
Greg Ward2689e3d1999-03-22 14:52:19 +0000134def make_file (src, dst, func, args,
135 verbose=0, update_message=None, noupdate_message=None):
136 """Makes 'dst' from 'src' (both filenames) by calling 'func' with
137 'args', but only if it needs to: i.e. if 'dst' does not exist or
138 'src' is newer than 'dst'."""
139
140 if newer (src, dst):
141 if verbose and update_message:
142 print update_message
143 apply (func, args)
144 else:
145 if verbose and noupdate_message:
146 print noupdate_message
147
148# make_file ()
149
150
151def _copy_file_contents (src, dst, buffer_size=16*1024):
152 """Copy the file 'src' to 'dst'; both must be filenames. Any error
153 opening either file, reading from 'src', or writing to 'dst',
154 raises DistutilsFileError. Data is read/written in chunks of
155 'buffer_size' bytes (default 16k). No attempt is made to handle
156 anything apart from regular files."""
157
158 # Stolen from shutil module in the standard library, but with
159 # custom error-handling added.
160
161 fsrc = None
162 fdst = None
163 try:
164 try:
165 fsrc = open(src, 'rb')
166 except os.error, (errno, errstr):
167 raise DistutilsFileError, "could not open %s: %s" % (src, errstr)
168
169 try:
170 fdst = open(dst, 'wb')
171 except os.error, (errno, errstr):
172 raise DistutilsFileError, "could not create %s: %s" % (dst, errstr)
173
174 while 1:
175 try:
176 buf = fsrc.read (buffer_size)
177 except os.error, (errno, errstr):
178 raise DistutilsFileError, \
179 "could not read from %s: %s" % (src, errstr)
180
181 if not buf:
182 break
183
184 try:
185 fdst.write(buf)
186 except os.error, (errno, errstr):
187 raise DistutilsFileError, \
188 "could not write to %s: %s" % (dst, errstr)
189
190 finally:
191 if fdst:
192 fdst.close()
193 if fsrc:
194 fsrc.close()
195
196# _copy_file_contents()
197
198
199def copy_file (src, dst,
200 preserve_mode=1,
201 preserve_times=1,
202 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000203 verbose=0,
204 dry_run=0):
Greg Ward2689e3d1999-03-22 14:52:19 +0000205
206 """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src'
207 is copied there with the same name; otherwise, it must be a
208 filename. (If the file exists, it will be ruthlessly clobbered.)
209 If 'preserve_mode' is true (the default), the file's mode (type
210 and permission bits, or whatever is analogous on the current
211 platform) is copied. If 'preserve_times' is true (the default),
212 the last-modified and last-access times are copied as well. If
213 'update' is true, 'src' will only be copied if 'dst' does not
214 exist, or if 'dst' does exist but is older than 'src'. If
215 'verbose' is true, then a one-line summary of the copy will be
Greg Ward884df451999-05-02 21:42:05 +0000216 printed to stdout.
217
218 Return true if the file was copied (or would have been copied),
219 false otherwise (ie. 'update' was true and the destination is
220 up-to-date)."""
Greg Ward2689e3d1999-03-22 14:52:19 +0000221
222 # XXX doesn't copy Mac-specific metadata
223
Greg Ward2689e3d1999-03-22 14:52:19 +0000224 from stat import *
225
226 if not os.path.isfile (src):
227 raise DistutilsFileError, \
Greg Ward138ce651999-09-13 03:09:38 +0000228 "can't copy %s: not a regular file" % src
Greg Ward2689e3d1999-03-22 14:52:19 +0000229
230 if os.path.isdir (dst):
231 dir = dst
232 dst = os.path.join (dst, os.path.basename (src))
233 else:
234 dir = os.path.dirname (dst)
235
236 if update and not newer (src, dst):
Greg Ward884df451999-05-02 21:42:05 +0000237 if verbose:
238 print "not copying %s (output up-to-date)" % src
239 return 0
Greg Ward2689e3d1999-03-22 14:52:19 +0000240
241 if verbose:
242 print "copying %s -> %s" % (src, dir)
243
Greg Warde765a3b1999-04-04 02:54:20 +0000244 if dry_run:
Greg Ward884df451999-05-02 21:42:05 +0000245 return 1
Greg Warde765a3b1999-04-04 02:54:20 +0000246
247 _copy_file_contents (src, dst)
Greg Ward2689e3d1999-03-22 14:52:19 +0000248 if preserve_mode or preserve_times:
249 st = os.stat (src)
Greg Ward5116f901999-06-08 17:05:21 +0000250
251 # According to David Ascher <da@ski.org>, utime() should be done
252 # before chmod() (at least under NT).
Greg Ward2689e3d1999-03-22 14:52:19 +0000253 if preserve_times:
254 os.utime (dst, (st[ST_ATIME], st[ST_MTIME]))
Greg Ward5116f901999-06-08 17:05:21 +0000255 if preserve_mode:
256 os.chmod (dst, S_IMODE (st[ST_MODE]))
Greg Ward2689e3d1999-03-22 14:52:19 +0000257
Greg Ward884df451999-05-02 21:42:05 +0000258 return 1
259
Greg Ward2689e3d1999-03-22 14:52:19 +0000260# copy_file ()
261
262
263def copy_tree (src, dst,
264 preserve_mode=1,
265 preserve_times=1,
266 preserve_symlinks=0,
267 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000268 verbose=0,
269 dry_run=0):
270
Greg Ward2689e3d1999-03-22 14:52:19 +0000271
272 """Copy an entire directory tree 'src' to a new location 'dst'. Both
273 'src' and 'dst' must be directory names. If 'src' is not a
274 directory, raise DistutilsFileError. If 'dst' does not exist, it
Greg Ward884df451999-05-02 21:42:05 +0000275 is created with 'mkpath'. The end result of the copy is that
Greg Ward2689e3d1999-03-22 14:52:19 +0000276 every file in 'src' is copied to 'dst', and directories under
Greg Ward884df451999-05-02 21:42:05 +0000277 'src' are recursively copied to 'dst'. Return the list of files
278 copied (under their output names) -- note that if 'update' is true,
279 this might be less than the list of files considered. Return
280 value is not affected by 'dry_run'.
Greg Ward2689e3d1999-03-22 14:52:19 +0000281
282 'preserve_mode' and 'preserve_times' are the same as for
283 'copy_file'; note that they only apply to regular files, not to
284 directories. If 'preserve_symlinks' is true, symlinks will be
285 copied as symlinks (on platforms that support them!); otherwise
286 (the default), the destination of the symlink will be copied.
287 'update' and 'verbose' are the same as for 'copy_file'."""
288
Greg Ward138ce651999-09-13 03:09:38 +0000289 if not dry_run and not os.path.isdir (src):
Greg Ward2689e3d1999-03-22 14:52:19 +0000290 raise DistutilsFileError, \
291 "cannot copy tree %s: not a directory" % src
292 try:
293 names = os.listdir (src)
294 except os.error, (errno, errstr):
Greg Ward138ce651999-09-13 03:09:38 +0000295 if dry_run:
296 names = []
297 else:
298 raise DistutilsFileError, \
299 "error listing files in %s: %s" % (src, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000300
Greg Warde765a3b1999-04-04 02:54:20 +0000301 if not dry_run:
302 mkpath (dst, verbose=verbose)
Greg Ward2689e3d1999-03-22 14:52:19 +0000303
Greg Ward884df451999-05-02 21:42:05 +0000304 outputs = []
305
Greg Ward2689e3d1999-03-22 14:52:19 +0000306 for n in names:
307 src_name = os.path.join (src, n)
308 dst_name = os.path.join (dst, n)
309
310 if preserve_symlinks and os.path.islink (src_name):
311 link_dest = os.readlink (src_name)
Greg Warde765a3b1999-04-04 02:54:20 +0000312 if verbose:
313 print "linking %s -> %s" % (dst_name, link_dest)
314 if not dry_run:
315 os.symlink (link_dest, dst_name)
Greg Ward884df451999-05-02 21:42:05 +0000316 outputs.append (dst_name)
317
Greg Ward2689e3d1999-03-22 14:52:19 +0000318 elif os.path.isdir (src_name):
Greg Ward884df451999-05-02 21:42:05 +0000319 outputs[-1:] = \
320 copy_tree (src_name, dst_name,
321 preserve_mode, preserve_times, preserve_symlinks,
322 update, verbose, dry_run)
Greg Ward2689e3d1999-03-22 14:52:19 +0000323 else:
Greg Ward884df451999-05-02 21:42:05 +0000324 if (copy_file (src_name, dst_name,
325 preserve_mode, preserve_times,
326 update, verbose, dry_run)):
327 outputs.append (dst_name)
328
329 return outputs
Greg Ward2689e3d1999-03-22 14:52:19 +0000330
331# copy_tree ()
Greg Ward138ce651999-09-13 03:09:38 +0000332
333
334# XXX I suspect this is Unix-specific -- need porting help!
335def move_file (src, dst,
336 verbose=0,
337 dry_run=0):
338
339 """Move a file 'src' to 'dst'. If 'dst' is a directory, the file
340 will be moved into it with the same name; otherwise, 'src' is
341 just renamed to 'dst'. Return the new full name of the file.
342
343 Handles cross-device moves on Unix using
344 'copy_file()'. What about other systems???"""
345
346 from os.path import exists, isfile, isdir, basename, dirname
347
348 if verbose:
349 print "moving %s -> %s" % (src, dst)
350
351 if dry_run:
352 return dst
353
354 if not isfile (src):
355 raise DistutilsFileError, \
356 "can't move '%s': not a regular file" % src
357
358 if isdir (dst):
359 dst = os.path.join (dst, basename (src))
360 elif exists (dst):
361 raise DistutilsFileError, \
362 "can't move '%s': destination '%s' already exists" % \
363 (src, dst)
364
365 if not isdir (dirname (dst)):
366 raise DistutilsFileError, \
367 "can't move '%s': destination '%s' not a valid path" % \
368 (src, dst)
369
370 copy_it = 0
371 try:
372 os.rename (src, dst)
373 except os.error, (num, msg):
374 if num == errno.EXDEV:
375 copy_it = 1
376 else:
377 raise DistutilsFileError, \
378 "couldn't move '%s' to '%s': %s" % (src, dst, msg)
379
380 if copy_it:
381 copy_file (src, dst)
382 try:
383 os.unlink (src)
384 except os.error, (num, msg):
385 try:
386 os.unlink (dst)
387 except os.error:
388 pass
389 raise DistutilsFileError, \
390 ("couldn't move '%s' to '%s' by copy/delete: " +
391 "delete '%s' failed: %s") % \
392 (src, dst, src, msg)
393
394 return dst
395
396# move_file ()