CMake自動(dòng)管理C/C++項(xiàng)目的實(shí)現(xiàn)
1. 介紹
CMake 是一個(gè)強(qiáng)大的構(gòu)建系統(tǒng),可用于跨平臺(tái)管理 C/C++ 項(xiàng)目的編譯過程。本 CMakeLists.txt
文件提供了一種自動(dòng)化的方式來管理 C/C++ 項(xiàng)目,包括創(chuàng)建代碼目錄、自動(dòng)編譯所有源文件、管理輸出文件等。
2. CMake 最低版本要求 & 項(xiàng)目信息
cmake_minimum_required(VERSION 3.14) project(TestDemo C CXX)
這段代碼確保 CMake 版本不低于 3.14,并定義項(xiàng)目名稱 TestDemo
,支持 C 和 C++ 語言。
3. 主要功能
該 CMakeLists.txt
具備以下功能:
- 自動(dòng)創(chuàng)建
src
目錄,存放源代碼文件; - 自動(dòng)遍歷
src
目錄,編譯其中的所有.c
和.cpp
文件; - 設(shè)定編譯輸出路徑,避免污染項(xiàng)目根目錄:
.runtime/
目錄存放可執(zhí)行文件;.library/
目錄存放靜態(tài)/動(dòng)態(tài)庫;.archive/
目錄存放編譯中間文件;
- 支持多個(gè)
main()
函數(shù),可以直接運(yùn)行單個(gè)源文件。
4. C/C++ 語言標(biāo)準(zhǔn)設(shè)定
set(CMAKE_C_STANDARD 99) set(CMAKE_CXX_STANDARD 17)
此處指定 C 語言標(biāo)準(zhǔn)為 C99,C++ 語言標(biāo)準(zhǔn)為 C++17。
5. 編譯輸出目錄設(shè)定
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)
這些配置確保編譯后的文件不會(huì)污染項(xiàng)目根目錄。
6. 自動(dòng)創(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
目錄不存在,則自動(dòng)創(chuàng)建,并在 CMake 輸出日志中打印 src
目錄路徑。
7. 頭文件搜索路徑
include_directories(${SRC_DIR})
這行代碼告訴編譯器在 src
目錄中查找頭文件。
8. 自動(dòng)遍歷 src 目錄下的 C/C++ 文件
file(GLOB_RECURSE SOURCE_FILES "${SRC_DIR}/*.c" "${SRC_DIR}/*.cpp")
使用 file(GLOB_RECURSE ...)
指令遞歸查找 src
目錄下的所有 .c
和 .cpp
文件。
9. 生成唯一目標(biāo)名并添加可執(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)
- 計(jì)算相對(duì)路徑(去掉
src/
部分),并將/
替換為.
,仿照 Java 包命名格式。 - 添加可執(zhí)行文件,確保每個(gè) C/C++ 文件都能獨(dú)立編譯。
10. 編譯警告設(shè)置
if (MSVC) add_compile_options(/W4) else () add_compile_options(-Wall -Wextra -Wpedantic) endif ()
- Windows(MSVC):開啟最高級(jí)別
/W4
警告。 - Linux/macOS(GCC/Clang):啟用
-Wall -Wextra -Wpedantic
,增強(qiáng)代碼質(zhì)量。
11. CMake 關(guān)鍵指令說明
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 "正則表達(dá)式" "替換字符串" 變量 輸入變量)
示例:
string(REGEX REPLACE "\\/" "." TARGET_NAME "src/com/example/Main.cpp")
11.4 數(shù)學(xué)運(yùn)算
math(EXPR 變量 "數(shù)學(xué)表達(dá)式")
示例:
math(EXPR RESULT "5 * 5")
11.5 編譯可執(zhí)行文件
add_executable(可執(zhí)行文件 源文件)
示例:
add_executable(my_program main.cpp)
11.6 目標(biāo)鏈接庫
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. 結(jié)論
這個(gè) CMakeLists.txt
通過自動(dòng)化管理 C/C++ 源文件、輸出目錄和編譯過程,使項(xiàng)目更加結(jié)構(gòu)化。它可以讓開發(fā)者專注于編寫代碼,而無需手動(dòng)管理每個(gè)可執(zhí)行文件的構(gòu)建。
13. 我常用的CMakeLists.txt配置
# 設(shè)置 CMake 最小版本 & 項(xiàng)目名稱, 編程語言C、C++ cmake_minimum_required(VERSION 3.14) project(TestDemo C CXX) # >------------------------------- CMake 功能說明 ------------------------------- # 本 CMakeLists.txt 具備以下功能: # 1. 重新加載 CMake 項(xiàng)目時(shí),自動(dòng)創(chuàng)建 src 目錄(存放代碼文件)。 # 2. 自動(dòng)遍歷 src 目錄下的所有 .c 和 .cpp 文件,并生成獨(dú)立的可執(zhí)行文件。 # 3. 設(shè)定編譯輸出路徑,避免污染項(xiàng)目根目錄: # - 可執(zhí)行文件存放于 `.runtime/` 目錄。(即編譯結(jié)果) # - 靜態(tài)/動(dòng)態(tài)庫存放于 `.library/` 目錄。 # - 編譯時(shí)中間文件存放于 `.archive/` 目錄。 # 4. 允許多個(gè) main() 函數(shù)共存,可直接運(yùn)行單個(gè)文件。 # >------------------------------- 使用說明 ------------------------------- # 使用此CMakeList時(shí),若要新建C/C++語言文件,請(qǐng)按照以下步驟: # 1. 右鍵src目錄(如果沒有src目錄,請(qǐng)先創(chuàng)建) ——> 新建 ——> C/C++源文件 # 2. 在彈出的對(duì)話框中,輸入文件名(僅允許英文小寫及下劃線,不要出現(xiàn)空格), # 后綴為 ".c(C語言文件)"或".cpp(C++文件)",切記不要勾選“添加到目標(biāo)”,點(diǎn)擊確定。 # 3. 點(diǎn)擊 左上角橫線 ——> 文件 ——> 重新加載CMake項(xiàng)目。 # 注:未重載前,進(jìn)入文件可能會(huì)有“不屬于任何項(xiàng)目目標(biāo)”的警告,重載后即會(huì)消失。 # 若此警告未消失,請(qǐng)檢查文件是否在src目錄下,以及文件名是否符合規(guī)范。 # >------------------------------- 運(yùn)行說明 ------------------------------- # 使用此CMakeList時(shí),若運(yùn)行 main() 函數(shù)代碼,請(qǐng)直接點(diǎn)擊函數(shù)前的綠色三角形按鈕。 # 右上角的運(yùn)行按鈕會(huì)自動(dòng)運(yùn)行最近一次運(yùn)行的程序,因此可能不是你想要的結(jié)果。 # >------------------------------- 注意事項(xiàng) ------------------------------- # 可執(zhí)行文件名,模仿Java包名 + 文件名 格式 # 例如: # 文件路徑 src/com/example/testdemo/Main.cpp # 可執(zhí)行文件名 com.example.testdemo.Main.cpp # ------------------------------------------------------------------------ # 按照書本要求設(shè)定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) # 自動(dòng)創(chuàng)建存放代碼文件夾 src (重新加載CMake項(xiàng)目后,會(huì)自動(dòng)創(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}") # 設(shè)定頭文件搜索路徑 include_directories(${SRC_DIR}) # 遍歷 src 目錄下所有 C/C++ 文件并自動(dòng)添加 file(GLOB_RECURSE SOURCE_FILES "${SRC_DIR}/*.c" "${SRC_DIR}/*.cpp") foreach (SOURCE_FILE ${SOURCE_FILES}) # 遍歷所有發(fā)現(xiàn)的文件 # 生成唯一的目標(biāo)名(避免重名問題) # get_filename_component(FILE_NAME ${SOURCE_FILE} NAME_WE) # 獲取文件名(去掉擴(kuò)展名)【注意: 這樣子 C、C++ 文件在同一個(gè)目錄, 并且文件名相同時(shí),會(huì)導(dǎo)致沖突】 get_filename_component(FILE_NAME ${SOURCE_FILE} NAME) # 獲取文件名 # 計(jì)算相對(duì)路徑(去掉 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 () # 遍歷結(jié)束 # -------------------------- CMake 關(guān)鍵指令說明 -------------------------- # 1. 獲取文件名組件: # get_filename_component(變量名 文件路徑 組件類型) # 例如: 獲取文件名(無擴(kuò)展名) # get_filename_component(FILE_NAME "D:/test/hello.txt" NAME_WE) # # 2. 遞歸掃描指定目錄下的所有 C/C++ 文件: # file(GLOB_RECURSE 保存結(jié)果的變量名 "路徑/*.c" "路徑/*.cpp") # 例如: 掃描 src 目錄下的所有 C 和 C++ 文件 # file(GLOB_RECURSE SOURCE_FILES "D:/test1/*.c" "D:/test2/*.cpp") # # 3. 字符串處理: # - 正則表達(dá)式替換: # string(REGEX REPLACE "正則表達(dá)式" "替換字符串" 存儲(chǔ)結(jié)果變量 輸入變量) # 例如: 將路徑中的 "/" 替換為 "." # string(REGEX REPLACE "/" "." TARGET_NAME "D:/test/hello.txt") # # - 計(jì)算字符串長度: # string(LENGTH 字符串變量 存儲(chǔ)結(jié)果變量) # # - 字符串截取: # string(SUBSTRING 字符串變量 起始下標(biāo) 截取元素個(gè)數(shù) 存儲(chǔ)結(jié)果變量) # 例如: 截取字符串的前 5 個(gè)字符 # string(SUBSTRING "hello world" 0 5 SUB_STR) # # 4. 數(shù)學(xué)運(yùn)算: # math(EXPR 存儲(chǔ)結(jié)果變量 "數(shù)學(xué)表達(dá)式") # 例如: 計(jì)算一個(gè)數(shù)值的平方 # math(EXPR RESULT "5 * 5") # # 5. 編譯可執(zhí)行文件: # add_executable(可執(zhí)行文件名稱 源碼文件) # 例如: 生成一個(gè)名為 my_program 的可執(zhí)行文件 # add_executable(my_program main.cpp) # # 6. 目標(biāo)鏈接庫: # 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}") # ---------------------------------------------------------------------- # ********************************************************************************* # 設(shè)置編譯選項(xiàng)(開啟警告) if (MSVC) add_compile_options(/W4) else () add_compile_options(-Wall -Wextra -Wpedantic) endif ()
效果圖
到此這篇關(guān)于CMake自動(dòng)管理C/C++項(xiàng)目的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)CMake自動(dòng)管理C/C++內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++數(shù)據(jù)封裝以及定義結(jié)構(gòu)的詳細(xì)講解
這篇文章主要詳細(xì)講解了C++數(shù)據(jù)封裝以及定義結(jié)構(gòu),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05C++實(shí)現(xiàn)管理系統(tǒng)的示例代碼
這篇文章主要介紹了C++實(shí)現(xiàn)管理系統(tǒng)的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10C語言與C++項(xiàng)目實(shí)現(xiàn)相互調(diào)用
extern?“c”的作用可以實(shí)現(xiàn)c語言和c++相互調(diào)用,本文就詳細(xì)的介紹一下C語言與C++項(xiàng)目實(shí)現(xiàn)相互調(diào)用,感興趣的可以了解一下2022-01-01C語言數(shù)據(jù)結(jié)構(gòu)之線索二叉樹及其遍歷
這篇文章主要介紹了C語言數(shù)據(jù)結(jié)構(gòu)之線索二叉樹及其遍歷的相關(guān)資料,為了加快查找節(jié)點(diǎn)的前驅(qū)和后繼。對(duì)二叉樹的線索化就是對(duì)二叉樹進(jìn)行一次遍歷,在遍歷的過程中檢測(cè)節(jié)點(diǎn)的左右指針是否為空,如果是空,則將他們改為指向前驅(qū)和后繼節(jié)點(diǎn)的線索,需要的朋友可以參考下2017-08-08C++實(shí)現(xiàn)LeetCode(24.成對(duì)交換節(jié)點(diǎn))
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(24.成對(duì)交換節(jié)點(diǎn)),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07C語言安全之?dāng)?shù)組長度與指針實(shí)例解析
這篇文章主要介紹了C語言安全之?dāng)?shù)組長度與指針,需要的朋友可以參考下2014-07-07C語言實(shí)現(xiàn)linux網(wǎng)卡檢測(cè)精簡版
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)linux網(wǎng)卡檢測(cè)的精簡版,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06