FortranCInterface.cmake 13.8 KB
Newer Older
1 2 3
# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.

4 5 6 7 8 9 10
#[=======================================================================[.rst:
FortranCInterface
-----------------

Fortran/C Interface Detection

This module automatically detects the API by which C and Fortran
11
languages interact.
12

13 14
Module Variables
^^^^^^^^^^^^^^^^
15

16
Variables that indicate if the mangling is found:
17

18 19
``FortranCInterface_GLOBAL_FOUND``
  Global subroutines and functions.
20

21 22
``FortranCInterface_MODULE_FOUND``
  Module subroutines and functions (declared by "MODULE PROCEDURE").
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
This module also provides the following variables to specify
the detected mangling, though a typical use case does not need
to reference them and can use the `Module Functions`_ below.

``FortranCInterface_GLOBAL_PREFIX``
  Prefix for a global symbol without an underscore.

``FortranCInterface_GLOBAL_SUFFIX``
  Suffix for a global symbol without an underscore.

``FortranCInterface_GLOBAL_CASE``
  The case for a global symbol without an underscore,
  either ``UPPER`` or ``LOWER``.

``FortranCInterface_GLOBAL__PREFIX``
  Prefix for a global symbol with an underscore.

``FortranCInterface_GLOBAL__SUFFIX``
  Suffix for a global symbol with an underscore.

``FortranCInterface_GLOBAL__CASE``
  The case for a global symbol with an underscore,
  either ``UPPER`` or ``LOWER``.

``FortranCInterface_MODULE_PREFIX``
  Prefix for a module symbol without an underscore.

``FortranCInterface_MODULE_MIDDLE``
  Middle of a module symbol without an underscore that appears
  between the name of the module and the name of the symbol.

``FortranCInterface_MODULE_SUFFIX``
  Suffix for a module symbol without an underscore.

``FortranCInterface_MODULE_CASE``
  The case for a module symbol without an underscore,
  either ``UPPER`` or ``LOWER``.

``FortranCInterface_MODULE__PREFIX``
  Prefix for a module symbol with an underscore.

``FortranCInterface_MODULE__MIDDLE``
  Middle of a module symbol with an underscore that appears
  between the name of the module and the name of the symbol.

``FortranCInterface_MODULE__SUFFIX``
  Suffix for a module symbol with an underscore.

``FortranCInterface_MODULE__CASE``
  The case for a module symbol with an underscore,
  either ``UPPER`` or ``LOWER``.

76 77
Module Functions
^^^^^^^^^^^^^^^^
78

79
.. command:: FortranCInterface_HEADER
80

81 82
  The ``FortranCInterface_HEADER`` function is provided to generate a
  C header file containing macros to mangle symbol names::
83

84 85 86 87
    FortranCInterface_HEADER(<file>
                             [MACRO_NAMESPACE <macro-ns>]
                             [SYMBOL_NAMESPACE <ns>]
                             [SYMBOLS [<module>:]<function> ...])
88

89
  It generates in ``<file>`` definitions of the following macros::
90

91 92 93 94
     #define FortranCInterface_GLOBAL (name,NAME) ...
     #define FortranCInterface_GLOBAL_(name,NAME) ...
     #define FortranCInterface_MODULE (mod,name, MOD,NAME) ...
     #define FortranCInterface_MODULE_(mod,name, MOD,NAME) ...
95

96
  These macros mangle four categories of Fortran symbols, respectively:
97

98 99 100 101
  * 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()``
102

103 104
  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.
105

106
  The options are:
107

108 109 110
  ``MACRO_NAMESPACE``
    Replace the default ``FortranCInterface_`` prefix with a given
    namespace ``<macro-ns>``.
111

112 113
  ``SYMBOLS``
    List symbols to mangle automatically with C preprocessor definitions::
114

115 116
      <function>          ==> #define <ns><function> ...
      <module>:<function> ==> #define <ns><module>_<function> ...
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
    If the mangling for some symbol is not known then no preprocessor
    definition is created, and a warning is displayed.

  ``SYMBOL_NAMESPACE``
    Prefix all preprocessor definitions generated by the ``SYMBOLS``
    option with a given namespace ``<ns>``.

.. command:: FortranCInterface_VERIFY

  The ``FortranCInterface_VERIFY`` 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.

Example Usage
^^^^^^^^^^^^^

.. code-block:: cmake
145 146 147 148

   include(FortranCInterface)
   FortranCInterface_HEADER(FC.h MACRO_NAMESPACE "FC_")

149 150
This creates a "FC.h" header that defines mangling macros ``FC_GLOBAL()``,
``FC_GLOBAL_()``, ``FC_MODULE()``, and ``FC_MODULE_()``.
151

152
.. code-block:: cmake
153 154 155 156 157 158 159

   include(FortranCInterface)
   FortranCInterface_HEADER(FCMangle.h
                            MACRO_NAMESPACE "FC_"
                            SYMBOL_NAMESPACE "FC_"
                            SYMBOLS mysub mymod:my_sub)

160
This creates a "FCMangle.h" header that defines the same ``FC_*()``
161
mangling macros as the previous example plus preprocessor symbols
162
``FC_mysub`` and ``FC_mymod_my_sub``.
163

164 165
Additional Manglings
^^^^^^^^^^^^^^^^^^^^
166

167 168 169
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::
170 171 172 173 174

   FortranCInterface_GLOBAL_SYMBOLS
   FortranCInterface_MODULE_SYMBOLS

before including FortranCInterface to specify manglings of the symbols
175 176
``MySub``, ``My_Sub``, ``MyModule:MySub``, and ``My_Module:My_Sub``.
For example, the code:
177

178
.. code-block:: cmake
179 180 181 182 183 184 185 186

   set(FortranCInterface_GLOBAL_SYMBOLS mysub_ my_sub__ MYSUB_)
     #                                  ^^^^^  ^^^^^^   ^^^^^
   set(FortranCInterface_MODULE_SYMBOLS
       __mymodule_MOD_mysub __my_module_MOD_my_sub)
     #   ^^^^^^^^     ^^^^^   ^^^^^^^^^     ^^^^^^
   include(FortranCInterface)

187
tells FortranCInterface to try given ``GLOBAL`` and ``MODULE`` manglings.
188 189 190
(The carets point at raw symbol names for clarity in this example but
are not needed.)
#]=======================================================================]
191 192 193 194 195 196 197

#-----------------------------------------------------------------------------
# Execute at most once in a project.
if(FortranCInterface_SOURCE_DIR)
  return()
endif()

198
cmake_policy(PUSH)
199
cmake_policy(SET CMP0007 NEW)
200

201 202 203 204 205 206 207 208 209
#-----------------------------------------------------------------------------
# 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()

210 211 212
#-----------------------------------------------------------------------------
set(FortranCInterface_SOURCE_DIR ${CMAKE_ROOT}/Modules/FortranCInterface)

213 214 215 216 217 218 219 220
# 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()

221 222 223 224
# 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)
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
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)
263
      string(APPEND HEADER_CONTENT "
264 265
${_desc_${macro}}
#define ${MACRO_NAMESPACE}${macro}${FortranCInterface_${macro}_MACRO}
266
")
267 268 269 270 271
    endif()
  endforeach()

  # Generate symbol mangling definitions.
  if(SYMBOLS)
272
    string(APPEND HEADER_CONTENT "
273 274
/*--------------------------------------------------------------------------*/
/* Mangle some symbols automatically.                                       */
275
")
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
  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)
293
        string(APPEND HEADER_CONTENT "#define ${SYMBOL_NAMESPACE}${module}_${function} ${MACRO_NAMESPACE}MODULE${form}(${m_lower},${f_lower}, ${m_upper},${f_upper})\n")
294 295 296 297 298 299 300 301 302 303 304 305 306
      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)
307
        string(APPEND HEADER_CONTENT "#define ${SYMBOL_NAMESPACE}${f} ${MACRO_NAMESPACE}GLOBAL${form}(${f_lower}, ${f_upper})\n")
308 309 310 311
      else()
        message(AUTHOR_WARNING "No FortranCInterface mangling known for ${f}")
      endif()
    endif()
312
  endforeach()
313

314 315 316
  # Store the content.
  configure_file(${FortranCInterface_SOURCE_DIR}/Macro.h.in ${FILE} @ONLY)
endfunction()
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343

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")
344
    message(CHECK_START "${_desc}")
345 346

    # Build a sample project which reports symbols.
347
    set(CMAKE_TRY_COMPILE_CONFIGURATION Release)
348 349 350
    try_compile(FortranCInterface_VERIFY_${lang}_COMPILED
      ${FortranCInterface_BINARY_DIR}/Verify${lang}
      ${FortranCInterface_SOURCE_DIR}/Verify
351 352
      VerifyFortranC # project name
      VerifyFortranC # target name
353
      CMAKE_FLAGS -DVERIFY_CXX=${verify_cxx}
354
                  -DCMAKE_VERBOSE_MAKEFILE=ON
355 356 357
                 "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS}"
                 "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}"
                 "-DCMAKE_Fortran_FLAGS:STRING=${CMAKE_Fortran_FLAGS}"
358 359 360
                 "-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}"
361 362 363 364 365
      OUTPUT_VARIABLE _output)
    file(WRITE "${FortranCInterface_BINARY_DIR}/Verify${lang}/output.txt" "${_output}")

    # Report results.
    if(FortranCInterface_VERIFY_${lang}_COMPILED)
366
      message(CHECK_PASS "Success")
367 368 369 370
      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()
371
      message(CHECK_FAIL "Failed")
372 373 374 375 376 377 378 379 380 381
      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)
382
    string(REPLACE "\n" "\n  " _output "${_output}")
383 384 385 386 387 388 389
    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()
390 391 392

# Restore including context policies.
cmake_policy(POP)