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 stand-alone file operations."""
15 def AtomicWriteFile(data, filename):
16 """Write a file atomically.
18 NOTE: Not atomic on Windows!
20 data: String to write to the file.
21 filename: Filename to write.
23 filename = os.path.abspath(filename)
24 handle, temp_file = tempfile.mkstemp(
25 prefix='atomic_write', suffix='.tmp',
26 dir=os.path.dirname(filename))
27 fh = os.fdopen(handle, 'wb')
30 # Window's can't move into place atomically, delete first.
31 if sys.platform in ['win32', 'cygwin']:
36 os.rename(temp_file, filename)
39 def WriteFile(data, filename):
40 """Write a file in one step.
43 data: String to write to the file.
44 filename: Filename to write.
46 fh = open(filename, 'wb')
51 def ReadFile(filename):
52 """Read a file in one step.
55 filename: Filename to read.
57 String containing complete file.
59 fh = open(filename, 'rb')
65 class ExecutableNotFound(Exception):
69 def Which(command, paths=None, require_executable=True):
70 """Find the absolute path of a command in the current PATH.
73 command: Command name to look for.
74 paths: Optional paths to search.
76 Absolute path of the command (first one found),
77 or default to a bare command if nothing is found.
80 paths = os.environ.get('PATH', '').split(os.pathsep)
82 if sys.platform == 'win32':
83 exe_suffixes += ['.exe']
85 np = os.path.abspath(os.path.join(p, command))
86 for suffix in exe_suffixes:
87 full_path = np + suffix
88 if (os.path.isfile(full_path) and
89 (not require_executable or os.access(full_path, os.X_OK))):
91 raise ExecutableNotFound('Unable to find: ' + command)
94 def MakeDirectoryIfAbsent(path):
95 """Create a directory if it doesn't already exist.
98 path: Directory to create.
100 if not os.path.exists(path):
104 def RemoveDirectoryIfPresent(path):
105 """Remove a directory if it exists.
108 path: Directory to remove.
111 # On Windows, attempts to remove read-only files get Error 5. This
112 # error handler fixes the permissions and retries the removal.
113 def onerror_readonly(func, path, exc_info):
115 if not os.access(path, os.W_OK):
116 os.chmod(path, stat.S_IWUSR)
119 if os.path.exists(path):
120 shutil.rmtree(path, onerror=onerror_readonly)
123 def CopyTree(src, dst):
124 """Recursively copy the items in the src directory to the dst directory.
126 Unlike shutil.copytree, the destination directory and any subdirectories and
127 files may exist. Existing directories are left untouched, and existing files
128 are removed and copied from the source using shutil.copy2. It is also not
132 src: Source. Must be an existing directory.
133 dst: Destination directory. If it exists, must be a directory. Otherwise it
134 will be created, along with parent directories.
136 if not os.path.isdir(dst):
138 for root, dirs, files in os.walk(src):
139 relroot = os.path.relpath(root, src)
140 dstroot = os.path.join(dst, relroot)
142 dstdir = os.path.join(dstroot, d)
143 if not os.path.isdir(dstdir):
146 dstfile = os.path.join(dstroot, f)
147 if os.path.isfile(dstfile):
149 shutil.copy2(os.path.join(root, f), dstfile)