# - 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( # [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 MACRO_NAMESPACE option replaces the default "FortranCInterface_" # prefix with a given namespace "". # # The SYMBOLS option lists 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. # The SYMBOL_NAMESPACE option prefixes all preprocessor definitions # generated by the SYMBOLS option with a given namespace "". # # 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 "FC.h" header that defines the same FC_*() mangling # macros as the previous example plus preprocessor symbols FC_mysub # and FC_mymod_my_sub. # # 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.) #----------------------------------------------------------------------------- # Execute at most once in a project. if(FortranCInterface_SOURCE_DIR) return() endif() #----------------------------------------------------------------------------- # Set up an interface detection project. set(FortranCInterface_SOURCE_DIR ${CMAKE_ROOT}/Modules/FortranCInterface) set(FortranCInterface_BINARY_DIR ${CMAKE_BINARY_DIR}/CMakeFiles/FortranCInterface) configure_file(${FortranCInterface_SOURCE_DIR}/Input.cmake.in ${FortranCInterface_BINARY_DIR}/Input.cmake @ONLY) # Detect the Fortran/C interface on the first run or when the # configuration changes. if(${FortranCInterface_BINARY_DIR}/Input.cmake IS_NEWER_THAN ${FortranCInterface_BINARY_DIR}/Output.cmake OR ${FortranCInterface_SOURCE_DIR}/Output.cmake.in IS_NEWER_THAN ${FortranCInterface_BINARY_DIR}/Output.cmake OR ${FortranCInterface_SOURCE_DIR}/CMakeLists.txt IS_NEWER_THAN ${FortranCInterface_BINARY_DIR}/Output.cmake OR ${CMAKE_CURRENT_LIST_FILE} IS_NEWER_THAN ${FortranCInterface_BINARY_DIR}/Output.cmake ) message(STATUS "Detecting Fortran/C Interface") set(_result) # Build a sample project which reports symbols. try_compile(FortranCInterface_COMPILED ${FortranCInterface_BINARY_DIR} ${FortranCInterface_SOURCE_DIR} FortranCInterface OUTPUT_VARIABLE FortranCInterface_OUTPUT) set(FortranCInterface_COMPILED ${FortranCInterface_COMPILED}) unset(FortranCInterface_COMPILED CACHE) # Locate the sample project executable. if(FortranCInterface_COMPILED) find_program(FortranCInterface_EXE NAMES FortranCInterface PATHS ${FortranCInterface_BINARY_DIR} ${FortranCInterface_BINARY_DIR}/Debug NO_DEFAULT_PATH ) set(FortranCInterface_EXE ${FortranCInterface_EXE}) unset(FortranCInterface_EXE CACHE) else() set(_result "Failed to compile") set(FortranCInterface_EXE) file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "Fortran/C interface test project failed with the following output:\n" "${FortranCInterface_OUTPUT}\n") endif() # Load symbols from INFO:symbol[] strings in the executable. set(FortranCInterface_SYMBOLS) if(FortranCInterface_EXE) file(STRINGS "${FortranCInterface_EXE}" _info_strings LIMIT_COUNT 8 REGEX "INFO:[^[]*\\[") foreach(info ${_info_strings}) if("${info}" MATCHES ".*INFO:symbol\\[([^]]*)\\].*") string(REGEX REPLACE ".*INFO:symbol\\[([^]]*)\\].*" "\\1" symbol "${info}") list(APPEND FortranCInterface_SYMBOLS ${symbol}) endif() endforeach() elseif(NOT _result) set(_result "Failed to load sample executable") endif() set(_case_mysub "LOWER") set(_case_my_sub "LOWER") set(_case_MYSUB "UPPER") set(_case_MY_SUB "UPPER") set(_global_regex "^(_*)(mysub|MYSUB)([_$]*)$") set(_global__regex "^(_*)(my_sub|MY_SUB)([_$]*)$") set(_module_regex "^(_*)(mymodule|MYMODULE)([A-Za-z_$]*)(mysub|MYSUB)([_$]*)$") set(_module__regex "^(_*)(my_module|MY_MODULE)([A-Za-z_$]*)(my_sub|MY_SUB)([_$]*)$") # Parse the symbol names. foreach(symbol ${FortranCInterface_SYMBOLS}) foreach(form "" "_") # Look for global symbols. string(REGEX REPLACE "${_global_${form}regex}" "\\1;\\2;\\3" pieces "${symbol}") list(LENGTH pieces len) if(len EQUAL 3) set(FortranCInterface_GLOBAL_${form}SYMBOL "${symbol}") list(GET pieces 0 FortranCInterface_GLOBAL_${form}PREFIX) list(GET pieces 1 name) list(GET pieces 2 FortranCInterface_GLOBAL_${form}SUFFIX) set(FortranCInterface_GLOBAL_${form}CASE "${_case_${name}}") endif() # Look for module symbols. string(REGEX REPLACE "${_module_${form}regex}" "\\1;\\2;\\3;\\4;\\5" pieces "${symbol}") list(LENGTH pieces len) if(len EQUAL 5) set(FortranCInterface_MODULE_${form}SYMBOL "${symbol}") list(GET pieces 0 FortranCInterface_MODULE_${form}PREFIX) list(GET pieces 1 module) list(GET pieces 2 FortranCInterface_MODULE_${form}MIDDLE) list(GET pieces 3 name) list(GET pieces 4 FortranCInterface_MODULE_${form}SUFFIX) set(FortranCInterface_MODULE_${form}CASE "${_case_${name}}") endif() endforeach() endforeach() # Construct mangling macro definitions. set(_name_LOWER "name") set(_name_UPPER "NAME") foreach(form "" "_") if(FortranCInterface_GLOBAL_${form}SYMBOL) if(FortranCInterface_GLOBAL_${form}PREFIX) set(_prefix "${FortranCInterface_GLOBAL_${form}PREFIX}##") else() set(_prefix "") endif() if(FortranCInterface_GLOBAL_${form}SUFFIX) set(_suffix "##${FortranCInterface_GLOBAL_${form}SUFFIX}") else() set(_suffix "") endif() set(_name "${_name_${FortranCInterface_GLOBAL_${form}CASE}}") set(FortranCInterface_GLOBAL${form}_MACRO "(name,NAME) ${_prefix}${_name}${_suffix}") endif() if(FortranCInterface_MODULE_${form}SYMBOL) if(FortranCInterface_MODULE_${form}PREFIX) set(_prefix "${FortranCInterface_MODULE_${form}PREFIX}##") else() set(_prefix "") endif() if(FortranCInterface_MODULE_${form}SUFFIX) set(_suffix "##${FortranCInterface_MODULE_${form}SUFFIX}") else() set(_suffix "") endif() set(_name "${_name_${FortranCInterface_MODULE_${form}CASE}}") set(_middle "##${FortranCInterface_MODULE_${form}MIDDLE}##") set(FortranCInterface_MODULE${form}_MACRO "(mod_name,name, mod_NAME,NAME) ${_prefix}mod_${_name}${_middle}${_name}${_suffix}") endif() endforeach() # Summarize what is available. foreach(scope GLOBAL MODULE) if(FortranCInterface_${scope}_SYMBOL AND FortranCInterface_${scope}__SYMBOL) set(FortranCInterface_${scope}_FOUND 1) else() set(FortranCInterface_${scope}_FOUND 0) endif() endforeach() # Record the detection results. configure_file(${FortranCInterface_SOURCE_DIR}/Output.cmake.in ${FortranCInterface_BINARY_DIR}/Output.cmake @ONLY) file(APPEND ${FortranCInterface_BINARY_DIR}/Output.cmake "\n") # Report the results. if(FortranCInterface_GLOBAL_FOUND) if(FortranCInterface_MODULE_FOUND) set(_result "Found GLOBAL and MODULE mangling") else(FortranCInterface_MODULE_FOUND) set(_result "Found GLOBAL but not MODULE mangling") endif() elseif(NOT _result) set(_result "Failed to recognize symbols") endif() message(STATUS "Detecting Fortran/C Interface - ${_result}") 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 ":" 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(f) # Store the content. configure_file(${FortranCInterface_SOURCE_DIR}/Macro.h.in ${FILE} @ONLY) endfunction()