【cpp查漏补缺】3-CMake入门
前言
上一节 介绍了Makefile的用法,但由于Makefile编写过于复杂,于是CMake工具出现了。CMake用法比Makefile更为简单,其作用是生成Makefile文件。除此之外,CMake允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化Makefile和工程文件。本文将介绍CMake的基本用法。
由于CMake的功能十分强大,本篇文章采用增量式更新的形式(即:作者用到的时候才进行更新)。
CMake常用函数一览
这里先扔出一大堆常见函数,先了解其用法,留下一个印象,之后在后面具体实操。
编译相关
1 | # 基本信息设置相关 |
1 | # 添加源代码相关 |
1 | # 库与链接相关 |
1 | # 头文件管理相关 |
1 | # 项目配置相关 |
安装相关
这里仅放置一些常见用法,具体见此 。
1 | # install 命令用于处理在CMake中生成的一系列目标 |
测试相关
待补充
特殊内建变量
更多内建变量请查阅官方文档 ,下面列出常用的内建变量。
CMAKE_SOURCE_DIR
:处理源代码时,整个项目的最顶层目录
PROJECT_SOURCE_DIR
:处理源代码时,最后一个调用Project()的CMakeLists.txt所在的目录
CMAKE_CURRENT_SOURCE_DIR
:处理源代码时,当前CMakeLists.txt所在目录
CMAKE_BINARY_DIR
:构建过程中,整个项目的顶层目录
CMAKE_LIBRARY_OUTPUT_DIRECTORY
:动态链接库的输出目录,默认目录为pwd
CMAKE_ARCHIVE_OUTPUT_DIRECTORY
:静态链接库的输出目录,默认目录为pwd
CMAKE_RUNTIME_OUTPUT_DIRECTORY
:目标文件的输出目录,默认目录为pwd
CMAKE_CXX_FLAGS
:gcc的编译选项,如-O1 -g -Wall
等等附加选项
CMAKE_CXX_STANDARD
:c++的标准
CMAKE_CXX_STANDARD_REQUIRED
:是否强制需要,True或False
举例:我在./build
中执行cmake ..
,而根目录有CMakeLists.txt
,那么CMAKE_SOURCE_DIR
为.
,CMAKE_BINARY_DIR
为./build
如果还是不明白,可以使用message()
函数进行打印。
初探CMake
这里沿用我们在上一节 中用到的项目,目录结构如下:
1 | . |
其次,我们应该如何使用CMake呢?首先创建名为CMakeLists.txt
的文件,然后开始编写内容。
基本信息维护
首先,我们应该考虑加入基本信息:
1 | cmake_minimum_required(VERSION 3.0) # 用于规定 cmake 工具的最低版本 |
这里我们用了几个函数,我们现在来一一介绍。
cmake_minimum_required
:对CMake的最小版本要求。值得注意的是,如果版本设置过低,其会因为兼容性的原因而不允许进行构建。
project
:定义项目名,后面可以跟版本号
set()
:这一函数用于设置变量名。值得注意的是,我们经常使用这个函数修改内建变量,以便于我们项目的构建。
配置选项
接下来,我们进行一些配置选项的设定。
1 | # 配置 |
1 | // ${PROJECT_SOURCE_DIR}/config/conf.h.in |
其中我们主要用到了configure_file() 函数。这个函数提供了一些可能的配置,第一个参数接收配置文件的路径,第二个参数接收生成头文件的路径。
下面我们重点介绍了一下配置文件的编写。首先,我们使用#cmakedefine
,来定义一个可能的宏。注意两个宏定义有所区分:
> #cmakedefine ECHO
:这一个宏定义和option函数对应,需要注意的是option和这里的宏名称要一模一样。option函数有两个取值:ON和OFF。如果为OFF,那么生成的头文件config.h
会被替换成/* #undef ECHO */
,否则会替换成#define ECHO
.
> #cmakedefine var1 "@var2@"
:注意这个宏定义,宏名称必须和CMake中设置的变量一样。其次,宏定义的内容可以任意取值。但比较特殊的是用"@
和@"
括起来的CMake定义过的变量。为加以区分,我们用var1和var2来表述。如果var1在CMake中被set或option为ON的话,就会使用var2定义的宏。但如果var2被"@
和@"
括起来的话,var2则会替换成CMake定义过的变量。
最后还需要留意两个地方:有一个是if-else-endif的控制结构。另外一个是内建变量${PROJECT_SOURCE_DIR}
,这个变量的含义请查阅《特殊内建变量》一节。
下面是configure_file()
函数生成的config.h
的内容:
1 |
在这里,也许
#cmakedefine var1 "@var2@"
的含义还是不太明确。下面给出一些示例。
1 | # case 1: |
Q:为什么我修改了OPTION的定义,但输出的头文件没有变化?
A:注意到CMake构建后产生的CMakeCache.txt文件,不难找到有:
1 ECHO:BOOL=OFF对的,如果你不清除这个文件,下次构建时就不会依照你的OPTION设定的选项,而是直接从CMakeCache.txt里面读取结果。CMakeCache.txt还包含了众多的内建变量。值得一提的是,
set()
设定的变量不会被包括进来。
添加源文件
下一步,我们需要添加源文件了:
1 | # ./CMakeLists.txt |
1 | #./src/calc/CMakeLists.txt |
这里需要强调的是,这两份配置来自于两个CMakeLists.txt。第一份来自于根目录,而第二份来自于src/calc
。下面介绍三个函数的作用:
add_subdirectory(path)
:将指定路径path加入项目,并根据指定路径的CMakeList.txt继续进行构建。
aux_source_directory(path, var)
:将指定目录path下的所有源代码存入变量var中。
这里仍然值得注意的是路径的声明,其中用到了内建变量${CMAKE_CURRENT_SOURCE_DIR}
。其含义仍然查询《特殊内建变量》一节。可以使用message()
函数打印。
添加静态库及动态库
上面的CMakeists.txt还有一个函数没有介绍,这个函数就是用于构建库的函数。
add_library(lib_name, [STATIC | SHARED | MODULE], src...)
:用于构建目标库,其中第一个参数为库名,第二个参数默认为STATIC,用于构建静态库,后面的参数为源代码。
依赖配置
下一步是为我们的目标文件添加依赖。看过【cpp查漏补缺】1-cpp是如何跑起来的? 的都知道,一个目标文件的构建,需要考虑头文件,依赖库和其他所有源文件。这就是一个目标文件所有的依赖。
1 | add_executable(main "${PROJECT_SOURCE_DIR}/src/main.cpp") |
其中我们用到了以下函数:
add_executable
:用于添加可执行目标。后面为所有需要的依赖。这里仅需要src/main.cpp
target_include_directories
:用于向目标添加头文件依赖所在的目录,这里是./include/
这个目录。关于PUBLIC,PRIVATE和INTERFACE的权限控制符的含义见《编译相关》一节。
target_link_libraries
:用于向目标添加库文件依赖,这里是calc
这个库,在src/calc/CMakeLists/txt中定义的库。
安装配置
安装配置的作用是将整个CMake构建过程的中间目标,通过make install
的方式输出到指定路径。增加配置如下:
1 | install(TARGETS calc DESTINATION "${PROJECT_SOURCE_DIR}/lib") |
这里仅仅是一些简单示例,更多用法请查看文档
如何启动
首先,由于CMake构建过程中会产生很多中间文件。因此,我们在build文件夹下进行构建:
1 | cd build |
构建完成后,我们发现有很多中间文件,其中生成了
makefile
。我们进行make:1 | make |
执行后的项目结构如下所示:
1 | . |
可以发现,相应的目标文件都在build里面,和其他的中间文件混在一起很乱。
如果我们在
build
目录下执行make install
呢?由于我们进行了安装配置,可以发现main和libcalc.a分别出现在了bin和lib下面,这达到了我们的预期。实际上,我们可以修改一些内建变量,达到执行
make
就能将相应文件放到我们想要放的地方中。在根目录下的CMakeLists.txt的前面几行加入如下内容:1 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin) # 可执行文件所在位置 |
再次执行
make
,可以发现相应目标文件都在根目录的bin和lib下了。但与此同时,build下面就不会出现这些目标文件了。
最后一种做法是作者在《编译原理》课程上看到的框架提供的做法,不清楚是否有潜在危害。
测试
待补充
CMake测试
CMake宏
项目迁移
待补充
参考资料
- Title: 【cpp查漏补缺】3-CMake入门
- Author: spiritTrance
- Created at: 2023-10-02 21:40:00
- Updated at: 2024-01-06 20:07:29
- Link: https://spirittrance.github.io/2023/10/02/cpp_3_CMake用法/
- License: This work is licensed under CC BY-NC-SA 4.0.