0%

CMake 安装使用

官方在线文档

安装与新建工程 (Ubuntu)

CMake 官网下载: https://cmake.org/download/ .

找个地方解压并进入.

1
2
tar -zxvf cmake-3.14.5.tar.gz
cd cmake-3.14.5/

根据目录下的 README 文件, 执行以下命令安装.

1
./bootstrap && make && sudo make install

编译好慢啊...

CMake 会默认安装在 /usr/local/bin 下面.

用 CMake 编译安装 CMake

1
2
3
4
cd build/
cmake ..
make
sudo make install

构建一个简单的 CMake 项目

新建一个文件夹作为项目文件夹, 新建文件 CMakeLists.txt. 以下为一个典型的配置:

1
2
3
4
5
6
7
8
9
10
11
# 项目名称
PROJECT(CMakeTest CXX)

# CMake最低版本需求,不加入此行会受到警告信息
CMAKE_MINIMUM_REQUIRED(VERSION 3.14)

# 把目录src下所有源代码文件和头文件加入变量SRC_LIST
AUX_SOURCE_DIRECTORY(src SRC_LIST)

# 生成应用程序 hello
ADD_EXECUTABLE(hello ${SRC_LIST})

src 文件夹写好源码后 (比如写个 hello_world.cpp), 在项目目录新建文件夹 build.

然后执行

1
2
3
cd build/
cmake ..
make

build 文件夹下就会生成编译文件和最终的可执行文件 hello.

GitHub 优秀 CMake 项目参考

CMake | OpenCV

Visual Studio 对 CMake 的支持

CMake 语法

语法特性

赋值语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 在当前及其子项目定义变量 VAR (不影响父域)
# 使用 ${<var>} 访问
# set(VAR a b c) 以定义 LIST (set(VAR "a b c") 则定义了个字符串)
set(<var> <value>)


# 在父域定义 VAR (而不影响当前环境!!)
set(<var> <value> PARENT_SCOPE)

# Cache 变量, 定义后全局访问, 且存在 CMakeCache.txt 中
# 使用 $CACHE{<var>} 访问, 或当同名常规变量未被定义时也可用 ${<var>} 访问
set(<var> <value> CACHE <STRING | BOOL | FILEPATH | PATH> INTERNAL)

# 环境变量
# 使用 $ENV{<var>} 访问
set(ENV{<var>} <path>)

# 不再定义
unset(<var> [CACHE | PARENT_SCOPE])
unset(ENV{<variable>})

LIST 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 读
list(LENGTH <list> <out-var>)
list(GET <list> <element index> [<index> ...] <out-var>)
list(JOIN <list> <glue> <out-var>)
list(SUBLIST <list> <begin> <length> <out-var>)

# 查
list(FIND <list> <value> <out-var>)

# 删改
list(APPEND <list> [<element>...])
list(FILTER <list> {INCLUDE | EXCLUDE} REGEX <regex>)
list(INSERT <list> <index> [<element>...])
list(POP_BACK <list> [<out-var>...])
list(POP_FRONT <list> [<out-var>...])
list(PREPEND <list> [<element>...])
list(REMOVE_ITEM <list> <value>...)
list(REMOVE_AT <list> <index>...)
list(REMOVE_DUPLICATES <list>)
list(TRANSFORM <list> <ACTION> [...])

# 排序
list(REVERSE <list>)
list(SORT <list> [...])

条件语句

1
2
3
4
5
6
7
if(<condition>)
<commands>
elseif(<condition>)
<commands>
else() # 或在括号内填上与 if 一模一样的 <condition>
<commands>
endif() # 或在括号内填上与 if 一模一样的 <condition>

函数

1
2
3
function(<func_name> [arg1 [arg2 [arg3 ...]]])
<commands>
endfunction() # 或在括号内一字不差地填上 function 括号内内容

1
2
3
macro(<name> [arg1 [arg2 [arg3 ...]]])
<commands>
endmacro() # 或在括号内一字不差地填上 macro 括号内内容

内置函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 确定所需 CMake 版本 (最好放在文件的最开头)
# cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
cmake_minimum_required(VERSION <min>[...<max>] [FATAL_ERROR])

# 确定项目名
project(<project>)

# 添加并构建一个子项目 (即包含 CMakeLists.txt 的文件夹)
add_subdirectory(<project_dir> [binary_dir] [EXCLUDE_FROM_ALL])

# 将一整个文件夹里的 c/cpp 文件添加到源文件列表,
# 得到如 "src/main.cpp;src/s.cpp;..." 的字符串
# aux_source_directory(src SRC_LIST)
aux_source_directory(<src_dir> <source_list>)

# 根据提供的源文件参数生成可执行文件
# add_executable(CMakeTest.exe ${SRC_LIST})
add_executable(<target_exe> [WIN32] [MACOSX_BUNDLE]
[EXCLUDE_FROM_ALL]
[source1] [source2 ...])

# 同上, 生成库
# add_library(Dependency.lib d1.cpp)
add_library(<target_lib> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
[source1] [source2 ...])

# 添加头文件目录 (添加后可识别 #include "xxx.h" 而不用使用相对路径)
# include_directories(Dependency)
include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])

# 给指定目标添加头文件目录
# target_include_directories(CMakeTest.exe PRIVATE Dependency)
target_include_directories(<target> [SYSTEM] [BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])

# 链接先前已经生成的可执行文件和库
# target_link_libraries(CMakeTest.exe Dependency.lib)
target_link_libraries(<target> ... <item>... ...)

内置变量

CMAKE_SOURCE_DIR | PROJECT_SOURCE_DIR | CMAKE_CURRENT_SOURCE_DIR | <PROJECT_NAME>_SOURCE_DIR

都指向项目的源目录的绝对路径 (CMakeLists.txt 所在的绝对路径).

CMAKE_SOURCE_DIR 指向当前最顶层项目 (父项目) CMakeLists.txt 的路径.

PROJECT_SOURCE_DIR 指向当前项目 (子项目) CMakeLists.txt 的路径.

CMAKE_CURRENT_SOURCE_DIR 指向当前正在处理的 CMakeLists.txt 的路径 (感觉和 PROJECT_SOURCE_DIR 没啥区别啊???).

<PROJECT_NAME>_SOURCE_DIR 就是指定某项目的 CMakeLists.txt 的路径. 但是当该项目还未被调用过之前, 该变量未定义.

所以当没有子项目时这几个都相同.


例如对于父项目 Outer 与子项目 Inner:

1
2
3
4
5
6
7
8
9
10
# Outer/CMakeLists.txt
...
project(Outer)
add_subdirectory(Inner)
...

# Outer/Inner/CMakeLists.txt
...
project(Inner)
...

Outer/CMakeLists.txt 中的变量值为:

CMAKE_SOURCE_DIR PROJECT_SOURCE_DIR CMAKE_CURRENT_SOURCE_DIR Outer_SOURCE_DIR Inner_SOURCE_DIR
/.../Outer /.../Outer /.../Outer /.../Outer /.../Outer/Inner

Outer/Inner/CMakeLists.txt 中的变量值为:

CMAKE_SOURCE_DIR PROJECT_SOURCE_DIR CMAKE_CURRENT_SOURCE_DIR Outer_SOURCE_DIR Inner_SOURCE_DIR
/.../Outer /.../Outer/Inner /.../Outer/Inner /.../Outer /.../Outer/Inner

TODO: PROJECT_SOURCE_DIRCMAKE_CURRENT_SOURCE_DIR 区别.

CMAKE_BINARY_DIR | PROJECT_BINARY_DIR | CMAKE_CURRENT_BINARY_DIR | <PROJECT_NAME>_BINARY_DIR

指向工程编译发生的绝对路径. 区别同上.

如果是 in source 编译, 指的就是工程顶层目录, 即与 <?>_SOURCE_DIR 一样. 而如果是:

1
2
3
cd build/
cmake ..
make

这种, 则其路径为 /.../CMake_Project/build .

踩坑

项目依赖作图 (graphviz)

生成依赖图的文件:

1
2
cd build/
cmake .. --graphviz=<graph>.dot

build/ 下生成 <graph>.dot 文件, <graph>.dot.<target> 文件和 <graph>.dot.<target>.dependers 文件.

安装 graphviz 以生成 png:

1
sudo apt-get install graphviz

导出依赖为图片:

1
dot <graph>.dot -T png -o <figure>.png

例如对于 cmake-3.14.5 项目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mkdir Build
cd Build
cmake .. --graphviz=graph.dot
ls | grep graph
# graph.dot
# graph.dot.cmake
# graph.dot.cmake.dependers
# graph.dot.CMakeLib
# graph.dot.CMakeLib.dependers
...
# graph.dot.testConsoleBufChild
# graph.dot.testConsoleBufChild.dependers
# graph.dot.testEncoding
# graph.dot.testEncoding.dependers

dot graph.dot -T png -o graph.png

打开 graph.png:

emmmm, 好丑的图.