翻译自cmake-tutorial. 注意:翻译不一定准确,内容仅供参考!
A Basic Starting Point (Step1)
一个最基本的项目是由一些源代码文件构建得到的可执行文件,对于一个简单的项目,只需要一个三行的CMakeLists.txt
文件,这个文件也将是本教程的出发点。这样一个CMakeLists.txt
文件像下面这样:
1 | cmake_minimum_required (VERSION 2.6) |
注意这个例子中的CMakeLists.txt
文件使用了小写字母的命令,CMake
支持大写、小写、混合字母的命令。源代码tutorial.cxx
计算一个数字的平方根,第一个版本非常简单,如下所示:
1 | // A simple program that computes the square root of a number |
Adding a Version Number and Configured Header File
这里要添加的第一个特性是为可执行文件和项目提供一个版本号,虽然这可以在源代码中完成,但在CMakeLists.txt
中添加更加具有灵活性,只需简单修改CMakeLists.txt
文件便可以添加一个版本号,这里将其做如下修改:
1 | cmake_minimum_required (VERSION 2.6) |
由于配置文件将被写入二进制树(binary tree),所以必须将该文件路径添加到搜索路径中以包含这些文件。然后在源代码树(source tree)中创建一个包含以下内容的TutorialConfig.h.in
文件:
1 | // the configured options and settings for Tutorial |
当CMake
配置该头文件时,@Tutorial_VERSION_MAJOR@
和@Tutorial_VERSION_MINOR@
的值将会被替换为CMakeLists.txt
文件中定义的值,接下来在tutorial.cxx
文件中包含配置头文件以使用版本号,修改后的源代码如下所示:
1 | // A simple program that computes the square root of a number |
主要的改变是添加了头文件TutorialConfig.h
的包含,并将版本号作为使用信息的一部分输出到屏幕。
Adding a Library (Step 2)
现在需要添加一个库到项目中,这个库包含了平方根计算的实现,可执行文件便可以使用这个库,而不去使用编译器提供的平方根计算函数。本教程将把这个库放在一个叫MathFunctions
的子文件夹中,CMakeLists.txt
文件需要有下面这样一行:1
add_library(MathFunctions mysqrt.cxx)
源文件mysqrt.cxx
有一个叫mysqrt
的函数,拥有跟编译器自带的sqrt
函数同样的功能,为了使用这个新库,需要在CMakeLists.txt
文件的上一层添加add_subdirectory
的调用,以让该库得到构建。此外还添加一个包含路径来让该函数能够找到MathFunctions/MathFunctions.h
头文件,最后一点需要改变的是添加这个新的库到可执行文件。现在CMakeLists.txt
文件中的最后几行如下所示:1
2
3
4
5
6include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
add_subdirectory (MathFunctions)
# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial MathFunctions)
接下来考虑将MathFunctions
库设置为可选的,在本教程中虽然没有必要这样做,但在使用更大的库,或者库依赖第三方代码时可能需要这样去做。首先需要在CMakeLists.txt
文件的前面添加一个选项。
1 | # should we use our own math functions? |
这个选项将在CMake GUI
中出现,且默认值为ON
,并且用户可以自己修改这个值。这个设置会被存储在缓存中,所以用户不用在每次用CMake
运行这个项目时都来设置。接下来需要进一步将关于MathFunctions
库的构建和链接设置为可选,只需将CMakeLists.txt
文件按如下示例更改:1
2
3
4
5
6
7
8
9
10
11# 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
来收集所有可选的库来在后面链接到可执行文件,这是一个保持有许多可选部件的大项目干净的通用方法。源代码对应的改变非常直观明了:
1 | // A simple program that computes the square root of a number |
在源代码中同样使用USE_MYMATH
,它是CMake
通过配置文件TutorialConfig.h.in
提供给源代码的,只需在配置文件中添加下面一行:1
#cmakedefine USE_MYMATH
Installing and Testing (Step 3)
接下来添加安装规则(Install Rules)和测试(Testing)支持到项目中,安装规则非常直接明了,对于MathFunctions
库,可以通过在MathFunctions
的CMakeLists.txt
文件中添加下面两行代码来安装库和头文件:1
2install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)
对于应用程序,通过在CMakeLists.txt
文件的前面添加下面几行来安装可执行文件和配置头文件:1
2
3
4# add the install targets
install (TARGETS Tutorial DESTINATION bin)
install (FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include)
这便是全部内容,现在便可以构建这个教程,然后输入make install
或者从IDE
构建INSTALL
目标,便可以安装合适的头文件、库文件和可执行文件。CMake
变量CMAKE_INSTALL_PREFIX
将决定这些文件安装的根目录。
添加测试同样也是一个非常简单直接的过程,通过在CMakeLists.txt
文件的末尾添加一些基础测试来检验应用程序是否正常工作。
1 | include(CTest) |
构建以后在命令行工具中输入ctest
便可以运行所有测试,第一个测试验证程序是否能够正常运行,不会出现段错误(segfault)或者崩溃(crash),能够返回0
,这是CTest
测试最基本的形式。接下来的几个测试适当地使用PASS_REGULAR_EXPRESSION
测试属性来验证测试的输出结构是否包含特定的字符串。在这个例子中即验证计算的平方根是否正确,当提供错误的参数时将使用信息打印到屏幕。如果需要添加许多测试来检测不同的输入值,可以考虑定义一个下面这样的宏:1
2
3
4
5
6
7
8
9
10#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
的每一次调用,一个新的测试便被添加到项目中,名字、输入、结果便是传入的相关参数。
Adding System Introspection (Step 4)
下面考虑向项目中添加一些代码,而这些代码取决于目标平台可能没有的特性。对于这个例子,这里将根据目标平台是否有log
和exp
函数来添加一些代码,当然,几乎所有的平台都包含这两个函数,但这个教程假设目标平台不是常见的那些。如果平台有log
函数,则直接在mysqrt
函数中使用这个函数计算平方根。这里首先在CMakeLists.txt
文件中利用CheckFunctionExists.cmake
宏来测试这些函数的可用性:1
2
3
4# 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
在平台找到这些函数则定义相应的值,如下所示:1
2
3// does the platform provide exp and log functions?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP
很重要的一点,函数log
和exp
的可用性测试需要在TutorialConfig.h
文件的configure_file
命令调用前完成,configure_file
命令立即在CMake
中用当前的设置配置文件。最后在mysqrt
函数中添加一个基于log
和exp
函数的替代实现,如果系统可以获取这两个函数,具体代码如下:1
2
3
4
5// if we have both log and exp then use them
result = exp(log(x)*0.5);
. . .
Adding a Generated File and Generator (Step 5)
这一小节介绍怎样在一个应用程序的构建过程中添加生成的源代码,这个例子将创建一个表,表中存储一些预先计算好的平方根作为构建过程的一部分,然后将这个表编译进应用程序。首先需要一个程序来生成这个表,在MathFunctions
子文件夹,一个叫MakeTable.cxx
的新的源文件会完成这个工作:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36// A simple program that builds a sqrt table
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++
代码生成的,输出文件的文件名由传入的参数确定。接下来要添加合适的命令到MathFunctions
的CMakeLists.txt
文件中来构建MakeTable
可执行文件,然后将其作为构建过程的一部分。下面是完成这些所需要的一些命令:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# 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
的可执行文件跟其他可执行文件的添加方式一样,然后添加一个自定义命令(custom command)来具体说明怎样通过运行MakeTable
来生成Table.h
,接下来必须让CMake
知道mysqrt.cxx
依赖于生成的Table.h
文件,具体通过将生成的Table.h
文件添加到MathFunctions
库的源文件代码列表中完成。此外还必须将当前的二进制路径添加到包含文件的列表中,以让Table.h
文件能够被mysqrt.cxx
找到并包含。当这个项目被构建时,首先会构建MakeTable
可执行文件,然后运行MakeTable
生成Table.h
,最后再编译包含有Table.h
的mysqrt.cxx
来生成MathFunctions
库。现在拥有这些已添加的特性的CMakeLists.txt
文件如下所示:
1 | cmake_minimum_required (VERSION 2.6) |
TutorialConfig.h.in
文件如下:1
2
3
4
5
6
7
8// 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
文件如下所示:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# 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)
Building an Installer (Step 6)
接下来假设需要将项目发布给其他人使用,需要在众多平台提供二进制和源代码发布包,这和在前面第三节讲的安装从源代码构建得到的二进制文件还有一点区别。这个例子将讲解如何构建安装包,并支持二进制安装和包管理这些在cygwin
、debian
、RPMs
等平台都拥有的特性。为了达到这个目的,这里使用CPack
来生成各个平台的安装程序,在Chapter Packaging with CPack有介绍。具体来说,在CMakeLists.txt
文件的底部需要加下面几行代码:1
2
3
4
5
6
7# 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
,这个模块将包含项目在当前平台所需要的所有运行库(runtime libraries),接下来设置一些CPack
变量,包括许可协议和项目的版本信息,版本信息利用教程前面设置的变量。最后再包含CPack
模块,CPack
将使用这些变量和当前系统的一些特性来设置安装程序。
下一步便使用通常的方法构建项目,然后运行CPack
。要构建一个二进制发布包(binary distribution)使用如下命令:1
cpack --config CPackConfig.cmake
要生成一个源代码发布包(source distribution)需要输入如下命令:1
cpack --config CPackSourceConfig.cmake
Adding Support for a Dashboard (Step 7)
增加提交测试结果到仪表板(dashboard)的支持非常简单,前面的教程已经在项目中定义了许多测试,现在只需运行这些测试然后提交到仪表板。为了添加仪表板的支持,首先需要在CMakeLists.txt
文件的前面包含CTest
模块:1
2# enable dashboard scripting
include (CTest)
然后创建一个CTestConfig.cmake
文件,并在里面设置这个项目在仪表板中的名字:1
set (CTEST_PROJECT_NAME "Tutorial")
CTest
在运行时会读入这个文件。如果想创建一个简单的仪表板,可以在项目中运行CMake
,然后将路径切换到二进制树目录,运行ctest –D Experimental
命令,仪表板的结果会被上传到Kitware
的公共仪表板。