cmake-compile-features(7)¶
简介¶
项目源代码可能依赖于编译器提供的特定功能,或者以这些功能是否存在为条件。有三种使用场景:编译功能需求、可选编译功能 和 条件编译选项。
虽然功能通常在编程语言标准中指定,但 CMake 提供了一个基于功能细粒度处理的主要用户界面,而不是引入功能的语言标准。
全局属性 CMAKE_C_KNOWN_FEATURES、CMAKE_CUDA_KNOWN_FEATURES 和 CMAKE_CXX_KNOWN_FEATURES 包含了 CMake 已知的全部功能,无论编译器是否支持这些功能。变量 CMAKE_C_COMPILE_FEATURES、CMAKE_CUDA_COMPILE_FEATURES 和 CMAKE_CXX_COMPILE_FEATURES 包含了 CMake 已知且编译器支持的所有功能,无论语言标准或使用它们所需的编译标志。
CMake 已知的功能命名大多遵循 Clang 功能测试宏的约定。有一些例外,例如 CMake 使用 cxx_final 和 cxx_override,而不是 Clang 使用的单个 cxx_override_control。
请注意,对于 OBJC 或 OBJCXX 语言没有单独的编译功能属性或变量。它们分别基于 C 或 C++,因此应使用对应基语言的属性和变量。
编译功能需求¶
可以使用 target_compile_features() 命令指定编译功能需求。例如,如果一个目标必须在编译器支持 cxx_constexpr 功能的情况下编译
add_library(mylib requires_constexpr.cpp)
target_compile_features(mylib PRIVATE cxx_constexpr)
在处理 cxx_constexpr 功能的需求时,cmake(1) 将确保所使用的 C++ 编译器具备该功能,并将添加任何必要的标志,例如 -std=gnu++11 到 mylib 目标 C++ 文件的编译行中。如果编译器不具备该功能,则会发出 FATAL_ERROR。
确切的编译标志和语言标准故意不包含在此用例的用户界面中。CMake 将通过考虑为每个目标指定的功能来计算要使用的适当编译标志。
即使编译器在没有该标志的情况下支持特定功能,也会添加此类编译标志。例如,GNU 编译器在 -std=gnu++98 使用时支持可变模板(带有警告)。如果 cxx_variadic_templates 被指定为需求,CMake 将添加 -std=gnu++11 标志。
在上面的示例中,mylib 在自身构建时需要 cxx_constexpr,但 mylib 的使用者不需要使用支持 cxx_constexpr 的编译器。如果 mylib 的接口确实需要 cxx_constexpr 功能(或任何其他已知功能),则可以使用 target_compile_features() 的 PUBLIC 或 INTERFACE 签名来指定。
add_library(mylib requires_constexpr.cpp)
# cxx_constexpr is a usage-requirement
target_compile_features(mylib PUBLIC cxx_constexpr)
# main.cpp will be compiled with -std=gnu++11 on GNU for cxx_constexpr.
add_executable(myexe main.cpp)
target_link_libraries(myexe mylib)
功能需求通过消耗链接实现进行传递式评估。有关构建属性和使用需求传递行为的更多信息,请参阅 cmake-buildsystem(7)。
要求语言标准¶
在大量使用某个特定语言标准(例如 C++11)的常用功能(例如 C++11)的项目中,可以指定一个元功能(例如 cxx_std_11),它要求使用至少能识别该标准的编译器模式,但可能更高级。这比单独指定所有功能更简单,但不能保证任何特定功能的可用性。对不支持功能的使用的诊断会延迟到编译时。
例如,如果一个项目在头文件中大量使用了 C++11 功能,那么客户端必须使用至少 C++11 的编译器模式。这可以通过以下代码请求:
target_compile_features(mylib PUBLIC cxx_std_11)
在此示例中,CMake 将确保编译器以至少 C++11(或 C++14、C++17 等)的模式调用,必要时添加 -std=gnu++11 等标志。这适用于 mylib 中的源文件以及任何依赖项(可能包括来自 mylib 的头文件)。
注意
如果编译器的默认标准级别至少等于所请求功能的标准级别,CMake 可能会省略 -std= 标志。如果编译器的默认扩展模式与 <LANG>_EXTENSIONS 目标属性不匹配,或者 <LANG>_STANDARD 目标属性已设置,则仍可能添加该标志。
编译器扩展的可用性¶
<LANG>_EXTENSIONS 目标属性默认为编译器的默认值(参见 CMAKE_<LANG>_EXTENSIONS_DEFAULT)。请注意,由于大多数编译器默认启用扩展,这可能会暴露用户代码或第三方依赖项头文件中的可移植性错误。
<LANG>_EXTENSIONS 以前默认为 ON。请参见 CMP0128。
可选编译功能¶
编译功能可以在可用时优先使用,而不会造成硬性要求。这可以通过不使用 target_compile_features() 指定功能,而是在项目代码中使用预处理器条件来检查编译器功能来实现。
在此用例中,项目可能希望在编译器可用时建立特定的语言标准,并使用预处理器条件来检测实际可用的功能。可以通过使用 target_compile_features() 和元功能(如 cxx_std_11)来建立语言标准,或者通过设置 CXX_STANDARD 目标属性或 CMAKE_CXX_STANDARD 变量来建立语言标准。
另请参阅策略 CMP0120 和关于已弃用的 WriteCompilerDetectionHeader 模块的示例用法 的旧文档。
条件编译选项¶
库可以根据请求的编译器功能提供完全不同的头文件。
例如,位于 with_variadics/interface.h 的头文件可能包含
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();
}
};
而位于 no_variadics/interface.h 的头文件可能包含
template<int I1, int I2 = 0, int I3 = 0, int I4 = 0>
struct Interface
{
static int accumulate() { return I1 + I2 + I3 + I4; }
};
可能可以编写一个抽象的 interface.h 头文件,其中包含类似以下内容:
#ifdef HAVE_CXX_VARIADIC_TEMPLATES
#include "with_variadics/interface.h"
#else
#include "no_variadics/interface.h"
#endif
然而,如果有大量文件需要抽象,这可能会变得难以维护。需要的是根据编译器功能使用备用的包含目录。
CMake 提供了一个 COMPILE_FEATURES 生成器表达式 来实现此类条件。这可以与构建属性命令(如 target_include_directories() 和 target_link_libraries())结合使用,以设置适当的 构建系统 属性
add_library(foo INTERFACE)
set(with_variadics ${CMAKE_CURRENT_SOURCE_DIR}/with_variadics)
set(no_variadics ${CMAKE_CURRENT_SOURCE_DIR}/no_variadics)
target_include_directories(foo
INTERFACE
"$<$<COMPILE_FEATURES:cxx_variadic_templates>:${with_variadics}>"
"$<$<NOT:$<COMPILE_FEATURES:cxx_variadic_templates>>:${no_variadics}>"
)
消耗代码然后像往常一样链接到 foo 目标,并使用适合功能的包含目录
add_executable(consumer_with consumer_with.cpp)
target_link_libraries(consumer_with foo)
set_property(TARGET consumer_with CXX_STANDARD 11)
add_executable(consumer_no consumer_no.cpp)
target_link_libraries(consumer_no foo)
支持的编译器¶
CMake 目前了解以下 编译器ID 中指定的版本所对应的 C++ 标准 和 编译 功能:
AppleClang: Apple Clang for Xcode 版本 4.4+。Clang: Clang 编译器版本 2.9+。GNU: GNU 编译器版本 4.4+。MSVC: Microsoft Visual Studio 版本 2010+。SunPro: Oracle SolarisStudio 版本 12.4+。Intel: Intel 编译器版本 12.1+。
CMake 目前了解 编译器ID 中指定的版本所对应的 C 标准 和 编译 功能:
C++ 列出的所有编译器和版本。
GNU: GNU 编译器版本 3.4+
CMake 目前了解以下 编译器ID 中指定的版本所对应的 C++ 标准 及其关联的元功能(例如 cxx_std_11):
Cray: Cray Compiler Environment 版本 8.1+。Fujitsu: Fujitsu HPC compiler 4.0+。PGI: PGI 版本 12.10+。NVHPC: NVIDIA HPC compilers 版本 11.0+。TI: Texas Instruments compiler。TIClang: Texas Instruments Clang-based compilers。XL: IBM XL 版本 10.1+。
CMake 目前了解以下 编译器ID 中指定的版本所对应的 C 标准 及其关联的元功能(例如 c_std_99):
C++ 列出的所有编译器和版本。
CMake 目前了解以下 编译器ID 中指定的版本所对应的 CUDA 标准 及其关联的元功能(例如 cuda_std_11):
Clang: Clang 编译器 5.0+。NVIDIA: NVIDIA nvcc 编译器 7.5+。
语言标准标志¶
为了满足 target_compile_features() 命令或 CMAKE_<LANG>_STANDARD 变量指定的 C++ 标准需求,CMake 可能会向编译器传递一个语言标准标志,例如 -std=c++11。
对于 Visual Studio 生成器,CMake 无法精确控制语言标准标志在编译器命令行上的位置。对于 Ninja 生成器、Makefile 生成器 和 Xcode,CMake 将语言标准标志放在 CMAKE_<LANG>_FLAGS 和 CMAKE_<LANG>_FLAGS_<CONFIG> 的语言级标志之后。
已于 3.26 版本更改:语言标准标志被放置在其他抽象(如 target_compile_options() 命令)指定的标志之前。在 CMake 3.26 之前,语言标准标志放在它们之后。