目錄
1.簡介
2.用?message()?輸出關鍵信息
2.1.message簡介
2.2.常用模式及作用
2.3.核心用法示例
2.4.常見問題及解決
3.查看緩存變量:cmake -L?與緩存文件
3.1.列出所有緩存變量(cmake -L)
3.2.直接查看 / 刪除?CMakeCache.txt
4.變量追蹤與作用域
4.1.CMAKE_MESSAGE_CONTEXT?(CMake 3.25+)
4.2.作用域問題
4.3.監控變量變化:variable_watch()
4.4.屬性獲取
4.4.1.獲取 CMake 全局屬性(get_cmake_property())
4.4.2.查詢目標屬性(get_target_property())
4.4.3.批量打印屬性(cmake_print_properties())
4.4.4.輸出到文件(file(WRITE))
5.詳細跟蹤 CMake 執行流程:--debug-output?與?--trace
5.1.--debug-output:輸出調試級信息
5.2.--trace:跟蹤所有執行的命令(最詳細)
5.3.--warn-uninitialized
5.4.--graphviz=?(CMake 2.8.10+)
6.調試?find_package?依賴查找失敗
7.使用 CMake GUI /?ccmake
8.調試工具鏈文件 (CMAKE_TOOLCHAIN_FILE)
9.檢查編譯器 / 平臺兼容性:日志文件
10.驗證條件判斷邏輯
11.其他實用技巧
12.總結
相關鏈接
1.簡介
????????在 CMake 腳本開發中,由于其語法特性(如變量作用域、緩存機制、條件邏輯)和跨平臺配置的復雜性,經常需要調試來定位問題(如變量值異常、依賴找不到、條件分支錯誤等)。下面就一些常見的調試方法介紹介紹。
2.用?message()
?輸出關鍵信息
2.1.message簡介
????????message()
?是用于輸出信息到控制臺的核心命令,主要用于調試配置過程、反饋狀態或提示錯誤,是跟蹤 CMake 腳本執行的重要工具。
? ? ? ? 基本語法:
message([<模式>] "消息內容")
- 模式(可選):指定消息級別,控制輸出樣式和影響(如是否中斷執行)。
- 消息內容:可包含文本、變量(用?
${變量名}
?引用)、表達式或列表。
2.2.常用模式及作用
CMake 通過模式區分消息的重要性,常用模式如下:
模式 | 作用與特點 |
---|---|
STATUS | 最常用,用于配置過程的狀態提示(如變量值、路徑信息)。輸出時會自動添加縮進,與 CMake 原生輸出風格一致(推薦優先使用)。 |
WARNING | 警告信息,以醒目樣式顯示(通常為黃色),但不中斷 CMake 執行(如提示過時用法、潛在問題)。 |
SEND_ERROR | 錯誤信息,會繼續執行后續腳本,但最終標記構建失敗(用于非致命錯誤,如可選依賴缺失)。 |
FATAL_ERROR | 致命錯誤,立即中斷 CMake 執行(用于關鍵依賴缺失、無效配置等必須解決的問題)。 |
無模式 | 默認模式,輸出普通文本(不推薦,風格與 CMake 原生輸出不一致,易混淆)。 |
DEPRECATION | 過時提示,僅在開發者模式(-Wdev )下顯示(用于標記即將廢棄的功能)。 |
2.3.核心用法示例
1.輸出狀態信息(STATUS
)
用于反饋配置過程中的關鍵信息(如變量值、路徑、條件分支):
# 輸出普通變量
set(MY_VAR "test")
message(STATUS "MY_VAR = ${MY_VAR}") # 輸出:-- MY_VAR = test# 輸出緩存變量(如 find_package 結果)
find_package(OpenSSL)
message(STATUS "OpenSSL_FOUND = ${OpenSSL_FOUND}") # 檢查依賴是否找到
message(STATUS "OpenSSL_INCLUDE_DIRS = ${OpenSSL_INCLUDE_DIRS}") # 路徑是否正確# 輸出內置變量(如路徑、編譯器信息)
message(STATUS "CMAKE_SOURCE_DIR = ${CMAKE_SOURCE_DIR}") # 源碼根目錄
message(STATUS "CMAKE_CXX_COMPILER = ${CMAKE_CXX_COMPILER}") # 編譯器路徑
2.調試變量與列表
變量引用需用?${}
,否則會被當作字符串:
set(MY_VAR "hello")
message(STATUS "變量值: ${MY_VAR}") # 正確:輸出 "變量值: hello"
# message(STATUS "變量值: MY_VAR") # 錯誤:輸出 "變量值: MY_VAR"
列表默認用分號分隔,可通過?string(JOIN)
?格式化輸出:
set(MYLIST "a" "b" "c") # CMake 列表(內部存儲為 "a;b;c")
string(JOIN ", " LIST_STR "${MYLIST}") # 轉換為 "a, b, c"
message(STATUS "列表內容: ${LIST_STR}") # 輸出:列表內容: a, b, c
3.跟蹤條件分支執行
當?if()
?分支邏輯不符合預期時,在分支內輸出標記,確認是否進入目標分支:
option(ENABLE_FEATURE "啟用功能" OFF)if(ENABLE_FEATURE)message(STATUS "進入 ENABLE_FEATURE 分支") # 若未輸出,說明條件不成立# ... 功能代碼 ...
else()message(STATUS "進入 ELSE 分支(功能未啟用)") # 確認是否走了默認分支
endif()# 復雜條件判斷(如版本比較)
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "11.0")message(STATUS "編譯器版本 > 11.0")
else()message(STATUS "編譯器版本 <= 11.0(當前: ${CMAKE_CXX_COMPILER_VERSION})")
endif()
4.錯誤與警告提示
警告(WARNING
):提示潛在問題但不中斷執行:
if(CMAKE_VERSION VERSION_LESS "3.10")message(WARNING "CMake 版本過低,部分功能可能受限(推薦 >=3.10)")
endif()
致命錯誤(FATAL_ERROR
):關鍵問題必須解決時中斷執行:
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/src/main.cpp")message(FATAL_ERROR "未找到核心源文件 src/main.cpp,請檢查源碼完整性!")
endif()
2.4.常見問題及解決
- 優先使用?
STATUS
?模式:保持輸出風格與 CMake 一致,避免混亂。 - 變量未展開:忘記用?
${}
?引用變量,導致輸出變量名而非值(如?message(STATUS "VAR: MY_VAR")
?應改為?message(STATUS "VAR: ${MY_VAR}")
)。 - 列表格式混亂:CMake 列表默認用分號分隔,可通過?
string(JOIN ", " 新變量 原列表)
?轉換為可讀性更高的格式。 - 消息不顯示:模式級別過高(如?
DEPRECATION
?默認不顯示)或 CMake 以靜默模式運行(如加了?-Wno-dev
),改用?STATUS
?或?WARNING
?模式即可。
3.查看緩存變量:cmake -L
?與緩存文件
CMake 會將關鍵變量(如?option
、find_package
?結果、路徑配置)存儲在?CMakeCache.txt?中(構建目錄下),這些變量可能被緩存而不更新,導致配置異常。
3.1.列出所有緩存變量(cmake -L
)
在構建目錄執行以下命令,可列出所有緩存變量及其值(快速確認變量是否被正確設置):
# 列出所有非高級緩存變量(常用)
cmake -L .# 列出所有緩存變量(包括高級變量,如編譯器細節)
cmake -LA .# 搜索特定變量(結合 grep)
cmake -LA . | grep "OpenSSL" # 查找與 OpenSSL 相關的緩存變量
示例輸出(部分):
ENABLE_FEATURE:BOOL=OFF
OpenSSL_FOUND:BOOL=ON
OpenSSL_INCLUDE_DIRS:PATH=/usr/include/openssl
...
3.2.直接查看 / 刪除?CMakeCache.txt
若懷疑舊緩存影響配置(如修改?option
?后值未更新),可:
- 直接打開構建目錄下的?
CMakeCache.txt
,搜索目標變量(如?ENABLE_FEATURE
),查看其實際值; - 刪除?
CMakeCache.txt
?或整個構建目錄(rm -rf build && mkdir build && cd build
),重新配置(避免緩存干擾)。
4.變量追蹤與作用域
4.1.CMAKE_MESSAGE_CONTEXT
?(CMake 3.25+)
-
設置一個上下文字符串,該字符串會自動添加到當前目錄及所有子目錄中所有后續?
message()
?調用的輸出中。 -
非常適合在大型項目或遞歸結構中追蹤消息來源。
list(APPEND CMAKE_MESSAGE_CONTEXT "MyModule")
message(STATUS "Configuring MyModule...") # 輸出: [MyModule] -- Configuring MyModule...
list(POP_BACK CMAKE_MESSAGE_CONTEXT) # 退出當前上下文
4.2.作用域問題
-
set(... PARENT_SCOPE)
: 修改父作用域變量。 -
set(... CACHE ... FORCE)
: 強制修改緩存變量。 -
使用?
message()
?在不同位置(函數內/外、不同?CMakeLists.txt
?中)打印變量值,觀察其變化,是診斷作用域問題的關鍵。
4.3.監控變量變化:variable_watch()
用于跟蹤指定變量的讀取、修改、刪除操作,當變量發生這些行為時,CMake 會自動輸出調試信息(包括操作類型、位置、新舊值等)。
用法:
# 監控單個變量
variable_watch(MY_VAR)# 監控多個變量
variable_watch(CMAKE_CXX_STANDARD)
variable_watch(FOO_BAR)
效果:當?MY_VAR
?被讀取(如?if(MY_VAR)
)、修改(如?set(MY_VAR 1)
)或刪除(如?unset(MY_VAR)
)時,會輸出類似:
Variable MY_VAR was modified at [...]/CMakeLists.txt:10 (set). Old value: "0", new value: "1"
4.4.屬性獲取
4.4.1.獲取 CMake 全局屬性(get_cmake_property())
?用于查詢 CMake 的全局屬性(如已定義的變量列表、目標列表、目錄列表等),結合?message()
?可打印全局狀態。
常用場景:
- 打印所有已定義的變量
- 打印所有已創建的目標(可執行文件、庫)
- 打印所有包含的目錄
示例:
# 打印所有已定義的變量
get_cmake_property(all_vars VARIABLES)
list(SORT all_vars) # 排序便于查看
message("All variables:\n${all_vars}")# 打印所有目標(可執行文件、庫等)
get_cmake_property(all_targets BUILDSYSTEM_TARGETS)
message("All targets:\n${all_targets}")# 打印所有包含的目錄
get_cmake_property(all_dirs SUBDIRECTORIES)
message("All subdirectories:\n${all_dirs}")
4.4.2.查詢目標屬性(get_target_property())
用于獲取特定目標(如可執行文件、庫)的屬性(如包含目錄、鏈接庫、編譯選項等),排查目標配置問題。
常用屬性:INCLUDE_DIRECTORIES
(包含目錄)、LINK_LIBRARIES
(鏈接庫)、COMPILE_DEFINITIONS
(編譯宏)、SOURCES
(源文件列表)等。
示例:
# 假設已創建目標 "my_app"
add_executable(my_app main.cpp)# 查看目標的包含目錄
get_target_property(inc_dirs my_app INCLUDE_DIRECTORIES)
message("my_app include dirs: ${inc_dirs}")# 查看目標的鏈接庫
get_target_property(link_libs my_app LINK_LIBRARIES)
message("my_app linked libs: ${link_libs}")# 查看目標的編譯選項
get_target_property(compile_opts my_app COMPILE_OPTIONS)
message("my_app compile options: ${compile_opts}")
4.4.3.批量打印屬性(cmake_print_properties())
用于批量打印指定類型(目標、源文件、目錄等)的屬性,比?get_target_property()
?更高效。
支持的類型:TARGETS
、SOURCES
、DIRECTORIES
、TESTS
?等。
示例:
# 打印目標 "my_app" 的所有屬性
cmake_print_properties(TARGETS my_appPROPERTIES INCLUDE_DIRECTORIES LINK_LIBRARIES COMPILE_DEFINITIONS
)# 打印當前目錄的屬性(如 CMAKE_CURRENT_SOURCE_DIR)
cmake_print_properties(DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}PROPERTIES CMAKE_CURRENT_SOURCE_DIR CMAKE_CURRENT_BINARY_DIR
)
4.4.4.輸出到文件(file(WRITE))
當調試信息過多(如大量變量或屬性),控制臺輸出混亂時,可將信息寫入文件查看。
示例:
# 將所有變量寫入文件
get_cmake_property(all_vars VARIABLES)
list(SORT all_vars)
file(WRITE "${CMAKE_BINARY_DIR}/cmake_vars.txt" "All variables:\n${all_vars}")# 將目標屬性寫入文件
get_target_property(inc_dirs my_app INCLUDE_DIRECTORIES)
file(APPEND "${CMAKE_BINARY_DIR}/my_app_info.txt" "Include dirs: ${inc_dirs}\n")
5.詳細跟蹤 CMake 執行流程:--debug-output
?與?--trace
5.1.--debug-output
:輸出調試級信息
顯示 CMake 內部的調試信息(如變量查找、緩存讀取),但不顯示所有命令:
cmake --debug-output .. # 在構建目錄執行,.. 是源碼目錄
5.2.--trace
:跟蹤所有執行的命令(最詳細)
輸出?每一行執行的 CMake 命令(包括?if
、set
、include
?等),適合追蹤腳本執行路徑:
cmake --trace .. # 輸出所有命令(可能非常多,建議重定向到文件)
cmake --trace .. > cmake_trace.log # 保存到文件,方便搜索
cmake --trace-expand .. > cmake_trace_expanded.log
-
啟用后會打印 CMake 執行的每一行腳本,是終極調試手段(輸出量巨大)。
-
cmake --trace .
: 基本跟蹤。 -
cmake --trace-expand .
: 跟蹤并展開所有變量。這是查看變量實際值如何被代入命令的最強方式。 -
可以指定跟蹤范圍?
--trace-source=<file>
,?--trace-redirect=<file>
。
5.3.--warn-uninitialized
-
警告使用了未顯式初始化(未設置)的變量(非常有用!)。
-
cmake --warn-uninitialized .
5.4.
--graphviz=
?(CMake 2.8.10+)
-
生成一個?
.dot
?文件,可視化顯示目標之間的依賴關系圖。 -
cmake --graphviz=graph.dot .
?然后用 Graphviz 工具(如?dot -Tpng graph.dot -o graph.png
)生成圖片查看。
6.調試?find_package
?依賴查找失敗
find_package
?找不到依賴是常見問題(如路徑錯誤、版本不匹配),可通過以下方法定位:
1.啟用查找調試模式(CMAKE_FIND_DEBUG_MODE
)
設置?CMAKE_FIND_DEBUG_MODE
?為?ON
,CMake 會輸出?find_package
?查找依賴的?詳細過程(搜索路徑、檢查的文件、匹配的版本等):
# 在 find_package 前設置(臨時生效)
set(CMAKE_FIND_DEBUG_MODE ON)
find_package(SomeLib REQUIRED)
set(CMAKE_FIND_DEBUG_MODE OFF) # 用完關閉,避免輸出過多
示例輸出(關鍵部分):
CMAKE_FIND_DEBUG_MODE: FIND_PACKAGE(SomeLib)
CMAKE_FIND_DEBUG_MODE: Checking prefixes: /usr/local, /usr, ...
CMAKE_FIND_DEBUG_MODE: Looking for SomeLibConfig.cmake in .../lib/cmake/SomeLib
CMAKE_FIND_DEBUG_MODE: Found SomeLibConfig.cmake at /usr/lib/cmake/SomeLib
...
2.檢查?SomeLib_DIR
?緩存變量
-
CMake GUI 或?
cmake -L
?查看緩存變量,確保?SomeLib_DIR
?指向了包含?SomeLibConfig.cmake
?的正確目錄。
3.手動檢查模塊路徑
-
打印?
CMAKE_MODULE_PATH
?和?CMAKE_PREFIX_PATH
?查看自定義查找路徑。 -
檢查標準路徑(
/usr/lib/cmake/SomeLib
,?/usr/local/lib/cmake/SomeLib
?等)。
7.使用 CMake GUI /?ccmake
1.cmake-gui
?(圖形界面)
-
可視化緩存變量:?清晰看到所有緩存變量的當前值(包括類型和描述)。
-
修改和重新配置:?方便地修改變量值(如?
CMAKE_BUILD_TYPE
,?BUILD_SHARED_LIBS
, 庫路徑等)并點擊 "Configure" 觀察效果。 -
查看生成輸出:?界面下方有輸出日志窗口。
-
分組和搜索:?方便管理大量變量。
2.ccmake
?(終端 curses 界面)
-
在終端中提供類似?
cmake-gui
?的交互式緩存變量編輯功能。 -
對于遠程開發或無 GUI 環境非常有用。基本操作:
-
c
?或?g
?進行配置/生成。 -
t
?切換高級變量顯示。 -
?
?查看幫助。 -
方向鍵移動,
Enter
?編輯變量。
-
8.調試工具鏈文件 (CMAKE_TOOLCHAIN_FILE
)
-
大量使用?
message()
:?在工具鏈文件中關鍵位置(設置編譯器標志、路徑、平臺變量前后)打印信息。 -
檢查環境變量:?工具鏈文件經常依賴環境變量(
PATH
,?CC
,?CXX
,?SDKROOT
?等),確保它們設置正確并在工具鏈文件中打印出來。 -
驗證編譯器:?在工具鏈文件末尾或之后添加:
message(STATUS "CMAKE_C_COMPILER = ${CMAKE_C_COMPILER}")
message(STATUS "CMAKE_CXX_COMPILER = ${CMAKE_CXX_COMPILER}")
enable_language(C CXX) # 強制嘗試檢測編譯器
9.檢查編譯器 / 平臺兼容性:日志文件
CMake 在配置時會執行編譯測試(如檢查編譯器特性、庫是否可鏈接),結果記錄在以下日志中,用于調試 “編譯失敗”“特性檢測錯誤”:
CMakeFiles/CMakeOutput.log
:記錄成功的編譯測試(如 “編譯器支持 C++17”)。CMakeFiles/CMakeError.log
:記錄失敗的編譯測試(如 “鏈接庫時未找到符號”“編譯器不支持某特性”)。
用法:
當遇到 “某特性被誤判不支持” 或 “庫鏈接失敗” 時,打開這兩個文件,搜索具體的測試代碼和錯誤信息(如編譯器報錯),定位問題(如缺少頭文件、庫路徑錯誤)。
10.驗證條件判斷邏輯
CMake 的?if()
?條件判斷支持多種語法(如版本比較、變量存在性、路徑檢查),若分支邏輯異常,可直接輸出條件表達式的結果:
# 檢查變量是否存在(NOT DEFINED)
message(STATUS "MY_VAR 是否未定義: $<NOT:$<DEFINED:MY_VAR>>") # 生成器表達式(CMake 3.15+)# 檢查版本比較結果
set(CMAKE_VERSION_STR "3.20.0")
message(STATUS "版本是否 >=3.10: ${CMAKE_VERSION_STR VERSION_GREATER_EQUAL 3.10}") # 輸出 TRUE/FALSE# 檢查路徑是否存在
message(STATUS "src 目錄是否存在: ${EXISTS ${CMAKE_SOURCE_DIR}/src}")
11.其他實用技巧
1.使用?VERBOSE
?構建輸出編譯命令
配置時設置?CMAKE_VERBOSE_MAKEFILE
?為?ON
,構建時會輸出詳細的編譯 / 鏈接命令(檢查是否使用了正確的頭文件路徑、庫路徑、宏定義):
set(CMAKE_VERBOSE_MAKEFILE ON) # 在 CMakeLists.txt 中設置
或構建時臨時啟用:
make VERBOSE=1 # 或 ninja -v
2.用?try_compile
/try_run
?調試編譯特性
當需要驗證 “某段代碼是否能編譯 / 運行” 時,使用?try_compile
(編譯測試)或?try_run
(運行測試),并輸出結果:
# 測試代碼是否能編譯(如檢查是否支持 std::optional)
try_compile(SUPPORT_OPTIONAL${CMAKE_BINARY_DIR}/test # 測試目錄SOURCES ${CMAKE_SOURCE_DIR}/test/optional_test.cpp # 測試代碼
)
message(STATUS "是否支持 std::optional: ${SUPPORT_OPTIONAL}")
3.縮小范圍:逐步注釋代碼
若腳本復雜,可逐步注釋部分代碼(如?include
、find_package
、條件分支),定位到具體哪段代碼導致異常(類似 “二分法” 調試)。
12.總結
CMake 調試的核心是?“驗證變量值”“跟蹤執行流程”“檢查依賴查找過程”,常用工具鏈包括:
message()
?輸出變量和分支狀態;cmake -L
?查看緩存變量;--trace
/--debug-output
?跟蹤命令執行;CMAKE_FIND_DEBUG_MODE
?調試依賴查找;- 日志文件(
CMakeOutput.log
/CMakeError.log
)分析編譯測試。
根據問題的復雜程度,由淺入深地運用這些技巧,大部分 CMake 配置問題都能有效定位和解決。調試完成后記得清理或注釋掉調試性的?message()
?語句,保持 CMakeLists.txt 的整潔。
相關鏈接
- CMake 官網?CMake - Upgrade Your Software Build System
- CMake 官方文檔:CMake Tutorial — CMake 4.1.0-rc1 Documentation
- CMake 源碼:https://github.com/Kitware/CMake
- CMake 源碼:CMake · GitLab
- 中文版基礎介紹:?CMake 入門實戰 | HaHack
- wiki:?Home · Wiki · CMake / Community · GitLab
- Modern CMake 簡體中文版:?Introduction · Modern CMake