2 # Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """Convience file system related operations."""
18 def AtomicWriteFile(data, filename):
19 """Write a file atomically.
21 NOTE: Not atomic on Windows!
23 data: String to write to the file.
24 filename: Filename to write.
26 filename = os.path.abspath(filename)
27 handle, temp_file = tempfile.mkstemp(
28 prefix='atomic_write', suffix='.tmp',
29 dir=os.path.dirname(filename))
30 fh = os.fdopen(handle, 'wb')
33 # Window's can't move into place atomically, delete first.
34 if sys.platform in ['win32', 'cygwin']:
39 os.rename(temp_file, filename)
42 def WriteFile(data, filename):
43 """Write a file in one step.
46 data: String to write to the file.
47 filename: Filename to write.
49 fh = open(filename, 'wb')
54 def ReadFile(filename):
55 """Read a file in one step.
58 filename: Filename to read.
60 String containing complete file.
62 fh = open(filename, 'rb')
68 class ExecutableNotFound(Exception):
72 def Which(command, paths=None, require_executable=True):
73 """Find the absolute path of a command in the current PATH.
76 command: Command name to look for.
77 paths: Optional paths to search.
79 Absolute path of the command (first one found),
80 or default to a bare command if nothing is found.
83 paths = os.environ.get('PATH', '').split(os.pathsep)
85 if sys.platform == 'win32':
86 exe_suffixes += ['.exe']
88 np = os.path.abspath(os.path.join(p, command))
89 for suffix in exe_suffixes:
90 full_path = np + suffix
91 if (os.path.isfile(full_path) and
92 (not require_executable or os.access(full_path, os.X_OK))):
94 raise ExecutableNotFound('Unable to find: ' + command)
97 def MakeDirectoryIfAbsent(path):
98 """Create a directory if it doesn't already exist.
101 path: Directory to create.
103 if not os.path.isdir(path):
107 def MakeParentDirectoryIfAbsent(path):
108 """Creates a directory for the parent if it doesn't already exist.
111 path: Path of child where parent directory should be created for.
113 MakeDirectoryIfAbsent(os.path.dirname(path))
116 def RemoveDirectoryIfPresent(path):
117 """Remove a directory if it exists.
120 path: Directory to remove.
122 # On Windows, attempts to remove read-only files get Error 5. This
123 # error handler fixes the permissions and retries the removal.
124 def onerror_readonly(func, path, exc_info):
126 if not os.access(path, os.W_OK):
127 os.chmod(path, stat.S_IWUSR)
130 if os.path.exists(path):
131 shutil.rmtree(path, onerror=onerror_readonly)
134 def CopyTree(src, dst):
135 """Recursively copy the items in the src directory to the dst directory.
137 Unlike shutil.copytree, the destination directory and any subdirectories and
138 files may exist. Existing directories are left untouched, and existing files
139 are removed and copied from the source using shutil.copy2. It is also not
143 src: Source. Must be an existing directory.
144 dst: Destination directory. If it exists, must be a directory. Otherwise it
145 will be created, along with parent directories.
147 if not os.path.isdir(dst):
149 for root, dirs, files in os.walk(src):
150 relroot = os.path.relpath(root, src)
151 dstroot = os.path.join(dst, relroot)
153 dstdir = os.path.join(dstroot, d)
154 if not os.path.isdir(dstdir):
157 dstfile = os.path.join(dstroot, f)
158 if os.path.isfile(dstfile):
160 shutil.copy2(os.path.join(root, f), dstfile)
163 def MoveAndMergeDirTree(src_dir, dest_dir):
164 """Moves everything from a source directory to a destination directory.
166 This is different from shutil's move implementation in that it only operates
167 on directories, and if the destination directory exists, it will move the
168 contents into the directory and merge any existing directories.
171 src_dir: Source directory which files should be moved from.
172 dest_dir: Destination directory where files should be moved and merged to.
174 if not os.path.isdir(src_dir):
175 raise OSError('MoveAndMergeDirTree can only operate on directories.')
177 if not os.path.exists(dest_dir):
178 # Simply move the directory over if destination doesn't exist.
179 MakeParentDirectoryIfAbsent(dest_dir)
180 os.rename(src_dir, dest_dir)
182 # Merge each item if destination directory exists.
183 for dir_item in os.listdir(src_dir):
184 source_item = os.path.join(src_dir, dir_item)
185 destination_item = os.path.join(dest_dir, dir_item)
186 if os.path.exists(destination_item):
187 if os.path.isdir(destination_item) and os.path.isdir(source_item):
188 # Merge the sub-directories together if they are both directories.
189 MoveAndMergeDirTree(source_item, destination_item)
190 elif os.path.isfile(destination_item) and os.path.isfile(source_item):
191 # Overwrite the file if they are both files.
192 os.unlink(destination_item)
193 os.rename(source_item, destination_item)
195 raise OSError('Cannot move directory tree, mismatching types.'
196 ' Source - %s. Destination - %s' %
197 (source_item, destination_item))
199 os.rename(source_item, destination_item)
201 # Remove the directory once all the contents have been moved
205 def Retry(op, *args):
206 # Windows seems to be prone to having commands that delete files or
207 # directories fail. We currently do not have a complete understanding why,
208 # and as a workaround we simply retry the command a few times.
209 # It appears that file locks are hanging around longer than they should. This
210 # may be a secondary effect of processes hanging around longer than they
211 # should. This may be because when we kill a browser sel_ldr does not exit
213 # Virus checkers can also accidently prevent files from being deleted, but
214 # that shouldn't be a problem on the bots.
215 if platform.IsWindows():
222 sys.stdout.write('FAILED: %s %s\n' % (op.__name__, repr(args)))
225 sys.stdout.write('RETRY: %s %s\n' % (op.__name__, repr(args)))
226 time.sleep(pow(2, count))
228 # Don't mask the exception.
234 def MoveDirCleanly(src, dst):
239 def MoveDir(src, dst):
240 Retry(shutil.move, src, dst)
244 if os.path.exists(path):
245 Retry(shutil.rmtree, path)
248 def RemoveFile(path):
249 if os.path.exists(path):
250 Retry(os.unlink, path)