diff --git a/projects/clib-wrapper/CMakeLists.txt b/projects/clib-wrapper/CMakeLists.txt new file mode 100644 index 0000000..8f0452c --- /dev/null +++ b/projects/clib-wrapper/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.13.0) +project(mylib LANGUAGES C) + + +find_package(PythonInterp REQUIRED) +find_package(PythonLibs REQUIRED) + + +### +# Private helper function to execute `python -c ""` +# +# Runs a python command and populates an outvar with the result of stdout. +# Be careful of indentation if `cmd` is multiline. +# +function(pycmd outvar cmd) + execute_process( + COMMAND "${PYTHON_EXECUTABLE}" -c "${cmd}" + RESULT_VARIABLE _exitcode + OUTPUT_VARIABLE _output) + if(NOT ${_exitcode} EQUAL 0) + message(ERROR "Failed when running python code: \"\"\" +${cmd}\"\"\"") + message(FATAL_ERROR "Python command failed with error code: ${_exitcode}") + endif() + # Remove supurflous newlines (artifacts of print) + string(STRIP "${_output}" _output) + set(${outvar} "${_output}" PARENT_SCOPE) +endfunction() + +### +# Find scikit-build and include its cmake resource scripts +# +if (NOT SKBUILD) + pycmd(skbuild_location "import os, skbuild; print(os.path.dirname(skbuild.__file__))") + set(skbuild_cmake_dir "${skbuild_location}/resources/cmake") + # If skbuild is not the driver, then we need to include its utilities in our CMAKE_MODULE_PATH + list(APPEND CMAKE_MODULE_PATH ${skbuild_cmake_dir}) +endif() + +find_package(PythonExtensions REQUIRED) +find_package(Cython REQUIRED) +find_package(NumPy REQUIRED) + + +# Backend C library +add_subdirectory("src/cxx") + +# Cython library +add_subdirectory("src/python/my_python_package") diff --git a/projects/clib-wrapper/setup.py b/projects/clib-wrapper/setup.py new file mode 100644 index 0000000..5be4423 --- /dev/null +++ b/projects/clib-wrapper/setup.py @@ -0,0 +1,18 @@ +""" +Command to demonstrates the content of a built wheel + +..code::bash + + python setup.py bdist_wheel + unzip -l dist/my_python_package-1.0.0-*.whl + +""" +from skbuild import setup + +if __name__ == '__main__': + setup( + package_dir={'': 'src/python/'}, + name="my_python_package", + version='1.0.0', + packages=['my_python_package'], + ) diff --git a/projects/clib-wrapper/src/cxx/CMakeLists.txt b/projects/clib-wrapper/src/cxx/CMakeLists.txt new file mode 100644 index 0000000..0a99346 --- /dev/null +++ b/projects/clib-wrapper/src/cxx/CMakeLists.txt @@ -0,0 +1,8 @@ +set(MY_C_MODULE_NAME "my_c_module") + +list(APPEND sources "my_c_module.c") + +add_library(${MY_C_MODULE_NAME} SHARED ${sources}) + +file(RELATIVE_PATH install_dest "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") +install(TARGETS ${MY_C_MODULE_NAME} LIBRARY DESTINATION "${install_dest}") diff --git a/projects/clib-wrapper/src/cxx/my_c_module.c b/projects/clib-wrapper/src/cxx/my_c_module.c new file mode 100644 index 0000000..a3dc018 --- /dev/null +++ b/projects/clib-wrapper/src/cxx/my_c_module.c @@ -0,0 +1,8 @@ + +int my_c_func(int* int32_array, int num_items){ + int result = 0; + for (int i = 0; i < num_items; i++){ + result += int32_array[i]; + } + return result; +} diff --git a/projects/clib-wrapper/src/cxx/my_c_module.h b/projects/clib-wrapper/src/cxx/my_c_module.h new file mode 100644 index 0000000..7c6f8ba --- /dev/null +++ b/projects/clib-wrapper/src/cxx/my_c_module.h @@ -0,0 +1,8 @@ +#ifndef MYLIB_H +#define MYLIB_H + + +int my_c_func(int* int32_array, int num_items); + + +#endif diff --git a/projects/clib-wrapper/src/python/my_python_package/CMakeLists.txt b/projects/clib-wrapper/src/python/my_python_package/CMakeLists.txt new file mode 100644 index 0000000..940a929 --- /dev/null +++ b/projects/clib-wrapper/src/python/my_python_package/CMakeLists.txt @@ -0,0 +1,34 @@ +set(cython_source "_cython_wrapper.pyx") +set(MODULE_NAME "_cython_wrapper") + +# Translate Cython into C/C++ +add_cython_target(${MODULE_NAME} "${cython_source}" C OUTPUT_VAR sources) + +# Add other C sources +list(APPEND sources) + +# Create C++ library. Specify include dirs and link libs as normal +add_library(${MODULE_NAME} MODULE ${sources}) +target_include_directories( + ${MODULE_NAME} + PUBLIC + ${NumPy_INCLUDE_DIRS} + ${PYTHON_INCLUDE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} +) + +# TODO: not sure what the right way to do this is +set(MY_C_MODULE_NAME "my_c_module") + +# What is the correct way to do this. +target_link_libraries(${MODULE_NAME} ${MY_C_MODULE_NAME}) + +target_compile_definitions(${MODULE_NAME} PUBLIC + "NPY_NO_DEPRECATED_API" + #"NPY_1_7_API_VERSION=0x00000007" + ) + +python_extension_module(${MODULE_NAME}) + +file(RELATIVE_PATH install_dest "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") +install(TARGETS ${MODULE_NAME} LIBRARY DESTINATION "${install_dest}") diff --git a/projects/clib-wrapper/src/python/my_python_package/__init__.py b/projects/clib-wrapper/src/python/my_python_package/__init__.py new file mode 100644 index 0000000..f3e1115 --- /dev/null +++ b/projects/clib-wrapper/src/python/my_python_package/__init__.py @@ -0,0 +1,4 @@ +from ._cython_wrapper import my_cython_func + + +__all__ = ['my_cython_func'] diff --git a/projects/clib-wrapper/src/python/my_python_package/_cython_wrapper.pyx b/projects/clib-wrapper/src/python/my_python_package/_cython_wrapper.pyx new file mode 100644 index 0000000..e9d69e4 --- /dev/null +++ b/projects/clib-wrapper/src/python/my_python_package/_cython_wrapper.pyx @@ -0,0 +1,22 @@ +cimport numpy as np + + +cdef extern from "../../cxx/my_c_module.h": + cdef int my_c_func(int* int32_array, int num_items); + + +def my_cython_func(np.ndarray[np.int32_t, ndim=1] int32_array): + """ + Example: + >>> import numpy as np + >>> int32_array = np.arange(10) * 10 + >>> result = my_cython_func(int32_array) + >>> print('result = {!r}'.format(result)) + """ + + # Get a raw c view of the ndarray + cdef int [:] int32_array_view = int32_array + cdef int num_items = len(int32_array) + + cdef int result = my_c_func(&int32_array_view[0], num_items) + return result diff --git a/projects/clib-wrapper/test_script.sh b/projects/clib-wrapper/test_script.sh new file mode 100644 index 0000000..a7eae27 --- /dev/null +++ b/projects/clib-wrapper/test_script.sh @@ -0,0 +1,14 @@ + + +method1(){ + # Build, view, install, and test the wheel + python setup.py bdist_wheel + unzip -l dist/my_python_package-1.0.0-*.whl + + pip install dist/my_python_package-1.0.0-*.whl + + pip uninstall my_python_package + + python -c "import my_python_package" + +}