CMake 手册
本文来源:http://www.cnblogs.com/coderfenghc/
目录:CMD#
CMake 用法导览
Preface : 本文是 CMake 官方文档 CMake Tutorial
(http://www.cmake.org/cmake/help/cmake_tutorial.html) 的翻译。通过一个样例工程
从简单到复杂的完善过程,文档介绍了 CMake 主要模块(cmake, ctest, cpack)的功能和使
用环境;从中可以一窥 cmake 的大体形貌。正文如下:
本文下述内容是一个手把手的使用指南;它涵盖了 CMake 需要解决的公共构建系统的
一些问题。这些主题中的许多主题已经在 Mastering CMake 一书中以单独的章节被介绍
过,但是通过一个样例工程看一看它们如何工作也是非常有帮助的。本指南可以在 CMake
源码树的 Tests/Tutorial 路径下找到。每一步都有它自己的子路径,其中包含该步骤的一
个完整的指南。
作为基础的起始点(步骤 1)
最基本的工程是一个从源代码文件中构建可执行文件的例子。对于简单工程,只要一个两行
的 CMakeLists 文件就足够了。这将会作为我们指南的起点。这份 CMakeLists 文件看起来像
是这样:
?
1
2
3
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
#include
#include
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 文件:
?
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 文件:
?
1
2
3
// 与 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 头文件然后使用其中的版本号。修改过的源代
码展列于下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 计算平方根的简单程序。
#include
#include
#include
#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;
23 }
引入库(步骤 2)
现在我们将会在我们的工程中引入一个库。这个库会包含我们自己实现的计算一个数的平方
根的函数。可执行文件随后可以使用这个库文件而不是编译器提供的标准开平方函数。在本指南
中,我们将会把库文件放到一个子目录 MathFunctions 中。它包含下述的单行 CMakeLists 文
件:
?
1 add_library(MathFunctions mysqrt.cxx)
源文件 mysqrt.cxx 有一个叫做 mysqrt 的函数,它提供了与编译器的 sqrt 函数类似的功
能。为了使用新的库,我们在顶层的 CMakeLists 中增加一个 add_subrirectory 调用,这样
这个库也会被构建。我们也要向可执行文件中增加另一个头文件路径,这样就可以从
MathFunctions/mysqrt.h 头文件中找到函数的原型。最后的一点更改是在向可执行文件中引
入新的库。顶层 CMakeLists 文件的最后几行现在看起来像是这样:
?
1
2
3
4
5
include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
add_subdirectory (MathFunctions)
# 引入可执行文件
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial MathFunctions)
现在,让我们考虑下让 MathFunctions 库变为可选的。在本指南中,确实没有必要这样画
蛇添足;但是对于更大型的库或者依赖于第三方代码的库,你可能需要这种可选择性。第一步是
为顶层的 CMakeLists 文件添加一个选项:
?
1
2
3
# 我们应该使用我们自己的数学函数吗?
option (USE_MYMATH
"Use tutorial provided math implementation" ON)
这将会在 CMake 的 GUI 中显示一个默认的 ON 值,并且用户可以随需改变这个设置。这
个设置会被存储在 cache 中,那么用户将不需要在 cmake 该工程时,每次都设置这个选项。
第二处改变是,让链接 MathFunctions 库变为可选的。要实现这一点,我们修改顶层
CMakeLists 文件的结尾部分:
?
1
2
3
4
5
6
7
8
9
# 添加 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)来收集所有以后会被连接到可执行文件中的可选的库。
这是保持带有许多可选部件的较大型工程干净清爽的一种通用的方法。源代码对应的改变相当直
白,如下所示:
?
1
2
3
4
5
6
7
8
9
// 计算一个数平方根的简单程序
#include
#include
#include
#include "TutorialConfig.h"
#ifdef USE_MYMATH
#include "MathFunctions.h"
#endif
10
int main (int argc, char *argv[])
11
{
12
13
14
15
16
if (argc < 2)
{
fprintf(stdout,"%s Version %d.%d\n", argv[0],
Tutorial_VERSION_MAJOR,
Tutorial_VERSION_MINOR);
17
18
19
fprintf(stdout,"Usage: %s number\n",argv[0]);
return 1;
}
20
21
double inputValue = atof(argv[1]);
22
23
24
#ifdef USE_MYMATH
double outputValue = mysqrt(inputValue);
25
#else
26
double outputValue = sqrt(inputValue);
27
#endif
28
29
30
31
fprintf(stdout,"The square root of %g is %g\n",
inputValue, outputValue);
return 0;
32
}
在源代码中,我们也使用了 USE_MYMATH。这个宏是由 CMake 通过 TutorialConfig.h.in
配置文件中的下述语句行提供给源代码的:
+ View Code?
1 #cmakedefine USE_MYMATH
安装与测试(步骤 3)
下一步我们会为我们的工程引入安装规则以及测试支持。安装规则相当直白,对于
MathFunctions 库,我们通过向 MathFunctions 的 CMakeLists 文件添加如下两条语句来设
置要安装的库以及头文件:
?
1
2
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)
1
2
3
4
这就是要做的全部;现在你应该可以构建 tutorial 工程了。然后,敲入命令 make install
(或者从 IDE 中构建 INSTALL 目标)然后它就会安装需要的头文件,库以及可执行文件 CMake
的变量 CMAKE_INSTALL_PREFIX 用来确定这些文件被安装的根目录。添加测试同样也只需
要相当浅显的过程。在顶层 CMakeLists 文件的的尾部补充许多基本的测试代码来确认应用程
序可以正确工作。
+ View Code?
1
2
3
4
5
6
7
8
9
10
11
12
13
# 应用程序是否运行?
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")
14
15
16
17
18
# 它是否可以处理较小的数字。
add_test (TutorialSmall Tutorial 0.0001)
set_tests_properties (TutorialSmall
PROPERTIES PASS_REGULAR_EXPRESSION "0.0001 is 0.01")
19
20
21
22
23
24
# 用法信息是否可用?
add_test (TutorialUsage Tutorial)
set_tests_properties (TutorialUsage
PROPERTIES
PASS_REGULAR_EXPRESSION "Usage:.*number")
第一个测试用例仅仅用来验证程序可以运行,没有出现段错误或其他的崩溃,并且返回值必
须是 0。这是 CTest 所做测试的基本格式。余下的几个测试都是用
PASS_REGULAR_EXPRESSION 测试属性来验证测试代码的输出是否包含有特定的字符串。
在本例中,测试样例用来验证计算得出的平方根与预定值一样;当指定错误的输入数据时,要打
印用法信息。如果你想要添加许多测试不同输入值的样例,你应该考虑创建如下所示的宏:
+ View Code?
#定义一个宏来简化添加测试的过程,然后使用它
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")
1
2
3
4
5
6
7
8
对于每个 do_test 宏调用,都会向工程中添加一个新的测试用例;宏参数是测试名、函数
的输入以及期望结果。
增加系统内省(步骤 4)
下一步,让我们考虑向我们的工程中引入一些依赖于目标平台上可能不具备的特性的代码。
在本例中,我们会增加一些依赖于目标平台是否有 log 或 exp 函数的代码。当然,几乎每个平
台都有这些函数;但是对于 tutorial 工程,我们假设它们并非如此普遍。如果该平台有 log 函
数,那么我们会在 mysqrt 函数中使用它去计算平方根。我们首先在顶层 CMakeLists 文件中
使用宏 CheckFunctionExists.cmake 测试这些函数的可用性:
+ View Code?
# 该系统提供 log 和 exp 函数吗?
include
(CheckFunctionExists.cmake)
check_function_exists (log HAVE_LOG)
check_function_exists (exp
HAVE_EXP)
1
2
下一步,如果 CMake 在对应平台上找到了它们,我们修改 TutorialConfig.h.in 来定义这
些值;如下: