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:
5WriteCompilerDetectionHeader
6----------------------------
7
8.. deprecated:: 3.20
9  This module is available only if policy :policy:`CMP0120`
10  is not set to ``NEW``.  Do not use it in new code.
11
12.. versionadded:: 3.1
13
14This module provides the function ``write_compiler_detection_header()``.
15
16This function can be used to generate a file suitable for preprocessor
17inclusion which contains macros to be used in source code::
18
19   write_compiler_detection_header(
20             FILE <file>
21             PREFIX <prefix>
22             [OUTPUT_FILES_VAR <output_files_var> OUTPUT_DIR <output_dir>]
23             COMPILERS <compiler> [...]
24             FEATURES <feature> [...]
25             [BARE_FEATURES <feature> [...]]
26             [VERSION <version>]
27             [PROLOG <prolog>]
28             [EPILOG <epilog>]
29             [ALLOW_UNKNOWN_COMPILERS]
30             [ALLOW_UNKNOWN_COMPILER_VERSIONS]
31   )
32
33This generates the file ``<file>`` with macros which all have the prefix
34``<prefix>``.
35
36By default, all content is written directly to the ``<file>``.  The
37``OUTPUT_FILES_VAR`` may be specified to cause the compiler-specific
38content to be written to separate files.  The separate files are then
39available in the ``<output_files_var>`` and may be consumed by the caller
40for installation for example.  The ``OUTPUT_DIR`` specifies a relative
41path from the main ``<file>`` to the compiler-specific files. For example:
42
43.. code-block:: cmake
44
45   write_compiler_detection_header(
46     FILE climbingstats_compiler_detection.h
47     PREFIX ClimbingStats
48     OUTPUT_FILES_VAR support_files
49     OUTPUT_DIR compilers
50     COMPILERS GNU Clang MSVC Intel
51     FEATURES cxx_variadic_templates
52   )
53   install(FILES
54     ${CMAKE_CURRENT_BINARY_DIR}/climbingstats_compiler_detection.h
55     DESTINATION include
56   )
57   install(FILES
58     ${support_files}
59     DESTINATION include/compilers
60   )
61
62
63``VERSION`` may be used to specify the API version to be generated.
64Future versions of CMake may introduce alternative APIs.  A given
65API is selected by any ``<version>`` value greater than or equal
66to the version of CMake that introduced the given API and less
67than the version of CMake that introduced its succeeding API.
68The value of the :variable:`CMAKE_MINIMUM_REQUIRED_VERSION`
69variable is used if no explicit version is specified.
70(As of CMake version |release| there is only one API version.)
71
72``PROLOG`` may be specified as text content to write at the start of the
73header. ``EPILOG`` may be specified as text content to write at the end
74of the header
75
76At least one ``<compiler>`` and one ``<feature>`` must be listed.  Compilers
77which are known to CMake, but not specified are detected and a preprocessor
78``#error`` is generated for them.  A preprocessor macro matching
79``<PREFIX>_COMPILER_IS_<compiler>`` is generated for each compiler
80known to CMake to contain the value ``0`` or ``1``.
81
82Possible compiler identifiers are documented with the
83:variable:`CMAKE_<LANG>_COMPILER_ID` variable.
84Available features in this version of CMake are listed in the
85:prop_gbl:`CMAKE_C_KNOWN_FEATURES` and
86:prop_gbl:`CMAKE_CXX_KNOWN_FEATURES` global properties.
87See the :manual:`cmake-compile-features(7)` manual for information on
88compile features.
89
90.. versionadded:: 3.2
91  Added ``MSVC`` and ``AppleClang`` compiler support.
92
93.. versionadded:: 3.6
94  Added ``Intel`` compiler support.
95
96.. versionchanged:: 3.8
97  The ``{c,cxx}_std_*`` meta-features are ignored if requested.
98
99.. versionadded:: 3.8
100  ``ALLOW_UNKNOWN_COMPILERS`` and ``ALLOW_UNKNOWN_COMPILER_VERSIONS`` cause
101  the module to generate conditions that treat unknown compilers as simply
102  lacking all features.  Without these options the default behavior is to
103  generate a ``#error`` for unknown compilers and versions.
104
105.. versionadded:: 3.12
106  ``BARE_FEATURES`` will define the compatibility macros with the name used in
107  newer versions of the language standard, so the code can use the new feature
108  name unconditionally.
109
110Feature Test Macros
111===================
112
113For each compiler, a preprocessor macro is generated matching
114``<PREFIX>_COMPILER_IS_<compiler>`` which has the content either ``0``
115or ``1``, depending on the compiler in use. Preprocessor macros for
116compiler version components are generated matching
117``<PREFIX>_COMPILER_VERSION_MAJOR`` ``<PREFIX>_COMPILER_VERSION_MINOR``
118and ``<PREFIX>_COMPILER_VERSION_PATCH`` containing decimal values
119for the corresponding compiler version components, if defined.
120
121A preprocessor test is generated based on the compiler version
122denoting whether each feature is enabled.  A preprocessor macro
123matching ``<PREFIX>_COMPILER_<FEATURE>``, where ``<FEATURE>`` is the
124upper-case ``<feature>`` name, is generated to contain the value
125``0`` or ``1`` depending on whether the compiler in use supports the
126feature:
127
128.. code-block:: cmake
129
130   write_compiler_detection_header(
131     FILE climbingstats_compiler_detection.h
132     PREFIX ClimbingStats
133     COMPILERS GNU Clang AppleClang MSVC Intel
134     FEATURES cxx_variadic_templates
135   )
136
137.. code-block:: c++
138
139   #if ClimbingStats_COMPILER_CXX_VARIADIC_TEMPLATES
140   template<typename... T>
141   void someInterface(T t...) { /* ... */ }
142   #else
143   // Compatibility versions
144   template<typename T1>
145   void someInterface(T1 t1) { /* ... */ }
146   template<typename T1, typename T2>
147   void someInterface(T1 t1, T2 t2) { /* ... */ }
148   template<typename T1, typename T2, typename T3>
149   void someInterface(T1 t1, T2 t2, T3 t3) { /* ... */ }
150   #endif
151
152Symbol Macros
153=============
154
155Some additional symbol-defines are created for particular features for
156use as symbols which may be conditionally defined empty:
157
158.. code-block:: c++
159
160   class MyClass ClimbingStats_FINAL
161   {
162       ClimbingStats_CONSTEXPR int someInterface() { return 42; }
163   };
164
165The ``ClimbingStats_FINAL`` macro will expand to ``final`` if the
166compiler (and its flags) support the ``cxx_final`` feature, and the
167``ClimbingStats_CONSTEXPR`` macro will expand to ``constexpr``
168if ``cxx_constexpr`` is supported.
169
170If ``BARE_FEATURES cxx_final`` was given as argument the ``final`` keyword
171will be defined for old compilers, too.
172
173The following features generate corresponding symbol defines and if they
174are available as ``BARE_FEATURES``:
175
176========================== =================================== ================= ======
177        Feature                          Define                      Symbol       bare
178========================== =================================== ================= ======
179``c_restrict``              ``<PREFIX>_RESTRICT``               ``restrict``      yes
180``cxx_constexpr``           ``<PREFIX>_CONSTEXPR``              ``constexpr``     yes
181``cxx_deleted_functions``   ``<PREFIX>_DELETED_FUNCTION``       ``= delete``
182``cxx_extern_templates``    ``<PREFIX>_EXTERN_TEMPLATE``        ``extern``
183``cxx_final``               ``<PREFIX>_FINAL``                  ``final``         yes
184``cxx_noexcept``            ``<PREFIX>_NOEXCEPT``               ``noexcept``      yes
185``cxx_noexcept``            ``<PREFIX>_NOEXCEPT_EXPR(X)``       ``noexcept(X)``
186``cxx_override``            ``<PREFIX>_OVERRIDE``               ``override``      yes
187========================== =================================== ================= ======
188
189Compatibility Implementation Macros
190===================================
191
192Some features are suitable for wrapping in a macro with a backward
193compatibility implementation if the compiler does not support the feature.
194
195When the ``cxx_static_assert`` feature is not provided by the compiler,
196a compatibility implementation is available via the
197``<PREFIX>_STATIC_ASSERT(COND)`` and
198``<PREFIX>_STATIC_ASSERT_MSG(COND, MSG)`` function-like macros. The macros
199expand to ``static_assert`` where that compiler feature is available, and
200to a compatibility implementation otherwise. In the first form, the
201condition is stringified in the message field of ``static_assert``.  In
202the second form, the message ``MSG`` is passed to the message field of
203``static_assert``, or ignored if using the backward compatibility
204implementation.
205
206The ``cxx_attribute_deprecated`` feature provides a macro definition
207``<PREFIX>_DEPRECATED``, which expands to either the standard
208``[[deprecated]]`` attribute or a compiler-specific decorator such
209as ``__attribute__((__deprecated__))`` used by GNU compilers.
210
211The ``cxx_alignas`` feature provides a macro definition
212``<PREFIX>_ALIGNAS`` which expands to either the standard ``alignas``
213decorator or a compiler-specific decorator such as
214``__attribute__ ((__aligned__))`` used by GNU compilers.
215
216The ``cxx_alignof`` feature provides a macro definition
217``<PREFIX>_ALIGNOF`` which expands to either the standard ``alignof``
218decorator or a compiler-specific decorator such as ``__alignof__``
219used by GNU compilers.
220
221============================= ================================ ===================== ======
222          Feature                          Define                     Symbol          bare
223============================= ================================ ===================== ======
224``cxx_alignas``                ``<PREFIX>_ALIGNAS``             ``alignas``
225``cxx_alignof``                ``<PREFIX>_ALIGNOF``             ``alignof``
226``cxx_nullptr``                ``<PREFIX>_NULLPTR``             ``nullptr``           yes
227``cxx_static_assert``          ``<PREFIX>_STATIC_ASSERT``       ``static_assert``
228``cxx_static_assert``          ``<PREFIX>_STATIC_ASSERT_MSG``   ``static_assert``
229``cxx_attribute_deprecated``   ``<PREFIX>_DEPRECATED``          ``[[deprecated]]``
230``cxx_attribute_deprecated``   ``<PREFIX>_DEPRECATED_MSG``      ``[[deprecated]]``
231``cxx_thread_local``           ``<PREFIX>_THREAD_LOCAL``        ``thread_local``
232============================= ================================ ===================== ======
233
234A use-case which arises with such deprecation macros is the deprecation
235of an entire library.  In that case, all public API in the library may
236be decorated with the ``<PREFIX>_DEPRECATED`` macro.  This results in
237very noisy build output when building the library itself, so the macro
238may be may be defined to empty in that case when building the deprecated
239library:
240
241.. code-block:: cmake
242
243  add_library(compat_support ${srcs})
244  target_compile_definitions(compat_support
245    PRIVATE
246      CompatSupport_DEPRECATED=
247  )
248
249.. _`WCDH Example Usage`:
250
251Example Usage
252=============
253
254.. note::
255
256  This section was migrated from the :manual:`cmake-compile-features(7)`
257  manual since it relies on the ``WriteCompilerDetectionHeader`` module
258  which is removed by policy :policy:`CMP0120`.
259
260Compile features may be preferred if available, without creating a hard
261requirement.  For example, a library may provide alternative
262implementations depending on whether the ``cxx_variadic_templates``
263feature is available:
264
265.. code-block:: c++
266
267  #if Foo_COMPILER_CXX_VARIADIC_TEMPLATES
268  template<int I, int... Is>
269  struct Interface;
270
271  template<int I>
272  struct Interface<I>
273  {
274    static int accumulate()
275    {
276      return I;
277    }
278  };
279
280  template<int I, int... Is>
281  struct Interface
282  {
283    static int accumulate()
284    {
285      return I + Interface<Is...>::accumulate();
286    }
287  };
288  #else
289  template<int I1, int I2 = 0, int I3 = 0, int I4 = 0>
290  struct Interface
291  {
292    static int accumulate() { return I1 + I2 + I3 + I4; }
293  };
294  #endif
295
296Such an interface depends on using the correct preprocessor defines for the
297compiler features.  CMake can generate a header file containing such
298defines using the :module:`WriteCompilerDetectionHeader` module.  The
299module contains the ``write_compiler_detection_header`` function which
300accepts parameters to control the content of the generated header file:
301
302.. code-block:: cmake
303
304  write_compiler_detection_header(
305    FILE "${CMAKE_CURRENT_BINARY_DIR}/foo_compiler_detection.h"
306    PREFIX Foo
307    COMPILERS GNU
308    FEATURES
309      cxx_variadic_templates
310  )
311
312Such a header file may be used internally in the source code of a project,
313and it may be installed and used in the interface of library code.
314
315For each feature listed in ``FEATURES``, a preprocessor definition
316is created in the header file, and defined to either ``1`` or ``0``.
317
318Additionally, some features call for additional defines, such as the
319``cxx_final`` and ``cxx_override`` features. Rather than being used in
320``#ifdef`` code, the ``final`` keyword is abstracted by a symbol
321which is defined to either ``final``, a compiler-specific equivalent, or
322to empty.  That way, C++ code can be written to unconditionally use the
323symbol, and compiler support determines what it is expanded to:
324
325.. code-block:: c++
326
327  struct Interface {
328    virtual void Execute() = 0;
329  };
330
331  struct Concrete Foo_FINAL {
332    void Execute() Foo_OVERRIDE;
333  };
334
335In this case, ``Foo_FINAL`` will expand to ``final`` if the
336compiler supports the keyword, or to empty otherwise.
337
338In this use-case, the project code may wish to enable a particular language
339standard if available from the compiler. The :prop_tgt:`CXX_STANDARD`
340target property may be set to the desired language standard for a particular
341target, and the :variable:`CMAKE_CXX_STANDARD` variable may be set to
342influence all following targets:
343
344.. code-block:: cmake
345
346  write_compiler_detection_header(
347    FILE "${CMAKE_CURRENT_BINARY_DIR}/foo_compiler_detection.h"
348    PREFIX Foo
349    COMPILERS GNU
350    FEATURES
351      cxx_final cxx_override
352  )
353
354  # Includes foo_compiler_detection.h and uses the Foo_FINAL symbol
355  # which will expand to 'final' if the compiler supports the requested
356  # CXX_STANDARD.
357  add_library(foo foo.cpp)
358  set_property(TARGET foo PROPERTY CXX_STANDARD 11)
359
360  # Includes foo_compiler_detection.h and uses the Foo_FINAL symbol
361  # which will expand to 'final' if the compiler supports the feature,
362  # even though CXX_STANDARD is not set explicitly.  The requirement of
363  # cxx_constexpr causes CMake to set CXX_STANDARD internally, which
364  # affects the compile flags.
365  add_library(foo_impl foo_impl.cpp)
366  target_compile_features(foo_impl PRIVATE cxx_constexpr)
367
368The ``write_compiler_detection_header`` function also creates compatibility
369code for other features which have standard equivalents.  For example, the
370``cxx_static_assert`` feature is emulated with a template and abstracted
371via the ``<PREFIX>_STATIC_ASSERT`` and ``<PREFIX>_STATIC_ASSERT_MSG``
372function-macros.
373#]=======================================================================]
374
375# Guard against inclusion by absolute path.
376cmake_policy(GET CMP0120 _WCDH_policy)
377if(_WCDH_policy STREQUAL "NEW")
378  message(FATAL_ERROR "The WriteCompilerDetectionHeader module has been removed by policy CMP0120.")
379elseif(_WCDH_policy STREQUAL "")
380  message(AUTHOR_WARNING
381    "The WriteCompilerDetectionHeader module will be removed by policy CMP0120.  "
382    "Projects should be ported away from the module, perhaps by bundling a copy "
383    "of the generated header or using a third-party alternative."
384    )
385endif()
386
387include(${CMAKE_CURRENT_LIST_DIR}/CMakeCompilerIdDetection.cmake)
388
389function(_load_compiler_variables CompilerId lang)
390  include("${CMAKE_ROOT}/Modules/Compiler/${CompilerId}-${lang}-FeatureTests.cmake" OPTIONAL)
391  set(_cmake_oldestSupported_${CompilerId} ${_cmake_oldestSupported} PARENT_SCOPE)
392  foreach(feature ${ARGN})
393    set(_cmake_feature_test_${CompilerId}_${feature} ${_cmake_feature_test_${feature}} PARENT_SCOPE)
394  endforeach()
395  include("${CMAKE_ROOT}/Modules/Compiler/${CompilerId}-${lang}-DetermineCompiler.cmake" OPTIONAL
396      RESULT_VARIABLE determinedCompiler)
397  if (NOT determinedCompiler)
398    include("${CMAKE_ROOT}/Modules/Compiler/${CompilerId}-DetermineCompiler.cmake" OPTIONAL)
399  endif()
400  set(_compiler_id_version_compute_${CompilerId} ${_compiler_id_version_compute} PARENT_SCOPE)
401endfunction()
402
403macro(_simpledefine FEATURE_NAME FEATURE_TESTNAME FEATURE_STRING FEATURE_DEFAULT_STRING)
404  if (feature STREQUAL "${FEATURE_NAME}")
405        set(def_value "${prefix_arg}_${FEATURE_TESTNAME}")
406        string(APPEND file_content "
407#  if defined(${def_name}) && ${def_name}
408#    define ${def_value} ${FEATURE_STRING}
409#  else
410#    define ${def_value} ${FEATURE_DEFAULT_STRING}
411#  endif
412\n")
413  endif()
414endmacro()
415
416macro(_simplebaredefine FEATURE_NAME FEATURE_STRING FEATURE_DEFAULT_STRING)
417  if (feature STREQUAL "${FEATURE_NAME}")
418        string(APPEND file_content "
419#  if !(defined(${def_name}) && ${def_name})
420#    define ${FEATURE_STRING} ${FEATURE_DEFAULT_STRING}
421#  endif
422\n")
423  endif()
424endmacro()
425
426function(_check_feature_lists C_FEATURE_VAR CXX_FEATURE_VAR)
427  foreach(feature ${ARGN})
428    if (feature MATCHES "^c_std_")
429      # ignored
430    elseif (feature MATCHES "^cxx_std_")
431      # ignored
432    elseif (feature MATCHES "^cxx_")
433      list(APPEND _langs CXX)
434      list(APPEND ${CXX_FEATURE_VAR} ${feature})
435    elseif (feature MATCHES "^c_")
436      list(APPEND _langs C)
437      list(APPEND ${C_FEATURE_VAR} ${feature})
438    else()
439      message(FATAL_ERROR "Unsupported feature ${feature}.")
440    endif()
441  endforeach()
442  set(${C_FEATURE_VAR} ${${C_FEATURE_VAR}} PARENT_SCOPE)
443  set(${CXX_FEATURE_VAR} ${${CXX_FEATURE_VAR}} PARENT_SCOPE)
444  set(_langs ${_langs} PARENT_SCOPE)
445endfunction()
446
447function(write_compiler_detection_header
448    file_keyword file_arg
449    prefix_keyword prefix_arg
450    )
451  if (NOT "x${file_keyword}" STREQUAL "xFILE")
452    message(FATAL_ERROR "write_compiler_detection_header: FILE parameter missing.")
453  endif()
454  if (NOT "x${prefix_keyword}" STREQUAL "xPREFIX")
455    message(FATAL_ERROR "write_compiler_detection_header: PREFIX parameter missing.")
456  endif()
457  set(options ALLOW_UNKNOWN_COMPILERS ALLOW_UNKNOWN_COMPILER_VERSIONS)
458  set(oneValueArgs VERSION EPILOG PROLOG OUTPUT_FILES_VAR OUTPUT_DIR)
459  set(multiValueArgs COMPILERS FEATURES BARE_FEATURES)
460  cmake_parse_arguments(_WCD "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
461
462  if (NOT _WCD_COMPILERS)
463    message(FATAL_ERROR "Invalid arguments.  write_compiler_detection_header requires at least one compiler.")
464  endif()
465  if (NOT _WCD_FEATURES AND NOT _WCD_BARE_FEATURES)
466    message(FATAL_ERROR "Invalid arguments.  write_compiler_detection_header requires at least one feature.")
467  endif()
468
469  if(_WCD_UNPARSED_ARGUMENTS)
470    message(FATAL_ERROR "Unparsed arguments: ${_WCD_UNPARSED_ARGUMENTS}")
471  endif()
472
473  if (prefix_arg STREQUAL "")
474    message(FATAL_ERROR "A prefix must be specified")
475  endif()
476  string(MAKE_C_IDENTIFIER ${prefix_arg} cleaned_prefix)
477  if (NOT prefix_arg STREQUAL cleaned_prefix)
478    message(FATAL_ERROR "The prefix must be a valid C identifier.")
479  endif()
480
481  if(NOT _WCD_VERSION)
482    set(_WCD_VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
483  endif()
484  set(_min_version 3.1.0) # Version which introduced this function
485  if (_WCD_VERSION VERSION_LESS _min_version)
486    set(err "VERSION compatibility for write_compiler_detection_header is set to ${_WCD_VERSION}, which is too low.")
487    string(APPEND err "  It must be set to at least ${_min_version}.  ")
488    string(APPEND err "  Either set the VERSION parameter to the write_compiler_detection_header function, or update")
489    string(APPEND err " your minimum required CMake version with the cmake_minimum_required command.")
490    message(FATAL_ERROR "${err}")
491  endif()
492
493  if(_WCD_OUTPUT_FILES_VAR)
494    if(NOT _WCD_OUTPUT_DIR)
495      message(FATAL_ERROR "If OUTPUT_FILES_VAR is specified, then OUTPUT_DIR must also be specified.")
496    endif()
497  endif()
498  if(_WCD_OUTPUT_DIR)
499    if(NOT _WCD_OUTPUT_FILES_VAR)
500      message(FATAL_ERROR "If OUTPUT_DIR is specified, then OUTPUT_FILES_VAR must also be specified.")
501    endif()
502    get_filename_component(main_file_dir ${file_arg} DIRECTORY)
503    if (NOT IS_ABSOLUTE ${main_file_dir})
504      set(main_file_dir "${CMAKE_CURRENT_BINARY_DIR}/${main_file_dir}")
505    endif()
506    if (NOT IS_ABSOLUTE ${_WCD_OUTPUT_DIR})
507      set(_WCD_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/${_WCD_OUTPUT_DIR}")
508    endif()
509    get_filename_component(out_file_dir ${_WCD_OUTPUT_DIR} ABSOLUTE)
510    string(FIND ${out_file_dir} ${main_file_dir} idx)
511    if (NOT idx EQUAL 0)
512      message(FATAL_ERROR "The compiler-specific output directory must be within the same directory as the main file.")
513    endif()
514
515    if (main_file_dir STREQUAL out_file_dir)
516      unset(_WCD_OUTPUT_DIR)
517    else()
518      string(REPLACE "${main_file_dir}/" "" _WCD_OUTPUT_DIR "${out_file_dir}/")
519    endif()
520  endif()
521
522  set(compilers
523    GNU
524    Clang
525    AppleClang
526    MSVC
527    SunPro
528    Intel
529  )
530
531  set(_hex_compilers ADSP Borland Embarcadero SunPro)
532
533  foreach(_comp ${_WCD_COMPILERS})
534    list(FIND compilers ${_comp} idx)
535    if (idx EQUAL -1)
536      message(FATAL_ERROR "Unsupported compiler ${_comp}.")
537    endif()
538    if (NOT _need_hex_conversion)
539      list(FIND _hex_compilers ${_comp} idx)
540      if (NOT idx EQUAL -1)
541        set(_need_hex_conversion TRUE)
542      endif()
543    endif()
544  endforeach()
545
546  set(file_content "
547// This is a generated file. Do not edit!
548
549#ifndef ${prefix_arg}_COMPILER_DETECTION_H
550#define ${prefix_arg}_COMPILER_DETECTION_H
551")
552
553  if (_WCD_PROLOG)
554    string(APPEND file_content "\n${_WCD_PROLOG}\n")
555  endif()
556
557  if (_need_hex_conversion)
558    string(APPEND file_content "
559#define ${prefix_arg}_DEC(X) (X)
560#define ${prefix_arg}_HEX(X) ( \\
561    ((X)>>28 & 0xF) * 10000000 + \\
562    ((X)>>24 & 0xF) *  1000000 + \\
563    ((X)>>20 & 0xF) *   100000 + \\
564    ((X)>>16 & 0xF) *    10000 + \\
565    ((X)>>12 & 0xF) *     1000 + \\
566    ((X)>>8  & 0xF) *      100 + \\
567    ((X)>>4  & 0xF) *       10 + \\
568    ((X)     & 0xF) \\
569    )\n")
570  endif()
571
572  _check_feature_lists(C_features CXX_features ${_WCD_FEATURES})
573  _check_feature_lists(C_bare_features CXX_bare_features ${_WCD_BARE_FEATURES})
574  list(REMOVE_DUPLICATES _langs)
575
576  if(_WCD_OUTPUT_FILES_VAR)
577    get_filename_component(main_file_name ${file_arg} NAME)
578    set(compiler_file_content_
579"#ifndef ${prefix_arg}_COMPILER_DETECTION_H
580#  error This file may only be included from ${main_file_name}
581#endif\n")
582  endif()
583
584  foreach(_lang ${_langs})
585    set(target_compilers)
586    foreach(compiler ${_WCD_COMPILERS})
587      _load_compiler_variables(${compiler} ${_lang} ${${_lang}_features})
588      if(_cmake_oldestSupported_${compiler})
589        list(APPEND target_compilers ${compiler})
590      endif()
591    endforeach()
592
593    get_property(known_features GLOBAL PROPERTY CMAKE_${_lang}_KNOWN_FEATURES)
594    foreach(feature ${${_lang}_features})
595      list(FIND known_features ${feature} idx)
596      if (idx EQUAL -1)
597        message(FATAL_ERROR "Unsupported feature ${feature}.")
598      endif()
599    endforeach()
600
601    if(_lang STREQUAL CXX)
602      string(APPEND file_content "\n#ifdef __cplusplus\n")
603    else()
604      string(APPEND file_content "\n#ifndef __cplusplus\n")
605    endif()
606
607    compiler_id_detection(ID_CONTENT ${_lang} PREFIX ${prefix_arg}_
608      ID_DEFINE
609    )
610
611    string(APPEND file_content "${ID_CONTENT}\n")
612
613    set(pp_if "if")
614    foreach(compiler ${target_compilers})
615      string(APPEND file_content "\n#  ${pp_if} ${prefix_arg}_COMPILER_IS_${compiler}\n")
616
617      if(_WCD_OUTPUT_FILES_VAR)
618        set(compile_file_name "${_WCD_OUTPUT_DIR}${prefix_arg}_COMPILER_INFO_${compiler}_${_lang}.h")
619        string(APPEND file_content "\n#    include \"${compile_file_name}\"\n")
620      endif()
621
622      if(_WCD_OUTPUT_FILES_VAR)
623        set(compiler_file_content compiler_file_content_${compiler}_${_lang})
624      else()
625        set(compiler_file_content file_content)
626      endif()
627
628      if(NOT _WCD_ALLOW_UNKNOWN_COMPILER_VERSIONS)
629        string(APPEND ${compiler_file_content} "
630#    if !(${_cmake_oldestSupported_${compiler}})
631#      error Unsupported compiler version
632#    endif\n")
633      endif()
634
635      set(PREFIX ${prefix_arg}_)
636      if (_need_hex_conversion)
637        set(MACRO_DEC ${prefix_arg}_DEC)
638        set(MACRO_HEX ${prefix_arg}_HEX)
639      else()
640        set(MACRO_DEC)
641        set(MACRO_HEX)
642      endif()
643      string(CONFIGURE "${_compiler_id_version_compute_${compiler}}" VERSION_BLOCK @ONLY)
644      string(APPEND ${compiler_file_content} "${VERSION_BLOCK}\n")
645      set(PREFIX)
646      set(MACRO_DEC)
647      set(MACRO_HEX)
648
649      set(pp_if "elif")
650      foreach(feature ${${_lang}_features})
651        string(TOUPPER ${feature} feature_upper)
652        set(feature_PP "COMPILER_${feature_upper}")
653        set(_define_item "\n#    define ${prefix_arg}_${feature_PP} 0\n")
654        if (_cmake_feature_test_${compiler}_${feature} STREQUAL "1")
655          set(_define_item "\n#    define ${prefix_arg}_${feature_PP} 1\n")
656        elseif (_cmake_feature_test_${compiler}_${feature})
657          set(_define_item "\n#      define ${prefix_arg}_${feature_PP} 0\n")
658          set(_define_item "\n#    if ${_cmake_feature_test_${compiler}_${feature}}\n#      define ${prefix_arg}_${feature_PP} 1\n#    else${_define_item}#    endif\n")
659        endif()
660        string(APPEND ${compiler_file_content} "${_define_item}")
661      endforeach()
662    endforeach()
663    if(pp_if STREQUAL "elif")
664      if(_WCD_ALLOW_UNKNOWN_COMPILERS)
665        string(APPEND file_content "
666#  endif\n")
667      else()
668        string(APPEND file_content "
669#  else
670#    error Unsupported compiler
671#  endif\n")
672      endif()
673    endif()
674    foreach(feature ${${_lang}_features})
675      string(TOUPPER ${feature} feature_upper)
676      set(feature_PP "COMPILER_${feature_upper}")
677      set(def_name ${prefix_arg}_${feature_PP})
678      _simpledefine(c_restrict RESTRICT restrict "")
679      _simpledefine(cxx_constexpr CONSTEXPR constexpr "")
680      _simpledefine(cxx_final FINAL final "")
681      _simpledefine(cxx_override OVERRIDE override "")
682      if (feature STREQUAL cxx_static_assert)
683        set(def_value "${prefix_arg}_STATIC_ASSERT(X)")
684        set(def_value_msg "${prefix_arg}_STATIC_ASSERT_MSG(X, MSG)")
685        set(def_fallback "enum { ${prefix_arg}_STATIC_ASSERT_JOIN(${prefix_arg}StaticAssertEnum, __LINE__) = sizeof(${prefix_arg}StaticAssert<X>) }")
686        string(APPEND file_content "#  if defined(${def_name}) && ${def_name}
687#    define ${def_value} static_assert(X, #X)
688#    define ${def_value_msg} static_assert(X, MSG)
689#  else
690#    define ${prefix_arg}_STATIC_ASSERT_JOIN(X, Y) ${prefix_arg}_STATIC_ASSERT_JOIN_IMPL(X, Y)
691#    define ${prefix_arg}_STATIC_ASSERT_JOIN_IMPL(X, Y) X##Y
692template<bool> struct ${prefix_arg}StaticAssert;
693template<> struct ${prefix_arg}StaticAssert<true>{};
694#    define ${def_value} ${def_fallback}
695#    define ${def_value_msg} ${def_fallback}
696#  endif
697\n")
698      endif()
699      if (feature STREQUAL cxx_alignas)
700        set(def_value "${prefix_arg}_ALIGNAS(X)")
701        string(APPEND file_content "
702#  if defined(${def_name}) && ${def_name}
703#    define ${def_value} alignas(X)
704#  elif ${prefix_arg}_COMPILER_IS_GNU || ${prefix_arg}_COMPILER_IS_Clang || ${prefix_arg}_COMPILER_IS_AppleClang
705#    define ${def_value} __attribute__ ((__aligned__(X)))
706#  elif ${prefix_arg}_COMPILER_IS_MSVC
707#    define ${def_value} __declspec(align(X))
708#  else
709#    define ${def_value}
710#  endif
711\n")
712      endif()
713      if (feature STREQUAL cxx_alignof)
714        set(def_value "${prefix_arg}_ALIGNOF(X)")
715        string(APPEND file_content "
716#  if defined(${def_name}) && ${def_name}
717#    define ${def_value} alignof(X)
718#  elif ${prefix_arg}_COMPILER_IS_GNU || ${prefix_arg}_COMPILER_IS_Clang || ${prefix_arg}_COMPILER_IS_AppleClang
719#    define ${def_value} __alignof__(X)
720#  elif ${prefix_arg}_COMPILER_IS_MSVC
721#    define ${def_value} __alignof(X)
722#  endif
723\n")
724      endif()
725      _simpledefine(cxx_deleted_functions DELETED_FUNCTION "= delete" "")
726      _simpledefine(cxx_extern_templates EXTERN_TEMPLATE extern "")
727      if (feature STREQUAL cxx_noexcept)
728        set(def_value "${prefix_arg}_NOEXCEPT")
729        string(APPEND file_content "
730#  if defined(${def_name}) && ${def_name}
731#    define ${def_value} noexcept
732#    define ${def_value}_EXPR(X) noexcept(X)
733#  else
734#    define ${def_value}
735#    define ${def_value}_EXPR(X)
736#  endif
737\n")
738      endif()
739      if (feature STREQUAL cxx_nullptr)
740        set(def_value "${prefix_arg}_NULLPTR")
741        string(APPEND file_content "
742#  if defined(${def_name}) && ${def_name}
743#    define ${def_value} nullptr
744#  elif ${prefix_arg}_COMPILER_IS_GNU
745#    define ${def_value} __null
746#  else
747#    define ${def_value} 0
748#  endif
749\n")
750      endif()
751      if (feature STREQUAL cxx_thread_local)
752        set(def_value "${prefix_arg}_THREAD_LOCAL")
753        string(APPEND file_content "
754#  if defined(${def_name}) && ${def_name}
755#    define ${def_value} thread_local
756#  elif ${prefix_arg}_COMPILER_IS_GNU || ${prefix_arg}_COMPILER_IS_Clang || ${prefix_arg}_COMPILER_IS_AppleClang
757#    define ${def_value} __thread
758#  elif ${prefix_arg}_COMPILER_IS_MSVC
759#    define ${def_value} __declspec(thread)
760#  else
761// ${def_value} not defined for this configuration.
762#  endif
763\n")
764      endif()
765      if (feature STREQUAL cxx_attribute_deprecated)
766        set(def_name ${prefix_arg}_${feature_PP})
767        set(def_value "${prefix_arg}_DEPRECATED")
768        string(APPEND file_content "
769#  ifndef ${def_value}
770#    if defined(${def_name}) && ${def_name}
771#      define ${def_value} [[deprecated]]
772#      define ${def_value}_MSG(MSG) [[deprecated(MSG)]]
773#    elif ${prefix_arg}_COMPILER_IS_GNU || ${prefix_arg}_COMPILER_IS_Clang
774#      define ${def_value} __attribute__((__deprecated__))
775#      define ${def_value}_MSG(MSG) __attribute__((__deprecated__(MSG)))
776#    elif ${prefix_arg}_COMPILER_IS_MSVC
777#      define ${def_value} __declspec(deprecated)
778#      define ${def_value}_MSG(MSG) __declspec(deprecated(MSG))
779#    else
780#      define ${def_value}
781#      define ${def_value}_MSG(MSG)
782#    endif
783#  endif
784\n")
785      endif()
786    endforeach()
787
788    foreach(feature ${${_lang}_bare_features})
789      string(TOUPPER ${feature} feature_upper)
790      set(feature_PP "COMPILER_${feature_upper}")
791      set(def_name ${prefix_arg}_${feature_PP})
792      _simplebaredefine(c_restrict restrict "")
793      _simplebaredefine(cxx_constexpr constexpr "")
794      _simplebaredefine(cxx_final final "")
795      _simplebaredefine(cxx_override override "")
796      if (feature STREQUAL cxx_nullptr)
797        set(def_value "nullptr")
798        string(APPEND file_content "
799#  if !(defined(${def_name}) && ${def_name})
800#    if ${prefix_arg}_COMPILER_IS_GNU
801#      define ${def_value} __null
802#    else
803#      define ${def_value} 0
804#    endif
805#  endif
806\n")
807      endif()
808      _simplebaredefine(cxx_noexcept noexcept "")
809    endforeach()
810
811    string(APPEND file_content "#endif\n")
812
813  endforeach()
814
815  if(_WCD_OUTPUT_FILES_VAR)
816    foreach(compiler ${_WCD_COMPILERS})
817      foreach(_lang ${_langs})
818        if(compiler_file_content_${compiler}_${_lang})
819          set(CMAKE_CONFIGURABLE_FILE_CONTENT "${compiler_file_content_}")
820          string(APPEND CMAKE_CONFIGURABLE_FILE_CONTENT "${compiler_file_content_${compiler}_${_lang}}")
821
822          set(compile_file_name "${_WCD_OUTPUT_DIR}${prefix_arg}_COMPILER_INFO_${compiler}_${_lang}.h")
823          set(full_path "${main_file_dir}/${compile_file_name}")
824          list(APPEND ${_WCD_OUTPUT_FILES_VAR} ${full_path})
825          configure_file("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in"
826            "${full_path}"
827            @ONLY
828          )
829        endif()
830      endforeach()
831    endforeach()
832    set(${_WCD_OUTPUT_FILES_VAR} ${${_WCD_OUTPUT_FILES_VAR}} PARENT_SCOPE)
833  endif()
834
835  if (_WCD_EPILOG)
836    string(APPEND file_content "\n${_WCD_EPILOG}\n")
837  endif()
838  string(APPEND file_content "\n#endif")
839
840  set(CMAKE_CONFIGURABLE_FILE_CONTENT ${file_content})
841  configure_file("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in"
842    "${file_arg}"
843    @ONLY
844  )
845endfunction()
846