Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v4: Updates for meson-f2py support #413

Merged
merged 10 commits into from
Nov 13, 2024
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
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Deprecated

## [4.9.0] - 2024-11-13

### Fixed

- Fixed `mepo status` code to allow for quiet failures. There seems to be an odd scenario on non-internet-connected machines where `mepo status` will fail in blobless clones of some repos. Running `mepo status` on a node with internet access seems to fix this.

### Changed

- For F2PY3 code, set CMake Policy CMP0132 if Python is 3.12+ or higher
- Add test to see if `ifort` spits out the deprecation warning. Needed to hack f2py/meson
- Set minimum CMake version to 3.24 for the meson + f2py fix

## [4.8.1] - 2024-11-07

### Fixed
Expand Down
25 changes: 25 additions & 0 deletions compiler/checks/check_fortran_support.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,28 @@ try_fortran_compile(
${CMAKE_CURRENT_LIST_DIR}/findloc.F90
FORTRAN_COMPILER_SUPPORTS_FINDLOC
)

# We also need to do something if we are using Intel Fortran Classic
# Namely, we need to know if when we run just plain 'ifort' if
# anything is output to stderr. If so, we need to set a CMake variable
# that will allow us to do something different later in the
# CMake process.

if (CMAKE_Fortran_COMPILER_ID STREQUAL "Intel")
if (NOT CMAKE_REQUIRED_QUIET)
message (STATUS "Checking if Intel Fortran Classic compiler has deprecation warning")
endif ()
execute_process(
COMMAND ${CMAKE_Fortran_COMPILER} --version
OUTPUT_QUIET
RESULT_VARIABLE IFORT_RESULT
ERROR_VARIABLE IFORT_STDERR
)
if (IFORT_STDERR)
message (STATUS "Checking if Intel Fortran Classic compiler has deprecation warning: FOUND")
message (STATUS "Setting IFORT_HAS_DEPRECATION_WARNING to TRUE")
set (IFORT_HAS_DEPRECATION_WARNING TRUE)
else ()
message (STATUS "Checking if Intel Fortran Classic compiler has deprecation warning: NOT FOUND")
endif ()
endif ()
3 changes: 2 additions & 1 deletion esma_support/esma_mepo_status.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ function(esma_capture_mepo_status)
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_FILE "${OUTPUT_FILE}"
RESULT_VARIABLE MEPO_STATUS_RESULT
ERROR_QUIET
)

if(NOT MEPO_STATUS_RESULT EQUAL 0)
message(WARNING "mepo state and command were found but failed to run mepo status --hashes. This is odd.")
message(WARNING "mepo state and command were found but failed to run mepo status --hashes. This seems to happen with blobless clones and running on a node without access to the internet. Skipping mepo status output capture, but to fix, run mepo status on a node with internet access.")
else()
message(STATUS "mepo status output captured in ${OUTPUT_FILE_NAME}")

Expand Down
3 changes: 2 additions & 1 deletion python/esma_python.cmake
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# CMake code involving Python

# FIND_STRATEGY needs CMake 3.15 or later
cmake_minimum_required(VERSION 3.15)
# The new policy needed for f2py3 and Meson is 3.24
cmake_minimum_required(VERSION 3.24)

# Find Python
find_package(Python COMPONENTS Interpreter)
Expand Down
13 changes: 11 additions & 2 deletions python/f2py/FindF2PY.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,23 @@ find_program(F2PY_EXECUTABLE NAMES "f2py${Python_VERSION_MAJOR}.${Python_VERSION
if(F2PY_EXECUTABLE)
# extract the version string
execute_process(COMMAND "${F2PY_EXECUTABLE}" -v
OUTPUT_VARIABLE F2PY_VERSION_STRING
OUTPUT_STRIP_TRAILING_WHITESPACE)
OUTPUT_VARIABLE F2PY_VERSION_STRING
OUTPUT_STRIP_TRAILING_WHITESPACE)
if("${F2PY_VERSION_STRING}" MATCHES "^([0-9]+)(.([0-9+]))?(.([0-9+]))?$")
set(F2PY_VERSION_MAJOR "${CMAKE_MATCH_1}")
set(F2PY_VERSION_MINOR "${CMAKE_MATCH_3}")
set(F2PY_VERSION_PATCH "${CMAKE_MATCH_5}")
endif()

# Testing has shown that f2py with Python 3.12+ needs to set
# a new CMake policy, CMP0132, because f2py uses Meson in the
# instead of distutils.
# See https://github.com/mesonbuild/meson/issues/13882
if (Python_VERSION_MINOR GREATER_EQUAL 12)
message(STATUS "[F2PY]: Setting CMP0132 policy to NEW")
cmake_policy(SET CMP0132 NEW)
endif ()

# Get the compiler-id and map it to compiler vendor as used by f2py.
# Currently, we only check for GNU, but this can easily be extended.
# Cache the result, so that we only need to check once.
Expand Down
94 changes: 73 additions & 21 deletions python/f2py/UseF2Py.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,32 @@ macro (add_f2py_module _name)

#message(STATUS "${_name} F2PY_Fortran_FLAGS ${F2PY_Fortran_FLAGS}")

set(_fcompiler_opts "--fcompiler=${F2PY_FCOMPILER}")
list(APPEND _fcompiler_opts "--f77exec=${CMAKE_Fortran_COMPILER}" "--f77flags='${F2PY_Fortran_FLAGS}'")
if(CMAKE_Fortran_COMPILER_SUPPORTS_F90)
list(APPEND _fcompiler_opts "--f90exec=${CMAKE_Fortran_COMPILER}" "--f90flags='${F2PY_Fortran_FLAGS}'")
endif(CMAKE_Fortran_COMPILER_SUPPORTS_F90)
# NOTE: This style of calling f2py is only for distutils. If you are using
# Python 3.12, the backend is now meson and this will not work
# so we need to test the Python version and then call the correct
# f2py

if (Python_VERSION VERSION_GREATER_EQUAL "3.12")
set(F2PY_BACKEND "meson")
else ()
set(F2PY_BACKEND "distutils")
endif ()

#message(STATUS "Using F2PY_BACKEND: ${F2PY_BACKEND}")

if (F2PY_BACKEND STREQUAL "distutils")
set(_fcompiler_opts "--fcompiler=${F2PY_FCOMPILER}")
list(APPEND _fcompiler_opts "--f77exec=${CMAKE_Fortran_COMPILER}" "--f77flags='${F2PY_Fortran_FLAGS}'")
if(CMAKE_Fortran_COMPILER_SUPPORTS_F90)
list(APPEND _fcompiler_opts "--f90exec=${CMAKE_Fortran_COMPILER}" "--f90flags='${F2PY_Fortran_FLAGS}'")
endif(CMAKE_Fortran_COMPILER_SUPPORTS_F90)
else ()
set(_fcompiler_opts "")
list(APPEND _fcompiler_opts "--f77flags='${F2PY_Fortran_FLAGS}'")
if(CMAKE_Fortran_COMPILER_SUPPORTS_F90)
list(APPEND _fcompiler_opts "--f90flags='${F2PY_Fortran_FLAGS}'")
endif(CMAKE_Fortran_COMPILER_SUPPORTS_F90)
endif ()

# Make the source filenames absolute.
set(_abs_srcs)
Expand All @@ -87,6 +108,13 @@ macro (add_f2py_module _name)
list(APPEND _abs_srcs ${_abs_src})
endforeach(_src ${add_f2py_module_SOURCES})

# Let's also get all directories that the sources are in
set(_src_inc_dirs)
foreach(_src ${_abs_srcs})
get_filename_component(_dir ${_src} DIRECTORY)
list(APPEND _src_inc_dirs ${_dir})
endforeach(_src ${_abs_srcs})

# Get a list of the include directories.
# The f2py --include_paths option, used when generating a signature file,
# needs a colon-separated list. The f2py -I option, used when compiling
Expand All @@ -103,6 +131,12 @@ macro (add_f2py_module _name)
endforeach(_dir)
string(REPLACE ";" ":" _inc_paths "${_inc_dirs}")

# We also want to include the directory where the
# sources are located as well into _inc_opts
foreach(_dir ${_src_inc_dirs})
list(APPEND _inc_opts "-I${_dir}")
endforeach(_dir)

set(_libs_opts)
foreach(_lib ${add_f2py_module_LIBRARIES})
# MAT This is hacky, but so is this whole code
Expand Down Expand Up @@ -189,8 +223,8 @@ macro (add_f2py_module _name)
continue()
endif ()
endif ()

list(APPEND _lib_opts "-l${_lib}")

endforeach()

else()
Expand Down Expand Up @@ -227,25 +261,43 @@ macro (add_f2py_module _name)

# Define the command to generate the Fortran to Python interface module. The
# output will be a shared library that can be imported by python.
if ( "${add_f2py_module_SOURCES}" MATCHES "^[^;]*\\.pyf;" )
add_custom_command(OUTPUT "${_name}${F2PY_SUFFIX}"
COMMAND ${F2PY_EXECUTABLE} ${F2PY_QUIET} -m ${_name}
--build-dir "${CMAKE_CURRENT_BINARY_DIR}/f2py-${_name}"
${_fcompiler_opts} ${_inc_opts} -c ${_abs_srcs} ${REDIRECT_TO_DEV_NULL}
DEPENDS ${add_f2py_module_SOURCES}
COMMENT "[F2PY] Building Fortran to Python interface module ${_name}")
else ( "${add_f2py_module_SOURCES}" MATCHES "^[^;]*\\.pyf;" )
# We also need to set FC in the environment to the fortran compiler
#message(STATUS "add_f2py_module_SOURCES: ${add_f2py_module_SOURCES}")
#message(STATUS "_inc_opts: ${_inc_opts}")
if ( F2PY_BACKEND STREQUAL "meson")
if(IFORT_HAS_DEPRECATION_WARNING)
set(MESON_F2PY_FCOMPILER "ifort -diag-disable=10448")
else()
set(MESON_F2PY_FCOMPILER "${CMAKE_Fortran_COMPILER}")
endif()
add_custom_command(OUTPUT "${_name}${F2PY_SUFFIX}"
COMMAND ${F2PY_EXECUTABLE} ${F2PY_QUIET} -m ${_name} -h ${_name}.pyf
--build-dir "${CMAKE_CURRENT_BINARY_DIR}/f2py-${_name}"
--include-paths ${_inc_paths} --overwrite-signature ${_abs_srcs} ${REDIRECT_TO_DEV_NULL}
COMMAND ${F2PY_EXECUTABLE} ${F2PY_QUIET} -m ${_name}
COMMAND ${CMAKE_COMMAND} -E env "FC=${MESON_F2PY_FCOMPILER}"
${F2PY_EXECUTABLE} ${F2PY_QUIET} -m ${_name}
--build-dir "${CMAKE_CURRENT_BINARY_DIR}/f2py-${_name}"
-c "${CMAKE_CURRENT_BINARY_DIR}/f2py-${_name}/${_name}.pyf"
${_fcompiler_opts} ${_inc_opts} ${_lib_opts} ${_abs_srcs} ${_lib_opts} ${_only} ${REDIRECT_TO_DEV_NULL}
${_fcompiler_opts} ${_inc_opts} ${_lib_opts} -c ${_abs_srcs} ${REDIRECT_TO_DEV_NULL}
DEPENDS ${add_f2py_module_SOURCES}
COMMENT "[F2PY] Building Fortran to Python interface module ${_name}")
endif ( "${add_f2py_module_SOURCES}" MATCHES "^[^;]*\\.pyf;" )
else ()
if ( "${add_f2py_module_SOURCES}" MATCHES "^[^;]*\\.pyf;" )
add_custom_command(OUTPUT "${_name}${F2PY_SUFFIX}"
COMMAND ${F2PY_EXECUTABLE} ${F2PY_QUIET} -m ${_name}
--build-dir "${CMAKE_CURRENT_BINARY_DIR}/f2py-${_name}"
${_fcompiler_opts} ${_inc_opts} -c ${_abs_srcs} ${REDIRECT_TO_DEV_NULL}
DEPENDS ${add_f2py_module_SOURCES}
COMMENT "[F2PY] Building Fortran to Python interface module ${_name}")
else ( "${add_f2py_module_SOURCES}" MATCHES "^[^;]*\\.pyf;" )
add_custom_command(OUTPUT "${_name}${F2PY_SUFFIX}"
COMMAND ${F2PY_EXECUTABLE} ${F2PY_QUIET} -m ${_name} -h ${_name}.pyf
--build-dir "${CMAKE_CURRENT_BINARY_DIR}/f2py-${_name}"
--include-paths ${_inc_paths} --overwrite-signature ${_abs_srcs} ${REDIRECT_TO_DEV_NULL}
COMMAND ${F2PY_EXECUTABLE} ${F2PY_QUIET} -m ${_name}
--build-dir "${CMAKE_CURRENT_BINARY_DIR}/f2py-${_name}"
-c "${CMAKE_CURRENT_BINARY_DIR}/f2py-${_name}/${_name}.pyf"
${_fcompiler_opts} ${_inc_opts} ${_lib_opts} ${_abs_srcs} ${_lib_opts} ${_only} ${REDIRECT_TO_DEV_NULL}
DEPENDS ${add_f2py_module_SOURCES}
COMMENT "[F2PY] Building Fortran to Python interface module ${_name}")
endif ( "${add_f2py_module_SOURCES}" MATCHES "^[^;]*\\.pyf;" )
endif ()



Expand Down
35 changes: 13 additions & 22 deletions python/f2py/esma_add_f2py_module.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,18 @@ macro (esma_add_f2py_module name)

add_f2py_module (${name} ${ARGN})

if (NOT CMAKE_Fortran_COMPILER_ID MATCHES GNU)
set(UNIT_TEST test_${name})
add_test (
NAME ${UNIT_TEST}
COMMAND ${CMAKE_COMMAND} -E env "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib:$ENV{LD_LIBRARY_PATH}" ${Python_EXECUTABLE} -c "import ${name}"
)

add_custom_command(
TARGET ${name}
COMMENT "Running Python import test on ${name}"
POST_BUILD
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND ${CMAKE_COMMAND} -E env "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib:$ENV{LD_LIBRARY_PATH}" ${Python_EXECUTABLE} -c "import ${name}"
)
else ()
# This code emits the warning once
get_property(prop_defined GLOBAL PROPERTY GNU_F2PY_WARNING_EMITTED DEFINED)
if (NOT prop_defined)
ecbuild_warn("There are currently issues with GNU and f2py from Conda that prevent running tests")
define_property(GLOBAL PROPERTY GNU_F2PY_WARNING_EMITTED BRIEF_DOCS "GNU-f2py" FULL_DOCS "GNU-f2py")
endif ()
endif ()
set(UNIT_TEST test_${name})
add_test (
NAME ${UNIT_TEST}
COMMAND ${CMAKE_COMMAND} -E env "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib:$ENV{LD_LIBRARY_PATH}" ${Python_EXECUTABLE} -c "import ${name}"
)

add_custom_command(
TARGET ${name}
COMMENT "Running Python import test on ${name}"
POST_BUILD
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND ${CMAKE_COMMAND} -E env "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib:$ENV{LD_LIBRARY_PATH}" ${Python_EXECUTABLE} -c "import ${name}"
)

endmacro ()
20 changes: 14 additions & 6 deletions python/f2py/try_f2py_compile.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,21 @@ macro (try_f2py_compile file var)
set( _f2py_check_bindir "${CMAKE_BINARY_DIR}/f2py_tmp")
file(MAKE_DIRECTORY ${_f2py_check_bindir})

# We need to work around a meson bug with ifort and stderr output
# Once we fully move to use ifx this can be removed
if (IFORT_HAS_DEPRECATION_WARNING)
message(STATUS "Using workaround for ifort with deprecation message")
set(MESON_F2PY_FCOMPILER "ifort -diag-disable=10448")
else ()
set(MESON_F2PY_FCOMPILER "${CMAKE_Fortran_COMPILER}")
endif ()
execute_process(
COMMAND ${F2PY_EXECUTABLE} -m test_ -c ${file} --fcompiler=${F2PY_FCOMPILER}
WORKING_DIRECTORY ${_f2py_check_bindir}
RESULT_VARIABLE result
OUTPUT_QUIET
ERROR_QUIET
)
COMMAND cmake -E env "FC=${MESON_F2PY_COMPILER}" ${F2PY_EXECUTABLE} -m test_ -c ${file} --fcompiler=${F2PY_FCOMPILER}
WORKING_DIRECTORY ${_f2py_check_bindir}
RESULT_VARIABLE result
OUTPUT_QUIET
ERROR_QUIET
)

if (result EQUAL 0)
file(GLOB F2PY_TEST_OUTPUT_FILE ${_f2py_check_bindir}/*.so)
Expand Down
21 changes: 15 additions & 6 deletions python/f2py3/FindF2PY3.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,31 @@

# Path to the f2py3 executable
find_program(F2PY3_EXECUTABLE NAMES "f2py${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}"
"f2py-${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}"
"f2py${Python3_VERSION_MAJOR}"
"f2py"
)
"f2py-${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}"
"f2py${Python3_VERSION_MAJOR}"
"f2py"
)

if(F2PY3_EXECUTABLE)
# extract the version string
execute_process(COMMAND "${F2PY3_EXECUTABLE}" -v
OUTPUT_VARIABLE F2PY3_VERSION_STRING
OUTPUT_STRIP_TRAILING_WHITESPACE)
OUTPUT_VARIABLE F2PY3_VERSION_STRING
OUTPUT_STRIP_TRAILING_WHITESPACE)
if("${F2PY3_VERSION_STRING}" MATCHES "^([0-9]+)(.([0-9+]))?(.([0-9+]))?$")
set(F2PY3_VERSION_MAJOR "${CMAKE_MATCH_1}")
set(F2PY3_VERSION_MINOR "${CMAKE_MATCH_3}")
set(F2PY3_VERSION_PATCH "${CMAKE_MATCH_5}")
endif()

# Testing has shown that f2py3 with Python 3.12+ needs to set
# a new CMake policy, CMP0132, because f2py3 uses Meson in the
# instead of distutils.
# See https://github.com/mesonbuild/meson/issues/13882
if (Python3_VERSION_MINOR GREATER_EQUAL 12)
message(STATUS "[F2PY3]: Setting CMP0132 policy to NEW")
cmake_policy(SET CMP0132 NEW)
endif ()

# Get the compiler-id and map it to compiler vendor as used by f2py3.
# Currently, we only check for GNU, but this can easily be extended.
# Cache the result, so that we only need to check once.
Expand Down
Loading
Loading