blob: 953c2e26e2a8c2979801aaa688b82e0e22723050 [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
Greg Wardf3b997a1999-10-03 20:50:41 +000040 name = os.path.normpath (name)
41
Greg Ward2689e3d1999-03-22 14:52:19 +000042 if os.path.isdir (name):
43 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):
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):
Greg Ward138ce651999-09-13 03:09:38 +0000103 """Walk two filename lists in parallel, testing if each 'target' is
104 up-to-date relative to its corresponding 'source'. If so, both
105 are deleted from their respective lists. Return a list of tuples
106 containing the deleted (source,target) pairs."""
107
108 if len (sources) != len (targets):
109 raise ValueError, "'sources' and 'targets' must be same length"
110
111 goners = []
112 for i in range (len (sources)-1, -1, -1):
113 if not newer (sources[i], targets[i]):
114 goners.append ((sources[i], targets[i]))
115 del sources[i]
116 del targets[i]
117 goners.reverse()
118 return goners
119
120# newer_pairwise ()
121
122
Greg Ward7b7679e2000-01-09 22:48:59 +0000123def newer_group (sources, target, missing='error'):
Greg Ward138ce651999-09-13 03:09:38 +0000124 """Return true if 'target' is out-of-date with respect to any
125 file listed in 'sources'. In other words, if 'target' exists and
126 is newer than every file in 'sources', return false; otherwise
Greg Ward7b7679e2000-01-09 22:48:59 +0000127 return true. 'missing' controls what we do when a source file is
128 missing; the default ("error") is to blow up with an OSError from
129 inside 'stat()'; if it is "ignore", we silently drop any missing
130 source files; if it is "newer", any missing source files make us
131 assume that 'target' is out-of-date (this is handy in "dry-run"
132 mode: it'll make you pretend to carry out commands that wouldn't
133 work because inputs are missing, but that doesn't matter because
134 you're not actually going to run the commands)."""
Greg Ward138ce651999-09-13 03:09:38 +0000135
136 # If the target doesn't even exist, then it's definitely out-of-date.
137 if not os.path.exists (target):
138 return 1
139
140 # Otherwise we have to find out the hard way: if *any* source file
141 # is more recent than 'target', then 'target' is out-of-date and
142 # we can immediately return true. If we fall through to the end
143 # of the loop, then 'target' is up-to-date and we return false.
144 from stat import ST_MTIME
145 target_mtime = os.stat (target)[ST_MTIME]
146 for source in sources:
Greg Ward7b7679e2000-01-09 22:48:59 +0000147 if not os.path.exists (source):
148 if missing == 'error': # blow up when we stat() the file
149 pass
150 elif missing == 'ignore': # missing source dropped from
151 continue # target's dependency list
152 elif missing == 'newer': # missing source means target is
153 return 1 # out-of-date
154
Greg Ward138ce651999-09-13 03:09:38 +0000155 source_mtime = os.stat(source)[ST_MTIME]
156 if source_mtime > target_mtime:
157 return 1
158 else:
159 return 0
160
161# newer_group ()
162
163
Greg Wardf3b997a1999-10-03 20:50:41 +0000164# XXX this isn't used anywhere, and worse, it has the same name as a method
165# in Command with subtly different semantics. (This one just has one
166# source -> one dest; that one has many sources -> one dest.) Nuke it?
Greg Ward2689e3d1999-03-22 14:52:19 +0000167def make_file (src, dst, func, args,
168 verbose=0, update_message=None, noupdate_message=None):
169 """Makes 'dst' from 'src' (both filenames) by calling 'func' with
170 'args', but only if it needs to: i.e. if 'dst' does not exist or
171 'src' is newer than 'dst'."""
172
173 if newer (src, dst):
174 if verbose and update_message:
175 print update_message
176 apply (func, args)
177 else:
178 if verbose and noupdate_message:
179 print noupdate_message
180
181# make_file ()
182
183
184def _copy_file_contents (src, dst, buffer_size=16*1024):
185 """Copy the file 'src' to 'dst'; both must be filenames. Any error
186 opening either file, reading from 'src', or writing to 'dst',
187 raises DistutilsFileError. Data is read/written in chunks of
188 'buffer_size' bytes (default 16k). No attempt is made to handle
189 anything apart from regular files."""
190
191 # Stolen from shutil module in the standard library, but with
192 # custom error-handling added.
193
194 fsrc = None
195 fdst = None
196 try:
197 try:
198 fsrc = open(src, 'rb')
199 except os.error, (errno, errstr):
200 raise DistutilsFileError, "could not open %s: %s" % (src, errstr)
201
202 try:
203 fdst = open(dst, 'wb')
204 except os.error, (errno, errstr):
205 raise DistutilsFileError, "could not create %s: %s" % (dst, errstr)
206
207 while 1:
208 try:
209 buf = fsrc.read (buffer_size)
210 except os.error, (errno, errstr):
211 raise DistutilsFileError, \
212 "could not read from %s: %s" % (src, errstr)
213
214 if not buf:
215 break
216
217 try:
218 fdst.write(buf)
219 except os.error, (errno, errstr):
220 raise DistutilsFileError, \
221 "could not write to %s: %s" % (dst, errstr)
222
223 finally:
224 if fdst:
225 fdst.close()
226 if fsrc:
227 fsrc.close()
228
229# _copy_file_contents()
230
231
232def copy_file (src, dst,
233 preserve_mode=1,
234 preserve_times=1,
235 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000236 verbose=0,
237 dry_run=0):
Greg Ward2689e3d1999-03-22 14:52:19 +0000238
239 """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src'
240 is copied there with the same name; otherwise, it must be a
241 filename. (If the file exists, it will be ruthlessly clobbered.)
242 If 'preserve_mode' is true (the default), the file's mode (type
243 and permission bits, or whatever is analogous on the current
244 platform) is copied. If 'preserve_times' is true (the default),
245 the last-modified and last-access times are copied as well. If
246 'update' is true, 'src' will only be copied if 'dst' does not
247 exist, or if 'dst' does exist but is older than 'src'. If
248 'verbose' is true, then a one-line summary of the copy will be
Greg Ward884df451999-05-02 21:42:05 +0000249 printed to stdout.
250
251 Return true if the file was copied (or would have been copied),
252 false otherwise (ie. 'update' was true and the destination is
253 up-to-date)."""
Greg Ward2689e3d1999-03-22 14:52:19 +0000254
255 # XXX doesn't copy Mac-specific metadata
256
Greg Ward2689e3d1999-03-22 14:52:19 +0000257 from stat import *
258
259 if not os.path.isfile (src):
260 raise DistutilsFileError, \
Greg Ward138ce651999-09-13 03:09:38 +0000261 "can't copy %s: not a regular file" % src
Greg Ward2689e3d1999-03-22 14:52:19 +0000262
263 if os.path.isdir (dst):
264 dir = dst
265 dst = os.path.join (dst, os.path.basename (src))
266 else:
267 dir = os.path.dirname (dst)
268
269 if update and not newer (src, dst):
Greg Ward884df451999-05-02 21:42:05 +0000270 if verbose:
271 print "not copying %s (output up-to-date)" % src
272 return 0
Greg Ward2689e3d1999-03-22 14:52:19 +0000273
274 if verbose:
275 print "copying %s -> %s" % (src, dir)
276
Greg Warde765a3b1999-04-04 02:54:20 +0000277 if dry_run:
Greg Ward884df451999-05-02 21:42:05 +0000278 return 1
Greg Warde765a3b1999-04-04 02:54:20 +0000279
280 _copy_file_contents (src, dst)
Greg Ward2689e3d1999-03-22 14:52:19 +0000281 if preserve_mode or preserve_times:
282 st = os.stat (src)
Greg Ward5116f901999-06-08 17:05:21 +0000283
284 # According to David Ascher <da@ski.org>, utime() should be done
285 # before chmod() (at least under NT).
Greg Ward2689e3d1999-03-22 14:52:19 +0000286 if preserve_times:
287 os.utime (dst, (st[ST_ATIME], st[ST_MTIME]))
Greg Ward5116f901999-06-08 17:05:21 +0000288 if preserve_mode:
289 os.chmod (dst, S_IMODE (st[ST_MODE]))
Greg Ward2689e3d1999-03-22 14:52:19 +0000290
Greg Ward884df451999-05-02 21:42:05 +0000291 return 1
292
Greg Ward2689e3d1999-03-22 14:52:19 +0000293# copy_file ()
294
295
296def copy_tree (src, dst,
297 preserve_mode=1,
298 preserve_times=1,
299 preserve_symlinks=0,
300 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000301 verbose=0,
302 dry_run=0):
303
Greg Ward2689e3d1999-03-22 14:52:19 +0000304
305 """Copy an entire directory tree 'src' to a new location 'dst'. Both
306 'src' and 'dst' must be directory names. If 'src' is not a
307 directory, raise DistutilsFileError. If 'dst' does not exist, it
Greg Wardf3b997a1999-10-03 20:50:41 +0000308 is created with 'mkpath()'. The end result of the copy is that
Greg Ward2689e3d1999-03-22 14:52:19 +0000309 every file in 'src' is copied to 'dst', and directories under
Greg Ward884df451999-05-02 21:42:05 +0000310 'src' are recursively copied to 'dst'. Return the list of files
311 copied (under their output names) -- note that if 'update' is true,
312 this might be less than the list of files considered. Return
313 value is not affected by 'dry_run'.
Greg Ward2689e3d1999-03-22 14:52:19 +0000314
315 'preserve_mode' and 'preserve_times' are the same as for
316 'copy_file'; note that they only apply to regular files, not to
317 directories. If 'preserve_symlinks' is true, symlinks will be
318 copied as symlinks (on platforms that support them!); otherwise
319 (the default), the destination of the symlink will be copied.
320 'update' and 'verbose' are the same as for 'copy_file'."""
321
Greg Ward138ce651999-09-13 03:09:38 +0000322 if not dry_run and not os.path.isdir (src):
Greg Ward2689e3d1999-03-22 14:52:19 +0000323 raise DistutilsFileError, \
324 "cannot copy tree %s: not a directory" % src
325 try:
326 names = os.listdir (src)
327 except os.error, (errno, errstr):
Greg Ward138ce651999-09-13 03:09:38 +0000328 if dry_run:
329 names = []
330 else:
331 raise DistutilsFileError, \
332 "error listing files in %s: %s" % (src, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000333
Greg Warde765a3b1999-04-04 02:54:20 +0000334 if not dry_run:
335 mkpath (dst, verbose=verbose)
Greg Ward2689e3d1999-03-22 14:52:19 +0000336
Greg Ward884df451999-05-02 21:42:05 +0000337 outputs = []
338
Greg Ward2689e3d1999-03-22 14:52:19 +0000339 for n in names:
340 src_name = os.path.join (src, n)
341 dst_name = os.path.join (dst, n)
342
343 if preserve_symlinks and os.path.islink (src_name):
344 link_dest = os.readlink (src_name)
Greg Warde765a3b1999-04-04 02:54:20 +0000345 if verbose:
346 print "linking %s -> %s" % (dst_name, link_dest)
347 if not dry_run:
348 os.symlink (link_dest, dst_name)
Greg Ward884df451999-05-02 21:42:05 +0000349 outputs.append (dst_name)
350
Greg Ward2689e3d1999-03-22 14:52:19 +0000351 elif os.path.isdir (src_name):
Greg Ward884df451999-05-02 21:42:05 +0000352 outputs[-1:] = \
353 copy_tree (src_name, dst_name,
354 preserve_mode, preserve_times, preserve_symlinks,
355 update, verbose, dry_run)
Greg Ward2689e3d1999-03-22 14:52:19 +0000356 else:
Greg Ward884df451999-05-02 21:42:05 +0000357 if (copy_file (src_name, dst_name,
358 preserve_mode, preserve_times,
359 update, verbose, dry_run)):
360 outputs.append (dst_name)
361
362 return outputs
Greg Ward2689e3d1999-03-22 14:52:19 +0000363
364# copy_tree ()
Greg Ward138ce651999-09-13 03:09:38 +0000365
366
367# XXX I suspect this is Unix-specific -- need porting help!
368def move_file (src, dst,
369 verbose=0,
370 dry_run=0):
371
372 """Move a file 'src' to 'dst'. If 'dst' is a directory, the file
373 will be moved into it with the same name; otherwise, 'src' is
374 just renamed to 'dst'. Return the new full name of the file.
375
376 Handles cross-device moves on Unix using
377 'copy_file()'. What about other systems???"""
378
379 from os.path import exists, isfile, isdir, basename, dirname
380
381 if verbose:
382 print "moving %s -> %s" % (src, dst)
383
384 if dry_run:
385 return dst
386
387 if not isfile (src):
388 raise DistutilsFileError, \
389 "can't move '%s': not a regular file" % src
390
391 if isdir (dst):
392 dst = os.path.join (dst, basename (src))
393 elif exists (dst):
394 raise DistutilsFileError, \
395 "can't move '%s': destination '%s' already exists" % \
396 (src, dst)
397
398 if not isdir (dirname (dst)):
399 raise DistutilsFileError, \
400 "can't move '%s': destination '%s' not a valid path" % \
401 (src, dst)
402
403 copy_it = 0
404 try:
405 os.rename (src, dst)
406 except os.error, (num, msg):
407 if num == errno.EXDEV:
408 copy_it = 1
409 else:
410 raise DistutilsFileError, \
411 "couldn't move '%s' to '%s': %s" % (src, dst, msg)
412
413 if copy_it:
414 copy_file (src, dst)
415 try:
416 os.unlink (src)
417 except os.error, (num, msg):
418 try:
419 os.unlink (dst)
420 except os.error:
421 pass
422 raise DistutilsFileError, \
423 ("couldn't move '%s' to '%s' by copy/delete: " +
424 "delete '%s' failed: %s") % \
425 (src, dst, src, msg)
426
427 return dst
428
429# move_file ()
Greg Wardac1424a1999-09-21 18:37:51 +0000430
431
432def write_file (filename, contents):
Greg Wardf3b997a1999-10-03 20:50:41 +0000433 """Create a file with the specified name and write 'contents' (a
Greg Wardac1424a1999-09-21 18:37:51 +0000434 sequence of strings without line terminators) to it."""
435
436 f = open (filename, "w")
437 for line in contents:
438 f.write (line + "\n")
439 f.close ()