1. 程式人生 > >如何構建一個CMake項目(譯)

如何構建一個CMake項目(譯)

生成 解決方案 mar ria com ima configure 如何配置 brush

CMake是一個能幫助你在幾乎所有平臺上構建C/C++項目的工具。很多流行的開源項目都使用了CMake,例如:LLVM, Qt, KDE 和 Blender。

所有的CMake項目都包含一個叫做 CMakelists.txt 的腳本,這篇博客就是為了指導如何配置和構建CMake項目而寫作的。這篇博客不會告訴你如何編寫CMake腳本-那樣做的話有些超前了。

舉個例子,我準備了一個使用SDL2 和 OpenGL來渲染一個3D logo的CMake項目,你可以在Windows, MacOS 或者 Linux下構建此項目。如果你還沒有安裝CMake,你可以去CMake官網下載安裝器或者二進制分發版本,也可以通過系統的包管理工具下載安裝(Unix-like 環境)。

Source和Binary文件夾

CMake會生成一個構建流水線(build pipelines)文件。一個構建流水線文件可能是Visual Studio的 .sln 文件, Xcode的 .xcodeproj 文件 或者是Unix風格的 Makefile 文件。構建流水線文件也可能以其他形式出現。

要生成構建流水線文件,CMake需要知道source目錄和binary目錄。source目錄會存放CMakeLists.txt 文件。 binary目錄下存放CMake生成的構建流水線文件。你可以將binary目錄放到任何位置,一個常見的做法是在CMakelist.txt下創建一個叫build的子目錄作為binary目錄。

技術分享圖片

通過分離binary和source目錄,你可以在任何時候刪掉binary目錄來回到幹凈的狀態。你甚至可以創建多個binary目錄,每一個對應不同的構建系統或者參數配置。

緩存(cache)也是一個重要的概念。它是binary目錄下一個叫做 CMakeCache.txt 的文件,裏面存放著緩存變量(cache variables)。緩存變量包括用戶配置選項-例如CMakeDemo中的 DEMO_ENABLE_MULTISAMPLE 選項, 以及用於加入編譯的預計算信息。binary文件夾一般不會加入版本管理,通常做法是每次clone時重新構建binary目錄。

配置(Configure)和生成(Generate)

下文將介紹幾種不同運行CMake的方式。但不管你如何運行,CMake都會執行以下兩步: 配置(configure)和 生成(generate)。

技術分享圖片

CMakeLists.txt在配置階段執行。這個腳本負責定義目標文件(Targets)。每個目標文件可以是可執行文件,庫文件或者是構建流水線的其他輸出。

如果配置階段成果完成-即 CMakeLists.txt運行成功-CMake將用CMakeLists.txt定義的目標文件生成一個構建流水線。 構建流水線的類型取決於生成器(generator)的類型,後面章節將詳細解釋。

根據CMakeLists.txt內容的不同,配置階段可能發生額外的事情。例如,在上述 CMakeDemo 項目中,配置階段還做了以下:

  • 尋找 SDL2 和 OpenGL的頭文件和庫
  • 在binary目錄生成頭文件 demo-config.h- 它將在C++代碼中被引用

技術分享圖片

在更復雜的項目中,配置階段也許還會檢測系統函數是否可用(傳統Unix configue腳本都會這麽做),或者定義特殊的“安裝(install)”目標文件(為了創建一個發行包)。如果你在binary文件夾下重新運行CMake的話,因為使用了緩存,很多緩慢的步驟都會被跳過。

在命令行中運行CMake

在運行CMake之前,請確保你有項目和平臺要求的依賴。我們經常需要告訴CMake用何種生成器(generator),你可以使用 cmake --help來查看可以選擇的生成器。

技術分享圖片

創建一個binary目錄,cd 到該目錄,然後運行cmake, 在命令行中指定到source目錄的路徑。通過 -G 選項指定生成器(如果未指定的話,會使用默認生成器)。

mkdir build
cd build
cmake -G "Visual Studio 15 2017" ..

如果有項目自身的配置選項的話,你也可以在命令行中指定。例如, CMakeDemo 項目中的 DEMO_ENABLE_MULTISAMPLE 的默認值是0, 你可以通過制定 -DDEMO_ENABLE_MULTISAMPLE=1 來打開此功能。修改 DEMO_ENABLE_MULTISAMPLE 的值會修改 demo-config.h中的內容。變量的值也存儲在緩存中,這樣我們可以在後續運行中使用。

cmake -G "Visual Studio 15 2017" -DDEMO_ENABLE_MULTISAMPLE=1 ..

如果你後面想改變 DEMO_ENABLE_MULTISAMPLE 的值,只用重新運行CMake即可。後續重新運行不需要傳入source目錄路徑,只用簡單制定已經存在的binary目錄即可。CMake將在binary目錄下找到之前緩存中的設置並且重用它們。

cmake -DDEMO_ENABLE_MULTISAMPLE=0 .

你可以通過 cmake -L -N . 來查看項目定義的緩存變量。如下圖所示,你可以看到 CMakeDemo 的 DEMO_ENABLE_MULTISAMPLE 選項被置回0:

技術分享圖片

運行 cmake-gui

我更喜歡通過命令行使用CMake,但CMake也有GUI。GUI提供了一個交互式的方式來設置緩存變量。再強調一遍,首先確認你安裝好了項目所需的依賴。

運行 cmake-gui 來打開圖形界面,填好source目錄和binary目錄的路徑,然後點擊Configure 。

技術分享圖片

如果binary目錄還不存在,CMake會詢問你是否創建該文件夾。然後CMake會讓你選擇一個生成器。

技術分享圖片

在初始配置完成後, GUI會顯示一列緩存變量 - 和在命令行中運行 cmake -L -N . 的結果類似。新的緩存變量會以紅色高亮顯示(在下圖中,所有的緩存變量都是新的)。如果你再次點擊Configure, 紅色的高亮將會消失,因為這些緩存變量不再是新的了。

技術分享圖片

當你完成緩存變量的設置後,點擊Generate。這將在binary目錄下生成構建流水線文件,你可以用它們來構建你的項目。

運行 ccmake

ccmake 就像是命令行下的 cmake-gui。就像GUI一樣,你可以交互式的設置緩存變量。

技術分享圖片

使用Unix Makefiles來構建

在Unix-like環境下運行CMake會默認生成一個Unix makefile。當然,你可以用 -G 選項顯式生成makefiles。生成makefile時,你應該定義 CMAKE_BUILD_TYPE 變量。假如souce目錄是binary目錄的父目錄:

cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug ..

因為CMake生成的makefiles都是 單一配置(single-configuration)的,你需要自己定義 CMAKE_BUILD_TYPE。不像Visual Studio中的解決方案(solution),你不能用同一個makefile來構建諸如Debug和Release等多種配置。一個makefile只能完成一種構建。默認的構建類型有: Debug,MinSizeRel,RelwithDebInfo 和 Release。當心使用 CMAKE_BUILD_TYPE, 如果你忘了定義 CMAKE_BUILD_TYPE,你可能得到一個沒有debug信息的未優化的構建。要改變構建類型,你必須重新運行CMake並生成新的makefile。

個人而言,我發現CMake默認的 Release 配置沒什麽用,因為這個配置不生成任何debug信息。 如果你曾打開過崩潰信息(crash dump)或者在 Release 版本中修復過bug, 你將感謝即使是在優化構建中debug信息的存在。因此,在我其他的CMake項目中,我經常刪除CMakeLists.txt中的Release配置並使用 RelWithDebInfo配置。

makefile文件存在後,你就能用 make 命令來構建你的項目了。make 默認會構建 CMakeLists.txt 中定義的每個目標文件。 在 CMakeDemo 的例子中,只有一個目標文件需要構建。你還可以通過傳入目標文件的名字給 make 來構建一個指定的目標文件:

make CMakeDemo

由CMake生成的makefile能自動檢測頭文件依賴,所以編輯一個都文件不必重新構建成歌項目。你還可以通過傳入 -j 4(或者更大的數字)給 make 來並行化構建。

CMake還可使用 Ninja生成器。 Ninja類似於 make, 但是比 make 更快。Ninja生成器會生成一個 build.ninja 文件 - 類似於 Makefile 文件。Ninja 生成器同樣是單一配置的。Ninja的 -j 選項自動調整成可用的 CPU 數。

其他CMake 特性

  • 你可以在命令行下不指定生成器運行構建
  • cmake --build . --target CMakeDemo --config Debug
  • 你可以借助 CMAKE_TOOLCHAIN_FILE 變量來穿件構建流水線以實現交叉編譯
  • 你可以生成一個 compile_commands.json 文件給 Clang 的 LibTooling 庫使用

限於我的興趣範圍,描述在 VS, Xcode, Qt下使用CMake的章節暫且不翻譯,有興趣的讀者可以參閱原文(https://preshing.com/20170511/how-to-build-a-cmake-based-project/)。

如何構建一個CMake項目(譯)