文章翻譯自:CMake Tutorial
第一步 | 第二步 | 第三步 | 第四步 | 第五步 | 第六步 | 第七步
下面會一步一步的來教你如何使用CMake
完成軟件的構(gòu)建系統(tǒng)。大部分章節(jié)在Mastering CMake都有專門的介紹倡鲸,但是把他們整理在一起還是非常有用的证薇。本教程可以在CMake
的源碼目錄中的Tests/Tutorial目錄找到施禾。每一步都有它自己的子目錄,子目錄有完整的源代碼表制。
<a name="s1" id="s1"></a>
基本使用方法(第一步)
最常見的工程都是從源代碼文件構(gòu)建出可執(zhí)行程序。最簡單的CMakeLists.txt文件只需要兩行,我們就從這個文件開始抑党。CMakeLists 文件是這樣的。
cmake_minimum_required (VERSION 2.6)
project(Tutorial)
add_executable(Tutorial tutorial.cxx)
需要說明一下本例程中CMakeLists文件都使用小寫的命令撵摆。對于大寫底靠,小寫,大小寫混合的命令書寫方法特铝,CMake都支持暑中。tutorial.cxx的源碼用來計算一個數(shù)字的平方根,第一個版本非常簡單鲫剿,如下:
// 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) {
printf("Usage: %s number\n", argv[0]);
return 1;
}
double inputValue = atof(argv[1]);
double outputValue = sqrt(inputValue);
printf("The square root of %g is %g\n", inputValue, outputValue);
return 0;
}
假如CMakeList.txt和tutorial.cxx文件都存放在~/cmake_tutorial
目錄鳄逾,使用如下命令就可以構(gòu)建出tutorial執(zhí)行文件:
# mkdir ~/cmake_tutorial/build
# cd ~/cmake_tutorial/build
# cmake -G "Unix Makefiles" ..
# make
# ./Tutorial 9
The square root of 9 is 3
添加版本號和配置頭文件
我們給工程添加的第一個功能就是版本號。當然了牵素,你也可以專門修改源代碼严衬,但是通過CMakeLists有更大的靈活性。為了增加版本號笆呆,CMakeLists 文件如下:
cmake_minimum_required (VERSION 2.6)
project(Tutorial)
# The version number.
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
"${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
"${PROJECT_BINARY_DIR}/TutorialConfig.h"
)
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
include_directories("${PROJECT_BINARY_DIR}")
# add the executable
add_executable(Tutorial tutorial.cxx)
因為我們把配置文件寫入到了二進制目錄中请琳,所有,我們需要把二進制目錄加入到包含目錄
中赠幕。在源代碼目錄中俄精,我們新建TutorialConfig.h.in文件,內(nèi)容如下:
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
CMake 會使用 CMakeLists 文件中的Tutorial_VERSION_MAJOR
和 Tutorial_VERSION_MINOR
值替換配置文件的@Tutorial_VERSION_MAJOR@
和 @Tutorial_VERSION_MINOR@
榕堰。接下來竖慧,我們修改Tutorial.cxx
,讓它包含配置頭文件逆屡,使用版本號圾旨。最后的源代碼文件如下:
// A simple program that computes the square root of a number
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "TutorialConfig.h"
int main(int argc, char *argv[])
{
if (argc < 2) {
printf("%s Version %d.%d\n",
argv[0],
Tutorial_VERSION_MAJOR,
Tutorial_VERSION_MINOR);
printf("Usage: %s number\n", argv[0]);
return 1;
}
double inputValue = atof(argv[1]);
double outputValue = sqrt(inputValue);
printf("The square root of %g is %g\n", inputValue, outputValue);
return 0;
}
主要的修改就是包含了TutorialConfig.h
頭文件,在使用消息中打印了版本號魏蔗。
執(zhí)行的命令如下:
# cmake ..
# make
# ./Tutorial
Tutorial Version 1.0
Usage: Tutorial number
<a name="s2" id="s2"></a>
添加一個庫(第二步)
現(xiàn)在我們添加一個庫到工程中砍的。這個庫是我們自己實現(xiàn)的求平方根≥褐危可執(zhí)行文件使用我們自己的庫替換由編譯器提供的求平方根函數(shù)廓鞠。在這個教程中帚稠,我們把庫源文件放到MathFunctions
,這個單獨的目錄中床佳。只需要一行的CMakeLists就足夠了:
add_library(MathFunctions mysqrt.cxx)
mysqrt.cxx
文件只有一個函數(shù)叫mysqrt
滋早,mysqrt
和編譯器提供的sqrt
提供相同的功能。為了使用新庫砌们,我們在頂層的CMakeLists文件中調(diào)用add_subdirectory
杆麸,這樣庫才會被編譯。我們還需要把MathFunctions
目錄添加到包含目錄中浪感,這樣才能找到MathFunctions/mysqrt.h
文件角溃。最后,還需要把新庫添加到可執(zhí)行文件中篮撑。最終的CMakeLists的最后幾行是這樣的:
include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
add_subdirectory (MathFunctions)
# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial MathFunctions)
現(xiàn)在我們考慮使用MathFunctions
作為可選項。在本教程中匆瓜,沒有理由需要這么做赢笨,但是,對于更大的庫或依賴第三方代碼的庫驮吱,你可能就原因這么做了茧妒。第一步在頂層CMakeLists文件中添加一個選項:
# should we use our own math functions?
option (USE_MYMATH
"Use tutorial provided math implementation" ON)
這會在CMake GUI中顯示默認值為ON
的選項,用戶可以根據(jù)需要修改左冬。這個配置會存儲在緩存中桐筏,在用戶下次CMake工程時,不需要重新配置這個選項拇砰。下一個修改是梅忌,通過配置確定是否需要編譯、鏈接MathFunctions
除破。我們把頂層CMakeLists文件的最后幾行修改成這樣:
# add the MathFunctions library?
#
if (USE_MYMATH)
include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
add_subdirectory (MathFunctions)
set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial ${EXTRA_LIBS})
使用USE_MYMATH
配置來確定是否需要編譯和使用MathFunctions
牧氮。使用變量(本教程中的EXTRA_LIBS
)來收集可選庫,然后瑰枫,鏈接進可執(zhí)行文件中踱葛。這是一個通用的方法來保持有很多可選庫的大工程的清晰性。對源代碼的修改已經(jīng)很直截了當了:
// A simple program that computes the square root of a number
#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)
{
printf("%s Version %d.%d\n", argv[0],
Tutorial_VERSION_MAJOR,
Tutorial_VERSION_MINOR);
printf(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
printf("The square root of %g is %g\n",
inputValue, outputValue);
return 0;
}
在源碼中光坝,我們同樣使用了USE_MYMATH
尸诽。這是通過在TutorialConfig.h.in
配置文件中添加下面的內(nèi)容實現(xiàn)的:
#cmakedefine USE_MYMATH
<a name="s3" id="s3"></a>
安裝和測試(第三步)
在這一步中,我們給工程添加安裝規(guī)則和測試支持盯另。安裝規(guī)則已經(jīng)很明顯了性含。對于MathFunctions
庫,我們需要安裝庫文件和頭文件土铺,在MathFunctions/CMakeLists
中間添加下面兩行:
install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)
對于程序文件胶滋,我們在頂層CMakeLists文件中添加下面幾行來安裝可以執(zhí)行文件和配置文件:
# add the install targets
install (TARGETS Tutorial DESTINATION bin)
install (FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include)
到此已經(jīng)全部做完了“鬻蓿現(xiàn)在,你可以構(gòu)建本教程究恤,執(zhí)行make install
(或在IDE中構(gòu)建INSTALL
目標)俭令,將會安裝合適的頭文件、庫文件部宿、可執(zhí)行文件抄腔。CMake變量CMAKE_INSTALL_PREFIX
用來確定文件安裝的根目錄。添加測試也已經(jīng)很明顯了理张。在頂層CMakeLists文件的最后赫蛇,我們使用一些計算好的數(shù)字來驗證程序是否正確運行:
# does the application run
add_test (TutorialRuns Tutorial 25)
# does it sqrt of 25
add_test (TutorialComp25 Tutorial 25)
set_tests_properties (TutorialComp25
PROPERTIES PASS_REGULAR_EXPRESSION "25 is 5")
# does it handle negative numbers
add_test (TutorialNegative Tutorial -25)
set_tests_properties (TutorialNegative
PROPERTIES PASS_REGULAR_EXPRESSION "-25 is 0")
# does it handle small numbers
add_test (TutorialSmall Tutorial 0.0001)
set_tests_properties (TutorialSmall
PROPERTIES PASS_REGULAR_EXPRESSION "0.0001 is 0.01")
# does the usage message work?
add_test (TutorialUsage Tutorial)
set_tests_properties (TutorialUsage
PROPERTIES
PASS_REGULAR_EXPRESSION "Usage:.*number")
第一個測試簡單的驗證程序是否可以執(zhí)行,有沒有段錯誤或其他原因?qū)е碌谋罎⑽戆龋⑶曳祷刂禐椋拔蛟拧_@個是最基本的CTest
測試。接下來幾個測試都使用PASS_REGULAR_EXPRESSION
測試屬性來驗證程序的輸出是否滿足特定的規(guī)則织狐。這些測試案例都是用來驗證程序計算出的平方根是否是正確的和在錯誤參數(shù)時暂幼,是否打印出使用方法。如果移迫,你需要驗證更多的數(shù)字旺嬉,你可以考慮使用下面的宏:
#define a macro to simplify adding tests, then use it
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 (25 "25 is 5")
do_test (-25 "-25 is 0")
每次調(diào)用do_test
都會添加名稱、輸入厨埋、結(jié)果基于參數(shù)的測試案例邪媳。
<a name="s4" id="s4"></a>
添加系統(tǒng)內(nèi)省(第四步)
接下來,讓我們根據(jù)目標平臺是否支持某些特性荡陷,來增加一些代碼到我們的工程雨效。比如這個例子,我們通過判斷目標平臺是否支持log
和exp
函數(shù)废赞,來確定是否啟用代碼设易。當然了,幾乎所有的平臺都支持這些函數(shù)蛹头,但對于本教授假設(shè)它們是不太常見的顿肺。如果平臺有log
函數(shù),我們在mysqrt
中用于記錄平方根渣蜗。我們首先在最上層的CMakeLists.txt里面屠尊,使用CheckFunctionExists.cmake宏來檢測是否有這些行數(shù)。
# does this system provide the log and exp functions?
include (CheckFunctionExists)
check_function_exists (log HAVE_LOG)
check_function_exists (exp HAVE_EXP)
接著我們在TutorialConfig.h.in定義以下變量
// does the platform provide exp and log functions?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP
一定要在log
和exp
檢測完成后耕拷,才能使用configure_file
命令讼昆。configure_file
命令使用當前的設(shè)置生成配置文件。最后骚烧,我們根據(jù)平臺是否存在log
和exp
函數(shù)浸赫,來完成不同的mysqrt
函數(shù)
// if we have both log and exp then use them
#if defined (HAVE_LOG) && defined (HAVE_EXP)
result = exp(log(x)*0.5);
#else // otherwise use an iterative approach
. . .
<a name="s5" id="s5"></a>
添加一個生成的文件和文件生成器(第五步)
在本章節(jié)中闰围,我們將向你展示如何在軟件的構(gòu)建過程中添加一個生成的源文件。在這個例子中既峡,我們將把預(yù)先計算好平方根的表格添加到構(gòu)建過程中魔招,并且把表格編譯進軟件中作谚。為了完成這個例子,我們首先需要一個程序來生成表格。在MathFunctions的子目錄尚洽,新建文件MakeTable.cxx來生成表格刨沦。
// A simple program that builds a sqrt table
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main (int argc, char *argv[])
{
int i;
double result;
// make sure we have enough arguments
if (argc < 2)
{
return 1;
}
// open the output file
FILE *fout = fopen(argv[1],"w");
if (!fout)
{
return 1;
}
// create a source file with a table of square roots
fprintf(fout,"double sqrtTable[] = {\n");
for (i = 0; i < 10; ++i)
{
result = sqrt(static_cast<double>(i));
fprintf(fout,"%g,\n",result);
}
// close the table with a zero
fprintf(fout,"0};\n");
fclose(fout);
return 0;
}
另外记盒,這個表格是有效的C++代碼体啰,表格保存的文件名通過命令行參數(shù)傳遞。接著我們在MathFunctions的CMakeLists.txt文件中添加合適的命令來生成MakeTable可執(zhí)行文件卦方,并且在構(gòu)建過程中執(zhí)行它羊瘩。需要幾個命令來實現(xiàn)這個目的,如下所示:
# first we add the executable that generates the table
add_executable(MakeTable MakeTable.cxx)
# add the command to generate the source code
add_custom_command (
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
DEPENDS MakeTable
)
# add the binary tree directory to the search path for
# include files
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
# add the main library
add_library(MathFunctions mysqrt.cxx ${CMAKE_CURRENT_BINARY_DIR}/Table.h )
首先添加命令生成MakeTable的可執(zhí)行文件盼砍。然后添加自定義命令指定如何使用MakeTable生成Table.h文件困后。接著,通過把生成的文件Table.h添加到MathFunctions庫的源文件列表衬廷,我們讓CMake知道m(xù)ysqrt.cxx依賴生成的文件Table.h。我們還需要把當前二進制目錄添加到包含目錄列表中汽绢,所以mysqrt.cxx才能包含Table.h吗跋,并且在包含目錄中發(fā)現(xiàn)Table.h。當工程被構(gòu)建時宁昭,首先構(gòu)建MakeTable可執(zhí)行文件跌宛,接著運行MakeTable生成Table.h。最后积仗,編譯包含Table.h的mysqrt.cxx文件生成MathFunctions庫疆拘。這時,所有功能已完成的最上層的CMakeList.txt文件看起來是這樣的:
cmake_minimum_required (VERSION 2.6)
project (Tutorial)
include(CTest)
# The version number.
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
# does this system provide the log and exp functions?
include (${CMAKE_ROOT}/Modules/CheckFunctionExists.cmake)
check_function_exists (log HAVE_LOG)
check_function_exists (exp HAVE_EXP)
# should we use our own math functions
option(USE_MYMATH
"Use tutorial provided math implementation" ON)
# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
"${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
"${PROJECT_BINARY_DIR}/TutorialConfig.h"
)
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
include_directories ("${PROJECT_BINARY_DIR}")
# add the MathFunctions library?
if (USE_MYMATH)
include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
add_subdirectory (MathFunctions)
set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial ${EXTRA_LIBS})
# add the install targets
install (TARGETS Tutorial DESTINATION bin)
install (FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include)
# does the application run
add_test (TutorialRuns Tutorial 25)
# does the usage message work?
add_test (TutorialUsage Tutorial)
set_tests_properties (TutorialUsage
PROPERTIES
PASS_REGULAR_EXPRESSION "Usage:.*number"
)
#define a macro to simplify adding tests
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.in是這樣的:
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
#cmakedefine USE_MYMATH
// does the platform provide exp and log functions?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP
MathFunctions的CMakeLists.txt如下:
# first we add the executable that generates the table
add_executable(MakeTable MakeTable.cxx)
# add the command to generate the source code
add_custom_command (
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
DEPENDS MakeTable
COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
)
# add the binary tree directory to the search path
# for include files
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
# add the main library
add_library(MathFunctions mysqrt.cxx ${CMAKE_CURRENT_BINARY_DIR}/Table.h)
install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)
<a name="s6" id="s6"></a>
構(gòu)建安裝包(第六步)
接下來寂曹,假設(shè)我們想要把我們的工程分發(fā)給其他人哎迄,以便他們可以使用它。我們想要在不同的平臺分發(fā)二進制和源碼隆圆。這里的安裝和我們在之前的章節(jié)安裝和測試(第三步)中的是有些區(qū)別的漱挚,在第三步我們編譯源代碼后,直接安裝二進制文件渺氧。在本例子中旨涝,我們將要構(gòu)建二進制安裝包,可以在包管理系統(tǒng)中使用侣背,例如:cygwin白华,debian慨默,RPMs等等。我們使用CPack來創(chuàng)建平臺相關(guān)的安裝包弧腥。我們需要在最上層的CMakeLists.txt的尾部添加一些代碼:
# build a CPack driven installer package
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
開始。這個模塊會包含當前平臺需要使用的運行時庫鸟赫。接下來我們設(shè)置一些CPack
變量蒜胖,比如:版權(quán)許可文件目錄,軟件版本信息抛蚤。在本教程的開始部分台谢,我們已經(jīng)設(shè)置好了版本信息。最后我們包含CPack
模塊岁经,它會使用這些變量和其他的系統(tǒng)屬性去生成安裝包朋沮。
下一步,正常的構(gòu)建工程缀壤,然后運行CPack
樊拓。建立一個二進制分發(fā),你可以運行:
cpack --config CPackConfig.cmake
建立一個源碼分發(fā)塘慕,你可以運行:
cpack --config CPackSourceConfig.cmake
<a name="s7" id="s7"></a>
添加儀表盤(Dashboard)支持(第七步)
把我們的測試結(jié)果提交到儀表盤是非常簡單的筋夏。在本教程前面的步驟中,我們已經(jīng)定義了一些測試用例图呢。我們只需要運行這些測試用例条篷,并且提交結(jié)果到儀表盤。為了支持儀表盤蛤织,我們需要在最上層的CMakeLists.txt
中包含CTest
模塊赴叹。
# enable dashboard scripting
include (CTest)
我們需要新建CTestConfig.cmake
,在文件中定義本項目在儀表盤中的名稱指蚜。
set (CTEST_PROJECT_NAME "Tutorial")
CTest
在執(zhí)行時會讀取這個文件乞巧。你可以使用CMake
為你的項目創(chuàng)建一個簡單的儀表盤,把目錄切換到二進制目錄摊鸡,然后運行ctest -D Experimental
绽媒。你的儀表盤的結(jié)果會提交到Kitware的公共儀表盤這里