blob: 65aa7e0fdd262c2f720b2389708b0c2f448a863a [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
Tarek Ziadé36797272010-07-22 12:50:05 +0000133 # On Mac OS, use the native file copy routine
134 if os.name == 'mac':
135 import macostools
136 try:
137 macostools.copy(src, dst, 0, preserve_times)
138 except os.error as exc:
139 raise DistutilsFileError(
140 "could not copy '%s' to '%s': %s" % (src, dst, exc.args[-1]))
141
Greg Wardaebf7062000-04-04 02:05:59 +0000142 # If linking (hard or symbolic), use the appropriate system call
143 # (Unix only, of course, but that's the caller's responsibility)
144 elif link == 'hard':
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000145 if not (os.path.exists(dst) and os.path.samefile(src, dst)):
146 os.link(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000147 elif link == 'sym':
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000148 if not (os.path.exists(dst) and os.path.samefile(src, dst)):
149 os.symlink(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000150
151 # Otherwise (non-Mac, not linking), copy the file contents and
152 # (optionally) copy the times and mode.
153 else:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000154 _copy_file_contents(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000155 if preserve_mode or preserve_times:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000156 st = os.stat(src)
Greg Wardaebf7062000-04-04 02:05:59 +0000157
158 # According to David Ascher <da@ski.org>, utime() should be done
159 # before chmod() (at least under NT).
160 if preserve_times:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000161 os.utime(dst, (st[ST_ATIME], st[ST_MTIME]))
Greg Wardaebf7062000-04-04 02:05:59 +0000162 if preserve_mode:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000163 os.chmod(dst, S_IMODE(st[ST_MODE]))
Greg Wardaebf7062000-04-04 02:05:59 +0000164
Greg Ward0d4a8532000-09-30 17:29:35 +0000165 return (dst, 1)
Greg Wardaebf7062000-04-04 02:05:59 +0000166
Greg Wardaebf7062000-04-04 02:05:59 +0000167
168# XXX I suspect this is Unix-specific -- need porting help!
Tarek Ziadé36797272010-07-22 12:50:05 +0000169def move_file (src, dst,
170 verbose=1,
171 dry_run=0):
Greg Wardaebf7062000-04-04 02:05:59 +0000172
Tarek Ziadé36797272010-07-22 12:50:05 +0000173 """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will
174 be moved into it with the same name; otherwise, 'src' is just renamed
175 to 'dst'. Return the new 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é35e6fd52009-02-06 00:53:43 +0000183 if verbose >= 1:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +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):
Collin Winter5b7e9d72007-08-30 03:52:21 +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):
Collin Winter5b7e9d72007-08-30 03:52:21 +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)):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000200 raise DistutilsFileError(
201 "can't move '%s': destination '%s' not a valid path" %
202 (src, dst))
Greg Wardaebf7062000-04-04 02:05:59 +0000203
Collin Winter5b7e9d72007-08-30 03:52:21 +0000204 copy_it = False
Greg Wardaebf7062000-04-04 02:05:59 +0000205 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000206 os.rename(src, dst)
Guido van Rossumb940e112007-01-10 16:19:56 +0000207 except os.error as e:
208 (num, msg) = e
Greg Wardaebf7062000-04-04 02:05:59 +0000209 if num == errno.EXDEV:
Collin Winter5b7e9d72007-08-30 03:52:21 +0000210 copy_it = True
Greg Wardaebf7062000-04-04 02:05:59 +0000211 else:
Collin Winter5b7e9d72007-08-30 03:52:21 +0000212 raise DistutilsFileError(
213 "couldn't move '%s' to '%s': %s" % (src, dst, msg))
Greg Wardaebf7062000-04-04 02:05:59 +0000214
215 if copy_it:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000216 copy_file(src, dst, verbose=verbose)
Greg Wardaebf7062000-04-04 02:05:59 +0000217 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000218 os.unlink(src)
Guido van Rossumb940e112007-01-10 16:19:56 +0000219 except os.error as e:
220 (num, msg) = e
Greg Wardaebf7062000-04-04 02:05:59 +0000221 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000222 os.unlink(dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000223 except os.error:
224 pass
Collin Winter5b7e9d72007-08-30 03:52:21 +0000225 raise DistutilsFileError(
226 "couldn't move '%s' to '%s' by copy/delete: "
227 "delete '%s' failed: %s"
228 % (src, dst, src, msg))
Greg Wardaebf7062000-04-04 02:05:59 +0000229 return dst
230
Greg Wardaebf7062000-04-04 02:05:59 +0000231
Tarek Ziadé36797272010-07-22 12:50:05 +0000232def write_file (filename, contents):
Greg Wardaebf7062000-04-04 02:05:59 +0000233 """Create a file with the specified name and write 'contents' (a
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000234 sequence of strings without line terminators) to it.
235 """
236 f = open(filename, "w")
Greg Wardaebf7062000-04-04 02:05:59 +0000237 for line in contents:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000238 f.write(line + "\n")
239 f.close()