cmake-compile-features(7)

簡介

專案原始碼可能依賴於或受限於編譯器的某些功能是否可用。有三種使用案例:編譯功能需求可選編譯功能條件式編譯選項

雖然功能通常在程式語言標準中指定,但 CMake 提供了一個主要的使用者介面,該介面基於對功能的細粒度處理,而不是引入該功能的語言標準。

CMAKE_C_KNOWN_FEATURESCMAKE_CUDA_KNOWN_FEATURESCMAKE_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

請注意,OBJCOBJCXX 語言沒有單獨的編譯功能屬性或變數。這些分別基於 CC++,因此應改為使用其對應基礎語言的屬性和變數。

編譯功能需求

編譯功能需求可以使用 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()PUBLICINTERFACE 簽章來指定。

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() 指定功能,而是使用專案程式碼中的前置處理器條件檢查編譯器功能來實現。

在此使用案例中,如果編譯器有提供特定的語言標準,專案可能會希望建立該標準,並使用預處理器條件來偵測實際可用的功能。語言標準可以透過要求語言標準,使用target_compile_features()搭配像是 cxx_std_11 的元功能 (meta-feature),或者設定 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 編譯器環境版本 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++ 列出的編譯器和版本,但僅包含 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>_FLAGSCMAKE_<LANG>_FLAGS_<CONFIG> 的語言範圍標誌之後。

在 3.26 版本變更:語言標準標誌會放置在其他抽象概念(例如 target_compile_options() 命令)指定的標誌之前。在 CMake 3.26 之前,語言標準標誌會放置在這些標誌之後。