卷1:第5章 CMake

2018-02-24 15:55 更新

原文鏈接:http://www.aosabook.org/en/cmake.html

作者:Bill Hoffman, Kenneth Martin

1999年, 國家醫(yī)學(xué)圖書館(National Libray of Medicine)雇用了一個(gè)稱為Kitware的小公司,為支持復(fù)雜軟件的跨平臺配置,構(gòu)建和發(fā)布來實(shí)現(xiàn)一個(gè)更好的解決方案。這個(gè)工作是ITK(一個(gè)醫(yī)學(xué)開源可視化軟件,Insight Segmentation and Registration Toolkit)項(xiàng)目的一部分。作為ITK工程的主導(dǎo)單位,Kitware負(fù)責(zé)開發(fā)一個(gè)供ITK項(xiàng)目研發(fā)人員使用的軟件構(gòu)建系統(tǒng),這個(gè)系統(tǒng)必須易于使用,并盡量不占用開發(fā)人員編程之外的時(shí)間?;谶@個(gè)指導(dǎo)思想,CMake需要基于已有工具揚(yáng)長避短,并能夠取代古老的autoconf/libtool方式。

經(jīng)過多年的發(fā)展,CMake從最初的軟件構(gòu)建系統(tǒng)演變成一個(gè)開發(fā)工具系列:CMake, CTest, CPack和CDash。CMake負(fù)責(zé)構(gòu)建軟件。CTest是測試驅(qū)動工具,用于回歸測試(regression tests)。CPack是打包工具,將CMake構(gòu)建的軟件發(fā)布成面向不同平臺的安裝軟件。CDash是一個(gè)Web應(yīng)用程序,用于執(zhí)行持續(xù)的集成測試并顯示測試結(jié)果。

5.1 CMake的歷史和需求

在開始開發(fā)CMake時(shí),項(xiàng)目管理常見的做法是,在Unix平臺上使用configure腳本和Makefile文件,在Windows平臺上使用Visual Studio工程。這種構(gòu)建系統(tǒng)的雙重性使得跨平臺開發(fā)變得十分枯燥,即使在工程中添加一個(gè)簡單的源碼文件都是非常痛苦的事情。開發(fā)者們希望能夠擁有一個(gè)統(tǒng)一的軟件構(gòu)建系統(tǒng),而CMake開發(fā)人員在這方面經(jīng)驗(yàn)豐富。歷史上,他們使用過兩種方法來解決這個(gè)問題:

一種方法是1999年開發(fā)的VTK構(gòu)建系統(tǒng)。在這個(gè)系統(tǒng)中,Unix系統(tǒng)中使用configure腳本,而在Window系統(tǒng)中使用一個(gè)叫做pcmaker的可執(zhí)行程序。pcmaker是一個(gè)C程序,它通過解析Unix Makefile文件來生成Windows下的NMake文件。?pcmaker的二進(jìn)制可執(zhí)行程序后來被簽入到了VTK 的CVS系統(tǒng)倉庫中。雖然從某種意義上講,這是一個(gè)統(tǒng)一的構(gòu)建系統(tǒng),但是其缺點(diǎn)是顯然的。 對于一些常見的情況,比如添加一個(gè)新的模塊,都需要修改pcmaker的源碼,然后再更新其系統(tǒng)倉庫中的可執(zhí)行文件。

另外一種方式是為TargetJr開發(fā)的基于gmake的構(gòu)建系統(tǒng)。TargetJr是一個(gè)C++編寫的計(jì)算機(jī)可視化環(huán)境,最初在Sun工作站上開發(fā)。一開始,TargetJr使用imake構(gòu)建系統(tǒng)來創(chuàng)建Makefiles。但當(dāng)有一天需要移植到Window時(shí),就不得不開發(fā)出另外一個(gè)gmake構(gòu)建系統(tǒng)。gmake構(gòu)建系統(tǒng)同時(shí)支持Unix編譯器和Windows編譯器,但在使用前需要設(shè)置一些環(huán)境變量,否則,用戶特別是終端用戶容易產(chǎn)生一些難于調(diào)試的錯(cuò)誤。

這兩種方法都有一個(gè)嚴(yán)重的不足: 它們要求Windows開發(fā)人員使用命令行。然而,熟練的Windows開發(fā)人員更傾向于使用集成開發(fā)環(huán)境(IDE),他們還是會選擇手動生成IDE文件然后添加到工程中去,使得構(gòu)建系統(tǒng)又重新退化成了"雙系統(tǒng)"。除了缺乏IDE支持,上述兩種方法也使得合并第三方軟件的項(xiàng)目變得非常困難。比如,VTK中罕有圖片加載模塊,主要是因?yàn)槠錁?gòu)建系統(tǒng)難于利用libtiff和libjpeg等第三方庫。

因此,開發(fā)ITK和其它C++軟件都需要一個(gè)新的軟件構(gòu)建系統(tǒng)。 這個(gè)新構(gòu)建系統(tǒng)必須滿足一些限制條件:

  • 對平臺的唯一的依賴: 操作系統(tǒng)中需要安裝C++編譯器
  • 能夠生成Visual Studio IDE輸入文件
  • 易于創(chuàng)建基本的構(gòu)建系統(tǒng)的目標(biāo)文件,包括靜態(tài)庫,動態(tài)庫,可執(zhí)行文件,插件。
  • 能夠運(yùn)行構(gòu)建時(shí)的代碼生成器
  • 支持源碼樹和構(gòu)建樹的分離
  • 能夠執(zhí)行系統(tǒng)"自省"(introspection),即能夠自動判斷目標(biāo)系統(tǒng)能夠做什么,和不能夠做什么
  • 能夠自動掃描C/C++頭文件的依賴關(guān)系
  • 所有特性對所支持的平臺一視同仁

為了避免依賴于三方軟件庫和語法分析器,CMake在設(shè)計(jì)時(shí)只考慮了一個(gè)主要的依賴:C++編譯器(因?yàn)橐獦?gòu)建的是C++代碼,所以我們可以放心地假設(shè)系統(tǒng)中已經(jīng)安裝好C++編譯器)。當(dāng)時(shí),在許多流行的UNIX和Windows操作系統(tǒng)上構(gòu)建和安裝Tcl之類的腳本語言是非常困難的。即便到如今,給超級計(jì)算機(jī)和沒聯(lián)網(wǎng)的安全計(jì)算機(jī)安裝軟件也非易事,所以編譯第三方軟件庫一直都是比較困難的。由于軟件構(gòu)建系統(tǒng)是一個(gè)基本工具,因此其設(shè)計(jì)不應(yīng)再引入其它的依賴關(guān)系。這確實(shí)限制了CMake提供自己的簡單的語言,導(dǎo)致至今都有人不太喜歡CMake。然而,如果CMake依賴于當(dāng)時(shí)最流行的嵌入式Tcl語言,它大概不會達(dá)到今天這樣的流行程度。

生成IDE工程的能力是CMake的重要賣點(diǎn),但這也限制了CMake不能提供本地IDE支持之外的特性。不過,支持本地IDE工程的重要性完全能夠彌補(bǔ)其不足。這個(gè)設(shè)計(jì)使得CMake的開發(fā)變得困難,卻令使用CMake的項(xiàng)目(如ITK)的開發(fā)更為容易,因?yàn)殚_發(fā)人員更喜歡使用自己熟悉的并且效率也更高的工具。允許開發(fā)人員選擇自己喜歡的工具, 項(xiàng)目就能充分利用最寶貴的人力資源。

所有的C/C++程序都至少包含以下的一個(gè)或多個(gè)基本的基本構(gòu)建單元: 可執(zhí)行文件,靜態(tài)鏈接庫,動態(tài)鏈接庫和插件。 CMake必須具備在所有支持的平臺上生成這些結(jié)果的能力。 雖然所有的平臺上都支持生成這些結(jié)果, 但不同的平臺和不同的編譯器會導(dǎo)致編譯器選項(xiàng)變化很大。 CMake將這些目標(biāo)的構(gòu)建過程抽象成一條條簡單的命令, 它們在實(shí)現(xiàn)上的復(fù)雜性和差異性則被隱藏了起來, 從而開發(fā)人員能夠同時(shí)在Windows, Unix和Mac上創(chuàng)建這些目標(biāo)的本地版本。 這樣,開發(fā)人員得以專心于工程本身,而不是糾結(jié)于如何編譯一個(gè)動態(tài)鏈接庫這樣的細(xì)節(jié)上。

代碼生成器為構(gòu)建系統(tǒng)增加了額外的復(fù)雜性。最開始,VTK提供了一個(gè)系統(tǒng)來解析C++頭文件,然后自動地將C++代碼封裝成Tcl,Python和Java代碼,并自動地生成一個(gè)封裝層。這要求構(gòu)建系統(tǒng)先生成一個(gè)C/C++程序(封裝生成器),然后在編譯時(shí)運(yùn)行此程序,以生成更多的C/C++源碼(特定模塊的封裝代碼)。隨后,生成的源碼將被編譯成可執(zhí)行文件或動態(tài)鏈接庫。所有這些過程必須在IDE環(huán)境和生成的Makefile中實(shí)現(xiàn)。

當(dāng)開發(fā)靈活的跨平臺C/C++軟件時(shí),很重要的一點(diǎn)是面向功能編程, 而不是面向特定的平臺。autotool工具支持系統(tǒng)"自省"(introspection),即通過編譯少量的代碼來查詢平臺特征并存儲查詢結(jié)果。由于跨平臺的需要,CMake也必須采用類似的策略,使得開發(fā)人員只需要針對標(biāo)準(zhǔn)平臺編碼,而不需要考慮特定的平臺。由于編譯器和操作系統(tǒng)時(shí)時(shí)在變,這個(gè)策略對于代碼的可移植性非常重要。比如,下面的代碼:

#ifdef linux
// do some linux stuff
#endif

就顯得非常脆弱,不如寫成

#ifdef HAS_FEATURE
// do something with a feature
#endif

另外一個(gè)CMake早期的需求也來自于autotool: 在源碼樹外生成構(gòu)建樹。這個(gè)特性使得從同一個(gè)源碼樹可得到多個(gè)不同的構(gòu)建,使得不同構(gòu)建之間的文件不會沖突,結(jié)合版本控制系統(tǒng)的時(shí)候顯得尤為有利。

構(gòu)建系統(tǒng)一個(gè)最更要的功能是依賴關(guān)系的管理能力。 如果一個(gè)源碼文件發(fā)生變化,所有使用了這個(gè)文件的生成結(jié)果都必須重新構(gòu)建。 對于C/C++代碼,被.c.cpp文件包含的頭文件也需要檢查依賴關(guān)系。如果依賴關(guān)系理解錯(cuò)誤,只有部分修改的代碼有可能導(dǎo)致全部重新編譯,從而浪費(fèi)大量時(shí)間。

這個(gè)新的構(gòu)建系統(tǒng)的所有的需求和功能都必須對所有支持的平臺一視同仁。CMake需要為開發(fā)者提供一個(gè)簡單的API,不需要關(guān)心平臺細(xì)節(jié)就可以創(chuàng)建復(fù)雜的軟件系統(tǒng)。事實(shí)上,使用CMake的軟件只不過是把構(gòu)建復(fù)雜性轉(zhuǎn)移給了CMake開發(fā)人員。一旦這些基本的需求確定下來,就需要用敏捷的方式來實(shí)現(xiàn)CMake。ITK項(xiàng)目從第一天開始就需要這樣一個(gè)構(gòu)建系統(tǒng),但其第一個(gè)版本的CMake并沒有滿足所有的需求,但已足夠支持在Windows和Unix上構(gòu)建軟件。

5.2 CMake是怎樣實(shí)現(xiàn)的

如前所述,CMake的開發(fā)語言是C和C++。為解釋其內(nèi)部結(jié)構(gòu),本節(jié)將首先從用戶的角度介紹CMake的處理過程,然后再描述其結(jié)構(gòu)。

5.2.1 CMake處理過程

CMake有兩個(gè)主要的階段。首先是"配置(configure)",在此階段CMake處理所有的輸入然后創(chuàng)建軟件構(gòu)建過程的內(nèi)部表達(dá)。第二個(gè)階段是"生成(generate)",負(fù)責(zé)創(chuàng)建出實(shí)際的構(gòu)建文件。

環(huán)境變量與緩存

對1999年甚至是今天的許多構(gòu)建系統(tǒng)來說,生成工程時(shí)都要用到底層(shell級別)的環(huán)境變量。典型的情況是,用PROJECT_ROOT環(huán)境變量來指向源碼樹的根目錄。環(huán)境變量還被用于指定可選軟件包和外部軟件包。但是使用環(huán)境變量的方法也有弊端,它需要每次構(gòu)建時(shí)都重新設(shè)置環(huán)境變量。為解決這個(gè)問題,CMake使用緩存文件來存儲生成過程中用到的所有變量。這些變量不再是環(huán)境變量,而是CMake變量。CMake針對某個(gè)特定構(gòu)建樹第一次運(yùn)行時(shí),會創(chuàng)建一個(gè)CMakeCache.txt文件,存儲當(dāng)前構(gòu)建過程中需要用到的CMake變量。這個(gè)緩存文件屬于構(gòu)建樹的一部分,所以在之后的每次針對該構(gòu)建樹的重新配置時(shí), 這些變量都是可重用的。

配置階段

在配置階段,CMake首先嘗試讀取CMakeCache.txt文件,該文件在第一次運(yùn)行時(shí)生成。然后,讀取源碼樹根目錄下的CMakeLists.txt文件,并使用CMake詞法分析器處理。CMakeLists.txt中的每條命令都由一個(gè)命令模式對象執(zhí)行。通過includeadd_subdirectory命令,更多的CMakeLists.txt得到執(zhí)行。對于每條命令,CMake都有一個(gè)C++對象來處理,比如add_library,if,?add_executable,?add_subdirectory,include等。實(shí)際上,整個(gè)CMake語言就是以命令調(diào)用的方式實(shí)現(xiàn)的。詞法分析器只不過將輸入文件內(nèi)容轉(zhuǎn)化為命令和命令參數(shù)而已。

配置階段主要是運(yùn)行用戶定義的CMake代碼。等到執(zhí)行完之后,以及所有緩存變量計(jì)算完成之后,CMake在內(nèi)存中得到一個(gè)項(xiàng)目構(gòu)建的內(nèi)部表達(dá)。這個(gè)內(nèi)存中的內(nèi)部表達(dá)包括了所有的庫文件,可執(zhí)行文件,定制的命令,以及生成指定generator(指特定的編譯環(huán)境)所需的其他必要信息。這時(shí),CMakeCache.txt會被存儲到磁盤上,供以后重新運(yùn)行CMake時(shí)使用。

項(xiàng)目在內(nèi)存中的表達(dá)實(shí)際上是一些待生成的目標(biāo)的集合,包括基本的庫文件和可執(zhí)行文件。CMake還支持目標(biāo)的定制,即用戶可以定義輸入和輸出,并提供定制的可在構(gòu)建過程中運(yùn)行的可執(zhí)行文件或腳本。CMake將每個(gè)目標(biāo)存儲在一個(gè)cmTarget對象中,然后多個(gè)cmTarget存儲在一個(gè)cmMakefile對象中,cmMakefile對象實(shí)際上用來存儲源碼樹中某個(gè)目錄中的所有目標(biāo)。最后得到的結(jié)果是一棵cmMakefile對象的樹,樹結(jié)點(diǎn)中存儲cmTarget對象的映射。

生成階段

一旦配置(configure)階段完成,生成(generator)階段就可以開始了。生成階段將生成用戶指定類型(如Visual Studio或GNU/Linux GCC)的構(gòu)建文件。這時(shí),目標(biāo)的內(nèi)部表達(dá)(庫,可執(zhí)行文件,定制目標(biāo))轉(zhuǎn)化為本地構(gòu)建工具的輸入文件,如Visual Studio或Makefile文件。CMake由配置階段獲得的內(nèi)部表達(dá)要盡可能地抽象和通用,這樣的數(shù)據(jù)結(jié)構(gòu)才能被不同的本地構(gòu)建工具所共享。

CMake處理過程簡圖如圖5.1所示。

每個(gè)CMakeLists.txt的解析結(jié)果都存儲在一個(gè)cmMakefile對象中。 除了存儲一個(gè)目錄的信息,cmMakefile對象還控制對?CMakeLists.txt的解析. CMake語言的解析函數(shù)使用了基于lex/yacc的分析器。 由于CMake語言的語法很少發(fā)生變化,而lex和yacc在本地系統(tǒng)上并不能保證已經(jīng)安裝,因此lex和yacc的輸出文件被處理和保存到了Source目錄中,和其它手工編寫的文件一起加入到版本控制系統(tǒng)中。

CMake另一個(gè)重要的類是cmCommand。這是CMake語言中所有命令的實(shí)現(xiàn)類的基類。每個(gè)子類不僅提供命令的實(shí)現(xiàn), 還包括其文檔。 比如, 下面cmUnsetCommand類的方法的作用是提供文檔:

virtual const char* GetTerseDocumentation()
{
    return "Unset a variable, cache variable, or environment variable.";
}

/**
  * More documentation
  */
virtual const char* GetFullDocumentation() 
{
    return
        " unset(<variable> [CACHE])\n"
        "Removes the specified variable causing it to become undefined."
        "If CACHE is present then the variable is removed from the cache"
        "instaead of the current scope. \n"
        "<variable> can be an environment variable such as:\n"
        "  unset(ENV{LD_LIBRARY_PATH})\n"
        "in which case the variable will be removed from the current "
        "environment.";
}

依賴分析

CMake內(nèi)置有強(qiáng)大的的依賴分析能力, 支持單個(gè)Fortran, C和C++的源碼文件。 因?yàn)榧砷_發(fā)環(huán)境(IDE)能夠支持和維護(hù)文件的依賴信息, 對于這類本地系統(tǒng)CMake將忽略依賴分析步驟, 只是創(chuàng)建一個(gè)本地IDE的輸入文件, 由IDE自行處理文件層次的依賴信息。而目標(biāo)層次的依賴信息則轉(zhuǎn)換為IDE所支持的依賴信息格式.

對于基于Makefile的本地構(gòu)建工具, 其make程序并不知道如何自動計(jì)算和更新依賴信息. 對于這樣的本地構(gòu)建系統(tǒng), CMake自動計(jì)算源碼(C,C++和Fortran)的依賴信息。 這些依賴關(guān)系的生成和維護(hù)都是由CMake完成的。 一旦一個(gè)項(xiàng)目由CMake首次配置完成, 用戶只需要運(yùn)行make, 剩下的工作將由CMake完成.

雖然用戶不需要知道CMake是如何工作的, 但查看一個(gè)項(xiàng)目的依賴信息還是很有幫助的。 在CMake中,每個(gè)目標(biāo)的依賴信息存儲在四個(gè)文件中:?depend.make,?flags.make,?build.makeDependInfo.cmake。?depend.make存儲指定目錄中所有對象(object)文件的依賴信息。flags.make包含了源碼文件的編譯選項(xiàng),如果編譯選項(xiàng)發(fā)生變化,目標(biāo)文件將被重新編譯。DependInfo.cmake用來維護(hù)和更新依賴關(guān)系, 它還存儲了工程中包含哪些文件和使用哪一種編碼語言等信息。?build.make則存儲創(chuàng)建依賴的規(guī)則。 如果一個(gè)目標(biāo)的依賴關(guān)系過時(shí)了,其依賴信息將被重新計(jì)算,保持為最新狀態(tài)。 比如, 添加一個(gè).h頭文件會導(dǎo)致增加一個(gè)新的依賴, 從而導(dǎo)致重新計(jì)算.

CTest和CPack

CMake由一個(gè)構(gòu)建系統(tǒng)漸漸發(fā)展為集構(gòu)建,測試和軟件打包為一體的工具家族。除了命令行工具cmake及CMake圖形界面(GUI)程序, CMake還包含測試工具CTest和打包工具CPack。 CTest和CPack共享CMake的底層代碼,但它們相對獨(dú)立并不依賴于基本的構(gòu)建過程。

ctest可執(zhí)行程序用于執(zhí)行回歸測試。簡單地使用一個(gè)add_test命令,項(xiàng)目就可以使用CTest來創(chuàng)建測試。這些測試可使用CTest來運(yùn)行,測試結(jié)果可以發(fā)送到CDash程序并顯示在網(wǎng)絡(luò)應(yīng)用中。CTest和CDash結(jié)合起來就構(gòu)成了類似于Hudson的測試工具。但兩者有很明顯的差別:CTest面向分布式測試環(huán)境, 客戶可以從版本控制系統(tǒng)中獲取代碼,運(yùn)行測試,然后將測試結(jié)果發(fā)送到CDash。而Hudson,客戶機(jī)器必須給予Hudson足夠的ssh權(quán)限來訪問目標(biāo)機(jī)器,測試才能進(jìn)行。

cpack可執(zhí)行程序用來生成項(xiàng)目的安裝程序。 CPack的執(zhí)行和CMake的構(gòu)建過程非常類似: 它也依賴于本地的工具. 比如, 在Windows上使用NSIS打包工具來生成項(xiàng)目安裝程序。 CPack執(zhí)行項(xiàng)目的安裝規(guī)則生成一棵安裝樹, 然后使用本地的打包工具(如NSIS)來獲得安裝程序。 CPack還支持創(chuàng)建RPM軟件安裝包, Debian的.deb文件,?.tar文件,?.tar.gz文件, 以及自解壓的tar文件。

5.2.3 圖形界面

許多用戶對CMake的第一印象是CMake的用戶界面。 CMake有兩個(gè)主要的用戶界面:基于Qt的圖形界面程序,和基于命令行的圖形界面程序。這些GUI實(shí)際上是CMakeCache.txt的可視化編輯器。這些界面都非常簡單,只有兩個(gè)按鈕: 配置(configure)和生成(generate),對應(yīng)于CMake的兩個(gè)主要的階段。命令行用戶界面用于Unix的TTY類型的終端和Cygwin, 而Qt圖形用戶界面則支持所有平臺。兩種GUI如圖5.3和圖5.4所示。

圖5.4 圖形用戶界面

兩種GUI都在左邊顯示緩存變量的名稱,在右邊顯示變量的值,值可以由用戶修改。其中有兩種類型的變量,普通變量和高級變量。默認(rèn)情況下只顯示有普通變量。在CMakeLists.txt中,項(xiàng)目可以指定哪些變量是高級變量。這個(gè)功能可以讓界面變得簡單,用戶配置時(shí)只需要考慮必要的選項(xiàng)。

由于緩存變量的值可能會隨著CMake命令的執(zhí)行而變化,整個(gè)生成(generate)過程可能是遞歸的。比如,打開一個(gè)選項(xiàng)可能會引入更多的選項(xiàng)。由于這個(gè)原因,GUI在配置(configure)過程中是禁用生成(generate)按鈕的,只有當(dāng)所有的選項(xiàng)都至少出現(xiàn)過一次時(shí)生成(generate)按鈕才可使用。每次按下配置(configure)按鈕,一些新出現(xiàn)的緩存變量將顯示為紅色。一旦不再有新的變量產(chǎn)生,生成(generate)按鈕就可以使用了。

5.2.4 測試CMake

任何一個(gè)新的CMake開發(fā)人員都會被首先介紹CMake開發(fā)中的測試過程,這個(gè)過程用到了多個(gè)CMake工具家族中的成員(CMake, CTest, CPack和CDash)。當(dāng)CMake代碼經(jīng)過開發(fā)并檢入到版本控制系統(tǒng)中后,運(yùn)行持續(xù)集成測試的機(jī)器將使用CTest來自動構(gòu)建和測試新的CMake代碼。其結(jié)果將發(fā)送到CDash服務(wù)器上,如果出現(xiàn)錯(cuò)誤,警告或測試失敗的情況,則通過郵件來通知開發(fā)者。

這個(gè)處理過程是一個(gè)典型的持續(xù)集成測試。當(dāng)新的代碼檢入到CMake代碼倉庫中時(shí),在CMake支持的測試平臺上將自動實(shí)施測試過程。考慮到CMake需要支持大量的編譯器和平臺,這種測試系統(tǒng)對于開發(fā)一個(gè)穩(wěn)定的系統(tǒng)是至關(guān)重要的。

比如, 如果一個(gè)新的開發(fā)者希望CMake能支持一個(gè)新的平臺, 他(她)首要要回答的問題是能否為CMake測試系統(tǒng)提供一個(gè)每晚dashboard的客戶端。 沒有經(jīng)常性不斷的測試, 新系統(tǒng)就難以保證過一段時(shí)間后不會出問題。

5.3 經(jīng)驗(yàn)教訓(xùn)

從構(gòu)建ITK的第一天開始,CMake就一直在成功運(yùn)行著,并成為了該項(xiàng)目的重要組成部分。如果重新來過,大概也不會有什么太大的不同。 但是,凡事有例外,總會有一些事情能夠做得更好。

5.3.1 后向兼容

維護(hù)后向兼容性對CMake團(tuán)隊(duì)來說是很重要的。 CMake的主要目標(biāo)是讓構(gòu)建軟件更為簡單。 當(dāng)一個(gè)工程或一個(gè)開發(fā)者選擇了CMake, 尊重其選擇并且不破壞其已有工作是非常重要的。 CMake 2.6實(shí)現(xiàn)了一個(gè)策略系統(tǒng), 它會在用戶不遵守某個(gè)命令的當(dāng)前行為時(shí)發(fā)出警告, 但仍會執(zhí)行舊的行為。 每個(gè)CMakeList.txt都要求指定期望使用的CMake版本。如果當(dāng)前運(yùn)行的CMake版本比指定的版本更新,CMake會發(fā)出警告, 但仍然使用舊的版本的行為。

5.3.2 語言,語言,語言

CMake語言盡量設(shè)計(jì)得簡單, 然而, 讓一個(gè)新項(xiàng)目考慮使用CMake的主要障礙仍然是語言。 CMake固然發(fā)展得不錯(cuò), 但CMake語言中確實(shí)存在一些古怪的行為。 CMake語言的第一個(gè)語法分析器居然只是一個(gè)簡單的字符分析器, 而不是lex/yacc等高級工具。 如果有機(jī)會重新實(shí)現(xiàn)語言部分, 我們會花時(shí)間尋找一個(gè)漂亮的已有嵌入式語言。 Lua應(yīng)該符合要求, 小且干凈。 即便不用Lua這樣的外部語言, 我也還是傾向于使用已有的語言。

5.3.3 插件不能工作

為了提供CMake語言的擴(kuò)展能力,CMake有一個(gè)插件類,允許項(xiàng)目使用C語言創(chuàng)建一個(gè)新的CMake命令。當(dāng)時(shí),這聽起來是個(gè)不錯(cuò)的主意。因?yàn)樘峁┑氖荂語言接口,還可以支持多種編譯器。但是,隨著針對不同平臺(Windows和Linux,32位和64位)的API的出現(xiàn),插件的兼容性變得難以維護(hù)。雖然只使用CMake語言顯得沒那么強(qiáng)大,但是至少不會令程序崩潰,項(xiàng)目也不會因?yàn)椴寮荒芄ぷ鞫鵁o法繼續(xù)構(gòu)建。

5.3.4 減少外部接口

在CMake的開發(fā)過程中得到的一個(gè)重要的教訓(xùn)是, 你不需要維護(hù)用戶訪問不到的功能的后向兼容性。 有些時(shí)候, 用戶和客戶要求CMake封裝成一個(gè)軟件庫供其它語言來使用。但這樣做不僅會因?yàn)椴煌腃Make使用方式而分裂CMake用戶群,也會為CMake的開發(fā)帶來巨大的維護(hù)成本。

腳注

  1. http://www.itk.org/
  2. 本譯文由筆者先獨(dú)立翻譯,而后又參考他人的翻譯成果http://www.sand-tower.net/archives/210。 主要是事先沒有上網(wǎng)調(diào)查,導(dǎo)致重復(fù)勞動。
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號