Skip to content

Commit

Permalink
Merge pull request #413 from GEOS-ESM/feature/mathomp4/meson-support
Browse files Browse the repository at this point in the history
v4: Updates for meson-f2py support
  • Loading branch information
mathomp4 authored Nov 13, 2024
2 parents d69e111 + e4b392b commit 47026fc
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 110 deletions.
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

0 comments on commit 47026fc

Please sign in to comment.