| Index: recipe_modules/shutil/resources/rmtree.py
 | 
| diff --git a/recipe_modules/shutil/resources/rmtree.py b/recipe_modules/shutil/resources/rmtree.py
 | 
| deleted file mode 100644
 | 
| index 25ef212b6d33ea6dfaae1f537cad6f49910bb040..0000000000000000000000000000000000000000
 | 
| --- a/recipe_modules/shutil/resources/rmtree.py
 | 
| +++ /dev/null
 | 
| @@ -1,111 +0,0 @@
 | 
| -# Copyright 2016 The LUCI Authors. All rights reserved.
 | 
| -# Use of this source code is governed under the Apache License, Version 2.0
 | 
| -# that can be found in the LICENSE file.
 | 
| -
 | 
| -# Copy of RemoveDirectory from scripts/common/chromium_utils.
 | 
| -# See also http://crbug.com/584783.
 | 
| -# The only difference is that now the method will properly report failures.
 | 
| -# The recipe can choose to ignore them explicitely with ok_ret='any'.
 | 
| -
 | 
| -import argparse
 | 
| -import os
 | 
| -import shutil
 | 
| -import subprocess
 | 
| -import sys
 | 
| -import time
 | 
| -
 | 
| -
 | 
| -def RemoveDirectory(file_path):
 | 
| -  """Recursively removes a directory, even if it's marked read-only.
 | 
| -
 | 
| -  Remove the directory located at *path, if it exists.
 | 
| -
 | 
| -  shutil.rmtree() doesn't work on Windows if any of the files or directories
 | 
| -  are read-only, which svn repositories and some .svn files are.  We need to
 | 
| -  be able to force the files to be writable (i.e., deletable) as we traverse
 | 
| -  the tree.
 | 
| -
 | 
| -  Even with all this, Windows still sometimes fails to delete a file, citing
 | 
| -  a permission error (maybe something to do with antivirus scans or disk
 | 
| -  indexing).  The best suggestion any of the user forums had was to wait a
 | 
| -  bit and try again, so we do that too.  It's hand-waving, but sometimes it
 | 
| -  works. :/
 | 
| -  """
 | 
| -  if not os.path.exists(file_path):
 | 
| -    return 0
 | 
| -
 | 
| -  if sys.platform == 'win32':
 | 
| -    # Give up and use cmd.exe's rd command.
 | 
| -    file_path = os.path.normcase(file_path)
 | 
| -    for _ in xrange(3):
 | 
| -      print 'RemoveDirectory running %s' % (' '.join(
 | 
| -          ['cmd.exe', '/c', 'rd', '/q', '/s', file_path]))
 | 
| -      if not subprocess.call(['cmd.exe', '/c', 'rd', '/q', '/s', file_path]):
 | 
| -        return 0
 | 
| -      print '  Failed'
 | 
| -      time.sleep(3)
 | 
| -    return 1
 | 
| -
 | 
| -  def RemoveWithRetry_non_win(rmfunc, path):
 | 
| -    if os.path.islink(path):
 | 
| -      return os.remove(path)
 | 
| -    else:
 | 
| -      return rmfunc(path)
 | 
| -
 | 
| -  remove_with_retry = RemoveWithRetry_non_win
 | 
| -
 | 
| -  def RmTreeOnError(function, path, excinfo):
 | 
| -    r"""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.
 | 
| -    """
 | 
| -
 | 
| -    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.
 | 
| -    os.chmod(root, 0770)
 | 
| -    for name in files:
 | 
| -      remove_with_retry(os.remove, os.path.join(root, name))
 | 
| -    for name in dirs:
 | 
| -      remove_with_retry(lambda p: shutil.rmtree(p, onerror=RmTreeOnError),
 | 
| -                        os.path.join(root, name))
 | 
| -
 | 
| -  remove_with_retry(os.rmdir, file_path)
 | 
| -  return 0
 | 
| -
 | 
| -
 | 
| -def main(args):
 | 
| -  parser = argparse.ArgumentParser()
 | 
| -  parser.add_argument('path', help='Absolute path to remove')
 | 
| -  options = parser.parse_args(args)
 | 
| -  return RemoveDirectory(options.path)
 | 
| -
 | 
| -
 | 
| -if __name__ == '__main__':
 | 
| -  sys.exit(main(sys.argv[1:]))
 | 
| 
 |