Cmake 学习 CMake(cross platform Make)是一个跨平台、开源的构建工具。CMake是MakeFile的上层工具,目的是为了构建可移植的makefile实现跨平台的编译。
Write once, run everywhere.
learning by doing.
参考 cmake-examples-Chinese
cmake-examples-github
CMake 教程 | CMake 从入门到应用
CMake 入门实战
开始前的Demo 文件树 1
2
3
// ..demo
|-- CMakeLists . txt
|-- main . cpp
Copy main.cpp 1
2
3
4
5
#include <iostream>
int main ( int argc , char * agrv []) {
std :: cout << " hello CMAKE demo." << std :: endl ;
return 0 ;
}
Copy CMakeLists.txt 1
2
3
4
5
6
# CMake 最低版本要求
cmake_minimum_required(VERSION 2.8)
#工程名称
project(demo)
#指定生成目标文件
add_executable(hello_demo main.cpp)
Copy 使用CMake( Linux) 1
2
3
cmake PATH // 如 : cmake . 在当前目录执行cmake
make // 编译生成
./hello_demo // 执行目标文件
Copy Learning CMake 依赖CMakeLists.txt,项目主目标只有一个,主目录中可指定包含的子目录;
1
2
3
4
5
6
7
8
9
10
11
12
13
#注释
变量:使用 set命令显示定义及赋值 ,在非 if语句中使用 $ {} 引用, if中直接使用变量名引用 ,后续的 set命令会清理变量原先的值 ;
command ( args ... ) #命令不分大小写,参数使用空格分隔,使用双引号引起参数中的空格
add_executable ( $ { var }) <==> add_executable ( a . c b . c c . c )
条件语句: if ( var ) # var 非 empty 0 N NO OFF FLASE
...
else () / elseif ()
...
endif ( var )
循环语句: set ( var a b c )
foreach ( f $ { var })
endforeach ( f )
while ().... endwhile ()
Copy 内部变量 1
2
3
4
5
6
7
8
9
CMAKE_C_COMPILER : 指定 C编译器
CMAKE_CXX_COMPILER : 指定 C ++ 编译器
CMAKE_C_FLAGS : 编译 C文件时的选项 ,如 - g ; 亦可通过 add_definition 添加编译选项
EXECUTABLE_OUTPUT_PATH : 可执行文件的存放路径
LIBRARY_OUTPUT_PATH : 库文件路径
CMAKE_BUILD_TYPE : build类型 ( Debug 、 Release ), CMAKE_BUILD_TYPE = Debug
BUILD + SHARED_LIBS
在 CMakeLists . txt中指定使用set
在 cmake命令行中使用 : cmake - ....
Copy cmake 常用变量 名称 描述 CMAKE_BINARY_DIR,PROJECT_BINARY_DIR,<project_name>_BINARY_DIR 如果是 in source编译,指的是工程顶层目录,如果是out-of-source编译,指的是工程编译发生的目录 CMAKE_SOURCE_DIR, PROJECT_SOURCE_DIR, <projectname>
_SOURCE_DIR 工程顶层目录。 CMAKE_CURRENT_SOURCE_DIR 当前处理的 CMakeLists.txt 所在的路径,比如上面我们提到的 src 子目录。 CMAKE_CURRRENT_BINARY_DIR 如果是 in-source
编译,它跟 CMAKE_CURRENT_SOURCE_DIR 一致,如果是 out-of-source
编译,他指的是 target 编译目录。 EXECUTABLE_OUTPUT_PATH , LIBRARY_OUTPUT_PATH 最终目标文件存放的路径。 PROJECT_NAME 通过 PROJECT 指令定义的项目名称。
cmake系统信息 名称 描述 CMAKE_MAJOR_VERSION CMAKE 主版本号,比如 2.4.6 中的 2 CMAKE_MINOR_VERSION CMAKE 次版本号,比如 2.4.6 中的 4 CMAKE_PATCH_VERSION CMAKE 补丁等级,比如 2.4.6 中的 6 CMAKE_SYSTEM 系统名称,比如 Linux-2.6.22 CMAKE_SYSTEM_NAME 不包含版本的系统名,比如 Linux CMAKE_SYSTEM_VERSION 系统版本,比如 2.6.22 CMAKE_SYSTEM_PROCESSOR 处理器名称,比如 i686.
cmake编译选项 编译控制开关名 描述 BUILD_SHARED_LIBS 使用 ADD_LIBRARY
时生成动态库 BUILD_STATIC_LIBS 使用 ADD_LIBRARY
时生成静态库 CMAKE_C_FLAGS 设置 C 编译选项,也可以通过指令 ADD_DEFINITIONS()添加。 CMAKE_CXX_FLAGS 设置 C++编译选项,也可以通过指令 ADD_DEFINITIONS()添加。
命令 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
project(projectName) # set the project name
-> as: ${ projectName_SOURCE_DIR } 表示项目根目录
inlcude_directories : 指定头文件的搜索路径,相当于gcc 的-l 参数
-> as: include_directories( ${ projectName_SOURCE_DIR } /header) #增加header目录为include头文件目录
link_directories : 动态链接库或静态链接库的搜索路径,相当于gcc 的-L 参数
-> as: link_diretories( ${ projectName } /link)
add_subdirectory : 包含子目录
-> as: add_subdirectory( math)
add_executable : 编译可执行程序,指定编译
-> as:add_executalble( out main.cpp hello.cpp) out为二进制可执行文件
add_definitions : 添加编译参数
-> as: add_definitions( -DDEBUG) 将在gcc命令行中添加DEBUG宏定义
target_link_libraries : 添加链接库,相同于指定-l 参数
-> as: target_link_librabies( demo lib)
add_library : 将hello .cpp 编译成静态库
-> as : add_library( hello hello.cpp)
add_custom_target :
aux_source_directory(<dir> <variable>) : 获得一个目录下所有源文件
-> as: aux_sourcedirectory( . DIR)
add_dependencies : 定义 target 依赖的其他 target ,确保在编译本 target 之前,其他的 target 已经被构建。
foreach()
message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR])
STATUS : 正常消息
WARNING:警告,继续执行;
AUTHOR_WARNING:警告,继续执行;
FATAL_ERROR:错误,终止所有处理过程;
SEND_ERROR:错误,继续执行,跳过生成的步骤;
Copy 1
2
3
string
# 返回当前目录的上层路径
string(REGEX REPLACE <regular expression> <replace expression> <ouput variable> <input> [<input> ...])
Copy 1
2
3
4
5
6
7
8
9
10
11
list
list(LENGTH <list> <output variable>) #列表长度
list(GET <list> <index> [<index> ...] <output variable>) #返回指定下标的元素
list(APPEND <list> <element>[<element> ...]) #添加新元素
list(FIND <list> <index> [<index> ...]) #
list(INSERT <list> <index> <element> [<element> ...]) #插入元素至指定位置
list(REMOVE_ITEM <list> <value> [<value> ...])#删除元素
list(REMOVE_AT <list> <index> [<index>...]) # 删除指定下标的元素
list(REMOVE_DUPLICATES <list>) #删除重复元素
list<REVERSE <list>) #反转自身
list(SORT <list>) #排序自身
Copy Declare a target Declare target’s traits It’s all about targets 边做边学,由浅入深,以问题驱动自己去做。比如如何使用CMake创建一个可执行程序,如何创建一个动态库/静态库,如何配合第三方库,如何支持不同平台不同编译器以及其参数,如何用CMake组织多层目录的项目,如何自定义CMake Target,如何使用CMake调用外部工具等等.
静态链接库 add_library(lib STATIC| SHARED )
target_include_directories(lib PRIVATE|PUBLIC|INTERFACE <include_source>)
add_library(hello::library ALIAS hello_library) # 给hello_library 起别名 hello::library
TIPs(private、public、interface) 1
2
3
PRIVATE -- the directory is added to this target's include directories
INTERFACE -- the directory is added to the include directories for any targets that link this library.
PUBLIC -- As above, it is included in this library and also any targets that link this library.
Copy PRIVATE: 目录被添加到目标库的包含路径下.
INTERFACE: 目录没有被添加到目标库的包含路径下,而是链接了这个库的其他目标(库或可执行程序)包含路径中
PUBLIC:目录既被 添加到目录的包含路径中,同时添加了到链接这个库的其他目标的包含路径中
包含第三方库 使用find_package()
从CMAKE_MODULE_PATH中的文件夹列表中搜索"FindXXX.cmake"中的CMake模块。
1
2
3
4
5
find_package ( Boost 1.46.1 REQUIRED COMPONENTS filesystem system )
Boost 库名称
1.46.1 需要的 boost库最低版本
required 必需的 找不到报错
components - 要查找的库列表
Copy checking if the package:大多数被包含的包都将设置变量XX_FOUND,该变量可用于检查软件包在系统上时候可用。
总结与举例 添加文件目录 1
2
3
4
5
6
7
8
文件目录树
+- CMakeLists.txt
+- main.cpp
+- hello.cpp
| - header
+- hello.cpp
//先定义一下CMakeLists.txt中的关键字
demo-static :最终built的可执行文件
Copy 将main.cpp和hello.cpp添加到生成可执行文件,可以是add_executable(hello main.cpp hello.cpp)
也可以使用变量set(SOURCE_CPP main.cpp hello.cpp) 和add_executable(hello ${SOURCE_CPP})
实现;添加header目录则是target_include_directories(hello private ${PROJECT_SOURCE_DIR}/header)
这里的private
和常用变量PROJECT_SOURCE_DIR
上面已经提过。首先,在这添加目录意味着编译时将知晓该目录下的头文件,那么在main.cpp和hello.cpp
中使用#include"hello.h"
就不会出现未定义
,否则需要写成#include"header/hello.h"
。
1
2
3
4
5
6
7
error example
[ ../build] $ cmake ..
[ ../build] $ make
Scanning dependencies of target header
[ 33%] Building CXX object CMakeFiles/header.dir/main.cpp.o
/root/code/hello-headers/main.cpp:1:9: fatal error: hello.h: 没有那个文件或目录
1 | #include"hello.h
Copy 1
2
3
4
5
# CMakeLists.txt
cmake_minimum_required( VERSION 3.5)
project( demo-add-dir)
add_executable( demo main.cpp hello.cpp)
target_include_directories( demo private ${ PROJECT_SOURCE_DIR } /header)
Copy 使用静态库/动态库 1
2
3
4
5
6
7
8
9
文件目录树
+- CMakeLists.txt
+- main.cpp
+- hello.cpp
|- header
+- hello.cpp
//先定义一下CMakeLists.txt中的关键字
demo-static :最终built的可执行文件
hellolib :添加的静态链接库
Copy 将hello.cpp作为静态链接库,链接到最终的可执行文件;必然是add_library(hellolib static hello.cpp)
;由于头文件hello.h在header目录下,所以需要添加目录,那么是target_include_directories(demo-static public ${PROJECT_SOURCE_DIR}/header)
还是target_include_directories(hellolib public ${PROJECT_SOURCE_DIR}/header)
呢?首先,在这里我们使用的PUBLIC
添加目录,意味着目录既被添加到目标(库)的包含路径中,同时添加到了链接了这个库的其他目标(库或者可执行程序)的包含路径中
,所以当添加header
目录到hellolib
时,也被添加到了链接demo-static
中,所以在main.cpp
中使用#include"hello.h"
不会出错.但是,如果是被添加到了demo-static
时呢?却会如上的错误,找不到hello.h
头文件,demo-static
不是库所以在public
时不会传递。
1
2
3
4
5
6
7
8
# CMakeLists.txt
cmake_minimum_required( VERSION 2.8)
project( hello-lib-static)
add_executable( demo main.cpp)
add_library( hellolib STATIC hello.cpp)
target_include_directories( hellolib
PUBLIC ${ PROJECT_SOURCE_DIR } /header)
target_link_libraries( demo PUBLIC hellolib)
Copy 1
2
3
4
5
6
7
8
Scanning dependencies of target hellolib
[ 25%] Building CXX object CMakeFiles/hellolib.dir/hello.cpp.o
[ 50%] Linking CXX static library libhellolib.a
[ 50%] Built target hellolib
Scanning dependencies of target demo
[ 75%] Building CXX object CMakeFiles/demo.dir/main.cpp.o
[ 100%] Linking CXX executable demo
[ 100%] Built target demo
Copy 动态库的使用类似,把static
改成shared
即可。
1
2
3
4
5
6
Scanning dependencies of target hellolib
[ 25%] Building CXX object CMakeFiles/hellolib.dir/src/hello.cpp.o
[ 50%] Linking CXX shared library libhellolib.so
[ 50%] Built target hellolib
[ 75%] Linking CXX executable demo
[ 100%] Built target demo
Copy 设置构建类型 CMake具有许多内置的构建配置,可用于编译工程。这些配置指定了代码优化的级别,以及调用信息是否包含在二进制文件中。
Release 不可以打断点调试,程序开发完成后发行使用的版本,占体积小。对代码做了优化;-03 -DNDEBUG
Debug 调试版本,体积大;-g
MinSizeRel 最小体积版本 -Os -DNDEBUG
RelWithDebInfo 优化且调试 -02 -g -DNDEBUG
设置编译方式 Compile Flags 1
2
3
4
5
6
7
8
9
10
target_compile_definitions( target [ private| public| interface] [ items...])
比如在WIN和LINUX下就编译时选择的头文件不同,可以使用如:
#ifdef WIN
#include<xxx>
#include<xxx>
#endif
#ifdef LINUX
#include<xxx>
#include<xxx>
#endif
Copy 然后再命令行使用: cmake .. -DCMAKE_CXX_FLAGS="-WIN"
SET 变量使用 1
2
set(<variable> <value>
[[CACHE <type> <docstring> [FORCE]] | PARENT_SCOPE])
Copy set一般变量(normal variable) set缓存变量(cache variable) 1
缓存变量可以理解为第一次cmake时,这些变量缓存到一份文件中(CMakeCache.txt)。再次运行cmake时候,这些变量会直接使用缓存值,缓存变量在整个cmake运行过程中都起作用。
Copy set环境变量(environment variable) 未完待续….