CMake自動管理C/C++項目的實現
1. 介紹
CMake 是一個強大的構建系統(tǒng),可用于跨平臺管理 C/C++ 項目的編譯過程。本 CMakeLists.txt 文件提供了一種自動化的方式來管理 C/C++ 項目,包括創(chuàng)建代碼目錄、自動編譯所有源文件、管理輸出文件等。
2. CMake 最低版本要求 & 項目信息
cmake_minimum_required(VERSION 3.14) project(TestDemo C CXX)
這段代碼確保 CMake 版本不低于 3.14,并定義項目名稱 TestDemo,支持 C 和 C++ 語言。
3. 主要功能
該 CMakeLists.txt 具備以下功能:
- 自動創(chuàng)建
src目錄,存放源代碼文件; - 自動遍歷
src目錄,編譯其中的所有.c和.cpp文件; - 設定編譯輸出路徑,避免污染項目根目錄:
.runtime/目錄存放可執(zhí)行文件;.library/目錄存放靜態(tài)/動態(tài)庫;.archive/目錄存放編譯中間文件;
- 支持多個
main()函數,可以直接運行單個源文件。
4. C/C++ 語言標準設定
set(CMAKE_C_STANDARD 99) set(CMAKE_CXX_STANDARD 17)
此處指定 C 語言標準為 C99,C++ 語言標準為 C++17。
5. 編譯輸出目錄設定
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/.archive)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/.library)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/.runtime)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/.runtime)
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/.library)
這些配置確保編譯后的文件不會污染項目根目錄。
6. 自動創(chuàng)建 src 目錄
set(SRC_DIR ${PROJECT_SOURCE_DIR}/src)
if (NOT EXISTS ${SRC_DIR})
file(MAKE_DIRECTORY ${SRC_DIR})
endif ()
message(STATUS "src目錄: ${SRC_DIR}")
若 src 目錄不存在,則自動創(chuàng)建,并在 CMake 輸出日志中打印 src 目錄路徑。
7. 頭文件搜索路徑
include_directories(${SRC_DIR})
這行代碼告訴編譯器在 src 目錄中查找頭文件。
8. 自動遍歷 src 目錄下的 C/C++ 文件
file(GLOB_RECURSE SOURCE_FILES "${SRC_DIR}/*.c" "${SRC_DIR}/*.cpp")
使用 file(GLOB_RECURSE ...) 指令遞歸查找 src 目錄下的所有 .c 和 .cpp 文件。
9. 生成唯一目標名并添加可執(zhí)行文件
foreach (SOURCE_FILE ${SOURCE_FILES})
get_filename_component(FILE_NAME ${SOURCE_FILE} NAME)
string(REGEX REPLACE "^${SRC_DIR}[\\/]" "" RELATIVE_PATH ${SOURCE_FILE})
string(REGEX REPLACE "[\\/]" "." TARGET_NAME ${RELATIVE_PATH})
add_executable(${TARGET_NAME} ${SOURCE_FILE})
message(STATUS "[Compile] ${SOURCE_FILE} --> ${TARGET_NAME}")
endforeach ()
- 獲取文件名:
get_filename_component(FILE_NAME ${SOURCE_FILE} NAME) - 計算相對路徑(去掉
src/部分),并將/替換為.,仿照 Java 包命名格式。 - 添加可執(zhí)行文件,確保每個 C/C++ 文件都能獨立編譯。
10. 編譯警告設置
if (MSVC)
add_compile_options(/W4)
else ()
add_compile_options(-Wall -Wextra -Wpedantic)
endif ()
- Windows(MSVC):開啟最高級別
/W4警告。 - Linux/macOS(GCC/Clang):啟用
-Wall -Wextra -Wpedantic,增強代碼質量。
11. CMake 關鍵指令說明
11.1 獲取文件名組件
get_filename_component(變量名 文件路徑 組件類型)
示例:
get_filename_component(FILE_NAME "D:/test/hello.txt" NAME_WE)
11.2 遞歸掃描目錄下的 C/C++ 文件
file(GLOB_RECURSE 變量名 "路徑/*.c" "路徑/*.cpp")
示例:
file(GLOB_RECURSE SOURCE_FILES "src/*.c" "src/*.cpp")
11.3 正則替換字符串
string(REGEX REPLACE "正則表達式" "替換字符串" 變量 輸入變量)
示例:
string(REGEX REPLACE "\\/" "." TARGET_NAME "src/com/example/Main.cpp")
11.4 數學運算
math(EXPR 變量 "數學表達式")
示例:
math(EXPR RESULT "5 * 5")
11.5 編譯可執(zhí)行文件
add_executable(可執(zhí)行文件 源文件)
示例:
add_executable(my_program main.cpp)
11.6 目標鏈接庫
target_link_libraries(可執(zhí)行文件 PRIVATE 庫名1 庫名2)
示例:
target_link_libraries(my_program PRIVATE jsoncpp_lib jsoncpp_object)
11.7 日志輸出
message(STATUS "日志信息")
示例:
message(STATUS "編譯目錄: ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
12. 結論
這個 CMakeLists.txt 通過自動化管理 C/C++ 源文件、輸出目錄和編譯過程,使項目更加結構化。它可以讓開發(fā)者專注于編寫代碼,而無需手動管理每個可執(zhí)行文件的構建。
13. 我常用的CMakeLists.txt配置
# 設置 CMake 最小版本 & 項目名稱, 編程語言C、C++
cmake_minimum_required(VERSION 3.14)
project(TestDemo C CXX)
# >------------------------------- CMake 功能說明 -------------------------------
# 本 CMakeLists.txt 具備以下功能:
# 1. 重新加載 CMake 項目時,自動創(chuàng)建 src 目錄(存放代碼文件)。
# 2. 自動遍歷 src 目錄下的所有 .c 和 .cpp 文件,并生成獨立的可執(zhí)行文件。
# 3. 設定編譯輸出路徑,避免污染項目根目錄:
# - 可執(zhí)行文件存放于 `.runtime/` 目錄。(即編譯結果)
# - 靜態(tài)/動態(tài)庫存放于 `.library/` 目錄。
# - 編譯時中間文件存放于 `.archive/` 目錄。
# 4. 允許多個 main() 函數共存,可直接運行單個文件。
# >------------------------------- 使用說明 -------------------------------
# 使用此CMakeList時,若要新建C/C++語言文件,請按照以下步驟:
# 1. 右鍵src目錄(如果沒有src目錄,請先創(chuàng)建) ——> 新建 ——> C/C++源文件
# 2. 在彈出的對話框中,輸入文件名(僅允許英文小寫及下劃線,不要出現空格),
# 后綴為 ".c(C語言文件)"或".cpp(C++文件)",切記不要勾選“添加到目標”,點擊確定。
# 3. 點擊 左上角橫線 ——> 文件 ——> 重新加載CMake項目。
# 注:未重載前,進入文件可能會有“不屬于任何項目目標”的警告,重載后即會消失。
# 若此警告未消失,請檢查文件是否在src目錄下,以及文件名是否符合規(guī)范。
# >------------------------------- 運行說明 -------------------------------
# 使用此CMakeList時,若運行 main() 函數代碼,請直接點擊函數前的綠色三角形按鈕。
# 右上角的運行按鈕會自動運行最近一次運行的程序,因此可能不是你想要的結果。
# >------------------------------- 注意事項 -------------------------------
# 可執(zhí)行文件名,模仿Java包名 + 文件名 格式
# 例如:
# 文件路徑 src/com/example/testdemo/Main.cpp
# 可執(zhí)行文件名 com.example.testdemo.Main.cpp
# ------------------------------------------------------------------------
# 按照書本要求設定C語言和C++版本
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 17)
# 定義輸出目錄,避免污染根目錄
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/.archive)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/.library)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/.runtime)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/.runtime)
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/.library)
# 自動創(chuàng)建存放代碼文件夾 src (重新加載CMake項目后,會自動創(chuàng)建)
set(SRC_DIR ${PROJECT_SOURCE_DIR}/src)
if (NOT EXISTS ${SRC_DIR})
file(MAKE_DIRECTORY ${SRC_DIR})
endif ()
message(STATUS "src目錄: ${SRC_DIR}")
# 設定頭文件搜索路徑
include_directories(${SRC_DIR})
# 遍歷 src 目錄下所有 C/C++ 文件并自動添加
file(GLOB_RECURSE SOURCE_FILES "${SRC_DIR}/*.c" "${SRC_DIR}/*.cpp")
foreach (SOURCE_FILE ${SOURCE_FILES}) # 遍歷所有發(fā)現的文件
# 生成唯一的目標名(避免重名問題)
# get_filename_component(FILE_NAME ${SOURCE_FILE} NAME_WE) # 獲取文件名(去掉擴展名)【注意: 這樣子 C、C++ 文件在同一個目錄, 并且文件名相同時,會導致沖突】
get_filename_component(FILE_NAME ${SOURCE_FILE} NAME) # 獲取文件名
# 計算相對路徑(去掉 src/ 部分),并且將`/`替換成`.` 模仿成Java的包名,方便后續(xù)添加到編譯命令中
string(REGEX REPLACE "^${SRC_DIR}[\\/]" "" RELATIVE_PATH ${SOURCE_FILE})
string(REGEX REPLACE "[\\/]" "." TARGET_NAME ${RELATIVE_PATH}) # 將`/`替換成`.` 模仿成Java的包名,避免重名問題
# 添加可執(zhí)行文件
add_executable(${TARGET_NAME} ${SOURCE_FILE})
message(STATUS "[Compile] ${SOURCE_FILE} --> ${TARGET_NAME}")
endforeach () # 遍歷結束
# -------------------------- CMake 關鍵指令說明 --------------------------
# 1. 獲取文件名組件:
# get_filename_component(變量名 文件路徑 組件類型)
# 例如: 獲取文件名(無擴展名)
# get_filename_component(FILE_NAME "D:/test/hello.txt" NAME_WE)
#
# 2. 遞歸掃描指定目錄下的所有 C/C++ 文件:
# file(GLOB_RECURSE 保存結果的變量名 "路徑/*.c" "路徑/*.cpp")
# 例如: 掃描 src 目錄下的所有 C 和 C++ 文件
# file(GLOB_RECURSE SOURCE_FILES "D:/test1/*.c" "D:/test2/*.cpp")
#
# 3. 字符串處理:
# - 正則表達式替換:
# string(REGEX REPLACE "正則表達式" "替換字符串" 存儲結果變量 輸入變量)
# 例如: 將路徑中的 "/" 替換為 "."
# string(REGEX REPLACE "/" "." TARGET_NAME "D:/test/hello.txt")
#
# - 計算字符串長度:
# string(LENGTH 字符串變量 存儲結果變量)
#
# - 字符串截取:
# string(SUBSTRING 字符串變量 起始下標 截取元素個數 存儲結果變量)
# 例如: 截取字符串的前 5 個字符
# string(SUBSTRING "hello world" 0 5 SUB_STR)
#
# 4. 數學運算:
# math(EXPR 存儲結果變量 "數學表達式")
# 例如: 計算一個數值的平方
# math(EXPR RESULT "5 * 5")
#
# 5. 編譯可執(zhí)行文件:
# add_executable(可執(zhí)行文件名稱 源碼文件)
# 例如: 生成一個名為 my_program 的可執(zhí)行文件
# add_executable(my_program main.cpp)
#
# 6. 目標鏈接庫:
# target_link_libraries(可執(zhí)行文件名稱 PRIVATE 庫名1 庫名2)
# 例如: 連接 jsoncpp 庫
# target_link_libraries(my_program PRIVATE jsoncpp_lib jsoncpp_object)
#
# 7. 日志輸出:
# message(STATUS "日志信息")
# 例如: 輸出 CMake 變量值
# message(STATUS "編譯目錄: ${OUTPUT_DIR_RUNTIME}")
# ----------------------------------------------------------------------
# *********************************************************************************
# 設置編譯選項(開啟警告)
if (MSVC)
add_compile_options(/W4)
else ()
add_compile_options(-Wall -Wextra -Wpedantic)
endif ()
效果圖

到此這篇關于CMake自動管理C/C++項目的實現的文章就介紹到這了,更多相關CMake自動管理C/C++內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

