WriteCompilerDetectionHeader

已弃用,自 3.20 版本起: 此模块仅在策略 CMP0120 不设置为 NEW 时可用。请勿在新代码中使用它。

版本 3.1 中新增。

此模块提供了一个用于生成包含预处理器宏的头文件的命令。

在 CMake 项目中加载此模块,使用

include(WriteCompilerDetectionHeader)

命令

此模块提供以下命令

write_compiler_detection_header

此函数可用于生成一个适合预处理器包含的,其中包含可在源代码中使用的宏的文件。

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]
)

这将生成文件 <file>,其中包含所有具有前缀 <prefix> 的宏。

默认情况下,所有内容都直接写入 <file>。可以指定 OUTPUT_FILES_VAR,使特定于编译器的内容写入单独的文件。然后,这些单独的文件可在 <output_files_var> 中找到,调用者可以将其用于安装等目的。 OUTPUT_DIR 指定了从主 <file> 到特定于编译器的文件的相对路径。例如:

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 可用于指定要生成的 API 版本。CMake 的未来版本可能会引入替代 API。通过任何大于等于引入给定 API 的 CMake 版本且小于引入其后继 API 的 CMake 版本的 <version> 值来选择一个给定的 API。如果未指定显式版本,则使用 CMAKE_MINIMUM_REQUIRED_VERSION 变量的值。(截至 CMake 4.2.0 版本,只有一个 API 版本。)

PROLOG 可指定为写入标头开头的文本内容。 EPILOG 可指定为写入标头末尾的文本内容。

至少必须列出 <compiler><feature> 中的一个。CMake 已知但未指定的编译器将被检测到,并为其生成一个预处理器 #error。对于 CMake 已知且值为 01 的每个编译器,将生成一个与 <PREFIX>_COMPILER_IS_<compiler> 匹配的预处理器宏。

CMAKE_<LANG>_COMPILER_ID 变量的文档一起记录了可能的编译器标识符。此版本 CMake 中可用的功能列在 CMAKE_C_KNOWN_FEATURESCMAKE_CXX_KNOWN_FEATURES 全局属性中。有关编译功能的信息,请参阅 cmake-compile-features(7) 手册。

已添加,自 3.2 版本起: 添加了对 MSVCAppleClang 编译器的支持。

已添加,自 3.6 版本起: 添加了对 Intel 编译器的支持。

已更改,自 3.8 版本起: 如果请求,则忽略 {c,cxx}_std_* 元功能。

已添加,自 3.8 版本起: ALLOW_UNKNOWN_COMPILERSALLOW_UNKNOWN_COMPILER_VERSIONS 会导致模块生成将未知编译器视为仅缺少所有功能的条件。没有这些选项,默认行为是为未知编译器和版本生成 #error

已添加,自 3.12 版本起: BARE_FEATURES 将使用新语言标准中的名称来定义兼容性宏,这样代码就可以无条件地使用新的功能名称。

功能测试宏

对于每个编译器,都会生成一个与 <PREFIX>_COMPILER_IS_<compiler> 匹配的预处理器宏,其内容为 01,具体取决于正在使用的编译器。如果已定义,则会生成与 <PREFIX>_COMPILER_VERSION_MAJOR <PREFIX>_COMPILER_VERSION_MINOR<PREFIX>_COMPILER_VERSION_PATCH 匹配的编译器版本组件的预处理器宏,其中包含相应编译器版本组件的十进制值。

基于编译器版本生成预处理器测试,指示每个功能是否已启用。将生成一个与 <PREFIX>_COMPILER_<FEATURE> 匹配的预处理器宏(其中 <FEATURE><feature> 名称的大写形式),该宏包含 01,具体取决于正在使用的编译器是否支持该功能。

write_compiler_detection_header(
  FILE climbingstats_compiler_detection.h
  PREFIX ClimbingStats
  COMPILERS GNU Clang AppleClang MSVC Intel
  FEATURES cxx_variadic_templates
)
#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

符号宏

创建了一些额外的符号定义,用于特定的功能,作为符号使用,这些符号可以被有条件地定义为空。

class MyClass ClimbingStats_FINAL
{
    ClimbingStats_CONSTEXPR int someInterface() { return 42; }
};

如果编译器(及其标志)支持 cxx_final 功能,则 ClimbingStats_FINAL 宏将展开为 final;如果支持 cxx_constexpr,则 ClimbingStats_CONSTEXPR 宏将展开为 constexpr

如果将 BARE_FEATURES cxx_final 作为参数给出,则 final 关键字也将为旧编译器定义。

以下功能会生成相应的符号定义,如果它们可用作为 BARE_FEATURES

功能

定义

符号

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

兼容实现宏

某些功能适合包装在具有向后兼容实现功能的宏中,以防编译器不支持该功能。

当编译器不提供 cxx_static_assert 功能时,可通过 <PREFIX>_STATIC_ASSERT(COND)<PREFIX>_STATIC_ASSERT_MSG(COND, MSG) 函数式宏提供兼容实现。这些宏展开为 static_assert(当编译器支持该功能时),否则展开为兼容实现。在第一种形式中,条件被字符串化为 static_assert 的消息字段。在第二种形式中,消息 MSG 被传递到 static_assert 的消息字段,或者在使用向后兼容实现时被忽略。

cxx_attribute_deprecated 功能提供宏定义 <PREFIX>_DEPRECATED,它展开为标准的 [[deprecated]] 属性或特定于编译器的装饰器,例如 GNU 编译器使用的 __attribute__((__deprecated__))

cxx_alignas 功能提供宏定义 <PREFIX>_ALIGNAS,它展开为标准的 alignas 装饰器或特定于编译器的装饰器,例如 GNU 编译器使用的 __attribute__ ((__aligned__))

cxx_alignof 功能提供宏定义 <PREFIX>_ALIGNOF,它展开为标准的 alignof 装饰器或特定于编译器的装饰器,例如 GNU 编译器使用的 __alignof__

功能

定义

符号

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

使用此类弃用宏的一个用例是弃用整个库。在这种情况下,库中的所有公共 API 都可以用 <PREFIX>_DEPRECATED 宏进行装饰。当构建被弃用的库本身时,这会导致非常冗长的构建输出,因此在这种情况下,宏可能被定义为空。

add_library(compat_support ${srcs})
target_compile_definitions(compat_support
  PRIVATE
    CompatSupport_DEPRECATED=
)

示例用法

注意

此部分已从 cmake-compile-features(7) 手册迁移,因为它依赖于 WriteCompilerDetectionHeader 模块,而该模块已被策略 CMP0120 移除。

如果可用,可以优先使用编译功能,而不会创建硬性要求。例如,库可以根据是否可用 cxx_variadic_templates 功能提供备用实现。

#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

这样的接口依赖于使用正确的预处理器定义来处理编译器功能。CMake 可以使用 WriteCompilerDetectionHeader 模块生成一个包含这些定义的头文件。该模块包含 write_compiler_detection_header 函数,该函数接受参数来控制生成头文件的内容。

write_compiler_detection_header(
  FILE "${CMAKE_CURRENT_BINARY_DIR}/foo_compiler_detection.h"
  PREFIX Foo
  COMPILERS GNU
  FEATURES
    cxx_variadic_templates
)

这样的头文件可以在项目的源代码内部使用,也可以安装并用于库代码的接口。

对于 FEATURES 中列出的每个功能,都会在头文件中创建一个预处理器定义,并将其定义为 10

此外,某些功能需要额外的定义,例如 cxx_finalcxx_override 功能。 final 关键字不是用在 #ifdef 代码中,而是通过一个符号来抽象,该符号被定义为 final、特定于编译器的等价物,或者为空。这样,C++ 代码就可以编写成无条件使用该符号,而编译器支持决定其展开方式。

struct Interface {
  virtual void Execute() = 0;
};

struct Concrete Foo_FINAL {
  void Execute() Foo_OVERRIDE;
};

在这种情况下,如果编译器支持该关键字,Foo_FINAL 将展开为 final,否则将展开为空。

在这种用例中,项目代码可能希望在编译器可用时启用特定的语言标准。 CXX_STANDARD 目标属性可以设置为特定目标的所需语言标准,并且 CMAKE_CXX_STANDARD 变量可以设置为影响所有后续目标。

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)

write_compiler_detection_header 函数还为具有标准等价物的其他功能创建了兼容代码。例如,cxx_static_assert 功能通过模板进行模拟,并通过 <PREFIX>_STATIC_ASSERT<PREFIX>_STATIC_ASSERT_MSG 函数式宏进行抽象。