Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / native_client / pynacl / file_tools.py
1 #!/usr/bin/python
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.
5
6 """Convience file system related operations."""
7
8
9 import os
10 import shutil
11 import sys
12 import tempfile
13
14 import platform
15 import time
16
17
18 def AtomicWriteFile(data, filename):
19   """Write a file atomically.
20
21   NOTE: Not atomic on Windows!
22   Args:
23     data: String to write to the file.
24     filename: Filename to write.
25   """
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')
31   fh.write(data)
32   fh.close()
33   # Window's can't move into place atomically, delete first.
34   if sys.platform in ['win32', 'cygwin']:
35     try:
36       os.remove(filename)
37     except OSError:
38       pass
39   os.rename(temp_file, filename)
40
41
42 def WriteFile(data, filename):
43   """Write a file in one step.
44
45   Args:
46     data: String to write to the file.
47     filename: Filename to write.
48   """
49   fh = open(filename, 'wb')
50   fh.write(data)
51   fh.close()
52
53
54 def ReadFile(filename):
55   """Read a file in one step.
56
57   Args:
58     filename: Filename to read.
59   Returns:
60     String containing complete file.
61   """
62   fh = open(filename, 'rb')
63   data = fh.read()
64   fh.close()
65   return data
66
67
68 class ExecutableNotFound(Exception):
69   pass
70
71
72 def Which(command, paths=None, require_executable=True):
73   """Find the absolute path of a command in the current PATH.
74
75   Args:
76     command: Command name to look for.
77     paths: Optional paths to search.
78   Returns:
79     Absolute path of the command (first one found),
80     or default to a bare command if nothing is found.
81   """
82   if paths is None:
83     paths = os.environ.get('PATH', '').split(os.pathsep)
84   exe_suffixes = ['']
85   if sys.platform == 'win32':
86     exe_suffixes += ['.exe']
87   for p in paths:
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))):
93         return full_path
94   raise ExecutableNotFound('Unable to find: ' + command)
95
96
97 def MakeDirectoryIfAbsent(path):
98   """Create a directory if it doesn't already exist.
99
100   Args:
101     path: Directory to create.
102   """
103   if not os.path.isdir(path):
104     os.makedirs(path)
105
106
107 def MakeParentDirectoryIfAbsent(path):
108   """Creates a directory for the parent if it doesn't already exist.
109
110   Args:
111     path: Path of child where parent directory should be created for.
112   """
113   MakeDirectoryIfAbsent(os.path.dirname(path))
114
115
116 def RemoveDirectoryIfPresent(path):
117   """Remove a directory if it exists.
118
119   Args:
120     path: Directory to remove.
121   """
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):
125     import stat
126     if not os.access(path, os.W_OK):
127       os.chmod(path, stat.S_IWUSR)
128       func(path)
129
130   if os.path.exists(path):
131     shutil.rmtree(path, onerror=onerror_readonly)
132
133
134 def CopyTree(src, dst):
135   """Recursively copy the items in the src directory to the dst directory.
136
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
140   symlink-aware.
141
142   Args:
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.
146   """
147   if not os.path.isdir(dst):
148     os.makedirs(dst)
149   for root, dirs, files in os.walk(src):
150     relroot = os.path.relpath(root, src)
151     dstroot = os.path.join(dst, relroot)
152     for d in dirs:
153       dstdir = os.path.join(dstroot, d)
154       if not os.path.isdir(dstdir):
155         os.mkdir(dstdir)
156     for f in files:
157       dstfile = os.path.join(dstroot, f)
158       if os.path.isfile(dstfile):
159         os.remove(dstfile)
160       shutil.copy2(os.path.join(root, f), dstfile)
161
162
163 def MoveAndMergeDirTree(src_dir, dest_dir):
164   """Moves everything from a source directory to a destination directory.
165
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.
169
170   Args:
171     src_dir: Source directory which files should be moved from.
172     dest_dir: Destination directory where files should be moved and merged to.
173   """
174   if not os.path.isdir(src_dir):
175     raise OSError('MoveAndMergeDirTree can only operate on directories.')
176
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)
181   else:
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)
194         else:
195           raise OSError('Cannot move directory tree, mismatching types.'
196                         ' Source - %s. Destination - %s' %
197                         (source_item, destination_item))
198       else:
199         os.rename(source_item, destination_item)
200
201     # Remove the directory once all the contents have been moved
202     os.rmdir(src_dir)
203
204
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
212   # immediately, etc.
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():
216     count = 0
217     while True:
218       try:
219         op(*args)
220         break
221       except Exception:
222         sys.stdout.write('FAILED: %s %s\n' % (op.__name__, repr(args)))
223         count += 1
224         if count < 5:
225           sys.stdout.write('RETRY: %s %s\n' % (op.__name__, repr(args)))
226           time.sleep(pow(2, count))
227         else:
228           # Don't mask the exception.
229           raise
230   else:
231     op(*args)
232
233
234 def MoveDirCleanly(src, dst):
235   RemoveDir(dst)
236   MoveDir(src, dst)
237
238
239 def MoveDir(src, dst):
240   Retry(shutil.move, src, dst)
241
242
243 def RemoveDir(path):
244   if os.path.exists(path):
245     Retry(shutil.rmtree, path)
246
247
248 def RemoveFile(path):
249   if os.path.exists(path):
250     Retry(os.unlink, path)