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