CMake的应用与实践
1.认识CMake及应用
CMake是什么?
全称Cross Platform Make,起初为了跨平台需要,而后不断完善并广泛使用。
一款优秀的工程构建工具
特点及优势:
开放源码
跨平台,支持Linux,Mac和Windows等
编译语言简单,易用,简化编译构建过程和编译过程
编程高效,可扩展
CMake与其他编译工具的对比
GCC
由GNU开发的编程语言译器,C/C++,Java等语言开发
当项目简单,可以gcc/g++编译目标和项目
但比较复杂时,只用gcc组织编译架构变得极其困难
Makefile
Makefile是有条理的gcc编译命令的文件,利用make工具来执行Makefile文件的编译指令
当程序简单,可以手写Makefile
当程序复杂时,一般利用CMake和autotools来自动生成Makefile
Autotools
autotools是一个工具集,具有灵活性较大,对用户角度使用较为友好
开发步骤太,配置繁琐
通常编译的./configure文件,大多通过由autotools构建的,最终生成Makefile和config.h文件
CMake
Cmake类似Make工具功能,用来读取并执行CMakeList.txt文件的语句,最终生成Makefile文件
Cmake语言开发相对简单,易于理解
目前很多项目正在抛弃Autotools,qmake等,转而采用cmake
2.CMake实战
1.同一个目录,单个源文件
工程结构如下:
1 | ./Demo1 |
对应Cmake文件如下:
1 | #CMake 最低版本号要求 |
CMakeLists.txt 的语法比较简单,由命令、注释和空格组成,其中命令是不区分大小写的。符号 #
后面的内容被认为是注释。命令由命令名称、小括号和参数组成,参数之间使用空格进行间隔。
对于上面的 CMakeLists.txt 文件,依次出现了几个命令:
cmake_minimum_required
:指定运行此配置文件所需的 CMake 的最低版本;project
:参数值是Demo1
,该命令表示项目的名称是Demo1
。add_executable
: 将名为 main.cc 的源文件编译成一个名称为 Demo 的可执行文件。
编译项目
之后,在当前目录执行 cmake .
,得到 Makefile 后再使用 make
命令编译得到 Demo1 可执行文件。
其实说白了,cmake是为了生成makefile文件,再用make指令来执行makefile文件,即编译
用cmake就可以简化写makefile的过程。
2.同一个目录,多个源文件
工程结构如下:
1 | ./Demo2 |
对应Cmake如下:
1 | # CMake 最低版本号要求 |
唯一的改动只是在 add_executable
命令中增加了一个 MathFunctions.cc
源文件。这样写当然没什么问题,但是如果源文件很多,把所有源文件的名字都加进去将是一件烦人的工作。更省事的方法是使用 aux_source_directory
命令,该命令会查找指定目录下的所有源文件,然后将结果存进指定变量名。
1 | aux_source_directory(<dir> <variable>) |
因此,可以修改 CMakeLists.txt 如下:
1 | # CMake 最低版本号要求 |
3.多个目录,多个源文件
工程结构如下:
1 | ./Demo3 |
对于这种情况,需要分别在项目根目录 Demo3 和 math 目录里各编写一个 CMakeLists.txt 文件。为了方便,我们可以先将 math 目录里的文件编译成静态库再由 main 函数调用。
根目录中的 CMakeLists.txt :
1 | # CMake 最低版本号要求 |
该文件添加了下面的内容: 第3行,使用命令 add_subdirectory
指明本项目包含一个子目录 math,这样 math 目录下的 CMakeLists.txt 文件和源代码也会被处理 。第6行,使用命令 target_link_libraries
指明可执行文件 main 需要连接一个名为 MathFunctions 的链接库 。
子目录中的 CMakeLists.txt:
1 | # 查找当前目录下的所有源文件 |
在该文件中使用命令 add_library
将 src 目录中的源文件编译为静态链接库。
3.自定义编译选项
CMake 允许为项目增加编译选项,从而可以根据用户的环境和需求选择最合适的编译方案。
例如,可以将 MathFunctions 库设为一个可选的库,如果该选项为 ON
,就使用该库定义的数学函数来进行运算。否则就调用标准库中的数学函数库。
修改 CMakeLists 文件
我们要做的第一步是在顶层的 CMakeLists.txt 文件中添加该选项:
1 | # CMake 最低版本号要求 |
configure_file
命令用于加入一个配置头文件 config.h ,这个文件由 CMake 从 config.h.in 生成,通过这样的机制,将可以通过预定义一些参数和变量来控制代码的生成。option
命令添加了一个 USE_MYMATH
选项,并且默认值为 ON
。根据 USE_MYMATH
变量的值来决定是否使用我们自己编写的 MathFunctions 库。
修改 main.cc 文件
之后修改 main.cc 文件,让其根据 USE_MYMATH
的预定义值来决定是否调用标准库还是 MathFunctions 库:
1 |
|
编写 config.h.in 文件
这个文件预定义了 USE_MYMATH
的值。但我们并不直接编写这个文件,为了方便从 CMakeLists.txt 中导入配置,我们编写一个 config.h.in 文件,内容如下:
1 |
这样 CMake 会自动根据 CMakeLists 配置文件中的设置自动生成 config.h 文件。
4.库
在 Linux 中,库文件分成静态库和共享库两种x。静态库以.a 作为后缀名,共享库以.so 结尾。所有库都是一些函数打包后的集合,差别在于静态库每次被调用都会生成一个副本, 而共享库则只有一个副本,更省空间。
生成静态库:
1 | add_library( hello libHelloSLAM.cpp ) |
生成动态库:
1 | add_library( hello_shared SHARED libHelloSLAM.cpp ) |
此时得到的文件是 libhello_shared.so 了。
库文件是一个压缩包,里头带有编译好的二进制函数。不过,仅有.a 或.so 库文件的 话,我们并不知道它里头的函数到底是什么,调用的形式又是什么样的。为了让别人(或 者自己)使用这个库,我们需要提供一个头文件,说明这些库里都有些什么。因此,对于库 的使用者,只要拿到了头文件和库文件,就可以调用这个库了。下面我们来写 libhello 的
头文件。