<<Modern CMake>> 翻譯 2. CMake 基礎

資料管理樂園發表於2019-07-25

<<Modern CMake>> 翻譯 2. CMake 基礎

最低版本

這是每個 CMakeLists.txt 檔案的第一行。CMakeLists.txt 是 CMake 所需的配置檔名稱:

cmake_minimum_required(VERSION 3.1)

我們來了解一點 CMake 語法。 命令名稱 cmake_minimum_required 不區分大小寫,因此通常的做法是使用小寫。1 這裡 VERSION 是該命令所需的特殊關鍵字。 版本號緊跟在 VERSION 關鍵字之後。 與本書中的任何其他地方一樣,你只需單擊命令名稱即可連結到官方文件,然後可以使用下拉選單切換不同版本的 CMake 文件。

這一行很特別!2 版本號也同時指明瞭 CMake 的行為變化。 因此,如果你設定 minimum_required 為 VERSION 2.8,在macOS上你就會獲得錯誤的連結行為, 例如,在最新的 CMake 版本中也是如此。 如果你把版本設定為 3.3 或更低,你會得到錯誤的符號隱藏行為,等等。 在 policies 有一個策略和版本列表。

在 CMake 3.12 中,可以這樣寫來指定支援的 CMake 版本範圍,例如 VERSION 3.1...3.12; 這意味著您最低支援 3.1,同時也測試過並支援到 3.12 的新策略。 這對於需要更好設定的使用者來說很不錯,並且由於語法上的技巧,它向後相容舊版本的 CMake(儘管實際執行 CMake 3.2-3.11 只會在此示例中設定 3.1 版本的策略)。 新版本的策略對於 macOS 和 Windows 使用者來說往往是最重要的,他們通常也有最新版本的 CMake。

新專案應該這樣寫:

cmake_minimum_required(VERSION 3.1...3.15)

if(${CMAKE_VERSION} VERSION_LESS 3.12)
    cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
endif()

如果 CMake 版本小於 3.12,則 if 塊將為 true,並且策略將設定為當前 CMake 版本。 如果 CMake 為 3.12 或更高,if 塊將為 false,此時新語法 cmake_minimum_required 將起作用,這將能夠正常工作!

警告:MSVC 的 CMake 伺服器模式最初在讀取此格式時有一個 bug, 因此如果您需要支援舊版 MSVC 的非命令列 Windows 版本,則需要執行以下操作:

cmake_minimum_required(VERSION 3.1)

if(${CMAKE_VERSION} VERSION_LESS 3.15)
    cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
    cmake_policy(VERSION 3.15)
endif()

 

 

如果您確實需要在此處設定較低的版本,則可以使用 cmake_policy 有條件地增加策略級別或設定特定策略。 請至少為您的 macOS 使用者執行此操作!

 

設定專案

現在,每個頂級 CMake 檔案(CMakeLists.txt)都有下面這一行:

project(MyProject VERSION 1.0
                  DESCRIPTION "非常出色的專案"
                  LANGUAGES CXX)

現在我們看到了更多的新語法。 字串被引號包圍起來,空格可多可少,專案名稱是第一個引數(位置引數)。 這裡的所有的關鍵字引數都是可選的。 通過 VERSION 引數,這會設定了一堆變數,比如 MyProject_VERSION 和 PROJECT_VERSION。 LANGUAGE 可以是 C,CXX,Fortran 和 CUDA(CMake 3.7+)。 C CXX 是預設值。 在 CMake 3.9 中,DESCRIPTION 新增進來設定對專案的描述。 你可以參考 project 這個文件。

 

 

你可以通過使用 # 開頭來新增 註釋。 CMake 也有註釋的內聯語法,但很少需要,因為空格並不重要。

 

專案名稱沒什麼特別之處。此時不新增任何目標。

生成可執行檔案

雖然連結庫更有趣,通常我們大部分時間都在生成連結庫,但這裡我們要從一個簡單的可執行檔案開始。

add_executable(one two.cpp three.h)

這裡有幾件要說明的事情。 one 是生成的可執行檔案的名稱,同時也是建立的 CMake 目標的名稱(我保證你會很快聽到很多關於目標的資訊)。 可執行檔名稱後緊接的是原始檔列表,您可以根據需要列出任意數量的原始檔列表。 CMake 很聰明,會根據副檔名正確識別原始檔,所以列表中的標頭檔案會被 CMake 理解並忽略。 大多數時候,我們在原始檔列表中列出標頭檔案的唯一原因是讓它們出現在 IDE 中。 有關通用構建系統和目標的更多資訊,請參見 buildsystem

生成連結庫

使用 add_library 生成連結庫, 也是非常簡單:

add_library(one STATIC two.cpp three.h)

您可以選擇連結庫型別:STATIC,SHARED 或 MODULE。 如果沒有設定,CMake 會根據變數 BUILD_SHARED_LIBS 的值在 STATIC 和 SHARED 之間選擇。

正如您將在下一節中看到的那樣,通常您需要建立一個偽目標,也就是一個不需要編譯任何檔案的目標,例如,對於僅包含標頭檔案的庫。這也可以稱為 INTERFACE 庫; 唯一的區別是介面庫不能跟檔名。

您還可以用一個現有的連結庫生成一個 ALIAS 連結庫,該庫簡單地為您提供目標的新名稱。這樣做的一個好處是你可以生成一個名稱中帶 :: 的連結庫(稍後會看到)。3

配置構建目標

現在我們已經指定了目標,然後我們怎麼給它新增相關資訊呢?例如,它可能需要一個 include 目錄:

target_include_directories(one PUBLIC include)

target_include_directories 將 include 目錄新增到目標. PUBLIC 對可執行檔案來說意義不大; 對於一個連結庫,它讓 CMake 知道連結到這個目標的任何目標也必須包含該目錄。 其他選項是 PRIVATE(僅影響當前目標,而不影響依賴項)和 INTERFACE(僅限依賴項所需)。

現在,我們可以把目標串聯起來:

add_library(another STATIC another.cpp another.h)
target_link_libraries(another PUBLIC one)

target_link_libraries 可能是 CMake 中最有用也最令人困惑的命令。 它需要一個target(another)並新增依賴目標項。 如果名為 one 的目標不存在,則它會新增指向路徑上的一個叫做 one 連結庫(即命令的名稱)。 或者你可以給它一個完整的連結庫路徑。或連結器標誌。 最後還有一點容易混淆的東西,那就是經典的 CMake 允許你忽略關鍵字 PUBLIC,等等。 如果目標已經連結完成,嘗試在鏈中進一步混合樣式,你會收到錯誤。

主要關注在任何地方使用目標和關鍵字,這就對了。

目標可以包含目錄,連結庫(或連結目標),編譯選項,編譯定義,編譯特徵(參見 C++11 章節)等。 正如您將在兩個包括專案章節中看到的那樣,您通常可以使用目標(並始終制作目標)來表示所有你使用的連結庫。 即使那不是真正的連結庫的,比如 OpenMP,也可以用目標來表示。 這就是現代 CMake 很棒的原因!

開始動手實踐

看看您是否可以理解以下檔案操作。 它建立了一個簡單的 C++11 連結庫和一個使用它的程式。 沒有依賴。 我稍後將使用 CMake 3.8 系統討論更多 C++ 標準選項。

cmake_minimum_required(VERSION 3.8)

project(Calculator LANGUAGES CXX)

add_library(calclib STATIC src/calclib.cpp include/calc/lib.hpp)
target_include_directories(calclib PUBLIC include)
target_compile_features(calclib PUBLIC cxx_std_11)

add_executable(calc apps/calc.cpp)
target_link_libraries(calc PUBLIC calclib)
1. 在這本書中,我將盡可能避免向你展示錯誤的做事方式; 你可以在網上找到很多這方面的例子。我偶爾會提到替代做法,但除非絕對必要,否則不推薦這些。通常它們只是幫助您閱讀較老的 CMake 程式碼。 ↩
2. 你有時會看到 FATAL_ERROR, 在 CMake <2.6 版本中,需要使用它來支援失敗,現在已經不需要了。
3. :: 語法最初用來生成 INTERFACE IMPORTED 連結庫, 但是,正因為如此,大多數 target_* 命令都不適用於 IMPORTED 連結庫。這使得它們很難自行設定。所以現在不要使用 IMPORTED 關鍵字,請使用 ALIAS 構件目標; 這在你匯出目標前都能正常工作。此限制已經在 CMake 3.11 中修復。 ↩

相關文章