跳到正文

Wiki

CMakePresets與構建安裝

約 19 分鐘閱讀

本文由簡體中文內容確定性轉換,並受版本化術語表保護。

CMakePresets.json 用來儲存常用的 CMake 配置。它解決的問題是:不要每次都手寫一長串 cmake -S ... -B ... -G ... -D... 命令。

本模板的 CMakePresets.json 如下:

{
  "version": 3,
  "cmakeMinimumRequired": {
    "major": 3,
    "minor": 22,
    "patch": 0
  },
  "configurePresets": [
    {
      "name": "base",
      "hidden": true,
      "generator": "Ninja",
      "binaryDir": "${sourceDir}/build/${presetName}",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug",
        "CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
        "CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}"
      }
    },
    {
      "name": "linux-debug",
      "displayName": "Linux Debug",
      "inherits": "base",
      "condition": {
        "type": "equals",
        "lhs": "${hostSystemName}",
        "rhs": "Linux"
      },
      "cacheVariables": {
        "BUILD_SHARED_LIBS": "ON"
      }
    },
    {
      "name": "linux-release",
      "displayName": "Linux Release",
      "inherits": "linux-debug",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release"
      }
    },
    {
      "name": "windows-debug",
      "displayName": "Windows GCC Debug",
      "inherits": "base",
      "condition": {
        "type": "equals",
        "lhs": "${hostSystemName}",
        "rhs": "Windows"
      },
      "cacheVariables": {
        "CMAKE_C_COMPILER": "gcc",
        "CMAKE_CXX_COMPILER": "g++",
        "BUILD_SHARED_LIBS": "OFF"
      }
    },
    {
      "name": "windows-release",
      "displayName": "Windows GCC Release",
      "inherits": "windows-debug",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release"
      }
    }
  ],
  "buildPresets": [
    {
      "name": "linux-debug",
      "configurePreset": "linux-debug"
    },
    {
      "name": "linux-release",
      "configurePreset": "linux-release"
    },
    {
      "name": "windows-debug",
      "configurePreset": "windows-debug"
    },
    {
      "name": "windows-release",
      "configurePreset": "windows-release"
    }
  ],
  "testPresets": [
    {
      "name": "linux-debug",
      "configurePreset": "linux-debug",
      "output": {
        "outputOnFailure": true
      }
    },
    {
      "name": "linux-release",
      "configurePreset": "linux-release",
      "output": {
        "outputOnFailure": true
      }
    },
    {
      "name": "windows-debug",
      "configurePreset": "windows-debug",
      "output": {
        "outputOnFailure": true
      }
    },
    {
      "name": "windows-release",
      "configurePreset": "windows-release",
      "output": {
        "outputOnFailure": true
      }
    }
  ]
}

後面逐項解釋時,很多地方繼續用 linux-debug 舉例。Windows 對應使用 windows-debug,Release 則分別使用 linux-release、windows-release;當前系統可選擇的配置由 condition 決定。

1.根欄位

1.1.version

"version": 3

表示 CMakePresets.json 檔案格式版本,不是專案版本。

可以填什麼:

值 含義
1、2、3、... CMake preset 檔案格式版本
3 本模板使用的版本,CMake 3.21 起支援;本工程統一要求 CMake 3.22 及以上
6 CMake 3.25 起支援,新增打包預設和工作流預設等功能

注意:

  1. 這裡不是 cmake_template 的版本號。
  2. 如果想相容更老的 CMake,要降低 version,但某些欄位可能不能再用。
  3. 如果本機 CMake 很新,也不一定要用最高 preset 版本;夠用即可。

本模板只使用格式 3 已支援的配置、構建、測試和平臺條件欄位,因此 Ubuntu 22.04 的 CMake 3.22.1 就能讀取。格式與欄位說明見 CMake 3.22 預設文件;格式 6 的新增功能見 CMake 3.25 預設文件。

1.2.cmakeMinimumRequired

"cmakeMinimumRequired": {
  "major": 3,
  "minor": 22,
  "patch": 0
}

宣告工程要求的最低 CMake 版本。CMake 還會單獨檢查自己是否支援前面的預設格式版本,兩個條件都要滿足。

可以填什麼:

欄位 示例 含義
major 3 主版本號
minor 22 次版本號
patch 0 補丁版本號

本模板以 CMake 3.22 為相容基線,頂層 CMakeLists.txt 同樣寫為:

cmake_minimum_required(VERSION 3.22)

二者保持一致。Ubuntu 22.04 可以直接使用 CMake 3.22.1;如果把舊模板改為這個配置,需要同時修改頂層最低版本、預設格式 version 和這裡的 minor 三處,原有的 Debug / Release 預設、目錄和 Ninja 生成器保持不變。

2.configurePresets

configurePresets 是"配置階段"的 preset。

配置階段對應命令:

cmake --preset linux-debug

它等價於告訴 CMake:

  1. 使用哪個生成器。
  2. 構建目錄在哪裡。
  3. 構建型別是什麼。
  4. 安裝目錄在哪裡。
  5. 是否生成 compile_commands.json。

2.1.name

"name": "linux-debug"

preset 的機器可讀名稱。

可以填什麼:

寫法 說明
linux-debug 本模板 Debug preset
linux-release 本模板 Release preset
debug 也可以,但不如 linux-debug 清楚
gcc-debug 如果區分編譯器,可以這樣命名
clang-debug 如果新增 Clang preset,可以這樣命名

要求:

  1. 同一個數組裡不能重名。
  2. buildPresets 裡的 configurePreset 要引用這裡的名字。
  3. 命令列使用的就是這個名字。

示例:

cmake --preset linux-debug

2.2.displayName

"displayName": "Linux Debug"

給人看的名字,主要用於 IDE 顯示,例如 VSCode CMake Tools 的 preset 列表。

可以填什麼:

寫法 說明
Linux Debug 清晰
Linux Release 清晰
Debug 可以,但不區分平臺
Fedora GCC Debug 如果 preset 很多,可以寫更具體

這個欄位不影響實際構建邏輯。

2.3.generator

"generator": "Ninja"

指定 CMake 要生成什麼構建系統檔案。

Linux 常見可選值:

值 說明
Ninja 推薦,速度快,輸出乾淨,適合 VSCode CMake Tools
Unix Makefiles 傳統 Makefile 工作流
Ninja Multi-Config 多配置 Ninja,可同時管理 Debug / Release

本模板只支援 Linux,並且使用 Ninja,所以固定寫:

"generator": "Ninja"

如果使用 Unix Makefiles,構建命令仍然可以用:

cmake --build --preset linux-debug

但底層就不是 Ninja 了。

2.4.binaryDir

"binaryDir": "${sourceDir}/build/linux-debug"

指定構建目錄。

可以填什麼:

寫法 說明
${sourceDir}/build/linux-debug 推薦,構建產物留在專案內的 build 子目錄
${sourceDir}/build/debug 也可以,但不如 linux-debug 清楚
/tmp/my-build 可以放到專案外,但不適合作為模板預設值

常見宏:

宏 含義
${sourceDir} 專案原始碼根目錄
${presetName} 當前 preset 名稱
${sourceParentDir} 原始碼根目錄的父目錄

也可以寫成:

"binaryDir": "${sourceDir}/build/${presetName}"

這樣 linux-debug 會自動生成到:

build/linux-debug

本模板顯式寫出目錄,是為了讓初學時更直觀。

2.5.cacheVariables

"cacheVariables": {
  "CMAKE_BUILD_TYPE": "Debug",
  "CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
  "CMAKE_INSTALL_PREFIX": "${sourceDir}/install/linux-debug"
}

cacheVariables 等價於命令列中的 -D 引數。

例如:

"CMAKE_BUILD_TYPE": "Debug"

等價於:

-DCMAKE_BUILD_TYPE=Debug

3.常用 cache 變數

3.1.CMAKE_BUILD_TYPE

"CMAKE_BUILD_TYPE": "Debug"

單配置生成器中使用的構建型別。Ninja 和 Unix Makefiles 都屬於常見的單配置生成器。

可以填什麼:

值 作用
Debug 除錯構建,通常帶 -g,最佳化較少
Release 釋出構建,通常開啟最佳化
RelWithDebInfo 最佳化 + 除錯資訊
MinSizeRel 最佳化體積

常用選擇:

場景 推薦
寫程式碼、除錯斷點 Debug
釋出、效能測試 Release
效能測試但還想保留符號 RelWithDebInfo
嵌入式或體積敏感 MinSizeRel

3.2.CMAKE_EXPORT_COMPILE_COMMANDS

"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"

是否生成 compile_commands.json。

可以填什麼:

值 作用
ON 生成 compile_commands.json
OFF 不生成

compile_commands.json 記錄每個原始檔真實的編譯命令。clangd、VSCode、很多靜態分析工具都會用它來獲得 include 路徑、巨集定義、編譯標準等資訊。

建議模板裡固定開啟:

"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"

3.2.1.讓 clangd 找到 compile_commands.json

僅僅生成 compile_commands.json,不代表 clangd 一定能自動找到它。

在 VSCode 中使用這種方式前,需要安裝 LLVM 官方的 clangd 擴充套件。 CMake Tools 負責配置、構建和選擇 target,clangd 則負責程式碼補全、 跳轉定義、語義檢查等編輯功能,它們不是同一個擴充套件。

這個模板把 Debug 構建目錄設定為:

build/linux-debug/

因此執行:

cmake --preset linux-debug

之後,編譯資料庫位於:

build/linux-debug/compile_commands.json

clangd 預設會從當前原始檔所在目錄逐級向上尋找 compile_commands.json。它通常能找到專案根目錄或 build/ 直屬目錄 中的資料庫,但不一定會繼續搜尋 build/linux-debug/ 這樣的多層 preset 目錄。

如果 clangd 沒有找到資料庫,它會進入 fallback 模式,只用少量預設 引數分析原始檔。這時即使 CMake 可以正常編譯,VSCode 中仍可能出現:

  1. 專案標頭檔案顯示找不到。
  2. Eigen、OpenCV 等第三方庫顯示找不到。
  3. C++ 標準識別錯誤,例如工程使用 C++20,clangd 卻按照 C++17 分析。
  4. 跳轉定義、自動補全和錯誤提示不準確。

推薦在專案根目錄建立 .clangd:

CompileFlags:
  CompilationDatabase: build/linux-debug

目錄結構會變成:

.
├── .clangd
├── CMakeLists.txt
├── CMakePresets.json
├── build/
│   └── linux-debug/
│       └── compile_commands.json
└── src/

CompilationDatabase 使用相對於 .clangd 所在目錄的路徑,所以不要 寫某個使用者電腦上的絕對路徑。這個檔案只儲存專案通用配置,可以提交到 Git。

注意,.clangd 不會生成編譯資料庫。第一次使用工程,或者修改了 CMakeLists.txt、編譯選項、依賴和 preset 後,仍然要重新執行:

cmake --preset linux-debug

或者在 VSCode CMake Tools 中選擇 linux-debug 並點選 Configure。

如果 .clangd 建立後沒有立即生效,可以在 VSCode 命令面板執行:

clangd: Restart language server

也可以關閉並重新開啟當前 .cpp 檔案。

如果同時安裝了 Microsoft C/C++ 擴充套件,並且編輯器出現兩套重複的錯誤 提示,可以保留該擴充套件提供的 GDB 除錯功能,同時關閉它的 IntelliSense, 讓 clangd 獨立負責程式碼分析。這屬於個人 VSCode 設定,不需要寫入 CMake 模板。

Windows 的資料庫位於 build/windows-debug/compile_commands.json,因此 .clangd 要對應改成:

CompileFlags:
  CompilationDatabase: build/windows-debug

CMAKE_EXPORT_COMPILE_COMMANDS 在 Ninja 生成器下適用,兩種平臺都保持使用 Ninja。Windows 中 clangd 查詢 GCC 標準庫路徑的配置見 Windows開發環境搭建與測試。

3.2.2.檢查 clangd 是否真的生效

在 VSCode 中開啟:

查看 -> 输出 -> clangd

正常情況下應當看到類似內容:

Loaded compilation database from .../build/linux-debug/compile_commands.json
Compile command from CDB is: ...

如果看到:

Failed to find compilation database
command clangd fallback

說明 clangd 仍然沒有讀到編譯資料庫。依次檢查:

  1. VSCode 開啟的是否為專案根目錄。
  2. .clangd 是否位於專案根目錄。
  3. 是否已經執行過 cmake --preset linux-debug。
  4. build/linux-debug/compile_commands.json 是否真實存在。
  5. .clangd 中的目錄名是否與 binaryDir 完全一致。

如果系統終端中可以直接使用 clangd,還可以檢查單個原始檔:

clangd --check=src/main.cpp

輸出中應當顯示它載入了 build/linux-debug/compile_commands.json, 並使用 CMake 生成的 include 路徑、巨集定義和 C++ 標準。

3.2.3.使用 Release 資料庫

如果希望 clangd 按照 Release 配置分析,可以改成:

CompileFlags:
  CompilationDatabase: build/linux-release

並先生成對應資料庫:

cmake --preset linux-release

日常開發通常使用 build/linux-debug 即可。不要同時讓 .clangd 指向 一個尚未生成或已經過期的構建目錄。

3.3.CMAKE_INSTALL_PREFIX

"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/linux-debug"

指定安裝目錄。

可以填什麼:

寫法 說明
${sourceDir}/install/linux-debug 本模板 Debug 安裝目錄
${sourceDir}/install/linux-release 本模板 Release 安裝目錄
/usr/local 系統級安裝路徑,需要許可權,不適合作為模板預設
/opt/my_project 系統級或部署路徑

本模板把安裝目錄放在專案內,是為了方便學習和刪除。

安裝後執行:

./install/linux-debug/bin/cmake_template

3.4.BUILD_SHARED_LIBS

當前模板的庫型別由預設選擇:

預設 值 專案中的庫
Linux Debug / Release ON 動態庫
Windows Debug / Release OFF 靜態庫

Release 預設繼承對應 Debug 預設中的設定。兩種系統使用同一份函式宣告和原始檔,按照自己的預設構建即可。這裡控制的是模板自己建立的庫,第三方安裝包仍按各自提供的方式使用。

4.buildPresets

buildPresets 是"構建階段"的 preset。

構建階段對應命令:

cmake --build --preset linux-debug

本模板寫法:

"buildPresets": [
  {
    "name": "linux-debug",
    "configurePreset": "linux-debug"
  },
  {
    "name": "linux-release",
    "configurePreset": "linux-release"
  }
]

4.1.name

"name": "linux-debug"

構建 preset 名稱。

一般建議和對應的 configure preset 同名,這樣命令比較一致:

cmake --preset linux-debug
cmake --build --preset linux-debug

4.2.configurePreset

"configurePreset": "linux-debug"

表示這個 build preset 使用哪個 configure preset 生成的構建目錄。

可以填什麼:

值 說明
linux-debug 使用 Debug 構建目錄
linux-release 使用 Release 構建目錄

必須對應 configurePresets 中已經存在的 name。

5.testPresets

當前模板還帶有測試預設,例如:

{
  "name": "windows-debug",
  "configurePreset": "windows-debug",
  "output": { "outputOnFailure": true }
}

構建完成後執行:

ctest --preset windows-debug

Linux 則使用 ctest --preset linux-debug。CTest 會執行原始 Eigen 示例,並檢查輸出中是否包含 Determinant = -2。後面修改 main.cpp 做語法練習時,輸出變化會使這個原始示例測試失敗;應該按新程式的要求修改測試,而不是把測試當作所有練習的統一執行命令。

6.繼承、平臺條件與其他欄位

當前模板已經用到了 hidden 和 inherits 來復用配置,下面再解釋它們的含義。

6.1.description

給 preset 新增更長的說明。

"description": "Debug build for Linux using Ninja"

6.2.hidden

隱藏某個 preset,不直接給使用者選擇,通常用於繼承。

"hidden": true

6.3.inherits

繼承另一個 preset,減少重複。

{
  "name": "linux-debug",
  "inherits": "linux-base",
  "cacheVariables": {
    "CMAKE_BUILD_TYPE": "Debug"
  }
}

6.4.${presetName}

公共預設把目錄寫成:

"binaryDir": "${sourceDir}/build/${presetName}"

當 windows-debug 繼承公共配置時,${presetName} 會展開成 windows-debug;使用 linux-release 時則展開成 linux-release。因此不同平臺、不同構建型別有自己的快取和安裝目錄。

6.5.condition

例如 Windows 預設使用:

"condition": {
  "type": "equals",
  "lhs": "${hostSystemName}",
  "rhs": "Windows"
}

這裡檢查執行 CMake 的宿主系統。Linux 對應 Linux,Windows 對應 Windows。在 Windows 原生 CMake 中,配置列表只顯示 Windows 的預設;公共 base 預設由於設定了 hidden: true,也不會單獨顯示。

6.6.environment

設定環境變數。

"environment": {
  "CC": "/usr/bin/gcc",
  "CXX": "/usr/bin/g++"
}

本模板的 Windows 預設明確選擇 gcc 和 g++,保證使用本教程的 MinGW-w64 路線,但沒有寫死它們的絕對路徑。Linux 預設讓 CMake 從系統環境選擇編譯器。自己的工具位置可以放進 CMakeUserPresets.json,不要把個人電腦路徑提交到公共預設中。

6.7.targets

指定預設構建哪些 target。

"targets": ["cmake_template"]

如果不寫,預設構建全部 target。

6.8.jobs

指定並行編譯數量。

"jobs": 8

不寫時由底層構建工具決定。

7.為什麼沒有 installPresets

很多人會猜測可以寫:

cmake --install --preset linux-debug

但常見 CMake preset 檔案並不使用這個根欄位。這個模板採用更通用的安裝命令:

cmake --install build/linux-debug

安裝命令需要傳入構建目錄,因為安裝規則是在 configure 階段生成到構建目錄裡的。

以下圖形化步驟和截圖以 Linux 為例。Windows 選擇 Windows GCC Debug,程式路徑改為 build/windows-debug/bin/cmake_template.exe,其他操作的意義相同。

8.VSCode CMake Tools 工作流

安裝 VSCode 的 CMake Tools 擴充套件後,它會自動識別 CMakePresets.json。

這一節把命令列操作和 VSCode 圖形介面操作對應起來。

8.1.開啟 CMake 面板

用 VSCode 開啟工程根目錄後,點選左側邊欄的 CMake。

如果 CMake Tools 正常識別工程,你會看到 preset、Configure、Build、執行 target 等入口。

8.2.Configure

命令列:

cmake --preset linux-debug

在 VSCode 圖形介面中,對應的是:

  1. 點選 Configure。
  2. 選擇 Linux Debug。

alt text

這一步會讀取:

CMakePresets.json
CMakeLists.txt

並生成:

build/linux-debug/

如果你改了 CMakeLists.txt、新增了原始檔、修改了第三方依賴,通常需要重新 Configure。

8.3.Build

命令列:

cmake --build --preset linux-debug

在 VSCode 圖形介面中,CMake Tools 會在 Configure 後識別對應的 build preset。

alt text

然後點選左下角或 CMake 面板裡的 Build 按鈕:

alt text

這一步會編譯 .cpp 檔案,並生成 build 目錄裡的執行檔和動態庫。

本模板的 Debug 執行檔通常在:

build/linux-debug/src/cmake_template

如果你只是修改了普通 .cpp 程式碼,一般只需要重新 Build,不一定要重新 Configure。

8.4.執行 build 目錄裡的程式

命令列:

./build/linux-debug/src/cmake_template

在 VSCode 圖形介面中,對應的是:

  1. 選擇執行/除錯 target。
  2. 選擇 cmake_template。
  3. 點選執行按鈕。

alt text

alt text

執行後,終端裡會看到程式輸出。

alt text

如果你要斷點除錯,就點選 CMake Tools 提供的 Debug 按鈕;如果只是直接執行程式,就點選 Launch 按鈕。

除了上面的操作入口,VSCode 底部狀態列中,Build 按鈕旁邊也有 Debug 和 Launch 快捷按鈕。選擇好 cmake_template target 並完成構建後,可以直接點選這裡執行或除錯程式。

alt text

8.5.Install

命令列:

cmake --install build/linux-debug

在 VSCode 圖形介面中,可以按 Ctrl+Shift+P 開啟命令面板,然後輸入:

CMake: Install

alt text

如果當前選擇的是 linux-debug preset,它等同於:

cmake --install build/linux-debug

安裝後的程式在:

install/linux-debug/bin/cmake_template

日常寫程式碼、執行、除錯,一般直接使用 build 目錄裡的程式:

build/linux-debug/src/cmake_template

只有在驗證安裝佈局、標頭檔案安裝、動態庫 RPATH 或準備釋出程式時,才需要 install。

8.6.對應關係總結

你想做的事 命令列 VSCode CMake Tools
配置工程 cmake --preset linux-debug 選擇 Linux Debug 後點擊 Configure
編譯工程 cmake --build --preset linux-debug 點選 Build
執行 build 產物 ./build/linux-debug/src/cmake_template 選擇 cmake_template target 後點擊執行按鈕
除錯 build 產物 用 GDB 除錯 build/linux-debug/src/cmake_template 選擇 cmake_template target 後點擊 Debug 按鈕
安裝工程 cmake --install build/linux-debug 命令面板執行 CMake: Install

這樣不需要手寫 .vscode/tasks.json,也不需要額外準備 VSCode 除錯配置檔案。

9.執行與除錯注意事項

CMake Tools 可以直接執行或除錯當前選擇的可執行 target,所以不需要手寫額外的執行指令碼。

如果要除錯,建議安裝 Microsoft C/C++ 擴充套件和 GDB,因為 CMake Tools 負責選擇 target,真正的 C/C++ 偵錯程式仍然需要 GDB/LLDB 支援。

如果你只改了 .cpp 程式碼,一般重新 Build 後再執行或除錯即可。

如果你改了 CMakeLists.txt、新增原始檔、改依賴,建議先 Configure,再 Build,然後再執行或除錯。

9.1.為什麼不寫 tasks.json

舊模板常用 .vscode/tasks.json 寫一長串命令:

cmake ..
make install
source setup.bash
run app

這個模板不這樣做。原因是構建、執行和除錯入口都可以交給 CMake Tools 管理,命令列流程則由 CMakePresets.json 固化。

這樣職責更清楚:

檔案 負責
CMakePresets.json configure/build 引數
VSCode CMake Tools 選擇 preset、Configure、Build、執行和除錯 target
CMakeLists.txt target、依賴、安裝規則

9.2.需要安裝 gdb

Ubuntu/Debian:

sudo apt install gdb

Fedora:

sudo dnf install gdb

10.常見錯誤

10.1.忘記安裝 Ninja

報錯可能類似:

CMake was unable to find a build program corresponding to "Ninja"

解決:

sudo apt install ninja-build

或:

sudo dnf install ninja-build

10.2.preset 名稱寫錯

錯誤命令:

cmake --preset debug

如果檔案裡沒有 debug 這個 preset,就會失敗。

檢視可用 preset:

cmake --list-presets

10.3.build 前沒有 configure

如果第一次直接執行:

cmake --build --preset linux-debug

可能會因為構建目錄還沒有生成而失敗。

正確順序:

cmake --preset linux-debug
cmake --build --preset linux-debug