如何評價 CMake?
首先必須肯定CMake。它簡化了cross platform, OS, compiler的開發成本,同時支持多個IDE(VS, QtCreator, Eclipse, CLion...etc.),並且有一個成熟開源社區。 當你掙扎於將based on autotools的項目遷移至OSX或者Windows的時候,你就會感謝CMake的美好。 網路上有各種資源可供你使用學習,運氣好可以直接照用別人的CMakeLists.txt。
支持多種Generator。make, Eclipse, Ninja, MSVC... 個人強推Ninja。 至少我司所有CMake Developer都偏愛它。 突出一個字 - 快。 極大提升了開發效率。
下面來說說問題。
贊同@Xi Yang和@pilot的說法。 首先CMake的learning curve真的不是一般的高。 build system本來就難,configuration + generation + compile time 任何一個環節都可能出錯。上手需要踩過無數的坑,你才能初步入門。
第二,CMake沒有好的paradigm, 就連我司的官方教程都是上古舊書 - CMake 3.1。 上周同事剛發布CMake 3.11.3,距離3.1已經過去4年多了。雖然官方的documentation很詳細,但特么都是描述性的。也就是說CMake告訴你他能做什麼,我有多屌,但是他不告訴你該怎麼做,以及為什麼要這麼做。從而開發過程就變成了一個試錯過程。 加這個link path解決問題了,慢著不對,為什麼windows上又炸了;我該link那個python environment,那我hard code一個path吧。 若干個月後,你可能會恨自己做了這件事。。。 CMake當然有正確的paradigm,但往往它是"口口相傳",局限於某幾個圈子裡。 往往做完了幾個大feature要求同事作code review的時候,我才能系統的從他們一大堆comments和change requests下學習正確的方式。
第三,CMake的本身屬性一定程度上限制了它的發展。 開源意味著有更大的用戶群體和一種讓世界更美好的願景,但是也隨之帶來了funding和customer service的問題。CMake的發展進步有賴於社區的維護,但是隨著開發人員的新老交替,無可避免的會慢慢產生冗餘代碼(參見FindBoost.cmake)。 我有問過同事們為什麼我們不每隔1,2年更新Mastering CMake這本官方教程,同事的回答讓我覺得很有道理:「我們也想啊! 但是問題是funding從哪裡來? 雖然我們負責維護,開發和發布CMake新版本,但這不是我們的主業。 編寫教程需要大量的時間,除非客戶願意資助我們,我們也愛莫能助。我們也要養家糊口啊。。。」
CMake的推動有賴於他的用戶群體。 i.e. 客戶A要我們幫他們開發一個新軟體。 開發的過程中,我們需要拓展CMake去支持一系列新功能。我們說服客戶這些新功能是必要的,並希望他們能支持把這些代碼回饋給CMake master, 於是CMake得到了拓展。如果沒有這些客戶,以及其他community CMake的開發者們,也不會有CMake的今天。 使用過程自然而然會遇到問題,但是人們往往只能求助stackOverFlow 或者CMake mailing list. 由於沒有人會贊助CMake的customer service所以一切都是out of courtesy。幫你是好心,沒人有義務幫你。遇上一些暴脾氣的在mailing list里罵人的,我們一般都當午休時候的笑料- -
最後,針對提到的一些問題,我要為CMake辯護下:
Modern CMake在target based的開發理念下,其實已經有了相當強的模塊化能力。`include` keyword已經淡出了歷史舞台,成為legacy。以下兩篇blog我覺得針對這個理念寫的不錯:
It』s Time To Do CMake Right
https://gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1
自動解決依賴問題早就不是什麼問題了: Package developers應該生成package files給upstream users使用。如果你創建了一個library foo, 理論上別人應當只需要find_package(foo)便足以。
cmake-packages(7) - CMake 3.12.20180606-g074bb Documentation
Windows上微軟出了一個叫vcpkg的管理工具,據說挺好用的,可以了解下。
"它雖然是過程式語言,但函數TMD沒有返回值!"好問題。。。我午休去問問同事
「作為一個string based language, 很少有利用CMake來處理functional programming的cases. 加上CMake誕生之初要兼容fortran, 所以這一功能也不了了之。 雖然community有幾個人提過這個feature request,但是沒有一個令人信服的使用場景 」by Brad King。就這樣。
首先,C、C++的項目管理,本質上就是屎樣化的,因為它們充滿了平台特異特性、歷史包袱、宏帶來的代碼變化。於是,IDE在光看到源代碼而看不到對應配置信息的情況下,是不能保證正確解析代碼語義的。編譯控制軟體,或者說項目管理軟體,主要的任務就是把這一大坨屎澆築成一塊塊的屎磚,以便你自己把它壘得齊一點,觀感上稍微美觀一點。
牆裂贊同 @pilot 的說法。CMake這個玩意,原本是一個GNU autotools類似的替代品,用來解決跨平台的環境差異,然而它自己也沒有避免一些一直存在在autotools里的問題。經過一段時間的重度使用,我發現它有幾個問題是根本性的:
1:它是基於過程式編程語言生成項目,而不是描述項目。這產生了一系列的惡果。
- CMakeLists基本不可能像各種XML的項目文件那樣,被IDE作為配置文件。於是你永遠需要手工編寫CMakeLists的內容,而不能使用圖形界面去配置一個個的目標。這對於開發人員的要求其實是很高的。
- 從CMakeLists裡面,你很難看到一個目標最終的編譯選項、包含路徑、預定義宏都有那些。如果你想要看到這些,除非你對CMakeLists「求解」,把項目生成出來,在生成的結果里才能看到。而對於大型項目,generate的時間可能很長。於是你就難以交互地編輯一個項目,經常需要耗時的修改CMakeLists、生成、編譯、查錯的循環。
2:它雖然是過程式語言,但函數TMD沒有返回值!
函數里返回一個值的方式,是把期望接受返回值的變數名傳進去,然後在函數里設置外層作用域的那個變數的值。
function(my_func ret_var_name)
set(${ret_var_name} "return value" PARENT_SCOPE)
endfunction()
於是,你不能像正常的語言那樣寫:
foo(bar(baz(input)))
而必須寫:
foo(foo_ret)
bar(foo_ret)
baz(bar_ret)
3:它通過project關鍵字和變數的目錄作用域,有一定的模塊化能力。然而這種模塊化能力(不管是include還是add_subdirectory)本質上都是基於include的,而不是基於更現代編程語言的真模塊化。這會為管理大型項目會帶來非常多的麻煩。
4:CMake分為配置時和生成時兩個階段。有些信息在生成時才能確定,但你又需要在配置時進行特異操作,那麼就需要使用generator expression。這本身其實已經是一坨屎了,因為generator expression自己有一坨語法,相當於你要學另一個語言。然而更屎的是,有時你感覺需要某種generator expression,但CMake不支持在那個位置使用它。。。
雖然有一堆問題,然而對於跨平台的大型C、C++項目,CMake現在依然是功能最強大的構建系統。幾個類似的東西里,
- qmake需要你完全使用Qt Creator;
- premake的子目錄、模塊功能是實驗特性,本質上只能寫一些小玩具;
- SCons自己就是編譯控制器,同時有CMake和Makefile的功能。這樣的優點是系統比較簡潔,缺點是IDE基本沒法支持;
- 至於GNU Autotools,反正我不想寫M4 macro。而且它的跨平台,指的是windows之外的各種Unix。。。
屠龍的英雄,自己也已經成為了龍1. CMake使得我們不用去熟悉不直觀的Makefile語法,畢竟Makefile改點東西不難,加功能很難。而CMake常用的功能都不難。
2. CMake支持多種編譯器,切換編譯器不再是難事。畢竟每個編譯器的CFLAGS CXXFLAGS等的表示都不盡相同。3. CMake可以生成IDE工程,像eclipse、Visual Studio,這樣你就可以在IDE里開發了,代碼提示更智能,開發更高效。4. 隨著Android官方支持CMake,Android mk這種基於Makefile的編譯系統也逐漸在退出。綜上,如果你是C或C++開發,想要開發跨平台的程序,或者想現有程序順手跨平台,想順手生成IDE工程方便開發,想一勞永逸的做一個編譯系統,你應該選擇CMake。除了CMake的功能以外,還要有反向CMake,這樣就天下無敵了。
推薦閱讀:
※關於bool類型的c++條件判斷問題?
※該如何設計實現一個telnet bbs?
※長期用 C++ 和 MATLAB 做機器學習的你,有沒有遇到一個讓你相見恨晚的語言或包?
※PS4 的圖形 API——GNM 和 GNMX 究竟是怎樣的?和 OpenGL/Direct3D 11 對比呢?
※編譯器能否對如下場景優化,以及如何檢查不同編譯器對此是否做了優化?