blob: b3fee35a6cce812ad856d3b84332e73fd1973420 [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 +00006import os
7from distutils.errors import DistutilsFileError
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +00008from distutils import log
Greg Wardaebf7062000-04-04 02:05:59 +00009
10# for generating verbose output in 'copy_file()'
Tarek Ziadé36797272010-07-22 12:50:05 +000011_copy_action = { None: 'copying',
12 'hard': 'hard linking',
13 'sym': 'symbolically linking' }
Greg Wardaebf7062000-04-04 02:05:59 +000014
15
Collin Winter5b7e9d72007-08-30 03:52:21 +000016def _copy_file_contents(src, dst, buffer_size=16*1024):
Tarek Ziadé36797272010-07-22 12:50:05 +000017 """Copy the file 'src' to 'dst'; both must be filenames. Any error
18 opening either file, reading from 'src', or writing to 'dst', raises
19 DistutilsFileError. Data is read/written in chunks of 'buffer_size'
20 bytes (default 16k). No attempt is made to handle anything apart from
21 regular files.
Greg Ward9e3dc4e2000-09-23 00:59:34 +000022 """
Greg Wardaebf7062000-04-04 02:05:59 +000023 # Stolen from shutil module in the standard library, but with
24 # custom error-handling added.
Greg Wardaebf7062000-04-04 02:05:59 +000025 fsrc = None
26 fdst = None
27 try:
28 try:
29 fsrc = open(src, 'rb')
Andrew Svetlovad28c7f2012-12-18 22:02:39 +020030 except OSError as e:
Mark Hammondd12dcae2008-10-05 09:00:28 +000031 raise DistutilsFileError("could not open '%s': %s" % (src, e.strerror))
Fred Drakeb94b8492001-12-06 20:51:35 +000032
Andrew M. Kuchling3b388ec2002-02-01 18:29:34 +000033 if os.path.exists(dst):
34 try:
35 os.unlink(dst)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +020036 except OSError as e:
Collin Winter5b7e9d72007-08-30 03:52:21 +000037 raise DistutilsFileError(
Mark Hammondd12dcae2008-10-05 09:00:28 +000038 "could not delete '%s': %s" % (dst, e.strerror))
Tim Peters182b5ac2004-07-18 06:16:08 +000039
Greg Wardaebf7062000-04-04 02:05:59 +000040 try:
41 fdst = open(dst, 'wb')
Andrew Svetlovad28c7f2012-12-18 22:02:39 +020042 except OSError as e:
Collin Winter5b7e9d72007-08-30 03:52:21 +000043 raise DistutilsFileError(
Mark Hammondd12dcae2008-10-05 09:00:28 +000044 "could not create '%s': %s" % (dst, e.strerror))
Fred Drakeb94b8492001-12-06 20:51:35 +000045
Collin Winter5b7e9d72007-08-30 03:52:21 +000046 while True:
Greg Wardaebf7062000-04-04 02:05:59 +000047 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +000048 buf = fsrc.read(buffer_size)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +020049 except OSError as e:
Collin Winter5b7e9d72007-08-30 03:52:21 +000050 raise DistutilsFileError(
Mark Hammondd12dcae2008-10-05 09:00:28 +000051 "could not read from '%s': %s" % (src, e.strerror))
Fred Drakeb94b8492001-12-06 20:51:35 +000052
Greg Wardaebf7062000-04-04 02:05:59 +000053 if not buf:
54 break
55
56 try:
57 fdst.write(buf)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +020058 except OSError as e:
Collin Winter5b7e9d72007-08-30 03:52:21 +000059 raise DistutilsFileError(
Mark Hammondd12dcae2008-10-05 09:00:28 +000060 "could not write to '%s': %s" % (dst, e.strerror))
Greg Wardaebf7062000-04-04 02:05:59 +000061 finally:
62 if fdst:
63 fdst.close()
64 if fsrc:
65 fsrc.close()
66
Collin Winter5b7e9d72007-08-30 03:52:21 +000067def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0,
Tarek Ziadé70a74eb2009-02-06 00:38:35 +000068 link=None, verbose=1, dry_run=0):
Tarek Ziadé36797272010-07-22 12:50:05 +000069 """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is
70 copied there with the same name; otherwise, it must be a filename. (If
71 the file exists, it will be ruthlessly clobbered.) If 'preserve_mode'
72 is true (the default), the file's mode (type and permission bits, or
73 whatever is analogous on the current platform) is copied. If
74 'preserve_times' is true (the default), the last-modified and
75 last-access times are copied as well. If 'update' is true, 'src' will
76 only be copied if 'dst' does not exist, or if 'dst' does exist but is
77 older than 'src'.
Greg Wardaebf7062000-04-04 02:05:59 +000078
Greg Ward9e3dc4e2000-09-23 00:59:34 +000079 'link' allows you to make hard links (os.link) or symbolic links
80 (os.symlink) instead of copying: set it to "hard" or "sym"; if it is
81 None (the default), files are copied. Don't set 'link' on systems that
82 don't support it: 'copy_file()' doesn't check if hard or symbolic
Antoine Pitroued14c862014-10-30 19:37:07 +010083 linking is available. If hardlink fails, falls back to
84 _copy_file_contents().
Greg Wardaebf7062000-04-04 02:05:59 +000085
Greg Ward9e3dc4e2000-09-23 00:59:34 +000086 Under Mac OS, uses the native file copy function in macostools; on
87 other systems, uses '_copy_file_contents()' to copy file contents.
Greg Wardaebf7062000-04-04 02:05:59 +000088
Greg Ward0d4a8532000-09-30 17:29:35 +000089 Return a tuple (dest_name, copied): 'dest_name' is the actual name of
90 the output file, and 'copied' is true if the file was copied (or would
91 have been copied, if 'dry_run' true).
Greg Ward9e3dc4e2000-09-23 00:59:34 +000092 """
Greg Wardaebf7062000-04-04 02:05:59 +000093 # XXX if the destination file already exists, we clobber it if
94 # copying, but blow up if linking. Hmmm. And I don't know what
95 # macostools.copyfile() does. Should definitely be consistent, and
96 # should probably blow up if destination exists and we would be
97 # changing it (ie. it's not already a hard/soft link to src OR
98 # (not update) and (src newer than dst).
99
Greg Wardaebf7062000-04-04 02:05:59 +0000100 from distutils.dep_util import newer
Greg Warde628a2f2001-07-25 19:48:03 +0000101 from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE
Greg Wardaebf7062000-04-04 02:05:59 +0000102
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000103 if not os.path.isfile(src):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000104 raise DistutilsFileError(
105 "can't copy '%s': doesn't exist or not a regular file" % src)
Greg Wardaebf7062000-04-04 02:05:59 +0000106
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000107 if os.path.isdir(dst):
Greg Wardaebf7062000-04-04 02:05:59 +0000108 dir = dst
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000109 dst = os.path.join(dst, os.path.basename(src))
Greg Wardaebf7062000-04-04 02:05:59 +0000110 else:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000111 dir = os.path.dirname(dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000112
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000113 if update and not newer(src, dst):
Tarek Ziadé35e6fd52009-02-06 00:53:43 +0000114 if verbose >= 1:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000115 log.debug("not copying %s (output up-to-date)", src)
Collin Winter5b7e9d72007-08-30 03:52:21 +0000116 return (dst, 0)
Greg Wardaebf7062000-04-04 02:05:59 +0000117
118 try:
119 action = _copy_action[link]
120 except KeyError:
Collin Winter5b7e9d72007-08-30 03:52:21 +0000121 raise ValueError("invalid value '%s' for 'link' argument" % link)
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000122
Tarek Ziadé35e6fd52009-02-06 00:53:43 +0000123 if verbose >= 1:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000124 if os.path.basename(dst) == os.path.basename(src):
125 log.info("%s %s -> %s", action, src, dir)
126 else:
127 log.info("%s %s -> %s", action, src, dst)
Fred Drakeb94b8492001-12-06 20:51:35 +0000128
Greg Wardaebf7062000-04-04 02:05:59 +0000129 if dry_run:
Greg Ward0d4a8532000-09-30 17:29:35 +0000130 return (dst, 1)
Greg Wardaebf7062000-04-04 02:05:59 +0000131
Greg Wardaebf7062000-04-04 02:05:59 +0000132 # If linking (hard or symbolic), use the appropriate system call
133 # (Unix only, of course, but that's the caller's responsibility)
134 elif link == 'hard':
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000135 if not (os.path.exists(dst) and os.path.samefile(src, dst)):
Antoine Pitroued14c862014-10-30 19:37:07 +0100136 try:
137 os.link(src, dst)
138 return (dst, 1)
139 except OSError:
140 # If hard linking fails, fall back on copying file
141 # (some special filesystems don't support hard linking
142 # even under Unix, see issue #8876).
143 pass
Greg Wardaebf7062000-04-04 02:05:59 +0000144 elif link == 'sym':
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000145 if not (os.path.exists(dst) and os.path.samefile(src, dst)):
146 os.symlink(src, dst)
Antoine Pitroued14c862014-10-30 19:37:07 +0100147 return (dst, 1)
Greg Wardaebf7062000-04-04 02:05:59 +0000148
149 # Otherwise (non-Mac, not linking), copy the file contents and
150 # (optionally) copy the times and mode.
Antoine Pitroued14c862014-10-30 19:37:07 +0100151 _copy_file_contents(src, dst)
152 if preserve_mode or preserve_times:
153 st = os.stat(src)
Greg Wardaebf7062000-04-04 02:05:59 +0000154
Antoine Pitroued14c862014-10-30 19:37:07 +0100155 # According to David Ascher <da@ski.org>, utime() should be done
156 # before chmod() (at least under NT).
157 if preserve_times:
158 os.utime(dst, (st[ST_ATIME], st[ST_MTIME]))
159 if preserve_mode:
160 os.chmod(dst, S_IMODE(st[ST_MODE]))
Greg Wardaebf7062000-04-04 02:05:59 +0000161
Greg Ward0d4a8532000-09-30 17:29:35 +0000162 return (dst, 1)
Greg Wardaebf7062000-04-04 02:05:59 +0000163
Greg Wardaebf7062000-04-04 02:05:59 +0000164
165# XXX I suspect this is Unix-specific -- need porting help!
Tarek Ziadé36797272010-07-22 12:50:05 +0000166def move_file (src, dst,
167 verbose=1,
168 dry_run=0):
Greg Wardaebf7062000-04-04 02:05:59 +0000169
Tarek Ziadé36797272010-07-22 12:50:05 +0000170 """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will
171 be moved into it with the same name; otherwise, 'src' is just renamed
172 to 'dst'. Return the new full name of the file.
Greg Wardaebf7062000-04-04 02:05:59 +0000173
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000174 Handles cross-device moves on Unix using 'copy_file()'. What about
175 other systems???
176 """
Greg Wardaebf7062000-04-04 02:05:59 +0000177 from os.path import exists, isfile, isdir, basename, dirname
Andrew M. Kuchling106ffdb2001-08-09 20:59:53 +0000178 import errno
Fred Drakeb94b8492001-12-06 20:51:35 +0000179
Tarek Ziadé35e6fd52009-02-06 00:53:43 +0000180 if verbose >= 1:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000181 log.info("moving %s -> %s", src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000182
183 if dry_run:
184 return dst
185
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000186 if not isfile(src):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000187 raise DistutilsFileError("can't move '%s': not a regular file" % src)
Greg Wardaebf7062000-04-04 02:05:59 +0000188
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000189 if isdir(dst):
190 dst = os.path.join(dst, basename(src))
191 elif exists(dst):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000192 raise DistutilsFileError(
193 "can't move '%s': destination '%s' already exists" %
194 (src, dst))
Greg Wardaebf7062000-04-04 02:05:59 +0000195
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000196 if not isdir(dirname(dst)):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000197 raise DistutilsFileError(
198 "can't move '%s': destination '%s' not a valid path" %
199 (src, dst))
Greg Wardaebf7062000-04-04 02:05:59 +0000200
Collin Winter5b7e9d72007-08-30 03:52:21 +0000201 copy_it = False
Greg Wardaebf7062000-04-04 02:05:59 +0000202 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000203 os.rename(src, dst)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +0200204 except OSError as e:
Berker Peksag66858832014-08-29 07:07:35 +0300205 (num, msg) = e.args
Greg Wardaebf7062000-04-04 02:05:59 +0000206 if num == errno.EXDEV:
Collin Winter5b7e9d72007-08-30 03:52:21 +0000207 copy_it = True
Greg Wardaebf7062000-04-04 02:05:59 +0000208 else:
Collin Winter5b7e9d72007-08-30 03:52:21 +0000209 raise DistutilsFileError(
210 "couldn't move '%s' to '%s': %s" % (src, dst, msg))
Greg Wardaebf7062000-04-04 02:05:59 +0000211
212 if copy_it:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000213 copy_file(src, dst, verbose=verbose)
Greg Wardaebf7062000-04-04 02:05:59 +0000214 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000215 os.unlink(src)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +0200216 except OSError as e:
Berker Peksag66858832014-08-29 07:07:35 +0300217 (num, msg) = e.args
Greg Wardaebf7062000-04-04 02:05:59 +0000218 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000219 os.unlink(dst)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +0200220 except OSError:
Greg Wardaebf7062000-04-04 02:05:59 +0000221 pass
Collin Winter5b7e9d72007-08-30 03:52:21 +0000222 raise DistutilsFileError(
223 "couldn't move '%s' to '%s' by copy/delete: "
224 "delete '%s' failed: %s"
225 % (src, dst, src, msg))
Greg Wardaebf7062000-04-04 02:05:59 +0000226 return dst
227
Greg Wardaebf7062000-04-04 02:05:59 +0000228
Tarek Ziadé36797272010-07-22 12:50:05 +0000229def write_file (filename, contents):
Greg Wardaebf7062000-04-04 02:05:59 +0000230 """Create a file with the specified name and write 'contents' (a
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000231 sequence of strings without line terminators) to it.
232 """
233 f = open(filename, "w")
Éric Araujobee5cef2010-11-05 23:51:56 +0000234 try:
235 for line in contents:
236 f.write(line + "\n")
237 finally:
238 f.close()