From a5f394ff2bab233500d8aec87344ff8eed31cd2b Mon Sep 17 00:00:00 2001 From: William Kearney Date: Thu, 18 Apr 2024 16:45:08 +0200 Subject: [PATCH] Interface with libtopotoolbox through scikit-build-core and pybind11 (#12) The current approach using `ctypes` requires separately building and installing libtopotoolbox in an appropriate directory, which is tricky to get right in a cross-platform way without substantial intervention from users. This set of changes introduces a new build system using scikit-build-core and pybind11 to build an extension module that includes an interface to our library. pyproject.toml is modified to switch the build backend to scikit-build-core to install pybind11 as a build-time dependency. src/lib.cpp contains the bindings for libtopotoolbox, defined using pybind11. The PYBIND11_MODULE declaration exposes a Python module _lib within the topotoolbox package that contains the bindings. Right now, it only binds `has_topotoolbox`, which has a trivial binding because it requires no marshalling of Python data into C/C++ types. Other functions such as `fillsinks` will require more complex binding declarations, but pybind11 provides an interface to working with NumPy arrays that should be helpful. CMakeLists.txt is used by scikit-build-core to build the package. It follows the guidance at https://scikit-build-core.readthedocs.io/en/latest/getting_started.html with the exception of adding libtopotoolbox using FetchContent. This will download the source from GitHub and then build a /static/ library for libtopotoolbox and link it into the /shared/ library that is built by pybind11. src/topotoolbox/__init__.py shows how to access the binding from the _lib module. In practice, we probably don't want to expose the raw bindings from the topotoolbox module but instead use them to implement methods within the proposed `GridObject` class. --- CMakeLists.txt | 23 +++++++++++++++++++++++ pyproject.toml | 4 ++-- src/lib.cpp | 11 +++++++++++ src/topotoolbox/__init__.py | 3 ++- 4 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 src/lib.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6e5363d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.15) + +project(${SKBUILD_PROJECT_NAME} + VERSION ${SKBUILD_PROJECT_VERSION} + LANGUAGES CXX) + +# Find TopoToolbox somewhere +include(FetchContent) +FetchContent_Declare( + topotoolbox + GIT_REPOSITORY https://github.com/TopoToolbox/libtopotoolbox.git + GIT_TAG main # In the future, we should track a specific tag/commit + # and bump it as we release versions +) +FetchContent_MakeAvailable(topotoolbox) + +set(PYBIND11_NEWPYTHON ON) +find_package(pybind11 CONFIG REQUIRED) + +pybind11_add_module(_lib src/lib.cpp) +target_link_libraries(_lib PRIVATE topotoolbox) + +install(TARGETS _lib LIBRARY DESTINATION topotoolbox) diff --git a/pyproject.toml b/pyproject.toml index 1d687a9..a981b04 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" +requires = ["scikit-build-core","pybind11"] +build-backend = "scikit_build_core.build" [project] name = "topotoolbox" diff --git a/src/lib.cpp b/src/lib.cpp new file mode 100644 index 0000000..50e61a1 --- /dev/null +++ b/src/lib.cpp @@ -0,0 +1,11 @@ +extern "C" { + #include +} + +#include + +namespace py = pybind11; + +PYBIND11_MODULE(_lib, m) { + m.def("has_topotoolbox",&has_topotoolbox); +} diff --git a/src/topotoolbox/__init__.py b/src/topotoolbox/__init__.py index 1a351e3..e7d647c 100644 --- a/src/topotoolbox/__init__.py +++ b/src/topotoolbox/__init__.py @@ -1 +1,2 @@ -from .grid_object import GridObject \ No newline at end of file +from .grid_object import GridObject +from ._lib import has_topotoolbox