文章目錄

  • 一、前言
  • 二、一些基本概念
  • 1、cmake
  • 2、列表項編譯工具鏈toolchain
  • 2.1 預處理器
  • 2.2 編譯器(gcc/g++)
  • 2.3 彙編器
  • 2.4 鏈接器
  • 3、編譯/項目構建
  • 3.1 少量文件
  • 3.2 大量文件結構複雜
  • 3.3 大量文件結構複雜(不依賴於平台)
  • 4、構建過程
  • 5、庫與進程
  • 6、版本發佈
  • 7、庫鏈接的傳遞性
  • 三、常用基本命令
  • 1、CMakeLists.txt文件通過 # 註釋行,通過 #[[內容]] 註釋塊
  • 2、指定使用的本地cmak工具最低版本
  • 3、定義工程名稱,還可指定版本、描述、URL、支持的語言
  • 4、設置C++語言標準,默認使用C++11標準
  • 4.1 設置標準版本
  • 4.2 版本支持與否的處理
  • 4.3 C++語言擴展的開關
  • 5、定義“可執行程序”輸出及名字,鏈接若干源文件
  • 5.1 多個源文件可用空格或分號隔開,也可用變量存儲源文件名
  • 5.2 使用變量(默認字符串類型)存儲多個源文件名
  • 6、製作動態/靜態庫:動態庫有可執行權限,靜態庫沒有可執行權限
  • 7、設置可執行文件/庫的輸出路徑,若路徑不存在自動生成
  • 7.1 指定—可執行文件輸出路徑
  • 7.1 指定—庫輸出路徑
  • 8、搜索文件(不必一一指定源文件)
  • 8.1 aux_source_directory(路徑 變量名)
  • 8.2 file(GLOB/GLOB_RECURSE 變量名 要搜索的文件路徑及類型)
  • 9、變量操作
  • 9.1 字符串拼接——會覆蓋原本變量字符串
  • 9.2 字符串追加——不會覆蓋
  • 9.3 字符串移除
  • 9.4 list命令的其它功能
  • 10、指定頭文件路徑
  • 11、鏈接三方庫(靜態、動態)
  • 11.1 靜態庫的鏈接——指定庫的路徑和庫名
  • 11.2 動態庫的鏈接——指定庫的路徑和庫名,鏈接動態庫的語句在生成可執行程序/庫之後
  • 12、日誌——調試命令message,可以打印變量值,默認為STATUS等級
  • 13、自定義宏——add_definitions
  • 14、定義可配置項
  • 15、條件語句
  • 15.1 單個分支:如果ENABKE_DEBUG為ON,則定義調試宏
  • 15.2 多個分支:根據系統類型添加不同的預定義宏
  • 16、CMake嵌套
  • 參考資料

一、前言

  • 個人學習、工作自用筆記,若有錯誤請看參考文檔與視頻

二、一些基本概念

1、cmake

  • cmake是一個項目構建工具,可以跨平台,makefile寫起來繁瑣,依賴於系統。
  • 可以通過命令行、可以通過GUI

2、列表項編譯工具鏈toolchain

  • 預處理器、編譯器、彙編器、鏈接器

2.1 預處理器

  • 頭文件展開、宏替換、去除註釋

2.2 編譯器(gcc/g++)

  • 將源文件編譯為彙編文件(詞法/語法/語義分析、目標代碼生成)

2.3 彙編器

  • 得到二進制文件(windows系統下為.obj文件,linux下為.o文件)

2.4 鏈接器

  • 將多個二進制文件鏈接,生成一個可執行程序或動態/靜態鏈接庫

3、編譯/項目構建

3.1 少量文件

  • 通過gcc/g++命令將源代碼編譯為可執行程序或庫

3.2 大量文件結構複雜

  • 編寫Makefile文件,填寫若干指令,告訴編譯器如何編譯文件,使用批處理命令make執行編譯

3.3 大量文件結構複雜(不依賴於平台)

  • 編寫CMakeLists.txt,使用cmake命令生成Makefile等一系列文件,後續步驟如3.2所示

4、構建過程

  • FirsetStep: cmake CMakeLists.txt文件所在路徑
  • SecondStep: make

5、庫與進程

  • 動態庫在內存中有且僅有一份,且不屬於某個進程

6、版本發佈

  • 版本發佈包括:include頭文件+靜態/動態庫;
  • 因為庫為二進制,所以需要提供頭文件,其為庫文件的接口與説明

7、庫鏈接的傳遞性

  • 庫鏈接具有傳遞性

三、常用基本命令

1、CMakeLists.txt文件通過 # 註釋行,通過 #[[內容]] 註釋塊

2、指定使用的本地cmak工具最低版本

cmake_minimum_required(3.16)

3、定義工程名稱,還可指定版本、描述、URL、支持的語言

project(qgis v1.0)

4、設置C++語言標準,默認使用C++11標準

4.1 設置標準版本

set(CMAKE_CXX_STANDARD 17)

  • 也可以在執行cmake命令的時候,設置CMAKE_CXX_STANDARD宏來指定C++標準
  • cmake CMakeLists.txt文件所在路徑 -DCMAKE_CXX_STANDARD=11
  • -D表示設置宏的值

4.2 版本支持與否的處理

  • 當編譯器不支持指定的C++標準版本時,若將該變量設為ON,CMake將報錯並終止構建過程
  • 若將該變量設為OFF,CMake將自動降級到編譯器支持的最接近的C++標準版本
    set(CMAKE_CXX_STANDARD_REQUIRED ON)

4.3 C++語言擴展的開關

  • 若設為OFF,可確保項目遵循C++標準,具有更好的可移植性;
  • 設為ON,則允許使用編譯器特定的C++語言擴展
    set(CMAKE_CXX_EXTENSIONS OFF)

5、定義“可執行程序”輸出及名字,鏈接若干源文件

5.1 多個源文件可用空格或分號隔開,也可用變量存儲源文件名

add_executable(qgis a.cpp b.app)

5.2 使用變量(默認字符串類型)存儲多個源文件名

  • 使用固定語法${變量名}取值
set(SRC a.cpp b.cpp)
add_executable(qgis ${SRC})

6、製作動態/靜態庫:動態庫有可執行權限,靜態庫沒有可執行權限

  • 庫全名的組成:lib+庫名+.後綴
  • 下方命令生成libqgis.a或libqgis.so文件(Linux)
  • 若未指定庫類型,默認生成靜態庫文件
add_library(庫名 STATIC ${SRC})
add_library(庫名 SHARED ${SRC})

7、設置可執行文件/庫的輸出路徑,若路徑不存在自動生成

7.1 指定—可執行文件輸出路徑

  • EXECUTABLE_OUTPUT_PATH為cmake自帶的指定可執行文件/動態庫輸出路徑的變量
    set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

7.1 指定—庫輸出路徑

  • EXECUTABLE_OUTPUT_PATH為cmake自帶的指定靜態/動態庫輸出路徑的變量
    set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

8、搜索文件(不必一一指定源文件)

8.1 aux_source_directory(路徑 變量名)

  • PROJECT_SOURCE_DIR為項目的絕對路徑,CMAKE_CURRENT_SOURCE_DIR為CMakeLists.txt所在路徑
aux_source_directory(${PROJECT_SOURCE_DIR}/src SRC_LIST)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)
add_executable(qgis ${SRC_LIST})

8.2 file(GLOB/GLOB_RECURSE 變量名 要搜索的文件路徑及類型)

  • 指定搜索.cpp後綴文件
    file(GLOB SRC_LIST ${PROJECT_SOURCE_DIR}/src/*.cpp)
  • 遞歸搜索文件夾中的文件
file(GLOB_RECURSE SRC_LIST ${PROJECT_SOURCE_DIR}/src/*.cpp)
add_executable(qgis ${SRC_LIST})

9、變量操作

9.1 字符串拼接——會覆蓋原本變量字符串

  • 使用set拼接兩個字符串變量(源文件分佈在多個目錄下,使用file讀取多個字符串)
    set(變量名 ${變量名1} ${變量名2} …)

9.2 字符串追加——不會覆蓋

  • 使用list對變量追加字符串(拼接),list就是變量
    list(APPEND list名 ${變量名1} ${變量名2} …)

9.3 字符串移除

  • 多個字符串拼接後會在底層維護字符串之間的分隔符(;),規範字符串的移除
    list(REMOVE_ITEM list名 字符串或${變量名1})

9.4 list命令的其它功能

  • 獲取list的長度
    list(LENGTH list名 存儲長度的變量名)
  • 獲取列表中指定索引值的元素,可以指定多個索引
  • 索引從0開始,從前往後;也可以為負數,從後往前
    list(GET list名 索引1 索引2)
  • 將列表中的字符串用連接符(字符串)連接起來,組成一個字符串
    list(JOIN list名 連接符 新創建的字符串變量名)
  • 查找列表中是否存在指定的元素,若未找到,返回負一
    list(FIND list名 字符串或變量名 新創建的變量名)
  • 指定位置插入若干元素
    list(INSERT list名 索引 若干字符串或變量名)
  • 0索引位置插入若干元素
    list(PREPEND list名 索引 若干字符串或變量名)
  • 移除列表最後一個元素
    list(POP_BACK list名 彈出元素變量名(可選))
  • 移除列表最後一個元素
    list(POP_FRONT list名 彈出元素變量名(可選))
  • 移除列表中指定索引元素
    list(REMOVE_AT list名 索引1 索引2 …)
  • 移除列表中的重複元素
    list(REMOVE_DUPLICATES list名)
  • 翻轉列表
    list(REVERSE list名)
  • 列表排序
  • 排序方法COMPARE:STRING|FILE_BASENAME|NATURAL
  • 大小寫敏感CASE:SENSITIVE|INSENSITIVE
  • 排序順序ORDER:ASCENDING|DESCENDING
    list(SORT list名 STRING SENSITIVE ASCENDING)

10、指定頭文件路徑

  • 指定頭文件路徑後,源文件include頭文件時只寫文件名.h即可,不必填寫全路徑
    include_directories(${PROJECT_SOURCE_DIR}/include)

11、鏈接三方庫(靜態、動態)

  • 系統庫直接指定庫的名字即可,因為系統知道其位置;第三方庫需要告訴當前項目庫的路徑
  • 若三方庫為靜態庫,編譯會將其打包到可執行文件;若三方庫為動態庫,後續部署項目,需要拷貝動態庫並設置環境變量
  • link_directories設置庫的查詢位置
  • 實際link_libraries和target_link_libraries均可鏈接動態庫和靜態庫,推薦後者

11.1 靜態庫的鏈接——指定庫的路徑和庫名

link_directories(${PROJECT_SOURCE_DIR}/lib)
link_libraries(庫名1 庫名1 ……)

11.2 動態庫的鏈接——指定庫的路徑和庫名,鏈接動態庫的語句在生成可執行程序/庫之後

link_directories(${PROJECT_SOURCE_DIR}/lib)
add_executable(qgis ${SRC}) 
target_link_libraries(target(可執行文件/庫/源文件) 權限PRIVATE/PUBLIC/INTERFACE(可省略,默認PUBLIC) 庫名)

12、日誌——調試命令message,可以打印變量值,默認為STATUS等級

  • 消息類型:STATUS|WARNING|AUTHOR_WARNING|SEND_ERROR|FATAL_ERROR
  • STATUS:非重要消息
  • WARNING:CMake警告,會繼續執行
  • AUTHOR_WARNING:CMake重要警告(dev),會繼續執行
  • SEND_ERROR:CMake錯誤,繼續執行,但是會跳過生成的步驟
  • FINAL_ERROR:CMake致命錯誤,終止所有處理過程
    message(消息類型 "輸出字符串")

13、自定義宏——add_definitions

  • 通過在代碼中定義宏,控制測試代碼是否生效(#ifdef 宏 測試語句 #endif)
  • 可以不在代碼中定義宏,在gcc/g++命令中指定宏:例如指定DEBUG宏(g++ test.cpp -DDEBUG -o app)
  • 在CMake中也可以做類似的事情,命令為add_definitions,宏名稱前加-D
    add_definitions(-DDEBUG_OUTPUT)

14、定義可配置項

  • 下列命令定義了一個名為ENABKE_DEBUG的可配置選項,為布爾型變量
  • 第二個參數是對配置項的描述,第三個為變量的值,默認為ON
    option(ENABKE_DEBUG "Enable debug output" ON)

15、條件語句

15.1 單個分支:如果ENABKE_DEBUG為ON,則定義調試宏

  • 可在執行CMake命令時候,通過cmake -DDEBUG_DEBUG=OFF 關閉宏,不輸出調試語句
if(ENABLE_DEBUG)
    add_definitions(-DDEBUG_OUTPUT)
endif()

15.2 多個分支:根據系統類型添加不同的預定義宏

if(WIN32)
    # 針對windows系統設置宏
    add_definitions(-DWINDOWS_VERSION)
elseif(UNIX)
    # 針對類Unix系統設置宏
else()
    # 其它系統:報錯
    message(FINAL_ERROR, "UnKnown operating system")
endif()

16、CMake嵌套

  • source_dir為子節點(模塊)對應的目錄
  • 主目錄含一個父CMakeLists.txt文件,每個模塊含一個子CMakeLists.txt文件
  • 通過以下命令在父文件中添加子文件目錄
    add_subdirectory(source_dir)

參考資料

參考1:CMake 保姆級教程(上) 參考2:CMake 保姆級教程(下) 參考3:CMake 保姆級教程—bilibili視頻【C/C++】 參考4:完全入門CMake語法與CMakeList編寫