Skip to content

Instantly share code, notes, and snippets.

@kprussing
Last active October 11, 2022 23:13
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kprussing/db21614ca5b51cedff07dfb70059f280 to your computer and use it in GitHub Desktop.
Save kprussing/db21614ca5b51cedff07dfb70059f280 to your computer and use it in GitHub Desktop.
Packaging executable, shared library, and Python bindings
cmake_minimum_required(VERSION 3.14)
project(mwe LANGUAGES C CXX)
# Define the primary library and executable and install in the usual way
add_library(mwe SHARED library.cpp)
target_include_directories(mwe
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
)
add_executable(mwe.exe executable.cpp)
target_link_libraries(mwe.exe mwe)
include(GNUInstallDirs)
install(TARGETS mwe mwe.exe
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
# Define the Python binding module
find_package(PythonExtensions REQUIRED)
execute_process(
COMMAND ${PYTHON_EXECUTABLE} -c
"import pybind11; print(pybind11.get_cmake_dir())"
OUTPUT_VARIABLE pybind11_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
find_package(pybind11 REQUIRED)
add_library(_mwe MODULE mwe/_mwe.cpp)
python_extension_module(_mwe)
target_link_libraries(_mwe pybind11::module mwe)
install(TARGETS _mwe DESTINATION
${PYTHON_RELATIVE_SITE_PACKAGES_DIR}/mwe)
# Force the RPATH installation so the user does not have to fiddle
# with the library path `(DY)?LD_LIBRARY_PATH` 1_.
#
# .. _1: https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/RPATH-handling#always-full-rpath
set(lib_path "${PYTHON_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${lib_path}" is_system)
if ("${is_system}" STREQUAL "-1")
set_target_properties(mwe.exe PROPERTIES
INSTALL_RPATH_USE_LINK_PATH TRUE
INSTALL_RPATH "${lib_path}")
# The following is necessary for installation in a virtual
# environment `python -m pip venv env`
set_target_properties(_mwe PROPERTIES
INSTALL_RPATH_USE_LINK_PATH TRUE
INSTALL_RPATH "${lib_path}")
endif()
name: /Users/kprussing/Development/mwe/env
channels:
- defaults
dependencies:
- ca-certificates=2021.10.26=hecd8cb5_2
- certifi=2021.10.8=py38hecd8cb5_0
- libcxx=12.0.0=h2f01273_0
- libffi=3.3=hb1e8313_2
- ncurses=6.3=hca72f7f_2
- openssl=1.1.1l=h9ed2024_0
- pip=21.2.4=py38hecd8cb5_0
- python=3.8.12=h88f2d9e_0
- readline=8.1=h9ed2024_0
- setuptools=58.0.4=py38hecd8cb5_0
- sqlite=3.36.0=hce871da_0
- tk=8.6.11=h7bc2e8c_0
- wheel=0.37.0=pyhd3eb1b0_1
- xz=5.2.5=h1de35cc_0
- zlib=1.2.11=h1de35cc_3
- pip:
- attrs==21.2.0
- cmake==3.22.0
- distro==1.6.0
- iniconfig==1.1.1
- packaging==21.3
- pluggy==1.0.0
- py==1.11.0
- pybind11==2.8.1
- pyparsing==3.0.6
- pytest==6.2.5
- scikit-build==0.12.0
- toml==0.10.2
prefix: /Users/kprussing/Development/mwe/env
#include <cstdlib>
#include <cstring>
#include <iostream>
#include "library.h"
int main(int argc, char * argv[]) {
if (argc < 3) {
std::cerr << "usage: mwe <i> <j>" << std::endl;
return EXIT_FAILURE;
}
int i = std::atoi(argv[1]);
int j = std::atoi(argv[2]);
std::cout << i << " + " << j << " = " << i + j << std::endl;
return EXIT_SUCCESS;
}
#include "library.h"
int add(int i, int j) {
return i + j;
}
#ifndef MWE_H_
#define MWE_H_
int add(int i, int j);
#endif
from ._mwe import add
#include <pybind11/pybind11.h>
#include "library.h"
PYBIND11_MODULE(_mwe, m) {
m.def("add", &add);
}
attrs==21.2.0
iniconfig==1.1.1
mwe @ file:///Users/kprussing/Development/mwe
packaging==21.3
pluggy==1.0.0
py==1.11.0
pyparsing==3.0.6
pytest==6.2.5
toml==0.10.2
[build-system]
# These are the assumed default build requirements from pip:
# https://pip.pypa.io/en/stable/reference/pip/#pep-517-and-518-support
requires = ["setuptools>=40.8.0",
"wheel",
"scikit-build>=0.12.0",
"cmake>=3.14",
"pybind11>=2.6.1",
]
build-backend = "setuptools.build_meta"
from skbuild import setup
setup(
name="mwe",
packages=["mwe"],
)
import pathlib
import shutil
import subprocess
import mwe
def test_python():
assert mwe.add(1, 2) == 3
def test_executable():
mwe = shutil.which("mwe.exe")
assert mwe is not None
expected = pathlib.Path(__file__).parent.parent / "env" / "bin" / "mwe.exe"
assert mwe == str(expected.resolve())
proc = subprocess.run([mwe, "1", "2"], check=True, text=True,
capture_output=True)
assert proc.stdout.strip() == "1 + 2 = 3"
@kprussing
Copy link
Author

The files that contain -slash- should be moved to subdirectories.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment