# Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
# file Copyright.txt or https://cmake.org/licensing for details. | |
#[=======================================================================[.rst: | |
WriteCompilerDetectionHeader | |
---------------------------- | |
.. deprecated:: 3.20 | |
This module is available only if policy :policy:`CMP0120` | |
is not set to ``NEW``. Do not use it in new code. | |
.. versionadded:: 3.1 | |
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. | |
See the :manual:`cmake-compile-features(7)` manual for information on | |
compile features. | |
.. versionadded:: 3.2 | |
Added ``MSVC`` and ``AppleClang`` compiler support. | |
.. versionadded:: 3.6 | |
Added ``Intel`` compiler support. | |
.. versionchanged:: 3.8 | |
The ``{c,cxx}_std_*`` meta-features are ignored if requested. | |
.. versionadded:: 3.8 | |
``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. | |
.. versionadded:: 3.12 | |
``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. | |
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= | |
) | |
.. _`WCDH Example Usage`: | |
Example Usage | |
============= | |
.. note:: | |
This section was migrated from the :manual:`cmake-compile-features(7)` | |
manual since it relies on the ``WriteCompilerDetectionHeader`` module | |
which is removed by policy :policy:`CMP0120`. | |
Compile features may be preferred if available, without creating a hard | |
requirement. For example, a library may provide alternative | |
implementations depending on whether the ``cxx_variadic_templates`` | |
feature is available: | |
.. code-block:: c++ | |
#if Foo_COMPILER_CXX_VARIADIC_TEMPLATES | |
template<int I, int... Is> | |
struct Interface; | |
template<int I> | |
struct Interface<I> | |
{ | |
static int accumulate() | |
{ | |
return I; | |
} | |
}; | |
template<int I, int... Is> | |
struct Interface | |
{ | |
static int accumulate() | |
{ | |
return I + Interface<Is...>::accumulate(); | |
} | |
}; | |
#else | |
template<int I1, int I2 = 0, int I3 = 0, int I4 = 0> | |
struct Interface | |
{ | |
static int accumulate() { return I1 + I2 + I3 + I4; } | |
}; | |
#endif | |
Such an interface depends on using the correct preprocessor defines for the | |
compiler features. CMake can generate a header file containing such | |
defines using the :module:`WriteCompilerDetectionHeader` module. The | |
module contains the ``write_compiler_detection_header`` function which | |
accepts parameters to control the content of the generated header file: | |
.. code-block:: cmake | |
write_compiler_detection_header( | |
FILE "${CMAKE_CURRENT_BINARY_DIR}/foo_compiler_detection.h" | |
PREFIX Foo | |
COMPILERS GNU | |
FEATURES | |
cxx_variadic_templates | |
) | |
Such a header file may be used internally in the source code of a project, | |
and it may be installed and used in the interface of library code. | |
For each feature listed in ``FEATURES``, a preprocessor definition | |
is created in the header file, and defined to either ``1`` or ``0``. | |
Additionally, some features call for additional defines, such as the | |
``cxx_final`` and ``cxx_override`` features. Rather than being used in | |
``#ifdef`` code, the ``final`` keyword is abstracted by a symbol | |
which is defined to either ``final``, a compiler-specific equivalent, or | |
to empty. That way, C++ code can be written to unconditionally use the | |
symbol, and compiler support determines what it is expanded to: | |
.. code-block:: c++ | |
struct Interface { | |
virtual void Execute() = 0; | |
}; | |
struct Concrete Foo_FINAL { | |
void Execute() Foo_OVERRIDE; | |
}; | |
In this case, ``Foo_FINAL`` will expand to ``final`` if the | |
compiler supports the keyword, or to empty otherwise. | |
In this use-case, the project code may wish to enable a particular language | |
standard if available from the compiler. The :prop_tgt:`CXX_STANDARD` | |
target property may be set to the desired language standard for a particular | |
target, and the :variable:`CMAKE_CXX_STANDARD` variable may be set to | |
influence all following targets: | |
.. code-block:: cmake | |
write_compiler_detection_header( | |
FILE "${CMAKE_CURRENT_BINARY_DIR}/foo_compiler_detection.h" | |
PREFIX Foo | |
COMPILERS GNU | |
FEATURES | |
cxx_final cxx_override | |
) | |
# Includes foo_compiler_detection.h and uses the Foo_FINAL symbol | |
# which will expand to 'final' if the compiler supports the requested | |
# CXX_STANDARD. | |
add_library(foo foo.cpp) | |
set_property(TARGET foo PROPERTY CXX_STANDARD 11) | |
# Includes foo_compiler_detection.h and uses the Foo_FINAL symbol | |
# which will expand to 'final' if the compiler supports the feature, | |
# even though CXX_STANDARD is not set explicitly. The requirement of | |
# cxx_constexpr causes CMake to set CXX_STANDARD internally, which | |
# affects the compile flags. | |
add_library(foo_impl foo_impl.cpp) | |
target_compile_features(foo_impl PRIVATE cxx_constexpr) | |
The ``write_compiler_detection_header`` function also creates compatibility | |
code for other features which have standard equivalents. For example, the | |
``cxx_static_assert`` feature is emulated with a template and abstracted | |
via the ``<PREFIX>_STATIC_ASSERT`` and ``<PREFIX>_STATIC_ASSERT_MSG`` | |
function-macros. | |
#]=======================================================================] | |
# Guard against inclusion by absolute path. | |
cmake_policy(GET CMP0120 _WCDH_policy) | |
if(_WCDH_policy STREQUAL "NEW") | |
message(FATAL_ERROR "The WriteCompilerDetectionHeader module has been removed by policy CMP0120.") | |
elseif(_WCDH_policy STREQUAL "") | |
message(AUTHOR_WARNING | |
"The WriteCompilerDetectionHeader module will be removed by policy CMP0120. " | |
"Projects should be ported away from the module, perhaps by bundling a copy " | |
"of the generated header or using a third-party alternative." | |
) | |
endif() | |
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() |