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