1a8c51b3fSopenharmony_ciimport contextlib 2a8c51b3fSopenharmony_ciimport os 3a8c51b3fSopenharmony_ciimport platform 4a8c51b3fSopenharmony_ciimport shutil 5a8c51b3fSopenharmony_ciimport sysconfig 6a8c51b3fSopenharmony_cifrom pathlib import Path 7a8c51b3fSopenharmony_ci 8a8c51b3fSopenharmony_ciimport setuptools 9a8c51b3fSopenharmony_cifrom setuptools.command import build_ext 10a8c51b3fSopenharmony_ci 11a8c51b3fSopenharmony_ci 12a8c51b3fSopenharmony_ciPYTHON_INCLUDE_PATH_PLACEHOLDER = "<PYTHON_INCLUDE_PATH>" 13a8c51b3fSopenharmony_ci 14a8c51b3fSopenharmony_ciIS_WINDOWS = platform.system() == "Windows" 15a8c51b3fSopenharmony_ciIS_MAC = platform.system() == "Darwin" 16a8c51b3fSopenharmony_ci 17a8c51b3fSopenharmony_ci 18a8c51b3fSopenharmony_ci@contextlib.contextmanager 19a8c51b3fSopenharmony_cidef temp_fill_include_path(fp: str): 20a8c51b3fSopenharmony_ci """Temporarily set the Python include path in a file.""" 21a8c51b3fSopenharmony_ci with open(fp, "r+") as f: 22a8c51b3fSopenharmony_ci try: 23a8c51b3fSopenharmony_ci content = f.read() 24a8c51b3fSopenharmony_ci replaced = content.replace( 25a8c51b3fSopenharmony_ci PYTHON_INCLUDE_PATH_PLACEHOLDER, 26a8c51b3fSopenharmony_ci Path(sysconfig.get_paths()['include']).as_posix(), 27a8c51b3fSopenharmony_ci ) 28a8c51b3fSopenharmony_ci f.seek(0) 29a8c51b3fSopenharmony_ci f.write(replaced) 30a8c51b3fSopenharmony_ci f.truncate() 31a8c51b3fSopenharmony_ci yield 32a8c51b3fSopenharmony_ci finally: 33a8c51b3fSopenharmony_ci # revert to the original content after exit 34a8c51b3fSopenharmony_ci f.seek(0) 35a8c51b3fSopenharmony_ci f.write(content) 36a8c51b3fSopenharmony_ci f.truncate() 37a8c51b3fSopenharmony_ci 38a8c51b3fSopenharmony_ci 39a8c51b3fSopenharmony_ciclass BazelExtension(setuptools.Extension): 40a8c51b3fSopenharmony_ci """A C/C++ extension that is defined as a Bazel BUILD target.""" 41a8c51b3fSopenharmony_ci 42a8c51b3fSopenharmony_ci def __init__(self, name: str, bazel_target: str): 43a8c51b3fSopenharmony_ci super().__init__(name=name, sources=[]) 44a8c51b3fSopenharmony_ci 45a8c51b3fSopenharmony_ci self.bazel_target = bazel_target 46a8c51b3fSopenharmony_ci stripped_target = bazel_target.split("//")[-1] 47a8c51b3fSopenharmony_ci self.relpath, self.target_name = stripped_target.split(":") 48a8c51b3fSopenharmony_ci 49a8c51b3fSopenharmony_ci 50a8c51b3fSopenharmony_ciclass BuildBazelExtension(build_ext.build_ext): 51a8c51b3fSopenharmony_ci """A command that runs Bazel to build a C/C++ extension.""" 52a8c51b3fSopenharmony_ci 53a8c51b3fSopenharmony_ci def run(self): 54a8c51b3fSopenharmony_ci for ext in self.extensions: 55a8c51b3fSopenharmony_ci self.bazel_build(ext) 56a8c51b3fSopenharmony_ci build_ext.build_ext.run(self) 57a8c51b3fSopenharmony_ci 58a8c51b3fSopenharmony_ci def bazel_build(self, ext: BazelExtension): 59a8c51b3fSopenharmony_ci """Runs the bazel build to create the package.""" 60a8c51b3fSopenharmony_ci with temp_fill_include_path("WORKSPACE"): 61a8c51b3fSopenharmony_ci temp_path = Path(self.build_temp) 62a8c51b3fSopenharmony_ci 63a8c51b3fSopenharmony_ci bazel_argv = [ 64a8c51b3fSopenharmony_ci "bazel", 65a8c51b3fSopenharmony_ci "build", 66a8c51b3fSopenharmony_ci ext.bazel_target, 67a8c51b3fSopenharmony_ci f"--symlink_prefix={temp_path / 'bazel-'}", 68a8c51b3fSopenharmony_ci f"--compilation_mode={'dbg' if self.debug else 'opt'}", 69a8c51b3fSopenharmony_ci # C++17 is required by nanobind 70a8c51b3fSopenharmony_ci f"--cxxopt={'/std:c++17' if IS_WINDOWS else '-std=c++17'}", 71a8c51b3fSopenharmony_ci ] 72a8c51b3fSopenharmony_ci 73a8c51b3fSopenharmony_ci if IS_WINDOWS: 74a8c51b3fSopenharmony_ci # Link with python*.lib. 75a8c51b3fSopenharmony_ci for library_dir in self.library_dirs: 76a8c51b3fSopenharmony_ci bazel_argv.append("--linkopt=/LIBPATH:" + library_dir) 77a8c51b3fSopenharmony_ci elif IS_MAC: 78a8c51b3fSopenharmony_ci if platform.machine() == "x86_64": 79a8c51b3fSopenharmony_ci # C++17 needs macOS 10.14 at minimum 80a8c51b3fSopenharmony_ci bazel_argv.append("--macos_minimum_os=10.14") 81a8c51b3fSopenharmony_ci 82a8c51b3fSopenharmony_ci # cross-compilation for Mac ARM64 on GitHub Mac x86 runners. 83a8c51b3fSopenharmony_ci # ARCHFLAGS is set by cibuildwheel before macOS wheel builds. 84a8c51b3fSopenharmony_ci archflags = os.getenv("ARCHFLAGS", "") 85a8c51b3fSopenharmony_ci if "arm64" in archflags: 86a8c51b3fSopenharmony_ci bazel_argv.append("--cpu=darwin_arm64") 87a8c51b3fSopenharmony_ci bazel_argv.append("--macos_cpus=arm64") 88a8c51b3fSopenharmony_ci 89a8c51b3fSopenharmony_ci elif platform.machine() == "arm64": 90a8c51b3fSopenharmony_ci bazel_argv.append("--macos_minimum_os=11.0") 91a8c51b3fSopenharmony_ci 92a8c51b3fSopenharmony_ci self.spawn(bazel_argv) 93a8c51b3fSopenharmony_ci 94a8c51b3fSopenharmony_ci shared_lib_suffix = '.dll' if IS_WINDOWS else '.so' 95a8c51b3fSopenharmony_ci ext_name = ext.target_name + shared_lib_suffix 96a8c51b3fSopenharmony_ci ext_bazel_bin_path = temp_path / 'bazel-bin' / ext.relpath / ext_name 97a8c51b3fSopenharmony_ci 98a8c51b3fSopenharmony_ci ext_dest_path = Path(self.get_ext_fullpath(ext.name)) 99a8c51b3fSopenharmony_ci shutil.copyfile(ext_bazel_bin_path, ext_dest_path) 100a8c51b3fSopenharmony_ci 101a8c51b3fSopenharmony_ci # explicitly call `bazel shutdown` for graceful exit 102a8c51b3fSopenharmony_ci self.spawn(["bazel", "shutdown"]) 103a8c51b3fSopenharmony_ci 104a8c51b3fSopenharmony_ci 105a8c51b3fSopenharmony_cisetuptools.setup( 106a8c51b3fSopenharmony_ci cmdclass=dict(build_ext=BuildBazelExtension), 107a8c51b3fSopenharmony_ci ext_modules=[ 108a8c51b3fSopenharmony_ci BazelExtension( 109a8c51b3fSopenharmony_ci name="google_benchmark._benchmark", 110a8c51b3fSopenharmony_ci bazel_target="//bindings/python/google_benchmark:_benchmark", 111a8c51b3fSopenharmony_ci ) 112a8c51b3fSopenharmony_ci ], 113a8c51b3fSopenharmony_ci) 114