blob: 3a71bfd1ac27c622549e9776cd8f4ab2cc6eed92 [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éc2b71882009-07-03 19:22:23 +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éc2b71882009-07-03 19:22:23 +000019 """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')
Guido van Rossumb940e112007-01-10 16:19:56 +000033 except os.error as e:
Mark Hammondd12dcae2008-10-05 09:00:28 +000034 raise DistutilsFileError("could not open '%s': %s" % (src, e.strerror))
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)
Guido van Rossumb940e112007-01-10 16:19:56 +000039 except os.error as e:
Collin Winter5b7e9d72007-08-30 03:52:21 +000040 raise DistutilsFileError(
Mark Hammondd12dcae2008-10-05 09:00:28 +000041 "could not delete '%s': %s" % (dst, e.strerror))
Tim Peters182b5ac2004-07-18 06:16:08 +000042
Greg Wardaebf7062000-04-04 02:05:59 +000043 try:
44 fdst = open(dst, 'wb')
Guido van Rossumb940e112007-01-10 16:19:56 +000045 except os.error as e:
Collin Winter5b7e9d72007-08-30 03:52:21 +000046 raise DistutilsFileError(
Mark Hammondd12dcae2008-10-05 09:00:28 +000047 "could not create '%s': %s" % (dst, e.strerror))
Fred Drakeb94b8492001-12-06 20:51:35 +000048
Collin Winter5b7e9d72007-08-30 03:52:21 +000049 while True:
Greg Wardaebf7062000-04-04 02:05:59 +000050 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +000051 buf = fsrc.read(buffer_size)
Guido van Rossumb940e112007-01-10 16:19:56 +000052 except os.error as e:
Collin Winter5b7e9d72007-08-30 03:52:21 +000053 raise DistutilsFileError(
Mark Hammondd12dcae2008-10-05 09:00:28 +000054 "could not read from '%s': %s" % (src, e.strerror))
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)
Guido van Rossumb940e112007-01-10 16:19:56 +000061 except os.error as e:
Collin Winter5b7e9d72007-08-30 03:52:21 +000062 raise DistutilsFileError(
Mark Hammondd12dcae2008-10-05 09:00:28 +000063 "could not write to '%s': %s" % (dst, e.strerror))
Greg Wardaebf7062000-04-04 02:05:59 +000064 finally:
65 if fdst:
66 fdst.close()
67 if fsrc:
68 fsrc.close()
69
Collin Winter5b7e9d72007-08-30 03:52:21 +000070def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0,
Tarek Ziadé70a74eb2009-02-06 00:38:35 +000071 link=None, verbose=1, dry_run=0):
Tarek Ziadéc2b71882009-07-03 19:22:23 +000072 """Copy a file 'src' to 'dst'.
73
74 If 'dst' is a directory, then 'src' is copied there with the same name;
75 otherwise, it must be a filename. (If the file exists, it will be
76 ruthlessly clobbered.) If 'preserve_mode' is true (the default),
77 the file's mode (type and permission bits, or whatever is analogous on
78 the current platform) is copied. If 'preserve_times' is true (the
79 default), the last-modified and last-access times are copied as well.
80 If 'update' is true, 'src' will only be copied if 'dst' does not exist,
81 or if 'dst' does exist but is older than 'src'.
Greg Wardaebf7062000-04-04 02:05:59 +000082
Greg Ward9e3dc4e2000-09-23 00:59:34 +000083 'link' allows you to make hard links (os.link) or symbolic links
84 (os.symlink) instead of copying: set it to "hard" or "sym"; if it is
85 None (the default), files are copied. Don't set 'link' on systems that
86 don't support it: 'copy_file()' doesn't check if hard or symbolic
87 linking is available.
Greg Wardaebf7062000-04-04 02:05:59 +000088
Greg Ward9e3dc4e2000-09-23 00:59:34 +000089 Under Mac OS, uses the native file copy function in macostools; on
90 other systems, uses '_copy_file_contents()' to copy file contents.
Greg Wardaebf7062000-04-04 02:05:59 +000091
Greg Ward0d4a8532000-09-30 17:29:35 +000092 Return a tuple (dest_name, copied): 'dest_name' is the actual name of
93 the output file, and 'copied' is true if the file was copied (or would
94 have been copied, if 'dry_run' true).
Greg Ward9e3dc4e2000-09-23 00:59:34 +000095 """
Greg Wardaebf7062000-04-04 02:05:59 +000096 # XXX if the destination file already exists, we clobber it if
97 # copying, but blow up if linking. Hmmm. And I don't know what
98 # macostools.copyfile() does. Should definitely be consistent, and
99 # should probably blow up if destination exists and we would be
100 # changing it (ie. it's not already a hard/soft link to src OR
101 # (not update) and (src newer than dst).
102
Greg Wardaebf7062000-04-04 02:05:59 +0000103 from distutils.dep_util import newer
Greg Warde628a2f2001-07-25 19:48:03 +0000104 from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE
Greg Wardaebf7062000-04-04 02:05:59 +0000105
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000106 if not os.path.isfile(src):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000107 raise DistutilsFileError(
108 "can't copy '%s': doesn't exist or not a regular file" % src)
Greg Wardaebf7062000-04-04 02:05:59 +0000109
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000110 if os.path.isdir(dst):
Greg Wardaebf7062000-04-04 02:05:59 +0000111 dir = dst
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000112 dst = os.path.join(dst, os.path.basename(src))
Greg Wardaebf7062000-04-04 02:05:59 +0000113 else:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000114 dir = os.path.dirname(dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000115
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000116 if update and not newer(src, dst):
Tarek Ziadé35e6fd52009-02-06 00:53:43 +0000117 if verbose >= 1:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000118 log.debug("not copying %s (output up-to-date)", src)
Collin Winter5b7e9d72007-08-30 03:52:21 +0000119 return (dst, 0)
Greg Wardaebf7062000-04-04 02:05:59 +0000120
121 try:
122 action = _copy_action[link]
123 except KeyError:
Collin Winter5b7e9d72007-08-30 03:52:21 +0000124 raise ValueError("invalid value '%s' for 'link' argument" % link)
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000125
Tarek Ziadé35e6fd52009-02-06 00:53:43 +0000126 if verbose >= 1:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000127 if os.path.basename(dst) == os.path.basename(src):
128 log.info("%s %s -> %s", action, src, dir)
129 else:
130 log.info("%s %s -> %s", action, src, dst)
Fred Drakeb94b8492001-12-06 20:51:35 +0000131
Greg Wardaebf7062000-04-04 02:05:59 +0000132 if dry_run:
Greg Ward0d4a8532000-09-30 17:29:35 +0000133 return (dst, 1)
Greg Wardaebf7062000-04-04 02:05:59 +0000134
Greg Wardaebf7062000-04-04 02:05:59 +0000135 # If linking (hard or symbolic), use the appropriate system call
136 # (Unix only, of course, but that's the caller's responsibility)
137 elif link == 'hard':
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000138 if not (os.path.exists(dst) and os.path.samefile(src, dst)):
139 os.link(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000140 elif link == 'sym':
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000141 if not (os.path.exists(dst) and os.path.samefile(src, dst)):
142 os.symlink(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000143
144 # Otherwise (non-Mac, not linking), copy the file contents and
145 # (optionally) copy the times and mode.
146 else:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000147 _copy_file_contents(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000148 if preserve_mode or preserve_times:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000149 st = os.stat(src)
Greg Wardaebf7062000-04-04 02:05:59 +0000150
151 # According to David Ascher <da@ski.org>, utime() should be done
152 # before chmod() (at least under NT).
153 if preserve_times:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000154 os.utime(dst, (st[ST_ATIME], st[ST_MTIME]))
Greg Wardaebf7062000-04-04 02:05:59 +0000155 if preserve_mode:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000156 os.chmod(dst, S_IMODE(st[ST_MODE]))
Greg Wardaebf7062000-04-04 02:05:59 +0000157
Greg Ward0d4a8532000-09-30 17:29:35 +0000158 return (dst, 1)
Greg Wardaebf7062000-04-04 02:05:59 +0000159
Greg Wardaebf7062000-04-04 02:05:59 +0000160
161# XXX I suspect this is Unix-specific -- need porting help!
Tarek Ziadéc2b71882009-07-03 19:22:23 +0000162def move_file(src, dst, verbose=1, dry_run=0):
163 """Move a file 'src' to 'dst'.
Greg Wardaebf7062000-04-04 02:05:59 +0000164
Tarek Ziadéc2b71882009-07-03 19:22:23 +0000165 If 'dst' is a directory, the file will be moved into it with the same
166 name; otherwise, 'src' is just renamed to 'dst'. Return the new
167 full name of the file.
Greg Wardaebf7062000-04-04 02:05:59 +0000168
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000169 Handles cross-device moves on Unix using 'copy_file()'. What about
170 other systems???
171 """
Greg Wardaebf7062000-04-04 02:05:59 +0000172 from os.path import exists, isfile, isdir, basename, dirname
Andrew M. Kuchling106ffdb2001-08-09 20:59:53 +0000173 import errno
Fred Drakeb94b8492001-12-06 20:51:35 +0000174
Tarek Ziadé35e6fd52009-02-06 00:53:43 +0000175 if verbose >= 1:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000176 log.info("moving %s -> %s", src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000177
178 if dry_run:
179 return dst
180
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000181 if not isfile(src):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000182 raise DistutilsFileError("can't move '%s': not a regular file" % src)
Greg Wardaebf7062000-04-04 02:05:59 +0000183
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000184 if isdir(dst):
185 dst = os.path.join(dst, basename(src))
186 elif exists(dst):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000187 raise DistutilsFileError(
188 "can't move '%s': destination '%s' already exists" %
189 (src, dst))
Greg Wardaebf7062000-04-04 02:05:59 +0000190
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000191 if not isdir(dirname(dst)):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000192 raise DistutilsFileError(
193 "can't move '%s': destination '%s' not a valid path" %
194 (src, dst))
Greg Wardaebf7062000-04-04 02:05:59 +0000195
Collin Winter5b7e9d72007-08-30 03:52:21 +0000196 copy_it = False
Greg Wardaebf7062000-04-04 02:05:59 +0000197 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000198 os.rename(src, dst)
Guido van Rossumb940e112007-01-10 16:19:56 +0000199 except os.error as e:
200 (num, msg) = e
Greg Wardaebf7062000-04-04 02:05:59 +0000201 if num == errno.EXDEV:
Collin Winter5b7e9d72007-08-30 03:52:21 +0000202 copy_it = True
Greg Wardaebf7062000-04-04 02:05:59 +0000203 else:
Collin Winter5b7e9d72007-08-30 03:52:21 +0000204 raise DistutilsFileError(
205 "couldn't move '%s' to '%s': %s" % (src, dst, msg))
Greg Wardaebf7062000-04-04 02:05:59 +0000206
207 if copy_it:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000208 copy_file(src, dst, verbose=verbose)
Greg Wardaebf7062000-04-04 02:05:59 +0000209 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000210 os.unlink(src)
Guido van Rossumb940e112007-01-10 16:19:56 +0000211 except os.error as e:
212 (num, msg) = e
Greg Wardaebf7062000-04-04 02:05:59 +0000213 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000214 os.unlink(dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000215 except os.error:
216 pass
Collin Winter5b7e9d72007-08-30 03:52:21 +0000217 raise DistutilsFileError(
218 "couldn't move '%s' to '%s' by copy/delete: "
219 "delete '%s' failed: %s"
220 % (src, dst, src, msg))
Greg Wardaebf7062000-04-04 02:05:59 +0000221 return dst
222
Greg Wardaebf7062000-04-04 02:05:59 +0000223
Tarek Ziadéc2b71882009-07-03 19:22:23 +0000224def write_file(filename, contents):
Greg Wardaebf7062000-04-04 02:05:59 +0000225 """Create a file with the specified name and write 'contents' (a
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000226 sequence of strings without line terminators) to it.
227 """
228 f = open(filename, "w")
Greg Wardaebf7062000-04-04 02:05:59 +0000229 for line in contents:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000230 f.write(line + "\n")
231 f.close()