WriteCompilerDetectionHeader¶
自 3.20 版本弃用: 此模块仅在政策:policy:CMP0120 未设置为``NEW`` 时可用。不要在新代码中使用它。
在 3.1 版本加入.
该模块提供函数``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 由大于或等于引入给定 API 的 CMake 版本且小于引入其后续 API 的 CMake 版本的任何“<version>”值选择。如果未指定显式版本,则使用 CMAKE_MINIMUM_REQUIRED_VERSION 变量的值。 (从 CMake 版本 3.26.4 开始,只有一个 API 版本。)
PROLOG 可以指定为要写在标题开头的文本内容。 EPILOG 可以指定为要写在标题末尾的文本内容
至少必须列出一个 <compiler> 和一个 <feature>。检测到 CMake 已知但未指定的编译器,并为它们生成预处理器“#error”。为 CMake 已知的每个编译器生成匹配“<PREFIX>_COMPILER_IS_<compiler>”的预处理器宏,包含值“0”或“1”。
可能的编译器标识符记录在 CMAKE_<LANG>_COMPILER_ID 变量中。此版本 CMake 中的可用功能列在 CMAKE_C_KNOWN_FEATURES 和 CMAKE_CXX_KNOWN_FEATURES 全局属性中。有关编译功能的信息,请参阅 cmake-compile-features(7) 手册。
在 3.2 版本加入: 添加了 MSVC 和 AppleClang 编译器支持。
在 3.6 版本加入: 添加了 Intel 编译器支持。
在 3.8 版本发生变更: 如果需要,{c,cxx}_std_* 元特征将被忽略。
在 3.8 版本加入: ALLOW_UNKNOWN_COMPILERS 和 ALLOW_UNKNOWN_COMPILER_VERSIONS 导致模块生成将未知编译器视为缺少所有功能的条件。如果没有这些选项,默认行为是为未知的编译器和版本生成 #error。
在 3.12 版本加入: BARE_FEATURES 将使用新版本语言标准中使用的名称定义兼容性宏,因此代码可以无条件地使用新功能名称。
功能测试宏¶
对于每个编译器,都会生成一个预处理器宏,匹配“<PREFIX>_COMPILER_IS_<compiler>”,其内容为“0”或“1”,具体取决于所使用的编译器。生成编译器版本组件的预处理器宏,匹配``<PREFIX>_COMPILER_VERSION_MAJOR`` <PREFIX>_COMPILER_VERSION_MINOR 和``<PREFIX>_COMPILER_VERSION_PATCH`` 包含相应编译器版本组件的十进制值(如果已定义)。
预处理器测试是根据编译器版本生成的,指示是否启用了每个功能。预处理器宏匹配 <PREFIX>_COMPILER_<FEATURE>,其中 <FEATURE> 是大写的 <feature> 名称,生成包含值 0 或``1`` 取决于使用的编译器是否支持该功能:
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”,如果“ClimbingStats_CONSTEXPR”宏将扩展为“constexpr”支持`cxx_constexpr``。
如果 BARE_FEATURES cxx_final 作为参数给出,final 关键字也将为旧编译器定义。
以下功能生成相应的符号定义,如果它们作为“BARE_FEATURES”可用:
特征 |
定义 |
象征 |
裸 |
|---|---|---|---|
|
|
|
是的 |
|
|
|
是的 |
|
|
|
|
|
|
|
|
|
|
|
是的 |
|
|
|
是的 |
|
|
|
|
|
|
|
是的 |
兼容性实现宏¶
如果编译器不支持某些功能,则某些功能适合包装在具有向后兼容性实现的宏中。
当编译器未提供“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]]`` 属性或特定于编译器的装饰器,例如``__attribute__((__deprecated__ ))`` 由 GNU 编译器使用。
cxx_alignas 功能提供了一个宏定义``<PREFIX>_ALIGNAS``,它扩展为标准的``alignas`` 装饰器或特定于编译器的装饰器,例如使用的``__attribute__ ((__aligned__))``由 GNU 编译器。
cxx_alignof 特性提供了一个宏定义``<PREFIX>_ALIGNOF``,它扩展为标准的``alignof`` 装饰器或特定于编译器的装饰器,例如 GNU 编译器使用的``__alignof__``。
特征 |
定义 |
象征 |
裸 |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
是的 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
这种弃用宏出现的一个用例是整个库的弃用。在这种情况下,库中的所有公共 API 都可以用 <PREFIX>_DEPRECATED 宏修饰。这会在构建库本身时产生非常嘈杂的构建输出,因此在构建已弃用的库时,宏可能会被定义为空:
add_library(compat_support ${srcs})
target_compile_definitions(compat_support
PRIVATE
CompatSupport_DEPRECATED=
)
用法示例¶
备注
本节是从 cmake-compile-features(7) 手册中迁移而来的,因为它依赖于被策略 CMP0120 删除的 WriteCompilerDetectionHeader 模块。
如果可用,编译功能可能是首选,而不会产生硬性要求。例如,库可能会根据“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”中列出的每个功能,都会在头文件中创建预处理器定义,并将其定义为“1”或“0”。
此外,一些功能需要额外的定义,例如“cxx_final”和“cxx_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`` 函数宏进行抽象。