blob: 9bdd14e42e38066f3e827dc99c7f8c7e12a808ef [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')
Guido van Rossumb940e112007-01-10 16:19:56 +000030 except os.error 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)
Guido van Rossumb940e112007-01-10 16:19:56 +000036 except os.error 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')
Guido van Rossumb940e112007-01-10 16:19:56 +000042 except os.error 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)
Guido van Rossumb940e112007-01-10 16:19:56 +000049 except os.error 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)
Guido van Rossumb940e112007-01-10 16:19:56 +000058 except os.error 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
83 linking is available.
Greg Wardaebf7062000-04-04 02:05:59 +000084
Greg Ward9e3dc4e2000-09-23 00:59:34 +000085 Under Mac OS, uses the native file copy function in macostools; on
86 other systems, uses '_copy_file_contents()' to copy file contents.
Greg Wardaebf7062000-04-04 02:05:59 +000087
Greg Ward0d4a8532000-09-30 17:29:35 +000088 Return a tuple (dest_name, copied): 'dest_name' is the actual name of
89 the output file, and 'copied' is true if the file was copied (or would
90 have been copied, if 'dry_run' true).
Greg Ward9e3dc4e2000-09-23 00:59:34 +000091 """
Greg Wardaebf7062000-04-04 02:05:59 +000092 # XXX if the destination file already exists, we clobber it if
93 # copying, but blow up if linking. Hmmm. And I don't know what
94 # macostools.copyfile() does. Should definitely be consistent, and
95 # should probably blow up if destination exists and we would be
96 # changing it (ie. it's not already a hard/soft link to src OR
97 # (not update) and (src newer than dst).
98
Greg Wardaebf7062000-04-04 02:05:59 +000099 from distutils.dep_util import newer
Greg Warde628a2f2001-07-25 19:48:03 +0000100 from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE
Greg Wardaebf7062000-04-04 02:05:59 +0000101
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000102 if not os.path.isfile(src):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000103 raise DistutilsFileError(
104 "can't copy '%s': doesn't exist or not a regular file" % src)
Greg Wardaebf7062000-04-04 02:05:59 +0000105
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000106 if os.path.isdir(dst):
Greg Wardaebf7062000-04-04 02:05:59 +0000107 dir = dst
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000108 dst = os.path.join(dst, os.path.basename(src))
Greg Wardaebf7062000-04-04 02:05:59 +0000109 else:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000110 dir = os.path.dirname(dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000111
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000112 if update and not newer(src, dst):
Tarek Ziadé35e6fd52009-02-06 00:53:43 +0000113 if verbose >= 1:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000114 log.debug("not copying %s (output up-to-date)", src)
Collin Winter5b7e9d72007-08-30 03:52:21 +0000115 return (dst, 0)
Greg Wardaebf7062000-04-04 02:05:59 +0000116
117 try:
118 action = _copy_action[link]
119 except KeyError:
Collin Winter5b7e9d72007-08-30 03:52:21 +0000120 raise ValueError("invalid value '%s' for 'link' argument" % link)
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000121
Tarek Ziadé35e6fd52009-02-06 00:53:43 +0000122 if verbose >= 1:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000123 if os.path.basename(dst) == os.path.basename(src):
124 log.info("%s %s -> %s", action, src, dir)
125 else:
126 log.info("%s %s -> %s", action, src, dst)
Fred Drakeb94b8492001-12-06 20:51:35 +0000127
Greg Wardaebf7062000-04-04 02:05:59 +0000128 if dry_run:
Greg Ward0d4a8532000-09-30 17:29:35 +0000129 return (dst, 1)
Greg Wardaebf7062000-04-04 02:05:59 +0000130
Greg Wardaebf7062000-04-04 02:05:59 +0000131 # If linking (hard or symbolic), use the appropriate system call
132 # (Unix only, of course, but that's the caller's responsibility)
133 elif link == 'hard':
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000134 if not (os.path.exists(dst) and os.path.samefile(src, dst)):
135 os.link(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000136 elif link == 'sym':
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000137 if not (os.path.exists(dst) and os.path.samefile(src, dst)):
138 os.symlink(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000139
140 # Otherwise (non-Mac, not linking), copy the file contents and
141 # (optionally) copy the times and mode.
142 else:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000143 _copy_file_contents(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000144 if preserve_mode or preserve_times:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000145 st = os.stat(src)
Greg Wardaebf7062000-04-04 02:05:59 +0000146
147 # According to David Ascher <da@ski.org>, utime() should be done
148 # before chmod() (at least under NT).
149 if preserve_times:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000150 os.utime(dst, (st[ST_ATIME], st[ST_MTIME]))
Greg Wardaebf7062000-04-04 02:05:59 +0000151 if preserve_mode:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000152 os.chmod(dst, S_IMODE(st[ST_MODE]))
Greg Wardaebf7062000-04-04 02:05:59 +0000153
Greg Ward0d4a8532000-09-30 17:29:35 +0000154 return (dst, 1)
Greg Wardaebf7062000-04-04 02:05:59 +0000155
Greg Wardaebf7062000-04-04 02:05:59 +0000156
157# XXX I suspect this is Unix-specific -- need porting help!
Tarek Ziadé36797272010-07-22 12:50:05 +0000158def move_file (src, dst,
159 verbose=1,
160 dry_run=0):
Greg Wardaebf7062000-04-04 02:05:59 +0000161
Tarek Ziadé36797272010-07-22 12:50:05 +0000162 """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will
163 be moved into it with the same name; otherwise, 'src' is just renamed
164 to 'dst'. Return the new full name of the file.
Greg Wardaebf7062000-04-04 02:05:59 +0000165
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000166 Handles cross-device moves on Unix using 'copy_file()'. What about
167 other systems???
168 """
Greg Wardaebf7062000-04-04 02:05:59 +0000169 from os.path import exists, isfile, isdir, basename, dirname
Andrew M. Kuchling106ffdb2001-08-09 20:59:53 +0000170 import errno
Fred Drakeb94b8492001-12-06 20:51:35 +0000171
Tarek Ziadé35e6fd52009-02-06 00:53:43 +0000172 if verbose >= 1:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000173 log.info("moving %s -> %s", src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000174
175 if dry_run:
176 return dst
177
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000178 if not isfile(src):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000179 raise DistutilsFileError("can't move '%s': not a regular file" % src)
Greg Wardaebf7062000-04-04 02:05:59 +0000180
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000181 if isdir(dst):
182 dst = os.path.join(dst, basename(src))
183 elif exists(dst):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000184 raise DistutilsFileError(
185 "can't move '%s': destination '%s' already exists" %
186 (src, dst))
Greg Wardaebf7062000-04-04 02:05:59 +0000187
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000188 if not isdir(dirname(dst)):
Collin Winter5b7e9d72007-08-30 03:52:21 +0000189 raise DistutilsFileError(
190 "can't move '%s': destination '%s' not a valid path" %
191 (src, dst))
Greg Wardaebf7062000-04-04 02:05:59 +0000192
Collin Winter5b7e9d72007-08-30 03:52:21 +0000193 copy_it = False
Greg Wardaebf7062000-04-04 02:05:59 +0000194 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000195 os.rename(src, dst)
Guido van Rossumb940e112007-01-10 16:19:56 +0000196 except os.error as e:
197 (num, msg) = e
Greg Wardaebf7062000-04-04 02:05:59 +0000198 if num == errno.EXDEV:
Collin Winter5b7e9d72007-08-30 03:52:21 +0000199 copy_it = True
Greg Wardaebf7062000-04-04 02:05:59 +0000200 else:
Collin Winter5b7e9d72007-08-30 03:52:21 +0000201 raise DistutilsFileError(
202 "couldn't move '%s' to '%s': %s" % (src, dst, msg))
Greg Wardaebf7062000-04-04 02:05:59 +0000203
204 if copy_it:
Tarek Ziadé70a74eb2009-02-06 00:38:35 +0000205 copy_file(src, dst, verbose=verbose)
Greg Wardaebf7062000-04-04 02:05:59 +0000206 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000207 os.unlink(src)
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 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000211 os.unlink(dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000212 except os.error:
213 pass
Collin Winter5b7e9d72007-08-30 03:52:21 +0000214 raise DistutilsFileError(
215 "couldn't move '%s' to '%s' by copy/delete: "
216 "delete '%s' failed: %s"
217 % (src, dst, src, msg))
Greg Wardaebf7062000-04-04 02:05:59 +0000218 return dst
219
Greg Wardaebf7062000-04-04 02:05:59 +0000220
Tarek Ziadé36797272010-07-22 12:50:05 +0000221def write_file (filename, contents):
Greg Wardaebf7062000-04-04 02:05:59 +0000222 """Create a file with the specified name and write 'contents' (a
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000223 sequence of strings without line terminators) to it.
224 """
225 f = open(filename, "w")
Éric Araujobee5cef2010-11-05 23:51:56 +0000226 try:
227 for line in contents:
228 f.write(line + "\n")
229 finally:
230 f.close()