blob: 3b236e1326ba1c50bab7b72818e3ae37296f5ba7 [file] [log] [blame]
Greg Wardaebf7062000-04-04 02:05:59 +00001"""distutils.file_util
2
Greg Ward449f5562000-09-26 02:03:34 +00003Utility functions for operating on single files.
4"""
Greg Wardaebf7062000-04-04 02:05:59 +00005
Greg Wardaebf7062000-04-04 02:05:59 +00006__revision__ = "$Id$"
7
8import os
9from distutils.errors import DistutilsFileError
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +000010from distutils import log
Greg Wardaebf7062000-04-04 02:05:59 +000011
12# for generating verbose output in 'copy_file()'
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +000013_copy_action = {None: 'copying',
14 'hard': 'hard linking',
15 'sym': 'symbolically linking'}
Greg Wardaebf7062000-04-04 02:05:59 +000016
17
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +000018def _copy_file_contents(src, dst, buffer_size=16*1024):
19 """Copy the file 'src' to 'dst'.
20
21 Both must be filenames. Any error opening either file, reading from
22 'src', or writing to 'dst', raises DistutilsFileError. Data is
23 read/written in chunks of 'buffer_size' bytes (default 16k). No attempt
24 is made to handle anything apart from regular files.
Greg Ward9e3dc4e2000-09-23 00:59:34 +000025 """
Greg Wardaebf7062000-04-04 02:05:59 +000026 # Stolen from shutil module in the standard library, but with
27 # custom error-handling added.
Greg Wardaebf7062000-04-04 02:05:59 +000028 fsrc = None
29 fdst = None
30 try:
31 try:
32 fsrc = open(src, 'rb')
33 except os.error, (errno, errstr):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +000034 raise DistutilsFileError("could not open '%s': %s" % (src, errstr))
Fred Drakeb94b8492001-12-06 20:51:35 +000035
Andrew M. Kuchling3b388ec2002-02-01 18:29:34 +000036 if os.path.exists(dst):
37 try:
38 os.unlink(dst)
39 except os.error, (errno, errstr):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +000040 raise DistutilsFileError(
41 "could not delete '%s': %s" % (dst, errstr))
Tim Peters182b5ac2004-07-18 06:16:08 +000042
Greg Wardaebf7062000-04-04 02:05:59 +000043 try:
44 fdst = open(dst, 'wb')
45 except os.error, (errno, errstr):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +000046 raise DistutilsFileError(
47 "could not create '%s': %s" % (dst, errstr))
Fred Drakeb94b8492001-12-06 20:51:35 +000048
Greg Wardaebf7062000-04-04 02:05:59 +000049 while 1:
50 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +000051 buf = fsrc.read(buffer_size)
Greg Wardaebf7062000-04-04 02:05:59 +000052 except os.error, (errno, errstr):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +000053 raise DistutilsFileError(
54 "could not read from '%s': %s" % (src, errstr))
Fred Drakeb94b8492001-12-06 20:51:35 +000055
Greg Wardaebf7062000-04-04 02:05:59 +000056 if not buf:
57 break
58
59 try:
60 fdst.write(buf)
61 except os.error, (errno, errstr):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +000062 raise DistutilsFileError(
63 "could not write to '%s': %s" % (dst, errstr))
Fred Drakeb94b8492001-12-06 20:51:35 +000064
Greg Wardaebf7062000-04-04 02:05:59 +000065 finally:
66 if fdst:
67 fdst.close()
68 if fsrc:
69 fsrc.close()
70
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +000071def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0,
72 link=None, verbose=1, dry_run=0):
73 """Copy a file 'src' to 'dst'.
Greg Wardaebf7062000-04-04 02:05:59 +000074
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +000075 If 'dst' is a directory, then 'src' is copied there with the same name;
76 otherwise, it must be a filename. (If the file exists, it will be
77 ruthlessly clobbered.) If 'preserve_mode' is true (the default),
78 the file's mode (type and permission bits, or whatever is analogous on
79 the current platform) is copied. If 'preserve_times' is true (the
80 default), the last-modified and last-access times are copied as well.
81 If 'update' is true, 'src' will only be copied if 'dst' does not exist,
82 or if 'dst' does exist but is older than 'src'.
Greg Wardaebf7062000-04-04 02:05:59 +000083
Greg Ward9e3dc4e2000-09-23 00:59:34 +000084 'link' allows you to make hard links (os.link) or symbolic links
85 (os.symlink) instead of copying: set it to "hard" or "sym"; if it is
86 None (the default), files are copied. Don't set 'link' on systems that
87 don't support it: 'copy_file()' doesn't check if hard or symbolic
Antoine Pitrou78be2f42014-10-30 19:37:07 +010088 linking is available. If hardlink fails, falls back to
89 _copy_file_contents().
Greg Wardaebf7062000-04-04 02:05:59 +000090
Greg Ward9e3dc4e2000-09-23 00:59:34 +000091 Under Mac OS, uses the native file copy function in macostools; on
92 other systems, uses '_copy_file_contents()' to copy file contents.
Greg Wardaebf7062000-04-04 02:05:59 +000093
Greg Ward0d4a8532000-09-30 17:29:35 +000094 Return a tuple (dest_name, copied): 'dest_name' is the actual name of
95 the output file, and 'copied' is true if the file was copied (or would
96 have been copied, if 'dry_run' true).
Greg Ward9e3dc4e2000-09-23 00:59:34 +000097 """
Greg Wardaebf7062000-04-04 02:05:59 +000098 # XXX if the destination file already exists, we clobber it if
99 # copying, but blow up if linking. Hmmm. And I don't know what
100 # macostools.copyfile() does. Should definitely be consistent, and
101 # should probably blow up if destination exists and we would be
102 # changing it (ie. it's not already a hard/soft link to src OR
103 # (not update) and (src newer than dst).
104
Greg Wardaebf7062000-04-04 02:05:59 +0000105 from distutils.dep_util import newer
Greg Warde628a2f2001-07-25 19:48:03 +0000106 from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE
Greg Wardaebf7062000-04-04 02:05:59 +0000107
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000108 if not os.path.isfile(src):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +0000109 raise DistutilsFileError(
110 "can't copy '%s': doesn't exist or not a regular file" % src)
Greg Wardaebf7062000-04-04 02:05:59 +0000111
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000112 if os.path.isdir(dst):
Greg Wardaebf7062000-04-04 02:05:59 +0000113 dir = dst
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000114 dst = os.path.join(dst, os.path.basename(src))
Greg Wardaebf7062000-04-04 02:05:59 +0000115 else:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000116 dir = os.path.dirname(dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000117
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000118 if update and not newer(src, dst):
Tarek Ziadéaaf2e182009-02-06 00:49:45 +0000119 if verbose >= 1:
Tarek Ziadéd5eb9852009-02-06 00:31:59 +0000120 log.debug("not copying %s (output up-to-date)", src)
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +0000121 return dst, 0
Greg Wardaebf7062000-04-04 02:05:59 +0000122
123 try:
124 action = _copy_action[link]
125 except KeyError:
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +0000126 raise ValueError("invalid value '%s' for 'link' argument" % link)
Tarek Ziadéd5eb9852009-02-06 00:31:59 +0000127
Tarek Ziadéaaf2e182009-02-06 00:49:45 +0000128 if verbose >= 1:
Tarek Ziadéd5eb9852009-02-06 00:31:59 +0000129 if os.path.basename(dst) == os.path.basename(src):
130 log.info("%s %s -> %s", action, src, dir)
131 else:
132 log.info("%s %s -> %s", action, src, dst)
Fred Drakeb94b8492001-12-06 20:51:35 +0000133
Greg Wardaebf7062000-04-04 02:05:59 +0000134 if dry_run:
Greg Ward0d4a8532000-09-30 17:29:35 +0000135 return (dst, 1)
Greg Wardaebf7062000-04-04 02:05:59 +0000136
Greg Wardaebf7062000-04-04 02:05:59 +0000137 # If linking (hard or symbolic), use the appropriate system call
138 # (Unix only, of course, but that's the caller's responsibility)
Ronald Oussoren9545a232010-05-05 19:09:31 +0000139 if link == 'hard':
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000140 if not (os.path.exists(dst) and os.path.samefile(src, dst)):
Antoine Pitrou78be2f42014-10-30 19:37:07 +0100141 try:
142 os.link(src, dst)
143 return (dst, 1)
144 except OSError:
145 # If hard linking fails, fall back on copying file
146 # (some special filesystems don't support hard linking
147 # even under Unix, see issue #8876).
148 pass
Greg Wardaebf7062000-04-04 02:05:59 +0000149 elif link == 'sym':
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000150 if not (os.path.exists(dst) and os.path.samefile(src, dst)):
151 os.symlink(src, dst)
Antoine Pitrou78be2f42014-10-30 19:37:07 +0100152 return (dst, 1)
Greg Wardaebf7062000-04-04 02:05:59 +0000153
154 # Otherwise (non-Mac, not linking), copy the file contents and
155 # (optionally) copy the times and mode.
Antoine Pitrou78be2f42014-10-30 19:37:07 +0100156 _copy_file_contents(src, dst)
157 if preserve_mode or preserve_times:
158 st = os.stat(src)
Greg Wardaebf7062000-04-04 02:05:59 +0000159
Antoine Pitrou78be2f42014-10-30 19:37:07 +0100160 # According to David Ascher <da@ski.org>, utime() should be done
161 # before chmod() (at least under NT).
162 if preserve_times:
163 os.utime(dst, (st[ST_ATIME], st[ST_MTIME]))
164 if preserve_mode:
165 os.chmod(dst, S_IMODE(st[ST_MODE]))
Greg Wardaebf7062000-04-04 02:05:59 +0000166
Greg Ward0d4a8532000-09-30 17:29:35 +0000167 return (dst, 1)
Greg Wardaebf7062000-04-04 02:05:59 +0000168
Greg Wardaebf7062000-04-04 02:05:59 +0000169# XXX I suspect this is Unix-specific -- need porting help!
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +0000170def move_file (src, dst, verbose=1, dry_run=0):
171 """Move a file 'src' to 'dst'.
Greg Wardaebf7062000-04-04 02:05:59 +0000172
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +0000173 If 'dst' is a directory, the file will be moved into it with the same
174 name; otherwise, 'src' is just renamed to 'dst'. Return the new
175 full name of the file.
Greg Wardaebf7062000-04-04 02:05:59 +0000176
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000177 Handles cross-device moves on Unix using 'copy_file()'. What about
178 other systems???
179 """
Greg Wardaebf7062000-04-04 02:05:59 +0000180 from os.path import exists, isfile, isdir, basename, dirname
Andrew M. Kuchling106ffdb2001-08-09 20:59:53 +0000181 import errno
Fred Drakeb94b8492001-12-06 20:51:35 +0000182
Tarek Ziadéaaf2e182009-02-06 00:49:45 +0000183 if verbose >= 1:
Tarek Ziadéd5eb9852009-02-06 00:31:59 +0000184 log.info("moving %s -> %s", src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000185
186 if dry_run:
187 return dst
188
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000189 if not isfile(src):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +0000190 raise DistutilsFileError("can't move '%s': not a regular file" % src)
Greg Wardaebf7062000-04-04 02:05:59 +0000191
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000192 if isdir(dst):
193 dst = os.path.join(dst, basename(src))
194 elif exists(dst):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +0000195 raise DistutilsFileError(
196 "can't move '%s': destination '%s' already exists" %
197 (src, dst))
Greg Wardaebf7062000-04-04 02:05:59 +0000198
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000199 if not isdir(dirname(dst)):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +0000200 raise DistutilsFileError(
Greg Wardaebf7062000-04-04 02:05:59 +0000201 "can't move '%s': destination '%s' not a valid path" % \
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +0000202 (src, dst))
Greg Wardaebf7062000-04-04 02:05:59 +0000203
204 copy_it = 0
205 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000206 os.rename(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000207 except os.error, (num, msg):
208 if num == errno.EXDEV:
209 copy_it = 1
210 else:
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +0000211 raise DistutilsFileError(
212 "couldn't move '%s' to '%s': %s" % (src, dst, msg))
Greg Wardaebf7062000-04-04 02:05:59 +0000213
214 if copy_it:
Tarek Ziadéd5eb9852009-02-06 00:31:59 +0000215 copy_file(src, dst, verbose=verbose)
Greg Wardaebf7062000-04-04 02:05:59 +0000216 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000217 os.unlink(src)
Greg Wardaebf7062000-04-04 02:05:59 +0000218 except os.error, (num, msg):
219 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000220 os.unlink(dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000221 except os.error:
222 pass
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +0000223 raise DistutilsFileError(
Fred Drakeb94b8492001-12-06 20:51:35 +0000224 ("couldn't move '%s' to '%s' by copy/delete: " +
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +0000225 "delete '%s' failed: %s") %
226 (src, dst, src, msg))
Greg Wardaebf7062000-04-04 02:05:59 +0000227 return dst
228
Greg Wardaebf7062000-04-04 02:05:59 +0000229
230def write_file (filename, contents):
231 """Create a file with the specified name and write 'contents' (a
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000232 sequence of strings without line terminators) to it.
233 """
234 f = open(filename, "w")
Éric Araujod1feff72010-11-06 04:06:18 +0000235 try:
236 for line in contents:
237 f.write(line + "\n")
238 finally:
239 f.close()