cmake 编译功能 (7)

介绍

项目源代码可能取决于或取决于编译器某些功能的可用性。出现了三个用例:编译特性要求可选编译特性 和`条件编译选项`_。

虽然功能通常在编程语言标准中指定,但 CMake 提供了一个基于对功能的精细处理的主要用户界面,而不是引入该功能的语言标准。

CMAKE_C_KNOWN_FEATURES、CMAKE_CUDA_KNOWN_FEATURES 和 CMAKE_CXX_KNOWN_FEATURES 全局属性包含 CMake 已知的所有功能,无论编译器是否支持该功能。 CMAKE_C_COMPILE_FEATURESCMAKE_CUDA_COMPILE_FEATURESCMAKE_CXX_COMPILE_FEATURES 变量包含 CMake 知道编译器已知的所有功能,无论使用它们所需的语言标准或编译标志如何。

CMake 已知的功能命名大多遵循与 Clang 功能测试宏相同的约定。有一些例外,例如 CMake 使用 cxx_finalcxx_override 而不是 Clang 使用的单个 cxx_override_control

请注意,“OBJC”或“OBJCXX”语言没有单独的编译功能属性或变量。它们分别基于 CC++,因此应该使用它们相应的基本语言的属性和变量。

编译特性要求

可以使用 target_compile_features 命令指定编译功能要求。例如,如果必须使用编译器支持编译目标:prop_gbl:cxx_constexpr <CMAKE_CXX_KNOWN_FEATURES> 特性:

add_library(mylib requires_constexpr.cpp)
target_compile_features(mylib PRIVATE cxx_constexpr)

在处理对 cxx_constexpr 功能的要求时,cmake(1)` 将确保正在使用的 C++ 编译器能够使用该功能,并将添加任何必要的标志,例如 -std= gnu++11mylib 目标中的 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 目标属性默认为编译器的默认值(参见 CMAKE_<LANG>_EXTENSIONS_DEFAULT)。请注意,由于大多数编译器默认启用扩展,这可能会暴露用户代码或第三方依赖项标头中的可移植性错误。

<LANG>_EXTENSIONS 过去默认为 ON。请参阅:policy:CMP0128

可选的编译功能

如果可用,编译功能可能是首选,而不会产生硬性要求。这可以通过*不*使用 target_compile_features() 指定功能,而是在项目代码中使用预处理器条件检查编译器功能来实现。

在此用例中,项目可能希望建立一个特定的语言标准(如果编译器可用),并使用预处理器条件来检测实际可用的功能。语言标准可以通过使用带有像 cxx_std_11 这样的元特征的 target_compile_features() 的`Requiring Language Standards`_ 来建立,或者通过设置 CXX_STANDARD 目标属性或 CMAKE_CXX_STANDARD 来建立。变量。

另请参阅策略:policy:CMP0120 和遗留文档:ref:Example Usage <WCDH Example Usage> of the deprecated 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 目前知道:prop_tgt:C++ 标准 <CXX_STANDARD> 和:prop_gbl:编译特性 <CMAKE_CXX_KNOWN_FEATURES> 可从以下 编译器 IDs 指定版本对于每个:

  • 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 目前知道:prop_tgt:C 标准 <C_STANDARD> 和:prop_gbl:编译特性 <CMAKE_C_KNOWN_FEATURES> 可从以下 编译器 ID 指定版本对于每个:

  • 上面列出的所有 C++ 编译器和版本。

  • GNU:GNU 编译器版本 3.4+

CMake 目前知道:prop_tgt:C++ 标准 <CXX_STANDARD> 及其相关的元特征(例如 cxx_std_11)可从以下 编译器 ids 获得为每个指定的版本:

  • Cray:Cray 编译器环境版本 8.1+。

  • Fujitsu:Fujitsu HPC 编译器 4.0+。

  • PGI:PGI 版本 12.10+。

  • NVHPC:NVIDIA HPC 编译器版本 11.0+。

  • TI:德州仪器编译器。

  • XL:IBM XL 版本 10.1+。

CMake 目前知道:prop_tgt:C 标准 <C_STANDARD> 及其相关的元特性(例如 c_std_99)可从以下 编译器 ids 中获得为每个指定的版本:

  • 上面列出的所有编译器和版本仅具有 C++ 的元功能。

CMake 目前知道 CUDA 标准 及其相关的元特征(例如 cuda_std_11)可从以下 compiler ids 获得为每个指定的版本:

  • Clang:Clang 编译器 5.0+。

  • NVIDIA:NVIDIA nvcc 编译器 7.5+。

语言标准标志

为了满足 target_compile_features() 命令或 CMAKE_<LANG>_STANDARD 变量指定的要求,CMake 可以将语言标准标志传递给编译器,例如 -std=c++ 11

对于 Visual Studio 生成器,CMake 无法精确控制语言标准标志在编译器命令行上的位置。对于 忍者生成器Makefile 生成器Xcode,CMake 将语言标准标志放在 CMAKE_<LANG>_FLAGSCMAKE_<LANG>_FLAGS_<CONFIG>

在 3.26 版本发生变更: 语言标准标志位于其他抽象指定的标志之前,例如 target_compile_options() 命令。在 CMake 3.26 之前,语言标准标志位于它们之后。