CMake安装使用
安装与新建工程 (Ubuntu)
CMake 官网下载: https://cmake.org/download/ .
找个地方解压并进入.
tar -zxvf cmake-3.14.5.tar.gz
cd cmake-3.14.5/
根据目录下的 README
文件, 执行以下命令安装.
./bootstrap && make && sudo make install
编译好慢啊…
CMake 会默认安装在 /usr/local/bin 下面.
用 CMake 编译安装 CMake
cd build/
cmake ..
make
sudo make install
构建一个简单的 CMake 项目
新建一个文件夹作为项目文件夹, 新建文件 CMakeLists.txt
. 以下为一个典型的配置:
# 项目名称
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
.
然后执行
cd build/
cmake ..
make
在 build
文件夹下就会生成编译文件和最终的可执行文件 hello
.
GitHub 优秀 CMake 项目参考
Visual Studio 对 CMake 的支持
CMake 语法
语法特性
赋值语句
# 在当前及其子项目定义变量 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 操作
# 读
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> [...])
条件语句
if(<condition>)
<commands>
elseif(<condition>)
<commands>
else() # 或在括号内填上与 if 一模一样的 <condition>
<commands>
endif() # 或在括号内填上与 if 一模一样的 <condition>
函数
function(<func_name> [arg1 [arg2 [arg3 ...]]])
<commands>
endfunction() # 或在括号内一字不差地填上 function 括号内内容
宏
macro(<name> [arg1 [arg2 [arg3 ...]]])
<commands>
endmacro() # 或在括号内一字不差地填上 macro 括号内内容
内置函数
# 确定所需 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
:
# 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_DIR
与 CMAKE_CURRENT_SOURCE_DIR
区别.
CMAKE_BINARY_DIR
| PROJECT_BINARY_DIR
| CMAKE_CURRENT_BINARY_DIR
| <PROJECT_NAME>_BINARY_DIR
指向工程编译发生的绝对路径. 区别同上.
如果是 in source 编译, 指的就是工程顶层目录, 即与 <?>_SOURCE_DIR
一样. 而如果是:
cd build/
cmake ..
make
这种, 则其路径为 /.../CMake_Project/build
.
踩坑
项目依赖作图 (graphviz)
生成依赖图的文件:
cd build/
cmake .. --graphviz=<graph>.dot
在 build/
下生成 <graph>.dot
文件, <graph>.dot.<target>
文件和 <graph>.dot.<target>.dependers
文件.
安装 graphviz 以生成 png:
sudo apt-get install graphviz
导出依赖为图片:
dot <graph>.dot -T png -o <figure>.png
例如对于 cmake-3.14.5
项目:
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, 好丑的图.