OLD | NEW |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 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 """Utility functions for sdk_update.py and sdk_update_main.py.""" | 5 """Utility functions for sdk_update.py and sdk_update_main.py.""" |
6 | 6 |
7 import errno | 7 import errno |
| 8 import logging |
8 import os | 9 import os |
9 import shutil | 10 import shutil |
10 import subprocess | 11 import subprocess |
11 import sys | 12 import sys |
12 import time | 13 import time |
13 | 14 |
14 | 15 |
15 class Error(Exception): | 16 class Error(Exception): |
16 """Generic error/exception for sdk_update module""" | 17 """Generic error/exception for sdk_update module""" |
17 pass | 18 pass |
18 | 19 |
19 | 20 |
| 21 def MakeDirs(directory): |
| 22 if not os.path.exists(directory): |
| 23 logging.info('Making directory %s' % (directory,)) |
| 24 os.makedirs(directory) |
| 25 |
| 26 |
20 def RemoveDir(outdir): | 27 def RemoveDir(outdir): |
21 """Removes the given directory | 28 """Removes the given directory |
22 | 29 |
23 On Unix systems, this just runs shutil.rmtree, but on Windows, this doesn't | 30 On Unix systems, this just runs shutil.rmtree, but on Windows, this doesn't |
24 work when the directory contains junctions (as does our SDK installer). | 31 work when the directory contains junctions (as does our SDK installer). |
25 Therefore, on Windows, it runs rmdir /S /Q as a shell command. This always | 32 Therefore, on Windows, it runs rmdir /S /Q as a shell command. This always |
26 does the right thing on Windows. If the directory already didn't exist, | 33 does the right thing on Windows. If the directory already didn't exist, |
27 RemoveDir will return successfully without taking any action. | 34 RemoveDir will return successfully without taking any action. |
28 | 35 |
29 Args: | 36 Args: |
30 outdir: The directory to delete | 37 outdir: The directory to delete |
31 | 38 |
32 Raises: | 39 Raises: |
33 CalledProcessError - if the delete operation fails on Windows | 40 Error - If this operation fails for any reason. |
34 OSError - if the delete operation fails on Linux | |
35 """ | 41 """ |
36 | 42 |
37 try: | 43 max_tries = 5 |
38 shutil.rmtree(outdir) | 44 last_exception = None |
39 except OSError: | 45 for num_tries in xrange(max_tries): |
40 if not os.path.exists(outdir): | 46 try: |
| 47 shutil.rmtree(outdir) |
41 return | 48 return |
42 # On Windows this could be an issue with junctions, so try again with rmdir | 49 except OSError as e: |
| 50 if not os.path.exists(outdir): |
| 51 # The directory can't be removed because it doesn't exist. |
| 52 return |
| 53 last_exception = e |
| 54 |
| 55 # On Windows this could be an issue with junctions, so try again with |
| 56 # rmdir. |
43 if sys.platform == 'win32': | 57 if sys.platform == 'win32': |
44 subprocess.check_call(['rmdir', '/S', '/Q', outdir], shell=True) | 58 try: |
| 59 cmd = ['rmdir', '/S', '/Q', outdir] |
| 60 process = subprocess.Popen(cmd, stderr=subprocess.PIPE, shell=True) |
| 61 _, stderr = process.communicate() |
| 62 if process.returncode != 0: |
| 63 raise Error('\"%s\" failed with code %d. Output:\n %s' % ( |
| 64 ' '.join(cmd), process.returncode, stderr)) |
| 65 return |
| 66 # Ignore failures, we'll just try again. |
| 67 except subprocess.CalledProcessError as e: |
| 68 # CalledProcessError has no error message, generate one. |
| 69 last_exception = Error('\"%s\" failed with code %d.' % ( |
| 70 ' '.join(e.cmd), e.returncode)) |
| 71 except Error as e: |
| 72 last_exception = e |
| 73 |
| 74 # Didn't work, sleep and try again. |
| 75 time.sleep(num_tries + 1) |
| 76 |
| 77 # Failed. |
| 78 raise Error('Unable to remove directory "%s"\n %s' % (outdir, |
| 79 last_exception)) |
45 | 80 |
46 | 81 |
47 def RenameDir(srcdir, destdir): | 82 def RenameDir(srcdir, destdir): |
48 """Renames srcdir to destdir. Removes destdir before doing the | 83 """Renames srcdir to destdir. Removes destdir before doing the |
49 rename if it already exists.""" | 84 rename if it already exists.""" |
50 | 85 |
51 max_tries = 5 | 86 max_tries = 5 |
52 num_tries = 0 | 87 num_tries = 0 |
53 for num_tries in xrange(max_tries): | 88 for num_tries in xrange(max_tries): |
54 try: | 89 try: |
55 RemoveDir(destdir) | 90 RemoveDir(destdir) |
56 shutil.move(srcdir, destdir) | 91 shutil.move(srcdir, destdir) |
57 return | 92 return |
58 except OSError as err: | 93 except OSError as err: |
59 if err.errno != errno.EACCES: | 94 if err.errno != errno.EACCES: |
60 raise err | 95 raise err |
61 # If we are here, we didn't exit due to raised exception, so we are | 96 # If we are here, we didn't exit due to raised exception, so we are |
62 # handling a Windows flaky access error. Sleep one second and try | 97 # handling a Windows flaky access error. Sleep one second and try |
63 # again. | 98 # again. |
64 time.sleep(num_tries + 1) | 99 time.sleep(num_tries + 1) |
65 | 100 |
66 # end of while loop -- could not RenameDir | 101 # end of while loop -- could not RenameDir |
67 raise Error('Could not RenameDir %s => %s after %d tries.\n' | 102 raise Error('Could not RenameDir %s => %s after %d tries.\n' |
68 'Please check that no shells or applications ' | 103 'Please check that no shells or applications ' |
69 'are accessing files in %s.' | 104 'are accessing files in %s.' |
70 % (srcdir, destdir, num_tries, destdir)) | 105 % (srcdir, destdir, num_tries + 1, destdir)) |
OLD | NEW |