summaryrefslogtreecommitdiff
path: root/Modules/FortranCInterface.cmake
blob: 3f86c50d54f9c6f13dca0f883b4cfe15cb4f8ff2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# 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 tages 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)

# 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
    ")
  
  foreach(interface 
      "test_interface$" 
      "TEST_INTERFACE_mp_" 
      "__test_interface_NMOD_" 
      "__test_interface_MOD_")
    test_fortran_mangling("${CODE}" "${interface}"
      ${FORTRAN_C_MANGLING_UPPERCASE} "" "module" "sub" worked)
    if(worked)
      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)
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(create_fortran_c_interface NAMESPACE FUNCTIONS HEADER)
  if(NOT FORTRAN_C_MANGLING_FOUND)
    # find regular fortran function mangling
    discover_fortran_mangling(prefix isupper suffix extra_under found)
    if(NOT found)
      message(SEND_ERROR "Could not find fortran c name mangling.")
      return()
    endif(NOT found)
    # 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")
      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 TRUE CACHE INTERNAL
        "SUFFIX for Fortran to c name mangling")
    else(found)
      set(FORTRAN_C_MODULE_MANGLING_FOUND FALSE CACHE INTERNAL
        "SUFFIX for Fortran to c name mangling")
    endif(found)
  endif(NOT FORTRAN_C_MANGLING_FOUND)
  foreach(f ${${FUNCTIONS}})
    if(FORTRAN_C_MANGLING_UPPERCASE)
      string(TOUPPER "${f}" f)
    else()
      string(TOLOWER "${f}" f)
    endif()
    if("${f}" MATCHES ":")
      string(REGEX REPLACE "(.*):(.*)" "\\1" module "${f}")
      string(REGEX REPLACE "(.*):(.*)" "\\2" function "${f}")
      set(HEADER_CONTENT "${HEADER_CONTENT}
#define ${NAMESPACE}${module}_${function} ${FORTRAN_C_MODULE_PREFIX}${module}${FORTRAN_C_MODULE_SUFFIX}${function}
")
    else("${f}" MATCHES ":")
      set(function "${FORTRAN_C_PREFIX}${f}${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)
  configure_file(
    "${CMAKE_ROOT}/Modules/FortranCInterface.h.in"
    ${HEADER} @ONLY)
  message(STATUS "created ${HEADER}")
endfunction()