Wiki
模板擴充套件與常見問題
本文由簡體中文內容確定性轉換,並受版本化術語表保護。
本節整理使用這個 CMake 模板時最常見的擴充套件方式和問題排查。
1.新增新的原始檔
當前庫目錄使用:
file(GLOB_RECURSE ${PREFIX}_SRC_LIST CONFIGURE_DEPENDS
"${CMAKE_CURRENT_LIST_DIR}/src/*.c"
"${CMAKE_CURRENT_LIST_DIR}/src/*.cpp"
)
所以只要把新的 .cpp 放到對應模組的 src/ 目錄下,CMake 會自動收集。
例如:
src/lib1/src/math_utils.cpp
然後重新構建:
cmake --build --preset linux-debug
如果發現新檔案沒有參與編譯,可以手動重新 configure:
cmake --preset linux-debug
cmake --build --preset linux-debug
2.新增新的標頭檔案
推薦路徑:
src/lib1/inc/lib1/math_utils.hpp
程式碼中包含:
#include "lib1/math_utils.hpp"
不要直接放成:
src/lib1/inc/math_utils.hpp
因為多個庫可能出現同名標頭檔案。使用 inc/lib1/ 這種結構可以避免衝突。
3.新增新的庫模組
假設要新增 lib3。
目錄結構:
src/lib3/
├── CMakeLists.txt
├── inc/
│ └── lib3/
│ └── example.hpp
└── src/
└── example.cpp
src/lib3/CMakeLists.txt:
set(PREFIX "lib3")
file(GLOB_RECURSE ${PREFIX}_SRC_LIST CONFIGURE_DEPENDS
"${CMAKE_CURRENT_LIST_DIR}/src/*.c"
"${CMAKE_CURRENT_LIST_DIR}/src/*.cpp"
)
add_library(${PREFIX}_src_lib
${${PREFIX}_SRC_LIST}
)
target_include_directories(${PREFIX}_src_lib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/inc>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
target_link_libraries(${PREFIX}_src_lib
PUBLIC
project_options
PRIVATE
project_warnings
)
# ========================
# Third-party dependencies
# ========================
# =======
# Install
# =======
install(TARGETS ${PREFIX}_src_lib
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/inc/"
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
example.hpp 使用普通的 C++ 函式宣告:
#pragma once
namespace lib3 {
void example();
}
對應的 example.cpp 包含這個標頭檔案,並提供 lib3::example() 的定義。
然後在 src/CMakeLists.txt 中新增:
add_subdirectory(lib3)
並連結到主程式:
target_link_libraries(${PROJECT_NAME}
PRIVATE
lib1_src_lib
lib2_src_lib
lib3_src_lib
)
4.新增新的執行檔
如果一個專案有多個程式,例如:
src/main.cpp
src/tools/calibrate_camera.cpp
可以在 src/CMakeLists.txt 裡新增:
add_executable(calibrate_camera
${CMAKE_CURRENT_SOURCE_DIR}/tools/calibrate_camera.cpp
)
target_link_libraries(calibrate_camera
PRIVATE
project_options
project_warnings
lib1_src_lib
lib2_src_lib
)
set_target_properties(calibrate_camera PROPERTIES
INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}"
)
install(TARGETS calibrate_camera
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
安裝後:
install/linux-debug/bin/calibrate_camera
5.改專案名
頂層:
project(cmake_template VERSION 1.0.0 LANGUAGES C CXX)
改成:
project(robot_app VERSION 1.0.0 LANGUAGES C CXX)
因為主程式使用:
add_executable(${PROJECT_NAME}
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
)
所以執行檔會從:
cmake_template
變成:
robot_app
README 裡的執行命令也要同步改:
./install/linux-debug/bin/robot_app
6.改 C++ 標準
當前模板預設使用 C++20:
set(CMAKE_CXX_STANDARD 20)
target_compile_features(project_options INTERFACE cxx_std_20)
如果要改 C++23:
set(CMAKE_CXX_STANDARD 23)
target_compile_features(project_options INTERFACE cxx_std_23)
如果專案使用 C++17:
set(CMAKE_CXX_STANDARD 17)
target_compile_features(project_options INTERFACE cxx_std_17)
修改 cmake/ProjectOptions.cmake 中已有的這兩行,並確認編譯器支援對應標準模式;具體語法和標準庫功能還取決於編譯器及其標準庫版本。本模板的 CMake 最低版本仍為 3.22。
7.改動態庫為靜態庫
如果某個模組顯式指定動態庫:
add_library(${PREFIX}_src_lib SHARED
${${PREFIX}_SRC_LIST}
)
改成靜態庫:
add_library(${PREFIX}_src_lib STATIC
${${PREFIX}_SRC_LIST}
)
動態庫和靜態庫對比:
| 型別 | 優點 | 缺點 |
|---|---|---|
SHARED |
執行檔較小,庫可獨立更新 | Linux 執行時要能找到相應 .so |
STATIC |
部署簡單,很多程式碼打進執行檔 | 執行檔更大,更新庫要重新連結 |
本模板採用下一節的配置方式:Linux 預設使用動態庫,Windows 預設使用靜態庫。兩種方式都保留同樣的標頭檔案宣告和呼叫程式碼。
8.用 BUILD_SHARED_LIBS 控制庫型別
當前模板不寫死庫型別:
add_library(${PREFIX}_src_lib
${${PREFIX}_SRC_LIST}
)
然後在 preset 中控制:
"BUILD_SHARED_LIBS": "ON"
或:
"BUILD_SHARED_LIBS": "OFF"
ON 選擇動態庫,OFF 選擇靜態庫。當前 Linux 預設為 ON,Windows 預設為 OFF,Release 繼承對應設定。不要在模組中再寫死 SHARED,否則會繞開這個開關。
9.新增巨集定義
給某個 target 新增宏:
target_compile_definitions(${PREFIX}_src_lib
PRIVATE
LIB1_ENABLE_LOG
)
C++ 中使用:
#ifdef LIB1_ENABLE_LOG
// log code
#endif
帶值宏:
target_compile_definitions(${PREFIX}_src_lib
PRIVATE
LIB1_VERSION="1.0.0"
)
可見性選擇:
| 關鍵字 | 場景 |
|---|---|
PRIVATE |
隻影響本庫原始碼 |
PUBLIC |
本庫原始碼和使用者都需要這個宏 |
INTERFACE |
本 target 自己不用,只傳給使用者 |
10.新增編譯選項
給某個庫單獨加選項:
target_compile_options(${PREFIX}_src_lib
PRIVATE
-Wshadow
)
不建議用全域性:
add_compile_options(-Wshadow)
因為全域性選項會影響所有 target,不利於排查。
11.新增 include 路徑
推薦:
target_include_directories(${PREFIX}_src_lib
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/some_private_include
)
不推薦:
include_directories(some_private_include)
原因:
include_directories是目錄級影響,範圍更大。target_include_directories能明確說明哪個 target 需要這個路徑。
12.新增測試目錄
如果以後要加測試,可以新增:
test/
└── CMakeLists.txt
頂層新增:
include(CTest)
if(BUILD_TESTING)
add_subdirectory(test)
endif()
preset 中可以控制:
"BUILD_TESTING": "ON"
不過當前模板要求不新增 examples,測試目錄是否新增要看專案需求。
13.模板已有的執行測試
原始模板已經通過 include(CTest) 啟用測試。構建後可以執行 ctest --preset linux-debug 或 ctest --preset windows-debug。
eigen_example 檢查原始 Eigen 輸出。把 main.cpp 換成 Hello World 或其他練習以後,這個測試的輸出斷言也要調整;測試失敗不等於新程式無法編譯。新增真實業務後,應為新行為編寫對應測試。
14.清理構建和安裝目錄
因為模板已經有 .gitignore 忽略:
build/
install/
log/
所以這些目錄都是生成物。
清理 Debug:
rm -rf build/linux-debug install/linux-debug
清理 Release:
rm -rf build/linux-release install/linux-release
重新構建:
cmake --preset linux-debug
cmake --build --preset linux-debug
cmake --install build/linux-debug
Windows 的 PowerShell 中,先確認對應目錄存在,再清理:
Remove-Item -Recurse -Force build/windows-debug
Remove-Item -Recurse -Force install/windows-debug
cmake --preset windows-debug
清理前檢查當前工作目錄和要刪除的路徑。不同編譯器、不同平臺使用各自的 preset 目錄,不共用同一份 CMake 快取。
15.為什麼安裝目錄裡可能是 lib64
本模板使用:
include(GNUInstallDirs)
庫安裝時使用:
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
在某些 Linux 發行版上:
CMAKE_INSTALL_LIBDIR = lib64
所以安裝結果是:
install/linux-debug/lib64/
這不是錯誤,而是系統慣例。
不要手寫:
LIBRARY DESTINATION lib
否則會繞開 CMake 對系統目錄的判斷。
16.Linux 執行時報找不到 .so
錯誤類似:
error while loading shared libraries: liblib1_src_lib.so: cannot open shared object file
常見原因:
- 沒有執行
cmake --install ...。 - 執行檔不是從安裝目錄執行的。
- 主程式或其依賴庫的
INSTALL_RPATH沒設定對。 - 手動移動了
bin/或lib64/的相對位置。
本模板在 src/CMakeLists.txt 中為主程式設定:
set_target_properties(${PROJECT_NAME} PROPERTIES
INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}"
)
還在 src/lib1/CMakeLists.txt 中為 lib1 設定:
set_target_properties(${PREFIX}_src_lib PROPERTIES
INSTALL_RPATH "$ORIGIN"
)
前者從主程式的 bin/ 目錄找到庫目錄,後者讓 lib1 找到同目錄的 lib2。如果報錯提到 liblib2_src_lib.so,即使主程式已經設定了搜尋路徑,也應檢查 lib1 的配置。修改後重新 Configure、Build 和 Install,再執行安裝後的程式。
以庫目錄為 lib64 的系統為例,保持這種結構即可:
install/linux-debug/
├── bin/
│ └── cmake_template
└── lib64/
├── liblib1_src_lib.so
└── liblib2_src_lib.so
17.Windows 程式啟動失敗
先確認執行的是 build/windows-debug/bin/cmake_template.exe,並且所用 GCC 的 bin/ 在 PATH 中。預設 MSYS2 安裝位置對應 C:/msys64/ucrt64/bin。
驗證安裝結果時,重新執行:
cmake --install build/windows-debug
./install/windows-debug/bin/cmake_template.exe
安裝後保持整個目錄結構再移動、執行。新增第三方依賴時,按相應庫的安裝要求檢查執行環境。
18.CMake 找不到 Eigen 或 OpenCV
先確認安裝開發包。
Eigen:
sudo apt install libeigen3-dev
sudo dnf install eigen3-devel
OpenCV:
sudo apt install libopencv-dev
sudo dnf install opencv-devel
再重新 configure:
cmake --preset linux-debug
如果庫安裝在非標準路徑,新增:
cmake --preset linux-debug -DCMAKE_PREFIX_PATH=/opt/my_library
或者寫入 preset:
"CMAKE_PREFIX_PATH": "/opt/my_library"
19.ccache 導致 configure 失敗
有些環境中,gcc、g++ 可能指向 ccache 包裝器。如果 ccache 目錄不可寫,CMake 檢查編譯器時可能失敗。
臨時解決:
rm -rf build/linux-debug
CC=/usr/bin/gcc CXX=/usr/bin/g++ cmake --preset linux-debug
這只是驗證或特殊環境下的處理,不建議寫死到模板中。
如果你確實想在 preset 裡指定編譯器,可以加:
"cacheVariables": {
"CMAKE_C_COMPILER": "/usr/bin/gcc",
"CMAKE_CXX_COMPILER": "/usr/bin/g++"
}
但這會降低模板通用性,所以預設不寫。
20.clangd 找不到專案標頭檔案或第三方庫
如果工程能夠正常編譯,但 VSCode 仍然提示找不到專案標頭檔案、Eigen 或
OpenCV,通常不是 target_include_directories 寫錯了,而是 clangd
沒有讀取 CMake 生成的編譯資料庫。
先確認 Debug 編譯資料庫存在:
cmake --preset linux-debug
test -f build/linux-debug/compile_commands.json && echo "找到了编译数据库"
然後在專案根目錄建立 .clangd:
CompileFlags:
CompilationDatabase: build/linux-debug
在 VSCode 命令面板執行:
clangd: Restart language server
再到 查看 -> 输出 -> clangd 檢查日誌。正常日誌應包含:
Loaded compilation database from .../build/linux-debug/compile_commands.json
如果日誌出現:
Failed to find compilation database
command clangd fallback
說明 clangd 還在使用 fallback 引數,專案的 include 路徑、第三方庫 路徑和 C++ 標準都可能識別錯誤。
關於 compile_commands.json、.clangd、Debug/Release 資料庫切換和
命令列驗證的完整說明,見
CMakePresets與構建安裝。
Windows 下先確認 .clangd 的 CompilationDatabase 指向 build/windows-debug,並使用正確的 clangd.exe。如果能編譯卻找不到標準庫標頭檔案,可按 Windows 環境搭建 配置 --query-driver。
21.VSCode 裡沒有識別 preset
檢查:
- 是否安裝 CMake Tools 擴充套件。
- 開啟的目錄是否是專案根目錄。
CMakePresets.json是否在專案根目錄。- JSON 是否合法。
命令列檢查:
cmake --list-presets
如果命令列能看到:
linux-debug
linux-release
說明 preset 檔案本身沒問題。
Windows 的配置列表對應 windows-debug、windows-release。如果 CMake Tools 仍拿到舊工具路徑,重啟 VSCode,再用 where.exe g++、where.exe cmake 檢查;更換編譯器後清理對應構建快取。
22.VSCode CMake Tools 不能執行或除錯
CMake Tools 執行或除錯需要幾件事同時正常:
- CMake Tools 能識別 preset。
- 工程已經 Configure 和 Build。
- CMake Tools 已經選擇可執行 target。
- 如果要 Debug,GDB 能正常啟動。
22.1.檢查 preset
先確認 VSCode 開啟的是專案根目錄,並且能識別 CMakePresets.json。
命令列可以這樣檢查:
cmake --list-presets
如果能看到:
linux-debug
linux-release
說明 preset 檔案本身沒問題。
VSCode 裡則需要選擇 Configure Preset,例如:
Linux Debug
然後執行 Configure。
22.2.檢查 target
CMake Tools 需要知道你要執行或除錯哪個可執行 target。
模板裡的可執行 target 是:
cmake_template
如果執行或除錯按鈕不可用,先確認 CMake Tools 已經選擇了 cmake_template。
22.3.檢查是否已經構建
CMake Tools 執行或除錯的通常是 build 目錄裡的執行檔,例如:
build/linux-debug/src/cmake_template
如果還沒有 Build,這個檔案不存在,執行或除錯就無法啟動。
先執行:
cmake --preset linux-debug
cmake --build --preset linux-debug
或者在 VSCode CMake Tools 中執行 Configure 和 Build。
22.4.檢查 gdb
如果只是普通執行程式,不一定需要 GDB。如果要 Debug,就需要確認 GDB 已安裝。
命令列檢查:
gdb --version
沒有安裝就執行:
sudo apt install gdb
或:
sudo dnf install gdb
22.5.執行和除錯不需要每次 install
日常開發時,CMake Tools 通常執行的是 build 目錄產物:
./build/linux-debug/src/cmake_template
不是安裝目錄產物:
./install/linux-debug/bin/cmake_template
install 主要用於驗證安裝佈局、標頭檔案安裝、動態庫 RPATH 等是否正確。
23..gitignore 怎樣處理 VSCode 配置
這個模板不依賴額外的 VSCode 除錯配置檔案,因為 CMake Tools 可以直接執行或除錯當前 CMake target。
如果不想把個人 VSCode 配置提交到倉庫,可以直接忽略 .vscode/:
.vscode/
也可以只忽略常見的本地配置:
.vscode/settings.json
.vscode/tasks.json
如果團隊確實想共享某些 VSCode 配置,比如推薦擴充套件或格式化設定,可以單獨討論哪些檔案進入 Git。執行和除錯本身可以交給 CMake Tools。
24.模板的核心規則
最後總結一下這個模板最重要的幾條規則:
- 頂層
CMakeLists.txt只做總控。 - 公共編譯規則放在
cmake/ProjectOptions.cmake。 - 主程式由
src/CMakeLists.txt管理。 - 每個庫目錄自己管理自己的原始碼、標頭檔案、安裝和第三方依賴。
- 標頭檔案路徑帶模組名,例如
lib1/eigen3_test.hpp。 - 第三方庫放在使用者自己的
Third-party dependencies區塊。 - 構建引數放在
CMakePresets.json,不要寫死在頂層 CMake。 - 生成目錄
build/、install/、log/不進入 Git。 - VSCode 裡執行和除錯交給 CMake Tools,命令列構建仍然交給 CMakePresets。
25.Windows 的執行和除錯路徑
前面圖形介面操作中選擇 cmake_template target 的方法不變。Windows 使用 windows-debug,執行的程式是 build/windows-debug/bin/cmake_template.exe。
GDB 來自同一套工具鏈,預設 MSYS2 路徑為 C:/msys64/ucrt64/bin/gdb.exe。先用 CMake Tools 的 Debug 按鈕檢查;想使用 F5 時,把 CMake: Debug 繫結到 F5 即可。
如果舊版本教程中看到 cmake --fresh,注意該選項從 CMake 3.24 才加入。本模板相容 CMake 3.22,因此這裡統一用清理構建目錄後重新 Configure 的方式處理快取。參見 CMake 3.24 釋出說明。