-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Bazel build system configuration
Provides a Bazel build configuration using bzlmod. Currently, the required `nanobind_bazel` dep is sourced from a local directory. This needs to be adjusted to either reflect the setup in CI, or point to a `nanobind-bazel` tag on BCR once it's released. Uses legacy setup.py / setuptools machinery since there's currently no build backend support for Bazel available. Builds a stable ABI wheel for CPython 3.12+ on all architectures.
- Loading branch information
1 parent
12778d3
commit 8a88c3b
Showing
6 changed files
with
201 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Enable automatic configs based on platform | ||
common --enable_platform_specific_config | ||
|
||
# Set minimum supported C++ version | ||
build:macos --host_cxxopt=-std=c++17 --cxxopt=-std=c++17 | ||
build:linux --host_cxxopt=-std=c++17 --cxxopt=-std=c++17 | ||
build:windows --host_cxxopt=/std:c++17 --cxxopt=/std:c++17 | ||
|
||
# Set minimum supported MacOS version to 10.14 for C++17. | ||
build:macos --macos_minimum_os=10.14 | ||
|
||
# nanobind's minsize. | ||
build --flag_alias=minsize=@nanobind_bazel//:minsize | ||
# nanobind's py-limited-api. | ||
build --flag_alias=py_limited_api=@nanobind_bazel//:py-limited-api | ||
# rules_python's Python version, should not collide with builtin --python_version. | ||
build --flag_alias=target_python_version=@rules_python//python/config_settings:python_version |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,3 +7,7 @@ build | |
*.egg-info | ||
.vscode | ||
.vs | ||
|
||
# Bazel-specific. | ||
!src/BUILD | ||
MODULE.bazel.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
module( | ||
name = "nanobind_example", | ||
version = "0.1.0", | ||
) | ||
|
||
bazel_dep(name = "nanobind_bazel", version = "") | ||
local_path_override( | ||
module_name = "nanobind_bazel", | ||
path = "../nanobind-bazel", | ||
) | ||
|
||
bazel_dep(name = "hedron_compile_commands", dev_dependency = True) | ||
git_override( | ||
module_name = "hedron_compile_commands", | ||
commit = "204aa593e002cbd177d30f11f54cff3559110bb9", | ||
remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git", | ||
) | ||
|
||
bazel_dep(name = "rules_python", version = "0.31.0") | ||
|
||
python = use_extension("@rules_python//python/extensions:python.bzl", "python") | ||
python.toolchain(python_version = "3.8") | ||
python.toolchain(python_version = "3.9") | ||
python.toolchain(python_version = "3.10") | ||
python.toolchain(python_version = "3.11") | ||
python.toolchain( | ||
is_default = True, | ||
python_version = "3.12", | ||
) | ||
python.toolchain(python_version = "3.13") | ||
|
||
use_repo( | ||
python, | ||
python = "python_versions", | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import os | ||
import platform | ||
import shutil | ||
import sys | ||
from pathlib import Path | ||
|
||
import setuptools | ||
from setuptools.command import build_ext | ||
|
||
IS_WINDOWS = platform.system() == "Windows" | ||
|
||
# hardcoded SABI-related options. Requires that each Python interpreter | ||
# (hermetic or not) participating is of the same major-minor version. | ||
py_limited_api = sys.version_info >= (3, 12) | ||
options = {"bdist_wheel": {"py_limited_api": "cp312"}} if py_limited_api else {} | ||
|
||
|
||
class BazelExtension(setuptools.Extension): | ||
"""A C/C++ extension that is defined as a Bazel BUILD target.""" | ||
|
||
def __init__(self, name: str, bazel_target: str, **kwargs): | ||
super().__init__(name=name, sources=[], **kwargs) | ||
|
||
self.bazel_target = bazel_target | ||
stripped_target = bazel_target.split("//")[-1] | ||
self.relpath, self.target_name = stripped_target.split(":") | ||
|
||
|
||
class BuildBazelExtension(build_ext.build_ext): | ||
"""A command that runs Bazel to build a C/C++ extension.""" | ||
|
||
def run(self): | ||
for ext in self.extensions: | ||
self.bazel_build(ext) | ||
super().run() | ||
# explicitly call `bazel shutdown` for graceful exit | ||
self.spawn(["bazel", "shutdown"]) | ||
|
||
def copy_extensions_to_source(self): | ||
""" | ||
Copy generated extensions into the source tree. | ||
This is done in the ``bazel_build`` method, so it's not necessary to | ||
do again in the `build_ext` base class. | ||
""" | ||
pass | ||
|
||
def bazel_build(self, ext: BazelExtension) -> None: | ||
"""Runs the bazel build to create a nanobind extension.""" | ||
temp_path = Path(self.build_temp) | ||
|
||
# Specifying only MAJOR.MINOR makes rules_python do an internal | ||
# lookup selecting the newest patch version. | ||
python_version = "{0}.{1}".format(*sys.version_info[:2]) | ||
|
||
bazel_argv = [ | ||
"bazel", | ||
"run", | ||
ext.bazel_target, | ||
f"--symlink_prefix={temp_path / 'bazel-'}", | ||
f"--compilation_mode={'dbg' if self.debug else 'opt'}", | ||
f"--target_python_version={python_version}", | ||
] | ||
|
||
if ext.py_limited_api: | ||
bazel_argv += ["--py_limited_api=cp312"] | ||
|
||
if IS_WINDOWS: | ||
# Link with python*.lib. | ||
# This technically breaks the hermeticity of rules_python, | ||
# but its library target does not contain libs/python3.lib for SABI builds, | ||
# so we source it from the build interpreter instead. | ||
for library_dir in self.library_dirs: | ||
bazel_argv.append("--linkopt=/LIBPATH:" + library_dir) | ||
|
||
self.spawn(bazel_argv) | ||
|
||
if IS_WINDOWS: | ||
suffix = ".pyd" | ||
else: | ||
suffix = ".abi3.so" if ext.py_limited_api else ".so" | ||
|
||
# copy the Bazel build artifacts into setuptools' libdir, | ||
# from where the wheel is built. | ||
srcdir = temp_path / "bazel-bin" / "src" | ||
libdir = Path(self.build_lib) / "nanobind_example" | ||
for root, dirs, files in os.walk(srcdir, topdown=True): | ||
# exclude runfiles directories and children. | ||
dirs[:] = [d for d in dirs if "runfiles" not in d] | ||
|
||
for f in files: | ||
fp = Path(f) | ||
should_copy = False | ||
# we do not want the bare .so file included | ||
# when building for ABI3, so we require a | ||
# full and exact match on the file extension. | ||
if "".join(fp.suffixes) == suffix: | ||
should_copy = True | ||
elif fp.suffix == ".pyi": | ||
should_copy = True | ||
elif Path(root) == srcdir and f == "py.typed": | ||
# copy py.typed, but only at the package root. | ||
should_copy = True | ||
|
||
if should_copy: | ||
shutil.copyfile(root / fp, libdir / fp) | ||
|
||
|
||
setuptools.setup( | ||
cmdclass=dict(build_ext=BuildBazelExtension), | ||
package_data={'nanobind_example': ["py.typed", "*.pyi", "**/*.pyi"]}, | ||
ext_modules=[ | ||
BazelExtension( | ||
name="nanobind_example.nanobind_example_ext", | ||
bazel_target="//src:nanobind_example_ext_stubgen", | ||
py_limited_api=py_limited_api, | ||
) | ||
], | ||
options=options, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
load( | ||
"@nanobind_bazel//:build_defs.bzl", | ||
"nanobind_extension", | ||
"nanobind_stubgen", | ||
) | ||
|
||
py_library( | ||
name = "nanobind_example", | ||
srcs = ["nanobind_example/__init__.py"], | ||
data = [":nanobind_example_ext"], | ||
visibility = ["//visibility:public"], | ||
) | ||
|
||
nanobind_extension( | ||
name = "nanobind_example_ext", | ||
srcs = ["nanobind_example_ext.cpp"], | ||
) | ||
|
||
nanobind_stubgen( | ||
name = "nanobind_example_ext_stubgen", | ||
module = ":nanobind_example_ext", | ||
marker_file = "src/py.typed", | ||
) |