近几年美国对中国的科技封杀十分严重,说不定哪天Windows操作系统就不让我们用了。因此,在构建C/C++项目时应考虑跨平台,哪天不让用Windows了,可以请容易的将项目移植到Linux上。不同系统平台有不同的C/C++编译器,不同编译器有不同的构建规则,针对每个平台的不同编译器编写构建规则十分复杂,幸好有CMake可简化构建规则的编写,实现一次编写,不同平台适用。下面简单介绍CMake的使用。
CMake简介
代码变成可执行文件,叫做编译(compile);先编译这个,还是先编译那个(即编译的安排),叫做构建(build)。
Make是最常用的构建工具,诞生于1977年,主要用于C语言的项目。但是实际上 ,任何只要某个文件有变化,就要重新构建的项目,都可以用Make构建。
Make工具有很多,例如 GNU Make ,QT 的 qmake ,微软的 MS nmake,BSD Make(pmake),Makepp,等等。这些 Make 工具遵循着不同的规范和标准,所执行的 Makefile 格式也千差万别。这样就带来了一个严峻的问题:如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile ,这将是一件让人抓狂的工作。
CMake就是针对上面问题所设计的工具:它首先允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。从而做到“Write once, run everywhere”。显然,CMake 是一个比上述几种 make 更高级的编译配置工具。一些使用 CMake 作为项目架构系统的知名开源项目有 VTK、ITK、KDE、OpenCV、OSG 等。
CMake教程入门
给工程起个名字
语法:
1 | project(<PROJECT-NAME> [LANGUAGES] [<language-name>...]) |
该指令定义工程名称。例如:
1 | project(UtilTool) |
添加头文件目录INCLUDE_DIRECTORIES
语法:
1 | include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...]) |
它相当于g++选项中的-I参数的作用,也相当于环境变量中增加路径到CPLUS_INCLUDE_PATH变量的作用。例如:
1 | include_directories(${CMAKE_CURRENT_LIST_DIR}/include) |
当头文件分散在不同层次和深度的目录中时,逐个使用include_directories命令添加包含目录十分麻烦,可使用如下方法递归加载各个目录:
1 | MARO(HEADER_DIRECTORIES return_list) |
添加需要链接的库文件目录LINK_DIRECTORIES
语法:
1 | link_directories(directory1 directory2 ...) |
它相当于g++命令的-L选项的作用,也相当于环境变量中增加LD_LIBRARY_PATH的路径的作用。
1 | link_directories(${CMAKE_CURRENT_LIST_DIR}/lib) |
向当前工程添加存放源文件的子目录ADD_SUBDIRECTORY
ADD_SUBDIRECTORY用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。语法如下:
1 | ADD_SUBDIRECTORY(source_dir [binary_dir] |
上面的例子定义了将 src 子目录加入工程,并指定了编译输出路径为 bin 目录。如果不指定 bin 目录,那么编译的结果都将存放在 build/src 目录。
设置要链接的库文件的名称TARGET_LINK_LIBRARIES
语法:
1 | target_link_libraries(<target> [item1 [item2 [...]]] |
该指令的作用为将目标文件与库文件进行链接。例如:
1 | TARGET_LINK_LIBRARIES(utiltool-example utiltool) |
为工程生成目标文件
语法:
1 | add_executable(<name> [WIN32] [MACOSX_BUNDLE] |
例如:
1 | ADD_EXECUTABLE(utiltool-example examples/ConverterTest.cpp) |
为工程生成共享库
语法:
1 | add_library(<name> [STATIC | SHARED | MODULE] |
该指令的主要作用就是将指定的源文件生成链接文件,然后添加到工程中去。例如:
1 | ADD_LIBRARY(utiltool SHARED ${UTILTOOL_SOURCES}) |
为工程制作简单的安装脚本
语法:
1 | install(TARGETS targets... [EXPORT <export-name>] |
该命令为一个工程生成安装规则。TARGETS格式的install命令规定了安装工程中的目标(targets)的规则。有5中可以被安装的目标文件:ARCHIVE,LIBRARY,RUNTIME,FRAMEWORK,和BUNDLE。静态链接的库文件总是被当做ARCHIVE目标。模块库总是被当做LIBRARY目标。例如:
1 | install(TARGETS utiltool |
为工程设置变量
语法:
1 | set(<variable> <value> |
该指令用于给一般变量,缓存变量,环境变量赋值。例如:
1 | # set up versioning. |
为工程设置预定义宏
语法:
1 | add_definitions(-DFOO -DBAR ...) |
该指令添加编译参数。例如:
1 | # 添加WIN32宏定义 |
OPTION变量
语法:
1 | option(<option_variable> "help string describing option" |
该指令提供一个用户可以任选的选项,可在之后由用户通过CMake的GUI或者命令行进行更改。例如:
1 | OPTION(UTILTOOL_EXAMPLES "Build the examples" ON) |
修改默认的CMAKE_MODULE_PATH目录
CMAKE_MODULE_PATH是供find_package搜索第三方库用的。cmake的默认Modules目录在安装目录中:cmake-3.11.3-win64-x64\share\cmake-3.11\Modules。
如果要追加Modules目录,有3种方式:
1 | SET(CMAKE_MODULE_PATH "${OpenSceneGraph_SOURCE_DIR}/CMakeModules;${CMAKE_MODULE_PATH}") |
find_package用法
通常情况下,包含第三方库需要写以下内容:
1 | include_directories("${project_root_path}/include/") |
如果引用的很多个第三方库,那么类似上面的内容会写很多,且如果自己的多个项目都引用了某个第三方库,那么我每个项目的CmakeList.txt都得写一遍,重复劳动很多。那么有没办法为每个第三方库只定义一次它的头文件和库文件信息,然后在自己的工程中只指定名称即可?(类似编译Java的Maven仓库)答案是当然可以,find_package帮你解决。
find_package定义在自己工程的CmakeList.txt中:
1 | find_package( XXX CONFIG REQUIRED ) |
然后cmake就会在默认的Modules(即CMAKE_MODULE_PATH指定的目录)目录中搜索这个XXX第三方库。
搜索有两种模式:FindXXX.cmake和XXXConfig.cmake。前者叫做Module模式,后者叫做Config模式。
优雅的软件项目结构模板
完整简单示例
1 | # CMakeList.txt: UtilTool 的 CMake 项目,在此处包括源代码并定义 |
参考链接
- Linux平台编译安装测试JSBSim,by jackhuang.
- 干货:构建C/C++良好的工程结构,by Froser.
- 基于CMake构建系统的C++工程框架,by zhongxiao_yao.
- CMake 手册详解(十九)install指令,by SirDigit.
- CMake中变量总结,by 拾荒志.
- cmake使用教程(十)-关于file,by saka.
- Recursive CMake search for header and source files,by stackoverflow.
- CMake shared library in subdirectory,by stackoverflow.
- CMake 语言和语法,by leosocy.
- 【cmake】——include_directories 和target_include_directories的区别,by 大川搬砖.
- CMake—优雅地构建软件项目实践(1),by Ethan.
- CMake测试,by dxa572862121.
- CMake–Set用法,by narjaja.
- CMake之message()函数的使用和打印变量值,by hp_cpp.
- A minimal CMake project template,by Matt Morse.
- [Build]cmake常用配置项,by 玄冬Wong.
- 简单介绍Cmake生成VS工程中的ALL_BUILD、INSTALL、ZERO_CHECK作用!!,by 醉逍遥_祥.