Attempt at making git-drover work on Windows

BUG=404755
R=sammc@chromium.org

Review URL: https://codereview.chromium.org/1387223002

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@297055 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/git_drover.py b/git_drover.py
index 18756d8..73c9200 100755
--- a/git_drover.py
+++ b/git_drover.py
@@ -20,6 +20,27 @@
   pass
 
 
+if os.name == 'nt':
+  # This is a just-good-enough emulation of os.symlink for drover to work on
+  # Windows. It uses junctioning of directories (most of the contents of
+  # the .git directory), but copies files. Note that we can't use
+  # CreateSymbolicLink or CreateHardLink here, as they both require elevation.
+  # Creating reparse points is what we want for the directories, but doing so
+  # is a relatively messy set of DeviceIoControl work at the API level, so we
+  # simply shell to `mklink /j` instead.
+  def emulate_symlink_windows(source, link_name):
+    if os.path.isdir(source):
+      subprocess.check_call(['mklink', '/j',
+                             link_name.replace('/', '\\'),
+                             source.replace('/', '\\')],
+                            shell=True)
+    else:
+      shutil.copy(source, link_name)
+  mk_symlink = emulate_symlink_windows
+else:
+  mk_symlink = os.symlink
+
+
 class _Drover(object):
 
   def __init__(self, branch, revision, parent_repo, dry_run):
@@ -72,7 +93,11 @@
       self._run_git_command(['branch', '-D', self._branch_name])
     if self._workdir:
       logging.debug('Deleting %s', self._workdir)
-      shutil.rmtree(self._workdir)
+      if os.name == 'nt':
+        # Use rmdir to properly handle the junctions we created.
+        subprocess.check_call(['rmdir', '/s', '/q', self._workdir], shell=True)
+      else:
+        shutil.rmtree(self._workdir)
     self._dev_null_file.close()
 
   @staticmethod
@@ -95,8 +120,6 @@
 
     if not os.path.isdir(self._parent_repo):
       raise Error('Invalid parent repo path %r' % self._parent_repo)
-    if not hasattr(os, 'symlink'):
-      raise Error('Symlink support is required')
 
     self._run_git_command(['--help'], error_message='Unable to run git')
     self._run_git_command(['status'],
@@ -140,7 +163,7 @@
     logging.debug('Creating checkout in %s', self._workdir)
     git_dir = os.path.join(self._workdir, '.git')
     git_common.make_workdir_common(parent_git_dir, git_dir, self.FILES_TO_LINK,
-                                   self.FILES_TO_COPY)
+                                   self.FILES_TO_COPY, mk_symlink)
     self._run_git_command(['config', 'core.sparsecheckout', 'true'])
     with open(os.path.join(git_dir, 'info', 'sparse-checkout'), 'w') as f:
       f.write('codereview.settings')