CMAKE使用记录
CMake简化了针对同一项目的不同平台、不同编译器的构建过程和编译过程,能够管理各种规模的项目。
CMAKE命令记录
cmake命令说明
语法:
# cmake构建项目的编译系统
Generate a Project Buildsystem
> cmake [<options>] -B <path-to-build> [-S <path-to-source>]
> cmake [<options>] <path-to-source | path-to-existing-build>
# cmake编译项目
Build a Project
> cmake --build <dir> [<options>] [-- <build-tool-options>]
# cmake安装项目的编译结果
Install a Project
> cmake --install <dir> [<options>]
# Open a Project
> cmake --open <dir>
# Run a Script
> cmake [-D <var>=<value>]... -P <cmake-script-file>
# Run a Command-Line Tool
> cmake -E <command> [<options>]
# Run the Find-Package Tool
> cmake --find-package [<options>]
# Run a Workflow Preset
> cmake --workflow [<options>]
# View Help
> cmake --help[-<topic>]
**cmake构建项目的编译系统**
cmake [<options>] -B <path-to-build> [-S <path-to-source>]
- cmake3.13版本要求
- <path-to-build>:指定build tree的目录
- <path-to-source>:指定source tree的目录
- 文件目录要求可以是绝对路径也可以是基于所在工作目录的相对路径
- source tree的目录必须包含CMakeLists.txt文件
- build tree的目录如果在命令运行前未创建,择自动会创建
# 例子
> cmake -S ./src -B ./build
cmake [<options>] <path-to-source>
- 当前工作目录作为build tree的目录
- <path-to-source>:指定source tree的目录
- 文件目录要求可以是绝对路径也可以是基于所在工作目录的相对路径
- source tree的目录必须包含CMakeLists.txt文件但是不能包含**CMakeCache.txt**文件,因为CMakeCache.txt是用来识别一个已经存在的build tree
# 例子:
> mkdir build ; cd build
> cmake ../src
cmake [<options>] <path-to-existing-build>
- <path-to-build>:指定build tree的目录
- 文件目录要求可以是绝对路径也可以是基于所在工作目录的相对路径
- 指定build tree的目录必须包含**CMakeCache.txt**文件,才能保证cmake加载CMakeCache.txt指定的source tree
cmake构建项目的编译系统的命令行选项如下
# 指定需要cmake构建的项目根目录
> -S <path-to-source>
# 指定cmake为项目构建的编译系统目录
> -B <path-to-build>
# 创建或者更新一个CMake的cache入口,一般就是设定项目的一些自定义配置
> -D <var>:<type>=<value>, -D <var>=<value>
# 指定编译系统生成器
> -G <generator-name>
> 例子: cmake -G "Visual Studio 16 2019"
# 指定编译系统生成器的工具集
> -T <toolset-spec>
> 例子:cmake -G "Visual Studio 16 2019" -T "v141"
# 指定编译系统生成器的平台
> -A <platform-name>
> 例子:cmake -G "Visual Studio 16 2019" -A Win32
# 指定编译成果安装目录
> --install-prefix <directory>
# 指定日志输出级别
> --log-level=<level>
需要注意:
# 如果需要重新构建,需要删除构建目录下的所有内容,再用cmake构建
# 例子:
> rm -rf ./*
> cmake ..
**cmake编译项目**
使用cmake构建出编译系统后,可以使用cmake本身命令编译项目,也可以使用原生工具集编译项目,例如Linux的make、windows的nmake。
cmake --build <dir> [<options>] [-- <build-tool-options>]
cmake --build --preset <preset> [<options>] [-- <build-tool-options>]
cmake编译项目命令行选项:
--build <dir>
指定项目编译目录。
-j [<jobs>], --parallel [<jobs>]
指定用于编译的并发进程最大数量。如果<jobs>未指定,使用默认的数量。
某些原生的编译工具总是并发来编译。可以将<jobs>设置为1,用来限制单个job来编译。
-v, --verbose
表示打印详细编译过程
--clean-first
编译之前先执行clean操作
**cmake安装项目**
编译完成项目后,可以使用cmake本身命令安装项目,也可以使用原生工具集安装项目,例如Linux的make、windows的nmake。
cmake --install <dir> [<options>]
cmake安装项目命令行选项:
--install <dir>
指定编译成果所在目录。
--prefix <prefix>
覆盖默认的安装目录前缀,CMAKE_INSTALL_PREFIX。
-v, --verbose
表示打印详细安装过程
引用参考:
1.CMake官网
MESSAGE函数:
用于在CMakeLists.txt文件中记录日志消息
message([<mode>] “message text” …) # General messages
message(<checkState> “message text” …) # Reporting checks
message(CONFIGURE_LOG <text>…) # Configure Log
General messages:在日志中记录指定的消息文本。如果给出了多个消息字符串(message string),最终会连接成一条消息,且字符串之间没有分隔符。
可选的关键字确定消息的类型:
FATAL_ERROR:CMake Error,停止处理和生成
SEND_ERROR:CMake Error,继续处理,但跳过生成
WARNING:CMake Warning,继续处理
AUTHOR_WARNING:CMake Warning(dev),继续处理
DEPRECATION:如果分别启用了CMAKE_ERROR_DEPRECATED或CMAKE_WARN_DEPRECATED,则CMake弃用(Deprecation)Error或Warning,否则没有消息
(none)或NOTICE:重要的消息打印到stderr以引起用户的注意。
STATUS:project用户可能感兴趣的主要信息。理想情况下,这些信息应该简明扼要,不超过一行,但仍然信息丰富
VERBOSE:针对project用户的详细信息消息。这些消息应提供在大多数情况下不感兴趣的额外详细信息,但是在编译项目时可以提供更丰富的消息让用户明白编译发生的过程
DEBUG: 针对工作项目的开发者的详细信息消息
TRACE:低级的细粒度消息,使用此日志级别的消息通常只是临时的,一般在发布项目、打包文件之前被删除
CMake3.15版本开始才增加了NOTICE、VERBOSE、DEBUG、TRACE
--log-level=<level>
命令行选项可用于控制显示哪些消息。若不指定,默认不会显示verbose, debug, trace消息。也可通过CMAKE_MESSAGE_LOG_LEVEL变量设置。有效日志级别如下:ERROR, WARNING, NOTICE, STATUS (default), VERBOSE, DEBUG, or TRACE
示例:
# 输出多条字符串组合
MESSAGE(“github addr:” “https://github.com/xxx”) # github addr:https://github.com/xxx
# 输出cmake预定义变量的值
MESSAGE(STATUS “Binary Dir:” ${DEMO_BINARY_DIR}) # Binary Dir: ~/Test
Reporting checks:CMake输出中的一个常见模式是一条消息表明某种检查的开始,然后是另一条消息报告检查的结果。这可以使用message命令的CHECK_…关键字形式可以更强大、更方便地表达这一点。其中必须是以下之一:
- CHECK_START:记录关于将要执行的检查的简明信息
- CHECK_PASS:记录检查的成功结果
- CHECK_FAIL:记录检查的不成功结果。
INSTALL:
用于cmake在编译完成后安装
安装Targets:
CMAKE_INSTALL_PREFIX: 指定安装目录的前缀路径
cmake --install时添加--prefix参数 会覆盖CMAKE_INSTALL_PREFIX指定的目录
> cmake --install . --prefix "/home/myuser/installdir"
install(TARGETS targets... [EXPORT <export-name>]
[RUNTIME_DEPENDENCIES args...|RUNTIME_DEPENDENCY_SET <set-name>]
[[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|
PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE|FILE_SET <set-name>|CXX_MODULES_BMI]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[NAMELINK_COMPONENT <component>]
[OPTIONAL] [EXCLUDE_FROM_ALL]
[NAMELINK_ONLY|NAMELINK_SKIP]
] [...]
[INCLUDES DESTINATION [<dir> ...]]
)
参数中的TARGETS后面跟的就是我们通过ADD_EXECUTABLE或者ADD_LIBRARY定义的目标文件,可能是可执行二进制、动态库、静态库。
目标类型:ARCHIVE特指静态库或者windows的dll对应的lib导入库,LIBRARY特指动态库,RUNTIME特指可执行目标二进制。
DESTINATION定义了安装的路径,如果路径以/开头,那么指的是绝对路径,这时候CMAKE_INSTALL_PREFIX其实就无效了。如果你希望使用CMAKE_INSTALL_PREFIX来定义安装路径,就要写成相对路径,即不要以/开头,那么安装后的路径就是
${CMAKE_INSTALL_PREFIX}/<DESTINATION定义的路径>
CONFIGURATIONS为不同的编译配置设置安装规则(Debug, Release, 等等)。
install(TARGETS target
CONFIGURATIONS Debug
RUNTIME DESTINATION Debug/bin)
install(TARGETS target
CONFIGURATIONS Release
RUNTIME DESTINATION Release/bin)
PERMISSIONS 权限:
OWNER_WRITE,OWNER_READ,GROUP_READ和WORLD_READ组合:表示权限644
举例:
INSTALL(TARGETS myrun mylib mystaticlib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION libstatic
)
# 二进制myrun安装到${CMAKE_INSTALL_PREFIX}/bin目录
# 动态库lib mylib安装${CMAKE_INSTALL_PREFIX}/lib目录
# 静态库lib mystaticlib安装到${CMAKE_INSTALL_PREFIX}/libstatic目录
# 特别注意的是不需要关心TARGETS具体生成的路径,只需要写上TARGETS名称就可以了。
安装Files:
CMAKE_INSTALL_PREFIX: 指定安装目录的前缀路径
cmake --install时添加--prefix参数 会覆盖CMAKE_INSTALL_PREFIX指定的目录
> cmake --install . --prefix "/home/myuser/installdir"
install(<FILES|PROGRAMS> files...
TYPE <type> | DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL] [EXCLUDE_FROM_ALL])
安装Directories:
CMAKE_INSTALL_PREFIX: 指定安装目录的前缀路径
cmake --install时添加--prefix参数 会覆盖CMAKE_INSTALL_PREFIX指定的目录
> cmake --install . --prefix "/home/myuser/installdir"
install(DIRECTORY dirs...
TYPE <type> | DESTINATION <dir>
[FILE_PERMISSIONS permissions...]
[DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS] [OPTIONAL] [MESSAGE_NEVER]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>] [EXCLUDE_FROM_ALL]
[FILES_MATCHING]
[[PATTERN <pattern> | REGEX <regex>]
[EXCLUDE] [PERMISSIONS permissions...]] [...])
DIRECTORY后面的参数是所在Source目录的相对路径,需要注意:
abc和abc/有很大的区别。
- 如果目录不以/结尾,那么这个目录将被安装到目标路径下的abc
- 如果目录以/结尾,代表将这个目录中的内容安装到目标路径,但不包括这个目录本身。
PATTERN用于使用正则表达式进行过滤,PERMISSIONS用于指定PATTERN过滤后的文件权限。
例子:
INSTALL(DIRECTORY icons scripts/ DESTINATION test/myproj
PATTERN "CVS" EXCLUDE
PATTERN "scripts/*"
PERMISSIONS OWNER_EXECUTE OWNER_WRITE WONER_READ GROUP+EXECUTE GROUP_READ)
# 将icons目录安装到<prefix>/test/myproj,将scripts/中的内容安装到<prefix>/test/myproj
# 不包含名为CVS的目录
# 对于scripts/*文件指定权限为OWNER_EXECUTE OWNER_WRITE WONER_READ GROUP_EXECUTE GROUP_READ
安装时CMAKE脚本的执行
INSTALL([ [SCRIPT < file>] [ CODE < code >]] [...])
# SCRIPT参数用于在安装时调用cmake脚本文件(也就是<abc>.cmake文件)
# CODE参数用于执行CMAKE指令,必须以双引号括起来。比如:
INSTALL(CODE "MESSAGE(\"example install message.\")")
引用参考:
1.CMake官网
2.CMake中message的使用
3.cmake使用教程(实操版)
典型例子
hello cmake例子
提示:最简单的cmake使用例子:
新建文件夹hello,并进入hello文件夹,文件组织如下
hello
├── build/: CMake构建过程、编译过程的中间文件和结果文件保存目录(这样子可以确保不会污染项目源文件夹)
├── CMakeLists.txt: CMake构建配置文件
├── main.cc: 编译的源码
└── install/: 项目编译结果安装目录
main.cc:
#include <iostream>
int main() {
std::cout << "hello cmake" << std::endl;
return 0;
}
CMakeLists.txt:
PROJECT(HELLO)
MESSAGE(STATUS "Binary Dir:" ${HELLO_BINARY_DIR})
MESSAGE(STATUS "Source Dir:" ${HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello main.cc)
构建过程:
#1. 切换到build目录
> cd ./build
#2. cmake构建
> cmake .. #..表示build的上级目录(即CMakeLists.txt所在的根目录)
#3. 编译(可直接使用cmake自带的编译命令,也可根据编译器选用特定的编译命令,比如Linux的make命令,windows的nmake命令)
#3.1 cmake自带的编译命令
> cmake --build . # .表示build当前目录
#3.2 make编译命令
> make .
#4. 安装(可直接使用cmake自带的安装命令,也可根据编译器选用特定的安装命令,比如Linux的make命令,windows的nmake命令)
> cmake --install . --prefix ../install
cmake .. 命令输出:
此时生成的编译系统及中间文件如下:
cmake --build . 命令输出:
此时编译成果如下:
例子中内容解释:
PROJECT(projectname [CXX] [C] [Java])
用这个语句定义工程名称,并且可以指定工程支持的语言,支持的语言列表是可以忽略的,默认情况表示支持所有语言。这个指令隐式的定义了两个cmake的变量:
<projectname>_BINARY_DIR:cmake构建的二进制目标目录
<projectname>_SOURCE_DIR:cmake构建项目的源目录
示例:见<cmake .. 命令输出:>截图
使用示例:${HELLO__BINARY_DIR}
这里需要注意:
使用 方式来取得变量中的值,如 {}方式来取得变量中的值,如 方式来取得变量中的值,如{HELLO__BINARY_DIR},而在IF语句中则直接使用变量名。
子目录 cmake例子
提示:子目录的cmake使用例子:
新建文件夹second,并进入second文件夹,文件组织如下
second
├── build/: CMake构建过程、编译过程的中间文件和结果文件保存目录(这样子可以确保不会污染项目源文件
├── CMakeLists.txt: CMake根目录构建配置文件
└── src/: 子目录
├── CMakeLists.txt: CMake子目录构建配置文件
└── main.cc: 编译的源码
src/main.cc:
#include <iostream>
int main() {
std::cout << "hello cmake" << std::endl;
return 0;
}
src/CMakeLists.txt:
SET(SRC_LIST main.cc)
ADD_EXECUTABLE(hello ${SRC_LIST})
CMakeLists.txt:
cmake_minimum_required(VERSION 3.15)
PROJECT(HELLO)
MESSAGE(STATUS "Binary Dir:" ${HELLO_BINARY_DIR})
MESSAGE(STATUS "Source Dir:" ${HELLO_SOURCE_DIR})
ADD_SUBDIRECTORY(src bin)
构建过程:
#1. 切换到build目录
> cd ./build
#2. cmake构建
> cmake .. #..表示build的上级目录(即CMakeLists.txt所在的根目录)
#3. 编译(可直接使用cmake自带的编译命令,也可根据编译器选用特定的编译命令,比如Linux的make命令,windows的nmake命令)
#3.1 cmake自带的编译命令
> cmake --build . # .表示build当前目录
#3.2 make编译命令
> make .
cmake .. 构建结果:
cmake --build . 编译结果:
例子中内容解释:
SET(SRC_LIST main.cc)
表示将源代码文件列表定义为一个变量,方便后续命令调用
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
这个命令用于向当前工程添加存放源文件的子目录。并可以指定中间二进制和目标二进制存放的位置。EXCLUDE_FROM_ALL参数的含义是将这个目录从编译过程中排除,比如,某个工程中的example,可能就需要工程构建编译完成后,再进入example目录单独进行构建编译(当然,你可以通过定义依赖来解决此类问题)。
本例子定义了将src子目录加入工程,并指定编译输出(包含编译中间结果)路径为bin目录。如果不进行bin目录的指定,那么编译结果(包括中间结果)都将存放在build/src目录(这个目录跟原来的src目录名字对应),指定bin目录后,相当于在编译时将src重命名为bin,所有的中间结果和目标二进制都存放在bin目录中。
可以通过SET指令重新定义EXECUTABLE_OUTPUT_PATH和LIBRARY_OUTPUT_PATH变量来指定最终的编译目标二进制的位置
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)