Message:
This supersedes the CL I did that attempted to use os.path.islink, which
didn't
work since os.path.islink always returns False in Python 2.x on Windows:
http://codereview.chromium.org/5975009/
http://codereview.chromium.org/6068003/
(That change apparently didn't do anything, since the svn repo got moved
from
trunk/tools/buildbot/
to
trunk/tools/build/)
Description:
Use shutil.rmtree's 'onerror' error handling parameter to catch and handle
the
error that occurs when it's used on a symlink. Ideally, we would just check
os.path.islink as the code does for Linux/Mac, but os.path.islink doesn't
work
properly in python 2.x on Windows.
BUG= http://code.google.com/p/nativeclient/issues/detail?id=1285
TEST=Tested on manually on the bot as follows:
> mkdir test
> mkdir test\1
> mklink /D test\current test\1
> python -c "import chromium_utils;
chromium_utils.RemoveDirectory('test')"
Please review this at http://codereview.chromium.org/6015012/
SVN Base: svn://svn.chromium.org/chrome/trunk/tools/build/
Affected files:
M scripts/common/chromium_utils.py
Index: scripts/common/chromium_utils.py
===================================================================
--- scripts/common/chromium_utils.py (revision 70419)
+++ scripts/common/chromium_utils.py (working copy)
@@ -266,6 +266,36 @@
else:
remove_with_retry = RemoveWithRetry_non_win
+ # This works around a problem whereby python 2.x on Windows has no
ability to
+ # check for symbolic links. os.path.islink always returns False. But
+ # shutil.rmtree will fail if invoked on a symbolic link whose target was
+ # deleted before the link. E.g., reproduce like this:
+ # > mkdir test
+ # > mkdir test\1
+ # > mklink /D test\current test\1
+ # > python -c "import chromium_utils;
chromium_utils.RemoveDirectory('test')"
+ # To avoid this issue, we pass this error-handling function to rmtree.
If
+ # we see the exact sort of failure, we ignore it. All other failures we
re-
+ # raise.
+ def RmTreeOnError(function, path, excinfo):
+ exception_type = excinfo[0]
+ exception_value = excinfo[1]
+ # If shutil.rmtree encounters a symbolic link on Windows, os.listdir
will
+ # fail with a WindowsError exception with an ENOENT errno (i.e., file
not
+ # found). We'll ignore that error. Note that WindowsError is not
defined
+ # for non-Windows platforms, so we use OSError (of which it is a
subclass)
+ # to avoid lint complaints about an undefined global on non-Windows
+ # platforms.
+ if (function is os.listdir) and issubclass(exception_type, OSError):
+ if exception_value.errno == errno.ENOENT:
+ # File does not exist, and we're trying to delete, so we can
ignore the
+ # failure.
+ print "WARNING: Failed to list %s during rmtree. Ignoring.\n" %
path
+ else:
+ raise
+ else:
+ raise
+
for root, dirs, files in os.walk(file_path, topdown=False):
# For POSIX: making the directory writable guarantees removability.
# Windows will ignore the non-read-only bits in the chmod value.
@@ -273,7 +303,8 @@
for name in files:
remove_with_retry(os.remove, os.path.join(root, name))
for name in dirs:
- remove_with_retry(shutil.rmtree, os.path.join(root, name))
+ remove_with_retry(lambda p: shutil.rmtree(p, onerror=RmTreeOnError),
+ os.path.join(root, name))
remove_with_retry(os.rmdir, file_path)