1. Running cmake
1.1. cmake选项
--build 指定输出目录
-j 指定并行度
--target 指定编译目标
1.2. 选择编译器
EXPORT CC=clang CXX=clang++ cmake ..
1.3. 选择生成器
默认为make 通过cmake -G Ninja指定ninja
1.4. 指定编译选项
cmake -DXXX=YYY
cmake会将各种编译选项缓存到一个文件中,即CMakeCache.txt
1.5. 调试
调试makefile: VERBOSE=1 make
调试cmake:
- cmake --trace, 打印出cmake运行的每一行脚本
- cmake --trace-source=打印出中运行的每一行脚本
1.6. 指定编译类型
-DCMAKE_BUILD_TYPE:Debug或Release或RelWithDebInfo
-DCMAKE_INSTALL_PREFIX: 指定安装路径,默认为/usr/local
-DBUILD_SHARED_LIBS: 是否编译共享库,ON or OFF
-DBUILD_TESTING: 是否编译tests
2. Do's and Don'ts
2.1. 不要做
- 不要使用全局函数: link_directories, include_libraries等
- 非必要不使用PULIBC限制
- 不要用glob匹配文件:当新增文件时,除非重新跑一遍cmake, make或其他编译工具将不会感知到新文件
- lib不要直接link到最终的target
- link lib时不要忽略PUBLIC/PRIVATE关键字
2.2. 要做
- 把cmake当做正经代码维护
- 站在target的角度思考
- 善用alias
- 将通用功能抽象成函数或宏
- 函数名统一小写
- 善用cmake_policy
2.3. 选择cmake版本
2.3.1. os支持
- 3.4: The bare minimum. Never set less.
- 3.7: Debian old-stable.
- 3.10: Ubuntu 18.04.
- 3.11: CentOS 8 (use EPEL or AppSteams, though)
- 3.13: Debian stable.
- 3.16: Ubuntu 20.04.
- 3.19: First to support Apple Silicon.
- latest: pip/conda-forge/homebew/chocolaty, ect.
笔者目前用的是3.16
2.3.2. 特性
- 3.8: C++ meta features, CUDA, lots more
- 3.11:
IMPORTED INTERFACE
setting, faster, FetchContent,COMPILE_LANGUAGE
in IDEs
- 3.12: C++20,
cmake --build build -j N
,SHELL:
, FindPython
- 3.14/3.15: CLI, FindPython updates
- 3.16: Unity builds / precompiled headers, CUDA meta features
- 3.17/3.18: Lots more CUDA, metaprogramming
3. What's new in in CMake
- cmake 3.0: interface libraries, 允许将一组头文件抽象成一个lib
- cmake 3.1: 支持c++11和compile features
- cmake 3.2: 支持UTF-8
- cmake 3.4: 支持swift语言,支持ccache
- cmake 3.5: 支持ARM平台
- cmake 3.6: 支持clang-tidy
- 其他:略
4. 基础知识
指定最小版本
cmake_minimum_required(VERSION 3.1)
创建工程
project(MyProject VERSION 1.0 DESCRIPTION "Very nice project" LANGUAGES CXX)
新增executable
add_executable(one two.cpp three.h)
新增library
add_library(one STATIC two.cpp three.h)
用户可指定STATIC、SHARED、MODULE 。缺省使用BUILD_SHARED_LIBS选项。
INTERFAC用于创建头文件库
ALIAS用于指定库的别名.
绑定头文件路径
target_include_directories(one PUBLIC include)
PUBLIC表示所有依赖lib的target都自动绑定了该头文件路径
PRIVATE表示该头文件路径仅对本target有效
INTERFACE表示该头文件路径仅对依赖该lib的target生效
PUBLIC = PRIVATE + INTERFACE
5. 变量和cache
定义变量
set(MY_VARIABLE "value")
访问变量时使用${MY_VARIABLE}
变量仅在当前作用域内有效。
定义变量(多个值)
set(MY_LIST "one" "two")
等效于
set(MY_LIST "one;two")
属性
set_property(TARGET TargetName PROPERTY CXX_STANDARD 11) set_target_properties(TargetName PROPERTIES CXX_STANDARD 11) get_property(ResultVariable TARGET TargetName PROPERTY CXX_STANDARD)
6. cmake编程
6.1. 控制流
if(variable) # If variable is `ON`, `YES`, `TRUE`, `Y`, or non zero number else() # If variable is `0`, `OFF`, `NO`, `FALSE`, `N`, `IGNORE`, `NOTFOUND`, `""`, or ends in `-NOTFOUND` endif() # If variable does not expand to one of the above, CMake will expand it then try again
There are a variety of keywords as well, such as:
- Unary:
NOT
,TARGET
,EXISTS
(file),DEFINED
, etc. - Binary:
STREQUAL
,AND
,OR
,MATCHES
(regular expression),VERSION_LESS
,VERSION_LESS_EQUAL
(CMake 3.7+), etc. - Parentheses can be used to group
generator-expressions
target_compile_options(MyTarget PRIVATE "$<$<CONFIG:Debug>:--my-flag>")
当使用debug编译时,加上--my-flag编译选项
That last one is very common. You'll see something like this in almost every package that supports installing:
target_include_directories( MyTarget PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include> )