Skip to content

Commit

Permalink
get python bindings working from venv/setup.py #15
Browse files Browse the repository at this point in the history
  • Loading branch information
timdewhirst committed Jul 29, 2022
1 parent ee33561 commit add7a02
Show file tree
Hide file tree
Showing 12 changed files with 79 additions and 46 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ endif()
add_subdirectory(openpiv)
add_subdirectory(test)
add_subdirectory(examples)
add_subdirectory(pybind)
33 changes: 32 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,35 @@ The binaries are located in the build directory:
* average_subtract
* openpiv -> libopenpivcore.so

### Python Bindings

Python bindings are made through pybind11. Building python modules appears to be a non-trivial
subject, however as openpivcore is based on vcpkg and cmake, a setup.py is provided to
build the core library as well as the python bindings using cmake.

Although vcpkg provides pybind11, this is not used as it has a dependency on vcpkg's python; the
aim is to allow the python bindings to work with the user's installed version of python. As such,
the supplied `pyproject.toml` specifies pybind11 as a required dependency.

To build using pip/setuptools:

* create a venv at the same level as the openpivcore directory
* activate the venv
* run pip against the name of the directory containing the openpiv code e.g. `pip install openpiv-c-qt/`

To build using cmake:

* install pybind11 using pip
* specify the location of the installed pybind11 libs/cmake modules using `-DCMAKE_PREFIX_PATH=<path to pybind11>`
* you can use pip & sed to find this: `pip show pybind11 | sed -n '/^Location/ s/.* \(.*\)/\1/ p'`
* specify that the python bindings should be built: `-DBUILD_PYBIND=ON`

e.g.

```
cmake -B build -S . -DBUILD_PYBIND=ON -DCMAKE_PREFIX_PATH=$(pip show pybind11 | sed -n '/^Location/ s/.* \(.*\)/\1/ p')
```

### Raspberry Pi

Build times are, as expected, much slower than on a modern Intel CPU, but the code
Expand Down Expand Up @@ -174,6 +203,7 @@ These are captured in `vcpkg.json`:
* benchmark: used to run performance benchmarks
* async++ (optional): implements c++17 parallel algorithms
* cxxopts: nice command line parsing
* pybind11

## Examples

Expand All @@ -187,6 +217,7 @@ These are captured in `vcpkg.json`:
* build
* [x] travis/github actions/CI
* [x] add clang/windows/OS X build
* [ ] python bindings
* core
* [x] logging
* [ ] iostream ops for ImageLoaders
Expand Down Expand Up @@ -218,7 +249,7 @@ These are captured in `vcpkg.json`:
* [ ] openCL
* [ ] apply kernel in Fourier space
* [ ] use SIMD?
* [ ] real -> complex FFT/correlation of real data
* [x] real -> complex FFT/correlation of real data
* [ ] direct correlation
* [x] peak detection
* [x] peak fitting
Expand Down
18 changes: 0 additions & 18 deletions openpiv/pybind/CMakeLists.txt

This file was deleted.

27 changes: 27 additions & 0 deletions pybind/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

option(BUILD_PYBIND "build python bindings" OFF)

if (BUILD_PYBIND)
message("building python bindings")

find_package(pybind11)

include_directories(${CMAKE_SOURCE_DIR}/openpiv)
if (pybind11_FOUND)
pybind11_add_module(
pyopenpivcore
core/module.cpp
core/point.cpp
core/rect.cpp
core/size.cpp
core/vector.cpp
)
target_link_libraries(
pyopenpivcore
PRIVATE openpivcore)

if (UNIX AND APPLE)
set_target_properties(pyopenpivcore PROPERTIES SUFFIX ".so")
endif()
endif()
endif()
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
19 changes: 5 additions & 14 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,10 @@ requires = [
]
build-backend = "setuptools.build_meta"

[tool.isort]
profile = "black"

[tool.pytest.ini_options]
minversion = "6.0"
addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"]
xfail_strict = true
filterwarnings = ["error"]
testpaths = ["tests"]

[tool.cibuildwheel]
test-command = "pytest {project}/tests"
test-extras = ["test"]
test-skip = ["*universal2:arm64"]
# test-command = "pytest {project}/tests"
# test-extras = ["test"]
# test-skip = ["*universal2:arm64"]

# Setuptools bug causes collision between pypy and cpython artifacts
before-build = "rm -rf {project}/build"
#before-build = "rm -rf {project}/build {project}/*.egg-info"
26 changes: 14 additions & 12 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from glob import glob
import os
import re
import subprocess
import sys

from setuptools import Extension, setup
from setuptools.command.build_ext import build_ext
from distutils import sysconfig as ds
from setuptools import setup
from setuptools import Extension as _extension
from setuptools.command.build_ext import build_ext as _build_ext

# Convert distutils Windows platform specifiers to CMake -A arguments
PLAT_TO_CMAKE = {
Expand All @@ -18,20 +21,21 @@
# A CMakeExtension needs a sourcedir instead of a file list.
# The name must be the _single_ output extension from the CMake build.
# If you need multiple extensions, see scikit-build.
class CMakeExtension(Extension):
class CMakeExtension(_extension):
def __init__(self, name, sourcedir=""):
Extension.__init__(self, name, sources=[])
super().__init__(name, sources=[])
self.sourcedir = os.path.abspath(sourcedir)


class CMakeBuild(build_ext):
class CMakeBuild(_build_ext):
def build_extension(self, ext):
extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))

# required for auto-detection & inclusion of auxiliary "native" libs
if not extdir.endswith(os.path.sep):
extdir += os.path.sep

# check for mode
debug = int(os.environ.get("DEBUG", 0)) if self.debug is None else self.debug
cfg = "Debug" if debug else "Release"

Expand All @@ -40,22 +44,20 @@ def build_extension(self, ext):
cmake_generator = os.environ.get("CMAKE_GENERATOR", "")

# Set Python_EXECUTABLE instead if you use PYBIND11_FINDPYTHON
# EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code
# from Python.
cmake_args = [
f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}",
f"-DCMAKE_PREFIX_PATH={ds.get_python_lib()}",
"-DBUILD_PYBIND=ON",
f"-DPYTHON_EXECUTABLE={sys.executable}",
f"-DCMAKE_BUILD_TYPE={cfg}", # not used on MSVC, but no harm
]
build_args = []

# Adding CMake arguments set as environment variable
# (needed e.g. to build for ARM OSx on conda-forge)
if "CMAKE_ARGS" in os.environ:
cmake_args += [item for item in os.environ["CMAKE_ARGS"].split(" ") if item]

# In this example, we pass in the version to C++. You might not need to.
cmake_args += [f"-DEXAMPLE_VERSION_INFO={self.distribution.get_version()}"]

if self.compiler.compiler_type != "msvc":
# Using Ninja-build since it a) is available as a wheel and b)
# multithreads automatically. MSVC would require all variables be
Expand Down Expand Up @@ -111,7 +113,7 @@ def build_extension(self, ext):
os.makedirs(build_temp)

subprocess.check_call(["cmake", ext.sourcedir] + cmake_args, cwd=build_temp)
subprocess.check_call(["cmake", "--build", "."] + build_args, cwd=build_temp)
subprocess.check_call(["cmake", "--build", ".", "--clean-first"] + build_args, cwd=build_temp)


# The information here can also be placed in setup.cfg - better separation of
Expand All @@ -123,7 +125,7 @@ def build_extension(self, ext):
author_email="[email protected]",
description="Python bindings for libopenpivcore",
long_description="",
ext_modules=[CMakeExtension("pyopenpivcore")],
ext_modules=[ CMakeExtension("pyopenpivcore") ],
cmdclass={"build_ext": CMakeBuild},
zip_safe=False,
extras_require={"test": ["pytest>=6.0"]},
Expand Down
1 change: 0 additions & 1 deletion vcpkg.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
"tiff",
"benchmark",
"asyncplusplus",
"pybind11",
{ "name": "mimalloc", "platform": "!(arm | uwp)" }
]
}

0 comments on commit add7a02

Please sign in to comment.