blob: c049bbd217e9f2767fac3bf39d84254df774ed68 [file] [log] [blame]
Greg Wardaebf7062000-04-04 02:05:59 +00001"""distutils.dir_util
2
3Utility functions for manipulating directories and directory trees."""
4
5# created 2000/04/03, Greg Ward (extracted from util.py)
6
7__revision__ = "$Id$"
8
9import os
10from distutils.errors import DistutilsFileError
11
12
13# cache for by mkpath() -- in addition to cheapening redundant calls,
14# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode
15PATH_CREATED = {}
16
17# I don't use os.makedirs because a) it's new to Python 1.5.2, and
18# b) it blows up if the directory already exists (I want to silently
19# succeed in that case).
20def mkpath (name, mode=0777, verbose=0, dry_run=0):
21 """Create a directory and any missing ancestor directories. If the
22 directory already exists (or if 'name' is the empty string, which
23 means the current directory, which of course exists), then do
24 nothing. Raise DistutilsFileError if unable to create some
25 directory along the way (eg. some sub-path exists, but is a file
26 rather than a directory). If 'verbose' is true, print a one-line
27 summary of each mkdir to stdout. Return the list of directories
28 actually created."""
29
30 global PATH_CREATED
31
32 # XXX what's the better way to handle verbosity? print as we create
33 # each directory in the path (the current behaviour), or only announce
34 # the creation of the whole path? (quite easy to do the latter since
35 # we're not using a recursive algorithm)
36
37 name = os.path.normpath (name)
38 created_dirs = []
39 if os.path.isdir (name) or name == '':
40 return created_dirs
41 if PATH_CREATED.get (name):
42 return created_dirs
43
44 (head, tail) = os.path.split (name)
45 tails = [tail] # stack of lone dirs to create
46
47 while head and tail and not os.path.isdir (head):
48 #print "splitting '%s': " % head,
49 (head, tail) = os.path.split (head)
50 #print "to ('%s','%s')" % (head, tail)
51 tails.insert (0, tail) # push next higher dir onto stack
52
53 #print "stack of tails:", tails
54
55 # now 'head' contains the deepest directory that already exists
56 # (that is, the child of 'head' in 'name' is the highest directory
57 # that does *not* exist)
58 for d in tails:
59 #print "head = %s, d = %s: " % (head, d),
60 head = os.path.join (head, d)
61 if PATH_CREATED.get (head):
62 continue
63
64 if verbose:
65 print "creating", head
66
67 if not dry_run:
68 try:
69 os.mkdir (head)
70 created_dirs.append(head)
71 except OSError, exc:
72 raise DistutilsFileError, \
73 "could not create '%s': %s" % (head, exc[-1])
74
75 PATH_CREATED[head] = 1
76 return created_dirs
77
78# mkpath ()
79
80
81def create_tree (base_dir, files, mode=0777, verbose=0, dry_run=0):
82
83 """Create all the empty directories under 'base_dir' needed to
84 put 'files' there. 'base_dir' is just the a name of a directory
85 which doesn't necessarily exist yet; 'files' is a list of filenames
86 to be interpreted relative to 'base_dir'. 'base_dir' + the
87 directory portion of every file in 'files' will be created if it
88 doesn't already exist. 'mode', 'verbose' and 'dry_run' flags are as
89 for 'mkpath()'."""
90
91 # First get the list of directories to create
92 need_dir = {}
93 for file in files:
94 need_dir[os.path.join (base_dir, os.path.dirname (file))] = 1
95 need_dirs = need_dir.keys()
96 need_dirs.sort()
97
98 # Now create them
99 for dir in need_dirs:
100 mkpath (dir, mode, verbose, dry_run)
101
102# create_tree ()
103
104
105def copy_tree (src, dst,
106 preserve_mode=1,
107 preserve_times=1,
108 preserve_symlinks=0,
109 update=0,
110 verbose=0,
111 dry_run=0):
112
113 """Copy an entire directory tree 'src' to a new location 'dst'. Both
114 'src' and 'dst' must be directory names. If 'src' is not a
115 directory, raise DistutilsFileError. If 'dst' does not exist, it is
116 created with 'mkpath()'. The end result of the copy is that every
117 file in 'src' is copied to 'dst', and directories under 'src' are
118 recursively copied to 'dst'. Return the list of files that were
119 copied or might have been copied, using their output name. The
120 return value is unaffected by 'update' or 'dry_run': it is simply
121 the list of all files under 'src', with the names changed to be
122 under 'dst'.
123
124 'preserve_mode' and 'preserve_times' are the same as for
125 'copy_file'; note that they only apply to regular files, not to
126 directories. If 'preserve_symlinks' is true, symlinks will be
127 copied as symlinks (on platforms that support them!); otherwise
128 (the default), the destination of the symlink will be copied.
129 'update' and 'verbose' are the same as for 'copy_file'."""
130
131 from distutils.file_util import copy_file
132
133 if not dry_run and not os.path.isdir (src):
134 raise DistutilsFileError, \
135 "cannot copy tree '%s': not a directory" % src
136 try:
137 names = os.listdir (src)
138 except os.error, (errno, errstr):
139 if dry_run:
140 names = []
141 else:
142 raise DistutilsFileError, \
143 "error listing files in '%s': %s" % (src, errstr)
144
145 if not dry_run:
146 mkpath (dst, verbose=verbose)
147
148 outputs = []
149
150 for n in names:
151 src_name = os.path.join (src, n)
152 dst_name = os.path.join (dst, n)
153
154 if preserve_symlinks and os.path.islink (src_name):
155 link_dest = os.readlink (src_name)
156 if verbose:
157 print "linking %s -> %s" % (dst_name, link_dest)
158 if not dry_run:
159 os.symlink (link_dest, dst_name)
160 outputs.append (dst_name)
161
162 elif os.path.isdir (src_name):
163 outputs.extend (
164 copy_tree (src_name, dst_name,
165 preserve_mode, preserve_times, preserve_symlinks,
166 update, verbose, dry_run))
167 else:
168 copy_file (src_name, dst_name,
169 preserve_mode, preserve_times,
170 update, None, verbose, dry_run)
171 outputs.append (dst_name)
172
173 return outputs
174
175# copy_tree ()
176
177
178def remove_tree (directory, verbose=0, dry_run=0):
179 """Recursively remove an entire directory tree. Any errors are ignored
180 (apart from being reported to stdout if 'verbose' is true)."""
181
182 from shutil import rmtree
183
184 if verbose:
185 print "removing '%s' (and everything under it)" % directory
186 if dry_run:
187 return
188 try:
189 rmtree(directory,1)
190 except (IOError, OSError), exc:
191 if verbose:
192 if exc.filename:
193 print "error removing %s: %s (%s)" % \
194 (directory, exc.strerror, exc.filename)
195 else:
196 print "error removing %s: %s" % (directory, exc.strerror)