blob: aee95d366813ec3b4a00b5f70c723e24a87dfedc [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)
Greg Wardcd1486f1999-09-29 12:14:16 +000046 if not tail: # in case 'name' has trailing slash
47 (head, tail) = os.path.split (head)
Greg Ward2689e3d1999-03-22 14:52:19 +000048 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):
74 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):
103
104 """Walk two filename lists in parallel, testing if each 'target' is
105 up-to-date relative to its corresponding 'source'. If so, both
106 are deleted from their respective lists. Return a list of tuples
107 containing the deleted (source,target) pairs."""
108
109 if len (sources) != len (targets):
110 raise ValueError, "'sources' and 'targets' must be same length"
111
112 goners = []
113 for i in range (len (sources)-1, -1, -1):
114 if not newer (sources[i], targets[i]):
115 goners.append ((sources[i], targets[i]))
116 del sources[i]
117 del targets[i]
118 goners.reverse()
119 return goners
120
121# newer_pairwise ()
122
123
124def newer_group (sources, target):
125 """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
128 return true."""
129
130 # If the target doesn't even exist, then it's definitely out-of-date.
131 if not os.path.exists (target):
132 return 1
133
134 # Otherwise we have to find out the hard way: if *any* source file
135 # is more recent than 'target', then 'target' is out-of-date and
136 # we can immediately return true. If we fall through to the end
137 # of the loop, then 'target' is up-to-date and we return false.
138 from stat import ST_MTIME
139 target_mtime = os.stat (target)[ST_MTIME]
140 for source in sources:
141 source_mtime = os.stat(source)[ST_MTIME]
142 if source_mtime > target_mtime:
143 return 1
144 else:
145 return 0
146
147# newer_group ()
148
149
Greg Ward2689e3d1999-03-22 14:52:19 +0000150def make_file (src, dst, func, args,
151 verbose=0, update_message=None, noupdate_message=None):
152 """Makes 'dst' from 'src' (both filenames) by calling 'func' with
153 'args', but only if it needs to: i.e. if 'dst' does not exist or
154 'src' is newer than 'dst'."""
155
156 if newer (src, dst):
157 if verbose and update_message:
158 print update_message
159 apply (func, args)
160 else:
161 if verbose and noupdate_message:
162 print noupdate_message
163
164# make_file ()
165
166
167def _copy_file_contents (src, dst, buffer_size=16*1024):
168 """Copy the file 'src' to 'dst'; both must be filenames. Any error
169 opening either file, reading from 'src', or writing to 'dst',
170 raises DistutilsFileError. Data is read/written in chunks of
171 'buffer_size' bytes (default 16k). No attempt is made to handle
172 anything apart from regular files."""
173
174 # Stolen from shutil module in the standard library, but with
175 # custom error-handling added.
176
177 fsrc = None
178 fdst = None
179 try:
180 try:
181 fsrc = open(src, 'rb')
182 except os.error, (errno, errstr):
183 raise DistutilsFileError, "could not open %s: %s" % (src, errstr)
184
185 try:
186 fdst = open(dst, 'wb')
187 except os.error, (errno, errstr):
188 raise DistutilsFileError, "could not create %s: %s" % (dst, errstr)
189
190 while 1:
191 try:
192 buf = fsrc.read (buffer_size)
193 except os.error, (errno, errstr):
194 raise DistutilsFileError, \
195 "could not read from %s: %s" % (src, errstr)
196
197 if not buf:
198 break
199
200 try:
201 fdst.write(buf)
202 except os.error, (errno, errstr):
203 raise DistutilsFileError, \
204 "could not write to %s: %s" % (dst, errstr)
205
206 finally:
207 if fdst:
208 fdst.close()
209 if fsrc:
210 fsrc.close()
211
212# _copy_file_contents()
213
214
215def copy_file (src, dst,
216 preserve_mode=1,
217 preserve_times=1,
218 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000219 verbose=0,
220 dry_run=0):
Greg Ward2689e3d1999-03-22 14:52:19 +0000221
222 """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src'
223 is copied there with the same name; otherwise, it must be a
224 filename. (If the file exists, it will be ruthlessly clobbered.)
225 If 'preserve_mode' is true (the default), the file's mode (type
226 and permission bits, or whatever is analogous on the current
227 platform) is copied. If 'preserve_times' is true (the default),
228 the last-modified and last-access times are copied as well. If
229 'update' is true, 'src' will only be copied if 'dst' does not
230 exist, or if 'dst' does exist but is older than 'src'. If
231 'verbose' is true, then a one-line summary of the copy will be
Greg Ward884df451999-05-02 21:42:05 +0000232 printed to stdout.
233
234 Return true if the file was copied (or would have been copied),
235 false otherwise (ie. 'update' was true and the destination is
236 up-to-date)."""
Greg Ward2689e3d1999-03-22 14:52:19 +0000237
238 # XXX doesn't copy Mac-specific metadata
239
Greg Ward2689e3d1999-03-22 14:52:19 +0000240 from stat import *
241
242 if not os.path.isfile (src):
243 raise DistutilsFileError, \
Greg Ward138ce651999-09-13 03:09:38 +0000244 "can't copy %s: not a regular file" % src
Greg Ward2689e3d1999-03-22 14:52:19 +0000245
246 if os.path.isdir (dst):
247 dir = dst
248 dst = os.path.join (dst, os.path.basename (src))
249 else:
250 dir = os.path.dirname (dst)
251
252 if update and not newer (src, dst):
Greg Ward884df451999-05-02 21:42:05 +0000253 if verbose:
254 print "not copying %s (output up-to-date)" % src
255 return 0
Greg Ward2689e3d1999-03-22 14:52:19 +0000256
257 if verbose:
258 print "copying %s -> %s" % (src, dir)
259
Greg Warde765a3b1999-04-04 02:54:20 +0000260 if dry_run:
Greg Ward884df451999-05-02 21:42:05 +0000261 return 1
Greg Warde765a3b1999-04-04 02:54:20 +0000262
263 _copy_file_contents (src, dst)
Greg Ward2689e3d1999-03-22 14:52:19 +0000264 if preserve_mode or preserve_times:
265 st = os.stat (src)
Greg Ward5116f901999-06-08 17:05:21 +0000266
267 # According to David Ascher <da@ski.org>, utime() should be done
268 # before chmod() (at least under NT).
Greg Ward2689e3d1999-03-22 14:52:19 +0000269 if preserve_times:
270 os.utime (dst, (st[ST_ATIME], st[ST_MTIME]))
Greg Ward5116f901999-06-08 17:05:21 +0000271 if preserve_mode:
272 os.chmod (dst, S_IMODE (st[ST_MODE]))
Greg Ward2689e3d1999-03-22 14:52:19 +0000273
Greg Ward884df451999-05-02 21:42:05 +0000274 return 1
275
Greg Ward2689e3d1999-03-22 14:52:19 +0000276# copy_file ()
277
278
279def copy_tree (src, dst,
280 preserve_mode=1,
281 preserve_times=1,
282 preserve_symlinks=0,
283 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000284 verbose=0,
285 dry_run=0):
286
Greg Ward2689e3d1999-03-22 14:52:19 +0000287
288 """Copy an entire directory tree 'src' to a new location 'dst'. Both
289 'src' and 'dst' must be directory names. If 'src' is not a
290 directory, raise DistutilsFileError. If 'dst' does not exist, it
Greg Ward884df451999-05-02 21:42:05 +0000291 is created with 'mkpath'. The end result of the copy is that
Greg Ward2689e3d1999-03-22 14:52:19 +0000292 every file in 'src' is copied to 'dst', and directories under
Greg Ward884df451999-05-02 21:42:05 +0000293 'src' are recursively copied to 'dst'. Return the list of files
294 copied (under their output names) -- note that if 'update' is true,
295 this might be less than the list of files considered. Return
296 value is not affected by 'dry_run'.
Greg Ward2689e3d1999-03-22 14:52:19 +0000297
298 'preserve_mode' and 'preserve_times' are the same as for
299 'copy_file'; note that they only apply to regular files, not to
300 directories. If 'preserve_symlinks' is true, symlinks will be
301 copied as symlinks (on platforms that support them!); otherwise
302 (the default), the destination of the symlink will be copied.
303 'update' and 'verbose' are the same as for 'copy_file'."""
304
Greg Ward138ce651999-09-13 03:09:38 +0000305 if not dry_run and not os.path.isdir (src):
Greg Ward2689e3d1999-03-22 14:52:19 +0000306 raise DistutilsFileError, \
307 "cannot copy tree %s: not a directory" % src
308 try:
309 names = os.listdir (src)
310 except os.error, (errno, errstr):
Greg Ward138ce651999-09-13 03:09:38 +0000311 if dry_run:
312 names = []
313 else:
314 raise DistutilsFileError, \
315 "error listing files in %s: %s" % (src, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000316
Greg Warde765a3b1999-04-04 02:54:20 +0000317 if not dry_run:
318 mkpath (dst, verbose=verbose)
Greg Ward2689e3d1999-03-22 14:52:19 +0000319
Greg Ward884df451999-05-02 21:42:05 +0000320 outputs = []
321
Greg Ward2689e3d1999-03-22 14:52:19 +0000322 for n in names:
323 src_name = os.path.join (src, n)
324 dst_name = os.path.join (dst, n)
325
326 if preserve_symlinks and os.path.islink (src_name):
327 link_dest = os.readlink (src_name)
Greg Warde765a3b1999-04-04 02:54:20 +0000328 if verbose:
329 print "linking %s -> %s" % (dst_name, link_dest)
330 if not dry_run:
331 os.symlink (link_dest, dst_name)
Greg Ward884df451999-05-02 21:42:05 +0000332 outputs.append (dst_name)
333
Greg Ward2689e3d1999-03-22 14:52:19 +0000334 elif os.path.isdir (src_name):
Greg Ward884df451999-05-02 21:42:05 +0000335 outputs[-1:] = \
336 copy_tree (src_name, dst_name,
337 preserve_mode, preserve_times, preserve_symlinks,
338 update, verbose, dry_run)
Greg Ward2689e3d1999-03-22 14:52:19 +0000339 else:
Greg Ward884df451999-05-02 21:42:05 +0000340 if (copy_file (src_name, dst_name,
341 preserve_mode, preserve_times,
342 update, verbose, dry_run)):
343 outputs.append (dst_name)
344
345 return outputs
Greg Ward2689e3d1999-03-22 14:52:19 +0000346
347# copy_tree ()
Greg Ward138ce651999-09-13 03:09:38 +0000348
349
350# XXX I suspect this is Unix-specific -- need porting help!
351def move_file (src, dst,
352 verbose=0,
353 dry_run=0):
354
355 """Move a file 'src' to 'dst'. If 'dst' is a directory, the file
356 will be moved into it with the same name; otherwise, 'src' is
357 just renamed to 'dst'. Return the new full name of the file.
358
359 Handles cross-device moves on Unix using
360 'copy_file()'. What about other systems???"""
361
362 from os.path import exists, isfile, isdir, basename, dirname
363
364 if verbose:
365 print "moving %s -> %s" % (src, dst)
366
367 if dry_run:
368 return dst
369
370 if not isfile (src):
371 raise DistutilsFileError, \
372 "can't move '%s': not a regular file" % src
373
374 if isdir (dst):
375 dst = os.path.join (dst, basename (src))
376 elif exists (dst):
377 raise DistutilsFileError, \
378 "can't move '%s': destination '%s' already exists" % \
379 (src, dst)
380
381 if not isdir (dirname (dst)):
382 raise DistutilsFileError, \
383 "can't move '%s': destination '%s' not a valid path" % \
384 (src, dst)
385
386 copy_it = 0
387 try:
388 os.rename (src, dst)
389 except os.error, (num, msg):
390 if num == errno.EXDEV:
391 copy_it = 1
392 else:
393 raise DistutilsFileError, \
394 "couldn't move '%s' to '%s': %s" % (src, dst, msg)
395
396 if copy_it:
397 copy_file (src, dst)
398 try:
399 os.unlink (src)
400 except os.error, (num, msg):
401 try:
402 os.unlink (dst)
403 except os.error:
404 pass
405 raise DistutilsFileError, \
406 ("couldn't move '%s' to '%s' by copy/delete: " +
407 "delete '%s' failed: %s") % \
408 (src, dst, src, msg)
409
410 return dst
411
412# move_file ()
Greg Wardac1424a1999-09-21 18:37:51 +0000413
414
415def write_file (filename, contents):
416 """Create a file with the specified naem and write 'contents' (a
417 sequence of strings without line terminators) to it."""
418
419 f = open (filename, "w")
420 for line in contents:
421 f.write (line + "\n")
422 f.close ()