1import os 2import posixpath 3import platform 4import re 5import shutil 6import sys 7 8from distutils import sysconfig 9import setuptools 10from setuptools.command import build_ext 11 12 13HERE = os.path.dirname(os.path.abspath(__file__)) 14 15 16IS_WINDOWS = sys.platform.startswith("win") 17 18 19def _get_version(): 20 """Parse the version string from __init__.py.""" 21 with open( 22 os.path.join(HERE, "bindings", "python", "google_benchmark", "__init__.py") 23 ) as init_file: 24 try: 25 version_line = next( 26 line for line in init_file if line.startswith("__version__") 27 ) 28 except StopIteration: 29 raise ValueError("__version__ not defined in __init__.py") 30 else: 31 namespace = {} 32 exec(version_line, namespace) # pylint: disable=exec-used 33 return namespace["__version__"] 34 35 36def _parse_requirements(path): 37 with open(os.path.join(HERE, path)) as requirements: 38 return [ 39 line.rstrip() 40 for line in requirements 41 if not (line.isspace() or line.startswith("#")) 42 ] 43 44 45class BazelExtension(setuptools.Extension): 46 """A C/C++ extension that is defined as a Bazel BUILD target.""" 47 48 def __init__(self, name, bazel_target): 49 self.bazel_target = bazel_target 50 self.relpath, self.target_name = posixpath.relpath(bazel_target, "//").split( 51 ":" 52 ) 53 setuptools.Extension.__init__(self, name, sources=[]) 54 55 56class BuildBazelExtension(build_ext.build_ext): 57 """A command that runs Bazel to build a C/C++ extension.""" 58 59 def run(self): 60 for ext in self.extensions: 61 self.bazel_build(ext) 62 build_ext.build_ext.run(self) 63 64 def bazel_build(self, ext): 65 """Runs the bazel build to create the package.""" 66 with open("WORKSPACE", "r") as workspace: 67 workspace_contents = workspace.read() 68 69 with open("WORKSPACE", "w") as workspace: 70 workspace.write( 71 re.sub( 72 r'(?<=path = ").*(?=", # May be overwritten by setup\.py\.)', 73 sysconfig.get_python_inc().replace(os.path.sep, posixpath.sep), 74 workspace_contents, 75 ) 76 ) 77 78 if not os.path.exists(self.build_temp): 79 os.makedirs(self.build_temp) 80 81 bazel_argv = [ 82 "bazel", 83 "build", 84 ext.bazel_target, 85 "--symlink_prefix=" + os.path.join(self.build_temp, "bazel-"), 86 "--compilation_mode=" + ("dbg" if self.debug else "opt"), 87 ] 88 89 if IS_WINDOWS: 90 # Link with python*.lib. 91 for library_dir in self.library_dirs: 92 bazel_argv.append("--linkopt=/LIBPATH:" + library_dir) 93 elif sys.platform == "darwin" and platform.machine() == "x86_64": 94 bazel_argv.append("--macos_minimum_os=10.9") 95 96 self.spawn(bazel_argv) 97 98 shared_lib_suffix = '.dll' if IS_WINDOWS else '.so' 99 ext_bazel_bin_path = os.path.join( 100 self.build_temp, 'bazel-bin', 101 ext.relpath, ext.target_name + shared_lib_suffix) 102 103 ext_dest_path = self.get_ext_fullpath(ext.name) 104 ext_dest_dir = os.path.dirname(ext_dest_path) 105 if not os.path.exists(ext_dest_dir): 106 os.makedirs(ext_dest_dir) 107 shutil.copyfile(ext_bazel_bin_path, ext_dest_path) 108 109 110setuptools.setup( 111 name="google_benchmark", 112 version=_get_version(), 113 url="https://github.com/google/benchmark", 114 description="A library to benchmark code snippets.", 115 author="Google", 116 author_email="benchmark-py@google.com", 117 # Contained modules and scripts. 118 package_dir={"": "bindings/python"}, 119 packages=setuptools.find_packages("bindings/python"), 120 install_requires=_parse_requirements("bindings/python/requirements.txt"), 121 cmdclass=dict(build_ext=BuildBazelExtension), 122 ext_modules=[ 123 BazelExtension( 124 "google_benchmark._benchmark", 125 "//bindings/python/google_benchmark:_benchmark", 126 ) 127 ], 128 zip_safe=False, 129 # PyPI package information. 130 classifiers=[ 131 "Development Status :: 4 - Beta", 132 "Intended Audience :: Developers", 133 "Intended Audience :: Science/Research", 134 "License :: OSI Approved :: Apache Software License", 135 "Programming Language :: Python :: 3.6", 136 "Programming Language :: Python :: 3.7", 137 "Programming Language :: Python :: 3.8", 138 "Topic :: Software Development :: Testing", 139 "Topic :: System :: Benchmark", 140 ], 141 license="Apache 2.0", 142 keywords="benchmark", 143) 144