blob: e1eb93292677dec187c2818bc3462f1d674c6658 [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é36797272010-07-22 12:50:05 +000013_copy_action = { None: 'copying',
14 'hard': 'hard linking',
15 'sym': 'symbolically linking' }
Greg Wardaebf7062000-04-04 02:05:59 +000016
17
Collin Winter5b7e9d72007-08-30 03:52:21 +000018def _copy_file_contents(src, dst, buffer_size=16*1024):
Tarek Ziadé36797272010-07-22 12:50:05 +000019 """Copy the file 'src' to 'dst'; both must be filenames. Any error
20 opening either file, reading from 'src', or writing to 'dst', raises
21 DistutilsFileError. Data is read/written in chunks of 'buffer_size'
22 bytes (default 16k). No attempt is made to handle anything apart from
23 regular files.
Greg Ward9e3dc4e2000-09-23 00:59:34 +000024 """
Greg Wardaebf7062000-04-04 02:05:59 +000025 # Stolen from shutil module in the standard library, but with
26 # custom error-handling added.
Greg Wardaebf7062000-04-04 02:05:59 +000027 fsrc = None
28 fdst = None
29 try:
30 try:
31 fsrc = open(src, 'rb')
Guido van Rossumb940e112007-01-10 16:19:56 +000032 except os.error as e:
Mark Hammondd12dcae2008-10-05 09:00:28 +000033 raise DistutilsFileError("could not open '%s': %s" % (src, e.strerror))
Fred Drakeb94b8492001-12-06 20:51:35 +000034
Andrew M. Kuchling3b388ec2002-02-01 18:29:34 +000035 if os.path.exists(dst):
36 try:
37 os.unlink(dst)
Guido van Rossumb940e112007-01-10 16:19:56 +000038 except os.error as e:
Collin Winter5b7e9d72007-08-30 03:52:21 +000039 raise DistutilsFileError(
Mark Hammondd12dcae2008-10-05 09:00:28 +000040 "could not delete '%s': %s" % (dst, e.strerror))
Tim Peters182b5ac2004-07-18 06:16:08 +000041
Greg Wardaebf7062000-04-04 02:05:59 +000042 try:
43 fdst = open(dst, 'wb')
Guido van Rossumb940e112007-01-10 16:19:56 +000044 except os.error as e:
Collin Winter5b7e9d72007-08-30 03:52:21 +000045 raise DistutilsFileError(
Mark Hammondd12dcae2008-10-05 09:00:28 +000046 "could not create '%s': %s" % (dst, e.strerror))
Fred Drakeb94b8492001-12-06 20:51:35 +000047
Collin Winter5b7e9d72007-08-30 03:52:21 +000048 while True:
Greg Wardaebf7062000-04-04 02:05:59 +000049 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +000050 buf = fsrc.read(buffer_size)
Guido van Rossumb940e112007-01-10 16:19:56 +000051 except os.error as e:
Collin Winter5b7e9d72007-08-30 03:52:21 +000052 raise DistutilsFileError(
Mark Hammondd12dcae2008-10-05 09:00:28 +000053 "could not read from '%s': %s" % (src, e.strerror))
Fred Drakeb94b8492001-12-06 20:51:35 +000054
Greg Wardaebf7062000-04-04 02:05:59 +000055 if not buf:
56 break
57
58 try:
59 fdst.write(buf)
Guido van Rossumb940e112007-01-10 16:19:56 +000060 except os.error as e:
Collin Winter5b7e9d72007-08-30 03:52:21 +000061 raise DistutilsFileError(
Mark Hammondd12dcae2008-10-05 09:00:28 +000062 "could not write to '%s': %s" % (dst, e.strerror))
Greg Wardaebf7062000-04-04 02:05:59 +000063 finally:
64 if fdst:
65 fdst.close()
66 if fsrc:
67 fsrc.close()
68
Collin Winter5b7e9d72007-08-30 03:52:21 +000069def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0,
Tarek Ziadé70a74eb2009-02-06 00:38:35 +000070 link=None, verbose=1, dry_run=0):
Tarek Ziadé36797272010-07-22 12:50:05 +000071 """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is
72 copied there with the same name; otherwise, it must be a filename. (If
73 the file exists, it will be ruthlessly clobbered.) If 'preserve_mode'
74 is true (the default), the file's mode (type and permission bits, or
75 whatever is analogous on the current platform) is copied. If
76 'preserve_times' is true (the default), the last-modified and
77 last-access times are copied as well. If 'update' is true, 'src' will
78 only be copied if 'dst' does not exist, or if 'dst' does exist but is
79 older than 'src'.
Greg Wardaebf7062000-04-04 02:05:59 +000080
Greg Ward9e3dc4e2000-09-23 00:59:34 +000081 'link' allows you to make hard links (os.link) or symbolic links
82 (os.symlink) instead of copying: set it to "hard" or "sym"; if it is
83 None (the default), files are copied. Don't set 'link' on systems that
84 don't support it: 'copy_file()' doesn't check if hard or symbolic
85 linking is available.
Greg Wardaebf7062000-04-04 02:05:59 +000086
Greg Ward9e3dc4e2000-09-23 00:59:34 +000087 Under Mac OS, uses the native file copy function in macostools; on
88 other systems, uses '_copy_file_contents()' to copy file contents.
Greg Wardaebf7062000-04-04 02:05:59 +000089
Greg Ward0d4a8532000-09-30 17:29:35 +000090 Return a tuple (dest_name, copied): 'dest_name' is the actual name of
91 the output file, and 'copied' is true if the file was copied (or would
92 have been copied, if 'dry_run' true).
Greg Ward9e3dc4e2000-09-23 00:59:34 +000093 """
Greg Wardaebf7062000-04-04 02:05:59 +000094 # XXX if the destination file already exists, we clobber it if
95 # copying, but blow up if linking. Hmmm. And I don't know what
96 # macostools.copyfile() does. Should definitely be consistent, and
97 # should probably blow up if destination exists and we would be
98 # changing it (ie. it's not already a hard/soft link to src OR
99 # (not update) and (src newer than dst).
100
Greg Wardaebf7062000-04-04 02:05:59 +0000101 from distutils.dep_util import newer
Greg Warde628a2f2001-07-25 19:48:03 +0000102 from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE
Greg Wardaebf7062000-04-04 02:05:59 +0000103
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000104 if not os.path.isfile(src):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000105 raise DistutilsFileError(
106 "can't copy '%s': doesn't exist or not a regular file" % src)
Greg Wardaebf7062000-04-04 02:05:59 +0000107
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000108 if os.path.isdir(dst):
Greg Wardaebf7062000-04-04 02:05:59 +0000109 dir = dst
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000110 dst = os.path.join(dst, os.path.basename(src))
Greg Wardaebf7062000-04-04 02:05:59 +0000111 else:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000112 dir = os.path.dirname(dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000113
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000114 if update and not newer(src, dst):
Tarek Ziadé35e6fd52009-02-06 00:53:43 +0000115 if verbose >= 1:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000116 log.debug("not copying %s (output up-to-date)", src)
Collin Winter5b7e9d72007-08-30 03:52:21 +0000117 return (dst, 0)
Greg Wardaebf7062000-04-04 02:05:59 +0000118
119 try:
120 action = _copy_action[link]
121 except KeyError:
Collin Winter5b7e9d72007-08-30 03:52:21 +0000122 raise ValueError("invalid value '%s' for 'link' argument" % link)
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000123
Tarek Ziadé35e6fd52009-02-06 00:53:43 +0000124 if verbose >= 1:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000125 if os.path.basename(dst) == os.path.basename(src):
126 log.info("%s %s -> %s", action, src, dir)
127 else:
128 log.info("%s %s -> %s", action, src, dst)
Fred Drakeb94b8492001-12-06 20:51:35 +0000129
Greg Wardaebf7062000-04-04 02:05:59 +0000130 if dry_run:
Greg Ward0d4a8532000-09-30 17:29:35 +0000131 return (dst, 1)
Greg Wardaebf7062000-04-04 02:05:59 +0000132
Greg Wardaebf7062000-04-04 02:05:59 +0000133 # If linking (hard or symbolic), use the appropriate system call
134 # (Unix only, of course, but that's the caller's responsibility)
135 elif link == 'hard':
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000136 if not (os.path.exists(dst) and os.path.samefile(src, dst)):
137 os.link(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000138 elif link == 'sym':
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000139 if not (os.path.exists(dst) and os.path.samefile(src, dst)):
140 os.symlink(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000141
142 # Otherwise (non-Mac, not linking), copy the file contents and
143 # (optionally) copy the times and mode.
144 else:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000145 _copy_file_contents(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000146 if preserve_mode or preserve_times:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000147 st = os.stat(src)
Greg Wardaebf7062000-04-04 02:05:59 +0000148
149 # According to David Ascher <da@ski.org>, utime() should be done
150 # before chmod() (at least under NT).
151 if preserve_times:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000152 os.utime(dst, (st[ST_ATIME], st[ST_MTIME]))
Greg Wardaebf7062000-04-04 02:05:59 +0000153 if preserve_mode:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000154 os.chmod(dst, S_IMODE(st[ST_MODE]))
Greg Wardaebf7062000-04-04 02:05:59 +0000155
Greg Ward0d4a8532000-09-30 17:29:35 +0000156 return (dst, 1)
Greg Wardaebf7062000-04-04 02:05:59 +0000157
Greg Wardaebf7062000-04-04 02:05:59 +0000158
159# XXX I suspect this is Unix-specific -- need porting help!
Tarek Ziadé36797272010-07-22 12:50:05 +0000160def move_file (src, dst,
161 verbose=1,
162 dry_run=0):
Greg Wardaebf7062000-04-04 02:05:59 +0000163
Tarek Ziadé36797272010-07-22 12:50:05 +0000164 """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will
165 be moved into it with the same name; otherwise, 'src' is just renamed
166 to 'dst'. Return the new full name of the file.
Greg Wardaebf7062000-04-04 02:05:59 +0000167
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000168 Handles cross-device moves on Unix using 'copy_file()'. What about
169 other systems???
170 """
Greg Wardaebf7062000-04-04 02:05:59 +0000171 from os.path import exists, isfile, isdir, basename, dirname
Andrew M. Kuchling106ffdb2001-08-09 20:59:53 +0000172 import errno
Fred Drakeb94b8492001-12-06 20:51:35 +0000173
Tarek Ziadé35e6fd52009-02-06 00:53:43 +0000174 if verbose >= 1:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000175 log.info("moving %s -> %s", src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000176
177 if dry_run:
178 return dst
179
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000180 if not isfile(src):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000181 raise DistutilsFileError("can't move '%s': not a regular file" % src)
Greg Wardaebf7062000-04-04 02:05:59 +0000182
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000183 if isdir(dst):
184 dst = os.path.join(dst, basename(src))
185 elif exists(dst):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000186 raise DistutilsFileError(
187 "can't move '%s': destination '%s' already exists" %
188 (src, dst))
Greg Wardaebf7062000-04-04 02:05:59 +0000189
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000190 if not isdir(dirname(dst)):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000191 raise DistutilsFileError(
192 "can't move '%s': destination '%s' not a valid path" %
193 (src, dst))
Greg Wardaebf7062000-04-04 02:05:59 +0000194
Collin Winter5b7e9d72007-08-30 03:52:21 +0000195 copy_it = False
Greg Wardaebf7062000-04-04 02:05:59 +0000196 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000197 os.rename(src, dst)
Guido van Rossumb940e112007-01-10 16:19:56 +0000198 except os.error as e:
199 (num, msg) = e
Greg Wardaebf7062000-04-04 02:05:59 +0000200 if num == errno.EXDEV:
Collin Winter5b7e9d72007-08-30 03:52:21 +0000201 copy_it = True
Greg Wardaebf7062000-04-04 02:05:59 +0000202 else:
Collin Winter5b7e9d72007-08-30 03:52:21 +0000203 raise DistutilsFileError(
204 "couldn't move '%s' to '%s': %s" % (src, dst, msg))
Greg Wardaebf7062000-04-04 02:05:59 +0000205
206 if copy_it:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000207 copy_file(src, dst, verbose=verbose)
Greg Wardaebf7062000-04-04 02:05:59 +0000208 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000209 os.unlink(src)
Guido van Rossumb940e112007-01-10 16:19:56 +0000210 except os.error as e:
211 (num, msg) = e
Greg Wardaebf7062000-04-04 02:05:59 +0000212 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000213 os.unlink(dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000214 except os.error:
215 pass
Collin Winter5b7e9d72007-08-30 03:52:21 +0000216 raise DistutilsFileError(
217 "couldn't move '%s' to '%s' by copy/delete: "
218 "delete '%s' failed: %s"
219 % (src, dst, src, msg))
Greg Wardaebf7062000-04-04 02:05:59 +0000220 return dst
221
Greg Wardaebf7062000-04-04 02:05:59 +0000222
Tarek Ziadé36797272010-07-22 12:50:05 +0000223def write_file (filename, contents):
Greg Wardaebf7062000-04-04 02:05:59 +0000224 """Create a file with the specified name and write 'contents' (a
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000225 sequence of strings without line terminators) to it.
226 """
227 f = open(filename, "w")
Éric Araujobee5cef2010-11-05 23:51:56 +0000228 try:
229 for line in contents:
230 f.write(line + "\n")
231 finally:
232 f.close()