Index: build/get_syzygy_binaries.py |
diff --git a/build/get_syzygy_binaries.py b/build/get_syzygy_binaries.py |
index 999d8ce2a4b1f7adaca686386747422b8182ab7f..ac1290224dc8cd1fdea30d9042b4c6331e35f231 100755 |
--- a/build/get_syzygy_binaries.py |
+++ b/build/get_syzygy_binaries.py |
@@ -7,12 +7,15 @@ |
import cStringIO |
import hashlib |
+import errno |
import json |
import logging |
import optparse |
import os |
import re |
import shutil |
+import stat |
+import sys |
import subprocess |
import urllib2 |
import zipfile |
@@ -172,6 +175,27 @@ def _DirIsEmpty(path): |
return not dirs and not files |
+def _RmTreeHandleReadOnly(func, path, exc): |
+ """An error handling function for use with shutil.rmtree. This will |
+ detect failures to remove read-only files, and will change their properties |
+ prior to removing them. This is necessary on Windows as os.remove will return |
+ an access error for read-only files, and git repos contain read-only |
+ pack/index files. |
+ """ |
+ excvalue = exc[1] |
+ if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES: |
+ _LOGGER.debug('Removing read-only path: %s', path) |
+ os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) |
+ func(path) |
+ else: |
+ raise |
+ |
+ |
+def _RmTree(path): |
+ """A wrapper of shutil.rmtree that handles read-only files.""" |
+ shutil.rmtree(path, ignore_errors=False, onerror=_RmTreeHandleReadOnly) |
+ |
+ |
def _CleanState(output_dir, state, dry_run=False): |
"""Cleans up files/directories in |output_dir| that are referenced by |
the given |state|. Raises an error if there are local changes. Returns a |
@@ -216,7 +240,7 @@ def _CleanState(output_dir, state, dry_run=False): |
if os.path.exists(p) and _DirIsEmpty(p): |
_LOGGER.debug('Deleting empty directory "%s".', p) |
if not dry_run: |
- shutil.rmtree(p, False) |
+ _RmTree(p) |
return deleted |
@@ -328,6 +352,11 @@ def _ParseCommandLine(): |
def main(): |
+ # We only care about Windows platforms, as the Syzygy binaries aren't used |
+ # elsewhere. |
+ if sys.platform != 'win32': |
+ return |
+ |
options = _ParseCommandLine() |
if options.dry_run: |
@@ -356,7 +385,7 @@ def main(): |
# If overwrite was specified then take a heavy-handed approach. |
_LOGGER.debug('Deleting entire installation directory.') |
if not options.dry_run: |
- shutil.rmtree(options.output_dir, False) |
+ _RmTree(options.output_dir) |
else: |
# Otherwise only delete things that the previous installation put in place, |
# and take care to preserve any local changes. |