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 将通过考虑为每个目标指定的特性来计算要使用的适当编译标志。
即使编译器在没有标志的情况下支持特定特性,也会添加此类编译标志。例如,即使使用 -std=gnu++98
,GNU 编译器也支持可变参数模板(带有警告)。如果 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)中大量常用特性的项目中,可以指定一个元特性(例如 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()
指定特性,而是使用项目代码中的预处理器条件检查编译器功能来实现。
在此用例中,项目可能希望在编译器可用时建立特定的语言标准,并使用预处理器条件来检测实际可用的特性。可以使用 要求语言标准,使用带有元特性(如 cxx_std_11
)的 target_compile_features()
,或者通过设置 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
generator expression
来实现此类条件。这可以与构建属性命令(如 target_include_directories()
和 target_link_libraries()
)一起使用,以设置适当的 buildsystem
属性
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
:适用于 Xcode 4.4+ 版本的 Apple Clang。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 编译器 4.0+。PGI
:PGI 版本 12.10+。NVHPC
:NVIDIA HPC 编译器版本 11.0+。TI
:Texas Instruments 编译器。TIClang
:Texas Instruments 基于 Clang 的编译器。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
变量指定的要求,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 之前,语言标准标志放置在它们之后。