Modern CMake 简介
作為擁有 20 年發(fā)展歷史的 CMake,肩負(fù) Build-system Generate 的重任,被越來(lái)越多的開(kāi)源庫(kù)接納,其自身理念也在不斷推陳出現(xiàn)。到底 Modern CMake 和 Traditional CMake 有什么區(qū)別,這是個(gè)值得討論的話題。
歷史背景
CMake 是一個(gè)構(gòu)建系統(tǒng)生成器(build-system generator)。常見(jiàn)的構(gòu)建系統(tǒng),有 Visual Studio,XCode,Make 等等。CMake 可以支持不同平臺(tái)下構(gòu)建系統(tǒng)的生成。
CMake 的出現(xiàn)已經(jīng)有接近 20 年的歷史,它的發(fā)展過(guò)程也初步經(jīng)歷了三個(gè)階段。
- ~2000 (~v2.x) ,剛剛啟動(dòng),過(guò)程式描述為主。
- 2000~2014 (v3.0~) ,引入 Target 概念。
- 2014~now (~v3.15),有了 Target 和 Property 的定義,更現(xiàn)代化。
概 述
現(xiàn)代化的 CMake 是圍繞 Target 和 Property 來(lái)定義的,并且竭力避免出現(xiàn)變量 variable 的定義。Variable 橫行是典型 CMake2.8 時(shí)期的風(fēng)格。現(xiàn)代版的 CMake 更像是在遵循 OOP 的規(guī)則,通過(guò) target 來(lái)約束 link、compile 等相關(guān)屬性的作用域。如果把一個(gè) Target 想象成一個(gè)對(duì)象(Object),會(huì)發(fā)現(xiàn)兩者的組織方式非常相似:
- 構(gòu)造函數(shù):addexecutableaddlibrary
- 成員函數(shù):gettargetproperty()settargetproperties()getproperty(TARGET)setproperty(TARGET)targetcompiledefinitions()targetcompilefeatures()targetcompileoptions()targetincludedirectories()targetlinklibraries()target_sources()
- 成員變量Target properties(太多)
在 Target 中有兩個(gè)概念非常重要:Build-Requirements 和 Usage-Requirements。這兩個(gè)概念對(duì)于理解為什么現(xiàn)代 CMake 會(huì)如此設(shè)計(jì)提供了指導(dǎo)意義。
Build-Requirements: 包含了所有構(gòu)建 Target必須的材料。如源代碼,include 路徑,預(yù)編譯命令,鏈接依賴,編譯/鏈接選項(xiàng),編譯/鏈接特性等。
Usage-Requirements:包含了所有使用 Target必須的材料。如源代碼,include 路徑,預(yù)編譯命令,鏈接依賴,編譯/鏈接選項(xiàng),編譯/鏈接特性等。這些往往是當(dāng)另一個(gè) Target 需要使用當(dāng)前 target 時(shí),必須包含的依賴。
傳統(tǒng)的 CMake 和現(xiàn)代化的 CMake 的主要區(qū)別(非語(yǔ)法層面)如下圖所示。Traditioncal CMake 在設(shè)置 build-requirements 和 usage-requirements 上都依賴手動(dòng)輸入命令,并且人工維持其作用域(變量的作用域以目錄為單位)。而 Modern CMake 在設(shè)置上述 requirement 均以 target 為單位,所以在傳遞 target 屬性到其依賴的下游鏈條中更自動(dòng)也更智能。
在 Moden CMake 中新增了不少關(guān)鍵字,其中最常見(jiàn)的是 PUBLIC、PRIVATE、INTERFACE。
- PRIVATE/INTERFACE/PUBLIC:定義了 Target 屬性的傳遞范圍。
- PRIVATE: 表示 Target 的屬性只定義在當(dāng)前 Target 中,任何依賴當(dāng)前 Target 的 Target 不共享 PRIVATE 關(guān)鍵字下定義的屬性。
- INTERFACE:表示 Target 的屬性不適用于其自身,而只適用于依賴其的 Target。
- PUBLIC:表示 Target 的屬性既是 build-requirements 也是 usage-requirements。凡是依賴。凡是依賴于當(dāng)前 Target 的 Target 都會(huì)共享本屬性。 -
解剖麻雀
我們來(lái)嘗試寫一個(gè)實(shí)例,看看在 CMake v3.13 及以后版本中的寫法如何。
HelloWorld |___ CMakeLists.txt |___ hello-exe |______ CMakeLists.txt |______ main.cpp |___ hello-lib |______ CMakeLists.txt |______ hello.hpp |______ hello.cpp以這樣一個(gè)簡(jiǎn)單的 HelloWorld 開(kāi)啟有助于我們快速進(jìn)入主題。這個(gè)項(xiàng)目結(jié)構(gòu)很簡(jiǎn)單,包含兩個(gè)子文件夾,hello-exe 生成 executable,hello-lib 生成鏈接庫(kù)(動(dòng)態(tài))。
- 我們先看下頂層 CMakeLists 的內(nèi)容:
這里沒(méi)有什么值得多討論的,與傳統(tǒng) CMake 一樣的寫法,定義 project 名稱,版本號(hào),添加子文件夾。
- 我們接著看 hello-lib。首先看源碼。
源碼比較簡(jiǎn)單,只是定義一個(gè) hello_printer 類,并在其 cpp 中定義成員函數(shù) print。請(qǐng)注意頭文件中的預(yù)編譯命令。這在 VS 中是非常常用的預(yù)編譯命令,用于導(dǎo)出動(dòng)態(tài)庫(kù)的符號(hào)。而當(dāng)該庫(kù)被其他 Target 調(diào)用時(shí),需要使用 dllimport 導(dǎo)入符號(hào)。注意這條預(yù)編譯命令剛好符合 build-requirement 和 usage-requirement 的定義。對(duì)于 hello-lib 而言,定義 DLL_EXPORT 從而將 DLLAPI 定義為declspec(dllexport)是 build-requirement,而對(duì)于該 Target 的調(diào)用者,需要的是不定義 DLLEXPORT。因而需要在定義 compile_definitions 時(shí)將 DllEXPORT 放在 PRIVATE 關(guān)鍵詞下。
當(dāng)其他 Target 使用 hello-lib 的時(shí)候,還需要知道 hello.hpp 的路徑。傳統(tǒng)的 CMake 寫法是通過(guò)在調(diào)用者的 CMakeLists.txt 中添加 includedirectory 來(lái)實(shí)現(xiàn)。但這種寫法會(huì)依賴庫(kù)之間的相對(duì)路徑,一旦調(diào)整路徑,所有的 CMakeLists 都將需要更新。在 Modern CMake 中不必如此,你只需要通過(guò) targetincludedirectories 指定 hello.hpp 的路徑,將之納入 INTERFACE(當(dāng)然 PUBLIC)也行。則調(diào)用者就可以得到該 include 路徑。
CMakeLists.txt 全文如下:
set(target_name "hello-lib")add_library(${target_name} SHARED hello.cpp hello.hpp )target_include_directories(${target_name} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})target_compile_definitions(${target_name} PRIVATE Dll_EXPORT)- 最后看下 hello-exe。hello-exe 中的 CMakeLists.txt 就可以比較簡(jiǎn)單了:
補(bǔ)充
Modern CMake 中還有些有意思的知識(shí)點(diǎn),這里沒(méi)法一一覆蓋,只能稍稍展開(kāi)。最有意思的點(diǎn)是 generator-expression。在現(xiàn)代 IDE 中,Build-type 一般都不是在 CMake config 期間能確定的。如 VS,XCode 都支持 Multi-configuration,具體使用 Debug 還是 Release 是在編譯時(shí)才確定,那如果 Target 的依賴路徑或者依賴庫(kù)需要區(qū)分 Configuration 來(lái)配置該怎么辦呢?在傳統(tǒng) CMake 中是比較難辦的,target_link_libraries 提供了一種手段,可以用 debug 和 optimized 來(lái)區(qū)分具體的庫(kù)名,而其他的編譯或鏈接設(shè)置則比較困難。在 Modern CMake 中,我們可以通過(guò) generator-expression 來(lái)實(shí)現(xiàn)。
generator-expression 定義為$<...>的形式。該表達(dá)式的值有多種形式,而且支持嵌套使用:
- 條件表達(dá)式
- $\ 當(dāng)條件為 1 時(shí),表達(dá)式為 true_string,否則為空
- $\ 當(dāng)條件為 1 時(shí),表達(dá)式為 true_string,否則為 false_string
- 變量表達(dá)式
- $ 當(dāng) target 存在為 1,否則為 0
- $\ 當(dāng) config 為 cfg 時(shí)為 1,否則為 0。這是非常高頻使用的一個(gè)表達(dá)式,可以通過(guò)它來(lái)區(qū)分 Debug/Release 等不同的 config。如下例所示,通過(guò)嵌套使用上述兩個(gè)表達(dá)式,可以達(dá)到區(qū)分 CONFIG 來(lái)設(shè)置依賴庫(kù)路徑的目的。
- ... 太多了,不一一列舉。以上是 Modern CMake 中常用的內(nèi)容,還有些如 IMPORTED,ALIAS 暫時(shí)還沒(méi)用到,等用到再更新吧。
參考
- https://crascit.com/2016/01/31/enhanced-source-file-handling-with-target_sources/#comment-414
- https://github.com/onqtam/awesome-cmake/blob/master/README.md
- https://www.youtube.com/watch?v=y7ndUhdQuU8
本文首發(fā)于 GitChat,未經(jīng)授權(quán)不得轉(zhuǎn)載,轉(zhuǎn)載需與 GitChat 聯(lián)系。
閱讀全文: http://gitbook.cn/gitchat/activity/5d4cbaf5f84543415feac3ee
您還可以下載 CSDN 旗下精品原創(chuàng)內(nèi)容社區(qū) GitChat App , GitChat 專享技術(shù)內(nèi)容哦。
總結(jié)
以上是生活随笔為你收集整理的Modern CMake 简介的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 磁带驱动器工作环境
- 下一篇: csdn博客 代码块的显示设置以及图片的