diff --git a/setup.py b/setup.py
index 4a91de0e88a3b9b787d46af1ec5d426f3d4c1dfc..de346a215014559c7d0dffbcd5fb29e1fd7fd006 100644
--- a/setup.py
+++ b/setup.py
@@ -62,8 +62,8 @@ import commands
 import grpc_core_dependencies
 import grpc_version
 
-# TODO(atash) make this conditional on being on a mingw32 build
-_unixccompiler_patch.monkeypatch_unix_compiler()
+if 'win32' in sys.platform:
+  _unixccompiler_patch.monkeypatch_unix_compiler()
 
 
 LICENSE = '3-clause BSD'
diff --git a/src/python/grpcio/_unixccompiler_patch.py b/src/python/grpcio/_unixccompiler_patch.py
index 9a697989b30c57620a45a4c54adcc8985f1dd9b2..0ce5d63e981a1893176c72213918c05b41f3325a 100644
--- a/src/python/grpcio/_unixccompiler_patch.py
+++ b/src/python/grpcio/_unixccompiler_patch.py
@@ -38,84 +38,36 @@ import shutil
 import sys
 import tempfile
 
+def _unix_commandfile_spawn(self, command):
+  """Wrapper around distutils.util.spawn that attempts to use command files.
 
-def _unix_piecemeal_link(
-    self, target_desc, objects, output_filename, output_dir=None,
-    libraries=None, library_dirs=None, runtime_library_dirs=None,
-    export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None,
-    build_temp=None, target_lang=None):
-  """`link` externalized method taken almost verbatim from UnixCCompiler.
+  Meant to replace the CCompiler method `spawn` on UnixCCompiler and its
+  derivatives (e.g. the MinGW32 compiler).
 
-  Modifies the link command for unix-like compilers by using a command file so
-  that long command line argument strings don't break the command shell's
-  ARG_MAX character limit.
+  Some commands like `gcc` (and friends like `clang`) support command files to
+  work around shell command length limits.
   """
-  objects, output_dir = self._fix_object_args(objects, output_dir)
-  libraries, library_dirs, runtime_library_dirs = self._fix_lib_args(
-      libraries, library_dirs, runtime_library_dirs)
-  # filter out standard library paths, which are not explicitely needed
-  # for linking
-  library_dirs = [dir for dir in library_dirs
-                  if not dir in ('/lib', '/lib64', '/usr/lib', '/usr/lib64')]
-  runtime_library_dirs = [dir for dir in runtime_library_dirs
-                          if not dir in ('/lib', '/lib64', '/usr/lib', '/usr/lib64')]
-  lib_opts = ccompiler.gen_lib_options(self, library_dirs, runtime_library_dirs,
-                             libraries)
-  if (not (isinstance(output_dir, str) or isinstance(output_dir, bytes))
-      and output_dir is not None):
-    raise TypeError("'output_dir' must be a string or None")
-  if output_dir is not None:
-    output_filename = os.path.join(output_dir, output_filename)
-
-  if self._need_link(objects, output_filename):
-    ld_args = (objects + self.objects +
-               lib_opts + ['-o', output_filename])
-    if debug:
-      ld_args[:0] = ['-g']
-    if extra_preargs:
-      ld_args[:0] = extra_preargs
-    if extra_postargs:
-      ld_args.extend(extra_postargs)
-    self.mkpath(os.path.dirname(output_filename))
-    try:
-      if target_desc == ccompiler.CCompiler.EXECUTABLE:
-        linker = self.linker_exe[:]
-      else:
-        linker = self.linker_so[:]
-      if target_lang == "c++" and self.compiler_cxx:
-        # skip over environment variable settings if /usr/bin/env
-        # is used to set up the linker's environment.
-        # This is needed on OSX. Note: this assumes that the
-        # normal and C++ compiler have the same environment
-        # settings.
-        i = 0
-        if os.path.basename(linker[0]) == "env":
-          i = 1
-          while '=' in linker[i]:
-            i = i + 1
-
-        linker[i] = self.compiler_cxx[i]
-
-      if sys.platform == 'darwin':
-        import _osx_support
-        linker = _osx_support.compiler_fixup(linker, ld_args)
-
-      temporary_directory = tempfile.mkdtemp()
-      command_filename = os.path.abspath(
-          os.path.join(temporary_directory, 'command'))
-      with open(command_filename, 'w') as command_file:
-        escaped_ld_args = [arg.replace('\\', '\\\\') for arg in ld_args]
-        command_file.write(' '.join(escaped_ld_args))
-      self.spawn(linker + ['@{}'.format(command_filename)])
-    except errors.DistutilsExecError:
-      raise ccompiler.LinkError
+  command_base = os.path.basename(command[0].strip())
+  if command_base == 'ccache':
+    command_base = command[:2]
+    command_args = command[2:]
+  elif command_base.startswith('ccache') or command_base in ['gcc', 'clang', 'clang++', 'g++']:
+    command_base = command[:1]
+    command_args = command[1:]
   else:
-    log.debug("skipping %s (up-to-date)", output_filename)
+    return ccompiler.CCompiler.spawn(self, command)
+  temporary_directory = tempfile.mkdtemp()
+  command_filename = os.path.abspath(os.path.join(temporary_directory, 'command'))
+  with open(command_filename, 'w') as command_file:
+    escaped_args = [arg.replace('\\', '\\\\') for arg in command_args]
+    command_file.write(' '.join(escaped_args))
+  modified_command = command_base + ['@{}'.format(command_filename)]
+  result = ccompiler.CCompiler.spawn(self, modified_command)
+  shutil.rmtree(temporary_directory)
+  return result
+
 
-# TODO(atash) try replacing this monkeypatch of the compiler harness' link
-# operation with a monkeypatch of the distutils `spawn` that applies
-# command-argument-file hacks where it can. Might be cleaner.
 def monkeypatch_unix_compiler():
   """Monkeypatching is dumb, but it's either that or we become maintainers of
      something much, much bigger."""
-  unixccompiler.UnixCCompiler.link = _unix_piecemeal_link
+  unixccompiler.UnixCCompiler.spawn = _unix_commandfile_spawn