CMake/Modules/FortranCInterface.cmake
Brad King 86578eccf2 Simplify CMake per-source license notices
Per-source copyright/license notice headers that spell out copyright holder
names and years are hard to maintain and often out-of-date or plain wrong.
Precise contributor information is already maintained automatically by the
version control tool.  Ultimately it is the receiver of a file who is
responsible for determining its licensing status, and per-source notices are
merely a convenience.  Therefore it is simpler and more accurate for
each source to have a generic notice of the license name and references to
more detailed information on copyright holders and full license terms.

Our `Copyright.txt` file now contains a list of Contributors whose names
appeared source-level copyright notices.  It also references version control
history for more precise information.  Therefore we no longer need to spell
out the list of Contributors in each source file notice.

Replace CMake per-source copyright/license notice headers with a short
description of the license and links to `Copyright.txt` and online information
available from "https://cmake.org/licensing".  The online URL also handles
cases of modules being copied out of our source into other projects, so we
can drop our notices about replacing links with full license text.

Run the `Utilities/Scripts/filter-notices.bash` script to perform the majority
of the replacements mechanically.  Manually fix up shebang lines and trailing
newlines in a few files.  Manually update the notices in a few files that the
script does not handle.
2016-09-27 15:14:44 -04:00

392 lines
14 KiB
CMake

# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
FortranCInterface
-----------------
Fortran/C Interface Detection
This module automatically detects the API by which C and Fortran
languages interact.
Module Variables
^^^^^^^^^^^^^^^^
Variables that indicate if the mangling is found:
``FortranCInterface_GLOBAL_FOUND``
Global subroutines and functions.
``FortranCInterface_MODULE_FOUND``
Module subroutines and functions (declared by "MODULE PROCEDURE").
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``.
Module Functions
^^^^^^^^^^^^^^^^
.. command:: FortranCInterface_HEADER
The ``FortranCInterface_HEADER`` function is provided to generate a
C header file containing macros to mangle symbol names::
FortranCInterface_HEADER(<file>
[MACRO_NAMESPACE <macro-ns>]
[SYMBOL_NAMESPACE <ns>]
[SYMBOLS [<module>:]<function> ...])
It generates in ``<file>`` 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 options are:
``MACRO_NAMESPACE``
Replace the default ``FortranCInterface_`` prefix with a given
namespace ``<macro-ns>``.
``SYMBOLS``
List symbols to mangle automatically with C preprocessor definitions::
<function> ==> #define <ns><function> ...
<module>:<function> ==> #define <ns><module>_<function> ...
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
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_()``.
.. code-block:: cmake
include(FortranCInterface)
FortranCInterface_HEADER(FCMangle.h
MACRO_NAMESPACE "FC_"
SYMBOL_NAMESPACE "FC_"
SYMBOLS mysub mymod:my_sub)
This creates a "FCMangle.h" header that defines the same ``FC_*()``
mangling macros as the previous example plus preprocessor symbols
``FC_mysub`` and ``FC_mymod_my_sub``.
Additional Manglings
^^^^^^^^^^^^^^^^^^^^
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:
.. code-block:: cmake
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()
cmake_policy(PUSH)
cmake_policy(SET CMP0007 NEW)
#-----------------------------------------------------------------------------
# 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()
#-----------------------------------------------------------------------------
set(FortranCInterface_SOURCE_DIR ${CMAKE_ROOT}/Modules/FortranCInterface)
# 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()
# 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)
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)
string(APPEND HEADER_CONTENT "
${_desc_${macro}}
#define ${MACRO_NAMESPACE}${macro}${FortranCInterface_${macro}_MACRO}
")
endif()
endforeach()
# Generate symbol mangling definitions.
if(SYMBOLS)
string(APPEND HEADER_CONTENT "
/*--------------------------------------------------------------------------*/
/* Mangle some symbols automatically. */
")
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)
string(APPEND 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)
string(APPEND 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()
# Store the content.
configure_file(${FortranCInterface_SOURCE_DIR}/Macro.h.in ${FILE} @ONLY)
endfunction()
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")
message(STATUS "${_desc}")
# Build a sample project which reports symbols.
set(CMAKE_TRY_COMPILE_CONFIGURATION Release)
try_compile(FortranCInterface_VERIFY_${lang}_COMPILED
${FortranCInterface_BINARY_DIR}/Verify${lang}
${FortranCInterface_SOURCE_DIR}/Verify
VerifyFortranC
CMAKE_FLAGS -DVERIFY_CXX=${verify_cxx}
-DCMAKE_VERBOSE_MAKEFILE=ON
"-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS}"
"-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}"
"-DCMAKE_Fortran_FLAGS:STRING=${CMAKE_Fortran_FLAGS}"
"-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}"
OUTPUT_VARIABLE _output)
file(WRITE "${FortranCInterface_BINARY_DIR}/Verify${lang}/output.txt" "${_output}")
# Report results.
if(FortranCInterface_VERIFY_${lang}_COMPILED)
message(STATUS "${_desc} - Success")
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()
message(STATUS "${_desc} - Failed")
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)
string(REPLACE "\n" "\n " _output "${_output}")
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()
# Restore including context policies.
cmake_policy(POP)