| # 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}" |
| ${_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") |
| 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(CHECK_FAIL "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) |