CMake
理论上来说,任意一个c++程序都可以使用g++来编译,但当程序规模越来越大时,一个工程可能有许多的文件夹和源文件,这时输入的编译命令将会越来越长。因此,对于C++使用一些工程管理工具会更加高效。这里我们使用CMake。
在一个CMake工程中,我们会使用cmake命令生成一个Makefile文件,然后,用make命令根据这个 makefile文件的内容来编译整个工程。
入门案例:单个源文件
- 编写 CMakeLists.txt
首先编写 CMakeLists.txt 文件,并保存在与 main.cc 源文件同个目录下:
# 声明要求的cmake最低版本
cmake_minimum_required (VERSION 2.8)
# 声明一个cmake工程
project (HelloSLAM)
# 添加一个可执行程序
#语法 :add_executable(程序名 源代码文件)
add_executable(Demo main.cc)
CMakeLists.txt 文件用于告诉cmake我们要对这个目录下的文件做什么事情,CmakeLists.txt 文件的内容需要遵守cmake的语法。
编译项目
在当前目录执行 cmake . ,得到 Makefile 后再使用 make 命令编译得到 Demo1 可执行文件。
cmake .
得到Makefile后再使用make
命令编译得到Demo1可执行文件
多个源文件
上面的例子只有单个源文件,现在假如把power
函数单独写进一个名为MathFunctions
的源文件里,使得这个工程变成如下形式:
./Demo2
|
+--- main.cc
|
+--- MathFunctions.cc
|
+--- MathFunctions.h
这个时候,CMakeLists.txt 可以改成如下的形式:
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo2)
# 指定生成目标
add_executable(Demo main.cc MathFunctions.cc)
唯一的改动只是在 add_executable
命令中增加了一个 MathFunctions.cc
源文件。这样写当然没什么问题,但是如果源文件很多,把所有源文件的名字都加进去将是一件烦人的工作。更省事的方法是使用aux_source_directory
命令,该命令会查找指定目录下的所有源文件,然后将结果存进指定变量名。其语法如下:
aux_source_directory(<dir> <variable>)
因此,可以修改 CMakeLists.txt 如下:
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo2)
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
# 指定生成目标
add_executable(Demo ${DIR_SRCS})
这样,CMake 会将当前目录所有源文件的文件名赋值给变量DIR_SRCS
,再指示变量 DIR_SRCS
中的源文件需要编译成一个名称为Demo
的可执行文件。
多个目录,多个源文件
现在进一步将 MathFunctions.h
和 MathFunctions.cc
文件移动到 math 目录下。
./Demo3
|
+--- main.cc
|
+--- math/
|
+--- MathFunctions.cc
|
+--- MathFunctions.h
对于这种情况,需要分别在项目根目录 Demo3 和 math 目录里各编写一个 CMakeLists.txt 文件。为了方便,我们可以先将 math 目录里的文件编译成静态库再由 main 函数调用。
根目录中的 CMakeLists.txt :
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project(Demo3)
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(.DIR_SRCS)
# 添加 math 子目录
add_subdirectory(math)
# 指定生成目标
add_executable(Demo main.cc)
# 添加链接库
target_link_libraries(Demo MathFunctions)
该文件添加了下面的内容: 第3行,使用命令 add_subdirectory
指明本项目包含一个子目录 math
,这样 math
目录下的CMakeLists.txt
文件和源代码也会被处理 。第6行,使用命令 target_link_libraries
指明可执行文件 main
需要连接一个名为 MathFunctions
的链接库 。
子目录中的 CMakeLists.txt:
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
aux_source_directory(. DIR_LIB_SRCS)
# 生成链接库
add_library (MathFunctions ${DIR_LIB_SRCS})
在该文件中使用命令 add_library
将 src
目录中的源文件编译为静态链接库。
自定义编译选项
CMake 允许为项目增加编译选项,从而可以根据用户的环境和需求选择最合适的编译方案。
例如,可以将 MathFunctions 库设为一个可选的库,如果该选项为 ON ,就使用该库定义的数学函数来进行运算。否则就调用标准库中的数学函数库。
修改 CMakeLists 文件
第一步是在顶层的 CMakeLists.txt 文件中添加该选项:
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo4)
# 加入一个配置头文件,用于处理 CMake 对源码的设置
configure_file (
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
# 是否使用自己的 MathFunctions 库
option (USE_MYMATH
"Use provided math implementation" ON)
# 是否加入 MathFunctions 库
if (USE_MYMATH)
include_directories ("${PROJECT_SOURCE_DIR}/math")
add_subdirectory (math)
set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
# 指定生成目标
add_executable(Demo ${DIR_SRCS})
target_link_libraries (Demo ${EXTRA_LIBS})
其中:
- 第7行的
configure_file
命令用于加入一个配置头文件 config.h ,这个文件由 CMake 从 config.h.in 生成,通过这样的机制,将可以通过预定义一些参数和变量来控制代码的生成。 - 第13行的
option
命令添加了一个USE_MYMATH
选项,并且默认值为ON
。 - 第17行根据
USE_MYMATH
变量的值来决定是否使用我们自己编写的 MathFunctions 库。
修改 main.cc文件
之后修改 main.cc]文件,让其根据 USE_MYMATH
的预定义值来决定是否调用标准库还是 MathFunctions 库:
#include
#include
#include "config.h"
#ifdef USE_MYMATH
#include "math/MathFunctions.h"
#else
#include
#endif
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]);
#ifdef USE_MYMATH
printf("Now we use our own Math library. \n");
double result = power(base, exponent);
#else
printf("Now we use the standard library. \n");
double result = pow(base, exponent);
#endif
printf("%g ^ %d is %g\n", base, exponent, result);
return 0;
}
编写 config.h.in 文件
上面的程序值得注意的是第2行,这里引用了一个 config.h 文件,这个文件预定义了 USE_MYMATH
的值。但我们并不直接编写这个文件,为了方便从 CMakeLists.txt 中导入配置,我们编写一个 config.h.in文件,内容如下:
#cmakedefine USE_MYMATH
这样 CMake 会自动根据 CMakeLists 配置文件中的设置自动生成 config.h 文件
编译项目
现在编译一下这个项目,为了便于交互式的选择该变量的值,可以使用 ccmake 命令