Replace all os.remove calls

os.remove raises an exception when deleting read-only files on
Windows. Replace all calls with calls to platform_utils.remove,
which deals with deals with that issue.

Change-Id: I4dc9e0c9a36b4238880520c69f5075eca40f3e66
diff --git a/editor.py b/editor.py
index 883a1a8..96d7ce4 100644
--- a/editor.py
+++ b/editor.py
@@ -21,6 +21,7 @@
 import tempfile
 
 from error import EditorError
+import platform_utils
 
 class Editor(object):
   """Manages the user's preferred text editor."""
@@ -107,4 +108,4 @@
     finally:
       if fd:
         os.close(fd)
-      os.remove(path)
+      platform_utils.remove(path)
diff --git a/git_config.py b/git_config.py
index 9d5874a..3ba9dbd 100644
--- a/git_config.py
+++ b/git_config.py
@@ -42,6 +42,7 @@
 
 from signal import SIGTERM
 from error import GitError, UploadError
+import platform_utils
 from trace import Trace
 if is_python3():
   from http.client import HTTPException
@@ -268,7 +269,7 @@
     try:
       if os.path.getmtime(self._json) \
       <= os.path.getmtime(self.file):
-        os.remove(self._json)
+        platform_utils.remove(self._json)
         return None
     except OSError:
       return None
@@ -280,7 +281,7 @@
       finally:
         fd.close()
     except (IOError, ValueError):
-      os.remove(self._json)
+      platform_utils.remove(self._json)
       return None
 
   def _SaveJson(self, cache):
@@ -292,7 +293,7 @@
         fd.close()
     except (IOError, TypeError):
       if os.path.exists(self._json):
-        os.remove(self._json)
+        platform_utils.remove(self._json)
 
   def _ReadGit(self):
     """
diff --git a/manifest_xml.py b/manifest_xml.py
index 05651c6..9b5d784 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -166,7 +166,7 @@
 
     try:
       if os.path.lexists(self.manifestFile):
-        os.remove(self.manifestFile)
+        platform_utils.remove(self.manifestFile)
       platform_utils.symlink(os.path.join('manifests', name), self.manifestFile)
     except OSError as e:
       raise ManifestParseError('cannot link manifest %s: %s' % (name, str(e)))
diff --git a/platform_utils.py b/platform_utils.py
index 2ad5649..33cb2ec 100644
--- a/platform_utils.py
+++ b/platform_utils.py
@@ -244,6 +244,23 @@
     os.rename(src, dst)
 
 
+def remove(path):
+  """Remove (delete) the file path. This is a replacement for os.remove, but
+  allows deleting read-only files on Windows.
+  """
+  if isWindows():
+    try:
+      os.remove(path)
+    except OSError as e:
+      if e.errno == errno.EACCES:
+        os.chmod(path, stat.S_IWRITE)
+        os.remove(path)
+      else:
+        raise
+  else:
+    os.remove(path)
+
+
 def islink(path):
   """Test whether a path is a symbolic link.
 
diff --git a/project.py b/project.py
index 655b202..338ce93 100644
--- a/project.py
+++ b/project.py
@@ -65,7 +65,7 @@
   try:
     platform_utils.rename(lock, path)
   except OSError:
-    os.remove(lock)
+    platform_utils.remove(lock)
     raise
 
 
@@ -250,7 +250,7 @@
       try:
         # remove existing file first, since it might be read-only
         if os.path.exists(dest):
-          os.remove(dest)
+          platform_utils.remove(dest)
         else:
           dest_dir = os.path.dirname(dest)
           if not os.path.isdir(dest_dir):
@@ -279,7 +279,7 @@
       try:
         # remove existing file first, since it might be read-only
         if os.path.lexists(absDest):
-          os.remove(absDest)
+          platform_utils.remove(absDest)
         else:
           dest_dir = os.path.dirname(absDest)
           if not os.path.isdir(dest_dir):
@@ -1242,7 +1242,7 @@
       if not self._ExtractArchive(tarpath, path=topdir):
         return False
       try:
-        os.remove(tarpath)
+        platform_utils.remove(tarpath)
       except OSError as e:
         _warn("Cannot remove archive %s: %s", tarpath, str(e))
       self._CopyAndLinkFiles()
@@ -1302,7 +1302,7 @@
     else:
       self._InitMirrorHead()
       try:
-        os.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
+        platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
       except OSError:
         pass
     return True
@@ -1812,7 +1812,7 @@
       except GitError:
         return [], []
       finally:
-        os.remove(temp_gitmodules_path)
+        platform_utils.remove(temp_gitmodules_path)
 
       names = set()
       paths = {}
@@ -2104,7 +2104,7 @@
         if old_packed != '':
           _lwrite(packed_refs, old_packed)
         else:
-          os.remove(packed_refs)
+          platform_utils.remove(packed_refs)
       self.bare_git.pack_refs('--all', '--prune')
 
     if is_sha1 and current_branch_only:
@@ -2166,14 +2166,14 @@
 
     ok = GitCommand(self, cmd, bare=True).Wait() == 0
     if os.path.exists(bundle_dst):
-      os.remove(bundle_dst)
+      platform_utils.remove(bundle_dst)
     if os.path.exists(bundle_tmp):
-      os.remove(bundle_tmp)
+      platform_utils.remove(bundle_tmp)
     return ok
 
   def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
     if os.path.exists(dstPath):
-      os.remove(dstPath)
+      platform_utils.remove(dstPath)
 
     cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
     if quiet:
@@ -2183,7 +2183,7 @@
       if size >= 1024:
         cmd += ['--continue-at', '%d' % (size,)]
       else:
-        os.remove(tmpPath)
+        platform_utils.remove(tmpPath)
     if 'http_proxy' in os.environ and 'darwin' == sys.platform:
       cmd += ['--proxy', os.environ['http_proxy']]
     with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
@@ -2217,7 +2217,7 @@
         platform_utils.rename(tmpPath, dstPath)
         return True
       else:
-        os.remove(tmpPath)
+        platform_utils.remove(tmpPath)
         return False
     else:
       return False
@@ -2390,7 +2390,7 @@
         continue
       if os.path.exists(dst):
         if filecmp.cmp(stock_hook, dst, shallow=False):
-          os.remove(dst)
+          platform_utils.remove(dst)
         else:
           _warn("%s: Not replacing locally modified %s hook",
                 self.relpath, name)
@@ -2508,7 +2508,7 @@
         # file doesn't either.
         if name in symlink_files and not os.path.lexists(src):
           try:
-            os.remove(dst)
+            platform_utils.remove(dst)
           except OSError:
             pass
 
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 93fea23..cda47fd 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -489,7 +489,7 @@
     for root, dirs, files in os.walk(path):
       for f in files:
         try:
-          os.remove(os.path.join(root, f))
+          platform_utils.remove(os.path.join(root, f))
         except OSError:
           print('Failed to remove %s' % os.path.join(root, f), file=sys.stderr)
           failed = True
@@ -500,7 +500,7 @@
     for d in reversed(dirs_to_remove):
       if platform_utils.islink(d):
         try:
-          os.remove(d)
+          platform_utils.remove(d)
         except OSError:
           print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr)
           failed = True
@@ -712,7 +712,7 @@
     else:  # Not smart sync or smart tag mode
       if os.path.isfile(smart_sync_manifest_path):
         try:
-          os.remove(smart_sync_manifest_path)
+          platform_utils.remove(smart_sync_manifest_path)
         except OSError as e:
           print('error: failed to remove existing smart sync override manifest: %s' %
                 e, file=sys.stderr)
@@ -956,7 +956,7 @@
           f.close()
       except (IOError, ValueError):
         try:
-          os.remove(self._path)
+          platform_utils.remove(self._path)
         except OSError:
           pass
         self._times = {}
@@ -980,7 +980,7 @@
         f.close()
     except (IOError, TypeError):
       try:
-        os.remove(self._path)
+        platform_utils.remove(self._path)
       except OSError:
         pass