blob: 54f5c68e28ecf69c78949fad74285179fda97a65 [file] [log] [blame]
Greg Wardaebf7062000-04-04 02:05:59 +00001"""distutils.dir_util
2
3Utility functions for manipulating directories and directory trees."""
4
Greg Wardaebf7062000-04-04 02:05:59 +00005__revision__ = "$Id$"
6
Andrew M. Kuchling40f23e02002-11-26 17:42:48 +00007import os, sys
Greg Ward2d238c52000-05-27 01:35:27 +00008from types import *
9from distutils.errors import DistutilsFileError, DistutilsInternalError
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +000010from distutils import log
Greg Wardaebf7062000-04-04 02:05:59 +000011
12# cache for by mkpath() -- in addition to cheapening redundant calls,
13# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode
Greg Wardb248b7f2000-06-17 02:19:30 +000014_path_created = {}
Greg Wardaebf7062000-04-04 02:05:59 +000015
16# I don't use os.makedirs because a) it's new to Python 1.5.2, and
17# b) it blows up if the directory already exists (I want to silently
18# succeed in that case).
19def mkpath (name, mode=0777, verbose=0, dry_run=0):
20 """Create a directory and any missing ancestor directories. If the
21 directory already exists (or if 'name' is the empty string, which
22 means the current directory, which of course exists), then do
23 nothing. Raise DistutilsFileError if unable to create some
24 directory along the way (eg. some sub-path exists, but is a file
25 rather than a directory). If 'verbose' is true, print a one-line
26 summary of each mkdir to stdout. Return the list of directories
27 actually created."""
28
Greg Wardb248b7f2000-06-17 02:19:30 +000029 global _path_created
Greg Wardaebf7062000-04-04 02:05:59 +000030
Greg Ward2d238c52000-05-27 01:35:27 +000031 # Detect a common bug -- name is None
Tim Peters9e34c042005-08-26 15:20:46 +000032 if not isinstance(name, StringTypes):
Greg Ward2d238c52000-05-27 01:35:27 +000033 raise DistutilsInternalError, \
Walter Dörwald70a6b492004-02-12 17:35:32 +000034 "mkpath: 'name' must be a string (got %r)" % (name,)
Greg Ward2d238c52000-05-27 01:35:27 +000035
Greg Wardaebf7062000-04-04 02:05:59 +000036 # XXX what's the better way to handle verbosity? print as we create
37 # each directory in the path (the current behaviour), or only announce
38 # the creation of the whole path? (quite easy to do the latter since
39 # we're not using a recursive algorithm)
40
Greg Ward071ed762000-09-26 02:12:31 +000041 name = os.path.normpath(name)
Greg Wardaebf7062000-04-04 02:05:59 +000042 created_dirs = []
Greg Ward071ed762000-09-26 02:12:31 +000043 if os.path.isdir(name) or name == '':
Greg Wardaebf7062000-04-04 02:05:59 +000044 return created_dirs
Greg Ward963cd2d2000-09-30 17:47:17 +000045 if _path_created.get(os.path.abspath(name)):
Greg Wardaebf7062000-04-04 02:05:59 +000046 return created_dirs
47
Greg Ward071ed762000-09-26 02:12:31 +000048 (head, tail) = os.path.split(name)
Greg Wardaebf7062000-04-04 02:05:59 +000049 tails = [tail] # stack of lone dirs to create
Fred Drakeb94b8492001-12-06 20:51:35 +000050
Greg Ward071ed762000-09-26 02:12:31 +000051 while head and tail and not os.path.isdir(head):
Greg Wardaebf7062000-04-04 02:05:59 +000052 #print "splitting '%s': " % head,
Greg Ward071ed762000-09-26 02:12:31 +000053 (head, tail) = os.path.split(head)
Greg Wardaebf7062000-04-04 02:05:59 +000054 #print "to ('%s','%s')" % (head, tail)
Greg Ward071ed762000-09-26 02:12:31 +000055 tails.insert(0, tail) # push next higher dir onto stack
Greg Wardaebf7062000-04-04 02:05:59 +000056
57 #print "stack of tails:", tails
58
59 # now 'head' contains the deepest directory that already exists
60 # (that is, the child of 'head' in 'name' is the highest directory
61 # that does *not* exist)
62 for d in tails:
63 #print "head = %s, d = %s: " % (head, d),
Greg Ward071ed762000-09-26 02:12:31 +000064 head = os.path.join(head, d)
Greg Ward963cd2d2000-09-30 17:47:17 +000065 abs_head = os.path.abspath(head)
66
67 if _path_created.get(abs_head):
Greg Wardaebf7062000-04-04 02:05:59 +000068 continue
69
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +000070 log.info("creating %s", head)
Greg Wardaebf7062000-04-04 02:05:59 +000071
72 if not dry_run:
73 try:
Greg Ward071ed762000-09-26 02:12:31 +000074 os.mkdir(head)
Greg Wardaebf7062000-04-04 02:05:59 +000075 created_dirs.append(head)
76 except OSError, exc:
77 raise DistutilsFileError, \
78 "could not create '%s': %s" % (head, exc[-1])
79
Greg Ward963cd2d2000-09-30 17:47:17 +000080 _path_created[abs_head] = 1
Greg Wardaebf7062000-04-04 02:05:59 +000081 return created_dirs
82
83# mkpath ()
84
85
86def create_tree (base_dir, files, mode=0777, verbose=0, dry_run=0):
87
88 """Create all the empty directories under 'base_dir' needed to
89 put 'files' there. 'base_dir' is just the a name of a directory
90 which doesn't necessarily exist yet; 'files' is a list of filenames
91 to be interpreted relative to 'base_dir'. 'base_dir' + the
92 directory portion of every file in 'files' will be created if it
93 doesn't already exist. 'mode', 'verbose' and 'dry_run' flags are as
94 for 'mkpath()'."""
95
96 # First get the list of directories to create
97 need_dir = {}
98 for file in files:
Greg Ward071ed762000-09-26 02:12:31 +000099 need_dir[os.path.join(base_dir, os.path.dirname(file))] = 1
Greg Wardaebf7062000-04-04 02:05:59 +0000100 need_dirs = need_dir.keys()
101 need_dirs.sort()
102
103 # Now create them
104 for dir in need_dirs:
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +0000105 mkpath(dir, mode, dry_run=dry_run)
Greg Wardaebf7062000-04-04 02:05:59 +0000106
107# create_tree ()
108
109
110def copy_tree (src, dst,
111 preserve_mode=1,
112 preserve_times=1,
113 preserve_symlinks=0,
114 update=0,
115 verbose=0,
116 dry_run=0):
117
118 """Copy an entire directory tree 'src' to a new location 'dst'. Both
119 'src' and 'dst' must be directory names. If 'src' is not a
120 directory, raise DistutilsFileError. If 'dst' does not exist, it is
121 created with 'mkpath()'. The end result of the copy is that every
122 file in 'src' is copied to 'dst', and directories under 'src' are
123 recursively copied to 'dst'. Return the list of files that were
124 copied or might have been copied, using their output name. The
125 return value is unaffected by 'update' or 'dry_run': it is simply
126 the list of all files under 'src', with the names changed to be
127 under 'dst'.
128
129 'preserve_mode' and 'preserve_times' are the same as for
130 'copy_file'; note that they only apply to regular files, not to
131 directories. If 'preserve_symlinks' is true, symlinks will be
132 copied as symlinks (on platforms that support them!); otherwise
133 (the default), the destination of the symlink will be copied.
134 'update' and 'verbose' are the same as for 'copy_file'."""
135
136 from distutils.file_util import copy_file
137
Greg Ward071ed762000-09-26 02:12:31 +0000138 if not dry_run and not os.path.isdir(src):
Greg Wardaebf7062000-04-04 02:05:59 +0000139 raise DistutilsFileError, \
Fred Drakeb94b8492001-12-06 20:51:35 +0000140 "cannot copy tree '%s': not a directory" % src
Greg Wardaebf7062000-04-04 02:05:59 +0000141 try:
Greg Ward071ed762000-09-26 02:12:31 +0000142 names = os.listdir(src)
Greg Wardaebf7062000-04-04 02:05:59 +0000143 except os.error, (errno, errstr):
144 if dry_run:
145 names = []
146 else:
147 raise DistutilsFileError, \
148 "error listing files in '%s': %s" % (src, errstr)
149
150 if not dry_run:
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +0000151 mkpath(dst)
Greg Wardaebf7062000-04-04 02:05:59 +0000152
153 outputs = []
154
155 for n in names:
Greg Ward071ed762000-09-26 02:12:31 +0000156 src_name = os.path.join(src, n)
157 dst_name = os.path.join(dst, n)
Greg Wardaebf7062000-04-04 02:05:59 +0000158
Greg Ward071ed762000-09-26 02:12:31 +0000159 if preserve_symlinks and os.path.islink(src_name):
160 link_dest = os.readlink(src_name)
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +0000161 log.info("linking %s -> %s", dst_name, link_dest)
Greg Wardaebf7062000-04-04 02:05:59 +0000162 if not dry_run:
Greg Ward071ed762000-09-26 02:12:31 +0000163 os.symlink(link_dest, dst_name)
164 outputs.append(dst_name)
Fred Drakeb94b8492001-12-06 20:51:35 +0000165
Greg Ward071ed762000-09-26 02:12:31 +0000166 elif os.path.isdir(src_name):
167 outputs.extend(
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +0000168 copy_tree(src_name, dst_name, preserve_mode,
169 preserve_times, preserve_symlinks, update,
170 dry_run=dry_run))
Greg Wardaebf7062000-04-04 02:05:59 +0000171 else:
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +0000172 copy_file(src_name, dst_name, preserve_mode,
173 preserve_times, update, dry_run=dry_run)
Greg Ward071ed762000-09-26 02:12:31 +0000174 outputs.append(dst_name)
Greg Wardaebf7062000-04-04 02:05:59 +0000175
176 return outputs
177
178# copy_tree ()
179
Greg Ward039accf2000-06-17 01:58:14 +0000180# Helper for remove_tree()
181def _build_cmdtuple(path, cmdtuples):
182 for f in os.listdir(path):
183 real_f = os.path.join(path,f)
184 if os.path.isdir(real_f) and not os.path.islink(real_f):
185 _build_cmdtuple(real_f, cmdtuples)
186 else:
187 cmdtuples.append((os.remove, real_f))
188 cmdtuples.append((os.rmdir, path))
189
Greg Wardaebf7062000-04-04 02:05:59 +0000190
191def remove_tree (directory, verbose=0, dry_run=0):
192 """Recursively remove an entire directory tree. Any errors are ignored
Greg Wardfcd4f872000-06-17 02:18:19 +0000193 (apart from being reported to stdout if 'verbose' is true).
194 """
195 from distutils.util import grok_environment_error
Greg Wardb248b7f2000-06-17 02:19:30 +0000196 global _path_created
Greg Wardfcd4f872000-06-17 02:18:19 +0000197
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +0000198 log.info("removing '%s' (and everything under it)", directory)
Greg Wardaebf7062000-04-04 02:05:59 +0000199 if dry_run:
200 return
Greg Ward039accf2000-06-17 01:58:14 +0000201 cmdtuples = []
202 _build_cmdtuple(directory, cmdtuples)
203 for cmd in cmdtuples:
204 try:
205 apply(cmd[0], (cmd[1],))
206 # remove dir from cache if it's already there
Greg Ward963cd2d2000-09-30 17:47:17 +0000207 abspath = os.path.abspath(cmd[1])
Guido van Rossum8bc09652008-02-21 18:18:37 +0000208 if abspath in _path_created:
Greg Ward963cd2d2000-09-30 17:47:17 +0000209 del _path_created[abspath]
Greg Ward039accf2000-06-17 01:58:14 +0000210 except (IOError, OSError), exc:
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +0000211 log.warn(grok_environment_error(
212 exc, "error removing %s: " % directory))
Andrew M. Kuchling40f23e02002-11-26 17:42:48 +0000213
214
215def ensure_relative (path):
216 """Take the full path 'path', and make it a relative path so
217 it can be the second argument to os.path.join().
218 """
219 drive, path = os.path.splitdrive(path)
220 if sys.platform == 'mac':
221 return os.sep + path
222 else:
223 if path[0:1] == os.sep:
224 path = drive + path[1:]
225 return path