I have been following this discussion silently, as the problem of interfacing C/C++ with blas and lapack (and without obliging users to install a fortran compiler) is recurrent in a few of the projects I'm involved in.

I'm attaching (for comments) a modified FortranCInterface.cmake that basically has two modes. If a fortran and a C compiler are available, it works as it does currently except for teo things: - Error messages when C or Fortran compiler is missing should be much more understandable.... - If the source list is "", then create_fortran_c_interface creates generic macros if possible... Eg: create_fortran_c_interface("" "" ${CMAKE_CURRENT_BINARY_DIR}/src/fortran.h)

But I have also added a new function: discover_fortran_c_interface
Without parameters, this function attempts to find the C/Fortran mangling from compilers (create_fortran_c_interface actually now uses that function). But more interestingly, this function can be called with parameters:

discover_fortran_c_interface(LIBRARY FUNCTIONS) will attempts to discover the mangling from an installed library LIBRARY which should contain the functions FUNCTIONS (right now I'm testing only up to two of those functions (one containing an _ an another without _). Then, create_fortran_c_interface can be used as usual to
create mangled manes (or generic interface).

Example of use:
SET(FUNCTIONS dgesvd daxpy dgess dcopy) # Blas and lapack functions
discover_fortran_c_interface(blas dcopy)
create_fortran_c_interface("" FUNCTIONS ${CMAKE_CURRENT_BINARY_DIR}/src/fortran.h)

This work is not totally finished: lack of support for modules, lack of proper error messages when mangling functions with _ when mangling has been detected only for functions without _ (and vice versa),
maybe a new way of calling create_fortran_c_interface:
create_fortran_c_interface(PREFIX FUNCTIONS FILE LIBRARY lib functions) or even
create_fortran_c_interface(PREFIX FUNCTIONS FILE lib functions)
where LIBRARY is meant to be an option keyword...

Still it is in a usable state, so I'd like to gather comments:
1) whether the idea is considered useful.
2) could it be integrated with the standard FortranCInterface module.
3) any suggestions for improvements in both the interface and the implementation.
4) ...

   Thank's a lot

      Theo.


# FortranCInterface.cmake
#
# This file defines the function create_fortran_c_interface.
# this function is used to create a configured header file 
# that contains a mapping from C to a Fortran function using
# the correct name mangling scheme as defined by the current 
# fortran compiler.  
#
# The function tags a list of functions and the name of 
# a header file to configure.  
#
# This file also defines some helper functions that are used
# to detect the fortran name mangling scheme used by the 
# current Fortran compiler.
#  test_fortran_mangling - test a single fortran mangling 
#  discover_fortran_mangling - loop over all combos of fortran
#   name mangling and call test_fortran_mangling until one of them
#   works.
#  discover_fortran_module_mangling - try different types of 
#  fortran modle name mangling to find one that works
#
#
#
# this function tests a single fortran mangling.  
# CODE - test code to try should define a subroutine called "sub"
# PREFIX - string to put in front of sub
# POSTFIX - string to put after sub
# ISUPPER - if TRUE then sub will be called as SUB
# DOC - string used in status checking Fortran ${DOC} linkage
# SUB - the name of the SUB to call
# RESULT place to store result TRUE if this linkage works, FALSE
#        if not.
#
function(test_fortran_mangling CODE PREFIX ISUPPER POSTFIX DOC SUB RESULT)
  if(ISUPPER)
    string(TOUPPER "${SUB}" sub)
  else(ISUPPER) 
    string(TOLOWER "${SUB}" sub)
  endif(ISUPPER)
  set(FUNCTION "${PREFIX}${sub}${POSTFIX}")
  # create a fortran file with sub called sub
  # 
  set(TMP_DIR
    "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckFortranLink")
  file(REMOVE_RECURSE "${TMP_DIR}")
  file(WRITE "${TMP_DIR}/test.f" "${CODE}"    )
  message(STATUS "checking Fortran ${DOC} linkage: ${FUNCTION}")
  file(WRITE "${TMP_DIR}/ctof.c"
    "
      extern ${FUNCTION}();
      int main() { ${FUNCTION}(); return 0;}
    "
    )
  file(WRITE "${TMP_DIR}/CMakeLists.txt"
    "
     project(testf C Fortran)
     add_library(flib test.f)
     add_executable(ctof ctof.c)
     target_link_libraries(ctof flib)
    "
    )
  set(FORTRAN_NAME_MANGLE_TEST FALSE)
  try_compile(FORTRAN_NAME_MANGLE_TEST "${TMP_DIR}" "${TMP_DIR}"
    testf
    OUTPUT_VARIABLE output)
  if(FORTRAN_NAME_MANGLE_TEST)
    set(${RESULT} TRUE PARENT_SCOPE)
  else()
    set(${RESULT} FALSE PARENT_SCOPE)
  endif()
endfunction(test_fortran_mangling)

function(test_fortran_mangling_from_library LIBNAME PREFIX ISUPPER POSTFIX DOC 
SUB RESULT)
  if(ISUPPER)
    string(TOUPPER "${SUB}" sub)
  else(ISUPPER) 
    string(TOLOWER "${SUB}" sub)
  endif(ISUPPER)
  set(FUNCTION "${PREFIX}${sub}${POSTFIX}")
  message(STATUS "checking Fortran ${DOC} linkage: ${FUNCTION}")
  INCLUDE (CheckLibraryExists)
  CHECK_LIBRARY_EXISTS(${LIBNAME} ${FUNCTION} "/usr/lib64" 
FORTRAN_NAME_MANGLE_TEST)
  set(${RESULT} ${FORTRAN_NAME_MANGLE_TEST} PARENT_SCOPE)
endfunction()

# this function discovers the name mangling scheme used
# for functions in a fortran module.  
function(discover_fortran_module_mangling prefix suffix found)
  set(CODE 
    "
      module test_interface
      interface dummy
         module procedure sub
      end interface
      contains
        subroutine sub
        end subroutine
      end module test_interface
    ")
  set(worked FALSE)
  foreach(interface 
      "test_interface$" 
      "TEST_INTERFACE_mp_" 
      "_test_interface__" 
      "__test_interface__" 
      "__test_interface_NMOD_" 
      "__test_interface_MOD_")
    test_fortran_mangling("${CODE}" "${interface}"
      ${FORTRAN_C_MANGLING_UPPERCASE} "" "module" "sub" worked)
    if(worked)
      # if this is the upper case module match then
      # lower case it for the extraction of pre and post strings
      if("${interface}" MATCHES "TEST_INTERFACE")
        string(TOLOWER "${interface}" interface)
      endif()
      string(REGEX REPLACE "(.*)test_interface(.*)" "\\1" pre "${interface}")
      string(REGEX REPLACE "(.*)test_interface(.*)" "\\2" post "${interface}")
      set(${prefix} "${pre}" PARENT_SCOPE)
      set(${suffix} "${post}" PARENT_SCOPE)
      set(${found} TRUE PARENT_SCOPE)
      return()
    endif(worked)
  endforeach(interface)
  if(NOT worked)
    message(STATUS "Failed to find C binding to Fortran module functions.")
    set(${prefix} "BROKEN_C_FORTRAN_MODULE_BINDING" PARENT_SCOPE)
    set(${suffix} "BROKEN_C_FORTRAN_MODULE_BINDING" PARENT_SCOPE)
    set(${found} FALSE PARENT_SCOPE)
  endif(NOT worked)
endfunction(discover_fortran_module_mangling)


function(discover_fortran_mangling prefix isupper suffix extra_under_score
    found )
  set(CODE 
    "
      subroutine sub
      end subroutine sub
    ")
  foreach(post "_" "")
    foreach(isup FALSE TRUE)
      foreach(pre "" "_" "__")
        set(worked FALSE)
        test_fortran_mangling("${CODE}" "${pre}" ${isup}
          "${post}" "function" sub worked )
        if(worked)
          message(STATUS "found Fortran function linkage")
          set(${isupper} "${isup}" PARENT_SCOPE)
          set(${prefix} "${pre}" PARENT_SCOPE)
          set(${suffix} "${post}" PARENT_SCOPE)
          set(${found} TRUE PARENT_SCOPE)
          set(CODE 
            "
      subroutine my_sub
      end subroutine my_sub
    ")
          set(worked FALSE)
          test_fortran_mangling("${CODE}" "${pre}" ${isup}
            "${post}" "function with _ " my_sub worked )
          if(worked)
            set(${extra_under_score} FALSE PARENT_SCOPE)
          else(worked)
            test_fortran_mangling("${CODE}" "${pre}" ${isup}
              "${post}_" "function with _ " my_sub worked )
            if(worked)
              set(${extra_under_score} TRUE PARENT_SCOPE)
            endif(worked)
          endif(worked)
        return()
        endif()
      endforeach()
    endforeach()
  endforeach()
  set(${found} FALSE PARENT_SCOPE)
endfunction(discover_fortran_mangling)

function(discover_fortran_mangling_from_library libname function prefix isupper 
suffix found )
  Find_Library(library ${libname})
  foreach(post "_" "")
    foreach(isup FALSE TRUE)
      foreach(pre "" "_" "__")
        set(worked FALSE)
        message("${libname}" "${library}" "${pre}" "${isup}" "${post}" 
"function" "${function}" worked )
        test_fortran_mangling_from_library("${libname}" "${pre}" "${isup}" 
"${post}" "function" "${function}" worked )
        if(worked)
          message(STATUS "found Fortran function linkage")
          set(${isupper} "${isup}" PARENT_SCOPE)
          set(${prefix} "${pre}" PARENT_SCOPE)
          set(${suffix} "${post}" PARENT_SCOPE)
          set(${found} TRUE PARENT_SCOPE)
          return()
        endif()
      endforeach()
    endforeach()
  endforeach()
  set(${found} FALSE PARENT_SCOPE)
endfunction()

function(is_language_available lang result)
  get_property(_LANGUAGES_ GLOBAL PROPERTY ENABLED_LANGUAGES)
  if(_LANGUAGES_ MATCHES "${lang}")
    SET(${result} TRUE PARENT_SCOPE)
    return()
  endif()
  SET(${result} FALSE PARENT_SCOPE)
endfunction()

# Parse arguments to create_fortran_c_interface and sets the variables
# with the corresponding parameters. variable names are those of named args
# or options prefixed with the given prefix. Arg names and options are
# given by the two lists arg_names and option_names.

function(PARSE_PARAMETERS prefix arg_names option_names)
  SET(DEFAULT_ARGS)
  FOREACH(name ${arg_names} ${option_names})
    SET(${prefix}_${name})
  ENDFOREACH()

  SET(current_arg_name DEFAULT_ARGS)
  SET(current_arg_list)
  FOREACH(arg ${ARGN})
    LIST(FIND ${arg_names} ${arg} is_arg_name)
    IF (is_arg_name NOT EQUAL -1)
      SET(${prefix}_${current_arg_name} ${current_arg_list})
      SET(current_arg_name ${arg})
      SET(current_arg_list)
    ELSE ()
      LIST(FIND ${option_names} ${arg} is_option)
      IF (is_option)
        SET(${prefix}_${arg} TRUE)
      ELSE ()
        SET(current_arg_list ${current_arg_list} ${arg})
      ENDIF ()
    ENDIF ()
  ENDFOREACH()
  SET(${prefix}_${current_arg_name} ${current_arg_list})
endfunction()

function(discover_fortran_c_interface)
  # find regular fortran function mangling
  SET(FORTRAN_MANGLING_NO_UNDERSCORE_ONLY FALSE)
  SET(FORTRAN_MANGLING_UNDERSCORE_ONLY FALSE)
  if (${ARGC} EQUAL 0)
    is_language_available(Fortran FORTRAN_AVAILABLE)
    if (NOT FORTRAN_AVAILABLE)
      message(SEND_ERROR "Cannot find fortran mangling without a fortran 
compiler or a fortran library.")
      return()
    endif()
    is_language_available(C C_AVAILABLE)
    if (NOT C_AVAILABLE)
      message(SEND_ERROR "Finding fortran mangling requires a C compiler.")
      return()
    endif()
    discover_fortran_mangling(prefix isupper suffix extra_under found)
    if(NOT found)
      message(SEND_ERROR "Could not find fortran c name mangling (probably no 
fortran compiler available).")
      return()
    endif()
    SET(FORTRAN_MANGLING_NO_UNDERSCORE_ONLY TRUE)
    SET(FORTRAN_MANGLING_UNDERSCORE_ONLY TRUE)
  else()
    LIST(GET ARGN 0 flibrary)
    LIST(REMOVE_AT ARGN 0)
    SET(name "")
    SET(name_ "")
    foreach (f ${ARGN})
      IF ("${f}" MATCHES "_")
        SET(name_ ${f})
      ELSE()
        IF ("${f}" MATCHES ":")
          MESSAGE(WARNING "Fortran C interface from library does not work yet 
for modules.")
        ELSE()
          SET(name ${f})
        ENDIF()
      ENDIF()
    endforeach()
    SET(extra_under FALSE)
    IF (name)
      discover_fortran_mangling_from_library(${flibrary} ${name} prefix isupper 
suffix found)
      IF (found AND name_)
        test_fortran_mangling_from_library(${flibrary} ${prefix} ${isupper} 
${suffix} "function" ${name_} worked)
        IF (NOT worked)
          test_fortran_mangling_from_library(${flibrary} ${prefix} ${isupper} 
${suffix} "function" ${name_}_ worked)
          IF (worked)
            SET(extra_under TRUE)
          ELSE()
            SET(FORTRAN_MANGLING_NO_UNDERSCORE_ONLY TRUE)
          ENDIF()
        ENDIF()
      ENDIF()
    ELSE()
      IF (name_)
        discover_fortran_mangling_from_library(${flibrary} ${name_} prefix 
isupper suffix extra_under found)
        IF (found)
          SET(FORTRAN_MANGLING_UNDERSCORE_ONLY TRUE)
        ENDIF()
      ELSE()
        MESSAGE(SEND_ERROR "No function name provided to ${ARGV[0]}")
        return()
      ENDIF()
    ENDIF()
    IF (NOT found)
      message(SEND_ERROR "Could not find fortran c name mangling (probably 
function is not in library).")
      return()
    ENDIF()
  endif()

  # find fortran module function mangling
  set(FORTRAN_C_PREFIX "${prefix}" CACHE INTERNAL
    "PREFIX for Fortran to c name mangling")
  set(FORTRAN_C_SUFFIX "${suffix}" CACHE INTERNAL
    "SUFFIX for Fortran to c name mangling")
  set(FORTRAN_C_MANGLING_UPPERCASE ${isupper} CACHE INTERNAL 
    "Was fortran to c mangling found" )
  set(FORTRAN_C_MANGLING_EXTRA_UNDERSCORE ${extra_under} CACHE INTERNAL 
    "If a function has a _ in the name does the compiler append an extra _" )
  set(FORTRAN_C_MANGLING_FOUND TRUE CACHE INTERNAL 
    "Was fortran to c mangling found" )
  set(prefix )
  set(suffix )
  set(found FALSE)
  # only try this if the compiler is F90 compatible
  if(CMAKE_Fortran_COMPILER_SUPPORTS_F90)
    discover_fortran_module_mangling(prefix suffix found)
  endif(CMAKE_Fortran_COMPILER_SUPPORTS_F90)
  if(found)
    message(STATUS "found Fortran module linkage")
  else(found)
    message(STATUS "Failed to find Fortran module linkage")
  endif(found)
  set(FORTRAN_C_MODULE_PREFIX "${prefix}" CACHE INTERNAL
    "PREFIX for Fortran to c name mangling")
  set(FORTRAN_C_MODULE_SUFFIX "${suffix}" CACHE INTERNAL
    "SUFFIX for Fortran to c name mangling")
  set(FORTRAN_C_MODULE_MANGLING_FOUND ${found} CACHE INTERNAL
    "Was for Fortran to c name mangling found for modules")
endfunction()

function(create_fortran_c_interface NAMESPACE FUNCTIONS HEADER)
  PARSE_PARAMETERS("FORTRAN_C_" "USE_LIBRARY" "APPEND" ${ARGN})
  if(NOT FORTRAN_C_MANGLING_FOUND)
    discover_fortran_c_interface()
  endif()
  if (NOT FUNCTIONS)
    set(fcase "x")
    if(FORTRAN_C_MANGLING_UPPERCASE)
      set(fcase "X")
    endif()
    if ( NOT ${FORTRAN_C_PREFIX} EQUAL "")
      set(fcase " ## ${fcase}")
    endif()
    if (NOT ${FORTRAN_C_SUFFIX} EQUAL "")
      set(fcase "${fcase} ## ")
    endif()
    set(HEADER_CONTENT "${HEADER_CONTENT}
#define Fortran_Function(x,X) ${FORTRAN_C_PREFIX}${fcase}${FORTRAN_C_SUFFIX}
")
    IF (FORTRAN_C_MANGLING_EXTRA_UNDERSCORE)
      set(HEADER_CONTENT "${HEADER_CONTENT}
#define Fortran_Function_With_Underscore(x,X) Fortran_Function(x,X) ## _
")
    ENDIF()
  else()
    foreach (f ${${FUNCTIONS}})
      if(FORTRAN_C_MANGLING_UPPERCASE)
        string(TOUPPER "${f}" fcase)
      else()
        string(TOLOWER "${f}" fcase)
      endif()
      if("${f}" MATCHES ":")
        string(REGEX REPLACE "(.*):(.*)" "\\1" module "${f}")
        string(REGEX REPLACE "(.*):(.*)" "\\2" function "${f}")
        string(REGEX REPLACE "(.*):(.*)" "\\1" module_case "${fcase}")
        string(REGEX REPLACE "(.*):(.*)" "\\2" function_case "${fcase}")
        set(HEADER_CONTENT "${HEADER_CONTENT}
#define ${NAMESPACE}${module}_${function} 
${FORTRAN_C_MODULE_PREFIX}${module_case}${FORTRAN_C_MODULE_SUFFIX}${function_case}
")
      else("${f}" MATCHES ":")
        set(function "${FORTRAN_C_PREFIX}${fcase}${FORTRAN_C_SUFFIX}")
        if("${f}" MATCHES "_" AND FORTRAN_C_MANGLING_EXTRA_UNDERSCORE)
          set(function "${function}_")
        endif("${f}" MATCHES "_" AND FORTRAN_C_MANGLING_EXTRA_UNDERSCORE)
        set(HEADER_CONTENT "${HEADER_CONTENT}
#define ${NAMESPACE}${f} ${function}
")
      endif("${f}" MATCHES ":")
    endforeach(f)
  endif()
  configure_file(
    "${CMAKE_ROOT}/Modules/FortranCInterface.h.in"
    ${HEADER} @ONLY)
  message(STATUS "created ${HEADER}")
endfunction()
_______________________________________________
Powered by www.kitware.com

Visit other Kitware open-source projects at 
http://www.kitware.com/opensource/opensource.html

Please keep messages on-topic and check the CMake FAQ at: 
http://www.cmake.org/Wiki/CMake_FAQ

Follow this link to subscribe/unsubscribe:
http://www.cmake.org/mailman/listinfo/cmake

Reply via email to