Skip to content

Commit

Permalink
Merge branch 'main' into with-nanobind
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholasjng committed Feb 3, 2023
2 parents d3c940c + f59d021 commit 36ad43b
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 100 deletions.
18 changes: 11 additions & 7 deletions .github/workflows/test_bindings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,21 @@ on:

jobs:
python_bindings:
runs-on: ubuntu-latest
name: Test GBM Python bindings on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ ubuntu-latest, macos-latest, windows-latest ]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v1
uses: actions/setup-python@v4
with:
python-version: 3.8
- name: Install benchmark
python-version: 3.11
- name: Install GBM Python bindings on ${{ matrix.os}}
run:
python setup.py install
- name: Run example bindings
python -m pip install wheel .
- name: Run bindings example on ${{ matrix.os }}
run:
python bindings/python/google_benchmark/example.py
2 changes: 1 addition & 1 deletion WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ pip3_install(
new_local_repository(
name = "python_headers",
build_file = "@//bindings/python:python_headers.BUILD",
path = "/usr/include/python3.6", # May be overwritten by setup.py.
path = "<PYTHON_INCLUDE_PATH>", # May be overwritten by setup.py.
)
183 changes: 91 additions & 92 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,60 +1,75 @@
import contextlib
import os
import posixpath
import platform
import re
import shutil
import sys
import sysconfig
from pathlib import Path
from typing import List

from distutils import sysconfig
import setuptools
from setuptools.command import build_ext


HERE = os.path.dirname(os.path.abspath(__file__))
PYTHON_INCLUDE_PATH_PLACEHOLDER = "<PYTHON_INCLUDE_PATH>"

IS_WINDOWS = platform.system() == "Windows"
IS_MAC = platform.system() == "Darwin"

IS_WINDOWS = sys.platform.startswith("win")

def _get_long_description(fp: str) -> str:
with open(fp, "r", encoding="utf-8") as f:
return f.read()

with open("README.md", "r", encoding="utf-8") as fp:
long_description = fp.read()


def _get_version():
"""Parse the version string from __init__.py."""
with open(
os.path.join(HERE, "bindings", "python", "google_benchmark", "__init__.py")
) as init_file:
try:
version_line = next(
line for line in init_file if line.startswith("__version__")
)
except StopIteration:
raise ValueError("__version__ not defined in __init__.py")
else:
namespace = {}
exec(version_line, namespace) # pylint: disable=exec-used
return namespace["__version__"]
def _get_version(fp: str) -> str:
"""Parse a version string from a file."""
with open(fp, "r") as f:
for line in f:
if "__version__" in line:
delim = '"'
return line.split(delim)[1]
raise RuntimeError(f"could not find a version string in file {fp!r}.")


def _parse_requirements(path):
with open(os.path.join(HERE, path)) as requirements:
def _parse_requirements(fp: str) -> List[str]:
with open(fp) as requirements:
return [
line.rstrip()
for line in requirements
if not (line.isspace() or line.startswith("#"))
]


@contextlib.contextmanager
def temp_fill_include_path(fp: str):
"""Temporarily set the Python include path in a file."""
with open(fp, "r+") as f:
try:
content = f.read()
replaced = content.replace(
PYTHON_INCLUDE_PATH_PLACEHOLDER,
Path(sysconfig.get_paths()['include']).as_posix(),
)
f.seek(0)
f.write(replaced)
f.truncate()
yield
finally:
# revert to the original content after exit
f.seek(0)
f.write(content)
f.truncate()


class BazelExtension(setuptools.Extension):
"""A C/C++ extension that is defined as a Bazel BUILD target."""

def __init__(self, name, bazel_target):
def __init__(self, name: str, bazel_target: str):
super().__init__(name=name, sources=[])

self.bazel_target = bazel_target
self.relpath, self.target_name = posixpath.relpath(bazel_target, "//").split(
":"
)
setuptools.Extension.__init__(self, name, sources=[])
stripped_target = bazel_target.split("//")[-1]
self.relpath, self.target_name = stripped_target.split(":")


class BuildBazelExtension(build_ext.build_ext):
Expand All @@ -65,74 +80,58 @@ def run(self):
self.bazel_build(ext)
build_ext.build_ext.run(self)

def bazel_build(self, ext):
def bazel_build(self, ext: BazelExtension):
"""Runs the bazel build to create the package."""
with open("WORKSPACE", "r") as workspace:
workspace_contents = workspace.read()

with open("WORKSPACE", "w") as workspace:
workspace.write(
re.sub(
r'(?<=path = ").*(?=", # May be overwritten by setup\.py\.)',
sysconfig.get_python_inc().replace(os.path.sep, posixpath.sep),
workspace_contents,
)
)

if not os.path.exists(self.build_temp):
os.makedirs(self.build_temp)

bazel_argv = [
"bazel",
"build",
ext.bazel_target,
"--symlink_prefix=" + os.path.join(self.build_temp, "bazel-"),
"--compilation_mode=" + ("dbg" if self.debug else "opt"),
"--cxxopt=-std=c++17", # required by nanobind
]

if IS_WINDOWS:
# Link with python*.lib.
for library_dir in self.library_dirs:
bazel_argv.append("--linkopt=/LIBPATH:" + library_dir)
elif sys.platform == "darwin":
if platform.machine() == "x86_64":
# C++17 needs macOS 10.14 at minimum
bazel_argv.append("--macos_minimum_os=10.14")

# cross-compilation for Mac ARM64 on GitHub Mac x86 runners.
# ARCHFLAGS is set by cibuildwheel before macOS wheel builds.
archflags = os.getenv("ARCHFLAGS", "")
if "arm64" in archflags:
bazel_argv.append("--cpu=darwin_arm64")
bazel_argv.append("--macos_cpus=arm64")

elif platform.machine() == "arm64":
bazel_argv.append("--macos_minimum_os=11.0")

self.spawn(bazel_argv)

shared_lib_suffix = '.dll' if IS_WINDOWS else '.so'
ext_bazel_bin_path = os.path.join(
self.build_temp, 'bazel-bin',
ext.relpath, ext.target_name + shared_lib_suffix)

ext_dest_path = self.get_ext_fullpath(ext.name)
ext_dest_dir = os.path.dirname(ext_dest_path)
if not os.path.exists(ext_dest_dir):
os.makedirs(ext_dest_dir)
shutil.copyfile(ext_bazel_bin_path, ext_dest_path)

# explicitly call `bazel shutdown` for graceful exit
self.spawn(["bazel", "shutdown"])
with temp_fill_include_path("WORKSPACE"):
temp_path = Path(self.build_temp)

bazel_argv = [
"bazel",
"build",
ext.bazel_target,
f"--symlink_prefix={temp_path / 'bazel-'}",
f"--compilation_mode={'dbg' if self.debug else 'opt'}",
"--cxxopt=-std=c++17", # required by nanobind
]

if IS_WINDOWS:
# Link with python*.lib.
for library_dir in self.library_dirs:
bazel_argv.append("--linkopt=/LIBPATH:" + library_dir)
elif IS_MAC:
if platform.machine() == "x86_64":
# C++17 needs macOS 10.14 at minimum
bazel_argv.append("--macos_minimum_os=10.14")

# cross-compilation for Mac ARM64 on GitHub Mac x86 runners.
# ARCHFLAGS is set by cibuildwheel before macOS wheel builds.
archflags = os.getenv("ARCHFLAGS", "")
if "arm64" in archflags:
bazel_argv.append("--cpu=darwin_arm64")
bazel_argv.append("--macos_cpus=arm64")

elif platform.machine() == "arm64":
bazel_argv.append("--macos_minimum_os=11.0")

self.spawn(bazel_argv)

shared_lib_suffix = '.dll' if IS_WINDOWS else '.so'
ext_name = ext.target_name + shared_lib_suffix
ext_bazel_bin_path = temp_path / 'bazel-bin' / ext.relpath / ext_name

ext_dest_path = Path(self.get_ext_fullpath(ext.name))
shutil.copyfile(ext_bazel_bin_path, ext_dest_path)

# explicitly call `bazel shutdown` for graceful exit
self.spawn(["bazel", "shutdown"])


setuptools.setup(
name="google_benchmark",
version=_get_version(),
version=_get_version("bindings/python/google_benchmark/__init__.py"),
url="https://github.com/google/benchmark",
description="A library to benchmark code snippets.",
long_description=long_description,
long_description=_get_long_description("README.md"),
long_description_content_type="text/markdown",
author="Google",
author_email="benchmark-py@google.com",
Expand Down

0 comments on commit 36ad43b

Please sign in to comment.