Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Build and Test

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.12"]

steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install build dependencies
run: |
python -m pip install --upgrade pip
# scikit-build-core usually pulls these, but manual install ensures availability
python -m pip install cmake ninja build

- name: Build Wheel
run: python -m build

- name: Install swigbind11 project
run: |
# Install the generated wheel to test the actual build artifact
python -m pip install dist/*.whl

- name: Build and install
run: |
cd example
pip install . -v

- name: Run Pytest
run: |
cd example
pytest
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
venv
build
69 changes: 63 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,19 +1,76 @@
# See the file "LICENSE" for the full license governing this code.
cmake_minimum_required(VERSION 3.18)
project(swigbind11)
project(${SKBUILD_PROJECT_NAME} VERSION ${SKBUILD_PROJECT_VERSION} LANGUAGES CXX)

enable_language(CXX)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)

# Generate swigpyrun.h while installing swigbind11
find_package(SWIG REQUIRED)
include(${SWIG_USE_FILE})

set(BINARY_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include")
set(SWIGPYRUN_OUTPUT "${BINARY_INCLUDE_DIR}/swigpyrun.h")

file(MAKE_DIRECTORY ${BINARY_INCLUDE_DIR})

add_custom_command(
OUTPUT ${SWIGPYRUN_OUTPUT}
COMMAND ${SWIG_EXECUTABLE} -python -external-runtime ${SWIGPYRUN_OUTPUT}
COMMENT "Generating swigpyrun.h"
VERBATIM
)
Comment thread
ramandeepjain marked this conversation as resolved.

add_custom_target(generate_swigpyrun ALL DEPENDS ${SWIGPYRUN_OUTPUT})

# # Use relative paths for DESTINATION to ensure the package is relocatable.
set(swigbind11_REL_ROOT "swigbind11")

# --- Library Targets ---
add_library(swigbind11 INTERFACE)
add_library(swigbind11::swigbind11 ALIAS swigbind11)
add_library(swigbind11::headers ALIAS swigbind11)

include(GNUInstallDirs)

target_include_directories(
swigbind11 INTERFACE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
swigbind11 INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
$<INSTALL_INTERFACE:${swigbind11_REL_ROOT}/include>
)

target_compile_features(swigbind11 INTERFACE cxx_std_17)

install(TARGETS swigbind11)
set(swigbind11_INCLUDE_DIR "\$\{PACKAGE_PREFIX_DIR\}/swigbind11/include")
set(swigbind11_REL_CMAKE_DEST "${swigbind11_REL_ROOT}/share/cmake")

# --- Configuration Files ---
configure_package_config_file(
"tools/${PROJECT_NAME}Config.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION "${swigbind11_REL_CMAKE_DEST}"
)

# --- Installation Logic ---

# 1. Install Python source files (the __init__.py and other scripts)
install(DIRECTORY "swigbind11/"
DESTINATION "${swigbind11_REL_ROOT}")

# 2. Install C++ Headers into swigbind11/include/
install(DIRECTORY "include/swigbind11"
DESTINATION "${swigbind11_REL_ROOT}/include")

install(FILES ${SWIGPYRUN_OUTPUT} DESTINATION "${swigbind11_REL_ROOT}/include/swigbind11")

# 3. Install CMake Config into swigbind11/share/cmake/
install(TARGETS swigbind11 EXPORT swigbind11Targets)

install(EXPORT swigbind11Targets
FILE swigbind11Targets.cmake
NAMESPACE swigbind11::
DESTINATION "${swigbind11_REL_CMAKE_DEST}"
)

install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
DESTINATION "${swigbind11_REL_CMAKE_DEST}")
63 changes: 56 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- [Basics of *pybind11*](#basics-of-pybind11)
- [Combining libraries using *SWIG* and *pybind11*](#combining-libraries-using-swig-and-pybind11)
- [Using *swigbind11*](#using-swigbind11)
- [Example: FS Plugin](#example-fs-plugin)
- [Example: Mesh Plugin](#example-mesh-plugin)

## Overview

Expand Down Expand Up @@ -166,10 +166,60 @@ bindings from the module `a_swig` would have a different type compared to
instances created for the same underlying class `a::Bar` but via the *pybind11*
bindings. But, as we shall see in the next section, there is a solution!

## Installation
### Include with PyPI
To install *swigbind11* as a standard Python package within a virtual environment, run the following commands:
```bash
python3 -m venv venv
source venv/bin/activate
pip install .
```

This installation provides a CLI utility and configures the necessary metadata for CMake to locate the package.

To use *swigbind11* in your CMake project, you can help CMake locate its configuration files by dynamically retrieving this path using ``execute_process``.
Add the following snippet to your CMakeLists.txt:

````cmake
# Get the installation directory from the swigbind11 module
execute_process(
COMMAND ${Python3_EXECUTABLE} -m swigbind11 --cmakedir
OUTPUT_VARIABLE swigbind11_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)

# Locate the package using the path retrieved above
find_package(swigbind11 REQUIRED)
````

**NOTE**:
Ensure that ``${Python3_EXECUTABLE}`` points to the interpreter inside the virtual environment where the package was installed.

### Include as a submodule
To include this library as a dependency using Git Submodules, follow these steps:

```bash
git submodule add https://github.com/dlr-sp/swigbind11.git third_party/swigbind11
git submodule update --init --recursive
```

We assume that you are placing swigbind11 in *third_party* folder.

## Building with CMake
This assumes that you have activated a python environment and would like to install it in site-packages.
```bash
# 1. Configure the project
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=$VIRTUAL_ENV/lib/python3.10/site-packages

# 2. Build and Install
cmake --build build
cmake --install build
```

## Using *swigbind11*

Note: It is the responsibility of the user to add the *pybind11* headers to the header-search path of his build-system.
*swigbind11* is not shipped with a *pybind11* clone for this purpose!
Note: It is the responsibility of the user to add the *pybind11* headers to the header-search path of his build-system (see [Link](https://pybind11.readthedocs.io/en/stable/installing.html)).
*swigbind11* is not shipped with a *pybind11* clone for this purpose!

Interfacing of libraries wrapped via *SWIG* and *pybind11* is quite easy using
*swigbind11*. It allows the automatic conversion between *SWIG*-wrapped objects
Expand Down Expand Up @@ -230,10 +280,9 @@ and the corresponding Python type generated by *SWIG*. If these two types do not
match up, there is no way for *swigbind11* to detect this, and one will (as a
best case scenario) encounter segmentation faults and similar errors.

## Example: FS Plugin
## Example: Mesh Plugin

A demonstration of the usage of *swigbind11* for a real-world example (plugin
for the *Flow Simulator*) can be found in the [example directory](example/). To make the example more self-contained,
*pybind11* in included as a third-party library.
for a *mesh_library*) can be found in the [example directory](example/).

[^1]: https://github.com/tensorflow/community/blob/master/rfcs/20190208-pybind11.md
[^1]: https://github.com/tensorflow/community/blob/master/rfcs/20190208-pybind11.md
22 changes: 12 additions & 10 deletions example/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,21 @@ set(Python_FIND_STRATEGY LOCATION)

find_package(Python3 COMPONENTS Interpreter Development.Module REQUIRED)

# for a real plugin, you would probably add 'swigbind11' as a Git submodule at
# the appropriate location, e.g., '${PROJECT_SOURCE_DIR}/third_party/swigbind11'
set(SWIGBIND11_ROOT "${PROJECT_SOURCE_DIR}/../")
execute_process(
COMMAND ${Python3_EXECUTABLE} -m pybind11 --cmakedir
OUTPUT_VARIABLE pybind11_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)

# for pybind11, either the version included in swigbind11 can be used or a
# project-specific one, e.g., '${PROJECT_SOURCE_DIR}/third_party/pybind11'
set(PYBIND11_ROOT "${PROJECT_SOURCE_DIR}/third_party/pybind11")
find_package(pybind11 REQUIRED)

# pybind11 shipped as a third-party dependency
add_subdirectory(${PYBIND11_ROOT} "third_party/pybind11" EXCLUDE_FROM_ALL)
execute_process(
COMMAND ${Python3_EXECUTABLE} -m swigbind11 --cmakedir
OUTPUT_VARIABLE swigbind11_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)

# # swigbind11 shipped as a third-party dependency
add_subdirectory(${SWIGBIND11_ROOT} "third_party/swigbind11" EXCLUDE_FROM_ALL)
find_package(swigbind11 REQUIRED)

# subdirectories for the swig-wrapped mesh and pybind11-wrapped mesh plugin
add_subdirectory(src/example_mesh_library)
Expand Down
28 changes: 28 additions & 0 deletions example/local_backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""
Custom build backend for scikit-build-core.

This module acts as a wrapper around scikit-build-core to handle local
dependencies that are not yet available on PyPI. It specifically allows
the injection of 'swigbind11' from a relative path, bypassing the
limitation in scikit-build-core which disallows relative paths in
the 'build-system.requires' section of pyproject.toml.
"""
import os
Comment thread
ramandeepjain marked this conversation as resolved.
from scikit_build_core.build import *

def get_requires_for_build_wheel(config_settings=None):
"""
Return a list of dependencies required to build the wheel.

This dynamically locates 'swigbind11' relative to this
script's directory and appends it to the build requirements as absolute path.
"""
from scikit_build_core.build import get_requires_for_build_wheel as _get_reqs
reqs = _get_reqs(config_settings)

# Dynamically find the swigbind11 relative to this file
base_dir = os.path.dirname(os.path.abspath(__file__))
local_dep = os.path.abspath(os.path.join(base_dir, ".."))

reqs.append(f"swigbind11 @ file://{local_dep}")
return reqs
19 changes: 12 additions & 7 deletions example/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
[build-system]
requires = ["scikit-build-core", "swig==4.3.1"]
build-backend = "scikit_build_core.build"
requires = [
"scikit-build-core",
"swig<4.4.0",
"pybind11",
]
# Use a custom in-tree backend to inject local dependencies.
# This is a workaround because scikit-build-core does not support relative paths
# in 'build-system.requires'. This allows us to install 'swigbind11' from a
# local relative path until it is officially available on PyPI.
build-backend = "local_backend"
backend-path = ["."]
Comment thread
ramandeepjain marked this conversation as resolved.

[project]
name = "meshplugin"
Expand All @@ -11,17 +20,13 @@ authors = [
]
description = "Sample project demonstrating usage of swigbind11 for an prototypical swig project"
readme = "README.md"
requires-python = ">=3.7"
requires-python = ">=3.10"
keywords = []
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Topic :: Software Development :: Build Tools",
"Topic :: Software Development :: Libraries",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9"
]

dependencies = [
Expand Down
1 change: 0 additions & 1 deletion example/third_party/pybind11
Submodule pybind11 deleted from 8a099e
Loading