CMake簡介
CMake是一個跨平臺的事期、開源的構(gòu)建工具溃睹。cmake是makefile的上層工具丑婿,它們的目的正是為了產(chǎn)生可移植的makefile口予,并簡化自己動手寫makefile時的巨大工作量.目前很多開源的項目都可以通過CMake工具來輕松構(gòu)建工程娄周,例如博客之前分享的openHMD、hidapi沪停、OSVR-Core等等昆咽,代碼的分享者提供源代碼和相應(yīng)的Cmake配置文件,使用者就可以非常方便的在自己的電腦上構(gòu)建相應(yīng)的工程牙甫,進(jìn)行開發(fā)和調(diào)試掷酗。
CMake鏈接
第一步:一個簡單的起點
最簡單的工程就是將一些源文件編譯為可執(zhí)行程序。對于簡單工程來說窟哺,只需要在CMakeList.txt添加2行內(nèi)容即可泻轰。這就是我們這個教程的起點,CMakeLists.txt文件內(nèi)容如下:
cmake_minimum_required (VERSION 2.6)
project (Tutorial)
add_executable(Tutorial tutorial.cxx)
注意:CMake文件是不區(qū)分大小寫的且轨,這個例子中CMakeLists.txt文件中都使用小寫字母浮声。
源文件”tutorial.cxx”用來計算一個數(shù)的平方根虚婿,它的第一個版本非常簡單,如下所示:
#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;
}
添加版本號和配置頭文件
我們添加的第一個特性就是為我們的可執(zhí)行程序和工程提供一個版本號.你可以在源代碼中寫入版本號,然而在CMakeLists.txt中提供(版本號)更靈活.修改CMakeLists.txt
增加一個版本號,內(nèi)容如下:
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)
因為配置文件將會被寫入到二進(jìn)制樹當(dāng)中泳挥,我們需要把這個目錄添加到頭文件路徑當(dāng)中然痊。我們接著創(chuàng)建一個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@
當(dāng)CMakE配置這個頭文件是@Tutorial_VERSION_MAJOR@
和@Tutorial_VERSION_MINOR@
的值將會被CMakeLists.txt文件中的值替換屉符。
下一步我們修改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)
{
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;
}
上面代碼最主要的修改就是包含了TutorialConfig.h頭文件,并且打印出版本號矗钟。
第二步:添加一個庫
現(xiàn)在我們?yōu)槲覀兊墓こ烫砑右粋€庫唆香。這個庫包含了自己實現(xiàn)的一個用來計算數(shù)的平方根函數(shù)。應(yīng)用程序可以使用這個庫來計算平方根吨艇,而不是使用編譯器提供的標(biāo)準(zhǔn)庫躬它。教程里我們會把這個庫放到一個叫做MathFunctions
的子文件夾中。它包含一個只有一行內(nèi)容的CMakeLists.txt文件:
add_library(MathFunctions mysqrt.cxx)
源文件”mysqrt.cxx”有一個叫做mysqrt的函數(shù)东涡,它跟編譯器提供的sqrt函數(shù)類似冯吓。為了使用這個新庫,我們要在頂層的CMakeLists.txt中添加”add_subdirectory”調(diào)用,以便使這個庫能夠被編譯到.我們還需要添加另外一個包含文件夾以便找到包含在頭文件”MathFunctions/MathFunctions.h”中的函數(shù)原型.最后把新庫添加到應(yīng)用程序當(dāng)中,在頂層的CMakeLists.txt文件中添加如下內(nèi)容:
include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
add_subdirectory (MathFunctions)
# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial MathFunctions)
注意:這里需要自己手動在MathFunctions
文件夾里添加mysqrt.cxx
和MathFunctions.h
文件,這兩個文件里的代碼可以參考文章最后提供的附件疮跑。
現(xiàn)在讓我們把MathFunctions庫做成可選的.當(dāng)然在這個教程的工程中這個操作有點多余.但是在你的其他工程中,多數(shù)情況會有大量的依賴第三方的庫文件.添加一個可選庫,需要在最高層的CMakeLists.txt文件中添加一些內(nèi)容:
# should we use our own math functions?
option (USE_MYMATH
"Use tutorial provided math implementation" ON)
這個USE_MYMATH
將會顯示在CMake GUI當(dāng)中,并且默認(rèn)值為ON,這個設(shè)置將會被保存到CACHE當(dāng)中,所以用戶每次打開工程時不必重新配置.下一步需要做一些修改,以便使MathFunctions庫參與編譯和鏈接.這個修改是在頂層的CMakeLists.txt文件中,內(nèi)容如下:
# 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})
通過配置USB_MYMATH
來決定是否編譯和使用MathFunctions.
注意上面的EXTRA_LIBS
變量是用來收集每一個可選庫的,以便他們鏈接到可執(zhí)行程序中.這是一個保持大項目工程可選庫清潔的通用方法.相應(yī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)
{
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配置文件來為源文件這個提供支持,添加內(nèi)容如下:
#cmakedefine USE_MYMATH
第三步:安裝和測試
下一步我們將為我們的工程添加安裝規(guī)則和測試.安裝規(guī)則相當(dāng)簡單,為了安裝MathFunctions庫和頭文件,我們需要在MathFunctions文件夾的CMakeLists.txt文件中,添加如下內(nèi)容:
install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)
對于應(yīng)用程序,需要在最頂層的CMakeLists.txt文件中安裝可執(zhí)行程序和配置頭文件.
# add the install targets
install (TARGETS Tutorial DESTINATION bin)
install (FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include)
注意:上面這幾行內(nèi)容,需要添加在如下代碼之后,否則編譯會報錯,提示找不到Tutorial
# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial ${EXTRA_LIBS})
這就是所有要做的.到這里你應(yīng)該可以構(gòu)建這個教程了,然后輸入make install
(或則使用IDE編譯出INSTALL目標(biāo)),它將安裝適當(dāng)?shù)念^文件组贺、庫和可執(zhí)行程序。
CMake變量CMAKE_INSTALL_PREFIX
用來決定這些文件將被安裝的根路徑.
添加測試也是很簡單的.在頂層的CMakeLists.txt文件中添加一些簡單的測試,來確認(rèn)應(yīng)用程序工作正常祸挪。
include(CTest)
# 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")
在構(gòu)建完成之后需要運行”ctest”命令來運行上面的測試.第一個測試用例用來確保應(yīng)用程序是否有段錯誤,crash,返回值非0.這個是一個簡單的CTest測試.接下來的幾個測試都是利用”PASS_REGULAR_EXPRESSION”測試屬性來驗證輸出是否包含特定字符串.在這個例子當(dāng)中驗證平方根計算是否正確,以及當(dāng)輸入錯誤信息時候輸出使用信息.如果你希望添加更多測試來測試不同的輸入值,你可以考慮創(chuàng)建一個宏像如下這樣:
#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")
do_test的每一次調(diào)用,就會有一次測試被添加到工程當(dāng)中,測試的名字、輸入贞间、結(jié)果,基于傳遞的參數(shù).
第四步:添加系統(tǒng)自檢
下一步讓我們考慮添加一些代碼到我們的工程,以支持目標(biāo)平臺沒有的特性.在這個例子當(dāng)中,我們將添加一些代碼來驗證目標(biāo)平臺是否具有l(wèi)og和exp函數(shù).當(dāng)然幾乎所有的平臺都有這些函數(shù),教程假設(shè)一下這種少數(shù)情況.如果平臺有l(wèi)og函數(shù),那么我們在mysqrt函數(shù)中用它來計算輸出.第一步我們利用CheckFunctionExists.cmake宏測試這些函數(shù)是否有效,在頂層CMakeLists.txt文件中添加如下內(nèi)容:
# 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”來定義那些CMake在平臺上查找到的值,修改如下:
// does the platform provide exp and log functions?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP
在使用log和exp之前,確定他們是否被定義,是非常重要的.在CMake中,配置文件的命令會立刻使用目前的配置.最后在mysqrt函數(shù)中,我們可以提供一個基于log和exp函數(shù)的實現(xiàn),代碼如下:
// 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
. . .
注意:這里需要在”mysqrt.cxx”文件里添加頭文件#include “TutorialConfig.h”,否則找不到定義的宏贿条。
第5步:添加/生成一個通用的文件
這一節(jié)我們將演示如何添加一個源文件到一個應(yīng)用程序當(dāng)中.在這個例子當(dāng)中,我們將會創(chuàng)建一個已經(jīng)計算好的平方根表,它作為構(gòu)建過程的一部分,并且編譯進(jìn)我們的應(yīng)用程序.為了實現(xiàn)這個功能,我們需要一個程序來生成這個表.在MathFunctions子文件夾中我們添加一個叫做MakeTable.cxx的文件,它的內(nèi)容如下:
// 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ù)傳入的.下一步就是添加一個適當(dāng)?shù)拿畹組athFunctions的CMakeLists.txt文件來構(gòu)建MakeTable應(yīng)用程序,然后在構(gòu)建過程中運行它.完成這些操作,我們需要添加如下命令:
# 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 )
首先,可執(zhí)行程序MakeTable像所有可執(zhí)行程序一樣被添加.接著我們添加一些命令來讓MakeTable生成Table.h.然后我們讓CMake知道m(xù)ysqrt.cxx依賴于生成的Table.h.這需要為MathFunctions庫把生成的Table.h文件添加到源文件列表當(dāng)中,我們還需要添加當(dāng)前的二進(jìn)制文件夾添加到include文件夾列表當(dāng)中,以便Table.h文件可以被mysqrt.cxx找到并包含.當(dāng)工程被構(gòu)建時,它將會先構(gòu)建MakeTable可執(zhí)行程序.它將運行MakeTable來生成Table.h文件.最后,它編譯包含了Table.h文件的mysqrt.cxx來生成MathFunctions庫.為了達(dá)到這個目的,頂層的CMakeLists.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
包含如下內(nèi)容:
// 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文件,內(nèi)容如下:
# 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)
第6步:構(gòu)建一個安裝器
接著假設(shè)我們想把工程發(fā)布給其他人使用.我們希望提供不同平臺的二進(jìn)制文件和源文件.這里與我們之前在第三步中的安裝有一些不同,那時我們安裝我們已經(jīng)從源代碼構(gòu)建出來的二進(jìn)制文件.在這個例子中,我們將構(gòu)建一個安裝包,它支持二進(jìn)制安裝,以及cygwin、 debian增热、RPMs等工具的包管理特性.為了實現(xiàn)這個功能,我們會使用CPack來創(chuàng)建平臺相關(guān)的安裝包.我們需要在頂層的CMakeLists.txt文件中的最后添加一些代碼,內(nèi)容如下:
# 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)
官方教程中沒有License.txt文件,所有要手動在頂層目錄下添加一個”License.txt”文件
以上就是我們所有要做的.以包含InstallRequiredSystemLibraries為開始.這個模塊將包含工程在目前平臺上所需要的所有運行時庫.接著我們設(shè)置一些CPack變量,包括我們保存license的地方,工程的版本信息.版本信息就是用了我們教程中之前設(shè)置的變量值.最后我們包含CPake模塊,它將使用這些變量和一些系統(tǒng)的其他屬性來設(shè)置安裝包.
下一步是以通常的方法構(gòu)建工程,然后在CPack上運行它.為了構(gòu)建二進(jìn)制發(fā)行包,你需要運行如下命令:
cpack --config CPackConfig.cmake
為了創(chuàng)建一個源文件發(fā)行包,你需要運行:
cpack --config CPackSourceConfig.cmake
注意:這里的2條命令需要在構(gòu)建工程的目錄下運行!
第7步:添加對Dashboard支持
將我們的測試結(jié)果添加到Dashboard上是非常容易的.在教程的前面步驟當(dāng)中我們已經(jīng)為我們的工程定義了一些測試.我們只需要運行這些測試,并且提交他們到一個dashboard上.為了支持dashboard我們需要在頂層的CMakeLists.txt文件中包含CTest模塊.
# enable dashboard scripting
include (CTest)
我們還需要創(chuàng)建一個CTestConfig.cmake文件,用來為dashboard指定工程名字.
set (CTEST_PROJECT_NAME "Tutorial")
CTest運行時將讀取這個文件.你可以在你的工程中運行CMake來生成一個簡單的dashboard,進(jìn)入構(gòu)建目錄下,然后執(zhí)行”ctest -D Experimental”,將會把你的dashboard結(jié)果上傳到Kitware’s工程dashboard上.
參考
https://blog.csdn.net/dabenxiong666/article/details/53998998