CMake 用法導覽
轉載自:http://www.cnblogs.com/coderfenghc/archive/2013/01/20/2846621.htmlhtml
Preface : 本文是CMake官方文檔CMake Tutorial (http://www.cmake.org/cmake/help/cmake_tutorial.html) 的翻譯。經過一個樣例工程從簡單到複雜的完善過程,文檔介紹了CMake主要模塊(cmake, ctest, cpack)的功能和使用環境;從中能夠一窺cmake的大致形貌。正文以下:ide
本文下述內容是一個手把手的使用指南;它涵蓋了CMake須要解決的公共構建系統的一些問題。這些主題中的許多主題已經在Mastering CMake一書中以單獨的章節被介紹過,可是經過一個樣例工程看一看它們如何工做也是很是有幫助的。本指南能夠在CMake源碼樹的Tests/Tutorial路徑下找到。每一步都有它本身的子路徑,其中包含該步驟的一個完整的指南。函數
做爲基礎的起始點(步驟1)
最基本的工程是一個從源代碼文件中構建可執行文件的例子。對於簡單工程,只要一個兩行的CMakeLists文件就足夠了。這將會做爲咱們指南的起點。這份CMakeLists文件看起來像是這樣:post
cmake_minimum_required (VERSION 2.6) project (Tutorial) add_executable(Tutorial tutorial.cxx)
注意到這個例子在CMakeLists文件中使用了小寫。CMake支持大寫、小寫、混合大小寫的命令。tutorial.cxx中的源代碼用來計算一個數的平方根,而且它的初版很是簡單,以下所示:測試
// A simple program that computes the square root of a number // 計算一個數的平方根的簡單程序 #include <stdio.h> #include <stdlib.h> #include <math.h> int main (int argc, char *argv[]) { if (argc < 2) { fprintf(stdout,"Usage: %s number\n",argv[0]); return 1; } double inputValue = atof(argv[1]); double outputValue = sqrt(inputValue); fprintf(stdout,"The square root of %g is %g\n", inputValue, outputValue); return 0; }
咱們添加的第一個特性用來爲工程和可執行文件指定一個版本號。雖然你能夠在源代碼中惟一指定它,可是你在CMakeLists文件中指定它能夠提供更好的靈活性。以下所示,我麼能夠經過添加一個版本號來修改CMakeLists文件:ui
cmake_minimum_required (VERSION 2.6) project (Tutorial) # 版本號 set (Tutorial_VERSION_MAJOR 1) set (Tutorial_VERSION_MINOR 0) # 配置一個頭文件,經過它向源代碼中傳遞一些CMake設置。 configure_file ( "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in" "${PROJECT_BINARY_DIR}/TutorialConfig.h" ) # 將二進制文件樹添加到包含文件的搜索路徑中,這樣咱們能夠找到TutorialConfig.h include_directories("${PROJECT_BINARY_DIR}") # 添加可執行文件 add_executable(Tutorial tutorial.cxx)
因爲配置過的文件將會被寫到二進制文件目錄下,咱們必須把該目錄添加到包含文件的搜索路徑清單中。而後,如下的代碼就能夠在源目錄下建立一份TotorialConfig.h.in文件:url
// 與tutorial相關的配置好的選項與設置; #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
當CMake配置這份頭文件時,@Tutorial_VERSION_MAJOR@和@Tutorial_VERSION_MINOR@的值將會被從CMakeLists文件中傳遞過來的值替代。下一步,咱們要修改tutorial.cxx來包含configured頭文件而後使用其中的版本號。修改過的源代碼展列於下:spa
// 計算平方根的簡單程序。 #include <stdio.h> #include <stdlib.h> #include <math.h> #include "TutorialConfig.h" int main (int argc, char *argv[]) { if (argc < 2) { fprintf(stdout,"%s Version %d.%d\n", argv[0], Tutorial_VERSION_MAJOR, Tutorial_VERSION_MINOR); fprintf(stdout,"Usage: %s number\n",argv[0]); return 1; } double inputValue = atof(argv[1]); double outputValue = sqrt(inputValue); fprintf(stdout,"The square root of %g is %g\n", inputValue, outputValue); return 0; }
引入庫(步驟2)翻譯
如今咱們將會在咱們的工程中引入一個庫。這個庫會包含咱們本身實現的計算一個數的平方根的函數。可執行文件隨後可使用這個庫文件而不是編譯器提供的標準開平方函數。在本指南中,咱們將會把庫文件放到一個子目錄MathFunctions中。它包含下述的單行CMakeLists文件:code
add_library(MathFunctions mysqrt.cxx)
源文件mysqrt.cxx有一個叫作mysqrt的函數,它提供了與編譯器的sqrt函數相似的功能。爲了使用新的庫,咱們在頂層的CMakeLists中增長一個add_subrirectory調用,這樣這個庫也會被構建。咱們也要向可執行文件中增長另外一個頭文件路徑,這樣就能夠從MathFunctions/mysqrt.h頭文件中找到函數的原型。最後的一點更改是在向可執行文件中引入新的庫。頂層CMakeLists文件的最後幾行如今看起來像是這樣:
include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions") add_subdirectory (MathFunctions) # 引入可執行文件 add_executable (Tutorial tutorial.cxx) target_link_libraries (Tutorial MathFunctions)
如今,讓咱們考慮下讓MathFunctions庫變爲可選的。在本指南中,確實沒有必要這樣多此一舉;可是對於更大型的庫或者依賴於第三方代碼的庫,你可能須要這種可選擇性。第一步是爲頂層的CMakeLists文件添加一個選項:
# 咱們應該使用咱們本身的數學函數嗎? option (USE_MYMATH "Use tutorial provided math implementation" ON)
這將會在CMake的GUI中顯示一個默認的ON值,而且用戶能夠隨需改變這個設置。這個設置會被存儲在cache中,那麼用戶將不須要在cmake該工程時,每次都設置這個選項。第二處改變是,讓連接MathFunctions庫變爲可選的。要實現這一點,咱們修改頂層CMakeLists文件的結尾部分:
# 添加MathFunctions庫嗎? if (USE_MYMATH) include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions") add_subdirectory (MathFunctions) set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions) endif (USE_MYMATH) # 添加可執行文件 add_executable (Tutorial tutorial.cxx) target_link_libraries (Tutorial ${EXTRA_LIBS})
這裏用USE_MYMATH設置來決定是否MathFunctions應該被編譯和執行。注意到,要用一個變量(在這裏是EXTRA_LIBS)來收集全部之後會被鏈接到可執行文件中的可選的庫。這是保持帶有許多可選部件的較大型工程乾淨清爽的一種通用的方法。源代碼對應的改變至關直白,以下所示:
// 計算一個數平方根的簡單程序 #include <stdio.h> #include <stdlib.h> #include <math.h> #include "TutorialConfig.h" #ifdef USE_MYMATH #include "MathFunctions.h" #endif int main (int argc, char *argv[]) { if (argc < 2) { fprintf(stdout,"%s Version %d.%d\n", argv[0], Tutorial_VERSION_MAJOR, Tutorial_VERSION_MINOR); fprintf(stdout,"Usage: %s number\n",argv[0]); return 1; } double inputValue = atof(argv[1]); #ifdef USE_MYMATH double outputValue = mysqrt(inputValue); #else double outputValue = sqrt(inputValue); #endif fprintf(stdout,"The square root of %g is %g\n", inputValue, outputValue); return 0; }
在源代碼中,咱們也使用了USE_MYMATH。這個宏是由CMake經過TutorialConfig.h.in配置文件中的下述語句行提供給源代碼的:
#cmakedefine USE_MYMATH
安裝與測試(步驟3)
下一步咱們會爲咱們的工程引入安裝規則以及測試支持。安裝規則至關直白,對於MathFunctions庫,咱們經過向MathFunctions的CMakeLists文件添加以下兩條語句來設置要安裝的庫以及頭文件:
install (TARGETS MathFunctions DESTINATION bin) install (FILES MathFunctions.h DESTINATION include)
對於應用程序,在頂層CMakeLists文件中添加下面幾行,它們用來安裝可執行文件以及配置頭文件:
# 添加安裝目標 install (TARGETS Tutorial DESTINATION bin) install (FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" DESTINATION include)
這就是要作的所有;如今你應該能夠構建tutorial工程了。而後,敲入命令make install(或者從IDE中構建INSTALL目標)而後它就會安裝須要的頭文件,庫以及可執行文件CMake的變量CMAKE_INSTALL_PREFIX用來肯定這些文件被安裝的根目錄。添加測試一樣也只須要至關淺顯的過程。在頂層CMakeLists文件的的尾部補充許多基本的測試代碼來確認應用程序能夠正確工做。
include(CTest) # 應用程序是否運行? add_test (TutorialRuns Tutorial 25) # 它是否對25作了開平方運算 add_test (TutorialComp25 Tutorial 25) set_tests_properties (TutorialComp25 PROPERTIES PASS_REGULAR_EXPRESSION "25 is 5") # 它是否能處理是負數做爲輸入的狀況 add_test (TutorialNegative Tutorial -25) set_tests_properties (TutorialNegative PROPERTIES PASS_REGULAR_EXPRESSION "-25 is 0") # 它是否能夠處理較小的數字。 add_test (TutorialSmall Tutorial 0.0001) set_tests_properties (TutorialSmall PROPERTIES PASS_REGULAR_EXPRESSION "0.0001 is 0.01") # 用法信息是否可用? add_test (TutorialUsage Tutorial) set_tests_properties (TutorialUsage PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number")
第一個測試用例僅僅用來驗證程序能夠運行,沒有出現段錯誤或其餘的崩潰,而且返回值必須是0。這是CTest所作測試的基本格式。餘下的幾個測試都是用PASS_REGULAR_EXPRESSION 測試屬性來驗證測試代碼的輸出是否包含有特定的字符串。在本例中,測試樣例用來驗證計算得出的平方根與預約值同樣;當指定錯誤的輸入數據時,要打印用法信息。若是你想要添加許多測試不一樣輸入值的樣例,你應該考慮建立以下所示的宏:
include(CTest) #定義一個宏來簡化添加測試的過程,而後使用它 macro (do_test arg result) add_test (TutorialComp${arg} Tutorial ${arg}) set_tests_properties (TutorialComp${arg} PROPERTIES PASS_REGULAR_EXPRESSION ${result}) endmacro (do_test) # 作一系列基於結果的測試 do_test (25 "25 is 5") do_test (-25 "-25 is 0")
對於每一個do_test宏調用,都會向工程中添加一個新的測試用例;宏參數是測試名、函數的輸入以及指望結果。
增長系統內省(步驟4)
下一步,讓咱們考慮向咱們的工程中引入一些依賴於目標平臺上可能不具有的特性的代碼。在本例中,咱們會增長一些依賴於目標平臺是否有log或exp函數的代碼。固然,幾乎每一個平臺都有這些函數;可是對於tutorial工程,咱們假設它們並不是如此廣泛。若是該平臺有log函數,那麼咱們會在mysqrt函數中使用它去計算平方根。咱們首先在頂層CMakeLists文件中使用宏CheckFunctionExists.cmake測試這些函數的可用性:
# 該系統提供log和exp函數嗎? include (CheckFunctionExists.cmake) check_function_exists (log HAVE_LOG) check_function_exists (exp HAVE_EXP)
下一步,若是CMake在對應平臺上找到了它們,咱們修改TutorialConfig.h.in來定義這些值;以下:
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP
這些log和exp函數的測試要在TutorialConfig.h的configure_file命令以前被處理,這一點很重要。最後,在mysqrt函數中,若是log和exp在當前系統上可用的話,咱們能夠提供一個基於它們的可選的實現:
// 若是咱們有log和exp兩個函數,那麼使用它們 #if defined (HAVE_LOG) && defined (HAVE_EXP) result = exp(log(x)*0.5); #else // 不然使用替代方法
添加一個生成文件以及生成器(步驟5)
在本節,咱們會展現你應該怎樣向一個應用程序的構建過程當中添加一個生成的源文件。在本範例中,咱們會建立一個預先計算出的平方根表做爲構建過程的一部分。MathFunctions子路徑下,一個新的MakeTable.cxx源文件來作這件事。
// 一個簡單的用於構建平方根表的程序 #include <stdio.h> #include <stdlib.h> #include <math.h> int main (int argc, char *argv[]) { int i; double result; // 確保有足夠多的參數 if (argc < 2) { return 1; } // 打開輸出文件 FILE *fout = fopen(argv[1],"w"); if (!fout) { return 1; } // 建立一個帶有平方根表的源文件 fprintf(fout,"double sqrtTable[] = {\n"); for (i = 0; i < 10; ++i) { result = sqrt(static_cast<double>(i)); fprintf(fout,"%g,\n",result); } // 該表以0結尾 fprintf(fout,"0};\n"); fclose(fout); return 0; }
注意到這個表是由合法的C++代碼生成的,而且被寫入的輸出文件的名字是做爲一個參數輸入的。下一步是將合適的命令添加到MathFunction的CMakeLists文件中,來構建MakeTable可執行文件,而後運行它,做爲構建過程的一部分。完成這幾步,須要少數的幾個命令,以下所示:
# 首先,咱們添加生成該表的可執行文件 add_executable(MakeTable MakeTable.cxx) # 而後添加該命令來生成源文件 add_custom_command ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h DEPENDS MakeTable ) # 爲包含文件,向搜索路徑中添加二進制樹路徑 include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) # 添加main庫 add_library(MathFunctions mysqrt.cxx ${CMAKE_CURRENT_BINARY_DIR}/Table.h )
首先,MakeTable的可執行文件也和其餘被加入的文件同樣被加入。而後,咱們添加一個自定義命令來指定如何經過運行MakeTable來生成Table.h。這是經過將生成Table.h增長到MathFunctions庫的源文件列表中來實現的。咱們還必須增長當前的二進制路徑到包含路徑的清單中,這樣Table.h能夠被找到而且能夠被mysqrt.cxx所包含。當該工程被構建後,它首先會構建MakeTable可執行文件。而後它會運行MakeTable來生成Table.h文件。最後,它會編譯mysqrt.cxx(其中包含Table.h)來生成MathFunctions庫。到目前爲止,擁有咱們添加的完整特性的頂層CMakeLists文件看起來像是這樣:
cmake_minimum_required (VERSION 2.6) project (Tutorial) include(CTest) # 版本號 set (Tutorial_VERSION_MAJOR 1) set (Tutorial_VERSION_MINOR 0) # 本系統是否提供log和exp函數? include (${CMAKE_ROOT}/Modules/CheckFunctionExists.cmake) check_function_exists (log HAVE_LOG) check_function_exists (exp HAVE_EXP) # 咱們應該使用本身的math函數嗎? option(USE_MYMATH "Use tutorial provided math implementation" ON) # 配置一個頭文件來向源代碼傳遞一些CMake設置。 configure_file ( "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in" "${PROJECT_BINARY_DIR}/TutorialConfig.h" ) # 爲包含文件的搜索路徑添加二進制樹,這樣才能發現TutorialConfig.h頭文件。 include_directories ("${PROJECT_BINARY_DIR}") # 添加MathFunctions庫嗎? if (USE_MYMATH) include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions") add_subdirectory (MathFunctions) set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions) endif (USE_MYMATH) # 添加可執行文件 add_executable (Tutorial tutorial.cxx) target_link_libraries (Tutorial ${EXTRA_LIBS}) # 添加安裝的目標 install (TARGETS Tutorial DESTINATION bin) install (FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" DESTINATION include) # 測試1 :應用程序能夠運行嗎? add_test (TutorialRuns Tutorial 25) # 測試2 : 使用信息可用嗎? add_test (TutorialUsage Tutorial) set_tests_properties (TutorialUsage PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number" ) # 定義一個能夠簡化引入測試過程的宏 macro (do_test arg result) add_test (TutorialComp${arg} Tutorial ${arg}) set_tests_properties (TutorialComp${arg} PROPERTIES PASS_REGULAR_EXPRESSION ${result} ) endmacro (do_test) # do a bunch of result based tests # 執行一系列基於結果的測試 do_test (4 "4 is 2") do_test (9 "9 is 3") do_test (5 "5 is 2.236") do_test (7 "7 is 2.645") do_test (25 "25 is 5") do_test (-25 "-25 is 0") do_test (0.0001 "0.0001 is 0.01")
TutorialConfig.h文件看起來像是這樣:
// Tutorial的配置選項與設置以下 #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ #cmakedefine USE_MYMATH // 該平臺提供exp和log函數嗎? #cmakedefine HAVE_LOG #cmakedefine HAVE_EXP
而後,MathFunctions工程的CMakeLists文件看起來像是這樣:
# 首先,咱們添加生成這個表的可執行文件 add_executable(MakeTable MakeTable.cxx) # 添加生成源代碼的命令 add_custom_command ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h DEPENDS MakeTable COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h ) # 爲包含文件向搜索路徑中添加二進制樹目錄 include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) # 添加main庫 add_library(MathFunctions mysqrt.cxx ${CMAKE_CURRENT_BINARY_DIR}/Table.h) install (TARGETS MathFunctions DESTINATION bin) install (FILES MathFunctions.h DESTINATION include)
構建一個安裝器(步驟6)
下一步假設咱們想要向其餘人分發咱們的工程,這樣他們就可使用它。咱們想同時提供在許多不一樣平臺上的源代碼和二進制文檔發行版。這與以前咱們在「安裝與測試(步驟3)」作過的安裝有一點不一樣,那裏咱們僅僅安裝咱們從源碼中構建出來的二進制文件。在本例子中,咱們會構建支持二進制安裝以及相似於cygwin,debian,RPM等具備包管理特性的安裝包。爲了完成這個目標,咱們會使用CPack來建立Packaging with CPack一章中描述的特定平臺的安裝器。
# 構建一個CPack驅動的安裝包 include (InstallRequiredSystemLibraries) set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") set (CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}") set (CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}") include (CPack)
須要作的所有事情就這些。咱們以包含InstallRequiredSystemLibraries開始。這個模塊將會包含許多在當前平臺上,當前工程須要的運行時庫。第一步咱們將一些CPack變量設置爲保存本工程的許可證和版本信息的位置。版本信息使用了咱們在本指南中先前設置的變量。最後,咱們要包含CPack模塊,它會使用這些變量以及你所處的系統的一些別的屬性,而後來設置一個安裝器。下一步是以一般的方式構建該工程而後隨後運行CPack。若是要構建一個二進制發行包,你應該運行:
cpack -C CPackConfig.cmake
爲了建立一個源代碼發行版,你應該鍵入:
cpack -C CPackSourceConfig.cmake
增長對Dashboard的支持(步驟7)
增長對向一個dashboard提交咱們的測試結果的功能的支持很是簡單。咱們在本指南的先前步驟中已經定義了咱們工程中的許多測試樣例。咱們僅僅須要運行這些測試樣例而後將它們提交到dashboard便可。爲了包含對dashboards的支持,咱們須要在頂層CMakeLists文件中包含CTest模塊。
# 支持dashboard腳本 include (CTest)
咱們也能夠建立一個CTestConfig.cmake文件,在其中來指定該dashboard的工程名。
set (CTEST_PROJECT_NAME "Tutorial")
CTest 將會在運行期間讀取這個文件。爲了建立一個簡單的dashboard,你能夠在你的工程下運行CMake,而後切換到二進制樹,而後運行ctest -DExperimental. 你的dashboard將會被更新到Kitware的公共dashboard.