1# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2# file Copyright.txt or https://cmake.org/licensing for details.
3
4#[=======================================================================[.rst:
5FortranCInterface
6-----------------
7
8Fortran/C Interface Detection
9
10This module automatically detects the API by which C and Fortran
11languages interact.
12
13Module Variables
14^^^^^^^^^^^^^^^^
15
16Variables that indicate if the mangling is found:
17
18``FortranCInterface_GLOBAL_FOUND``
19  Global subroutines and functions.
20
21``FortranCInterface_MODULE_FOUND``
22  Module subroutines and functions (declared by "MODULE PROCEDURE").
23
24This module also provides the following variables to specify
25the detected mangling, though a typical use case does not need
26to reference them and can use the `Module Functions`_ below.
27
28``FortranCInterface_GLOBAL_PREFIX``
29  Prefix for a global symbol without an underscore.
30
31``FortranCInterface_GLOBAL_SUFFIX``
32  Suffix for a global symbol without an underscore.
33
34``FortranCInterface_GLOBAL_CASE``
35  The case for a global symbol without an underscore,
36  either ``UPPER`` or ``LOWER``.
37
38``FortranCInterface_GLOBAL__PREFIX``
39  Prefix for a global symbol with an underscore.
40
41``FortranCInterface_GLOBAL__SUFFIX``
42  Suffix for a global symbol with an underscore.
43
44``FortranCInterface_GLOBAL__CASE``
45  The case for a global symbol with an underscore,
46  either ``UPPER`` or ``LOWER``.
47
48``FortranCInterface_MODULE_PREFIX``
49  Prefix for a module symbol without an underscore.
50
51``FortranCInterface_MODULE_MIDDLE``
52  Middle of a module symbol without an underscore that appears
53  between the name of the module and the name of the symbol.
54
55``FortranCInterface_MODULE_SUFFIX``
56  Suffix for a module symbol without an underscore.
57
58``FortranCInterface_MODULE_CASE``
59  The case for a module symbol without an underscore,
60  either ``UPPER`` or ``LOWER``.
61
62``FortranCInterface_MODULE__PREFIX``
63  Prefix for a module symbol with an underscore.
64
65``FortranCInterface_MODULE__MIDDLE``
66  Middle of a module symbol with an underscore that appears
67  between the name of the module and the name of the symbol.
68
69``FortranCInterface_MODULE__SUFFIX``
70  Suffix for a module symbol with an underscore.
71
72``FortranCInterface_MODULE__CASE``
73  The case for a module symbol with an underscore,
74  either ``UPPER`` or ``LOWER``.
75
76Module Functions
77^^^^^^^^^^^^^^^^
78
79.. command:: FortranCInterface_HEADER
80
81  The ``FortranCInterface_HEADER`` function is provided to generate a
82  C header file containing macros to mangle symbol names::
83
84    FortranCInterface_HEADER(<file>
85                             [MACRO_NAMESPACE <macro-ns>]
86                             [SYMBOL_NAMESPACE <ns>]
87                             [SYMBOLS [<module>:]<function> ...])
88
89  It generates in ``<file>`` definitions of the following macros::
90
91     #define FortranCInterface_GLOBAL (name,NAME) ...
92     #define FortranCInterface_GLOBAL_(name,NAME) ...
93     #define FortranCInterface_MODULE (mod,name, MOD,NAME) ...
94     #define FortranCInterface_MODULE_(mod,name, MOD,NAME) ...
95
96  These macros mangle four categories of Fortran symbols, respectively:
97
98  * Global symbols without '_': ``call mysub()``
99  * Global symbols with '_'   : ``call my_sub()``
100  * Module symbols without '_': ``use mymod; call mysub()``
101  * Module symbols with '_'   : ``use mymod; call my_sub()``
102
103  If mangling for a category is not known, its macro is left undefined.
104  All macros require raw names in both lower case and upper case.
105
106  The options are:
107
108  ``MACRO_NAMESPACE``
109    Replace the default ``FortranCInterface_`` prefix with a given
110    namespace ``<macro-ns>``.
111
112  ``SYMBOLS``
113    List symbols to mangle automatically with C preprocessor definitions::
114
115      <function>          ==> #define <ns><function> ...
116      <module>:<function> ==> #define <ns><module>_<function> ...
117
118    If the mangling for some symbol is not known then no preprocessor
119    definition is created, and a warning is displayed.
120
121  ``SYMBOL_NAMESPACE``
122    Prefix all preprocessor definitions generated by the ``SYMBOLS``
123    option with a given namespace ``<ns>``.
124
125.. command:: FortranCInterface_VERIFY
126
127  The ``FortranCInterface_VERIFY`` function is provided to verify
128  that the Fortran and C/C++ compilers work together::
129
130    FortranCInterface_VERIFY([CXX] [QUIET])
131
132  It tests whether a simple test executable using Fortran and C (and C++
133  when the CXX option is given) compiles and links successfully.  The
134  result is stored in the cache entry ``FortranCInterface_VERIFIED_C``
135  (or ``FortranCInterface_VERIFIED_CXX`` if ``CXX`` is given) as a boolean.
136  If the check fails and ``QUIET`` is not given the function terminates with a
137  fatal error message describing the problem.  The purpose of this check
138  is to stop a build early for incompatible compiler combinations.  The
139  test is built in the ``Release`` configuration.
140
141Example Usage
142^^^^^^^^^^^^^
143
144.. code-block:: cmake
145
146   include(FortranCInterface)
147   FortranCInterface_HEADER(FC.h MACRO_NAMESPACE "FC_")
148
149This creates a "FC.h" header that defines mangling macros ``FC_GLOBAL()``,
150``FC_GLOBAL_()``, ``FC_MODULE()``, and ``FC_MODULE_()``.
151
152.. code-block:: cmake
153
154   include(FortranCInterface)
155   FortranCInterface_HEADER(FCMangle.h
156                            MACRO_NAMESPACE "FC_"
157                            SYMBOL_NAMESPACE "FC_"
158                            SYMBOLS mysub mymod:my_sub)
159
160This creates a "FCMangle.h" header that defines the same ``FC_*()``
161mangling macros as the previous example plus preprocessor symbols
162``FC_mysub`` and ``FC_mymod_my_sub``.
163
164Additional Manglings
165^^^^^^^^^^^^^^^^^^^^
166
167FortranCInterface is aware of possible ``GLOBAL`` and ``MODULE`` manglings
168for many Fortran compilers, but it also provides an interface to specify
169new possible manglings.  Set the variables::
170
171   FortranCInterface_GLOBAL_SYMBOLS
172   FortranCInterface_MODULE_SYMBOLS
173
174before including FortranCInterface to specify manglings of the symbols
175``MySub``, ``My_Sub``, ``MyModule:MySub``, and ``My_Module:My_Sub``.
176For example, the code:
177
178.. code-block:: cmake
179
180   set(FortranCInterface_GLOBAL_SYMBOLS mysub_ my_sub__ MYSUB_)
181     #                                  ^^^^^  ^^^^^^   ^^^^^
182   set(FortranCInterface_MODULE_SYMBOLS
183       __mymodule_MOD_mysub __my_module_MOD_my_sub)
184     #   ^^^^^^^^     ^^^^^   ^^^^^^^^^     ^^^^^^
185   include(FortranCInterface)
186
187tells FortranCInterface to try given ``GLOBAL`` and ``MODULE`` manglings.
188(The carets point at raw symbol names for clarity in this example but
189are not needed.)
190#]=======================================================================]
191
192#-----------------------------------------------------------------------------
193# Execute at most once in a project.
194if(FortranCInterface_SOURCE_DIR)
195  return()
196endif()
197
198cmake_policy(PUSH)
199cmake_policy(SET CMP0007 NEW)
200
201#-----------------------------------------------------------------------------
202# Verify that C and Fortran are available.
203foreach(lang C Fortran)
204  if(NOT CMAKE_${lang}_COMPILER_LOADED)
205    message(FATAL_ERROR
206      "FortranCInterface requires the ${lang} language to be enabled.")
207  endif()
208endforeach()
209
210#-----------------------------------------------------------------------------
211set(FortranCInterface_SOURCE_DIR ${CMAKE_ROOT}/Modules/FortranCInterface)
212
213# MinGW's make tool does not always like () in the path
214if("${CMAKE_GENERATOR}" MATCHES "MinGW" AND
215    "${FortranCInterface_SOURCE_DIR}" MATCHES "[()]")
216  file(COPY ${FortranCInterface_SOURCE_DIR}/
217    DESTINATION ${CMAKE_BINARY_DIR}/CMakeFiles/FortranCInterfaceMinGW)
218  set(FortranCInterface_SOURCE_DIR ${CMAKE_BINARY_DIR}/CMakeFiles/FortranCInterfaceMinGW)
219endif()
220
221# Create the interface detection project if it does not exist.
222if(NOT FortranCInterface_BINARY_DIR)
223  set(FortranCInterface_BINARY_DIR ${CMAKE_BINARY_DIR}/CMakeFiles/FortranCInterface)
224  include(${FortranCInterface_SOURCE_DIR}/Detect.cmake)
225endif()
226
227# Load the detection results.
228include(${FortranCInterface_BINARY_DIR}/Output.cmake)
229
230#-----------------------------------------------------------------------------
231function(FortranCInterface_HEADER file)
232  # Parse arguments.
233  if(IS_ABSOLUTE "${file}")
234    set(FILE "${file}")
235  else()
236    set(FILE "${CMAKE_CURRENT_BINARY_DIR}/${file}")
237  endif()
238  set(MACRO_NAMESPACE "FortranCInterface_")
239  set(SYMBOL_NAMESPACE)
240  set(SYMBOLS)
241  set(doing)
242  foreach(arg ${ARGN})
243    if("x${arg}" MATCHES "^x(SYMBOLS|SYMBOL_NAMESPACE|MACRO_NAMESPACE)$")
244      set(doing "${arg}")
245    elseif("x${doing}" MATCHES "^x(SYMBOLS)$")
246      list(APPEND "${doing}" "${arg}")
247    elseif("x${doing}" MATCHES "^x(SYMBOL_NAMESPACE|MACRO_NAMESPACE)$")
248      set("${doing}" "${arg}")
249      set(doing)
250    else()
251      message(AUTHOR_WARNING "Unknown argument: \"${arg}\"")
252    endif()
253  endforeach()
254
255  # Generate macro definitions.
256  set(HEADER_CONTENT)
257  set(_desc_GLOBAL  "/* Mangling for Fortran global symbols without underscores. */")
258  set(_desc_GLOBAL_ "/* Mangling for Fortran global symbols with underscores. */")
259  set(_desc_MODULE  "/* Mangling for Fortran module symbols without underscores. */")
260  set(_desc_MODULE_ "/* Mangling for Fortran module symbols with underscores. */")
261  foreach(macro GLOBAL GLOBAL_ MODULE MODULE_)
262    if(FortranCInterface_${macro}_MACRO)
263      string(APPEND HEADER_CONTENT "
264${_desc_${macro}}
265#define ${MACRO_NAMESPACE}${macro}${FortranCInterface_${macro}_MACRO}
266")
267    endif()
268  endforeach()
269
270  # Generate symbol mangling definitions.
271  if(SYMBOLS)
272    string(APPEND HEADER_CONTENT "
273/*--------------------------------------------------------------------------*/
274/* Mangle some symbols automatically.                                       */
275")
276  endif()
277  foreach(f ${SYMBOLS})
278    if("${f}" MATCHES ":")
279      # Module symbol name.  Parse "<module>:<function>" syntax.
280      string(REPLACE ":" ";" pieces "${f}")
281      list(GET pieces 0 module)
282      list(GET pieces 1 function)
283      string(TOUPPER "${module}" m_upper)
284      string(TOLOWER "${module}" m_lower)
285      string(TOUPPER "${function}" f_upper)
286      string(TOLOWER "${function}" f_lower)
287      if("${function}" MATCHES "_")
288        set(form "_")
289      else()
290        set(form "")
291      endif()
292      if(FortranCInterface_MODULE${form}_MACRO)
293        string(APPEND HEADER_CONTENT "#define ${SYMBOL_NAMESPACE}${module}_${function} ${MACRO_NAMESPACE}MODULE${form}(${m_lower},${f_lower}, ${m_upper},${f_upper})\n")
294      else()
295        message(AUTHOR_WARNING "No FortranCInterface mangling known for ${f}")
296      endif()
297    else()
298      # Global symbol name.
299      if("${f}" MATCHES "_")
300        set(form "_")
301      else()
302        set(form "")
303      endif()
304      string(TOUPPER "${f}" f_upper)
305      string(TOLOWER "${f}" f_lower)
306      if(FortranCInterface_GLOBAL${form}_MACRO)
307        string(APPEND HEADER_CONTENT "#define ${SYMBOL_NAMESPACE}${f} ${MACRO_NAMESPACE}GLOBAL${form}(${f_lower}, ${f_upper})\n")
308      else()
309        message(AUTHOR_WARNING "No FortranCInterface mangling known for ${f}")
310      endif()
311    endif()
312  endforeach()
313
314  # Store the content.
315  configure_file(${FortranCInterface_SOURCE_DIR}/Macro.h.in ${FILE} @ONLY)
316endfunction()
317
318function(FortranCInterface_VERIFY)
319  # Check arguments.
320
321  set(lang C)
322  set(quiet 0)
323  set(verify_cxx 0)
324  foreach(arg ${ARGN})
325    if("${arg}" STREQUAL "QUIET")
326      set(quiet 1)
327    elseif("${arg}" STREQUAL "CXX")
328      set(lang CXX)
329      set(verify_cxx 1)
330    else()
331      message(FATAL_ERROR
332        "FortranCInterface_VERIFY - called with unknown argument:\n  ${arg}")
333    endif()
334  endforeach()
335
336  if(NOT CMAKE_${lang}_COMPILER_LOADED)
337    message(FATAL_ERROR
338      "FortranCInterface_VERIFY(${lang}) requires ${lang} to be enabled.")
339  endif()
340
341  # Build the verification project if not yet built.
342  if(NOT DEFINED FortranCInterface_VERIFIED_${lang})
343    set(_desc "Verifying Fortran/${lang} Compiler Compatibility")
344    message(CHECK_START "${_desc}")
345
346    cmake_policy(GET CMP0056 _FortranCInterface_CMP0056)
347    if(_FortranCInterface_CMP0056 STREQUAL "NEW")
348      set(_FortranCInterface_EXE_LINKER_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS}")
349    else()
350      set(_FortranCInterface_EXE_LINKER_FLAGS "")
351    endif()
352
353    # Build a sample project which reports symbols.
354    set(CMAKE_TRY_COMPILE_CONFIGURATION Release)
355    try_compile(FortranCInterface_VERIFY_${lang}_COMPILED
356      ${FortranCInterface_BINARY_DIR}/Verify${lang}
357      ${FortranCInterface_SOURCE_DIR}/Verify
358      VerifyFortranC # project name
359      VerifyFortranC # target name
360      CMAKE_FLAGS -DVERIFY_CXX=${verify_cxx}
361                  -DCMAKE_VERBOSE_MAKEFILE=ON
362                 "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS}"
363                 "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}"
364                 "-DCMAKE_Fortran_FLAGS:STRING=${CMAKE_Fortran_FLAGS}"
365                 "-DCMAKE_C_FLAGS_RELEASE:STRING=${CMAKE_C_FLAGS_RELEASE}"
366                 "-DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELEASE}"
367                 "-DCMAKE_Fortran_FLAGS_RELEASE:STRING=${CMAKE_Fortran_FLAGS_RELEASE}"
368                 ${_FortranCInterface_EXE_LINKER_FLAGS}
369      OUTPUT_VARIABLE _output)
370    file(WRITE "${FortranCInterface_BINARY_DIR}/Verify${lang}/output.txt" "${_output}")
371
372    # Report results.
373    if(FortranCInterface_VERIFY_${lang}_COMPILED)
374      message(CHECK_PASS "Success")
375      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
376        "${_desc} passed with the following output:\n${_output}\n\n")
377      set(FortranCInterface_VERIFIED_${lang} 1 CACHE INTERNAL "Fortran/${lang} compatibility")
378    else()
379      message(CHECK_FAIL "Failed")
380      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
381        "${_desc} failed with the following output:\n${_output}\n\n")
382      set(FortranCInterface_VERIFIED_${lang} 0 CACHE INTERNAL "Fortran/${lang} compatibility")
383    endif()
384    unset(FortranCInterface_VERIFY_${lang}_COMPILED CACHE)
385  endif()
386
387  # Error if compilers are incompatible.
388  if(NOT FortranCInterface_VERIFIED_${lang} AND NOT quiet)
389    file(READ "${FortranCInterface_BINARY_DIR}/Verify${lang}/output.txt" _output)
390    string(REPLACE "\n" "\n  " _output "${_output}")
391    message(FATAL_ERROR
392      "The Fortran compiler:\n  ${CMAKE_Fortran_COMPILER}\n"
393      "and the ${lang} compiler:\n  ${CMAKE_${lang}_COMPILER}\n"
394      "failed to compile a simple test project using both languages.  "
395      "The output was:\n  ${_output}")
396  endif()
397endfunction()
398
399# Restore including context policies.
400cmake_policy(POP)
401