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]