WriteCompilerDetectionHeader

自 3.20 版本起已棄用:此模組僅在政策 CMP0120 未設定為 NEW 時才可用。請勿在新程式碼中使用。

新增於 3.1 版本。

此模組提供函數 write_compiler_detection_header()

此函數可用於產生適合預處理器包含的檔案,其中包含要在原始碼中使用的巨集。

write_compiler_detection_header(
          FILE <file>
          PREFIX <prefix>
          [OUTPUT_FILES_VAR <output_files_var> OUTPUT_DIR <output_dir>]
          COMPILERS <compiler> [...]
          FEATURES <feature> [...]
          [BARE_FEATURES <feature> [...]]
          [VERSION <version>]
          [PROLOG <prolog>]
          [EPILOG <epilog>]
          [ALLOW_UNKNOWN_COMPILERS]
          [ALLOW_UNKNOWN_COMPILER_VERSIONS]
)

這會產生檔案 <file>,其中包含所有具有前綴 <prefix> 的巨集。

預設情況下,所有內容都會直接寫入 <file>。可以指定 OUTPUT_FILES_VAR,使編譯器特定的內容寫入到單獨的檔案中。然後,可以在 <output_files_var> 中取得這些單獨的檔案,並且可以由呼叫者使用它們,例如進行安裝。 OUTPUT_DIR 指定從主 <file> 到編譯器特定檔案的相對路徑。例如:

write_compiler_detection_header(
  FILE climbingstats_compiler_detection.h
  PREFIX ClimbingStats
  OUTPUT_FILES_VAR support_files
  OUTPUT_DIR compilers
  COMPILERS GNU Clang MSVC Intel
  FEATURES cxx_variadic_templates
)
install(FILES
  ${CMAKE_CURRENT_BINARY_DIR}/climbingstats_compiler_detection.h
  DESTINATION include
)
install(FILES
  ${support_files}
  DESTINATION include/compilers
)

可以使用 VERSION 來指定要產生的 API 版本。CMake 的未來版本可能會引入替代 API。透過任何大於或等於引入給定 API 的 CMake 版本且小於引入其後續 API 的 CMake 版本的 <version> 值來選擇給定的 API。如果未明確指定版本,則會使用 CMAKE_MINIMUM_REQUIRED_VERSION 變數的值。(截至 CMake 3.31.0 版本,只有一個 API 版本。)

可以將 PROLOG 指定為要在標頭開頭寫入的文字內容。EPILOG 可以指定為要在標頭末尾寫入的文字內容。

至少必須列出一個 <compiler> 和一個 <feature>。CMake 已知的但未指定的編譯器會被偵測到,並為它們產生一個預處理器 #error。為每個 CMake 已知的編譯器產生一個符合 <PREFIX>_COMPILER_IS_<compiler> 的預處理器巨集,其中包含值 01

可能的編譯器識別碼記錄在 CMAKE_<LANG>_COMPILER_ID 變數中。此版本的 CMake 中可用的功能列在 CMAKE_C_KNOWN_FEATURESCMAKE_CXX_KNOWN_FEATURES 全域屬性中。有關編譯功能的資訊,請參閱 cmake-compile-features(7) 手冊。

新增於 3.2 版本:新增 MSVCAppleClang 編譯器支援。

新增於 3.6 版本:新增 Intel 編譯器支援。

變更於 3.8 版本:如果請求 {c,cxx}_std_* 元功能,則會忽略它們。

新增於 3.8 版本:ALLOW_UNKNOWN_COMPILERSALLOW_UNKNOWN_COMPILER_VERSIONS 使模組產生將未知編譯器視為僅缺少所有功能的條件。如果沒有這些選項,預設行為是為未知編譯器和版本產生 #error

新增於 3.12 版本:BARE_FEATURES 將使用較新版本的語言標準中使用的名稱來定義相容性巨集,以便程式碼可以無條件地使用新功能名稱。

功能測試巨集

對於每個編譯器,都會產生一個符合 <PREFIX>_COMPILER_IS_<compiler> 的預處理器巨集,其內容為 01,具體取決於所使用的編譯器。如果已定義,則會產生符合 <PREFIX>_COMPILER_VERSION_MAJOR<PREFIX>_COMPILER_VERSION_MINOR<PREFIX>_COMPILER_VERSION_PATCH 的編譯器版本元件的預處理器巨集,其中包含相應編譯器版本元件的十進制值。

會根據編譯器版本產生預處理器測試,表示是否啟用每個功能。產生一個符合 <PREFIX>_COMPILER_<FEATURE> 的預處理器巨集,其中 <FEATURE> 是大寫的 <feature> 名稱,以包含值 01,具體取決於所使用的編譯器是否支援該功能。

write_compiler_detection_header(
  FILE climbingstats_compiler_detection.h
  PREFIX ClimbingStats
  COMPILERS GNU Clang AppleClang MSVC Intel
  FEATURES cxx_variadic_templates
)
#if ClimbingStats_COMPILER_CXX_VARIADIC_TEMPLATES
template<typename... T>
void someInterface(T t...) { /* ... */ }
#else
// Compatibility versions
template<typename T1>
void someInterface(T1 t1) { /* ... */ }
template<typename T1, typename T2>
void someInterface(T1 t1, T2 t2) { /* ... */ }
template<typename T1, typename T2, typename T3>
void someInterface(T1 t1, T2 t2, T3 t3) { /* ... */ }
#endif

符號巨集

為特定功能建立一些額外的符號定義,用作可以有條件地定義為空的符號。

class MyClass ClimbingStats_FINAL
{
    ClimbingStats_CONSTEXPR int someInterface() { return 42; }
};

如果編譯器(及其標誌)支援 cxx_final 功能,則 ClimbingStats_FINAL 巨集將擴充為 final;如果支援 cxx_constexpr,則 ClimbingStats_CONSTEXPR 巨集將擴充為 constexpr

如果 BARE_FEATURES cxx_final 作為引數給出,則也會為舊編譯器定義 final 關鍵字。

以下功能會產生相應的符號定義,如果它們以 BARE_FEATURES 提供的話

功能

定義

符號

c_restrict

<PREFIX>_RESTRICT

restrict

cxx_constexpr

<PREFIX>_CONSTEXPR

constexpr

cxx_deleted_functions

<PREFIX>_DELETED_FUNCTION

= delete

cxx_extern_templates

<PREFIX>_EXTERN_TEMPLATE

extern

cxx_final

<PREFIX>_FINAL

final

cxx_noexcept

<PREFIX>_NOEXCEPT

noexcept

cxx_noexcept

<PREFIX>_NOEXCEPT_EXPR(X)

noexcept(X)

cxx_override

<PREFIX>_OVERRIDE

override

相容性實作巨集

如果編譯器不支援該功能,則某些功能適合包裝在具有向後相容性實作的巨集中。

當編譯器未提供 cxx_static_assert 功能時,可透過 <PREFIX>_STATIC_ASSERT(COND)<PREFIX>_STATIC_ASSERT_MSG(COND, MSG) 函式式巨集取得相容性實作。巨集會擴充為 static_assert,在該編譯器功能可用時,否則會擴充為相容性實作。在第一種形式中,條件會在 static_assert 的訊息欄位中字串化。在第二種形式中,訊息 MSG 會傳遞給 static_assert 的訊息欄位,如果使用向後相容性實作,則會忽略該訊息。

cxx_attribute_deprecated 功能提供巨集定義 <PREFIX>_DEPRECATED,它會擴充為標準 [[deprecated]] 屬性,或編譯器特定的裝飾器,例如 GNU 編譯器使用的 __attribute__((__deprecated__))

cxx_alignas 功能提供巨集定義 <PREFIX>_ALIGNAS,它會擴充為標準 alignas 裝飾器,或編譯器特定的裝飾器,例如 GNU 編譯器使用的 __attribute__ ((__aligned__))

cxx_alignof 功能提供巨集定義 <PREFIX>_ALIGNOF,它會擴充為標準 alignof 裝飾器,或編譯器特定的裝飾器,例如 GNU 編譯器使用的 __alignof__

功能

定義

符號

cxx_alignas

<PREFIX>_ALIGNAS

alignas

cxx_alignof

<PREFIX>_ALIGNOF

alignof

cxx_nullptr

<PREFIX>_NULLPTR

nullptr

cxx_static_assert

<PREFIX>_STATIC_ASSERT

static_assert

cxx_static_assert

<PREFIX>_STATIC_ASSERT_MSG

static_assert

cxx_attribute_deprecated

<PREFIX>_DEPRECATED

[[deprecated]]

cxx_attribute_deprecated

<PREFIX>_DEPRECATED_MSG

[[deprecated]]

cxx_thread_local

<PREFIX>_THREAD_LOCAL

thread_local

當使用此類棄用巨集時,會出現一個案例是棄用整個函式庫。在這種情況下,函式庫中所有公開的 API 都可以使用 <PREFIX>_DEPRECATED 巨集來裝飾。當建置函式庫本身時,這會導致非常雜亂的建置輸出,因此在建置已棄用的函式庫時,可以將巨集定義為空。

add_library(compat_support ${srcs})
target_compile_definitions(compat_support
  PRIVATE
    CompatSupport_DEPRECATED=
)

範例用法

注意

此章節從 cmake-compile-features(7) 手冊遷移而來,因為它依賴於 WriteCompilerDetectionHeader 模組,該模組已由政策 CMP0120 移除。

如果可以使用,則可以優先選擇編譯功能,而無需建立硬性要求。例如,函式庫可以根據 cxx_variadic_templates 功能是否可用來提供替代的實作方式。

#if Foo_COMPILER_CXX_VARIADIC_TEMPLATES
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();
  }
};
#else
template<int I1, int I2 = 0, int I3 = 0, int I4 = 0>
struct Interface
{
  static int accumulate() { return I1 + I2 + I3 + I4; }
};
#endif

此類介面取決於為編譯器功能使用正確的預處理器定義。CMake 可以使用 WriteCompilerDetectionHeader 模組來產生包含此類定義的標頭檔。該模組包含 write_compiler_detection_header 函式,該函式接受參數來控制產生的標頭檔的內容。

write_compiler_detection_header(
  FILE "${CMAKE_CURRENT_BINARY_DIR}/foo_compiler_detection.h"
  PREFIX Foo
  COMPILERS GNU
  FEATURES
    cxx_variadic_templates
)

此類標頭檔可以在專案的原始碼中內部使用,並且可以安裝並在函式庫程式碼的介面中使用。

對於 FEATURES 中列出的每個功能,都會在標頭檔中建立預處理器定義,並定義為 10

此外,某些功能需要額外的定義,例如 cxx_finalcxx_override 功能。 final 關鍵字不是在 #ifdef 程式碼中使用,而是由一個符號抽象化,該符號定義為 final、編譯器特定的等效項或空。這樣,C++ 程式碼可以編寫為無條件使用符號,而編譯器支援會決定它擴展為什麼。

struct Interface {
  virtual void Execute() = 0;
};

struct Concrete Foo_FINAL {
  void Execute() Foo_OVERRIDE;
};

在這種情況下,如果編譯器支援該關鍵字,則 Foo_FINAL 將會擴展為 final,否則擴展為空。

在此使用案例中,專案程式碼可能希望在編譯器提供的情況下啟用特定的語言標準。可以為特定目標將 CXX_STANDARD 目標屬性設定為所需的語言標準,並且可以設定 CMAKE_CXX_STANDARD 變數來影響所有後續目標。

write_compiler_detection_header(
  FILE "${CMAKE_CURRENT_BINARY_DIR}/foo_compiler_detection.h"
  PREFIX Foo
  COMPILERS GNU
  FEATURES
    cxx_final cxx_override
)

# Includes foo_compiler_detection.h and uses the Foo_FINAL symbol
# which will expand to 'final' if the compiler supports the requested
# CXX_STANDARD.
add_library(foo foo.cpp)
set_property(TARGET foo PROPERTY CXX_STANDARD 11)

# Includes foo_compiler_detection.h and uses the Foo_FINAL symbol
# which will expand to 'final' if the compiler supports the feature,
# even though CXX_STANDARD is not set explicitly.  The requirement of
# cxx_constexpr causes CMake to set CXX_STANDARD internally, which
# affects the compile flags.
add_library(foo_impl foo_impl.cpp)
target_compile_features(foo_impl PRIVATE cxx_constexpr)

write_compiler_detection_header 函式還會為具有標準等效項的其他功能建立相容性程式碼。例如, cxx_static_assert 功能會使用範本模擬,並透過 <PREFIX>_STATIC_ASSERT<PREFIX>_STATIC_ASSERT_MSG 函式巨集來抽象化。