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 版本的值来选择给定的 API。如果未指定显式版本,则使用 CMAKE_MINIMUM_REQUIRED_VERSION
变量的值。(截至 CMake 4.1.0 版,只有一个 API 版本。)
可以指定 PROLOG
作为文本内容,写入到头文件的开头。EPILOG
可以指定为文本内容,写入到头文件的末尾。
至少必须列出一种 <compiler>
和一种 <feature>
。CMake 已知的但未指定的编译器将被检测到,并为它们生成一个预处理器 #error
。为 CMake 已知的每个包含值为 0
或 1
的编译器生成一个匹配 <PREFIX>_COMPILER_IS_<compiler>
的预处理器宏。
可能的编译器标识符在 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
可用:
功能 |
定义 |
符号 |
裸 |
---|---|---|---|
|
|
|
yes |
|
|
|
yes |
|
|
|
|
|
|
|
|
|
|
|
yes |
|
|
|
yes |
|
|
|
|
|
|
|
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__
。
功能 |
定义 |
符号 |
裸 |
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
yes |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
这种弃用宏的用例之一是整个库的弃用。在这种情况下,库中的所有公共 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
功能。这些功能不是用于 #ifdef
代码,而是将 final
关键字抽象为一个符号,该符号定义为 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
函数宏进行抽象。