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: 12 additions & 37 deletions projects/pi-fortran/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,52 +1,27 @@
cmake_minimum_required(VERSION 3.17.2...3.26)
cmake_minimum_required(VERSION 3.17.2...3.29)

project(pi
VERSION 1.0.1
DESCRIPTION "pi estimator"
LANGUAGES C Fortran
)

find_package(Python REQUIRED COMPONENTS Interpreter Development.Module NumPy)
find_package(
Python
COMPONENTS Interpreter Development.Module NumPy
REQUIRED)

# F2PY headers
execute_process(
COMMAND "${Python_EXECUTABLE}"
-c "import numpy.f2py; print(numpy.f2py.get_include())"
OUTPUT_VARIABLE F2PY_INCLUDE_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# On Windows the path comes back with backslashes, which CMake reads as escapes.
file(TO_CMAKE_PATH "${F2PY_INCLUDE_DIR}" F2PY_INCLUDE_DIR)

set(f2py_module_name "pi")
set(fortran_src_file "${CMAKE_SOURCE_DIR}/pi/_pi.f")
set(f2py_module_c "${f2py_module_name}module.c")

add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${f2py_module_c}"
COMMAND ${PYTHON_EXECUTABLE} -m "numpy.f2py"
"${fortran_src_file}"
"${CMAKE_SOURCE_DIR}/pi/pi.pyf" # Must include custom .pyf file
-m "pi"
--lower # Important
DEPENDS pi/_pi.f # Fortran source
)

python_add_library(${CMAKE_PROJECT_NAME} MODULE
"${f2py_module_name}module.c"
"${F2PY_INCLUDE_DIR}/fortranobject.c"
"${fortran_src_file}" WITH_SOABI)

target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC
${F2PY_INCLUDE_DIR}
)
include(UseF2Py)

target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC Python::NumPy)
# Build the extension in one call: f2py generates the wrappers from the
# checked-in signature file, then it and _pi.f are compiled and linked together
# with fortranobject. The module name (pi) is taken from the .pyf stem.
f2py_add_module(pi/pi.pyf pi/_pi.f)

# With the MinGW toolchain (Windows), statically link the GNU runtime so the
# module has no external libgfortran/libgcc/libwinpthread DLL dependencies.
if(WIN32 AND CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
target_link_options(${CMAKE_PROJECT_NAME} PRIVATE -static)
target_link_options(pi PRIVATE -static)
endif()

install(TARGETS ${CMAKE_PROJECT_NAME} DESTINATION pi)
install(TARGETS pi DESTINATION pi)
6 changes: 4 additions & 2 deletions projects/pi-fortran/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ pytest

This is slightly modified from the example in the [numpy docs](https://numpy.org/devdocs/f2py/buildtools/skbuild.html), but we are using Monte Carlo to estimate the value of $\pi$.

The f2py glue is handled by [f2py-cmake](https://github.com/scikit-build/f2py-cmake): `include(UseF2Py)` and a single `f2py_add_module(pi/pi.pyf pi/_pi.f)` call generate the wrappers and build the extension, instead of wiring up `add_custom_command` and `fortranobject.c` by hand.

A few surprises:
1. The dreaded underscore problem has a way of cropping up. One solution is explicitly writing out the interface in a [signature (`.pyf`) file](https://numpy.org/devdocs/f2py/signature-file.html).
1. The dreaded underscore problem has a way of cropping up. One solution is explicitly writing out the interface in a [signature (`.pyf`) file](https://numpy.org/devdocs/f2py/signature-file.html), which we pass to `f2py_add_module`.
2. The module will require numpy to work.
3. Between failed builds, it is best to clear out the `_skbuild` folder.
3. Between failed builds, it is best to clear out the `build` folder.
1 change: 1 addition & 0 deletions projects/pi-fortran/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
requires = [
"scikit-build-core",
"numpy>=1.21",
"f2py-cmake>=0.2",
]
build-backend = "scikit_build_core.build"

Expand Down
Loading