blob: 758bde38bfe471523b9ea65c61f4f7e41307cb2e [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 Ward0d4a8532000-09-30 17:29:35 +0000135 # On Mac OS, use the native file copy routine
Greg Wardaebf7062000-04-04 02:05:59 +0000136 if os.name == 'mac':
137 import macostools
138 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000139 macostools.copy(src, dst, 0, preserve_times)
Guido van Rossumb940e112007-01-10 16:19:56 +0000140 except os.error as exc:
Collin Winter5b7e9d72007-08-30 03:52:21 +0000141 raise DistutilsFileError(
Georg Brandld11b68a2008-01-06 21:13:42 +0000142 "could not copy '%s' to '%s': %s" % (src, dst, exc.args[-1]))
Fred Drakeb94b8492001-12-06 20:51:35 +0000143
Greg Wardaebf7062000-04-04 02:05:59 +0000144 # If linking (hard or symbolic), use the appropriate system call
145 # (Unix only, of course, but that's the caller's responsibility)
146 elif link == 'hard':
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000147 if not (os.path.exists(dst) and os.path.samefile(src, dst)):
148 os.link(src, dst)
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)
Greg Wardaebf7062000-04-04 02:05:59 +0000152
153 # Otherwise (non-Mac, not linking), copy the file contents and
154 # (optionally) copy the times and mode.
155 else:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000156 _copy_file_contents(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000157 if preserve_mode or preserve_times:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000158 st = os.stat(src)
Greg Wardaebf7062000-04-04 02:05:59 +0000159
160 # According to David Ascher <da@ski.org>, utime() should be done
161 # before chmod() (at least under NT).
162 if preserve_times:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000163 os.utime(dst, (st[ST_ATIME], st[ST_MTIME]))
Greg Wardaebf7062000-04-04 02:05:59 +0000164 if preserve_mode:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000165 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
170# XXX I suspect this is Unix-specific -- need porting help!
Tarek Ziadéc2b71882009-07-03 19:22:23 +0000171def move_file(src, dst, verbose=1, dry_run=0):
172 """Move a file 'src' to 'dst'.
Greg Wardaebf7062000-04-04 02:05:59 +0000173
Tarek Ziadéc2b71882009-07-03 19:22:23 +0000174 If 'dst' is a directory, the file will be moved into it with the same
175 name; otherwise, 'src' is just renamed to 'dst'. Return the new
176 full name of the file.
Greg Wardaebf7062000-04-04 02:05:59 +0000177
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000178 Handles cross-device moves on Unix using 'copy_file()'. What about
179 other systems???
180 """
Greg Wardaebf7062000-04-04 02:05:59 +0000181 from os.path import exists, isfile, isdir, basename, dirname
Andrew M. Kuchling106ffdb2001-08-09 20:59:53 +0000182 import errno
Fred Drakeb94b8492001-12-06 20:51:35 +0000183
Tarek Ziadé35e6fd52009-02-06 00:53:43 +0000184 if verbose >= 1:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000185 log.info("moving %s -> %s", src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000186
187 if dry_run:
188 return dst
189
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000190 if not isfile(src):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000191 raise DistutilsFileError("can't move '%s': not a regular file" % src)
Greg Wardaebf7062000-04-04 02:05:59 +0000192
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000193 if isdir(dst):
194 dst = os.path.join(dst, basename(src))
195 elif exists(dst):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000196 raise DistutilsFileError(
197 "can't move '%s': destination '%s' already exists" %
198 (src, dst))
Greg Wardaebf7062000-04-04 02:05:59 +0000199
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000200 if not isdir(dirname(dst)):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000201 raise DistutilsFileError(
202 "can't move '%s': destination '%s' not a valid path" %
203 (src, dst))
Greg Wardaebf7062000-04-04 02:05:59 +0000204
Collin Winter5b7e9d72007-08-30 03:52:21 +0000205 copy_it = False
Greg Wardaebf7062000-04-04 02:05:59 +0000206 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000207 os.rename(src, dst)
Guido van Rossumb940e112007-01-10 16:19:56 +0000208 except os.error as e:
209 (num, msg) = e
Greg Wardaebf7062000-04-04 02:05:59 +0000210 if num == errno.EXDEV:
Collin Winter5b7e9d72007-08-30 03:52:21 +0000211 copy_it = True
Greg Wardaebf7062000-04-04 02:05:59 +0000212 else:
Collin Winter5b7e9d72007-08-30 03:52:21 +0000213 raise DistutilsFileError(
214 "couldn't move '%s' to '%s': %s" % (src, dst, msg))
Greg Wardaebf7062000-04-04 02:05:59 +0000215
216 if copy_it:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000217 copy_file(src, dst, verbose=verbose)
Greg Wardaebf7062000-04-04 02:05:59 +0000218 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000219 os.unlink(src)
Guido van Rossumb940e112007-01-10 16:19:56 +0000220 except os.error as e:
221 (num, msg) = e
Greg Wardaebf7062000-04-04 02:05:59 +0000222 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000223 os.unlink(dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000224 except os.error:
225 pass
Collin Winter5b7e9d72007-08-30 03:52:21 +0000226 raise DistutilsFileError(
227 "couldn't move '%s' to '%s' by copy/delete: "
228 "delete '%s' failed: %s"
229 % (src, dst, src, msg))
Greg Wardaebf7062000-04-04 02:05:59 +0000230 return dst
231
Greg Wardaebf7062000-04-04 02:05:59 +0000232
Tarek Ziadéc2b71882009-07-03 19:22:23 +0000233def write_file(filename, contents):
Greg Wardaebf7062000-04-04 02:05:59 +0000234 """Create a file with the specified name and write 'contents' (a
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000235 sequence of strings without line terminators) to it.
236 """
237 f = open(filename, "w")
Greg Wardaebf7062000-04-04 02:05:59 +0000238 for line in contents:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000239 f.write(line + "\n")
240 f.close()