Skip to content

Commit 0b79dc4

Browse files
committed
Add nanobind and pybind support
1 parent 8d0af69 commit 0b79dc4

File tree

20 files changed

+166
-108
lines changed

20 files changed

+166
-108
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ A simple, extensible C++ build plugin for [hatch](https://hatch.pypa.io/latest/)
1414
```toml
1515
[tool.hatch.build.hooks.hatch-cpp]
1616
libraries = [
17-
{name = "basic_project/extension", sources = ["cpp/basic-project/basic.cpp"], include-dirs = ["cpp"]}
17+
{name = "project/extension", sources = ["cpp/project/basic.cpp"], include-dirs = ["cpp"]}
1818
]
1919
```
2020

hatch_cpp/structs.py

+34-8
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
BuildType = Literal["debug", "release"]
2020
CompilerToolchain = Literal["gcc", "clang", "msvc"]
2121
Language = Literal["c", "c++"]
22+
Binding = Literal["cpython", "pybind11", "nanobind"]
2223
Platform = Literal["linux", "darwin", "win32"]
2324
PlatformDefaults = {
2425
"linux": {"CC": "gcc", "CXX": "g++", "LD": "ld"},
@@ -33,12 +34,18 @@ class HatchCppLibrary(BaseModel):
3334
name: str
3435
sources: List[str]
3536
language: Language = "c++"
37+
38+
binding: Binding = "cpython"
39+
std: Optional[str] = None
40+
3641
include_dirs: List[str] = Field(default_factory=list, alias="include-dirs")
3742
library_dirs: List[str] = Field(default_factory=list, alias="library-dirs")
3843
libraries: List[str] = Field(default_factory=list)
44+
3945
extra_compile_args: List[str] = Field(default_factory=list, alias="extra-compile-args")
4046
extra_link_args: List[str] = Field(default_factory=list, alias="extra-link-args")
4147
extra_objects: List[str] = Field(default_factory=list, alias="extra-objects")
48+
4249
define_macros: List[str] = Field(default_factory=list, alias="define-macros")
4350
undef_macros: List[str] = Field(default_factory=list, alias="undef-macros")
4451

@@ -82,29 +89,51 @@ def default() -> HatchCppPlatform:
8289

8390
def get_compile_flags(self, library: HatchCppLibrary, build_type: BuildType = "release") -> str:
8491
flags = ""
92+
93+
# Python.h
94+
library.include_dirs.append(get_path("include"))
95+
96+
if library.binding == "pybind11":
97+
import pybind11
98+
99+
library.include_dirs.append(pybind11.get_include())
100+
if not library.std:
101+
library.std = "c++11"
102+
elif library.binding == "nanobind":
103+
import nanobind
104+
105+
library.include_dirs.append(nanobind.include_dir())
106+
if not library.std:
107+
library.std = "c++17"
108+
library.sources.append(str(Path(nanobind.include_dir()).parent / "src" / "nb_combined.cpp"))
109+
library.include_dirs.append(str((Path(nanobind.include_dir()).parent / "ext" / "robin_map" / "include")))
110+
85111
if self.toolchain == "gcc":
86-
flags = f"-I{get_path('include')}"
87112
flags += " " + " ".join(f"-I{d}" for d in library.include_dirs)
88113
flags += " -fPIC"
89114
flags += " " + " ".join(library.extra_compile_args)
90115
flags += " " + " ".join(f"-D{macro}" for macro in library.define_macros)
91116
flags += " " + " ".join(f"-U{macro}" for macro in library.undef_macros)
117+
if library.std:
118+
flags += f" -std={library.std}"
92119
elif self.toolchain == "clang":
93-
flags = f"-I{get_path('include')} "
94120
flags += " ".join(f"-I{d}" for d in library.include_dirs)
95121
flags += " -fPIC"
96122
flags += " " + " ".join(library.extra_compile_args)
97123
flags += " " + " ".join(f"-D{macro}" for macro in library.define_macros)
98124
flags += " " + " ".join(f"-U{macro}" for macro in library.undef_macros)
125+
if library.std:
126+
flags += f" -std={library.std}"
99127
elif self.toolchain == "msvc":
100-
flags = f"/I{get_path('include')} "
101128
flags += " ".join(f"/I{d}" for d in library.include_dirs)
102129
flags += " " + " ".join(library.extra_compile_args)
103130
flags += " " + " ".join(library.extra_link_args)
104131
flags += " " + " ".join(library.extra_objects)
105132
flags += " " + " ".join(f"/D{macro}" for macro in library.define_macros)
106133
flags += " " + " ".join(f"/U{macro}" for macro in library.undef_macros)
107134
flags += " /EHsc /DWIN32"
135+
if library.std:
136+
flags += f" /std:{library.std}"
108137
# clean
109138
while flags.count(" "):
110139
flags = flags.replace(" ", " ")
@@ -142,7 +171,6 @@ def get_link_flags(self, library: HatchCppLibrary, build_type: BuildType = "rele
142171
flags += " " + " ".join(library.extra_link_args)
143172
flags += " " + " ".join(library.extra_objects)
144173
flags += " /LD"
145-
flags += f" /Fo:{library.name}.obj"
146174
flags += f" /Fe:{library.name}.pyd"
147175
flags += " /link /DLL"
148176
if (Path(executable).parent / "libs").exists():
@@ -178,10 +206,8 @@ def execute(self):
178206

179207
def cleanup(self):
180208
if self.platform.platform == "win32":
181-
for library in self.libraries:
182-
temp_obj = Path(f"{library.name}.obj")
183-
if temp_obj.exists():
184-
temp_obj.unlink()
209+
for temp_obj in Path(".").glob("*.obj"):
210+
temp_obj.unlink()
185211

186212

187213
class HatchCppBuildConfig(BaseModel):

hatch_cpp/tests/test_project_basic/cpp/basic-project/basic.cpp renamed to hatch_cpp/tests/test_project_basic/cpp/project/basic.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#include "basic-project/basic.hpp"
1+
#include "project/basic.hpp"
22

33
PyObject* hello(PyObject*, PyObject*) {
44
return PyUnicode_FromString("A string");

hatch_cpp/tests/test_project_basic/pyproject.toml

+6-34
Original file line numberDiff line numberDiff line change
@@ -14,50 +14,22 @@ dependencies = [
1414

1515
[tool.hatch.build]
1616
artifacts = [
17-
"basic_project/*.dll",
18-
"basic_project/*.dylib",
19-
"basic_project/*.so",
17+
"project/*.dll",
18+
"project/*.dylib",
19+
"project/*.so",
2020
]
2121

2222
[tool.hatch.build.sources]
2323
src = "/"
2424

2525
[tool.hatch.build.targets.sdist]
26-
packages = ["basic_project"]
26+
packages = ["project"]
2727

2828
[tool.hatch.build.targets.wheel]
29-
packages = ["basic_project"]
29+
packages = ["project"]
3030

3131
[tool.hatch.build.hooks.hatch-cpp]
3232
verbose = true
3333
libraries = [
34-
{name = "basic_project/extension", sources = ["cpp/basic-project/basic.cpp"], include-dirs = ["cpp"]}
34+
{name = "project/extension", sources = ["cpp/project/basic.cpp"], include-dirs = ["cpp"]}
3535
]
36-
37-
# build-function = "hatch_cpp.cpp_builder"
38-
39-
# [tool.hatch.build.hooks.defaults]
40-
# build-type = "release"
41-
42-
# [tool.hatch.build.hooks.env-vars]
43-
# TODO: these will all be available via
44-
# CLI after https://github.com/pypa/hatch/pull/1743
45-
# e.g. --hatch-cpp-build-type=debug
46-
# build-type = "BUILD_TYPE"
47-
# ccache = "USE_CCACHE"
48-
# manylinux = "MANYLINUX"
49-
# vcpkg = "USE_VCPKG"
50-
51-
# [tool.hatch.build.hooks.cmake]
52-
53-
# [tool.hatch.build.hooks.vcpkg]
54-
# triplets = {linux="x64-linux", macos="x64-osx", windows="x64-windows-static-md"}
55-
# clone = true
56-
# update = true
57-
58-
# [tool.hatch.build.hooks.hatch-cpp.build-kwargs]
59-
# path = "cpp"
60-
61-
[tool.pytest.ini_options]
62-
asyncio_mode = "strict"
63-
testpaths = "basic_project/tests"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#include "project/basic.hpp"
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#pragma once
2+
#include <nanobind/nanobind.h>
3+
#include <nanobind/stl/string.h>
4+
5+
NB_MODULE(extension, m) {
6+
m.def("hello", []() { return "A string"; });
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
[build-system]
2+
requires = ["hatchling>=1.20"]
3+
build-backend = "hatchling.build"
4+
5+
[project]
6+
name = "hatch-cpp-test-project-basic"
7+
description = "Basic test project for hatch-cpp"
8+
version = "0.1.0"
9+
requires-python = ">=3.9"
10+
dependencies = [
11+
"hatchling>=1.20",
12+
"hatch-cpp",
13+
]
14+
15+
[tool.hatch.build]
16+
artifacts = [
17+
"project/*.dll",
18+
"project/*.dylib",
19+
"project/*.so",
20+
]
21+
22+
[tool.hatch.build.sources]
23+
src = "/"
24+
25+
[tool.hatch.build.targets.sdist]
26+
packages = ["project"]
27+
28+
[tool.hatch.build.targets.wheel]
29+
packages = ["project"]
30+
31+
[tool.hatch.build.hooks.hatch-cpp]
32+
verbose = true
33+
libraries = [
34+
{name = "project/extension", sources = ["cpp/project/basic.cpp"], include-dirs = ["cpp"], binding = "nanobind"},
35+
]

hatch_cpp/tests/test_project_override_classes/cpp/basic-project/basic.cpp renamed to hatch_cpp/tests/test_project_override_classes/cpp/project/basic.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#include "basic-project/basic.hpp"
1+
#include "project/basic.hpp"
22

33
PyObject* hello(PyObject*, PyObject*) {
44
return PyUnicode_FromString("A string");

hatch_cpp/tests/test_project_override_classes/project/__init__.py

Whitespace-only changes.

hatch_cpp/tests/test_project_override_classes/pyproject.toml

+6-34
Original file line numberDiff line numberDiff line change
@@ -14,52 +14,24 @@ dependencies = [
1414

1515
[tool.hatch.build]
1616
artifacts = [
17-
"basic_project/*.dll",
18-
"basic_project/*.dylib",
19-
"basic_project/*.so",
17+
"project/*.dll",
18+
"project/*.dylib",
19+
"project/*.so",
2020
]
2121

2222
[tool.hatch.build.sources]
2323
src = "/"
2424

2525
[tool.hatch.build.targets.sdist]
26-
packages = ["basic_project"]
26+
packages = ["project"]
2727

2828
[tool.hatch.build.targets.wheel]
29-
packages = ["basic_project"]
29+
packages = ["project"]
3030

3131
[tool.hatch.build.hooks.hatch-cpp]
3232
build-config-class = "hatch_cpp.HatchCppBuildConfig"
3333
build-plan-class = "hatch_cpp.HatchCppBuildPlan"
3434
verbose = true
3535
libraries = [
36-
{name = "basic_project/extension", sources = ["cpp/basic-project/basic.cpp"], include-dirs = ["cpp"]}
36+
{name = "project/extension", sources = ["cpp/project/basic.cpp"], include-dirs = ["cpp"]}
3737
]
38-
39-
# build-function = "hatch_cpp.cpp_builder"
40-
41-
# [tool.hatch.build.hooks.defaults]
42-
# build-type = "release"
43-
44-
# [tool.hatch.build.hooks.env-vars]
45-
# TODO: these will all be available via
46-
# CLI after https://github.com/pypa/hatch/pull/1743
47-
# e.g. --hatch-cpp-build-type=debug
48-
# build-type = "BUILD_TYPE"
49-
# ccache = "USE_CCACHE"
50-
# manylinux = "MANYLINUX"
51-
# vcpkg = "USE_VCPKG"
52-
53-
# [tool.hatch.build.hooks.cmake]
54-
55-
# [tool.hatch.build.hooks.vcpkg]
56-
# triplets = {linux="x64-linux", macos="x64-osx", windows="x64-windows-static-md"}
57-
# clone = true
58-
# update = true
59-
60-
# [tool.hatch.build.hooks.hatch-cpp.build-kwargs]
61-
# path = "cpp"
62-
63-
[tool.pytest.ini_options]
64-
asyncio_mode = "strict"
65-
testpaths = "basic_project/tests"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include "project/basic.hpp"
2+
3+
std::string hello() {
4+
return "A string";
5+
}
6+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#pragma once
2+
#include <pybind11/pybind11.h>
3+
#include <string>
4+
5+
std::string hello();
6+
7+
PYBIND11_MODULE(extension, m) {
8+
m.def("hello", &hello);
9+
}

hatch_cpp/tests/test_project_pybind/project/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
[build-system]
2+
requires = ["hatchling>=1.20"]
3+
build-backend = "hatchling.build"
4+
5+
[project]
6+
name = "hatch-cpp-test-project-basic"
7+
description = "Basic test project for hatch-cpp"
8+
version = "0.1.0"
9+
requires-python = ">=3.9"
10+
dependencies = [
11+
"hatchling>=1.20",
12+
"hatch-cpp",
13+
]
14+
15+
[tool.hatch.build]
16+
artifacts = [
17+
"project/*.dll",
18+
"project/*.dylib",
19+
"project/*.so",
20+
]
21+
22+
[tool.hatch.build.sources]
23+
src = "/"
24+
25+
[tool.hatch.build.targets.sdist]
26+
packages = ["project"]
27+
28+
[tool.hatch.build.targets.wheel]
29+
packages = ["project"]
30+
31+
[tool.hatch.build.hooks.hatch-cpp]
32+
verbose = true
33+
libraries = [
34+
{name = "project/extension", sources = ["cpp/project/basic.cpp"], include-dirs = ["cpp"], binding="pybind11"},
35+
]

0 commit comments

Comments
 (0)