From 777f3f1073ea783c57357e8afa11e5598a5d8b3f Mon Sep 17 00:00:00 2001 From: Frank Male Date: Wed, 1 Mar 2023 22:37:34 -0500 Subject: [PATCH 01/10] :tada: new fortran module --- projects/pi-fortran/CMakeLists.txt | 22 ++++++++++++++++++++++ projects/pi-fortran/README.md | 18 ++++++++++++++++++ projects/pi-fortran/pi/_pi.f | 17 +++++++++++++++++ projects/pi-fortran/pyproject.toml | 9 +++++++++ projects/pi-fortran/setup.py | 11 +++++++++++ projects/pi-fortran/tests/test_pi.py | 8 ++++++++ 6 files changed, 85 insertions(+) create mode 100644 projects/pi-fortran/CMakeLists.txt create mode 100644 projects/pi-fortran/README.md create mode 100644 projects/pi-fortran/pi/_pi.f create mode 100644 projects/pi-fortran/pyproject.toml create mode 100644 projects/pi-fortran/setup.py create mode 100644 projects/pi-fortran/tests/test_pi.py diff --git a/projects/pi-fortran/CMakeLists.txt b/projects/pi-fortran/CMakeLists.txt new file mode 100644 index 0000000..8ead677 --- /dev/null +++ b/projects/pi-fortran/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.4...3.22) + +project(pi) + +find_package(PythonInterp REQUIRED) +find_package(PythonLibs) +find_package(PythonExtensions 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 +) +find_package(F2PY REQUIRED) + +add_f2py_target(pi pi/_pi.f) + +add_library(pi MODULE ${pi}) +target_link_libraries(pi) +install(TARGETS pi DESTINATION .) diff --git a/projects/pi-fortran/README.md b/projects/pi-fortran/README.md new file mode 100644 index 0000000..947d9bd --- /dev/null +++ b/projects/pi-fortran/README.md @@ -0,0 +1,18 @@ +# Pi + +This is an example project demonstrating the use of scikit-build for distributing a standalone FORTRAN library, *pi*; +a CMake package for that library; and a Python wrapper implemented in f2py. + +The example assume some familiarity with CMake and f2py, only really going into detail on the scikit-build parts. + +To install the package run in the project directory + +```bash +pip install . +``` + +To run the Python tests, first install the package, then in the project directory run + +```bash +pytest +``` diff --git a/projects/pi-fortran/pi/_pi.f b/projects/pi-fortran/pi/_pi.f new file mode 100644 index 0000000..be09dc9 --- /dev/null +++ b/projects/pi-fortran/pi/_pi.f @@ -0,0 +1,17 @@ + subroutine estimate_pi(pi_estimated, num_trials) +C Estimate pi using Monte Carlo + integer num_trials, num_in_circle + real*8 pi_estimated, x, y +Cf2py intent(in) num_trials +Cf2py intent(out) pi_estimated + + num_in_circle = 0d0 + do i = 1, num_trials + call random_number(x) + call random_number(y) + if (x**2 + y**2 < 1.0d0) then + num_in_circle = num_in_circle + 1 + endif + end do + pi_estimated = (4d0 * num_in_circle) / num_trials + end subroutine estimate_pi diff --git a/projects/pi-fortran/pyproject.toml b/projects/pi-fortran/pyproject.toml new file mode 100644 index 0000000..d266727 --- /dev/null +++ b/projects/pi-fortran/pyproject.toml @@ -0,0 +1,9 @@ +[build-system] +requires = [ + "setuptools>=42", + "scikit-build>=0.13", + "cmake>=3.18", + "ninja", + "numpy>=1.21", +] +build-backend = "setuptools.build_meta" diff --git a/projects/pi-fortran/setup.py b/projects/pi-fortran/setup.py new file mode 100644 index 0000000..4e8c444 --- /dev/null +++ b/projects/pi-fortran/setup.py @@ -0,0 +1,11 @@ +from skbuild import setup + +setup( + name="pi-fortran", + version="1.0.1", + description="a minimal example package (fortran version)", + author="The scikit-build team", + license="MIT", + packages=["pi"], + python_requires=">=3.7", +) diff --git a/projects/pi-fortran/tests/test_pi.py b/projects/pi-fortran/tests/test_pi.py new file mode 100644 index 0000000..3af765f --- /dev/null +++ b/projects/pi-fortran/tests/test_pi.py @@ -0,0 +1,8 @@ +import math +import pi +import pytest + + +def test_estimate_pi(): + pi_est = pi.estimate_pi(1e8) + assert pi_est == pytest.approx(math.pi, rel=0.001) From 5ad5096e9761d1e6f0a580324ac672bd9225024a Mon Sep 17 00:00:00 2001 From: frank1010111 Date: Fri, 3 Mar 2023 20:57:43 -0500 Subject: [PATCH 02/10] :bug: fix f2py build error with numpy headers not being found by cmake --- projects/pi-fortran/CMakeLists.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/projects/pi-fortran/CMakeLists.txt b/projects/pi-fortran/CMakeLists.txt index 8ead677..94b02cf 100644 --- a/projects/pi-fortran/CMakeLists.txt +++ b/projects/pi-fortran/CMakeLists.txt @@ -6,6 +6,14 @@ find_package(PythonInterp REQUIRED) find_package(PythonLibs) find_package(PythonExtensions REQUIRED) +#NumPy headers +execute_process( + COMMAND "${PYTHON_EXECUTABLE}" + -c "import numpy; print(numpy.get_include())" + OUTPUT_VARIABLE NumPy_INCLUDE_DIRS + OUTPUT_STRIP_TRAILING_WHITESPACE +) + # F2PY headers execute_process( COMMAND "${PYTHON_EXECUTABLE}" From ff7021933700e6d03a8774a37276fceed7eeb114 Mon Sep 17 00:00:00 2001 From: frank1010111 Date: Fri, 3 Mar 2023 21:04:31 -0500 Subject: [PATCH 03/10] add pi-fortran project to nox test list --- noxfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index 0f937e6..3ee8c63 100644 --- a/noxfile.py +++ b/noxfile.py @@ -6,7 +6,7 @@ hello_list = ["hello-pure", "hello-cpp", "hello-pybind11", "hello-cython"] if not sys.platform.startswith("win"): hello_list.append("hello-cmake-package") -long_hello_list = hello_list + ["pen2-cython", "core-c-hello", "core-pybind11-hello"] +long_hello_list = hello_list + ["pen2-cython", "core-c-hello", "core-pybind11-hello", "pi-fortran"] @nox.session From 38bbe7795628df22968474e5c504d5327367dcb8 Mon Sep 17 00:00:00 2001 From: frank1010111 Date: Fri, 3 Mar 2023 21:24:52 -0500 Subject: [PATCH 04/10] Use CMakeLists suggested by numpy, which does not depend on find_package(F2PY) --- projects/pi-fortran/CMakeLists.txt | 47 ++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/projects/pi-fortran/CMakeLists.txt b/projects/pi-fortran/CMakeLists.txt index 94b02cf..4e82d07 100644 --- a/projects/pi-fortran/CMakeLists.txt +++ b/projects/pi-fortran/CMakeLists.txt @@ -21,10 +21,47 @@ execute_process( OUTPUT_VARIABLE F2PY_INCLUDE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE ) -find_package(F2PY REQUIRED) -add_f2py_target(pi pi/_pi.f) +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_library(pi MODULE ${pi}) -target_link_libraries(pi) -install(TARGETS pi DESTINATION .) +# Target for enforcing dependencies +add_custom_target(genpyf + DEPENDS "${fortran_src_file}" +) +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${f2py_module_c}" + COMMAND ${PYTHON_EXECUTABLE} -m "numpy.f2py" + "${fortran_src_file}" + -m "pi" + --lower # Important + DEPENDS pi/_pi.f # Fortran source +) + +add_library(${CMAKE_PROJECT_NAME} MODULE + "${f2py_module_name}module.c" + "${F2PY_INCLUDE_DIR}/fortranobject.c" + "${fortran_src_file}") + +target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC + ${F2PY_INCLUDE_DIR} + ${NumPy_INCLUDE_DIRS} + ${PYTHON_INCLUDE_DIRS}) +set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES SUFFIX "${PYTHON_EXTENSION_MODULE_SUFFIX}") +set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES PREFIX "") + +# Linker fixes +if (UNIX) + if (APPLE) + set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES + LINK_FLAGS '-Wl,-dylib,-undefined,dynamic_lookup') + else() + set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES + LINK_FLAGS '-Wl,--allow-shlib-undefined') + endif() +endif() + +add_dependencies(${CMAKE_PROJECT_NAME} genpyf) + +install(TARGETS ${CMAKE_PROJECT_NAME} DESTINATION pi) From 681f16c9e442063df001b90857fd077063c36044 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sat, 4 Mar 2023 17:28:52 -0500 Subject: [PATCH 05/10] ci: skip Windows for Fortran --- noxfile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noxfile.py b/noxfile.py index 3ee8c63..eed532f 100644 --- a/noxfile.py +++ b/noxfile.py @@ -5,8 +5,8 @@ hello_list = ["hello-pure", "hello-cpp", "hello-pybind11", "hello-cython"] if not sys.platform.startswith("win"): - hello_list.append("hello-cmake-package") -long_hello_list = hello_list + ["pen2-cython", "core-c-hello", "core-pybind11-hello", "pi-fortran"] + hello_list.extend(["hello-cmake-package", "pi-fortran"]) +long_hello_list = hello_list + ["pen2-cython", "core-c-hello", "core-pybind11-hello"] @nox.session From 76de73a7b0929135cbdfc8528985ee70717f24f8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 5 Mar 2023 18:41:23 +0000 Subject: [PATCH 06/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- projects/pi-fortran/tests/test_pi.py | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/pi-fortran/tests/test_pi.py b/projects/pi-fortran/tests/test_pi.py index 3af765f..a044400 100644 --- a/projects/pi-fortran/tests/test_pi.py +++ b/projects/pi-fortran/tests/test_pi.py @@ -1,4 +1,5 @@ import math + import pi import pytest From 87d408a0ba73373a1139cb446c106abc22983a29 Mon Sep 17 00:00:00 2001 From: frank1010111 Date: Mon, 6 Mar 2023 21:40:16 -0500 Subject: [PATCH 07/10] :bug: fix f2py underscore problem --- projects/pi-fortran/CMakeLists.txt | 13 ++++++------- projects/pi-fortran/README.md | 7 +++++++ projects/pi-fortran/pi/pi.pyf | 10 ++++++++++ projects/pi-fortran/setup.py | 1 + projects/pi-fortran/tests/test_pi.py | 2 +- 5 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 projects/pi-fortran/pi/pi.pyf diff --git a/projects/pi-fortran/CMakeLists.txt b/projects/pi-fortran/CMakeLists.txt index 4e82d07..465af82 100644 --- a/projects/pi-fortran/CMakeLists.txt +++ b/projects/pi-fortran/CMakeLists.txt @@ -1,6 +1,10 @@ cmake_minimum_required(VERSION 3.4...3.22) -project(pi) +project(pi + VERSION 1.0.1 + DESCRIPTION "pi estimator" + LANGUAGES C Fortran +) find_package(PythonInterp REQUIRED) find_package(PythonLibs) @@ -26,14 +30,11 @@ set(f2py_module_name "pi") set(fortran_src_file "${CMAKE_SOURCE_DIR}/pi/_pi.f") set(f2py_module_c "${f2py_module_name}module.c") -# Target for enforcing dependencies -add_custom_target(genpyf - DEPENDS "${fortran_src_file}" -) 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 @@ -62,6 +63,4 @@ if (UNIX) endif() endif() -add_dependencies(${CMAKE_PROJECT_NAME} genpyf) - install(TARGETS ${CMAKE_PROJECT_NAME} DESTINATION pi) diff --git a/projects/pi-fortran/README.md b/projects/pi-fortran/README.md index 947d9bd..72bb506 100644 --- a/projects/pi-fortran/README.md +++ b/projects/pi-fortran/README.md @@ -16,3 +16,10 @@ To run the Python tests, first install the package, then in the project director ```bash 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$. + +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). +2. The module will require numpy to work. +3. Between failed builds, it is best to clear out the `_skbuild` folder. diff --git a/projects/pi-fortran/pi/pi.pyf b/projects/pi-fortran/pi/pi.pyf new file mode 100644 index 0000000..ef1fe39 --- /dev/null +++ b/projects/pi-fortran/pi/pi.pyf @@ -0,0 +1,10 @@ +! -*- f90 -*- +! Note: the context of this file is case sensitive. +pymodule pi +interface +subroutine estimate_pi(pi_estimated,num_trials) ! in pi/_pi.f + real*8 intent(out) :: pi_estimated + integer intent(in) :: num_trials +end subroutine estimate_pi +end interface +end pymodule pi diff --git a/projects/pi-fortran/setup.py b/projects/pi-fortran/setup.py index 4e8c444..9b232dd 100644 --- a/projects/pi-fortran/setup.py +++ b/projects/pi-fortran/setup.py @@ -8,4 +8,5 @@ license="MIT", packages=["pi"], python_requires=">=3.7", + install_requires=["numpy>=1.21"], ) diff --git a/projects/pi-fortran/tests/test_pi.py b/projects/pi-fortran/tests/test_pi.py index a044400..e1c505a 100644 --- a/projects/pi-fortran/tests/test_pi.py +++ b/projects/pi-fortran/tests/test_pi.py @@ -1,7 +1,7 @@ import math -import pi import pytest +from pi import pi def test_estimate_pi(): From 1980549a06dc2c170a831ca804a42b93ead44d35 Mon Sep 17 00:00:00 2001 From: frank1010111 Date: Tue, 7 Mar 2023 18:21:53 -0500 Subject: [PATCH 08/10] add fortran compiler setup to CI workflow --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 131b648..d680dc8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,11 @@ jobs: steps: - uses: actions/checkout@v3 + - uses: awvwgk/setup-fortran@main + id: setup-fortran + with: + compiler: gcc + version: 11 - uses: wntrblm/nox@2022.11.21 - run: nox From 21408d07bbccffd838430404b65eebfb2e6bf895 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Thu, 11 May 2023 17:22:23 -0400 Subject: [PATCH 09/10] chore: use scikit-build-core instead Signed-off-by: Henry Schreiner --- projects/pi-fortran/CMakeLists.txt | 36 ++++++------------------------ projects/pi-fortran/pyproject.toml | 17 +++++++++----- 2 files changed, 19 insertions(+), 34 deletions(-) diff --git a/projects/pi-fortran/CMakeLists.txt b/projects/pi-fortran/CMakeLists.txt index 465af82..b04d74a 100644 --- a/projects/pi-fortran/CMakeLists.txt +++ b/projects/pi-fortran/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.4...3.22) +cmake_minimum_required(VERSION 3.17.2...3.26) project(pi VERSION 1.0.1 @@ -6,21 +6,11 @@ project(pi LANGUAGES C Fortran ) -find_package(PythonInterp REQUIRED) -find_package(PythonLibs) -find_package(PythonExtensions REQUIRED) - -#NumPy headers -execute_process( - COMMAND "${PYTHON_EXECUTABLE}" - -c "import numpy; print(numpy.get_include())" - OUTPUT_VARIABLE NumPy_INCLUDE_DIRS - OUTPUT_STRIP_TRAILING_WHITESPACE -) +find_package(Python REQUIRED COMPONENTS Interpreter Development.Module NumPy) # F2PY headers execute_process( - COMMAND "${PYTHON_EXECUTABLE}" + COMMAND "${Python_EXECUTABLE}" -c "import numpy.f2py; print(numpy.f2py.get_include())" OUTPUT_VARIABLE F2PY_INCLUDE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE @@ -40,27 +30,15 @@ add_custom_command( DEPENDS pi/_pi.f # Fortran source ) -add_library(${CMAKE_PROJECT_NAME} MODULE +python_add_library(${CMAKE_PROJECT_NAME} MODULE "${f2py_module_name}module.c" "${F2PY_INCLUDE_DIR}/fortranobject.c" - "${fortran_src_file}") + "${fortran_src_file}" WITH_SOABI) target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC ${F2PY_INCLUDE_DIR} - ${NumPy_INCLUDE_DIRS} - ${PYTHON_INCLUDE_DIRS}) -set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES SUFFIX "${PYTHON_EXTENSION_MODULE_SUFFIX}") -set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES PREFIX "") + ) -# Linker fixes -if (UNIX) - if (APPLE) - set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES - LINK_FLAGS '-Wl,-dylib,-undefined,dynamic_lookup') - else() - set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES - LINK_FLAGS '-Wl,--allow-shlib-undefined') - endif() -endif() +target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC Python::NumPy) install(TARGETS ${CMAKE_PROJECT_NAME} DESTINATION pi) diff --git a/projects/pi-fortran/pyproject.toml b/projects/pi-fortran/pyproject.toml index d266727..cb3eef4 100644 --- a/projects/pi-fortran/pyproject.toml +++ b/projects/pi-fortran/pyproject.toml @@ -1,9 +1,16 @@ [build-system] requires = [ - "setuptools>=42", - "scikit-build>=0.13", - "cmake>=3.18", - "ninja", + "scikit-build-core", "numpy>=1.21", ] -build-backend = "setuptools.build_meta" +build-backend = "scikit_build_core.build" + +[project] +name = "pi-fortran" +version = "1.0.1" +requires-python = ">=3.7" +dependencies = ["numpy>=1.21"] + +[tool.scikit-build] +ninja.minimum-version = "1.10" +cmake.minimum-version = "3.17.2" From b47c111e41c1c641ccb8a087c3d9d72662f54cef Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Thu, 11 May 2023 17:54:21 -0400 Subject: [PATCH 10/10] Delete setup.py --- projects/pi-fortran/setup.py | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 projects/pi-fortran/setup.py diff --git a/projects/pi-fortran/setup.py b/projects/pi-fortran/setup.py deleted file mode 100644 index 9b232dd..0000000 --- a/projects/pi-fortran/setup.py +++ /dev/null @@ -1,12 +0,0 @@ -from skbuild import setup - -setup( - name="pi-fortran", - version="1.0.1", - description="a minimal example package (fortran version)", - author="The scikit-build team", - license="MIT", - packages=["pi"], - python_requires=">=3.7", - install_requires=["numpy>=1.21"], -)