diff --git a/setup.py b/setup.py
index 93b1a3ae473e2d1a7504bbf7882a714222ee3a28..b70fab33987bcf96194939dd429043f8a663b39c 100644
--- a/setup.py
+++ b/setup.py
@@ -54,6 +54,8 @@ sys.path.insert(0, PYTHON_STEM)
 import commands
 import grpc_core_dependencies
 
+LICENSE = '3-clause BSD'
+
 # Environment variable to determine whether or not the Cython extension should
 # *use* Cython or use the generated C files. Note that this requires the C files
 # to have been generated by building first *with* Cython support.
@@ -79,15 +81,10 @@ EXTENSION_LIBRARIES = ()
 if not "darwin" in sys.platform:
     EXTENSION_LIBRARIES += ('rt',)
 
-EXTRA_COMPILE_ARGS = ()
-if not "win" in sys.platform:
-  EXTRA_COMPILE_ARGS = ('-pthread',)
-
 DEFINE_MACROS = (('OPENSSL_NO_ASM', 1),)
 
 def cython_extensions(package_names, module_names, include_dirs, libraries,
-                      define_macros, extra_compile_args,
-                      build_with_cython=False):
+                      define_macros, build_with_cython=False):
   if ENABLE_CYTHON_TRACING:
     define_macros = define_macros + [('CYTHON_TRACE_NOGIL', 1)]
   file_extension = 'pyx' if build_with_cython else 'c'
@@ -99,7 +96,6 @@ def cython_extensions(package_names, module_names, include_dirs, libraries,
           name=module_name,
           sources=[module_file] + grpc_core_dependencies.CORE_SOURCE_FILES,
           include_dirs=include_dirs, libraries=libraries,
-          extra_compile_args=extra_compile_args,
           define_macros=define_macros,
       ) for (module_name, module_file) in zip(module_names, module_files)
   ]
@@ -115,7 +111,7 @@ def cython_extensions(package_names, module_names, include_dirs, libraries,
 CYTHON_EXTENSION_MODULES = cython_extensions(
     list(CYTHON_EXTENSION_PACKAGE_NAMES), list(CYTHON_EXTENSION_MODULE_NAMES),
     list(EXTENSION_INCLUDE_DIRECTORIES), list(EXTENSION_LIBRARIES),
-    list(DEFINE_MACROS), list(EXTRA_COMPILE_ARGS), bool(BUILD_WITH_CYTHON))
+    list(DEFINE_MACROS), bool(BUILD_WITH_CYTHON))
 
 PACKAGE_DIRECTORIES = {
     '': PYTHON_STEM,
@@ -135,6 +131,7 @@ COMMAND_CLASS = {
     'build_proto_modules': commands.BuildProtoModules,
     'build_project_metadata': commands.BuildProjectMetadata,
     'build_py': commands.BuildPy,
+    'build_ext': commands.BuildExt,
     'gather': commands.Gather,
     'run_interop': commands.RunInterop,
 }
@@ -186,7 +183,8 @@ else:
 
 setuptools.setup(
     name='grpcio',
-    version='0.12.0b1',
+    version='0.12.0b5',
+    license=LICENSE,
     ext_modules=CYTHON_EXTENSION_MODULES,
     packages=list(PACKAGES),
     package_dir=PACKAGE_DIRECTORIES,
diff --git a/src/python/grpcio/commands.py b/src/python/grpcio/commands.py
index 81dab1b51860f91888cc370c7679684283246557..e1a3f4bed3cc9ed74644e69d0aed202261b9a422 100644
--- a/src/python/grpcio/commands.py
+++ b/src/python/grpcio/commands.py
@@ -40,6 +40,17 @@ import setuptools
 from setuptools.command import build_py
 from setuptools.command import test
 
+# Because we need to support building without Cython but simultaneously need to
+# subclass its command class when we need to and because distutils requires a
+# special hook to acquire a command class, we attempt to import Cython's
+# build_ext, and if that fails we import setuptools'.
+try:
+  # Due to the strange way Cython's Distutils module re-imports build_ext, we
+  # import the build_ext class directly.
+  from Cython.Distutils.build_ext import build_ext
+except ImportError:
+  from setuptools.command.build_ext import build_ext
+
 PYTHON_STEM = os.path.dirname(os.path.abspath(__file__))
 
 CONF_PY_ADDENDUM = """
@@ -51,6 +62,10 @@ html_theme = 'sphinx_rtd_theme'
 """
 
 
+class CommandError(Exception):
+  """Simple exception class for GRPC custom commands."""
+
+
 class SphinxDocumentation(setuptools.Command):
   """Command to generate documentation via sphinx."""
 
@@ -104,10 +119,10 @@ class BuildProtoModules(setuptools.Command):
 
   def run(self):
     if not self.protoc_command:
-      raise Exception('could not find protoc')
+      raise CommandError('could not find protoc')
     if not self.grpc_python_plugin_command:
-      raise Exception('could not find grpc_python_plugin '
-                      '(protoc plugin for GRPC Python)')
+      raise CommandError('could not find grpc_python_plugin '
+                         '(protoc plugin for GRPC Python)')
     include_regex = re.compile(self.include)
     exclude_regex = re.compile(self.exclude) if self.exclude else None
     paths = []
@@ -130,7 +145,7 @@ class BuildProtoModules(setuptools.Command):
       subprocess.check_output(' '.join(command), cwd=root_directory, shell=True,
                               stderr=subprocess.STDOUT)
     except subprocess.CalledProcessError as e:
-      raise Exception('Command:\n{}\nMessage:\n{}\nOutput:\n{}'.format(
+      raise CommandError('Command:\n{}\nMessage:\n{}\nOutput:\n{}'.format(
           command, e.message, e.output))
 
 
@@ -156,13 +171,34 @@ class BuildPy(build_py.build_py):
   """Custom project build command."""
 
   def run(self):
-    # TODO(atash): make this warn if the proto modules couldn't be built rather
-    # than cause build failure
-    self.run_command('build_proto_modules')
+    try:
+      self.run_command('build_proto_modules')
+    except CommandError as error:
+      sys.stderr.write('warning: %s\n' % error.message)
     self.run_command('build_project_metadata')
     build_py.build_py.run(self)
 
 
+class BuildExt(build_ext):
+  """Custom build_ext command to enable compiler-specific flags."""
+
+  C_OPTIONS = {
+      'unix': ('-pthread', '-std=gnu99'),
+      'msvc': (),
+  }
+  LINK_OPTIONS = {}
+
+  def build_extensions(self):
+    compiler = self.compiler.compiler_type
+    if compiler in BuildExt.C_OPTIONS:
+      for extension in self.extensions:
+        extension.extra_compile_args += list(BuildExt.C_OPTIONS[compiler])
+    if compiler in BuildExt.LINK_OPTIONS:
+      for extension in self.extensions:
+        extension.extra_link_args += list(BuildExt.LINK_OPTIONS[compiler])
+    build_ext.build_extensions(self)
+
+
 class Gather(setuptools.Command):
   """Command to gather project dependencies."""
 
diff --git a/tools/distrib/python/submit.py b/tools/distrib/python/submit.py
index 08ace7c69015b814380aab553472907232bc454b..9b012be672024b2af153ac03741e31a7cd3bdfb5 100755
--- a/tools/distrib/python/submit.py
+++ b/tools/distrib/python/submit.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python2.7
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -55,6 +55,14 @@ parser.add_argument(
     help='Password to authenticate with the repository. Not needed if you have '
          'configured your .pypirc to include your password.'
 )
+parser.add_argument(
+    '--bdist', '-b', action='store_true',
+    help='Generate a binary distribution (wheel) for the current OS.'
+)
+parser.add_argument(
+    '--dist-args', type=str,
+    help='Additional arguments to pass to the *dist setup.py command.'
+)
 args = parser.parse_args()
 
 # Move to the root directory of Python GRPC.
@@ -73,7 +81,12 @@ cmd = ['python', 'setup.py', 'build_ext', '--inplace']
 subprocess.call(cmd, cwd=pkgdir, env=build_env)
 
 # Make the push.
-cmd = ['python', 'setup.py', 'sdist']
+if args.bdist:
+  cmd = ['python', 'setup.py', 'bdist_wheel']
+else:
+  cmd = ['python', 'setup.py', 'sdist']
+if args.dist_args:
+  cmd += args.dist_args.split()
 subprocess.call(cmd, cwd=pkgdir)
 
 cmd = ['twine', 'upload', '-r', args.repository]