WriteCompilerDetectionHeader¶
3.20 版之后不推荐使用: 仅当策略 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 的 CMake 版本且小于引入其后续 API 的 CMake 版本的 <version>
值都将选择给定的 API。如果未指定明确版本,则使用 CMAKE_MINIMUM_REQUIRED_VERSION
变量的值。(截至 CMake 3.31.0 版本,仅有一个 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
;如果支持 cxx_constexpr
,ClimbingStats_CONSTEXPR
宏将扩展到 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]]
属性,要么扩展到特定于编译器的修饰符,如 GNU 编译器使用的 __attribute__((__deprecated__))
。
cxx_alignas
功能提供了一个宏定义 <PREFIX>_ALIGNAS
,它要么扩展到标准 alignas
修饰符,要么扩展到特定于编译器的修饰符,如 GNU 编译器使用的 __attribute__ ((__aligned__))
。
特性提供一个宏定义 cxx_alignof
,它展开为标准的 <PREFIX>_ALIGNOF
装饰器或特定编译器的装饰器,例如 GNU 编译器使用的 alignof
。__alignof__
功能 |
定义 |
符号 |
裸机 |
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
是 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
这种弃用宏产生的一个用例是整个库的弃用。在这种情况下,库中的所有公共 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
或 1
。0
此外,一些特性需要额外的定义,例如
和 cxx_final
特性。cxx_override
关键字不会在 final
代码中使用,而是由一个符号抽象出来,该符号被定义为 #ifdef
,编译器特定的等价项或空。这样,C++ 代码可以编写为无条件地使用符号,并且编译器支持决定展开为哪个关键字。final
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
函数宏抽象出来。