diff options
Diffstat (limited to 'Modules/FortranCInterface.cmake')
-rw-r--r-- | Modules/FortranCInterface.cmake | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/Modules/FortranCInterface.cmake b/Modules/FortranCInterface.cmake new file mode 100644 index 0000000..70c3fd7 --- /dev/null +++ b/Modules/FortranCInterface.cmake @@ -0,0 +1,340 @@ +#.rst: +# FortranCInterface +# ----------------- +# +# Fortran/C Interface Detection +# +# This module automatically detects the API by which C and Fortran +# languages interact. Variables indicate if the mangling is found: +# +# :: +# +# FortranCInterface_GLOBAL_FOUND = Global subroutines and functions +# FortranCInterface_MODULE_FOUND = Module subroutines and functions +# (declared by "MODULE PROCEDURE") +# +# A 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 +# MACRO_NAMESPACE option replaces the default "FortranCInterface_" +# prefix with a given namespace "<macro-ns>". +# +# The SYMBOLS option lists 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. The +# SYMBOL_NAMESPACE option prefixes all preprocessor definitions +# generated by the SYMBOLS option with a given namespace "<ns>". +# +# Example usage: +# +# :: +# +# 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_(). +# +# Example usage: +# +# :: +# +# 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. +# +# Another 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. +# +# 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: +# +# :: +# +# 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.) + +#============================================================================= +# Copyright 2008-2009 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +#----------------------------------------------------------------------------- +# 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) + set(HEADER_CONTENT "${HEADER_CONTENT} +${_desc_${macro}} +#define ${MACRO_NAMESPACE}${macro}${FortranCInterface_${macro}_MACRO} +") + endif() + endforeach() + + # Generate symbol mangling definitions. + if(SYMBOLS) + set(HEADER_CONTENT "${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) + set(HEADER_CONTENT "${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) + set(HEADER_CONTENT "${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 + 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) |