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)或透過設定 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 目前了解以下 編譯器 識別碼 中的 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 目前了解以下 編譯器 識別碼 中的 C 標準編譯 功能,以及每個指定的版本

  • 上述針對 C++ 列出的所有編譯器和版本。

  • GNU:GNU 編譯器版本 3.4+

CMake 目前了解以下 編譯器 識別碼 中的 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 目前了解以下 編譯器 識別碼 中的 C 標準 及其相關的元功能(例如 c_std_99),以及每個指定的版本

  • 上述僅具有 C++ 元功能的所有編譯器和版本。

CMake 目前了解以下 編譯器 識別碼 中的 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 之前,語言標準旗標放置在它們之後。