All problems in computer science can be solved by another level of indirection.
David Wheeler
你或許聽過好幾種 Make 工具,例如 GNU Make ,QT 的 qmake ,微軟的 MS nmake,BSD Make(pmake),Makepp,等等。這些 Make 工具遵循著不同的規(guī)范和標(biāo)準(zhǔn),所執(zhí)行的 Makefile 格式也千差萬別。這樣就帶來了一個(gè)嚴(yán)峻的問題:如果軟件想跨平臺(tái),必須要保證能夠在不同平臺(tái)編譯。而如果使用上面的 Make 工具,就得為每一種標(biāo)準(zhǔn)寫一次 Makefile ,這將是一件讓人抓狂的工作。
CMake就是針對(duì)上面問題所設(shè)計(jì)的工具:它首先允許開發(fā)者編寫一種平臺(tái)無關(guān)的 CMakeList.txt 文件來定制整個(gè)編譯流程,然后再根據(jù)目標(biāo)用戶的平臺(tái)進(jìn)一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。從而做到“Write once, run everywhere”。顯然,CMake 是一個(gè)比上述幾種 make 更高級(jí)的編譯配置工具。一些使用 CMake 作為項(xiàng)目架構(gòu)系統(tǒng)的知名開源項(xiàng)目有 VTK、ITK、KDE、OpenCV、OSG 等 [1]。
在 linux 平臺(tái)下使用 CMake 生成 Makefile 并編譯的流程如下:
編寫 CMake 配置文件 CMakeLists.txt 。
執(zhí)行命令 cmake PATH 或者 ccmake PATH 生成 Makefile 1 1ccmake 和 cmake 的區(qū)別在于前者提供了一個(gè)交互式的界面。。其中, PATH 是 CMakeLists.txt 所在的目錄。
使用 make 命令進(jìn)行編譯。
本文將從實(shí)例入手,一步步講解 CMake 的常見用法,文中所有的實(shí)例代碼可以在這里找到。如果你讀完仍覺得意猶未盡,可以繼續(xù)學(xué)習(xí)我在文章末尾提供的其他資源。
入門案例:單個(gè)源文件本節(jié)對(duì)應(yīng)的源代碼所在目錄:Demo1。
對(duì)于簡單的項(xiàng)目,只需要寫幾行代碼就可以了。例如,假設(shè)現(xiàn)在我們的項(xiàng)目中只有一個(gè)源文件 main.cc ,該程序的用途是計(jì)算一個(gè)數(shù)的指數(shù)冪。
#include <stdio.h> #include <stdlib.h> /** * power - Calculate the power of number. * @param base: Base value. * @param exponent: Exponent value. * * @return base raised to the power exponent. */ double power(double base, int exponent) { int result = base; int i; if (exponent == 0) { return 1; } for(i = 1; i < exponent; ++i){ result = result * base; } return result; } int main(int argc, char *argv[]) { if (argc < 3){ printf("Usage: %s base exponent \n", argv[0]); return 1; } double base = atof(argv[1]); int exponent = atoi(argv[2]); double result = power(base, exponent); printf("%g ^ %d is %g\n", base, exponent, result); return 0; }
編寫 CMakeLists.txt
首先編寫 CMakeLists.txt 文件,并保存在與 main.cc 源文件同個(gè)目錄下:
# CMake 最低版本號(hào)要求 cmake_minimum_required (VERSION 2.8) # 項(xiàng)目信息 project (Demo1) # 指定生成目標(biāo) add_executable(Demo main.cc)
CMakeLists.txt 的語法比較簡單,由命令、注釋和空格組成,其中命令是不區(qū)分大小寫的。符號(hào) # 后面的內(nèi)容被認(rèn)為是注釋。命令由命令名稱、小括號(hào)和參數(shù)組成,參數(shù)之間使用空格進(jìn)行間隔。
對(duì)于上面的 CMakeLists.txt 文件,依次出現(xiàn)了幾個(gè)命令:
cmake_minimum_required:指定運(yùn)行此配置文件所需的 CMake 的最低版本;
project:參數(shù)值是 Demo1,該命令表示項(xiàng)目的名稱是 Demo1 。
add_executable: 將名為 main.cc 的源文件編譯成一個(gè)名稱為 Demo 的可執(zhí)行文件。
編譯項(xiàng)目
之后,在當(dāng)前目錄執(zhí)行 cmake . ,得到 Makefile 后再使用 make 命令編譯得到 Demo1 可執(zhí)行文件。
[ehome@xman Demo1]$ cmake .
-- The C compiler identification is GNU 4.8.2
-- The CXX compiler identification is GNU 4.8.2
-- Check for working C compiler: /usr/sbin/cc
-- Check for working C compiler: /usr/sbin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/sbin/c++
-- Check for working CXX compiler: /usr/sbin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/ehome/Documents/programming/C/power/Demo1
[ehome@xman Demo1]$ make
Scanning dependencies of target Demo
[100%] Building C object CMakeFiles/Demo.dir/main.cc.o
Linking C executable Demo
[100%] Built target Demo
[ehome@xman Demo1]$ ./Demo 5 4
5 ^ 4 is 625
[ehome@xman Demo1]$ ./Demo 7 3
7 ^ 3 is 343
[ehome@xman Demo1]$ ./Demo 2 10
2 ^ 10 is 1024
本小節(jié)對(duì)應(yīng)的源代碼所在目錄:Demo2。
上面的例子只有單個(gè)源文件?,F(xiàn)在假如把 power 函數(shù)單獨(dú)寫進(jìn)一個(gè)名為 MathFunctions.c 的源文件里,使得這個(gè)工程變成如下的形式:
1 ./Demo2 2 3 | 4 5 +--- main.cc 6 7 | 8 9 +--- MathFunctions.cc10 11 |12 13 +--- MathFunctions.h
這個(gè)時(shí)候,CMakeLists.txt 可以改成如下的形式:
1 # CMake 最低版本號(hào)要求 2 3 cmake_minimum_required (VERSION 2.8) 4 5 # 項(xiàng)目信息 6 7 project (Demo2) 8 9 # 指定生成目標(biāo)10 11 add_executable(Demo main.cc MathFunctions.cc)
唯一的改動(dòng)只是在 add_executable 命令中增加了一個(gè) MathFunctions.cc 源文件。這樣寫當(dāng)然沒什么問題,但是如果源文件很多,把所有源文件的名字都加進(jìn)去將是一件煩人的工作。更省事的方法是使用 aux_source_directory 命令,該命令會(huì)查找指定目錄下的所有源文件,然后將結(jié)果存進(jìn)指定變量名。其語法如下:
aux_source_directory(<dir> <variable>)
因此,可以修改 CMakeLists.txt 如下:
1 # CMake 最低版本號(hào)要求 2 3 cmake_minimum_required (VERSION 2.8) 4 5 # 項(xiàng)目信息 6 7 project (Demo2) 8 9 # 查找當(dāng)前目錄下的所有源文件10 11 # 并將名稱保存到 DIR_SRCS 變量12 13 aux_source_directory(. DIR_SRCS)14 15 # 指定生成目標(biāo)16 17 add_executable(Demo ${DIR_SRCS})
這樣,CMake 會(huì)將當(dāng)前目錄所有源文件的文件名賦值給變量 DIR_SRCS ,再指示變量 DIR_SRCS 中的源文件需要編譯成一個(gè)名稱為 Demo 的可執(zhí)行文件。
多個(gè)目錄,多個(gè)源文件本小節(jié)對(duì)應(yīng)的源代碼所在目錄:Demo3。
現(xiàn)在進(jìn)一步將 MathFunctions.h 和 MathFunctions.cc 文件移動(dòng)到 math 目錄下。
1 ./Demo3 2 3 | 4 5 +--- main.cc 6 7 | 8 9 +--- math/10 11 |12 13 +--- MathFunctions.cc14 15 |16 17 +--- MathFunctions.h
對(duì)于這種情況,需要分別在項(xiàng)目根目錄 Demo3 和 math 目錄里各編寫一個(gè) CMakeLists.txt 文件。為了方便,我們可以先將 math 目錄里的文件編譯成靜態(tài)庫再由 main 函數(shù)調(diào)用。
根目錄中的 CMakeLists.txt :
1 # CMake 最低版本號(hào)要求 2 3 cmake_minimum_required (VERSION 2.8) 4 5 # 項(xiàng)目信息 6 7 project (Demo3) 8 9 # 查找當(dāng)前目錄下的所有源文件10 11 # 并將名稱保存到 DIR_SRCS 變量12 13 aux_source_directory(. DIR_SRCS)14 15 # 添加 math 子目錄16 17 add_subdirectory(math)18 19 # 指定生成目標(biāo)20 21 add_executable(Demo main.cc)22 23 # 添加鏈接庫24 25 target_link_libraries(Demo MathFunctions)
該文件添加了下面的內(nèi)容: 第3行,使用命令 add_subdirectory 指明本項(xiàng)目包含一個(gè)子目錄 math,這樣 math 目錄下的 CMakeLists.txt 文件和源代碼也會(huì)被處理 。第6行,使用命令 target_link_libraries 指明可執(zhí)行文件 main 需要連接一個(gè)名為 MathFunctions 的鏈接庫 。
子目錄中的 CMakeLists.txt:
1 # 查找當(dāng)前目錄下的所有源文件2 3 # 并將名稱保存到 DIR_LIB_SRCS 變量4 5 aux_source_directory(. DIR_LIB_SRCS)6 7 # 生成鏈接庫8 9 add_library (MathFunctions ${DIR_LIB_SRCS})
在該文件中使用命令 add_library 將 src 目錄中的源文件編譯為靜態(tài)鏈接庫。
自定義編譯選項(xiàng)本節(jié)對(duì)應(yīng)的源代碼所在目錄:Demo4。
CMake 允許為項(xiàng)目增加編譯選項(xiàng),從而可以根據(jù)用戶的環(huán)境和需求選擇最合適的編譯方案。
例如,可以將 MathFunctions 庫設(shè)為一個(gè)可選的庫,如果該選項(xiàng)為 ON ,就使用該庫定義的數(shù)學(xué)函數(shù)來進(jìn)行運(yùn)算。否則就調(diào)用標(biāo)準(zhǔn)庫中的數(shù)學(xué)函數(shù)庫。
修改 CMakeLists 文件
我們要做的第一步是在頂層的 CMakeLists.txt 文件中添加該選項(xiàng):
1 # CMake 最低版本號(hào)要求 2 3 cmake_minimum_required (VERSION 2.8) 4 5 # 項(xiàng)目信息 6 7 project (Demo4) 8 9 # 加入一個(gè)配置頭文件,用于處理 CMake 對(duì)源碼的設(shè)置10 11 configure_file (12 13 "${PROJECT_SOURCE_DIR}/config.h.in"14 15 "${PROJECT_BINARY_DIR}/config.h"16 17 )18 19 # 是否使用自己的 MathFunctions 庫20 21 option (USE_MYMATH22 23 "Use provided math implementation" ON)24 25 # 是否加入 MathFunctions 庫26 27 if (USE_MYMATH)28 29 include_directories ("${PROJECT_SOURCE_DIR}/math")30 31 add_subdirectory (math)32 33 set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)34 35 endif (USE_MYMATH)36 37 # 查找當(dāng)前目錄下的所有源文件38 39 # 并將名稱保存到 DIR_SRCS 變量40 41 aux_source_directory(. DIR_SRCS)42 43 # 指定生成目標(biāo)44 45 add_executable(Demo ${DIR_SRCS})46 47 target_link_libraries (Demo ${EXTRA_LIBS})
其中:
第7行的 configure_file 命令用于加入一個(gè)配置頭文件 config.h ,這個(gè)文件由 CMake 從 config.h.in 生成,通過這樣的機(jī)制,將可以通過預(yù)定義一些參數(shù)和變量來控制代碼的生成。
第13行的 option 命令添加了一個(gè) USE_MYMATH 選項(xiàng),并且默認(rèn)值為 ON 。
第17行根據(jù) USE_MYMATH 變量的值來決定是否使用我們自己編寫的 MathFunctions 庫。
修改 main.cc 文件
之后修改 main.cc 文件,讓其根據(jù) USE_MYMATH 的預(yù)定義值來決定是否調(diào)用標(biāo)準(zhǔn)庫還是 MathFunctions 庫:
1 #include 2 3 #include 4 5 #include "config.h" 6 7 #ifdef USE_MYMATH 8 9 #include "math/MathFunctions.h"10 11 #else12 13 #include14 15 #endif16 17 int main(int argc, char *argv[])18 19 {20 21 if (argc < 3){22 23 printf("Usage: %s base exponent \n", argv[0]);24 25 return 1;26 27 }28 29 double base = atof(argv[1]);30 31 int exponent = atoi(argv[2]);32 33 #ifdef USE_MYMATH34 35 printf("Now we use our own Math library. \n");36 37 double result = power(base, exponent);38 39 #else40 41 printf("Now we use the standard library. \n");42 43 double result = pow(base, exponent);44 45 #endif46 47 printf("%g ^ %d is %g\n", base, exponent, result);48 49 return 0;50 51 }
編寫 config.h.in 文件
上面的程序值得注意的是第2行,這里引用了一個(gè) config.h 文件,這個(gè)文件預(yù)定義了 USE_MYMATH 的值。但我們并不直接編寫這個(gè)文件,為了方便從 CMakeLists.txt 中導(dǎo)入配置,我們編寫一個(gè) config.h.in 文件,內(nèi)容如下:
#cmakedefine USE_MYMATH
這樣 CMake 會(huì)自動(dòng)根據(jù) CMakeLists 配置文件中的設(shè)置自動(dòng)生成 config.h 文件。
編譯項(xiàng)目
現(xiàn)在編譯一下這個(gè)項(xiàng)目,為了便于交互式的選擇該變量的值,可以使用 ccmake 命令 2 2也可以使用 cmake -i 命令,該命令會(huì)提供一個(gè)會(huì)話式的交互式配置界面。:
從中可以找到剛剛定義的 USE_MYMATH 選項(xiàng),按鍵盤的方向鍵可以在不同的選項(xiàng)窗口間跳轉(zhuǎn),按下 enter 鍵可以修改該選項(xiàng)。修改完成后可以按下 c 選項(xiàng)完成配置,之后再按 g 鍵確認(rèn)生成 Makefile 。ccmake 的其他操作可以參考窗口下方給出的指令提示。
我們可以試試分別將 USE_MYMATH 設(shè)為 ON 和 OFF 得到的結(jié)果:
USE_MYMATH 為 ON
運(yùn)行結(jié)果:
1 [ehome@xman Demo4]$ ./Demo2 3 Now we use our own MathFunctions library.4 5 7 ^ 3 = 343.0000006 7 10 ^ 5 = 100000.0000008 9 2 ^ 10 = 1024.000000
此時(shí) config.h 的內(nèi)容為:
#define USE_MYMATH
USE_MYMATH 為 OFF
運(yùn)行結(jié)果:
1 [ehome@xman Demo4]$ ./Demo2 3 Now we use the standard library.4 5 7 ^ 3 = 343.0000006 7 10 ^ 5 = 100000.0000008 9 2 ^ 10 = 1024.000000
此時(shí) config.h 的內(nèi)容為:
/* #undef USE_MYMATH */
本節(jié)對(duì)應(yīng)的源代碼所在目錄:Demo5。
CMake 也可以指定安裝規(guī)則,以及添加測(cè)試。這兩個(gè)功能分別可以通過在產(chǎn)生 Makefile 后使用 make install 和 make test 來執(zhí)行。在以前的 GNU Makefile 里,你可能需要為此編寫 install 和 test 兩個(gè)偽目標(biāo)和相應(yīng)的規(guī)則,但在 CMake 里,這樣的工作同樣只需要簡單的調(diào)用幾條命令。
定制安裝規(guī)則首先先在 math/CMakeLists.txt 文件里添加下面兩行:
1 # 指定 MathFunctions 庫的安裝路徑2 3 install (TARGETS MathFunctions DESTINATION bin)4 5 install (FILES MathFunctions.h DESTINATION include)
指明 MathFunctions 庫的安裝路徑。之后同樣修改根目錄的 CMakeLists 文件,在末尾添加下面幾行:
1 # 指定安裝路徑2 3 install (TARGETS Demo DESTINATION bin)4 5 install (FILES "${PROJECT_BINARY_DIR}/config.h"6 7 DESTINATION include)
通過上面的定制,生成的 Demo 文件和 MathFunctions 函數(shù)庫 libMathFunctions.o 文件將會(huì)被復(fù)制到 /usr/local/bin 中,而 MathFunctions.h 和生成的 config.h 文件則會(huì)被復(fù)制到 /usr/local/include 中。我們可以驗(yàn)證一下3 3順帶一提的是,這里的 /usr/local/ 是默認(rèn)安裝到的根目錄,可以通過修改 CMAKE_INSTALL_PREFIX 變量的值來指定這些文件應(yīng)該拷貝到哪個(gè)根目錄。:
1 [ehome@xman Demo5]$ sudo make install 2 3 [ 50%] Built target MathFunctions 4 5 [100%] Built target Demo 6 7 Install the project... 8 9 -- Install configuration: ""10 11 -- Installing: /usr/local/bin/Demo12 13 -- Installing: /usr/local/include/config.h14 15 -- Installing: /usr/local/bin/libMathFunctions.a16 17 -- Up-to-date: /usr/local/include/MathFunctions.h18 19 [ehome@xman Demo5]$ ls /usr/local/bin20 21 Demo libMathFunctions.a22 23 [ehome@xman Demo5]$ ls /usr/local/include24 25 config.h MathFunctions.h
添加測(cè)試同樣很簡單。CMake 提供了一個(gè)稱為 CTest 的測(cè)試工具。我們要做的只是在項(xiàng)目根目錄的 CMakeLists 文件中調(diào)用一系列的 add_test 命令。
1 # 啟用測(cè)試 2 3 enable_testing() 4 5 # 測(cè)試程序是否成功運(yùn)行 6 7 add_test (test_run Demo 5 2) 8 9 # 測(cè)試幫助信息是否可以正常提示10 11 add_test (test_usage Demo)12 13 set_tests_properties (test_usage14 15 PROPERTIES PASS_REGULAR_EXPRESSION "Usage: .* base exponent")16 17 # 測(cè)試 5 的平方18 19 add_test (test_5_2 Demo 5 2)20 21 set_tests_properties (test_5_222 23 PROPERTIES PASS_REGULAR_EXPRESSION "is 25")24 25 # 測(cè)試 10 的 5 次方26 27 add_test (test_10_5 Demo 10 5)28 29 set_tests_properties (test_10_530 31 PROPERTIES PASS_REGULAR_EXPRESSION "is 100000")32 33 # 測(cè)試 2 的 10 次方34 35 add_test (test_2_10 Demo 2 10)36 37 set_tests_properties (test_2_1038 39 PROPERTIES PASS_REGULAR_EXPRESSION "is 1024")
上面的代碼包含了四個(gè)測(cè)試。第一個(gè)測(cè)試 test_run 用來測(cè)試程序是否成功運(yùn)行并返回 0 值。剩下的三個(gè)測(cè)試分別用來測(cè)試 5 的 平方、10 的 5 次方、2 的 10 次方是否都能得到正確的結(jié)果。其中 PASS_REGULAR_EXPRESSION 用來測(cè)試輸出是否包含后面跟著的字符串。
讓我們看看測(cè)試的結(jié)果:
1 [ehome@xman Demo5]$ make test 2 3 Running tests... 4 5 Test project /home/ehome/Documents/programming/C/power/Demo5 6 7 Start 1: test_run 8 9 1/4 Test #1: test_run ......................... Passed 0.00 sec10 11 Start 2: test_5_212 13 2/4 Test #2: test_5_2 ......................... Passed 0.00 sec14 15 Start 3: test_10_516 17 3/4 Test #3: test_10_5 ........................ Passed 0.00 sec18 19 Start 4: test_2_1020 21 4/4 Test #4: test_2_10 ........................ Passed 0.00 sec22 23 100% tests passed, 0 tests failed out of 424 25 Total Test time (real) = 0.01 sec
如果要測(cè)試更多的輸入數(shù)據(jù),像上面那樣一個(gè)個(gè)寫測(cè)試用例未免太繁瑣。這時(shí)可以通過編寫宏來實(shí)現(xiàn):
1 # 定義一個(gè)宏,用來簡化測(cè)試工作 2 3 macro (do_test arg1 arg2 result) 4 5 add_test (test_${arg1}_${arg2} Demo ${arg1} ${arg2}) 6 7 set_tests_properties (test_${arg1}_${arg2} 8 9 PROPERTIES PASS_REGULAR_EXPRESSION ${result})10 11 endmacro (do_test)12 13 # 使用該宏進(jìn)行一系列的數(shù)據(jù)測(cè)試14 15 do_test (5 2 "is 25")16 17 do_test (10 5 "is 100000")18 19 do_test (2 10 "is 1024")
關(guān)于 CTest 的更詳細(xì)的用法可以通過 man 1 ctest 參考 CTest 的文檔。
支持 gdb讓 CMake 支持 gdb 的設(shè)置也很容易,只需要指定 Debug 模式下開啟 -g 選項(xiàng):
1 set(CMAKE_BUILD_TYPE "Debug")2 3 set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")4 5 set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
之后可以直接對(duì)生成的程序使用 gdb 來調(diào)試。
添加環(huán)境檢查本節(jié)對(duì)應(yīng)的源代碼所在目錄:Demo6。
有時(shí)候可能要對(duì)系統(tǒng)環(huán)境做點(diǎn)檢查,例如要使用一個(gè)平臺(tái)相關(guān)的特性的時(shí)候。在這個(gè)例子中,我們檢查系統(tǒng)是否自帶 pow 函數(shù)。如果帶有 pow 函數(shù),就使用它;否則使用我們定義的 power 函數(shù)。
添加 CheckFunctionExists 宏
首先在頂層 CMakeLists 文件中添加 CheckFunctionExists.cmake 宏,并調(diào)用 check_function_exists 命令測(cè)試鏈接器是否能夠在鏈接階段找到 pow 函數(shù)。
1 # 檢查系統(tǒng)是否支持 pow 函數(shù)2 3 include (${CMAKE_ROOT}/Modules/CheckFunctionExists.cmake)4 5 check_function_exists (pow HAVE_POW)
將上面這段代碼放在 configure_file 命令前。
預(yù)定義相關(guān)宏變量
接下來修改 config.h.in 文件,預(yù)定義相關(guān)的宏變量。
1 // does the platform provide pow function?2 3 #cmakedefine HAVE_POW
在代碼中使用宏和函數(shù)
最后一步是修改 main.cc ,在代碼中使用宏和函數(shù):
1 #ifdef HAVE_POW 2 3 printf("Now we use the standard library. \n"); 4 5 double result = pow(base, exponent); 6 7 #else 8 9 printf("Now we use our own Math library. \n");10 11 double result = power(base, exponent);12 13 #endif
本節(jié)對(duì)應(yīng)的源代碼所在目錄:Demo7。
給項(xiàng)目添加和維護(hù)版本號(hào)是一個(gè)好習(xí)慣,這樣有利于用戶了解每個(gè)版本的維護(hù)情況,并及時(shí)了解當(dāng)前所用的版本是否過時(shí),或是否可能出現(xiàn)不兼容的情況。
首先修改頂層 CMakeLists 文件,在 project 命令之后加入如下兩行:
1 set (Demo_VERSION_MAJOR 1)2 3 set (Demo_VERSION_MINOR 0)
分別指定當(dāng)前的項(xiàng)目的主版本號(hào)和副版本號(hào)。
之后,為了在代碼中獲取版本信息,我們可以修改 config.h.in 文件,添加兩個(gè)預(yù)定義變量:
1 // the configured options and settings for Tutorial2 3 #define Demo_VERSION_MAJOR @Demo_VERSION_MAJOR@4 5 #define Demo_VERSION_MINOR @Demo_VERSION_MINOR@
這樣就可以直接在代碼中打印版本信息了:
1 #include <stdio.h> 2 3 #include <stdlib.h> 4 5 #include <math.h> 6 7 #include "config.h" 8 9 #include "math/MathFunctions.h"10 11 int main(int argc, char *argv[])12 13 {14 15 if (argc < 3){16 17 // print version info18 19 printf("%s Version %d.%d\n",20 21 argv[0],22 23 Demo_VERSION_MAJOR,24 25 Demo_VERSION_MINOR);26 27 printf("Usage: %s base exponent \n", argv[0]);28 29 return 1;30 31 }32 33 double base = atof(argv[1]);34 35 int exponent = atoi(argv[2]);36 37 #if defined (HAVE_POW)38 39 printf("Now we use the standard library. \n");40 41 double result = pow(base, exponent);42 43 #else44 45 printf("Now we use our own Math library. \n");46 47 double result = power(base, exponent);48 49 #endif50 51 printf("%g ^ %d is %g\n", base, exponent, result);52 53 return 0;54 55 }
本節(jié)對(duì)應(yīng)的源代碼所在目錄:Demo8。
本節(jié)將學(xué)習(xí)如何配置生成各種平臺(tái)上的安裝包,包括二進(jìn)制安裝包和源碼安裝包。為了完成這個(gè)任務(wù),我們需要用到 CPack ,它同樣也是由 CMake 提供的一個(gè)工具,專門用于打包。
首先在頂層的 CMakeLists.txt 文件尾部添加下面幾行:
1 # 構(gòu)建一個(gè) CPack 安裝包 2 3 include (InstallRequiredSystemLibraries) 4 5 set (CPACK_RESOURCE_FILE_LICENSE 6 7 "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") 8 9 set (CPACK_PACKAGE_VERSION_MAJOR "${Demo_VERSION_MAJOR}")10 11 set (CPACK_PACKAGE_VERSION_MINOR "${Demo_VERSION_MINOR}")12 13 include (CPack)
上面的代碼做了以下幾個(gè)工作:
導(dǎo)入 InstallRequiredSystemLibraries 模塊,以便之后導(dǎo)入 CPack 模塊;
設(shè)置一些 CPack 相關(guān)變量,包括版權(quán)信息和版本信息,其中版本信息用了上一節(jié)定義的版本號(hào);
導(dǎo)入 CPack 模塊。
接下來的工作是像往常一樣構(gòu)建工程,并執(zhí)行 cpack 命令。
生成二進(jìn)制安裝包:
cpack -C CPackConfig.cmake
生成源碼安裝包
cpack -C CPackSourceConfig.cmake
我們可以試一下。在生成項(xiàng)目后,執(zhí)行 cpack -C CPackConfig.cmake 命令:
1 [ehome@xman Demo8]$ cpack -C CPackSourceConfig.cmake 2 3 CPack: Create package using STGZ 4 5 CPack: Install projects 6 7 CPack: - Run preinstall target for: Demo8 8 9 CPack: - Install project: Demo810 11 CPack: Create package12 13 CPack: - package: /home/ehome/Documents/programming/C/power/Demo8/Demo8-1.0.1-Linux.sh generated.14 15 CPack: Create package using TGZ16 17 CPack: Install projects18 19 CPack: - Run preinstall target for: Demo820 21 CPack: - Install project: Demo822 23 CPack: Create package24 25 CPack: - package: /home/ehome/Documents/programming/C/power/Demo8/Demo8-1.0.1-Linux.tar.gz generated.26 27 CPack: Create package using TZ28 29 CPack: Install projects30 31 CPack: - Run preinstall target for: Demo832 33 CPack: - Install project: Demo834 35 CPack: Create package36 37 CPack: - package: /home/ehome/Documents/programming/C/power/Demo8/Demo8-1.0.1-Linux.tar.Z generated.
此時(shí)會(huì)在該目錄下創(chuàng)建 3 個(gè)不同格式的二進(jìn)制包文件:
1 [ehome@xman Demo8]$ ls Demo8-*2 3 Demo8-1.0.1-Linux.sh Demo8-1.0.1-Linux.tar.gz Demo8-1.0.1-Linux.tar.Z
這 3 個(gè)二進(jìn)制包文件所包含的內(nèi)容是完全相同的。我們可以執(zhí)行其中一個(gè)。此時(shí)會(huì)出現(xiàn)一個(gè)由 CPack 自動(dòng)生成的交互式安裝界面:
1 [ehome@xman Demo8]$ sh Demo8-1.0.1-Linux.sh 2 3 Demo8 Installer Version: 1.0.1, Copyright (c) Humanity 4 5 This is a self-extracting archive. 6 7 The archive will be extracted to: /home/ehome/Documents/programming/C/power/Demo8 8 9 If you want to stop extracting, please press <ctrl-C>.10 11 The MIT License (MIT)12 13 Copyright (c) 2013 Joseph Pan(http://hahack.com)14 15 Permission is hereby granted, free of charge, to any person obtaining a copy of16 17 this software and associated documentation files (the "Software"), to deal in18 19 the Software without restriction, including without limitation the rights to20 21 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of22 23 the Software, and to permit persons to whom the Software is furnished to do so,24 25 subject to the following conditions:26 27 The above copyright notice and this permission notice shall be included in all28 29 copies or substantial portions of the Software.30 31 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR32 33 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS34 35 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR36 37 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER38 39 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN40 41 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.42 43 Do you accept the license? [yN]:44 45 y46 47 By default the Demo8 will be installed in:48 49 "/home/ehome/Documents/programming/C/power/Demo8/Demo8-1.0.1-Linux"50 51 Do you want to include the subdirectory Demo8-1.0.1-Linux?52 53 Saying no will install in: "/home/ehome/Documents/programming/C/power/Demo8" [Yn]:54 55 y56 57 Using target directory: /home/ehome/Documents/programming/C/power/Demo8/Demo8-1.0.1-Linux58 59 Extracting, please wait...60 61 Unpacking finished successfully
完成后提示安裝到了 Demo8-1.0.1-Linux 子目錄中,我們可以進(jìn)去執(zhí)行該程序:
1 [ehome@xman Demo8]$ ./Demo8-1.0.1-Linux/bin/Demo 5 22 3 Now we use our own Math library.4 5 5 ^ 2 is 25