| 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 |