WriteCompilerDetectionHeader.cmake 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. #.rst:
  2. # WriteCompilerDetectionHeader
  3. # ----------------------------
  4. #
  5. # This module provides the function write_compiler_detection_header().
  6. #
  7. # The ``WRITE_COMPILER_DETECTION_HEADER`` function can be used to generate
  8. # a file suitable for preprocessor inclusion which contains macros to be
  9. # used in source code::
  10. #
  11. # write_compiler_detection_header(
  12. # FILE <file>
  13. # PREFIX <prefix>
  14. # COMPILERS <compiler> [...]
  15. # FEATURES <feature> [...]
  16. # [VERSION <version>]
  17. # [PROLOG <prolog>]
  18. # [EPILOG <epilog>]
  19. # )
  20. #
  21. # The ``write_compiler_detection_header`` function generates the
  22. # file ``<file>`` with macros which all have the prefix ``<prefix>``.
  23. #
  24. # ``VERSION`` may be used to specify the API version to be generated.
  25. # Future versions of CMake may introduce alternative APIs. A given
  26. # API is selected by any ``<version>`` value greater than or equal
  27. # to the version of CMake that introduced the given API and less
  28. # than the version of CMake that introduced its succeeding API.
  29. # The value of the :variable:`CMAKE_MINIMUM_REQUIRED_VERSION`
  30. # variable is used if no explicit version is specified.
  31. # (As of CMake version |release| there is only one API version.)
  32. #
  33. # ``PROLOG`` may be specified as text content to write at the start of the
  34. # header. ``EPILOG`` may be specified as text content to write at the end
  35. # of the header
  36. #
  37. # At least one ``<compiler>`` and one ``<feature>`` must be listed. Compilers
  38. # which are known to CMake, but not specified are detected and a preprocessor
  39. # ``#error`` is generated for them. A preprocessor macro matching
  40. # ``<PREFIX>_COMPILER_IS_<compiler>`` is generated for each compiler
  41. # known to CMake to contain the value ``0`` or ``1``.
  42. #
  43. # Possible compiler identifiers are documented with the
  44. # :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
  45. # Available features in this version of CMake are listed in the
  46. # :prop_gbl:`CMAKE_C_KNOWN_FEATURES` and
  47. # :prop_gbl:`CMAKE_CXX_KNOWN_FEATURES` global properties.
  48. #
  49. # Feature Test Macros
  50. # ===================
  51. #
  52. # For each compiler, a preprocessor test of the compiler version is generated
  53. # denoting whether each feature is enabled. A preprocessor macro
  54. # matching ``<PREFIX>_COMPILER_<FEATURE>``, where ``<FEATURE>`` is the
  55. # upper-case ``<feature>`` name, is generated to contain the value
  56. # ``0`` or ``1`` depending on whether the compiler in use supports the
  57. # feature:
  58. #
  59. # .. code-block:: cmake
  60. #
  61. # write_compiler_detection_header(
  62. # FILE climbingstats_compiler_detection.h
  63. # PREFIX ClimbingStats
  64. # COMPILERS GNU Clang MSVC
  65. # FEATURES cxx_variadic_templates
  66. # )
  67. #
  68. # .. code-block:: c++
  69. #
  70. # #if ClimbingStats_COMPILER_CXX_VARIADIC_TEMPLATES
  71. # template<typename... T>
  72. # void someInterface(T t...) { /* ... */ }
  73. # #else
  74. # // Compatibility versions
  75. # template<typename T1>
  76. # void someInterface(T1 t1) { /* ... */ }
  77. # template<typename T1, typename T2>
  78. # void someInterface(T1 t1, T2 t2) { /* ... */ }
  79. # template<typename T1, typename T2, typename T3>
  80. # void someInterface(T1 t1, T2 t2, T3 t3) { /* ... */ }
  81. # #endif
  82. #
  83. # Symbol Macros
  84. # =============
  85. #
  86. # Some additional symbol-defines are created for particular features for
  87. # use as symbols which may be conditionally defined empty:
  88. #
  89. # .. code-block:: c++
  90. #
  91. # class MyClass ClimbingStats_DECL_CXX_FINAL
  92. # {
  93. # ClimbingStats_DECL_CXX_CONSTEXPR int someInterface() { return 42; }
  94. # };
  95. #
  96. # The ``ClimbingStats_DECL_CXX_FINAL`` macro will expand to ``final`` if the
  97. # compiler (and its flags) support the ``cxx_final`` feature, and the
  98. # ``ClimbingStats_DECL_CXX_CONSTEXPR`` macro will expand to ``constexpr``
  99. # if ``cxx_constexpr`` is supported.
  100. #
  101. # The following features generate corresponding symbol defines:
  102. #
  103. # ========================== =================================== =================
  104. # Feature Define Symbol
  105. # ========================== =================================== =================
  106. # ``c_restrict`` ``<PREFIX>_RESTRICT`` ``restrict``
  107. # ``cxx_constexpr`` ``<PREFIX>_CONSTEXPR`` ``constexpr``
  108. # ``cxx_deleted_functions`` ``<PREFIX>_DELETED_FUNCTION`` ``= delete``
  109. # ``cxx_extern_templates`` ``<PREFIX>_EXTERN_TEMPLATE`` ``extern``
  110. # ``cxx_final`` ``<PREFIX>_FINAL`` ``final``
  111. # ``cxx_noexcept`` ``<PREFIX>_NOEXCEPT`` ``noexcept``
  112. # ``cxx_noexcept`` ``<PREFIX>_NOEXCEPT_EXPR(X)`` ``noexcept(X)``
  113. # ``cxx_override`` ``<PREFIX>_OVERRIDE`` ``override``
  114. # ========================== =================================== =================
  115. #
  116. # Compatibility Implementation Macros
  117. # ===================================
  118. #
  119. # Some features are suitable for wrapping in a macro with a backward
  120. # compatibility implementation if the compiler does not support the feature.
  121. #
  122. # When the ``cxx_static_assert`` feature is not provided by the compiler,
  123. # a compatibility implementation is available via the
  124. # ``<PREFIX>_STATIC_ASSERT(COND)`` and
  125. # ``<PREFIX>_STATIC_ASSERT_MSG(COND, MSG)`` function-like macros. The macros
  126. # expand to ``static_assert`` where that compiler feature is available, and
  127. # to a compatibility implementation otherwise. In the first form, the
  128. # condition is stringified in the message field of ``static_assert``. In
  129. # the second form, the message ``MSG`` is passed to the message field of
  130. # ``static_assert``, or ignored if using the backward compatibility
  131. # implementation.
  132. #
  133. # ====================== ================================ ===================
  134. # Feature Define Symbol
  135. # ====================== ================================ ===================
  136. # ``cxx_alignas`` ``<PREFIX>_ALIGNAS`` ``alignas``
  137. # ``cxx_alignof`` ``<PREFIX>_ALIGNOF`` ``alignof``
  138. # ``cxx_nullptr`` ``<PREFIX>_NULLPTR`` ``nullptr``
  139. # ``cxx_static_assert`` ``<PREFIX>_STATIC_ASSERT`` ``static_assert``
  140. # ``cxx_static_assert`` ``<PREFIX>_STATIC_ASSERT_MSG`` ``static_assert``
  141. # ====================== ================================ ===================
  142. #=============================================================================
  143. # Copyright 2014 Stephen Kelly <[email protected]>
  144. #
  145. # Distributed under the OSI-approved BSD License (the "License");
  146. # see accompanying file Copyright.txt for details.
  147. #
  148. # This software is distributed WITHOUT ANY WARRANTY; without even the
  149. # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  150. # See the License for more information.
  151. #=============================================================================
  152. # (To distribute this file outside of CMake, substitute the full
  153. # License text for the above reference.)
  154. include(${CMAKE_CURRENT_LIST_DIR}/CMakeParseArguments.cmake)
  155. include(${CMAKE_CURRENT_LIST_DIR}/CMakeCompilerIdDetection.cmake)
  156. function(_load_compiler_variables CompilerId lang)
  157. include("${CMAKE_ROOT}/Modules/Compiler/${CompilerId}-${lang}-FeatureTests.cmake" OPTIONAL)
  158. set(_cmake_oldestSupported_${CompilerId} ${_cmake_oldestSupported} PARENT_SCOPE)
  159. foreach(feature ${ARGN})
  160. set(_cmake_feature_test_${CompilerId}_${feature} ${_cmake_feature_test_${feature}} PARENT_SCOPE)
  161. endforeach()
  162. endfunction()
  163. function(write_compiler_detection_header
  164. file_keyword file_arg
  165. prefix_keyword prefix_arg
  166. )
  167. if (NOT file_keyword STREQUAL FILE)
  168. message(FATAL_ERROR "write_compiler_detection_header: FILE parameter missing.")
  169. endif()
  170. if (NOT prefix_keyword STREQUAL PREFIX)
  171. message(FATAL_ERROR "write_compiler_detection_header: PREFIX parameter missing.")
  172. endif()
  173. set(options)
  174. set(oneValueArgs VERSION EPILOG PROLOG)
  175. set(multiValueArgs COMPILERS FEATURES)
  176. cmake_parse_arguments(_WCD "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
  177. if (NOT _WCD_COMPILERS)
  178. message(FATAL_ERROR "Invalid arguments. write_compiler_detection_header requires at least one compiler.")
  179. endif()
  180. if (NOT _WCD_FEATURES)
  181. message(FATAL_ERROR "Invalid arguments. write_compiler_detection_header requires at least one feature.")
  182. endif()
  183. if(_WCD_UNPARSED_ARGUMENTS)
  184. message(FATAL_ERROR "Unparsed arguments: ${_WCD_UNPARSED_ARGUMENTS}")
  185. endif()
  186. if(NOT _WCD_VERSION)
  187. set(_WCD_VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
  188. endif()
  189. if (_WCD_VERSION VERSION_LESS 3.1.0) # Version which introduced this function
  190. message(FATAL_ERROR "VERSION parameter too low.")
  191. endif()
  192. set(compilers
  193. GNU
  194. Clang
  195. )
  196. foreach(_comp ${_WCD_COMPILERS})
  197. list(FIND compilers ${_comp} idx)
  198. if (idx EQUAL -1)
  199. message(FATAL_ERROR "Unsupported compiler ${_comp}.")
  200. endif()
  201. endforeach()
  202. set(file_content "
  203. // This is a generated file. Do not edit!
  204. #ifndef ${prefix_arg}_COMPILER_DETECTION_H
  205. #define ${prefix_arg}_COMPILER_DETECTION_H
  206. ")
  207. if (_WCD_PROLOG)
  208. set(file_content "${file_content}\n${_WCD_PROLOG}\n")
  209. endif()
  210. foreach(feature ${_WCD_FEATURES})
  211. if (feature MATCHES "^cxx_")
  212. list(APPEND _langs CXX)
  213. list(APPEND CXX_features ${feature})
  214. elseif (feature MATCHES "^c_")
  215. list(APPEND _langs C)
  216. list(APPEND C_features ${feature})
  217. else()
  218. message(FATAL_ERROR "Unsupported feature ${feature}.")
  219. endif()
  220. endforeach()
  221. list(REMOVE_DUPLICATES _langs)
  222. foreach(_lang ${_langs})
  223. get_property(known_features GLOBAL PROPERTY CMAKE_${_lang}_KNOWN_FEATURES)
  224. foreach(feature ${${_lang}_features})
  225. list(FIND known_features ${feature} idx)
  226. if (idx EQUAL -1)
  227. message(FATAL_ERROR "Unsupported feature ${feature}.")
  228. endif()
  229. endforeach()
  230. if(_lang STREQUAL CXX)
  231. set(file_content "${file_content}\n#ifdef __cplusplus\n")
  232. else()
  233. set(file_content "${file_content}\n#ifndef __cplusplus\n")
  234. endif()
  235. compiler_id_detection(ID_CONTENT ${_lang} PREFIX ${prefix_arg}_
  236. ID_DEFINE
  237. )
  238. set(file_content "${file_content}${ID_CONTENT}\n")
  239. set(pp_if "if")
  240. foreach(compiler ${_WCD_COMPILERS})
  241. _load_compiler_variables(${compiler} ${_lang} ${${_lang}_features})
  242. set(file_content "${file_content}\n# ${pp_if} ${prefix_arg}_COMPILER_IS_${compiler}\n")
  243. set(file_content "${file_content}
  244. # if !(${_cmake_oldestSupported_${compiler}})
  245. # error Unsupported compiler version
  246. # endif\n")
  247. set(pp_if "elif")
  248. foreach(feature ${${_lang}_features})
  249. string(TOUPPER ${feature} feature_upper)
  250. set(feature_PP "COMPILER_${feature_upper}")
  251. set(_define_item "\n# define ${prefix_arg}_${feature_PP} 0\n")
  252. if (_cmake_feature_test_${compiler}_${feature} STREQUAL "1")
  253. set(_define_item "\n# define ${prefix_arg}_${feature_PP} 1\n")
  254. elseif (_cmake_feature_test_${compiler}_${feature})
  255. set(_define_item "\n# define ${prefix_arg}_${feature_PP} 0\n")
  256. set(_define_item "\n# if ${_cmake_feature_test_${compiler}_${feature}}\n# define ${prefix_arg}_${feature_PP} 1\n# else${_define_item}# endif\n")
  257. endif()
  258. set(file_content "${file_content}${_define_item}")
  259. endforeach()
  260. endforeach()
  261. if(pp_if STREQUAL "elif")
  262. set(file_content "${file_content}
  263. # else
  264. # error Unsupported compiler
  265. # endif\n")
  266. endif()
  267. foreach(feature ${${_lang}_features})
  268. string(TOUPPER ${feature} feature_upper)
  269. set(feature_PP "COMPILER_${feature_upper}")
  270. set(def_name ${prefix_arg}_${feature_PP})
  271. if (feature STREQUAL c_restrict)
  272. set(def_value "${prefix_arg}_RESTRICT")
  273. set(file_content "${file_content}
  274. # if ${def_name}
  275. # define ${def_value} restrict
  276. # else
  277. # define ${def_value}
  278. # endif
  279. \n")
  280. endif()
  281. if (feature STREQUAL cxx_constexpr)
  282. set(def_value "${prefix_arg}_DECL_${feature_upper}")
  283. set(file_content "${file_content}
  284. # if ${def_name}
  285. # define ${def_value} constexpr
  286. # else
  287. # define ${def_value}
  288. # endif
  289. \n")
  290. endif()
  291. if (feature STREQUAL cxx_final)
  292. set(def_value "${prefix_arg}_DECL_${feature_upper}")
  293. set(file_content "${file_content}
  294. # if ${def_name}
  295. # define ${def_value} final
  296. # else
  297. # define ${def_value}
  298. # endif
  299. \n")
  300. endif()
  301. if (feature STREQUAL cxx_override)
  302. set(def_value "${prefix_arg}_DECL_${feature_upper}")
  303. set(file_content "${file_content}
  304. # if ${def_name}
  305. # define ${def_value} override
  306. # else
  307. # define ${def_value}
  308. # endif
  309. \n")
  310. endif()
  311. if (feature STREQUAL cxx_static_assert)
  312. set(def_value "${prefix_arg}_STATIC_ASSERT(X)")
  313. set(def_value_msg "${prefix_arg}_STATIC_ASSERT_MSG(X, MSG)")
  314. set(static_assert_struct "template<bool> struct ${prefix_arg}StaticAssert;\ntemplate<> struct ${prefix_arg}StaticAssert<true>{};\n")
  315. set(def_standard "# define ${def_value} static_assert(X, #X)\n# define ${def_value_msg} static_assert(X, MSG)")
  316. set(def_alternative "${static_assert_struct}# define ${def_value} sizeof(${prefix_arg}StaticAssert<X>)\n# define ${def_value_msg} sizeof(${prefix_arg}StaticAssert<X>)")
  317. set(file_content "${file_content}# if ${def_name}\n${def_standard}\n# else\n${def_alternative}\n# endif\n\n")
  318. endif()
  319. if (feature STREQUAL cxx_alignas)
  320. set(def_value "${prefix_arg}_ALIGNAS(X)")
  321. set(file_content "${file_content}
  322. # if ${def_name}
  323. # define ${def_value} alignas(X)
  324. # elif ${prefix_arg}_COMPILER_IS_GNU
  325. # define ${def_value} __attribute__ ((__aligned__(X)))
  326. # else
  327. # define ${def_value}
  328. # endif
  329. \n")
  330. endif()
  331. if (feature STREQUAL cxx_alignof)
  332. set(def_value "${prefix_arg}_ALIGNOF(X)")
  333. set(file_content "${file_content}
  334. # if ${def_name}
  335. # define ${def_value} alignof(X)
  336. # elif ${prefix_arg}_COMPILER_IS_GNU
  337. # define ${def_value} __alignof__(X)
  338. # endif
  339. \n")
  340. endif()
  341. if (feature STREQUAL cxx_deleted_functions)
  342. set(def_value "${prefix_arg}_DELETED_FUNCTION")
  343. set(file_content "${file_content}
  344. # if ${def_name}
  345. # define ${def_value} = delete
  346. # else
  347. # define ${def_value}
  348. # endif
  349. \n")
  350. endif()
  351. if (feature STREQUAL cxx_extern_templates)
  352. set(def_value "${prefix_arg}_EXTERN_TEMPLATE")
  353. set(file_content "${file_content}
  354. # if ${def_name}
  355. # define ${def_value} extern
  356. # else
  357. # define ${def_value}
  358. # endif
  359. \n")
  360. endif()
  361. if (feature STREQUAL cxx_noexcept)
  362. set(def_value "${prefix_arg}_NOEXCEPT")
  363. set(file_content "${file_content}
  364. # if ${def_name}
  365. # define ${def_value} noexcept
  366. # define ${def_value}_EXPR(X) noexcept(X)
  367. # else
  368. # define ${def_value}
  369. # define ${def_value}_EXPR(X)
  370. # endif
  371. \n")
  372. endif()
  373. if (feature STREQUAL cxx_nullptr)
  374. set(def_value "${prefix_arg}_NULLPTR")
  375. set(file_content "${file_content}
  376. # if ${def_name}
  377. # define ${def_value} nullptr
  378. # else
  379. # define ${def_value} static_cast<void*>(0)
  380. # endif
  381. \n")
  382. endif()
  383. endforeach()
  384. set(file_content "${file_content}#endif\n")
  385. endforeach()
  386. if (_WCD_EPILOG)
  387. set(file_content "${file_content}\n${_WCD_EPILOG}\n")
  388. endif()
  389. set(file_content "${file_content}\n#endif")
  390. set(CMAKE_CONFIGURABLE_FILE_CONTENT ${file_content})
  391. configure_file("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in"
  392. "${file_arg}"
  393. @ONLY
  394. )
  395. endfunction()