OLD | NEW |
1 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """Generic utils.""" | 5 """Generic utils.""" |
6 | 6 |
7 import errno | 7 import errno |
8 import logging | 8 import logging |
9 import os | 9 import os |
10 import Queue | 10 import Queue |
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
175 | 175 |
176 | 176 |
177 def FileWrite(filename, content, mode='w'): | 177 def FileWrite(filename, content, mode='w'): |
178 f = open(filename, mode) | 178 f = open(filename, mode) |
179 try: | 179 try: |
180 f.write(content) | 180 f.write(content) |
181 finally: | 181 finally: |
182 f.close() | 182 f.close() |
183 | 183 |
184 | 184 |
185 def RemoveDirectory(*path): | 185 def rmtree(path): |
186 """Recursively removes a directory, even if it's marked read-only. | 186 """shutil.rmtree() on steroids. |
187 | 187 |
188 Remove the directory located at *path, if it exists. | 188 Recursively removes a directory, even if it's marked read-only. |
189 | 189 |
190 shutil.rmtree() doesn't work on Windows if any of the files or directories | 190 shutil.rmtree() doesn't work on Windows if any of the files or directories |
191 are read-only, which svn repositories and some .svn files are. We need to | 191 are read-only, which svn repositories and some .svn files are. We need to |
192 be able to force the files to be writable (i.e., deletable) as we traverse | 192 be able to force the files to be writable (i.e., deletable) as we traverse |
193 the tree. | 193 the tree. |
194 | 194 |
195 Even with all this, Windows still sometimes fails to delete a file, citing | 195 Even with all this, Windows still sometimes fails to delete a file, citing |
196 a permission error (maybe something to do with antivirus scans or disk | 196 a permission error (maybe something to do with antivirus scans or disk |
197 indexing). The best suggestion any of the user forums had was to wait a | 197 indexing). The best suggestion any of the user forums had was to wait a |
198 bit and try again, so we do that too. It's hand-waving, but sometimes it | 198 bit and try again, so we do that too. It's hand-waving, but sometimes it |
199 works. :/ | 199 works. :/ |
200 | 200 |
201 On POSIX systems, things are a little bit simpler. The modes of the files | 201 On POSIX systems, things are a little bit simpler. The modes of the files |
202 to be deleted doesn't matter, only the modes of the directories containing | 202 to be deleted doesn't matter, only the modes of the directories containing |
203 them are significant. As the directory tree is traversed, each directory | 203 them are significant. As the directory tree is traversed, each directory |
204 has its mode set appropriately before descending into it. This should | 204 has its mode set appropriately before descending into it. This should |
205 result in the entire tree being removed, with the possible exception of | 205 result in the entire tree being removed, with the possible exception of |
206 *path itself, because nothing attempts to change the mode of its parent. | 206 *path itself, because nothing attempts to change the mode of its parent. |
207 Doing so would be hazardous, as it's not a directory slated for removal. | 207 Doing so would be hazardous, as it's not a directory slated for removal. |
208 In the ordinary case, this is not a problem: for our purposes, the user | 208 In the ordinary case, this is not a problem: for our purposes, the user |
209 will never lack write permission on *path's parent. | 209 will never lack write permission on *path's parent. |
210 """ | 210 """ |
211 logging.debug(path) | 211 if not os.path.exists(path): |
212 file_path = os.path.join(*path) | |
213 if not os.path.exists(file_path): | |
214 return | 212 return |
215 | 213 |
216 if os.path.islink(file_path) or not os.path.isdir(file_path): | 214 if os.path.islink(path) or not os.path.isdir(path): |
217 raise Error('RemoveDirectory asked to remove non-directory %s' % file_path) | 215 raise Error('Called rmtree(%s) in non-directory' % path) |
218 | 216 |
219 has_win32api = False | |
220 if sys.platform == 'win32': | 217 if sys.platform == 'win32': |
221 has_win32api = True | |
222 # Some people don't have the APIs installed. In that case we'll do without. | 218 # Some people don't have the APIs installed. In that case we'll do without. |
223 try: | 219 try: |
224 win32api = __import__('win32api') | 220 win32api = __import__('win32api') |
225 win32con = __import__('win32con') | 221 win32con = __import__('win32con') |
226 except ImportError: | 222 except ImportError: |
227 has_win32api = False | 223 pass |
228 else: | 224 else: |
229 # On POSIX systems, we need the x-bit set on the directory to access it, | 225 # On POSIX systems, we need the x-bit set on the directory to access it, |
230 # the r-bit to see its contents, and the w-bit to remove files from it. | 226 # the r-bit to see its contents, and the w-bit to remove files from it. |
231 # The actual modes of the files within the directory is irrelevant. | 227 # The actual modes of the files within the directory is irrelevant. |
232 os.chmod(file_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) | 228 os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) |
233 for fn in os.listdir(file_path): | |
234 fullpath = os.path.join(file_path, fn) | |
235 | 229 |
| 230 def remove(func, subpath): |
| 231 if sys.platform == 'win32': |
| 232 os.chmod(subpath, stat.S_IWRITE) |
| 233 if win32api and win32con: |
| 234 win32api.SetFileAttributes(subpath, win32con.FILE_ATTRIBUTE_NORMAL) |
| 235 try: |
| 236 func(subpath) |
| 237 except OSError, e: |
| 238 if e.errno != errno.EACCES or sys.platform != 'win32': |
| 239 raise |
| 240 # Failed to delete, try again after a 100ms sleep. |
| 241 time.sleep(0.1) |
| 242 func(subpath) |
| 243 |
| 244 for fn in os.listdir(path): |
236 # If fullpath is a symbolic link that points to a directory, isdir will | 245 # If fullpath is a symbolic link that points to a directory, isdir will |
237 # be True, but we don't want to descend into that as a directory, we just | 246 # be True, but we don't want to descend into that as a directory, we just |
238 # want to remove the link. Check islink and treat links as ordinary files | 247 # want to remove the link. Check islink and treat links as ordinary files |
239 # would be treated regardless of what they reference. | 248 # would be treated regardless of what they reference. |
| 249 fullpath = os.path.join(path, fn) |
240 if os.path.islink(fullpath) or not os.path.isdir(fullpath): | 250 if os.path.islink(fullpath) or not os.path.isdir(fullpath): |
241 if sys.platform == 'win32': | 251 remove(os.remove, fullpath) |
242 os.chmod(fullpath, stat.S_IWRITE) | |
243 if has_win32api: | |
244 win32api.SetFileAttributes(fullpath, win32con.FILE_ATTRIBUTE_NORMAL) | |
245 try: | |
246 os.remove(fullpath) | |
247 except OSError, e: | |
248 if e.errno != errno.EACCES or sys.platform != 'win32': | |
249 raise | |
250 print 'Failed to delete %s: trying again' % fullpath | |
251 time.sleep(0.1) | |
252 os.remove(fullpath) | |
253 else: | 252 else: |
254 RemoveDirectory(fullpath) | 253 # Recurse. |
| 254 rmtree(fullpath) |
255 | 255 |
256 if sys.platform == 'win32': | 256 remove(os.rmdir, path) |
257 os.chmod(file_path, stat.S_IWRITE) | 257 |
258 if has_win32api: | 258 # TODO(maruel): Rename the references. |
259 win32api.SetFileAttributes(file_path, win32con.FILE_ATTRIBUTE_NORMAL) | 259 RemoveDirectory = rmtree |
260 try: | |
261 os.rmdir(file_path) | |
262 except OSError, e: | |
263 if e.errno != errno.EACCES or sys.platform != 'win32': | |
264 raise | |
265 print 'Failed to remove %s: trying again' % file_path | |
266 time.sleep(0.1) | |
267 os.rmdir(file_path) | |
268 | 260 |
269 | 261 |
270 def CheckCallAndFilterAndHeader(args, always=False, **kwargs): | 262 def CheckCallAndFilterAndHeader(args, always=False, **kwargs): |
271 """Adds 'header' support to CheckCallAndFilter. | 263 """Adds 'header' support to CheckCallAndFilter. |
272 | 264 |
273 If |always| is True, a message indicating what is being done | 265 If |always| is True, a message indicating what is being done |
274 is printed to stdout all the time even if not output is generated. Otherwise | 266 is printed to stdout all the time even if not output is generated. Otherwise |
275 the message header is printed only if the call generated any ouput. | 267 the message header is printed only if the call generated any ouput. |
276 """ | 268 """ |
277 stdout = kwargs.get('stdout', None) or sys.stdout | 269 stdout = kwargs.get('stdout', None) or sys.stdout |
(...skipping 430 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
708 logging.info('Caught exception in thread %s' % self.item.name) | 700 logging.info('Caught exception in thread %s' % self.item.name) |
709 logging.info(str(sys.exc_info())) | 701 logging.info(str(sys.exc_info())) |
710 work_queue.exceptions.put(sys.exc_info()) | 702 work_queue.exceptions.put(sys.exc_info()) |
711 logging.info('Task %s done' % self.item.name) | 703 logging.info('Task %s done' % self.item.name) |
712 | 704 |
713 work_queue.ready_cond.acquire() | 705 work_queue.ready_cond.acquire() |
714 try: | 706 try: |
715 work_queue.ready_cond.notifyAll() | 707 work_queue.ready_cond.notifyAll() |
716 finally: | 708 finally: |
717 work_queue.ready_cond.release() | 709 work_queue.ready_cond.release() |
OLD | NEW |