blob: b9f07861335c911497e05f38b6559e7e1a8f6fd2 [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é9ad7bbc2009-07-03 19:14:49 +000013_copy_action = {None: 'copying',
14 'hard': 'hard linking',
15 'sym': 'symbolically linking'}
Greg Wardaebf7062000-04-04 02:05:59 +000016
17
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +000018def _copy_file_contents(src, dst, buffer_size=16*1024):
19 """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')
33 except os.error, (errno, errstr):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +000034 raise DistutilsFileError("could not open '%s': %s" % (src, errstr))
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)
39 except os.error, (errno, errstr):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +000040 raise DistutilsFileError(
41 "could not delete '%s': %s" % (dst, errstr))
Tim Peters182b5ac2004-07-18 06:16:08 +000042
Greg Wardaebf7062000-04-04 02:05:59 +000043 try:
44 fdst = open(dst, 'wb')
45 except os.error, (errno, errstr):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +000046 raise DistutilsFileError(
47 "could not create '%s': %s" % (dst, errstr))
Fred Drakeb94b8492001-12-06 20:51:35 +000048
Greg Wardaebf7062000-04-04 02:05:59 +000049 while 1:
50 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +000051 buf = fsrc.read(buffer_size)
Greg Wardaebf7062000-04-04 02:05:59 +000052 except os.error, (errno, errstr):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +000053 raise DistutilsFileError(
54 "could not read from '%s': %s" % (src, errstr))
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)
61 except os.error, (errno, errstr):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +000062 raise DistutilsFileError(
63 "could not write to '%s': %s" % (dst, errstr))
Fred Drakeb94b8492001-12-06 20:51:35 +000064
Greg Wardaebf7062000-04-04 02:05:59 +000065 finally:
66 if fdst:
67 fdst.close()
68 if fsrc:
69 fsrc.close()
70
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +000071def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0,
72 link=None, verbose=1, dry_run=0):
73 """Copy a file 'src' to 'dst'.
Greg Wardaebf7062000-04-04 02:05:59 +000074
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +000075 If 'dst' is a directory, then 'src' is copied there with the same name;
76 otherwise, it must be a filename. (If the file exists, it will be
77 ruthlessly clobbered.) If 'preserve_mode' is true (the default),
78 the file's mode (type and permission bits, or whatever is analogous on
79 the current platform) is copied. If 'preserve_times' is true (the
80 default), the last-modified and last-access times are copied as well.
81 If 'update' is true, 'src' will only be copied if 'dst' does not exist,
82 or if 'dst' does exist but is older than 'src'.
Greg Wardaebf7062000-04-04 02:05:59 +000083
Greg Ward9e3dc4e2000-09-23 00:59:34 +000084 'link' allows you to make hard links (os.link) or symbolic links
85 (os.symlink) instead of copying: set it to "hard" or "sym"; if it is
86 None (the default), files are copied. Don't set 'link' on systems that
87 don't support it: 'copy_file()' doesn't check if hard or symbolic
88 linking is available.
Greg Wardaebf7062000-04-04 02:05:59 +000089
Greg Ward9e3dc4e2000-09-23 00:59:34 +000090 Under Mac OS, uses the native file copy function in macostools; on
91 other systems, uses '_copy_file_contents()' to copy file contents.
Greg Wardaebf7062000-04-04 02:05:59 +000092
Greg Ward0d4a8532000-09-30 17:29:35 +000093 Return a tuple (dest_name, copied): 'dest_name' is the actual name of
94 the output file, and 'copied' is true if the file was copied (or would
95 have been copied, if 'dry_run' true).
Greg Ward9e3dc4e2000-09-23 00:59:34 +000096 """
Greg Wardaebf7062000-04-04 02:05:59 +000097 # XXX if the destination file already exists, we clobber it if
98 # copying, but blow up if linking. Hmmm. And I don't know what
99 # macostools.copyfile() does. Should definitely be consistent, and
100 # should probably blow up if destination exists and we would be
101 # changing it (ie. it's not already a hard/soft link to src OR
102 # (not update) and (src newer than dst).
103
Greg Wardaebf7062000-04-04 02:05:59 +0000104 from distutils.dep_util import newer
Greg Warde628a2f2001-07-25 19:48:03 +0000105 from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE
Greg Wardaebf7062000-04-04 02:05:59 +0000106
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000107 if not os.path.isfile(src):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +0000108 raise DistutilsFileError(
109 "can't copy '%s': doesn't exist or not a regular file" % src)
Greg Wardaebf7062000-04-04 02:05:59 +0000110
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000111 if os.path.isdir(dst):
Greg Wardaebf7062000-04-04 02:05:59 +0000112 dir = dst
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000113 dst = os.path.join(dst, os.path.basename(src))
Greg Wardaebf7062000-04-04 02:05:59 +0000114 else:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000115 dir = os.path.dirname(dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000116
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000117 if update and not newer(src, dst):
Tarek Ziadéaaf2e182009-02-06 00:49:45 +0000118 if verbose >= 1:
Tarek Ziadéd5eb9852009-02-06 00:31:59 +0000119 log.debug("not copying %s (output up-to-date)", src)
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +0000120 return dst, 0
Greg Wardaebf7062000-04-04 02:05:59 +0000121
122 try:
123 action = _copy_action[link]
124 except KeyError:
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +0000125 raise ValueError("invalid value '%s' for 'link' argument" % link)
Tarek Ziadéd5eb9852009-02-06 00:31:59 +0000126
Tarek Ziadéaaf2e182009-02-06 00:49:45 +0000127 if verbose >= 1:
Tarek Ziadéd5eb9852009-02-06 00:31:59 +0000128 if os.path.basename(dst) == os.path.basename(src):
129 log.info("%s %s -> %s", action, src, dir)
130 else:
131 log.info("%s %s -> %s", action, src, dst)
Fred Drakeb94b8492001-12-06 20:51:35 +0000132
Greg Wardaebf7062000-04-04 02:05:59 +0000133 if dry_run:
Greg Ward0d4a8532000-09-30 17:29:35 +0000134 return (dst, 1)
Greg Wardaebf7062000-04-04 02:05:59 +0000135
Greg Wardaebf7062000-04-04 02:05:59 +0000136 # If linking (hard or symbolic), use the appropriate system call
137 # (Unix only, of course, but that's the caller's responsibility)
Ronald Oussoren9545a232010-05-05 19:09:31 +0000138 if link == 'hard':
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000139 if not (os.path.exists(dst) and os.path.samefile(src, dst)):
140 os.link(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000141 elif link == 'sym':
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000142 if not (os.path.exists(dst) and os.path.samefile(src, dst)):
143 os.symlink(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000144
145 # Otherwise (non-Mac, not linking), copy the file contents and
146 # (optionally) copy the times and mode.
147 else:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000148 _copy_file_contents(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000149 if preserve_mode or preserve_times:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000150 st = os.stat(src)
Greg Wardaebf7062000-04-04 02:05:59 +0000151
152 # According to David Ascher <da@ski.org>, utime() should be done
153 # before chmod() (at least under NT).
154 if preserve_times:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000155 os.utime(dst, (st[ST_ATIME], st[ST_MTIME]))
Greg Wardaebf7062000-04-04 02:05:59 +0000156 if preserve_mode:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000157 os.chmod(dst, S_IMODE(st[ST_MODE]))
Greg Wardaebf7062000-04-04 02:05:59 +0000158
Greg Ward0d4a8532000-09-30 17:29:35 +0000159 return (dst, 1)
Greg Wardaebf7062000-04-04 02:05:59 +0000160
Greg Wardaebf7062000-04-04 02:05:59 +0000161# XXX I suspect this is Unix-specific -- need porting help!
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +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é9ad7bbc2009-07-03 19:14:49 +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éaaf2e182009-02-06 00:49:45 +0000175 if verbose >= 1:
Tarek Ziadéd5eb9852009-02-06 00:31:59 +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):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +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):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +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)):
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +0000192 raise DistutilsFileError(
Greg Wardaebf7062000-04-04 02:05:59 +0000193 "can't move '%s': destination '%s' not a valid path" % \
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +0000194 (src, dst))
Greg Wardaebf7062000-04-04 02:05:59 +0000195
196 copy_it = 0
197 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000198 os.rename(src, dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000199 except os.error, (num, msg):
200 if num == errno.EXDEV:
201 copy_it = 1
202 else:
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +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éd5eb9852009-02-06 00:31:59 +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)
Greg Wardaebf7062000-04-04 02:05:59 +0000210 except os.error, (num, msg):
211 try:
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000212 os.unlink(dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000213 except os.error:
214 pass
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +0000215 raise DistutilsFileError(
Fred Drakeb94b8492001-12-06 20:51:35 +0000216 ("couldn't move '%s' to '%s' by copy/delete: " +
Tarek Ziadé9ad7bbc2009-07-03 19:14:49 +0000217 "delete '%s' failed: %s") %
218 (src, dst, src, msg))
Greg Wardaebf7062000-04-04 02:05:59 +0000219 return dst
220
Greg Wardaebf7062000-04-04 02:05:59 +0000221
222def write_file (filename, contents):
223 """Create a file with the specified name and write 'contents' (a
Greg Ward9e3dc4e2000-09-23 00:59:34 +0000224 sequence of strings without line terminators) to it.
225 """
226 f = open(filename, "w")
Éric Araujod1feff72010-11-06 04:06:18 +0000227 try:
228 for line in contents:
229 f.write(line + "\n")
230 finally:
231 f.close()