為什麼會選擇 make,cmake 之流來控制程序編譯,而不是用現成的腳本語言,如 Python?

我這幾天要做一個東西,要本地編譯github上面一個小項目,其中一個選擇make,另一個選擇了cmake來編譯。沒辦法,只好看看make,cmake官方文檔來學習下如何編譯。之前都是在windows上面用vs,這些編譯都讓vs解決了,感覺非常方便,效率很高。但是到了unix上面,我就得自己重新學習一下make,或者cmake。

但是我發現,這make,cmake也太tmd難學了,根本不是一時半會能掌握的。特別是cmake,真是太坑了,雖然自動化的尋找庫,但是內在邏輯根本搞不清,像我這種新手根本沒法輕鬆應用。這幾天,搞make,cmake都吐血了。make,cmake的語法也較奇特,與常規的語言(c/c++,python,等等)相差很遠,學習比較麻煩。最後,你學習了語法,你還要充分掌握各個指令,或者模塊的功能。這一下學習下來,實在是效率低,而且還不靠譜。

我就很鬱悶,編譯控制這種小case的事情,用python這樣常用的腳本語言來實現不是很好嗎?做一個python寫的跨平台編譯控制庫,我覺得技術上應該沒問題,而且難度也不是太大把?這種編譯控制方式不需要重新學習語法,而且思維方式與常規編程較相似,容易被人接受。

那麼,為什麼大家會開發make,cmake這樣的而不是寫一個python這樣語言編寫的庫來實現程序編譯控制呢?


奧巴馬總統,你還記得大明湖畔的scons、gyp、waf么?


make、cmake 這種是領域特定語言(DSL),使用前需要掌握特定的領域知識。具體到這裡,就是需要理解程序和庫,怎麼從源碼一步步構建出來的。中間需要配置目標、路徑、依賴關係、生成規則等等。難的並非是 make 和 cmake 語法本身,而是構建和編譯的基本知識。沒有這些基本知識,就算換了 Python 來寫,也是一樣會難的。這裡說換 Python 來寫,是指配置文件用 Python 的語法來寫。如果你不使用任何已有工具,從頭使用 Python 來控制整個編譯構建過程,工作量太大。

-----------

編譯構建本身是個複雜過程,不同的項目構建規則會有所不同。比如項目有個 txt 的列表文件,需要先調用一個 Python 腳本生成 c 數組,產生一個 list.c 文件,之後讓這個 list.c 跟項目其它文件一起編譯。或者項目有 Debug 和 Release 兩種設置,Debug 不優化,Release 使用 O3 優化。或者項目依賴一個第三方庫,需要指定庫的路徑。

總需要某種方式來告訴構建工具,各種各樣的規則。使用 make 工具,規則就寫在 makefile 中。而一旦寫好 makefile,之後的構建就只需要敲 make 命令。假如不寫 makefile,規則還是會以另一種方式存在,比如像 go 語言那樣,按照一定的命名方式來組件代碼,再運行 go build。

對於 make,最關鍵的規則是指定依賴關係。當某些文件變了,直接或者間接依賴這些文件的目標就需要重新構建,其它沒有依賴的目標不用重新構建。這樣可以大大加快構建速度。make 用於構建,並不一定非要用於編譯。比如寫一本書,就可以用 make 來管理,書稿一但變動,敲入 make 命令,就生成書的交叉索引和目錄,並同時生成 word 文檔,網頁格式、pdf 格式。另外也可以用 make 來生成網站。

make 是很基礎的工具。

現在基本不會手寫 makefile, 而是用其它更高級的工具根據平台,額外配置來生成 makefile(或者生成常用 IDE 的工程文件), 之後再調用 make。cmake 就是這樣的工具,類似的工具還有 Qt 用的 qmake,Lua 寫的 premake。

實際上開源庫,這些生成規則已經寫好了。構建通常只需要三步

./configure
make
make install

configure 根據平台和配置自動生成 makefile,之後用 make 來構建項目,再使用 make install 來安裝到相應目錄。並不需要過多了解 make 和 cmake 的語法。當編寫自己的庫,才需要了解 make 或者 cmake 的具體語法。

---------

現在編寫程序經常依靠 IDE,IDE 將整個構建的過程封裝起來。構建過程對於很多人來說是個黑箱子,只知道將文件拖進工程,按個 Build 按鈕,程序就出來了。有些人甚至連 Debug 和 Release 有什麼區別也不知道。缺乏基本的編譯和構建概念,之前只接觸過 IDE,初次接觸 make、cmake 這些工具, 多少會有不適應的。

那為什麼有了 IDE 這樣「先進」的東西,還需要 make 和 cmake 這些看似「原始」的工具呢?

1. 某些語言根本就沒有 IDE。make 工具是很通用的,任何語言都可以使用,甚至並非是編程也可以使用。上文就提到可以用 make 工具來管理書稿,或者編寫網站。

2. 為了跨平台。不同的平台有不同的 IDE,比如同一個 C++ 工程,在 Windows 常用的 IDE 是 Visual Studio, Mac 平台的 IDE 是 Xcode。假如使用 IDE,就需要分別為每個 IDE 重複配置。使用 cmake 工具,只需要寫好配置一次,開發的時候根據開發人員的習慣來選擇開發方式。有些人在 Linux 平台選擇 Vim 來開發;有些人習慣用 IDE,就生成 IDE 項目文件。Mac 平台開發人員就可以用 Xcode,Windows 平台開發人員可以使用 Visual Studio。發布時候,不用發布 IDE 的項目文件。每個平台都用相應的 cmake 來構建。

3. 需要很多控制。讓同一份代碼,根據不同的情況、不同的平台、編譯出不同的版本。比如可以選擇編譯成靜態庫和動態庫,是否需要 WebKit 功能,是否需要界面,是否需要支持特定的硬體等等。這些控制,使用 IDE 也很難做得。


用clion直接修改cmake吧,make反人類。這東西跟VS在編輯標準C++上的區別,就是你要多插幾根內存條而已。


有啊,SCons,Waf,只不過沒幾個人用而已。


寫寫還是挺費勁的,要支持好才有人用,參考premake4, waf的現狀。

一直在尋找適合c++的build automation 工具,試過vs, makefile+pkgconfig, qmake,premake4, cmake, scons, waf,幾年前定下來用cmake(應該是4.8.12之後)。作為一個專註c++工程的DSL,cmake具有以下優勢:

1 跨平台支持,可生成vs, makefile, xcode, eclipse等工程,且有諸多ide支持

2 支持依賴管理,包括include dirs, lib path, compile flags等c++工程特有屬性

3 通過custom toolchain file可以支持很多編譯工具鏈,方便交叉編譯

4 較成熟,相對bug少

5 手寫findxxx.cmake,容易擴展

6 帶部分distribution輔助功能(INSTALL PACK)

7 test支持

。。。

缺點是

1 語法古老,寫起來比較啰嗦

總之,c++工程就用它吧,不會錯


「編譯控制這種小case」?,你能花一天時間用python寫個kbuild出來?

在我看來,計算機(或者任何工科)最重要的是抽象:抽象讓你在更高層次專註問題的解決方案,這樣效率更高。

很多人都說了,make就是一種DSL,目的是寫更少的代碼,從而提高效率。

即使你用python寫make,也就是用python做開發,你是不是也得考慮做一下抽象,從而方便代碼的維護?如果你沒有一個合理和可擴展的抽象層,在多個層次來回切換,反而是沒有效率的。如果你做了一個好的抽象,你會發現自己又造了一個輪子。

用時間檢驗過的別人造的輪子,有什麼不好?

另外,pythoner經常說「人生苦短……」,未必明白寫代碼的更高境界是不寫代碼。


現在通常不直接手寫 makefile,而是使用工具自動生成。

生成工具很多,如 premake 是用 Lua 的,但 CMake 是最流行的。這些工具通常也可生成 VS、Xcode 等配置文件,方便跨平台開發。


makefile和CMake不是一層級上的概念,手寫makefile最大的問題在於編譯依賴上……而用CMake生成makefile就可以很好地解決這個問題。

CMake我大概花了一天的時間讀了文檔和一些例子,然後就用在我自己的項目上了。

不知道怎麼寫的就在stackoverflow上找,一般自己能遇到的坑別人都遇過了。

CMake最大好處是跨平台,而且能針對IDE生成解決方案文件,這一點非常友好。很多開源程序都是VS8/9/10/12/13/15的解決方案帶了一堆,太麻煩了。用CMake直接一個CMakeLists.txt就搞定了。

autoconf對windows平台不是很友好,你需要安裝一個cygwin的環境才能用……然後你去下載了msys,然後用小水管拖了一大堆包下來,累到死。


Waf Build Tool

是基於python的開源編譯系統。

autotools之後的當代,主流是cmake,還是好好學吧。我用cmake+ninja。


利益相關:cmake 重度用戶,曾經在 openhub 因為自己的項目刷上了 cmake 使用排行榜……

0. 序

scons 哭暈在街頭。

1. Makefile

首先關於 Makefile ……你要去看歷史,那會哪有什麼 Python。

編譯系統的核心就是控制構建目標和構建目標的依賴關係。

大家一直在用 Makefile 當然就是因為 Makefile 是事實上的標準,很多人喜歡 autoconf / automake 而不是 cmake 的一個原因就是你在隨便某個系統只要有 shell 和 make 就可以用。而用 cmake 生成了之後其實仍舊需要 cmake 的 binary 才可以用。而 automake 生成之後則不再依賴 automake 了(除非修改 http://Makefile.am),但是這樣對於發布源碼包就有不需要額外安裝 cmake 的優勢。

2. CMake

CMake 畢竟是 "C" Make,顯然比 python 快啊。而且對於 C/C++ 的支持是十分好的,[1] 中也提到對於常見任務簡單。add_binary(test http://test.cc) 就能編譯了,而且直觀。automake 畢竟也遺留了 makefile 的缺點(生成文件只使用 make),對於並行化編譯和跨目錄 target 依賴的處理都不如 cmake。

同樣是生成 makefile,make -j5 (5個任務並行),對於有菱形依賴關係的 target,不用一個正確的姿勢寫 Makefile,在執行的時候就會有問題,而 cmake 幫助解決了這個問題。

當然由於需要用 shell,在 windows 上 automake 就不如標榜跨平台的 cmake,cmake 支持生成許多 win 下 vs 系的工具。

3. 構建工具的生態

生態上說,以前 automake 是在一定程度上優於 cmake 的,但是還是要看誰站在後面。cmake 背後是有一個公司的,而且 KDE 後來選擇使用 cmake,對於 cmake 的貢獻是很大的。cmake 3 之後它的文件的範式其實也有所變化。automake 以前憑藉著 pkg-config 的良好支持更勝一籌。但是現在越來越多的庫意識到的一個問題就是,使用它並不是代表只是 link 它就完事了,而是經常需要調用一些這個庫配套的 binary 來執行一些任務。這裡就需要提供除了僅僅是找庫之外的一些 macro/script 來供開發者使用。例如 KDE / Qt 就需要使用 moc,qdbus2cppxml 之類的 binary。

CMake 社區提供 find_package 使用的 file 的趨勢也是使用新的範式,不是直接手寫 include_directories , library_directories target_link_libraries 的路徑,而是通過包裝好的 target 把外部庫也抽象為一個 target 並且把路徑依附於這個 target 上。對於大項目來說,有些項目需要使用 third_party 靜態鏈接或者動態鏈接,這樣管理起來的話,靜態鏈接動態鏈接都可以統一管理。

相比 python,C/C++ 社區是更青睞於 cmake 的,其他許多有跨平台需求的重量級項目 boost,llvm 都是使用 cmake 進行構建。畢竟 python 並不原生提供構建系統,你在構建前要先裝 python,再裝一個 scons 什麼的。而對於 autotools, cmake 來說你只需要 cmake,還是節省了工作量的。

4. 速度

速度對於小項目來說可能不重要,但是對於大項目來說真的非常重要。對於 automake 的一個批評就是它太慢,configure 一圈下來因為歷史因素檢查了許許多多奇怪的地方。m4 宏也慢。這是許多項目從 automake 轉向 cmake 的一個理由。而對於很多腳本語言來說,速度實在是比不上。

5. 你自己也要能打啊!

我只接觸過 scons,所以就說一下 scons。[2] 從 sunpinyin 裡面找的一小段。顯而易見的問題是為啥要先搞個 environment?沒有 pkg-config 內置支持。結論就是有些奇怪的設計問題,而且沒有為了常用功能實現簡化命令。

所以說到底還是生態問題。來了一個新項目,沒有公司支持,那就得有大批用戶來支持。如果你都沒有? ╮( ̄▽ ̄)╭

cmake 早期明顯生態不足,所以它自己就 bundle 了很多常用的 find_package 腳本,畢竟還是提供了方便。以前 Qt 的 cmake 腳本都是 cmake 自己 bundle,現在 Qt 5 就反過來由 Qt 來提供了。

特別的,對於 C/C++ 項目,提供 pkg-config 雖然已經是定番,但是也有越來越多的項目額外提供 cmake 腳本,你要是提供個基於與你項目無關的獨立語言,不是憑空為這個項目增加依賴么。

作為要取代 automake 的 cmake,現在也早就不局限於 make 而同時也支持性能更好的 ninja 了。

[1] scons / SCons / wiki / SconsVsOtherBuildTools

[2] sunpinyin/SConstruct at master · sunpinyin/sunpinyin · GitHub


我覺得你對複雜和簡單的理解出了偏差……

make內部是做了大量的邏輯和功能才實現了完整的編譯工作的,你要拿Python寫個一樣的,我覺得沒有個一萬行是做不成這個事情的

話說別人的項目,不應該是直接./configure, make, make install就搞定了嗎,為什麼你需要去動Makefile


試試我廠大神開源的blade?語法類似json,寫好目標名稱,寫好依賴庫,不用管頭文件。2個小時上手,從此告別makefile。github上搜就有

補充:就是用python寫的,好像是基於scons


CMake可以針對不同的平台生成特定的工程文件。而且其語法相對來說也會簡單一些,大概幾十個指令,以及一些概念和關鍵字。作用就是可以方便的指出項目之間的依賴關係,從而自動的調用編譯器來對不同的代碼部分進行編譯,最後進行鏈接。

CMake相對來說簡單一些,用起來簡潔一些,CLion這樣的編譯器可以自動生成,但是很多時候還是需要手動更改或者手動編寫。其實不會很難,CMake的官方網站上有專門的教程,列舉了一些常用的指令。

使用make、CMake之前要知道代碼之間如何編譯、生成的中間文件是什麼以及如何鏈接。CMake用一些特定的指令來描述整個過程,如果有一些編程經驗以及懂得代碼編譯原理之後,掌握起來不會特別困難,但是仍然具有一定的學習成本。至於用腳本語言來實現,就算是用腳本來實現,對於一個不懂得編譯過程的人來說,依然沒辦法寫出相應的腳本,而對於懂得編譯過程的人來說,使用CMake這種專用領域的語言更加方便也更加直觀,所以沒必要使用腳本。


Make originally created by Stuart Feldman in April 1976 at Bell Labs. Feldman received the 2003 ACM Software System Award for the authoring of this widespread tool. -- Make (software) - Wikipedia

Python: Guido van Rossum, 1989. 1991 first public release.


首先 make 和 cmake 是兩個層級,make 主要解決增量編譯的問題,cmake 主要則是分析依賴跨平台對接 IDE,你要是用 Python 實現這兩個玩意沒一萬行搞不定的……

所以我也反對 nodejs 圈子裡面 grunt 和 gulp 這種東西。


CMake是為了跨平台。

make是Unix的首選構建系統,那會還沒有vs啥事。

Makefile都難學,那我無話可說。至少比自己寫個容易


分頁阅读: 1 2 3