看了B站一个cmake教程,部分笔记内容来自别人
CMake
中的变量
变量和含义
常用变量 含义 PROJECT_NAME
工程名变量 PROJECT_SOURCE_DIR
顶层的项目目录 PROJECT_BINARY_DIR
使用 cmake
的路径CMAKE_ROOT
CMAKE
安装的根目录CMAKE_BUILD_TYPE
编译类型: empty
,Debug
,Release
…CMAKE_SOURCE_DIR
顶层的 CMakeLists.txt
所在路径CMAKE_BINARY_DIR
顶层的 CMakeLists.txt
的build
所在目录CMAKE_<LANG>_COMPILER
设定某个语言 LANG
的编译器,比如g--
CMAKE_INSTALL_PREFIX
指令 install
的路径CMAKE_CURRENT_SOURCE_DIR
当前 CMakeLists.txt
所在路径CMAKE_CURRENT_BINARY_DIR
当前 CMakeLists.txt
的build
所在目录EXECUTABLE_OUTPUT_PATH
可执行文件输出路径 LIBRARY_OUTPUT_PATH
库输出路径
个别变量解释
源码目录结构1
2
3
4
5
6
7
8
9
10
11
12
13
14
15jmudou
├── build.sh
├── CMakeLists.txt
└── muduo
└── base
├── Atomic.h
├── CMakeLists.txt
├── copyable.h
├── tests
│ ├── Atomic_unittest.cc
│ ├── CMakeLists.txt
│ └── Timestamp_unittest.cc
├── Timestamp.cc
├── Timestamp.h
└── Types.h三个
CMakeLists.txt
输出1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24jmuduo/CMakeLists.txt:
PROJECT_SOURCE_DIR= 11/jmuduo
PROJECT_BINARY_DIR= 11/build/debug
CMAKE_SOURCE_DIR= 11/jmuduo # 指定顶层的CMakeLists.txt的,因此所有的都相同
CMAKE_BINARY_DIR= 11/build/debug
# 上面四项都是与整个项目相关,因此都是一致的。
CMAKE_CURRENT_SOURCE_DIR= 11/jmuduo # 指定当前层的CMakeLists.txt的,因此与层相关
CMAKE_CURRENT_BINARY_DIR= 11/build/debug
muduo/base/test/CMakeLists.txt:
PROJECT_SOURCE_DIR= 11/jmuduo
PROJECT_BINARY_DIR= 11/build/debug
CMAKE_SOURCE_DIR= 11/jmuduo
CMAKE_BINARY_DIR= 11/build/debug
CMAKE_CURRENT_SOURCE_DIR= 11/jmuduo/muduo/base/tests
CMAKE_CURRENT_BINARY_DIR= 11/build/debug/muduo/base/tests
muduo/base/CMakeLists.txt:
PROJECT_SOURCE_DIR= 11/jmuduo
PROJECT_BINARY_DIR= 11/build/debug
CMAKE_SOURCE_DIR= 11/jmuduo
CMAKE_BINARY_DIR= 11/build/debug
CMAKE_CURRENT_SOURCE_DIR= 11/jmuduo/muduo/base
CMAKE_CURRENT_BINARY_DIR= 11/build/debug/muduo/base
函数
所有的函数列表中,<>
表示的参数必须要有,[]
表示的参数为可选。
set
可以设置三个类型的变量值:正常变量,cache variable
、环境变量。Normal Variable
:set(<variable> <value>... [PARENT_SCOPE])
cache
:set(<variable> <value>... CACHE <type> <docstring> [FORCE])
env
:set(ENV{<variable>} [<value>])
OPTION
:提供用户可以选择的选项- 格式:
option(<variable> "description" [initial value])
- 比如:
1
2
3
4
5option(
USE_MYPATH
"user path"
ON
)
- 格式:
aux_source_directory
- 语法:
aux_source_directory(<dir> <variable>)
- 查找目录
dir
下的所有源文件(即.c, .cpp, .cc等文件),并将名称保存到variable
变量
- 语法:
add_compile_options
add_compile_options(<option> ...)
- 向
COMPILE_OPTIONS
目录属性中添加选项,这些选项在编译当前目录及子目录的对象时被使用。 - e.g:
add_compile_options(-std=c++11)
add_subdirectory
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
- 添加一个将被编译的子目录。指明
CMakeLists.txt
所在目录下包含了一个子目录source_dir
。这样source_dir
下的源文件和CMakeLists.txt
等也会被处理。
target_link_libraries
target_link_libraries(exec libs)
- 表示可执行程序
exec
需要链接到一个名为libs
的链接库。
add_library
`add_library(
[STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] [source1] [source2 ...])`
添加一个名为
的库文件,该库文件将会根据调用的命令里列出的源文件来创建。 对应于逻辑目标名称,而且在一个工程的全局域内必须是唯一的。待构建的库文件的实际文件名根据对应平台的命名约定来构造(比如lib .a或者 .lib)。 指定STATIC,SHARED,或者MODULE参数用来指定要创建的库的类型。STATIC库是目标文件的归档文件,在链接其它目标的时候使用。SHARED库会被动态链接,在运行时被加载。MODULE库是不会被链接到其它目标中的插件,但是可能会在运行时使用dlopen-系列的函数动态链接。如果没有类型被显式指定,这个选项将会根据变量BUILD_SHARED_LIBS的当前值是否为真决定是STATIC还是SHARED。
configure_file
加入一个配置头文件,用于处理 CMake 对源码的设置
1
2
3
4configure_file (
"${PROJECT_SOURCE_DIR}/config.h.in" # config.h.in文件目录
"${PROJECT_BINARY_DIR}/config.h" # config.h 生成的头文件目录
)在配置文件
config.h
中,配置相关项,比如options
中的USE_MYPATH
:1
#cmakedefine USE_MYMATH
include
include(file [optional])
:读取CMake
的相关文件。include(moudle [optional])
the file with name.cmake is searched in the CMAKE_MODULE_PATH
。
include_directories
1
include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
- 将给定的路径添加到编译器搜索包含文件(.h文件)的路径列表中。缺省情况下,该路径会被附加在当前路径列表的后面。这种缺省行为可以通过设置CMAKE_include_directories_BEFORE变量为ON被改变。通过将该变量改变为BEFORE或AFTER,你可以在追加和附加在前端这两种方式中选择,而不用理会缺省设置。如果指定了SYSTEM选项,编译器将会认为该路径是某种平台上的系统包含路径。
install
使用:cmake
之后,sudo make install
就可以执行相应的库和头文件的安装。TARGET
格式1
2
3
4
5
6
7
8install(TARGETS targets...
[[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL]
] [...])targets
的类型
可以安装的库有[ARCHIVE|LIBRARY|RUNTIME]
三种:
1) 可执行程序视为runtime
2) 静态库视为archieve
3)Module Library
视为library
4) 共享库和平台有关
另一种说法:
有5中可以被安装的目标文件:ARCHIVE,LIBRARY,RUNTIME,FRAMEWORK,和BUNDLE。
除了被标记为MACOSX_BUNDLE属性的可执行文件被当做OS X上的BUNDLE目标外,其他的可执行文件都被当做RUNTIME目标。
静态链接的库文件总是被当做ARCHIVE目标。
模块库总是被当做LIBRARY目标。
对于动态库不是DLL格式的平台来说,动态库会被当做LIBRARY目标来对待,
被标记为FRAMEWORK的动态库是例外,它们被当做OS X上的FRAMEWORK目标。
对于DLL平台而言,动态库的DLL部分被当做一个RUNTIME目标而对应的导出库被当做是一个ARCHIVE目标。
所有基于Windows的系统,包括Cygwin,都是DLL平台。ARCHIVE,LIBRARYRUNTIME和FRAMEWORK参数改变了后续属性会加诸之上的目标的类型。如果只出了一种类型,那么只有那种类型的目标会被安装(这样通常只会安装一个LL或者一个导出库。)
- 参数
- `DESTINATION`:
指定一个文件将要被安装的目录。如果给的是一个全路径,那么就直接使用;如果是相对路径,默认是相对`CMAKE_INSTALL_PREFIX`,其值默认是`/usr/local/`。
1) 头文件:`inclide`
2) 可执行文件:`bin`
3) 库:`lib`
- `PERMISSIONS`: 指定安装文件的权限:
  1) user : ` OWNER_READ, OWNER_WRITE, OWNER_EXECUTE`
  2) group:`GROUP_READ, GROUP_WRITE, GROUP_EXECUTE`
  3) other:`WORLD_READ, WORLD_WRITE, WORLD_EXECUTE`
  4) uid :` SETUID, and SETGID`。
- `CONFIGURATIONS`:为安装规则建立一个配置文件列表。
install
FILES
格式1
2
3
4
5
6INSTALL(FILES files...
DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])files
:即文件名
测试
enanle_testing()
:启动测试add_test(testname Exename arg1 arg2 ...)
:- 需要先运行测试程序
enanle_testing()
,这个指令才有效。 Exename
是可执行程序名,参数arg1, arg2
。
- 需要先运行测试程序
set_tests_properties(...)
- 括号内格式:
(Exename [Exename2...] PROPERTIES prop1 value1 prop2 value2)
,其中PROPERTIES
是固定的单词不能改 - 为
Exename
设置属性,如果没有这个属性,就报错,有如下属性:
1)WILL_FAIL
:如果设置为true
,那么会反转测试结果的pass/fail
标志。
2)PASS_REGULAR_EXPRESSION
: 匹配正则表达式,只少有一个匹配,则pass
2)FAIL_REGULAR_EXPRESSION
: 匹配正则表达式,则fail
- 括号内格式:
宏测试
1
2
3
4
5macro(<name> [arg1 [arg2 [arg3 ...]]])
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
endmacro(<name>)就类似于写一个函数,用宏实现,调用:
name(arg1,arg2,...)
。
设置项目的版本号
在顶层的
CMakeLists.txt
中:1
2
3# 加入版本号是 1.0
set (Project_VERSION_MAJOR 1) # 主版本号
set (Project_VERSION_MINOR 0) # 副版本号在配置文件
config.h.in
中设置:1
2#define Project_VERSION_MAJOR @Project_VERSION_MAJOR@
#define Project_VERSION_MINOR @Project_VERSION_MINOR@main
函数中就可以直接使用这两个宏,代表版本号:1
2
3printf("Version %d.%d\n",
Project_VERSION_MAJOR,
Project_VERSION_MINOR);
target_compile_definitions
格式
1
2
3target_compile_definitions(<target>
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])作用:编译时定义的宏
string
REPLACE
1
2
3string(REPLACE
<match_string> <replace_string>
<output_variable> <input> [<input>...])将所有
input
中出现的match_String
替换为replace_string
,并且将结果存在output_variable
。
message
1
message([<mode>] "message to display" ...)
显示信息给用户
mode
取决于信息的类型:STATUS
:以简洁的方式显示用户感兴趣的信息。1
2
3
4
5
6message(STATUS
"CXX_FLAGS = "
${CMAKE_CXX_FLAGS}
" "
${CMAKE_CXX_FLAGS_${BUILD_TYPE}}
)可以理解为
printf
,把后面的几个信息以空格相间,然后打印出来。显示结果为(和上面对应分成三段):1
2
3
4
5
6
7
8
9CXX_FLAGS =
-g -D_FILE_OFFSET_BITS=64 -Wall -Wextra
-Werror -Wconversion -Wno-unused-parameter
-Wold-style-cast -Woverloaded-virtual
-Wpointer-arith -Wshadow -Wwrite-strings
-march=native -rdynamic
-O0
1
2
3
4
5
6
(无) = 重要消息;
STATUS = 非重要消息;
WARNING = CMake 警告, 会继续执行;
AUTHOR_WARNING = CMake 警告 (dev), 会继续执行;
SEND_ERROR = CMake 错误, 继续执行,但是会跳过生成的步骤;
FATAL_ERROR = CMake 错误, 终止所有处理过程;
find_package
1
2
3
4
5find_package(<PackageName>
[version] [EXACT] [QUIET] [MODULE]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[NO_POLICY_SCOPE])主要是寻找和加载外部项目。如果
PackageName
找到了,PackageName-found
会显出,当没有找到时,默认显示
PackageName-not found
。通过模式的选择,可以处理在没有找到包时的解决方案。QUIET
:不显示有用信息,REQUIRED
:报错
find_path
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17find_path (
<VAR>
name | NAMES name1 [name2 ...]
[HINTS path1 [path2 ... ENV var]]
[PATHS path1 [path2 ... ENV var]]
[PATH_SUFFIXES suffix1 [suffix2 ...]]
[DOC "cache documentation string"]
[NO_DEFAULT_PATH]
[NO_PACKAGE_ROOT_PATH]
[NO_CMAKE_PATH]
[NO_CMAKE_ENVIRONMENT_PATH]
[NO_SYSTEM_ENVIRONMENT_PATH]
[NO_CMAKE_SYSTEM_PATH]
[CMAKE_FIND_ROOT_PATH_BOTH |
ONLY_CMAKE_FIND_ROOT_PATH |
NO_CMAKE_FIND_ROOT_PATH]
)用以寻找包含着
name1
等文件的目录,如果找到了结果存储在VAR
,没有找到结果结果是VAR-not found
。成功时,变量被清除find_path
再次搜索,没有成功,fin_path
再次以相同的变量被调用时搜索。find_library
同上find_path
1
find_library (<VAR> name0|NAMES name1 [path1 path2 ...])
- OPTIONS
NAMES
为library
指定一个或多个可能的名字。
- OPTIONS
生成库
- 用add_library命令
静态库与动态库的区别
静态库:
- 是在链接阶段,库中目标文件所含的所有将被程序使用的函数的机器码被拷贝到最终的可执行文件中。
- 这个链接的过程是在编译期完成的,好处是程序运行时与函数库无关了,移植方便。运行效率快,以为所有的代码都变成机器码了。
- 当然,编译结果对象体积会变大。而且如果多个程序公用一个库代码,其实每个程序运行起来都会在内存中包含一份库的代码,造成内存浪费。
动态库:
- 相反的,目标文件需要的库代码在编译时不会被连接到目标代码中,而是在运行时才被载入。
- 可执行文件只包含了它需要的函数的引用表,而不是代码。
- 只有执行时需要的函数代码才被拷贝到内存中。
- 可以增量更新。
- 缺点也明显,如果缺失了一些动态库程序会报错。
生成库的使用
- cmake中链接内部库的依赖
1 | cmake_minimum_required(2.8) |
- 导入外部库文件
- 绝对路径引用方式
- install方式
- find_package
- 定义FindlibB.cmake:这个文件本质是为了find_package服务器的,因为这个函数并没有查询自定义目录的能力。
1 | find_path(libB_INCLUDE_DIR |
- 使用find_package的完整版
1 | cmake_minimum_required(VERSION 3.0.0) |
cmake中有大量的约定性的变量,比如FindlibB.cmake这个文件,Find就是标准写法。比如
libB_INCLUDE_DIR
、libB_LIBRARY
、libB_FOUND
这些都是固定下发,可以看下 Find Modules 这部分。cmake语言本身很接近编程语言,因此也有一些类型判断的内容,可以参考 if 的用法这块。
参考