blob: 85b04e7a809ec3a6bcfe530c4a86c3f707a1b91f [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
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
40 if os.path.isdir (name):
41 return
Greg Wardac1424a1999-09-21 18:37:51 +000042 if PATH_CREATED.get (name):
43 return
Greg Ward2689e3d1999-03-22 14:52:19 +000044
45 (head, tail) = os.path.split (name)
46 tails = [tail] # stack of lone dirs to create
47
48 while head and tail and not os.path.isdir (head):
49 #print "splitting '%s': " % head,
50 (head, tail) = os.path.split (head)
51 #print "to ('%s','%s')" % (head, tail)
52 tails.insert (0, tail) # push next higher dir onto stack
53
54 #print "stack of tails:", tails
55
Greg Warde765a3b1999-04-04 02:54:20 +000056 # now 'head' contains the deepest directory that already exists
57 # (that is, the child of 'head' in 'name' is the highest directory
58 # that does *not* exist)
Greg Ward2689e3d1999-03-22 14:52:19 +000059 for d in tails:
60 #print "head = %s, d = %s: " % (head, d),
61 head = os.path.join (head, d)
62 if verbose:
63 print "creating", head
Greg Warde765a3b1999-04-04 02:54:20 +000064
65 if not dry_run:
66 try:
67 os.mkdir (head)
68 except os.error, (errno, errstr):
69 raise DistutilsFileError, "%s: %s" % (head, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +000070
Greg Wardac1424a1999-09-21 18:37:51 +000071 PATH_CREATED[head] = 1
72
Greg Ward2689e3d1999-03-22 14:52:19 +000073# mkpath ()
74
75
Greg Ward138ce651999-09-13 03:09:38 +000076def newer (source, target):
77 """Return true if 'source' exists and is more recently modified than
78 'target', or if 'source' exists and 'target' doesn't. Return
79 false if both exist and 'target' is the same age or younger than
80 'source'. Raise DistutilsFileError if 'source' does not
81 exist."""
Greg Ward2689e3d1999-03-22 14:52:19 +000082
Greg Ward138ce651999-09-13 03:09:38 +000083 if not os.path.exists (source):
84 raise DistutilsFileError, "file '%s' does not exist" % source
85 if not os.path.exists (target):
Greg Ward2689e3d1999-03-22 14:52:19 +000086 return 1
87
Greg Ward138ce651999-09-13 03:09:38 +000088 from stat import ST_MTIME
89 mtime1 = os.stat(source)[ST_MTIME]
90 mtime2 = os.stat(target)[ST_MTIME]
Greg Ward2689e3d1999-03-22 14:52:19 +000091
92 return mtime1 > mtime2
93
94# newer ()
95
96
Greg Ward138ce651999-09-13 03:09:38 +000097def newer_pairwise (sources, targets):
98
99 """Walk two filename lists in parallel, testing if each 'target' is
100 up-to-date relative to its corresponding 'source'. If so, both
101 are deleted from their respective lists. Return a list of tuples
102 containing the deleted (source,target) pairs."""
103
104 if len (sources) != len (targets):
105 raise ValueError, "'sources' and 'targets' must be same length"
106
107 goners = []
108 for i in range (len (sources)-1, -1, -1):
109 if not newer (sources[i], targets[i]):
110 goners.append ((sources[i], targets[i]))
111 del sources[i]
112 del targets[i]
113 goners.reverse()
114 return goners
115
116# newer_pairwise ()
117
118
119def newer_group (sources, target):
120 """Return true if 'target' is out-of-date with respect to any
121 file listed in 'sources'. In other words, if 'target' exists and
122 is newer than every file in 'sources', return false; otherwise
123 return true."""
124
125 # If the target doesn't even exist, then it's definitely out-of-date.
126 if not os.path.exists (target):
127 return 1
128
129 # Otherwise we have to find out the hard way: if *any* source file
130 # is more recent than 'target', then 'target' is out-of-date and
131 # we can immediately return true. If we fall through to the end
132 # of the loop, then 'target' is up-to-date and we return false.
133 from stat import ST_MTIME
134 target_mtime = os.stat (target)[ST_MTIME]
135 for source in sources:
136 source_mtime = os.stat(source)[ST_MTIME]
137 if source_mtime > target_mtime:
138 return 1
139 else:
140 return 0
141
142# newer_group ()
143
144
Greg Ward2689e3d1999-03-22 14:52:19 +0000145def make_file (src, dst, func, args,
146 verbose=0, update_message=None, noupdate_message=None):
147 """Makes 'dst' from 'src' (both filenames) by calling 'func' with
148 'args', but only if it needs to: i.e. if 'dst' does not exist or
149 'src' is newer than 'dst'."""
150
151 if newer (src, dst):
152 if verbose and update_message:
153 print update_message
154 apply (func, args)
155 else:
156 if verbose and noupdate_message:
157 print noupdate_message
158
159# make_file ()
160
161
162def _copy_file_contents (src, dst, buffer_size=16*1024):
163 """Copy the file 'src' to 'dst'; both must be filenames. Any error
164 opening either file, reading from 'src', or writing to 'dst',
165 raises DistutilsFileError. Data is read/written in chunks of
166 'buffer_size' bytes (default 16k). No attempt is made to handle
167 anything apart from regular files."""
168
169 # Stolen from shutil module in the standard library, but with
170 # custom error-handling added.
171
172 fsrc = None
173 fdst = None
174 try:
175 try:
176 fsrc = open(src, 'rb')
177 except os.error, (errno, errstr):
178 raise DistutilsFileError, "could not open %s: %s" % (src, errstr)
179
180 try:
181 fdst = open(dst, 'wb')
182 except os.error, (errno, errstr):
183 raise DistutilsFileError, "could not create %s: %s" % (dst, errstr)
184
185 while 1:
186 try:
187 buf = fsrc.read (buffer_size)
188 except os.error, (errno, errstr):
189 raise DistutilsFileError, \
190 "could not read from %s: %s" % (src, errstr)
191
192 if not buf:
193 break
194
195 try:
196 fdst.write(buf)
197 except os.error, (errno, errstr):
198 raise DistutilsFileError, \
199 "could not write to %s: %s" % (dst, errstr)
200
201 finally:
202 if fdst:
203 fdst.close()
204 if fsrc:
205 fsrc.close()
206
207# _copy_file_contents()
208
209
210def copy_file (src, dst,
211 preserve_mode=1,
212 preserve_times=1,
213 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000214 verbose=0,
215 dry_run=0):
Greg Ward2689e3d1999-03-22 14:52:19 +0000216
217 """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src'
218 is copied there with the same name; otherwise, it must be a
219 filename. (If the file exists, it will be ruthlessly clobbered.)
220 If 'preserve_mode' is true (the default), the file's mode (type
221 and permission bits, or whatever is analogous on the current
222 platform) is copied. If 'preserve_times' is true (the default),
223 the last-modified and last-access times are copied as well. If
224 'update' is true, 'src' will only be copied if 'dst' does not
225 exist, or if 'dst' does exist but is older than 'src'. If
226 'verbose' is true, then a one-line summary of the copy will be
Greg Ward884df451999-05-02 21:42:05 +0000227 printed to stdout.
228
229 Return true if the file was copied (or would have been copied),
230 false otherwise (ie. 'update' was true and the destination is
231 up-to-date)."""
Greg Ward2689e3d1999-03-22 14:52:19 +0000232
233 # XXX doesn't copy Mac-specific metadata
234
Greg Ward2689e3d1999-03-22 14:52:19 +0000235 from stat import *
236
237 if not os.path.isfile (src):
238 raise DistutilsFileError, \
Greg Ward138ce651999-09-13 03:09:38 +0000239 "can't copy %s: not a regular file" % src
Greg Ward2689e3d1999-03-22 14:52:19 +0000240
241 if os.path.isdir (dst):
242 dir = dst
243 dst = os.path.join (dst, os.path.basename (src))
244 else:
245 dir = os.path.dirname (dst)
246
247 if update and not newer (src, dst):
Greg Ward884df451999-05-02 21:42:05 +0000248 if verbose:
249 print "not copying %s (output up-to-date)" % src
250 return 0
Greg Ward2689e3d1999-03-22 14:52:19 +0000251
252 if verbose:
253 print "copying %s -> %s" % (src, dir)
254
Greg Warde765a3b1999-04-04 02:54:20 +0000255 if dry_run:
Greg Ward884df451999-05-02 21:42:05 +0000256 return 1
Greg Warde765a3b1999-04-04 02:54:20 +0000257
258 _copy_file_contents (src, dst)
Greg Ward2689e3d1999-03-22 14:52:19 +0000259 if preserve_mode or preserve_times:
260 st = os.stat (src)
Greg Ward5116f901999-06-08 17:05:21 +0000261
262 # According to David Ascher <da@ski.org>, utime() should be done
263 # before chmod() (at least under NT).
Greg Ward2689e3d1999-03-22 14:52:19 +0000264 if preserve_times:
265 os.utime (dst, (st[ST_ATIME], st[ST_MTIME]))
Greg Ward5116f901999-06-08 17:05:21 +0000266 if preserve_mode:
267 os.chmod (dst, S_IMODE (st[ST_MODE]))
Greg Ward2689e3d1999-03-22 14:52:19 +0000268
Greg Ward884df451999-05-02 21:42:05 +0000269 return 1
270
Greg Ward2689e3d1999-03-22 14:52:19 +0000271# copy_file ()
272
273
274def copy_tree (src, dst,
275 preserve_mode=1,
276 preserve_times=1,
277 preserve_symlinks=0,
278 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000279 verbose=0,
280 dry_run=0):
281
Greg Ward2689e3d1999-03-22 14:52:19 +0000282
283 """Copy an entire directory tree 'src' to a new location 'dst'. Both
284 'src' and 'dst' must be directory names. If 'src' is not a
285 directory, raise DistutilsFileError. If 'dst' does not exist, it
Greg Ward884df451999-05-02 21:42:05 +0000286 is created with 'mkpath'. The end result of the copy is that
Greg Ward2689e3d1999-03-22 14:52:19 +0000287 every file in 'src' is copied to 'dst', and directories under
Greg Ward884df451999-05-02 21:42:05 +0000288 'src' are recursively copied to 'dst'. Return the list of files
289 copied (under their output names) -- note that if 'update' is true,
290 this might be less than the list of files considered. Return
291 value is not affected by 'dry_run'.
Greg Ward2689e3d1999-03-22 14:52:19 +0000292
293 'preserve_mode' and 'preserve_times' are the same as for
294 'copy_file'; note that they only apply to regular files, not to
295 directories. If 'preserve_symlinks' is true, symlinks will be
296 copied as symlinks (on platforms that support them!); otherwise
297 (the default), the destination of the symlink will be copied.
298 'update' and 'verbose' are the same as for 'copy_file'."""
299
Greg Ward138ce651999-09-13 03:09:38 +0000300 if not dry_run and not os.path.isdir (src):
Greg Ward2689e3d1999-03-22 14:52:19 +0000301 raise DistutilsFileError, \
302 "cannot copy tree %s: not a directory" % src
303 try:
304 names = os.listdir (src)
305 except os.error, (errno, errstr):
Greg Ward138ce651999-09-13 03:09:38 +0000306 if dry_run:
307 names = []
308 else:
309 raise DistutilsFileError, \
310 "error listing files in %s: %s" % (src, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000311
Greg Warde765a3b1999-04-04 02:54:20 +0000312 if not dry_run:
313 mkpath (dst, verbose=verbose)
Greg Ward2689e3d1999-03-22 14:52:19 +0000314
Greg Ward884df451999-05-02 21:42:05 +0000315 outputs = []
316
Greg Ward2689e3d1999-03-22 14:52:19 +0000317 for n in names:
318 src_name = os.path.join (src, n)
319 dst_name = os.path.join (dst, n)
320
321 if preserve_symlinks and os.path.islink (src_name):
322 link_dest = os.readlink (src_name)
Greg Warde765a3b1999-04-04 02:54:20 +0000323 if verbose:
324 print "linking %s -> %s" % (dst_name, link_dest)
325 if not dry_run:
326 os.symlink (link_dest, dst_name)
Greg Ward884df451999-05-02 21:42:05 +0000327 outputs.append (dst_name)
328
Greg Ward2689e3d1999-03-22 14:52:19 +0000329 elif os.path.isdir (src_name):
Greg Ward884df451999-05-02 21:42:05 +0000330 outputs[-1:] = \
331 copy_tree (src_name, dst_name,
332 preserve_mode, preserve_times, preserve_symlinks,
333 update, verbose, dry_run)
Greg Ward2689e3d1999-03-22 14:52:19 +0000334 else:
Greg Ward884df451999-05-02 21:42:05 +0000335 if (copy_file (src_name, dst_name,
336 preserve_mode, preserve_times,
337 update, verbose, dry_run)):
338 outputs.append (dst_name)
339
340 return outputs
Greg Ward2689e3d1999-03-22 14:52:19 +0000341
342# copy_tree ()
Greg Ward138ce651999-09-13 03:09:38 +0000343
344
345# XXX I suspect this is Unix-specific -- need porting help!
346def move_file (src, dst,
347 verbose=0,
348 dry_run=0):
349
350 """Move a file 'src' to 'dst'. If 'dst' is a directory, the file
351 will be moved into it with the same name; otherwise, 'src' is
352 just renamed to 'dst'. Return the new full name of the file.
353
354 Handles cross-device moves on Unix using
355 'copy_file()'. What about other systems???"""
356
357 from os.path import exists, isfile, isdir, basename, dirname
358
359 if verbose:
360 print "moving %s -> %s" % (src, dst)
361
362 if dry_run:
363 return dst
364
365 if not isfile (src):
366 raise DistutilsFileError, \
367 "can't move '%s': not a regular file" % src
368
369 if isdir (dst):
370 dst = os.path.join (dst, basename (src))
371 elif exists (dst):
372 raise DistutilsFileError, \
373 "can't move '%s': destination '%s' already exists" % \
374 (src, dst)
375
376 if not isdir (dirname (dst)):
377 raise DistutilsFileError, \
378 "can't move '%s': destination '%s' not a valid path" % \
379 (src, dst)
380
381 copy_it = 0
382 try:
383 os.rename (src, dst)
384 except os.error, (num, msg):
385 if num == errno.EXDEV:
386 copy_it = 1
387 else:
388 raise DistutilsFileError, \
389 "couldn't move '%s' to '%s': %s" % (src, dst, msg)
390
391 if copy_it:
392 copy_file (src, dst)
393 try:
394 os.unlink (src)
395 except os.error, (num, msg):
396 try:
397 os.unlink (dst)
398 except os.error:
399 pass
400 raise DistutilsFileError, \
401 ("couldn't move '%s' to '%s' by copy/delete: " +
402 "delete '%s' failed: %s") % \
403 (src, dst, src, msg)
404
405 return dst
406
407# move_file ()
Greg Wardac1424a1999-09-21 18:37:51 +0000408
409
410def write_file (filename, contents):
411 """Create a file with the specified naem and write 'contents' (a
412 sequence of strings without line terminators) to it."""
413
414 f = open (filename, "w")
415 for line in contents:
416 f.write (line + "\n")
417 f.close ()