Skip to content

Commit 4e47570

Browse files
authored
feat(pi-fortran): use f2py-cmake instead of hand-rolled f2py wiring (#73)
Replace the manual numpy.f2py include probe, add_custom_command wrapper generation, and explicit fortranobject.c plumbing with f2py-cmake's UseF2Py module and a single f2py_add_module call. Default --lower and F77 detection preserve the previous behavior; the checked-in .pyf signature (the underscore-problem fix) is passed straight through. Assisted-by: ClaudeCode:claude-opus-4.8
1 parent cf5d3dd commit 4e47570

3 files changed

Lines changed: 17 additions & 39 deletions

File tree

projects/pi-fortran/CMakeLists.txt

Lines changed: 12 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,27 @@
1-
cmake_minimum_required(VERSION 3.17.2...3.26)
1+
cmake_minimum_required(VERSION 3.17.2...3.29)
22

33
project(pi
44
VERSION 1.0.1
55
DESCRIPTION "pi estimator"
66
LANGUAGES C Fortran
77
)
88

9-
find_package(Python REQUIRED COMPONENTS Interpreter Development.Module NumPy)
9+
find_package(
10+
Python
11+
COMPONENTS Interpreter Development.Module NumPy
12+
REQUIRED)
1013

11-
# F2PY headers
12-
execute_process(
13-
COMMAND "${Python_EXECUTABLE}"
14-
-c "import numpy.f2py; print(numpy.f2py.get_include())"
15-
OUTPUT_VARIABLE F2PY_INCLUDE_DIR
16-
OUTPUT_STRIP_TRAILING_WHITESPACE
17-
)
18-
# On Windows the path comes back with backslashes, which CMake reads as escapes.
19-
file(TO_CMAKE_PATH "${F2PY_INCLUDE_DIR}" F2PY_INCLUDE_DIR)
20-
21-
set(f2py_module_name "pi")
22-
set(fortran_src_file "${CMAKE_SOURCE_DIR}/pi/_pi.f")
23-
set(f2py_module_c "${f2py_module_name}module.c")
24-
25-
add_custom_command(
26-
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${f2py_module_c}"
27-
COMMAND ${PYTHON_EXECUTABLE} -m "numpy.f2py"
28-
"${fortran_src_file}"
29-
"${CMAKE_SOURCE_DIR}/pi/pi.pyf" # Must include custom .pyf file
30-
-m "pi"
31-
--lower # Important
32-
DEPENDS pi/_pi.f # Fortran source
33-
)
34-
35-
python_add_library(${CMAKE_PROJECT_NAME} MODULE
36-
"${f2py_module_name}module.c"
37-
"${F2PY_INCLUDE_DIR}/fortranobject.c"
38-
"${fortran_src_file}" WITH_SOABI)
39-
40-
target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC
41-
${F2PY_INCLUDE_DIR}
42-
)
14+
include(UseF2Py)
4315

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

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

52-
install(TARGETS ${CMAKE_PROJECT_NAME} DESTINATION pi)
27+
install(TARGETS pi DESTINATION pi)

projects/pi-fortran/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ pytest
1919

2020
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$.
2121

22+
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.
23+
2224
A few surprises:
23-
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).
25+
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`.
2426
2. The module will require numpy to work.
25-
3. Between failed builds, it is best to clear out the `_skbuild` folder.
27+
3. Between failed builds, it is best to clear out the `build` folder.

projects/pi-fortran/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
requires = [
33
"scikit-build-core",
44
"numpy>=1.21",
5+
"f2py-cmake>=0.2",
56
]
67
build-backend = "scikit_build_core.build"
78

0 commit comments

Comments
 (0)