# 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( [MACRO_NAMESPACE ] [SYMBOL_NAMESPACE ] [SYMBOLS [:] ...]) It generates in ```` 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 ````. ``SYMBOLS`` List symbols to mangle automatically with C preprocessor definitions:: ==> #define ... : ==> #define _ ... 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 ````. .. 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 ":" 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(STATUS "${_desc}") # Build a sample project which reports symbols. set(CMAKE_TRY_COMPILE_CONFIGURATION Release) try_compile(FortranCInterface_VERIFY_${lang}_COMPILED ${FortranCInterface_BINARY_DIR}/Verify${lang} ${FortranCInterface_SOURCE_DIR}/Verify VerifyFortranC # project name VerifyFortranC # target name 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}" OUTPUT_VARIABLE _output) file(WRITE "${FortranCInterface_BINARY_DIR}/Verify${lang}/output.txt" "${_output}") # Report results. if(FortranCInterface_VERIFY_${lang}_COMPILED) message(STATUS "${_desc} - Success") file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log "${_desc} passed with the following output:\n${_output}\n\n") set(FortranCInterface_VERIFIED_${lang} 1 CACHE INTERNAL "Fortran/${lang} compatibility") else() message(STATUS "${_desc} - Failed") file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "${_desc} failed with the following output:\n${_output}\n\n") 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)