blob: 7c13abe09d582167d4b67c1794ff4f7a4302c4d2 [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).
21def mkpath (name, mode=0777, verbose=0):
22 """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
47 # now 'head' contains the highest directory that already exists
48 for d in tails:
49 #print "head = %s, d = %s: " % (head, d),
50 head = os.path.join (head, d)
51 if verbose:
52 print "creating", head
53 try:
54 os.mkdir (head)
55 except os.error, (errno, errstr):
56 raise DistutilsFileError, "%s: %s" % (head, errstr)
57
58# mkpath ()
59
60
61def newer (file1, file2):
62 """Return true if file1 exists and is more recently modified than
63 file2, or if file1 exists and file2 doesn't. Return false if both
64 exist and file2 is the same age or younger than file1. Raises
65 DistutilsFileError if file1 does not exist."""
66
67 if not os.path.exists (file1):
68 raise DistutilsFileError, "file '%s' does not exist" % file1
69 if not os.path.exists (file2):
70 return 1
71
72 from stat import *
73 mtime1 = os.stat(file1)[ST_MTIME]
74 mtime2 = os.stat(file2)[ST_MTIME]
75
76 return mtime1 > mtime2
77
78# newer ()
79
80
81def make_file (src, dst, func, args,
82 verbose=0, update_message=None, noupdate_message=None):
83 """Makes 'dst' from 'src' (both filenames) by calling 'func' with
84 'args', but only if it needs to: i.e. if 'dst' does not exist or
85 'src' is newer than 'dst'."""
86
87 if newer (src, dst):
88 if verbose and update_message:
89 print update_message
90 apply (func, args)
91 else:
92 if verbose and noupdate_message:
93 print noupdate_message
94
95# make_file ()
96
97
98def _copy_file_contents (src, dst, buffer_size=16*1024):
99 """Copy the file 'src' to 'dst'; both must be filenames. Any error
100 opening either file, reading from 'src', or writing to 'dst',
101 raises DistutilsFileError. Data is read/written in chunks of
102 'buffer_size' bytes (default 16k). No attempt is made to handle
103 anything apart from regular files."""
104
105 # Stolen from shutil module in the standard library, but with
106 # custom error-handling added.
107
108 fsrc = None
109 fdst = None
110 try:
111 try:
112 fsrc = open(src, 'rb')
113 except os.error, (errno, errstr):
114 raise DistutilsFileError, "could not open %s: %s" % (src, errstr)
115
116 try:
117 fdst = open(dst, 'wb')
118 except os.error, (errno, errstr):
119 raise DistutilsFileError, "could not create %s: %s" % (dst, errstr)
120
121 while 1:
122 try:
123 buf = fsrc.read (buffer_size)
124 except os.error, (errno, errstr):
125 raise DistutilsFileError, \
126 "could not read from %s: %s" % (src, errstr)
127
128 if not buf:
129 break
130
131 try:
132 fdst.write(buf)
133 except os.error, (errno, errstr):
134 raise DistutilsFileError, \
135 "could not write to %s: %s" % (dst, errstr)
136
137 finally:
138 if fdst:
139 fdst.close()
140 if fsrc:
141 fsrc.close()
142
143# _copy_file_contents()
144
145
146def copy_file (src, dst,
147 preserve_mode=1,
148 preserve_times=1,
149 update=0,
150 verbose=0):
151
152 """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src'
153 is copied there with the same name; otherwise, it must be a
154 filename. (If the file exists, it will be ruthlessly clobbered.)
155 If 'preserve_mode' is true (the default), the file's mode (type
156 and permission bits, or whatever is analogous on the current
157 platform) is copied. If 'preserve_times' is true (the default),
158 the last-modified and last-access times are copied as well. If
159 'update' is true, 'src' will only be copied if 'dst' does not
160 exist, or if 'dst' does exist but is older than 'src'. If
161 'verbose' is true, then a one-line summary of the copy will be
162 printed to stdout."""
163
164 # XXX doesn't copy Mac-specific metadata
165
166 from shutil import copyfile
167 from stat import *
168
169 if not os.path.isfile (src):
170 raise DistutilsFileError, \
171 "can't copy %s:not a regular file" % src
172
173 if os.path.isdir (dst):
174 dir = dst
175 dst = os.path.join (dst, os.path.basename (src))
176 else:
177 dir = os.path.dirname (dst)
178
179 if update and not newer (src, dst):
180 return
181
182 if verbose:
183 print "copying %s -> %s" % (src, dir)
184
185 copyfile (src, dst)
186 if preserve_mode or preserve_times:
187 st = os.stat (src)
188 if preserve_mode:
189 os.chmod (dst, S_IMODE (st[ST_MODE]))
190 if preserve_times:
191 os.utime (dst, (st[ST_ATIME], st[ST_MTIME]))
192
193# copy_file ()
194
195
196def copy_tree (src, dst,
197 preserve_mode=1,
198 preserve_times=1,
199 preserve_symlinks=0,
200 update=0,
201 verbose=0):
202
203 """Copy an entire directory tree 'src' to a new location 'dst'. Both
204 'src' and 'dst' must be directory names. If 'src' is not a
205 directory, raise DistutilsFileError. If 'dst' does not exist, it
206 is created with 'mkpath'. The endresult of the copy is that
207 every file in 'src' is copied to 'dst', and directories under
208 'src' are recursively copied to 'dst'.
209
210 'preserve_mode' and 'preserve_times' are the same as for
211 'copy_file'; note that they only apply to regular files, not to
212 directories. If 'preserve_symlinks' is true, symlinks will be
213 copied as symlinks (on platforms that support them!); otherwise
214 (the default), the destination of the symlink will be copied.
215 'update' and 'verbose' are the same as for 'copy_file'."""
216
217 if not os.path.isdir (src):
218 raise DistutilsFileError, \
219 "cannot copy tree %s: not a directory" % src
220 try:
221 names = os.listdir (src)
222 except os.error, (errno, errstr):
223 raise DistutilsFileError, \
224 "error listing files in %s: %s" % (src, errstr)
225
226
227 mkpath (dst, verbose=verbose)
228
229 for n in names:
230 src_name = os.path.join (src, n)
231 dst_name = os.path.join (dst, n)
232
233 if preserve_symlinks and os.path.islink (src_name):
234 link_dest = os.readlink (src_name)
235 os.symlink (link_dest, dst_name)
236 elif os.path.isdir (src_name):
237 copy_tree (src_name, dst_name,
238 preserve_mode, preserve_times, preserve_symlinks,
239 update, verbose)
240 else:
241 copy_file (src_name, dst_name,
242 preserve_mode, preserve_times,
243 update, verbose)
244
245# copy_tree ()