blob: 7aedc1c6df8f2f6dbaefe83cb2e8b339f3df6bd6 [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
65def newer (file1, file2):
66 """Return true if file1 exists and is more recently modified than
67 file2, or if file1 exists and file2 doesn't. Return false if both
68 exist and file2 is the same age or younger than file1. Raises
69 DistutilsFileError if file1 does not exist."""
70
71 if not os.path.exists (file1):
72 raise DistutilsFileError, "file '%s' does not exist" % file1
73 if not os.path.exists (file2):
74 return 1
75
76 from stat import *
77 mtime1 = os.stat(file1)[ST_MTIME]
78 mtime2 = os.stat(file2)[ST_MTIME]
79
80 return mtime1 > mtime2
81
82# newer ()
83
84
85def make_file (src, dst, func, args,
86 verbose=0, update_message=None, noupdate_message=None):
87 """Makes 'dst' from 'src' (both filenames) by calling 'func' with
88 'args', but only if it needs to: i.e. if 'dst' does not exist or
89 'src' is newer than 'dst'."""
90
91 if newer (src, dst):
92 if verbose and update_message:
93 print update_message
94 apply (func, args)
95 else:
96 if verbose and noupdate_message:
97 print noupdate_message
98
99# make_file ()
100
101
102def _copy_file_contents (src, dst, buffer_size=16*1024):
103 """Copy the file 'src' to 'dst'; both must be filenames. Any error
104 opening either file, reading from 'src', or writing to 'dst',
105 raises DistutilsFileError. Data is read/written in chunks of
106 'buffer_size' bytes (default 16k). No attempt is made to handle
107 anything apart from regular files."""
108
109 # Stolen from shutil module in the standard library, but with
110 # custom error-handling added.
111
112 fsrc = None
113 fdst = None
114 try:
115 try:
116 fsrc = open(src, 'rb')
117 except os.error, (errno, errstr):
118 raise DistutilsFileError, "could not open %s: %s" % (src, errstr)
119
120 try:
121 fdst = open(dst, 'wb')
122 except os.error, (errno, errstr):
123 raise DistutilsFileError, "could not create %s: %s" % (dst, errstr)
124
125 while 1:
126 try:
127 buf = fsrc.read (buffer_size)
128 except os.error, (errno, errstr):
129 raise DistutilsFileError, \
130 "could not read from %s: %s" % (src, errstr)
131
132 if not buf:
133 break
134
135 try:
136 fdst.write(buf)
137 except os.error, (errno, errstr):
138 raise DistutilsFileError, \
139 "could not write to %s: %s" % (dst, errstr)
140
141 finally:
142 if fdst:
143 fdst.close()
144 if fsrc:
145 fsrc.close()
146
147# _copy_file_contents()
148
149
150def copy_file (src, dst,
151 preserve_mode=1,
152 preserve_times=1,
153 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000154 verbose=0,
155 dry_run=0):
Greg Ward2689e3d1999-03-22 14:52:19 +0000156
157 """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src'
158 is copied there with the same name; otherwise, it must be a
159 filename. (If the file exists, it will be ruthlessly clobbered.)
160 If 'preserve_mode' is true (the default), the file's mode (type
161 and permission bits, or whatever is analogous on the current
162 platform) is copied. If 'preserve_times' is true (the default),
163 the last-modified and last-access times are copied as well. If
164 'update' is true, 'src' will only be copied if 'dst' does not
165 exist, or if 'dst' does exist but is older than 'src'. If
166 'verbose' is true, then a one-line summary of the copy will be
Greg Ward884df451999-05-02 21:42:05 +0000167 printed to stdout.
168
169 Return true if the file was copied (or would have been copied),
170 false otherwise (ie. 'update' was true and the destination is
171 up-to-date)."""
Greg Ward2689e3d1999-03-22 14:52:19 +0000172
173 # XXX doesn't copy Mac-specific metadata
174
Greg Ward2689e3d1999-03-22 14:52:19 +0000175 from stat import *
176
177 if not os.path.isfile (src):
178 raise DistutilsFileError, \
179 "can't copy %s:not a regular file" % src
180
181 if os.path.isdir (dst):
182 dir = dst
183 dst = os.path.join (dst, os.path.basename (src))
184 else:
185 dir = os.path.dirname (dst)
186
187 if update and not newer (src, dst):
Greg Ward884df451999-05-02 21:42:05 +0000188 if verbose:
189 print "not copying %s (output up-to-date)" % src
190 return 0
Greg Ward2689e3d1999-03-22 14:52:19 +0000191
192 if verbose:
193 print "copying %s -> %s" % (src, dir)
194
Greg Warde765a3b1999-04-04 02:54:20 +0000195 if dry_run:
Greg Ward884df451999-05-02 21:42:05 +0000196 return 1
Greg Warde765a3b1999-04-04 02:54:20 +0000197
198 _copy_file_contents (src, dst)
Greg Ward2689e3d1999-03-22 14:52:19 +0000199 if preserve_mode or preserve_times:
200 st = os.stat (src)
201 if preserve_mode:
202 os.chmod (dst, S_IMODE (st[ST_MODE]))
203 if preserve_times:
204 os.utime (dst, (st[ST_ATIME], st[ST_MTIME]))
205
Greg Ward884df451999-05-02 21:42:05 +0000206 return 1
207
Greg Ward2689e3d1999-03-22 14:52:19 +0000208# copy_file ()
209
210
211def copy_tree (src, dst,
212 preserve_mode=1,
213 preserve_times=1,
214 preserve_symlinks=0,
215 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000216 verbose=0,
217 dry_run=0):
218
Greg Ward2689e3d1999-03-22 14:52:19 +0000219
220 """Copy an entire directory tree 'src' to a new location 'dst'. Both
221 'src' and 'dst' must be directory names. If 'src' is not a
222 directory, raise DistutilsFileError. If 'dst' does not exist, it
Greg Ward884df451999-05-02 21:42:05 +0000223 is created with 'mkpath'. The end result of the copy is that
Greg Ward2689e3d1999-03-22 14:52:19 +0000224 every file in 'src' is copied to 'dst', and directories under
Greg Ward884df451999-05-02 21:42:05 +0000225 'src' are recursively copied to 'dst'. Return the list of files
226 copied (under their output names) -- note that if 'update' is true,
227 this might be less than the list of files considered. Return
228 value is not affected by 'dry_run'.
Greg Ward2689e3d1999-03-22 14:52:19 +0000229
230 'preserve_mode' and 'preserve_times' are the same as for
231 'copy_file'; note that they only apply to regular files, not to
232 directories. If 'preserve_symlinks' is true, symlinks will be
233 copied as symlinks (on platforms that support them!); otherwise
234 (the default), the destination of the symlink will be copied.
235 'update' and 'verbose' are the same as for 'copy_file'."""
236
237 if not os.path.isdir (src):
238 raise DistutilsFileError, \
239 "cannot copy tree %s: not a directory" % src
240 try:
241 names = os.listdir (src)
242 except os.error, (errno, errstr):
243 raise DistutilsFileError, \
244 "error listing files in %s: %s" % (src, errstr)
245
Greg Warde765a3b1999-04-04 02:54:20 +0000246 if not dry_run:
247 mkpath (dst, verbose=verbose)
Greg Ward2689e3d1999-03-22 14:52:19 +0000248
Greg Ward884df451999-05-02 21:42:05 +0000249 outputs = []
250
Greg Ward2689e3d1999-03-22 14:52:19 +0000251 for n in names:
252 src_name = os.path.join (src, n)
253 dst_name = os.path.join (dst, n)
254
255 if preserve_symlinks and os.path.islink (src_name):
256 link_dest = os.readlink (src_name)
Greg Warde765a3b1999-04-04 02:54:20 +0000257 if verbose:
258 print "linking %s -> %s" % (dst_name, link_dest)
259 if not dry_run:
260 os.symlink (link_dest, dst_name)
Greg Ward884df451999-05-02 21:42:05 +0000261 outputs.append (dst_name)
262
Greg Ward2689e3d1999-03-22 14:52:19 +0000263 elif os.path.isdir (src_name):
Greg Ward884df451999-05-02 21:42:05 +0000264 outputs[-1:] = \
265 copy_tree (src_name, dst_name,
266 preserve_mode, preserve_times, preserve_symlinks,
267 update, verbose, dry_run)
Greg Ward2689e3d1999-03-22 14:52:19 +0000268 else:
Greg Ward884df451999-05-02 21:42:05 +0000269 if (copy_file (src_name, dst_name,
270 preserve_mode, preserve_times,
271 update, verbose, dry_run)):
272 outputs.append (dst_name)
273
274 return outputs
Greg Ward2689e3d1999-03-22 14:52:19 +0000275
276# copy_tree ()