I maintain a Python module here and I'm having difficulty getting it to build correctly. I'm currently trying to switch from the old numpy distutils over to scikit-build-core
on the change-setup
branch.
The following steps apparently successfully install the module and put a file called geopack_tsyganenko.cpython-313-darwin.so
in my site-packages
:
git clone https://github.com/johncoxon/tsyganenko
cd tsyganenko
git checkout change-setup
pip install .
This results in the following in IPython:
In [1]: import geopack_tsyganenko
In [2]: geopack_tsyganenko.igrf_gsw_08?
Signature: geopack_tsyganenko.igrf_gsw_08(*args, **kwargs)
Type: fortran
String form: <fortran function igrf_gsw_08>
Docstring:
igrf_gsw_08(xgsw,ygsw,zgsw,hxgsw,hygsw,hzgsw)
Wrapper for ``igrf_gsw_08``.
Parameters
----------
xgsw : input float
ygsw : input float
zgsw : input float
hxgsw : input float
hygsw : input float
hzgsw : input float
However, geopack.pyf
specifies that the hxgsw
, hygsw
, and hzgsw
variables should be outputs, and they are not. This indicates that the build process is not seeing the geopack.pyf
file correctly.
On the other hand, if I then run:
cd src/tsyganenko
python -m numpy.f2py -c --f77flags="-w" geopack.pyf geopack.f T96.f T02.f -m geopack_tsyganenko --lower
It creates geopack_tsyganenko.cpython-313-darwin.so
in src/tsyganenko
and then if I copy that file manually into my site-packages
the following happens in IPython:
In [1]: import geopack_tsyganenko
In [2]: geopack_tsyganenko.igrf_gsw_08?
Signature: geopack_tsyganenko.igrf_gsw_08(*args, **kwargs)
Type: fortran
String form: <fortran function igrf_gsw_08>
Docstring:
hxgsw,hygsw,hzgsw = igrf_gsw_08(xgsw,ygsw,zgsw)
Wrapper for ``igrf_gsw_08``.
Parameters
----------
xgsw : input float
ygsw : input float
zgsw : input float
Returns
-------
hxgsw : float
hygsw : float
hzgsw : float
My question is, how do I need to change my CMakeLists.txt
file to get the build process to take account of geopack.pyf
and compile correctly? At the moment, CMakeLists.txt
looks like this:
cmake_minimum_required(VERSION 3.17.2...3.29)
project(${SKBUILD_PROJECT_NAME} LANGUAGES C Fortran)
find_package(
Python
COMPONENTS Interpreter Development.Module NumPy
REQUIRED)
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_Fortran_FLAGS "-w")
# 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)
add_library(fortranobject OBJECT "${F2PY_INCLUDE_DIR}/fortranobject.c")
target_link_libraries(fortranobject PUBLIC Python::NumPy)
target_include_directories(fortranobject PUBLIC "${F2PY_INCLUDE_DIR}")
set_property(TARGET fortranobject PROPERTY POSITION_INDEPENDENT_CODE ON)
# Define variables
set(FORTRAN_PYF_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/tsyganenko/geopack.pyf)
set(FORTRAN_SRC_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/tsyganenko/geopack.f)
set(FORTRAN_T96_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/tsyganenko/T96.f)
set(FORTRAN_T02_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/tsyganenko/T02.f)
set(MODULE_NAME geopack_tsyganenko)
set(F2PY_MODULE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME}module.c)
set(F2PY_WRAPPERS ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME}-f2pywrappers.f)
add_custom_command(
OUTPUT ${F2PY_MODULE} ${F2PY_WRAPPERS}
DEPENDS
${FORTRAN_PYF_FILE}
${FORTRAN_SRC_FILE}
${FORTRAN_T96_FILE}
${FORTRAN_T02_FILE}
VERBATIM
COMMAND "${PYTHON_EXECUTABLE}" -m numpy.f2py
"${FORTRAN_PYF_FILE}"
"${FORTRAN_SRC_FILE}"
"${FORTRAN_T96_FILE}"
"${FORTRAN_T02_FILE}"
-m ${MODULE_NAME}
--lower
)
python_add_library(
geopack_tsyganenko MODULE "${F2PY_MODULE}"
"${F2PY_WRAPPERS}"
"${FORTRAN_PYF_FILE}"
"${FORTRAN_SRC_FILE}"
"${FORTRAN_T96_FILE}"
"${FORTRAN_T02_FILE}"
WITH_SOABI)
target_link_libraries(${MODULE_NAME} PRIVATE fortranobject)
install(TARGETS ${MODULE_NAME} DESTINATION .)
I have finally got this to build correctly by modifying my CMakeLists.txt
to look like this:
cmake_minimum_required(VERSION 3.17.2...3.29)
project(${SKBUILD_PROJECT_NAME} LANGUAGES C Fortran)
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)
add_library(fortranobject OBJECT "${F2PY_INCLUDE_DIR}/fortranobject.c")
target_link_libraries(fortranobject PUBLIC Python::NumPy)
target_include_directories(fortranobject PUBLIC "${F2PY_INCLUDE_DIR}")
set_property(TARGET fortranobject PROPERTY POSITION_INDEPENDENT_CODE ON)
# Define variables
set(FORTRAN_FLAGS "-w -O2 -fbacktrace -fno-automatic -fPIC")
set(PYF_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/geopack_tsyganenko/Geopack.pyf)
set(GEOPACK_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/geopack_tsyganenko/Geopack.for)
set(T96_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/geopack_tsyganenko/T96.for)
set(T02_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/geopack_tsyganenko/T01_01c.for)
set(MODULE_NAME geopack_tsyganenko)
set(F2PY_SO_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME}${CMAKE_SHARED_MODULE_SUFFIX})
add_custom_command(
OUTPUT ${F2PY_SO_FILE}
DEPENDS
${PYF_FILE}
${GEOPACK_FILE}
${T96_FILE}
${T02_FILE}
VERBATIM
COMMAND "${PYTHON_EXECUTABLE}" -m numpy.f2py -c --f77flags="${FORTRAN_FLAGS}"
"${PYF_FILE}"
"${GEOPACK_FILE}"
"${T96_FILE}"
"${T02_FILE}"
-m ${MODULE_NAME}
--lower
)
add_custom_target(${MODULE_NAME}_f2py ALL
DEPENDS ${F2PY_SO_FILE}
)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/ DESTINATION .
FILES_MATCHING PATTERN "${MODULE_NAME}*${CMAKE_SHARED_MODULE_SUFFIX}")
I had trouble with this until I modified pyproject.toml
to look like this, specifically including meson
as a requirement in line 2:
[build-system]
requires = ["scikit-build-core", "meson", "numpy"]
build-backend = "scikit_build_core.build"
[project]
name = "tsyganenko"
version = "2020.2.0"
dependencies = [
"matplotlib",
"numpy",
"pandas"
]
requires-python = ">=3.12"
authors = [
{name = "John C Coxon", email = "work@johncoxon.co.uk"},
{name = "Sebastien de Larquier"}
]
description = "A Python wrapper for N A Tsyganenko’s field-line tracing routines."
readme = "README.md"
license = "MIT"
license-files = ["LICENCE.txt"]
keywords = [
"magnetic field",
"magnetosphere"
]
classifiers = ["Development Status :: 4 - Beta",
"Intended Audience :: Science/Research",
"Natural Language :: English",
"Programming Language :: Python :: 3",
"Topic :: Scientific/Engineering :: Physics"
]
[project.optional-dependencies]
notebook = ["jupyter"]
[project.urls]
Geopack = "https://geo.phys.spbu.ru/~tsyganenko/empirical-models/"
doi = "https://doi.org/10.5281/zenodo.3937276"
repository = "https://github.com/johncoxon/tsyganenko"
[tool.scikit-build]
ninja.version = ">=1.10"
cmake.version = ">=3.17.2"
[tool.setuptools.packages.find]
where = ["src"]