694 lines
26 KiB
CMake
694 lines
26 KiB
CMake
|
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||
|
# file Copyright.txt or https://cmake.org/licensing for details.
|
||
|
|
||
|
#[=======================================================================[.rst:
|
||
|
WriteCompilerDetectionHeader
|
||
|
----------------------------
|
||
|
|
||
|
This module provides the function ``write_compiler_detection_header()``.
|
||
|
|
||
|
This function can be used to generate a file suitable for preprocessor
|
||
|
inclusion which contains macros to be used in source code::
|
||
|
|
||
|
write_compiler_detection_header(
|
||
|
FILE <file>
|
||
|
PREFIX <prefix>
|
||
|
[OUTPUT_FILES_VAR <output_files_var> OUTPUT_DIR <output_dir>]
|
||
|
COMPILERS <compiler> [...]
|
||
|
FEATURES <feature> [...]
|
||
|
[BARE_FEATURES <feature> [...]]
|
||
|
[VERSION <version>]
|
||
|
[PROLOG <prolog>]
|
||
|
[EPILOG <epilog>]
|
||
|
[ALLOW_UNKNOWN_COMPILERS]
|
||
|
[ALLOW_UNKNOWN_COMPILER_VERSIONS]
|
||
|
)
|
||
|
|
||
|
This generates the file ``<file>`` with macros which all have the prefix
|
||
|
``<prefix>``.
|
||
|
|
||
|
By default, all content is written directly to the ``<file>``. The
|
||
|
``OUTPUT_FILES_VAR`` may be specified to cause the compiler-specific
|
||
|
content to be written to separate files. The separate files are then
|
||
|
available in the ``<output_files_var>`` and may be consumed by the caller
|
||
|
for installation for example. The ``OUTPUT_DIR`` specifies a relative
|
||
|
path from the main ``<file>`` to the compiler-specific files. For example:
|
||
|
|
||
|
.. code-block:: cmake
|
||
|
|
||
|
write_compiler_detection_header(
|
||
|
FILE climbingstats_compiler_detection.h
|
||
|
PREFIX ClimbingStats
|
||
|
OUTPUT_FILES_VAR support_files
|
||
|
OUTPUT_DIR compilers
|
||
|
COMPILERS GNU Clang MSVC Intel
|
||
|
FEATURES cxx_variadic_templates
|
||
|
)
|
||
|
install(FILES
|
||
|
${CMAKE_CURRENT_BINARY_DIR}/climbingstats_compiler_detection.h
|
||
|
DESTINATION include
|
||
|
)
|
||
|
install(FILES
|
||
|
${support_files}
|
||
|
DESTINATION include/compilers
|
||
|
)
|
||
|
|
||
|
|
||
|
``VERSION`` may be used to specify the API version to be generated.
|
||
|
Future versions of CMake may introduce alternative APIs. A given
|
||
|
API is selected by any ``<version>`` value greater than or equal
|
||
|
to the version of CMake that introduced the given API and less
|
||
|
than the version of CMake that introduced its succeeding API.
|
||
|
The value of the :variable:`CMAKE_MINIMUM_REQUIRED_VERSION`
|
||
|
variable is used if no explicit version is specified.
|
||
|
(As of CMake version |release| there is only one API version.)
|
||
|
|
||
|
``PROLOG`` may be specified as text content to write at the start of the
|
||
|
header. ``EPILOG`` may be specified as text content to write at the end
|
||
|
of the header
|
||
|
|
||
|
At least one ``<compiler>`` and one ``<feature>`` must be listed. Compilers
|
||
|
which are known to CMake, but not specified are detected and a preprocessor
|
||
|
``#error`` is generated for them. A preprocessor macro matching
|
||
|
``<PREFIX>_COMPILER_IS_<compiler>`` is generated for each compiler
|
||
|
known to CMake to contain the value ``0`` or ``1``.
|
||
|
|
||
|
Possible compiler identifiers are documented with the
|
||
|
:variable:`CMAKE_<LANG>_COMPILER_ID` variable.
|
||
|
Available features in this version of CMake are listed in the
|
||
|
:prop_gbl:`CMAKE_C_KNOWN_FEATURES` and
|
||
|
:prop_gbl:`CMAKE_CXX_KNOWN_FEATURES` global properties.
|
||
|
The ``{c,cxx}_std_*`` meta-features are ignored if requested.
|
||
|
|
||
|
See the :manual:`cmake-compile-features(7)` manual for information on
|
||
|
compile features.
|
||
|
|
||
|
``BARE_FEATURES`` will define the compatibility macros with the name used in
|
||
|
newer versions of the language standard, so the code can use the new feature
|
||
|
name unconditionally.
|
||
|
|
||
|
``ALLOW_UNKNOWN_COMPILERS`` and ``ALLOW_UNKNOWN_COMPILER_VERSIONS`` cause
|
||
|
the module to generate conditions that treat unknown compilers as simply
|
||
|
lacking all features. Without these options the default behavior is to
|
||
|
generate a ``#error`` for unknown compilers and versions.
|
||
|
|
||
|
Feature Test Macros
|
||
|
===================
|
||
|
|
||
|
For each compiler, a preprocessor macro is generated matching
|
||
|
``<PREFIX>_COMPILER_IS_<compiler>`` which has the content either ``0``
|
||
|
or ``1``, depending on the compiler in use. Preprocessor macros for
|
||
|
compiler version components are generated matching
|
||
|
``<PREFIX>_COMPILER_VERSION_MAJOR`` ``<PREFIX>_COMPILER_VERSION_MINOR``
|
||
|
and ``<PREFIX>_COMPILER_VERSION_PATCH`` containing decimal values
|
||
|
for the corresponding compiler version components, if defined.
|
||
|
|
||
|
A preprocessor test is generated based on the compiler version
|
||
|
denoting whether each feature is enabled. A preprocessor macro
|
||
|
matching ``<PREFIX>_COMPILER_<FEATURE>``, where ``<FEATURE>`` is the
|
||
|
upper-case ``<feature>`` name, is generated to contain the value
|
||
|
``0`` or ``1`` depending on whether the compiler in use supports the
|
||
|
feature:
|
||
|
|
||
|
.. code-block:: cmake
|
||
|
|
||
|
write_compiler_detection_header(
|
||
|
FILE climbingstats_compiler_detection.h
|
||
|
PREFIX ClimbingStats
|
||
|
COMPILERS GNU Clang AppleClang MSVC Intel
|
||
|
FEATURES cxx_variadic_templates
|
||
|
)
|
||
|
|
||
|
.. code-block:: c++
|
||
|
|
||
|
#if ClimbingStats_COMPILER_CXX_VARIADIC_TEMPLATES
|
||
|
template<typename... T>
|
||
|
void someInterface(T t...) { /* ... */ }
|
||
|
#else
|
||
|
// Compatibility versions
|
||
|
template<typename T1>
|
||
|
void someInterface(T1 t1) { /* ... */ }
|
||
|
template<typename T1, typename T2>
|
||
|
void someInterface(T1 t1, T2 t2) { /* ... */ }
|
||
|
template<typename T1, typename T2, typename T3>
|
||
|
void someInterface(T1 t1, T2 t2, T3 t3) { /* ... */ }
|
||
|
#endif
|
||
|
|
||
|
Symbol Macros
|
||
|
=============
|
||
|
|
||
|
Some additional symbol-defines are created for particular features for
|
||
|
use as symbols which may be conditionally defined empty:
|
||
|
|
||
|
.. code-block:: c++
|
||
|
|
||
|
class MyClass ClimbingStats_FINAL
|
||
|
{
|
||
|
ClimbingStats_CONSTEXPR int someInterface() { return 42; }
|
||
|
};
|
||
|
|
||
|
The ``ClimbingStats_FINAL`` macro will expand to ``final`` if the
|
||
|
compiler (and its flags) support the ``cxx_final`` feature, and the
|
||
|
``ClimbingStats_CONSTEXPR`` macro will expand to ``constexpr``
|
||
|
if ``cxx_constexpr`` is supported.
|
||
|
|
||
|
If ``BARE_FEATURES cxx_final`` was given as argument the ``final`` keyword
|
||
|
will be defined for old compilers, too.
|
||
|
|
||
|
The following features generate corresponding symbol defines and if they
|
||
|
are available as ``BARE_FEATURES``:
|
||
|
|
||
|
========================== =================================== ================= ======
|
||
|
Feature Define Symbol bare
|
||
|
========================== =================================== ================= ======
|
||
|
``c_restrict`` ``<PREFIX>_RESTRICT`` ``restrict`` yes
|
||
|
``cxx_constexpr`` ``<PREFIX>_CONSTEXPR`` ``constexpr`` yes
|
||
|
``cxx_deleted_functions`` ``<PREFIX>_DELETED_FUNCTION`` ``= delete``
|
||
|
``cxx_extern_templates`` ``<PREFIX>_EXTERN_TEMPLATE`` ``extern``
|
||
|
``cxx_final`` ``<PREFIX>_FINAL`` ``final`` yes
|
||
|
``cxx_noexcept`` ``<PREFIX>_NOEXCEPT`` ``noexcept`` yes
|
||
|
``cxx_noexcept`` ``<PREFIX>_NOEXCEPT_EXPR(X)`` ``noexcept(X)``
|
||
|
``cxx_override`` ``<PREFIX>_OVERRIDE`` ``override`` yes
|
||
|
========================== =================================== ================= ======
|
||
|
|
||
|
Compatibility Implementation Macros
|
||
|
===================================
|
||
|
|
||
|
Some features are suitable for wrapping in a macro with a backward
|
||
|
compatibility implementation if the compiler does not support the feature.
|
||
|
|
||
|
When the ``cxx_static_assert`` feature is not provided by the compiler,
|
||
|
a compatibility implementation is available via the
|
||
|
``<PREFIX>_STATIC_ASSERT(COND)`` and
|
||
|
``<PREFIX>_STATIC_ASSERT_MSG(COND, MSG)`` function-like macros. The macros
|
||
|
expand to ``static_assert`` where that compiler feature is available, and
|
||
|
to a compatibility implementation otherwise. In the first form, the
|
||
|
condition is stringified in the message field of ``static_assert``. In
|
||
|
the second form, the message ``MSG`` is passed to the message field of
|
||
|
``static_assert``, or ignored if using the backward compatibility
|
||
|
implementation.
|
||
|
|
||
|
The ``cxx_attribute_deprecated`` feature provides a macro definition
|
||
|
``<PREFIX>_DEPRECATED``, which expands to either the standard
|
||
|
``[[deprecated]]`` attribute or a compiler-specific decorator such
|
||
|
as ``__attribute__((__deprecated__))`` used by GNU compilers.
|
||
|
|
||
|
The ``cxx_alignas`` feature provides a macro definition
|
||
|
``<PREFIX>_ALIGNAS`` which expands to either the standard ``alignas``
|
||
|
decorator or a compiler-specific decorator such as
|
||
|
``__attribute__ ((__aligned__))`` used by GNU compilers.
|
||
|
|
||
|
The ``cxx_alignof`` feature provides a macro definition
|
||
|
``<PREFIX>_ALIGNOF`` which expands to either the standard ``alignof``
|
||
|
decorator or a compiler-specific decorator such as ``__alignof__``
|
||
|
used by GNU compilers.
|
||
|
|
||
|
============================= ================================ ===================== ======
|
||
|
Feature Define Symbol bare
|
||
|
============================= ================================ ===================== ======
|
||
|
``cxx_alignas`` ``<PREFIX>_ALIGNAS`` ``alignas``
|
||
|
``cxx_alignof`` ``<PREFIX>_ALIGNOF`` ``alignof``
|
||
|
``cxx_nullptr`` ``<PREFIX>_NULLPTR`` ``nullptr`` yes
|
||
|
``cxx_static_assert`` ``<PREFIX>_STATIC_ASSERT`` ``static_assert``
|
||
|
``cxx_static_assert`` ``<PREFIX>_STATIC_ASSERT_MSG`` ``static_assert``
|
||
|
``cxx_attribute_deprecated`` ``<PREFIX>_DEPRECATED`` ``[[deprecated]]``
|
||
|
``cxx_attribute_deprecated`` ``<PREFIX>_DEPRECATED_MSG`` ``[[deprecated]]``
|
||
|
``cxx_thread_local`` ``<PREFIX>_THREAD_LOCAL`` ``thread_local``
|
||
|
============================= ================================ ===================== ======
|
||
|
|
||
|
A use-case which arises with such deprecation macros is the deprecation
|
||
|
of an entire library. In that case, all public API in the library may
|
||
|
be decorated with the ``<PREFIX>_DEPRECATED`` macro. This results in
|
||
|
very noisy build output when building the library itself, so the macro
|
||
|
may be may be defined to empty in that case when building the deprecated
|
||
|
library:
|
||
|
|
||
|
.. code-block:: cmake
|
||
|
|
||
|
add_library(compat_support ${srcs})
|
||
|
target_compile_definitions(compat_support
|
||
|
PRIVATE
|
||
|
CompatSupport_DEPRECATED=
|
||
|
)
|
||
|
#]=======================================================================]
|
||
|
|
||
|
include(${CMAKE_CURRENT_LIST_DIR}/CMakeCompilerIdDetection.cmake)
|
||
|
|
||
|
function(_load_compiler_variables CompilerId lang)
|
||
|
include("${CMAKE_ROOT}/Modules/Compiler/${CompilerId}-${lang}-FeatureTests.cmake" OPTIONAL)
|
||
|
set(_cmake_oldestSupported_${CompilerId} ${_cmake_oldestSupported} PARENT_SCOPE)
|
||
|
foreach(feature ${ARGN})
|
||
|
set(_cmake_feature_test_${CompilerId}_${feature} ${_cmake_feature_test_${feature}} PARENT_SCOPE)
|
||
|
endforeach()
|
||
|
include("${CMAKE_ROOT}/Modules/Compiler/${CompilerId}-${lang}-DetermineCompiler.cmake" OPTIONAL
|
||
|
RESULT_VARIABLE determinedCompiler)
|
||
|
if (NOT determinedCompiler)
|
||
|
include("${CMAKE_ROOT}/Modules/Compiler/${CompilerId}-DetermineCompiler.cmake" OPTIONAL)
|
||
|
endif()
|
||
|
set(_compiler_id_version_compute_${CompilerId} ${_compiler_id_version_compute} PARENT_SCOPE)
|
||
|
endfunction()
|
||
|
|
||
|
macro(_simpledefine FEATURE_NAME FEATURE_TESTNAME FEATURE_STRING FEATURE_DEFAULT_STRING)
|
||
|
if (feature STREQUAL "${FEATURE_NAME}")
|
||
|
set(def_value "${prefix_arg}_${FEATURE_TESTNAME}")
|
||
|
string(APPEND file_content "
|
||
|
# if defined(${def_name}) && ${def_name}
|
||
|
# define ${def_value} ${FEATURE_STRING}
|
||
|
# else
|
||
|
# define ${def_value} ${FEATURE_DEFAULT_STRING}
|
||
|
# endif
|
||
|
\n")
|
||
|
endif()
|
||
|
endmacro()
|
||
|
|
||
|
macro(_simplebaredefine FEATURE_NAME FEATURE_STRING FEATURE_DEFAULT_STRING)
|
||
|
if (feature STREQUAL "${FEATURE_NAME}")
|
||
|
string(APPEND file_content "
|
||
|
# if !(defined(${def_name}) && ${def_name})
|
||
|
# define ${FEATURE_STRING} ${FEATURE_DEFAULT_STRING}
|
||
|
# endif
|
||
|
\n")
|
||
|
endif()
|
||
|
endmacro()
|
||
|
|
||
|
function(_check_feature_lists C_FEATURE_VAR CXX_FEATURE_VAR)
|
||
|
foreach(feature ${ARGN})
|
||
|
if (feature MATCHES "^c_std_")
|
||
|
# ignored
|
||
|
elseif (feature MATCHES "^cxx_std_")
|
||
|
# ignored
|
||
|
elseif (feature MATCHES "^cxx_")
|
||
|
list(APPEND _langs CXX)
|
||
|
list(APPEND ${CXX_FEATURE_VAR} ${feature})
|
||
|
elseif (feature MATCHES "^c_")
|
||
|
list(APPEND _langs C)
|
||
|
list(APPEND ${C_FEATURE_VAR} ${feature})
|
||
|
else()
|
||
|
message(FATAL_ERROR "Unsupported feature ${feature}.")
|
||
|
endif()
|
||
|
endforeach()
|
||
|
set(${C_FEATURE_VAR} ${${C_FEATURE_VAR}} PARENT_SCOPE)
|
||
|
set(${CXX_FEATURE_VAR} ${${CXX_FEATURE_VAR}} PARENT_SCOPE)
|
||
|
set(_langs ${_langs} PARENT_SCOPE)
|
||
|
endfunction()
|
||
|
|
||
|
function(write_compiler_detection_header
|
||
|
file_keyword file_arg
|
||
|
prefix_keyword prefix_arg
|
||
|
)
|
||
|
if (NOT "x${file_keyword}" STREQUAL "xFILE")
|
||
|
message(FATAL_ERROR "write_compiler_detection_header: FILE parameter missing.")
|
||
|
endif()
|
||
|
if (NOT "x${prefix_keyword}" STREQUAL "xPREFIX")
|
||
|
message(FATAL_ERROR "write_compiler_detection_header: PREFIX parameter missing.")
|
||
|
endif()
|
||
|
set(options ALLOW_UNKNOWN_COMPILERS ALLOW_UNKNOWN_COMPILER_VERSIONS)
|
||
|
set(oneValueArgs VERSION EPILOG PROLOG OUTPUT_FILES_VAR OUTPUT_DIR)
|
||
|
set(multiValueArgs COMPILERS FEATURES BARE_FEATURES)
|
||
|
cmake_parse_arguments(_WCD "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||
|
|
||
|
if (NOT _WCD_COMPILERS)
|
||
|
message(FATAL_ERROR "Invalid arguments. write_compiler_detection_header requires at least one compiler.")
|
||
|
endif()
|
||
|
if (NOT _WCD_FEATURES AND NOT _WCD_BARE_FEATURES)
|
||
|
message(FATAL_ERROR "Invalid arguments. write_compiler_detection_header requires at least one feature.")
|
||
|
endif()
|
||
|
|
||
|
if(_WCD_UNPARSED_ARGUMENTS)
|
||
|
message(FATAL_ERROR "Unparsed arguments: ${_WCD_UNPARSED_ARGUMENTS}")
|
||
|
endif()
|
||
|
|
||
|
if (prefix_arg STREQUAL "")
|
||
|
message(FATAL_ERROR "A prefix must be specified")
|
||
|
endif()
|
||
|
string(MAKE_C_IDENTIFIER ${prefix_arg} cleaned_prefix)
|
||
|
if (NOT prefix_arg STREQUAL cleaned_prefix)
|
||
|
message(FATAL_ERROR "The prefix must be a valid C identifier.")
|
||
|
endif()
|
||
|
|
||
|
if(NOT _WCD_VERSION)
|
||
|
set(_WCD_VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
|
||
|
endif()
|
||
|
set(_min_version 3.1.0) # Version which introduced this function
|
||
|
if (_WCD_VERSION VERSION_LESS _min_version)
|
||
|
set(err "VERSION compatibility for write_compiler_detection_header is set to ${_WCD_VERSION}, which is too low.")
|
||
|
string(APPEND err " It must be set to at least ${_min_version}. ")
|
||
|
string(APPEND err " Either set the VERSION parameter to the write_compiler_detection_header function, or update")
|
||
|
string(APPEND err " your minimum required CMake version with the cmake_minimum_required command.")
|
||
|
message(FATAL_ERROR "${err}")
|
||
|
endif()
|
||
|
|
||
|
if(_WCD_OUTPUT_FILES_VAR)
|
||
|
if(NOT _WCD_OUTPUT_DIR)
|
||
|
message(FATAL_ERROR "If OUTPUT_FILES_VAR is specified, then OUTPUT_DIR must also be specified.")
|
||
|
endif()
|
||
|
endif()
|
||
|
if(_WCD_OUTPUT_DIR)
|
||
|
if(NOT _WCD_OUTPUT_FILES_VAR)
|
||
|
message(FATAL_ERROR "If OUTPUT_DIR is specified, then OUTPUT_FILES_VAR must also be specified.")
|
||
|
endif()
|
||
|
get_filename_component(main_file_dir ${file_arg} DIRECTORY)
|
||
|
if (NOT IS_ABSOLUTE ${main_file_dir})
|
||
|
set(main_file_dir "${CMAKE_CURRENT_BINARY_DIR}/${main_file_dir}")
|
||
|
endif()
|
||
|
if (NOT IS_ABSOLUTE ${_WCD_OUTPUT_DIR})
|
||
|
set(_WCD_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/${_WCD_OUTPUT_DIR}")
|
||
|
endif()
|
||
|
get_filename_component(out_file_dir ${_WCD_OUTPUT_DIR} ABSOLUTE)
|
||
|
string(FIND ${out_file_dir} ${main_file_dir} idx)
|
||
|
if (NOT idx EQUAL 0)
|
||
|
message(FATAL_ERROR "The compiler-specific output directory must be within the same directory as the main file.")
|
||
|
endif()
|
||
|
|
||
|
if (main_file_dir STREQUAL out_file_dir)
|
||
|
unset(_WCD_OUTPUT_DIR)
|
||
|
else()
|
||
|
string(REPLACE "${main_file_dir}/" "" _WCD_OUTPUT_DIR "${out_file_dir}/")
|
||
|
endif()
|
||
|
endif()
|
||
|
|
||
|
set(compilers
|
||
|
GNU
|
||
|
Clang
|
||
|
AppleClang
|
||
|
MSVC
|
||
|
SunPro
|
||
|
Intel
|
||
|
)
|
||
|
|
||
|
set(_hex_compilers ADSP Borland Embarcadero SunPro)
|
||
|
|
||
|
foreach(_comp ${_WCD_COMPILERS})
|
||
|
list(FIND compilers ${_comp} idx)
|
||
|
if (idx EQUAL -1)
|
||
|
message(FATAL_ERROR "Unsupported compiler ${_comp}.")
|
||
|
endif()
|
||
|
if (NOT _need_hex_conversion)
|
||
|
list(FIND _hex_compilers ${_comp} idx)
|
||
|
if (NOT idx EQUAL -1)
|
||
|
set(_need_hex_conversion TRUE)
|
||
|
endif()
|
||
|
endif()
|
||
|
endforeach()
|
||
|
|
||
|
set(file_content "
|
||
|
// This is a generated file. Do not edit!
|
||
|
|
||
|
#ifndef ${prefix_arg}_COMPILER_DETECTION_H
|
||
|
#define ${prefix_arg}_COMPILER_DETECTION_H
|
||
|
")
|
||
|
|
||
|
if (_WCD_PROLOG)
|
||
|
string(APPEND file_content "\n${_WCD_PROLOG}\n")
|
||
|
endif()
|
||
|
|
||
|
if (_need_hex_conversion)
|
||
|
string(APPEND file_content "
|
||
|
#define ${prefix_arg}_DEC(X) (X)
|
||
|
#define ${prefix_arg}_HEX(X) ( \\
|
||
|
((X)>>28 & 0xF) * 10000000 + \\
|
||
|
((X)>>24 & 0xF) * 1000000 + \\
|
||
|
((X)>>20 & 0xF) * 100000 + \\
|
||
|
((X)>>16 & 0xF) * 10000 + \\
|
||
|
((X)>>12 & 0xF) * 1000 + \\
|
||
|
((X)>>8 & 0xF) * 100 + \\
|
||
|
((X)>>4 & 0xF) * 10 + \\
|
||
|
((X) & 0xF) \\
|
||
|
)\n")
|
||
|
endif()
|
||
|
|
||
|
_check_feature_lists(C_features CXX_features ${_WCD_FEATURES})
|
||
|
_check_feature_lists(C_bare_features CXX_bare_features ${_WCD_BARE_FEATURES})
|
||
|
list(REMOVE_DUPLICATES _langs)
|
||
|
|
||
|
if(_WCD_OUTPUT_FILES_VAR)
|
||
|
get_filename_component(main_file_name ${file_arg} NAME)
|
||
|
set(compiler_file_content_
|
||
|
"#ifndef ${prefix_arg}_COMPILER_DETECTION_H
|
||
|
# error This file may only be included from ${main_file_name}
|
||
|
#endif\n")
|
||
|
endif()
|
||
|
|
||
|
foreach(_lang ${_langs})
|
||
|
set(target_compilers)
|
||
|
foreach(compiler ${_WCD_COMPILERS})
|
||
|
_load_compiler_variables(${compiler} ${_lang} ${${_lang}_features})
|
||
|
if(_cmake_oldestSupported_${compiler})
|
||
|
list(APPEND target_compilers ${compiler})
|
||
|
endif()
|
||
|
endforeach()
|
||
|
|
||
|
get_property(known_features GLOBAL PROPERTY CMAKE_${_lang}_KNOWN_FEATURES)
|
||
|
foreach(feature ${${_lang}_features})
|
||
|
list(FIND known_features ${feature} idx)
|
||
|
if (idx EQUAL -1)
|
||
|
message(FATAL_ERROR "Unsupported feature ${feature}.")
|
||
|
endif()
|
||
|
endforeach()
|
||
|
|
||
|
if(_lang STREQUAL CXX)
|
||
|
string(APPEND file_content "\n#ifdef __cplusplus\n")
|
||
|
else()
|
||
|
string(APPEND file_content "\n#ifndef __cplusplus\n")
|
||
|
endif()
|
||
|
|
||
|
compiler_id_detection(ID_CONTENT ${_lang} PREFIX ${prefix_arg}_
|
||
|
ID_DEFINE
|
||
|
)
|
||
|
|
||
|
string(APPEND file_content "${ID_CONTENT}\n")
|
||
|
|
||
|
set(pp_if "if")
|
||
|
foreach(compiler ${target_compilers})
|
||
|
string(APPEND file_content "\n# ${pp_if} ${prefix_arg}_COMPILER_IS_${compiler}\n")
|
||
|
|
||
|
if(_WCD_OUTPUT_FILES_VAR)
|
||
|
set(compile_file_name "${_WCD_OUTPUT_DIR}${prefix_arg}_COMPILER_INFO_${compiler}_${_lang}.h")
|
||
|
string(APPEND file_content "\n# include \"${compile_file_name}\"\n")
|
||
|
endif()
|
||
|
|
||
|
if(_WCD_OUTPUT_FILES_VAR)
|
||
|
set(compiler_file_content compiler_file_content_${compiler}_${_lang})
|
||
|
else()
|
||
|
set(compiler_file_content file_content)
|
||
|
endif()
|
||
|
|
||
|
if(NOT _WCD_ALLOW_UNKNOWN_COMPILER_VERSIONS)
|
||
|
string(APPEND ${compiler_file_content} "
|
||
|
# if !(${_cmake_oldestSupported_${compiler}})
|
||
|
# error Unsupported compiler version
|
||
|
# endif\n")
|
||
|
endif()
|
||
|
|
||
|
set(PREFIX ${prefix_arg}_)
|
||
|
if (_need_hex_conversion)
|
||
|
set(MACRO_DEC ${prefix_arg}_DEC)
|
||
|
set(MACRO_HEX ${prefix_arg}_HEX)
|
||
|
else()
|
||
|
set(MACRO_DEC)
|
||
|
set(MACRO_HEX)
|
||
|
endif()
|
||
|
string(CONFIGURE "${_compiler_id_version_compute_${compiler}}" VERSION_BLOCK @ONLY)
|
||
|
string(APPEND ${compiler_file_content} "${VERSION_BLOCK}\n")
|
||
|
set(PREFIX)
|
||
|
set(MACRO_DEC)
|
||
|
set(MACRO_HEX)
|
||
|
|
||
|
set(pp_if "elif")
|
||
|
foreach(feature ${${_lang}_features})
|
||
|
string(TOUPPER ${feature} feature_upper)
|
||
|
set(feature_PP "COMPILER_${feature_upper}")
|
||
|
set(_define_item "\n# define ${prefix_arg}_${feature_PP} 0\n")
|
||
|
if (_cmake_feature_test_${compiler}_${feature} STREQUAL "1")
|
||
|
set(_define_item "\n# define ${prefix_arg}_${feature_PP} 1\n")
|
||
|
elseif (_cmake_feature_test_${compiler}_${feature})
|
||
|
set(_define_item "\n# define ${prefix_arg}_${feature_PP} 0\n")
|
||
|
set(_define_item "\n# if ${_cmake_feature_test_${compiler}_${feature}}\n# define ${prefix_arg}_${feature_PP} 1\n# else${_define_item}# endif\n")
|
||
|
endif()
|
||
|
string(APPEND ${compiler_file_content} "${_define_item}")
|
||
|
endforeach()
|
||
|
endforeach()
|
||
|
if(pp_if STREQUAL "elif")
|
||
|
if(_WCD_ALLOW_UNKNOWN_COMPILERS)
|
||
|
string(APPEND file_content "
|
||
|
# endif\n")
|
||
|
else()
|
||
|
string(APPEND file_content "
|
||
|
# else
|
||
|
# error Unsupported compiler
|
||
|
# endif\n")
|
||
|
endif()
|
||
|
endif()
|
||
|
foreach(feature ${${_lang}_features})
|
||
|
string(TOUPPER ${feature} feature_upper)
|
||
|
set(feature_PP "COMPILER_${feature_upper}")
|
||
|
set(def_name ${prefix_arg}_${feature_PP})
|
||
|
_simpledefine(c_restrict RESTRICT restrict "")
|
||
|
_simpledefine(cxx_constexpr CONSTEXPR constexpr "")
|
||
|
_simpledefine(cxx_final FINAL final "")
|
||
|
_simpledefine(cxx_override OVERRIDE override "")
|
||
|
if (feature STREQUAL cxx_static_assert)
|
||
|
set(def_value "${prefix_arg}_STATIC_ASSERT(X)")
|
||
|
set(def_value_msg "${prefix_arg}_STATIC_ASSERT_MSG(X, MSG)")
|
||
|
set(def_fallback "enum { ${prefix_arg}_STATIC_ASSERT_JOIN(${prefix_arg}StaticAssertEnum, __LINE__) = sizeof(${prefix_arg}StaticAssert<X>) }")
|
||
|
string(APPEND file_content "# if defined(${def_name}) && ${def_name}
|
||
|
# define ${def_value} static_assert(X, #X)
|
||
|
# define ${def_value_msg} static_assert(X, MSG)
|
||
|
# else
|
||
|
# define ${prefix_arg}_STATIC_ASSERT_JOIN(X, Y) ${prefix_arg}_STATIC_ASSERT_JOIN_IMPL(X, Y)
|
||
|
# define ${prefix_arg}_STATIC_ASSERT_JOIN_IMPL(X, Y) X##Y
|
||
|
template<bool> struct ${prefix_arg}StaticAssert;
|
||
|
template<> struct ${prefix_arg}StaticAssert<true>{};
|
||
|
# define ${def_value} ${def_fallback}
|
||
|
# define ${def_value_msg} ${def_fallback}
|
||
|
# endif
|
||
|
\n")
|
||
|
endif()
|
||
|
if (feature STREQUAL cxx_alignas)
|
||
|
set(def_value "${prefix_arg}_ALIGNAS(X)")
|
||
|
string(APPEND file_content "
|
||
|
# if defined(${def_name}) && ${def_name}
|
||
|
# define ${def_value} alignas(X)
|
||
|
# elif ${prefix_arg}_COMPILER_IS_GNU || ${prefix_arg}_COMPILER_IS_Clang || ${prefix_arg}_COMPILER_IS_AppleClang
|
||
|
# define ${def_value} __attribute__ ((__aligned__(X)))
|
||
|
# elif ${prefix_arg}_COMPILER_IS_MSVC
|
||
|
# define ${def_value} __declspec(align(X))
|
||
|
# else
|
||
|
# define ${def_value}
|
||
|
# endif
|
||
|
\n")
|
||
|
endif()
|
||
|
if (feature STREQUAL cxx_alignof)
|
||
|
set(def_value "${prefix_arg}_ALIGNOF(X)")
|
||
|
string(APPEND file_content "
|
||
|
# if defined(${def_name}) && ${def_name}
|
||
|
# define ${def_value} alignof(X)
|
||
|
# elif ${prefix_arg}_COMPILER_IS_GNU || ${prefix_arg}_COMPILER_IS_Clang || ${prefix_arg}_COMPILER_IS_AppleClang
|
||
|
# define ${def_value} __alignof__(X)
|
||
|
# elif ${prefix_arg}_COMPILER_IS_MSVC
|
||
|
# define ${def_value} __alignof(X)
|
||
|
# endif
|
||
|
\n")
|
||
|
endif()
|
||
|
_simpledefine(cxx_deleted_functions DELETED_FUNCTION "= delete" "")
|
||
|
_simpledefine(cxx_extern_templates EXTERN_TEMPLATE extern "")
|
||
|
if (feature STREQUAL cxx_noexcept)
|
||
|
set(def_value "${prefix_arg}_NOEXCEPT")
|
||
|
string(APPEND file_content "
|
||
|
# if defined(${def_name}) && ${def_name}
|
||
|
# define ${def_value} noexcept
|
||
|
# define ${def_value}_EXPR(X) noexcept(X)
|
||
|
# else
|
||
|
# define ${def_value}
|
||
|
# define ${def_value}_EXPR(X)
|
||
|
# endif
|
||
|
\n")
|
||
|
endif()
|
||
|
if (feature STREQUAL cxx_nullptr)
|
||
|
set(def_value "${prefix_arg}_NULLPTR")
|
||
|
string(APPEND file_content "
|
||
|
# if defined(${def_name}) && ${def_name}
|
||
|
# define ${def_value} nullptr
|
||
|
# elif ${prefix_arg}_COMPILER_IS_GNU
|
||
|
# define ${def_value} __null
|
||
|
# else
|
||
|
# define ${def_value} 0
|
||
|
# endif
|
||
|
\n")
|
||
|
endif()
|
||
|
if (feature STREQUAL cxx_thread_local)
|
||
|
set(def_value "${prefix_arg}_THREAD_LOCAL")
|
||
|
string(APPEND file_content "
|
||
|
# if defined(${def_name}) && ${def_name}
|
||
|
# define ${def_value} thread_local
|
||
|
# elif ${prefix_arg}_COMPILER_IS_GNU || ${prefix_arg}_COMPILER_IS_Clang || ${prefix_arg}_COMPILER_IS_AppleClang
|
||
|
# define ${def_value} __thread
|
||
|
# elif ${prefix_arg}_COMPILER_IS_MSVC
|
||
|
# define ${def_value} __declspec(thread)
|
||
|
# else
|
||
|
// ${def_value} not defined for this configuration.
|
||
|
# endif
|
||
|
\n")
|
||
|
endif()
|
||
|
if (feature STREQUAL cxx_attribute_deprecated)
|
||
|
set(def_name ${prefix_arg}_${feature_PP})
|
||
|
set(def_value "${prefix_arg}_DEPRECATED")
|
||
|
string(APPEND file_content "
|
||
|
# ifndef ${def_value}
|
||
|
# if defined(${def_name}) && ${def_name}
|
||
|
# define ${def_value} [[deprecated]]
|
||
|
# define ${def_value}_MSG(MSG) [[deprecated(MSG)]]
|
||
|
# elif ${prefix_arg}_COMPILER_IS_GNU || ${prefix_arg}_COMPILER_IS_Clang
|
||
|
# define ${def_value} __attribute__((__deprecated__))
|
||
|
# define ${def_value}_MSG(MSG) __attribute__((__deprecated__(MSG)))
|
||
|
# elif ${prefix_arg}_COMPILER_IS_MSVC
|
||
|
# define ${def_value} __declspec(deprecated)
|
||
|
# define ${def_value}_MSG(MSG) __declspec(deprecated(MSG))
|
||
|
# else
|
||
|
# define ${def_value}
|
||
|
# define ${def_value}_MSG(MSG)
|
||
|
# endif
|
||
|
# endif
|
||
|
\n")
|
||
|
endif()
|
||
|
endforeach()
|
||
|
|
||
|
foreach(feature ${${_lang}_bare_features})
|
||
|
string(TOUPPER ${feature} feature_upper)
|
||
|
set(feature_PP "COMPILER_${feature_upper}")
|
||
|
set(def_name ${prefix_arg}_${feature_PP})
|
||
|
_simplebaredefine(c_restrict restrict "")
|
||
|
_simplebaredefine(cxx_constexpr constexpr "")
|
||
|
_simplebaredefine(cxx_final final "")
|
||
|
_simplebaredefine(cxx_override override "")
|
||
|
if (feature STREQUAL cxx_nullptr)
|
||
|
set(def_value "nullptr")
|
||
|
string(APPEND file_content "
|
||
|
# if !(defined(${def_name}) && ${def_name})
|
||
|
# if ${prefix_arg}_COMPILER_IS_GNU
|
||
|
# define ${def_value} __null
|
||
|
# else
|
||
|
# define ${def_value} 0
|
||
|
# endif
|
||
|
# endif
|
||
|
\n")
|
||
|
endif()
|
||
|
_simplebaredefine(cxx_noexcept noexcept "")
|
||
|
endforeach()
|
||
|
|
||
|
string(APPEND file_content "#endif\n")
|
||
|
|
||
|
endforeach()
|
||
|
|
||
|
if(_WCD_OUTPUT_FILES_VAR)
|
||
|
foreach(compiler ${_WCD_COMPILERS})
|
||
|
foreach(_lang ${_langs})
|
||
|
if(compiler_file_content_${compiler}_${_lang})
|
||
|
set(CMAKE_CONFIGURABLE_FILE_CONTENT "${compiler_file_content_}")
|
||
|
string(APPEND CMAKE_CONFIGURABLE_FILE_CONTENT "${compiler_file_content_${compiler}_${_lang}}")
|
||
|
|
||
|
set(compile_file_name "${_WCD_OUTPUT_DIR}${prefix_arg}_COMPILER_INFO_${compiler}_${_lang}.h")
|
||
|
set(full_path "${main_file_dir}/${compile_file_name}")
|
||
|
list(APPEND ${_WCD_OUTPUT_FILES_VAR} ${full_path})
|
||
|
configure_file("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in"
|
||
|
"${full_path}"
|
||
|
@ONLY
|
||
|
)
|
||
|
endif()
|
||
|
endforeach()
|
||
|
endforeach()
|
||
|
set(${_WCD_OUTPUT_FILES_VAR} ${${_WCD_OUTPUT_FILES_VAR}} PARENT_SCOPE)
|
||
|
endif()
|
||
|
|
||
|
if (_WCD_EPILOG)
|
||
|
string(APPEND file_content "\n${_WCD_EPILOG}\n")
|
||
|
endif()
|
||
|
string(APPEND file_content "\n#endif")
|
||
|
|
||
|
set(CMAKE_CONFIGURABLE_FILE_CONTENT ${file_content})
|
||
|
configure_file("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in"
|
||
|
"${file_arg}"
|
||
|
@ONLY
|
||
|
)
|
||
|
endfunction()
|