
GCC needs to be taught about every macOS release it seems, so older builds may not support usage on newer releases by default.
408 lines
14 KiB
CMake
408 lines
14 KiB
CMake
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
# file Copyright.txt or https://cmake.org/licensing for details.
|
|
|
|
#[=======================================================================[.rst:
|
|
FortranCInterface
|
|
-----------------
|
|
|
|
Fortran/C Interface Detection
|
|
|
|
This module automatically detects the API by which C and Fortran
|
|
languages interact.
|
|
|
|
Module Variables
|
|
^^^^^^^^^^^^^^^^
|
|
|
|
Variables that indicate if the mangling is found:
|
|
|
|
``FortranCInterface_GLOBAL_FOUND``
|
|
Global subroutines and functions.
|
|
|
|
``FortranCInterface_MODULE_FOUND``
|
|
Module subroutines and functions (declared by "MODULE PROCEDURE").
|
|
|
|
This module also provides the following variables to specify
|
|
the detected mangling, though a typical use case does not need
|
|
to reference them and can use the `Module Functions`_ below.
|
|
|
|
``FortranCInterface_GLOBAL_PREFIX``
|
|
Prefix for a global symbol without an underscore.
|
|
|
|
``FortranCInterface_GLOBAL_SUFFIX``
|
|
Suffix for a global symbol without an underscore.
|
|
|
|
``FortranCInterface_GLOBAL_CASE``
|
|
The case for a global symbol without an underscore,
|
|
either ``UPPER`` or ``LOWER``.
|
|
|
|
``FortranCInterface_GLOBAL__PREFIX``
|
|
Prefix for a global symbol with an underscore.
|
|
|
|
``FortranCInterface_GLOBAL__SUFFIX``
|
|
Suffix for a global symbol with an underscore.
|
|
|
|
``FortranCInterface_GLOBAL__CASE``
|
|
The case for a global symbol with an underscore,
|
|
either ``UPPER`` or ``LOWER``.
|
|
|
|
``FortranCInterface_MODULE_PREFIX``
|
|
Prefix for a module symbol without an underscore.
|
|
|
|
``FortranCInterface_MODULE_MIDDLE``
|
|
Middle of a module symbol without an underscore that appears
|
|
between the name of the module and the name of the symbol.
|
|
|
|
``FortranCInterface_MODULE_SUFFIX``
|
|
Suffix for a module symbol without an underscore.
|
|
|
|
``FortranCInterface_MODULE_CASE``
|
|
The case for a module symbol without an underscore,
|
|
either ``UPPER`` or ``LOWER``.
|
|
|
|
``FortranCInterface_MODULE__PREFIX``
|
|
Prefix for a module symbol with an underscore.
|
|
|
|
``FortranCInterface_MODULE__MIDDLE``
|
|
Middle of a module symbol with an underscore that appears
|
|
between the name of the module and the name of the symbol.
|
|
|
|
``FortranCInterface_MODULE__SUFFIX``
|
|
Suffix for a module symbol with an underscore.
|
|
|
|
``FortranCInterface_MODULE__CASE``
|
|
The case for a module symbol with an underscore,
|
|
either ``UPPER`` or ``LOWER``.
|
|
|
|
Module Functions
|
|
^^^^^^^^^^^^^^^^
|
|
|
|
.. command:: FortranCInterface_HEADER
|
|
|
|
The ``FortranCInterface_HEADER`` function is provided to generate a
|
|
C header file containing macros to mangle symbol names::
|
|
|
|
FortranCInterface_HEADER(<file>
|
|
[MACRO_NAMESPACE <macro-ns>]
|
|
[SYMBOL_NAMESPACE <ns>]
|
|
[SYMBOLS [<module>:]<function> ...])
|
|
|
|
It generates in ``<file>`` definitions of the following macros::
|
|
|
|
#define FortranCInterface_GLOBAL (name,NAME) ...
|
|
#define FortranCInterface_GLOBAL_(name,NAME) ...
|
|
#define FortranCInterface_MODULE (mod,name, MOD,NAME) ...
|
|
#define FortranCInterface_MODULE_(mod,name, MOD,NAME) ...
|
|
|
|
These macros mangle four categories of Fortran symbols, respectively:
|
|
|
|
* Global symbols without '_': ``call mysub()``
|
|
* Global symbols with '_' : ``call my_sub()``
|
|
* Module symbols without '_': ``use mymod; call mysub()``
|
|
* Module symbols with '_' : ``use mymod; call my_sub()``
|
|
|
|
If mangling for a category is not known, its macro is left undefined.
|
|
All macros require raw names in both lower case and upper case.
|
|
|
|
The options are:
|
|
|
|
``MACRO_NAMESPACE``
|
|
Replace the default ``FortranCInterface_`` prefix with a given
|
|
namespace ``<macro-ns>``.
|
|
|
|
``SYMBOLS``
|
|
List symbols to mangle automatically with C preprocessor definitions::
|
|
|
|
<function> ==> #define <ns><function> ...
|
|
<module>:<function> ==> #define <ns><module>_<function> ...
|
|
|
|
If the mangling for some symbol is not known then no preprocessor
|
|
definition is created, and a warning is displayed.
|
|
|
|
``SYMBOL_NAMESPACE``
|
|
Prefix all preprocessor definitions generated by the ``SYMBOLS``
|
|
option with a given namespace ``<ns>``.
|
|
|
|
.. command:: FortranCInterface_VERIFY
|
|
|
|
The ``FortranCInterface_VERIFY`` function is provided to verify
|
|
that the Fortran and C/C++ compilers work together::
|
|
|
|
FortranCInterface_VERIFY([CXX] [QUIET])
|
|
|
|
It tests whether a simple test executable using Fortran and C (and C++
|
|
when the CXX option is given) compiles and links successfully. The
|
|
result is stored in the cache entry ``FortranCInterface_VERIFIED_C``
|
|
(or ``FortranCInterface_VERIFIED_CXX`` if ``CXX`` is given) as a boolean.
|
|
If the check fails and ``QUIET`` is not given the function terminates with a
|
|
fatal error message describing the problem. The purpose of this check
|
|
is to stop a build early for incompatible compiler combinations. The
|
|
test is built in the ``Release`` configuration.
|
|
|
|
Example Usage
|
|
^^^^^^^^^^^^^
|
|
|
|
.. code-block:: cmake
|
|
|
|
include(FortranCInterface)
|
|
FortranCInterface_HEADER(FC.h MACRO_NAMESPACE "FC_")
|
|
|
|
This creates a "FC.h" header that defines mangling macros ``FC_GLOBAL()``,
|
|
``FC_GLOBAL_()``, ``FC_MODULE()``, and ``FC_MODULE_()``.
|
|
|
|
.. code-block:: cmake
|
|
|
|
include(FortranCInterface)
|
|
FortranCInterface_HEADER(FCMangle.h
|
|
MACRO_NAMESPACE "FC_"
|
|
SYMBOL_NAMESPACE "FC_"
|
|
SYMBOLS mysub mymod:my_sub)
|
|
|
|
This creates a "FCMangle.h" header that defines the same ``FC_*()``
|
|
mangling macros as the previous example plus preprocessor symbols
|
|
``FC_mysub`` and ``FC_mymod_my_sub``.
|
|
|
|
Additional Manglings
|
|
^^^^^^^^^^^^^^^^^^^^
|
|
|
|
FortranCInterface is aware of possible ``GLOBAL`` and ``MODULE`` manglings
|
|
for many Fortran compilers, but it also provides an interface to specify
|
|
new possible manglings. Set the variables::
|
|
|
|
FortranCInterface_GLOBAL_SYMBOLS
|
|
FortranCInterface_MODULE_SYMBOLS
|
|
|
|
before including FortranCInterface to specify manglings of the symbols
|
|
``MySub``, ``My_Sub``, ``MyModule:MySub``, and ``My_Module:My_Sub``.
|
|
For example, the code:
|
|
|
|
.. code-block:: cmake
|
|
|
|
set(FortranCInterface_GLOBAL_SYMBOLS mysub_ my_sub__ MYSUB_)
|
|
# ^^^^^ ^^^^^^ ^^^^^
|
|
set(FortranCInterface_MODULE_SYMBOLS
|
|
__mymodule_MOD_mysub __my_module_MOD_my_sub)
|
|
# ^^^^^^^^ ^^^^^ ^^^^^^^^^ ^^^^^^
|
|
include(FortranCInterface)
|
|
|
|
tells FortranCInterface to try given ``GLOBAL`` and ``MODULE`` manglings.
|
|
(The carets point at raw symbol names for clarity in this example but
|
|
are not needed.)
|
|
#]=======================================================================]
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Execute at most once in a project.
|
|
if(FortranCInterface_SOURCE_DIR)
|
|
return()
|
|
endif()
|
|
|
|
cmake_policy(PUSH)
|
|
cmake_policy(SET CMP0007 NEW)
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Verify that C and Fortran are available.
|
|
foreach(lang C Fortran)
|
|
if(NOT CMAKE_${lang}_COMPILER_LOADED)
|
|
message(FATAL_ERROR
|
|
"FortranCInterface requires the ${lang} language to be enabled.")
|
|
endif()
|
|
endforeach()
|
|
|
|
#-----------------------------------------------------------------------------
|
|
set(FortranCInterface_SOURCE_DIR ${CMAKE_ROOT}/Modules/FortranCInterface)
|
|
|
|
# MinGW's make tool does not always like () in the path
|
|
if("${CMAKE_GENERATOR}" MATCHES "MinGW" AND
|
|
"${FortranCInterface_SOURCE_DIR}" MATCHES "[()]")
|
|
file(COPY ${FortranCInterface_SOURCE_DIR}/
|
|
DESTINATION ${CMAKE_BINARY_DIR}/CMakeFiles/FortranCInterfaceMinGW)
|
|
set(FortranCInterface_SOURCE_DIR ${CMAKE_BINARY_DIR}/CMakeFiles/FortranCInterfaceMinGW)
|
|
endif()
|
|
|
|
# Create the interface detection project if it does not exist.
|
|
if(NOT FortranCInterface_BINARY_DIR)
|
|
set(FortranCInterface_BINARY_DIR ${CMAKE_BINARY_DIR}/CMakeFiles/FortranCInterface)
|
|
include(${FortranCInterface_SOURCE_DIR}/Detect.cmake)
|
|
endif()
|
|
|
|
# Load the detection results.
|
|
include(${FortranCInterface_BINARY_DIR}/Output.cmake)
|
|
|
|
#-----------------------------------------------------------------------------
|
|
function(FortranCInterface_HEADER file)
|
|
# Parse arguments.
|
|
if(IS_ABSOLUTE "${file}")
|
|
set(FILE "${file}")
|
|
else()
|
|
set(FILE "${CMAKE_CURRENT_BINARY_DIR}/${file}")
|
|
endif()
|
|
set(MACRO_NAMESPACE "FortranCInterface_")
|
|
set(SYMBOL_NAMESPACE)
|
|
set(SYMBOLS)
|
|
set(doing)
|
|
foreach(arg ${ARGN})
|
|
if("x${arg}" MATCHES "^x(SYMBOLS|SYMBOL_NAMESPACE|MACRO_NAMESPACE)$")
|
|
set(doing "${arg}")
|
|
elseif("x${doing}" MATCHES "^x(SYMBOLS)$")
|
|
list(APPEND "${doing}" "${arg}")
|
|
elseif("x${doing}" MATCHES "^x(SYMBOL_NAMESPACE|MACRO_NAMESPACE)$")
|
|
set("${doing}" "${arg}")
|
|
set(doing)
|
|
else()
|
|
message(AUTHOR_WARNING "Unknown argument: \"${arg}\"")
|
|
endif()
|
|
endforeach()
|
|
|
|
# Generate macro definitions.
|
|
set(HEADER_CONTENT)
|
|
set(_desc_GLOBAL "/* Mangling for Fortran global symbols without underscores. */")
|
|
set(_desc_GLOBAL_ "/* Mangling for Fortran global symbols with underscores. */")
|
|
set(_desc_MODULE "/* Mangling for Fortran module symbols without underscores. */")
|
|
set(_desc_MODULE_ "/* Mangling for Fortran module symbols with underscores. */")
|
|
foreach(macro GLOBAL GLOBAL_ MODULE MODULE_)
|
|
if(FortranCInterface_${macro}_MACRO)
|
|
string(APPEND HEADER_CONTENT "
|
|
${_desc_${macro}}
|
|
#define ${MACRO_NAMESPACE}${macro}${FortranCInterface_${macro}_MACRO}
|
|
")
|
|
endif()
|
|
endforeach()
|
|
|
|
# Generate symbol mangling definitions.
|
|
if(SYMBOLS)
|
|
string(APPEND HEADER_CONTENT "
|
|
/*--------------------------------------------------------------------------*/
|
|
/* Mangle some symbols automatically. */
|
|
")
|
|
endif()
|
|
foreach(f ${SYMBOLS})
|
|
if("${f}" MATCHES ":")
|
|
# Module symbol name. Parse "<module>:<function>" syntax.
|
|
string(REPLACE ":" ";" pieces "${f}")
|
|
list(GET pieces 0 module)
|
|
list(GET pieces 1 function)
|
|
string(TOUPPER "${module}" m_upper)
|
|
string(TOLOWER "${module}" m_lower)
|
|
string(TOUPPER "${function}" f_upper)
|
|
string(TOLOWER "${function}" f_lower)
|
|
if("${function}" MATCHES "_")
|
|
set(form "_")
|
|
else()
|
|
set(form "")
|
|
endif()
|
|
if(FortranCInterface_MODULE${form}_MACRO)
|
|
string(APPEND HEADER_CONTENT "#define ${SYMBOL_NAMESPACE}${module}_${function} ${MACRO_NAMESPACE}MODULE${form}(${m_lower},${f_lower}, ${m_upper},${f_upper})\n")
|
|
else()
|
|
message(AUTHOR_WARNING "No FortranCInterface mangling known for ${f}")
|
|
endif()
|
|
else()
|
|
# Global symbol name.
|
|
if("${f}" MATCHES "_")
|
|
set(form "_")
|
|
else()
|
|
set(form "")
|
|
endif()
|
|
string(TOUPPER "${f}" f_upper)
|
|
string(TOLOWER "${f}" f_lower)
|
|
if(FortranCInterface_GLOBAL${form}_MACRO)
|
|
string(APPEND HEADER_CONTENT "#define ${SYMBOL_NAMESPACE}${f} ${MACRO_NAMESPACE}GLOBAL${form}(${f_lower}, ${f_upper})\n")
|
|
else()
|
|
message(AUTHOR_WARNING "No FortranCInterface mangling known for ${f}")
|
|
endif()
|
|
endif()
|
|
endforeach()
|
|
|
|
# Store the content.
|
|
configure_file(${FortranCInterface_SOURCE_DIR}/Macro.h.in ${FILE} @ONLY)
|
|
endfunction()
|
|
|
|
function(FortranCInterface_VERIFY)
|
|
# Check arguments.
|
|
|
|
set(lang C)
|
|
set(quiet 0)
|
|
set(verify_cxx 0)
|
|
foreach(arg ${ARGN})
|
|
if("${arg}" STREQUAL "QUIET")
|
|
set(quiet 1)
|
|
elseif("${arg}" STREQUAL "CXX")
|
|
set(lang CXX)
|
|
set(verify_cxx 1)
|
|
else()
|
|
message(FATAL_ERROR
|
|
"FortranCInterface_VERIFY - called with unknown argument:\n ${arg}")
|
|
endif()
|
|
endforeach()
|
|
|
|
if(NOT CMAKE_${lang}_COMPILER_LOADED)
|
|
message(FATAL_ERROR
|
|
"FortranCInterface_VERIFY(${lang}) requires ${lang} to be enabled.")
|
|
endif()
|
|
|
|
# Build the verification project if not yet built.
|
|
if(NOT DEFINED FortranCInterface_VERIFIED_${lang})
|
|
set(_desc "Verifying Fortran/${lang} Compiler Compatibility")
|
|
message(CHECK_START "${_desc}")
|
|
|
|
# Perform verification with only one architecture.
|
|
# FIXME: Add try_compile whole-project option to forward architectures.
|
|
if(CMAKE_OSX_ARCHITECTURES MATCHES "^([^;]+)(;|$)")
|
|
set(_FortranCInterface_OSX_ARCH "-DCMAKE_OSX_ARCHITECTURES=${CMAKE_MATCH_1}")
|
|
else()
|
|
set(_FortranCInterface_OSX_ARCH "")
|
|
endif()
|
|
|
|
cmake_policy(GET CMP0056 _FortranCInterface_CMP0056)
|
|
if(_FortranCInterface_CMP0056 STREQUAL "NEW")
|
|
set(_FortranCInterface_EXE_LINKER_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS}")
|
|
else()
|
|
set(_FortranCInterface_EXE_LINKER_FLAGS "")
|
|
endif()
|
|
|
|
# Build a sample project which reports symbols.
|
|
set(CMAKE_TRY_COMPILE_CONFIGURATION Release)
|
|
try_compile(FortranCInterface_VERIFY_${lang}_COMPILED
|
|
PROJECT VerifyFortranC
|
|
TARGET VerifyFortranC
|
|
SOURCE_DIR ${FortranCInterface_SOURCE_DIR}/Verify
|
|
BINARY_DIR ${FortranCInterface_BINARY_DIR}/Verify${lang}
|
|
CMAKE_FLAGS -DVERIFY_CXX=${verify_cxx}
|
|
-DCMAKE_VERBOSE_MAKEFILE=ON
|
|
"-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS}"
|
|
"-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}"
|
|
"-DCMAKE_Fortran_FLAGS:STRING=${CMAKE_Fortran_FLAGS}"
|
|
"-DCMAKE_C_FLAGS_RELEASE:STRING=${CMAKE_C_FLAGS_RELEASE}"
|
|
"-DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELEASE}"
|
|
"-DCMAKE_Fortran_FLAGS_RELEASE:STRING=${CMAKE_Fortran_FLAGS_RELEASE}"
|
|
"-DFortranCInterface_BINARY_DIR=${FortranCInterface_BINARY_DIR}"
|
|
"-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}"
|
|
${_FortranCInterface_OSX_ARCH}
|
|
${_FortranCInterface_EXE_LINKER_FLAGS}
|
|
OUTPUT_VARIABLE _output)
|
|
file(WRITE "${FortranCInterface_BINARY_DIR}/Verify${lang}/output.txt" "${_output}")
|
|
|
|
# Report results.
|
|
if(FortranCInterface_VERIFY_${lang}_COMPILED)
|
|
message(CHECK_PASS "Success")
|
|
set(FortranCInterface_VERIFIED_${lang} 1 CACHE INTERNAL "Fortran/${lang} compatibility")
|
|
else()
|
|
message(CHECK_FAIL "Failed")
|
|
set(FortranCInterface_VERIFIED_${lang} 0 CACHE INTERNAL "Fortran/${lang} compatibility")
|
|
endif()
|
|
unset(FortranCInterface_VERIFY_${lang}_COMPILED CACHE)
|
|
endif()
|
|
|
|
# Error if compilers are incompatible.
|
|
if(NOT FortranCInterface_VERIFIED_${lang} AND NOT quiet)
|
|
file(READ "${FortranCInterface_BINARY_DIR}/Verify${lang}/output.txt" _output)
|
|
string(REPLACE "\n" "\n " _output "${_output}")
|
|
message(FATAL_ERROR
|
|
"The Fortran compiler:\n ${CMAKE_Fortran_COMPILER}\n"
|
|
"and the ${lang} compiler:\n ${CMAKE_${lang}_COMPILER}\n"
|
|
"failed to compile a simple test project using both languages. "
|
|
"The output was:\n ${_output}")
|
|
endif()
|
|
endfunction()
|
|
|
|
# Restore including context policies.
|
|
cmake_policy(POP)
|