CMake Notes
文章目录
教程
官方
入门教程
中文翻译
cmake 语言语法(语法规范)
cmake 构建系统(如何编译)
生成表达式
书 Mastering CMake
- hahack.com更基础
- 进阶
- 完整详细的
静态库、动态库和动态加载库教程
c 和 cpp 编译工具
参考:
工具:
gcc 和 g++
- compiler, 编译器
- eg:
gcc -o hello.o hello.c
ld
- linker, 链接器
- eg:
ld -o main hello.o nihao.o -lmylib
ldd
- obj 文件依赖打印工具,print shared object dependencies
- 用来分析一个 可执行文件、obj 文件、so 文件或 .a 文件的依赖库(.so 文件)存放位置
eg:
1 2 3 4 5 6 7 8 9 10$ ldd /bin/ls linux-vdso.so.1 (0x00007ffcc3563000) libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f87e5459000) libcap.so.2 => /lib64/libcap.so.2 (0x00007f87e5254000) libc.so.6 => /lib64/libc.so.6 (0x00007f87e4e92000) libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f87e4c22000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f87e4a1e000) /lib64/ld-linux-x86-64.so.2 (0x00005574bf12e000) libattr.so.1 => /lib64/libattr.so.1 (0x00007f87e4817000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f87e45fa000)- 查看哪个 .so 文件缺失(找不到)
objdump
- 用来打印,object 文件的各种不同信息,例如:汇编内容打印(反汇编)
- 适用场景:编辑器开发者,使用编译器编译程序的人并不适合使用
eg:
- 反汇编:
objdump -d /usr/bin/ls - 源码解析:
objdump -s /usr/bin/ls
- 反汇编:
readelf
- 打印 object 文件的各种信息,类似
objdump eg:
查看动态库信息部分:
readelf -d /usr/bin/ls- 查看依赖那些 .so 文件
- 打印 object 文件的各种信息,类似
命令行
参考:
初始化
- cmake -S <source_dir> -B <build_dir>
build
cmake –build <build_dir> –target <target_to_run>
- 作用:build 指定的 target
eg:
1 2 3 4 5 6 7cmake --build ./build --target main # 构建target-->main cmake --build ./build --target install # target --> install cmake --build ./build --target package # cpack 打包 binary 包 cmake --build ./build --target package_source # cpack 打包 source 包
install
cmake –install <build_dir>
- 注:
cmake --build <build_dir> --target install的快捷调用方法
- 注:
指定 编译器
- CC=clang CXX=clang++ cmake ..
单文件
| |
命令解析
cmake_minimum_required
- 版本限制
project
- project("Project information")
- 项目信息
add_executable
- 生成目标,可执行文件
多文件
没有子文件夹 No sub_dir
同单文件没什么区别
| |
含文件夹 with sub_dir
根文件夹,即项目根目录
| |
命令解析
子文件夹 sub_dir —> 变量
- aux_source_directory
aux_source_directory(dir_path, DIR_SRCS)
- $DIR_SRCS 是变量名
子文件夹 sub_dir —> 具体指出来
- add_subdirectory
- add_subdirectory(sub_dir_name)
链接库
- target_link_libraries
target_link_libraries(executable_file, link_lib_name)
link_lib_name
- 链接库的变量名
- 将在这个库的所在文件夹定义
子文件夹
| |
命令解析
aux_source_directory
- aux_source_directory(. link_lib_path_name)
- 把链接库路径,赋值给变量$link_lib_path_name(即:上例中的${DIR_LIB_SRCS})
add_library
- add_library(link_lib_name ${link_lib_path_name})
- 定义链接库变量名称
注意
aux_source_directory(. path_var_name)
- 给路径设置一个名称
add_library(lib_name ${lib_path_var_name})
- 生成链接库名称
target_link_libraries(executable_file lib_name)
- 把链接库,添加到可执行文件 target(建立对应关系)
其它,命令
- add_subdirectory
- cmake_minimum_required(VERSION 2.8)
- project
实战
技巧
| |
来源: https://legends2k.github.io/note/install-irony-win32/
目的:编译 irony-server, rtags on windows.
命令
find_package
project 命令
参考:http://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20151012/305757.html
policy CMP0048 NEW 错误
| |
configure_file 命令
TutorialConfigure.h.in 设置
原教程
1 2 3 4// 原教程 // the configured options and settings for Tutorial #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@实际(生效)文件
1 2#define Tutorial_VERSION_MAJOR 2 // @Tutorial_VERSION_MAJOR@ #define Tutorial_VERSION_MINOR 3 //@Tutorial_VERSION_MINOR@- 注意:使用正常的 c++ 宏定义
CMakeLists.txt 配置
1 2 3 4 5 6 7 8configure_file(TutorialConfig.h.in TutorialConfig.h) add_executable(Tutorial tutorial.cxx) # target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}/") target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}" )- 指定 include 文件目录
- target_link_libraries 要使用 PUBLIC + 路径
头文件路径
注意:
- cmake 中 include() 命令是用来添加 list 文件(.cmake 文件或 CMakeLists.txt 文件)的
- include_directories() 后缀的命令,才是用来指定 c 语言中的头文件目录的命令
include_directories
| |
target_include_directories
把 include_directory 添加到特定的 your_lib 和 your_excutable
| |
链接库
参考:
- Shared Libraries: Understanding Dynamic Loading
动态库 debug, Cheat Sheet
链接库类型
normal 正常类型
STATIC
静态链接库
- .a 或 .lib 文件
SHARED
动态链接库
- .so 或 .dll 文件
MODULE
- 不导出 symbol 的库,一般通过 dlopen 打开
OBJECT
- non-archieval collection of object files, 收集 obj 文件,作为中间变量
参考:
调用链接库方法
动态链接库
| |
静态链接库
| |
add_library()
特点:
默认添加的是静态库(
STATIC)- 修改默认行为的方法:
set(BUILD_SHARED_LIBS ON) - 修改变量
BUILD_SHARED_LIBS
- 修改默认行为的方法:
Windows 动态链接库 定义格式
函数
头文件
注意
- 定义的宏 PYTHON_TOOLS_EXPORT
放置在 函数声明的最前面
- 无指针时,也可放在类型声明后面
| |
cpp 代码文件
- 只需添加对应的 head file 即可
- 不必在使用定义的宏【 __declspec(dllexport)】 加以修饰
class 类
头文件 head file
在 class 后面添加 __declspec 的宏即可
实践证明
1 2 3 4 5 6 7 8 9 10 11// * 错误 DLLEXPORT class Hello { public: void print(); } // * 正确 class DLLEXPORT Hello { public: void print(); }
- 不用每个方法,都添加宏修饰符
| |
cpp 代码文件
- 只要 include 相应的头文件即可
切换 Release 与 Debug 版本
| |
- cmake -DCMAKE_BUILD_TYPE=Release ..
帮助 与 find_package
查找 cmake 帮助
| |
通过 cmake –help-module FindPythonLibs 查找 Module 的帮助文档,可以查看这个 Module 定义的变量的 “真实名称”
注意
- 这个真实名称很重要,大小写,版本号等,很容易错误(网上的人,也很多写的是错误的。)
判断平台 Platform
| |
https://gitlab.kitware.com/cmake/community/-/wikis/doc/tutorials/How-To-Write-Platform-Checks
注意
- 这里是 MATCHES, 不是 MATCH
CMAKE_SYSTEM_NAME
CMAKE_SYSTEM_NAME ==> Linux-4.15.0-29deepin-generic CMAKE_SYSTEM ==> Linux
- 相比 Win32, Unix, APPLE (存储的是 trule, false)更据跨平台特性
Windows 动态链接库
| |
注意
- 有时候,必须要把 dll 文件复制到需要的目录,(通常只是指定目录)
实例, CMake
参考:
全面的教程
中文简略说明
| |
在 dll 的代码中
1 2 3 4#include "myDLLExports.h" class MYDLLExports A { // definition }
GENERATE_EXPORT_HEADER
快捷方法
使用 CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS
Create dlls on Windows without declspec() using new CMake export all feature
1 2set(CXX_STANDARD 11) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)注意
- 对全局变量的兼容性太差
列表类型, list
| |
变量,cmake 预定义变量
cmake-variables(7) — CMake 3.0.2 Documentation
directories
project variable
- CMAKE_SOURCE_DIR –> PROJECT_SOURCE_DIR
- CMAKE_BINARY_DIR –> PROJECT_BINARY_DIR
current variable
- CMAKE_CURRENT_SOURCE_DIR
- CMAKE_CURRENT_BINARY_DIR
特殊变量
CMAKE_PREFIX_PATH
CMAKE_PREFIX_PATH — CMake 3.24.0-rc3 Documentation
- 一个通过分号“;”分割的路径 path 列表, eg: "/usr;/usr/local"
- 用来指定安装库,软件等位置,用于用户需要查找的东西
- 作用工具:find_package, find_program, find_library, find_file, find_path 等
如何被使用的
- 当 find_library 等命令调用 CMAKE_PREFIX_PATH 时, 会搜索其目录下的 bin,include,lib 等目录
设定者:project 使用它
我的理解:
- 应当是用户在写一个“项目”时,定义它
- 默认值:为空
CMAKE_LIBRARY_PATH
CMAKE_LIBRARY_PATH — CMake 3.24.0-rc3 Documentation
- 用途:find_library
- 分号“;”分割的路径 path 列表,
- 设定者:project
- 默认值:为空
CMAKE_FRAMEWORK_PATH
CMAKE_FRAMEWORK_PATH — CMake 3.24.0-rc3 Documentation
- 用途:find_library, find_package, find_path, find_file
- 平台:macOS
- 分号“;”分割的路径 path 列表
CMAKE_CURRENT_SOURCE_DIR
当前 CMakeLists.txt 文件所在目录
- This the full path to the source directory that is currently being processed by cmake.
CMAKE_CURRENT_LIST_DIR vs CMAKE_CURRENT_SOURCE_DIR
参考:
辨析:
- 当 CMakeLists.txt 自身生效时,两者是同一个文件
当 module_name.cmake 文件被其他文件(一个 CMakeLists.txt)通过
include(module_path/module_name.cmake)命令调用时- CMAKE_CURRENT_LIST_DIR 指的是: module.name.cmake 文件所在目录
project_root/module_path/ - CMAKE_CURRENT_SOURCE_DIR 指的是:发起调用方(CMakeLists.txt)所在目录
project_root - 注意: listfile 可以是 module.cmake 文件,也可以是另一个 CMakeLists.txt 文件
- CMAKE_CURRENT_LIST_DIR 指的是: module.name.cmake 文件所在目录
CMAKE_CURRENT_LIST_DIR 有动态作用域
- 永远是正在处理的 listfile 所在文件目录
- 处理 include 命令前,是发起调用方所在目录
- 处理 include 命令过程中,是被调用文件所在目录
- 处理 include 命令后,又变成调用方所在目录
CMAKE_SOURCE_DIR vs PROJECT_SOURCE_DIR
辨析:
CMAKE_SOURCE_DIR: 项目根目录所存在的 CMakeLists.txt 文件所在目录
- 需要有 project() 命令
- 这里的项目根目录是 cmake 项目的根目录
- PROJECT_SOURCE_DIR: 最近的 有 project() 命令的 CMakeLists.txt 文件所在目录
CMAKE_CURRENT_SOURCE_DIR: 当前 CMakeLists.txt 文件所在文件目录
- 不一定有 project() 命令
CMAKE_CURRENT_LIST_DIR: cmake 代码所在文件,不一定是 CMakeLists.txt 文件
- 不一定有 project() 命令
find_library
默认搜索的路径
find_library — CMake 3.24.0-rc3 Documentation
下面的内容由 NO_DEFAULT_PATH 反推
被 find_package 在内部嵌套调用
先搜索当前的 package
- 搜索 cmake 指定的 <PackageName>_ROOT
- 搜索 shell 的环境变量 ENV 中的<PackageName>_ROOT, 如果有的情况下
再搜索父 package
- 搜索 cmake 指定的<ParentName>_ROOT
- 搜索 shell 环境变量<ParentName>_ROOT
禁用方法
- set CMAKE_FIND_USE_PACKAGE_ROOT_PATH to FALSE
总之
- 通过 ROOT 变量指定,如:BOOST_ROOT, Python_ROOT
- 再通过 find_package 调用
搜索 cmake 定义的变量
cmake-specific cache variable
- 命令行通过-DVAR=value 指定
- set(VAR value) 指定
禁用方法
- This can be skipped if NO_CMAKE_PATH is passed or by setting the “CMAKE_FIND_USE_CMAKE_PATH” to FALSE.
典型使用方法
- 定义 CMAKE_PREFIX_PATH, CMAKE_LIBRARY_PATH
搜索环境变量
- cmake 指定的环境变量
格式
- windows, 分号分割
- linux, 冒号分割
- 通过变量 CMAKE_LIBRARY_ARCHITECTURE 指定架构
禁用方法
- NO_CMAKE_ENVIRONMENT_PATH is passed or by setting the “CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH” to FALSE.
HINTS 和 PATHS
HINTS
- 通常是系统产生的
- 如别的 find_package 找到的等
PATHS
- 用户硬性指定的
系统环境变量
- shell 定义的环境变量
典型环境变量
- INCLUDE
- PATH
windows
通过变量 CMAKE_LIBRARY_ARCHITECTURE 指定架构
- 如:x86 等
用途
- 搜索<prefix>/lib/<arch>
platform files 中定义的 cmake 变量
- 系统平台
相关变量
- CMAKE_SYSTEM_PREFIX_PATH
- CMAKE_SYSTEM_LIBRARY_PATH
目的
- 指定系统默认的路径等
- 如:/usr/bin /usr/include /usr/lib (这里是猜测)
自定义搜索路径
指定 PATHS 选项
调用形式
1 2find_library (<VAR> NAMES name PATHS paths... NO_DEFAULT_PATH) find_library (<VAR> NAMES name)- 先仅仅搜索自定义路径
- 再搜索默认路径
这种方法的优点
- 前面自定义搜索,如果已经找到,后面的搜索就不会再调用了
定义 CMAKE_FIND_ROOT_PATH
- 格式:冒号分割的路径列表
- 用途:find_library, find_package
作用方式
- 添加搜索路径到,所有搜索路径的最前面,prepend
- 默认值:为空
Modern CMake
面向对象
- target 类似对象
与 target 有关的函数 是 对应的方法
- target_link_libraries
- target_include_directories
target_compile_features
编译特征– 指定 compiler 必须具有的特征
1arget_compile_features(mylib PUBLIC cxx_constexpr)
target_compile_options
1target_compile_options(MyTarget PRIVATE "$<$<CONFIG:Debug>:--my-flag>")- 注:如果 BUILD_TYPE 是 Debug, 添加 –my-flag 选项参数
- target_compile_definitions
set_target_property – cmake 提供属性
1 2set_property(TARGET TargetName PROPERTY CXX_STANDARD 11) set_target_properties(TargetName PROPERTIES CXX_STANDARD 11)get_target_property
1get_property(ResultVariable TARGET TargetName PROPERTY CXX_STANDARD)
target_link_libraries 关键字
参考:
说明:
PRIVATE
- header file 不包含 依赖库内容(即库的头文件,以下类推)
- source file 包含 依赖库内容
INTERFACE
- header file 包含 依赖库内容
- source file 不包含 依赖库内容
PUBLIC
- header file 包含 依赖库内容
- source file 包含 依赖库内容
PRIVATE
- 仅仅 target 需要
INTERFACE
- 使用 target 时需要
PUBLIC
- 当前 target 需要
- 使用 target 时也需要
例子 A:
1 2 3 4add_library(another STATIC another.cpp another.h) target_link_libraries(another PUBLIC one) target_include_directories(one PUBLIC include)
意义
作用
- 依赖的继承关系说明
target_include_directories
这些关键字
- 对于 executable 无特别意义
对于 链接库
PUBLIC:
- 当前 lib 需要给定链接库
- 以后使用当前 lib 的其它 target 也需要这个链接库,找不到,报错
PRIVATE:
- 只有当前 lib 需要 给定链接库
- 用户使用 lib 时,不需要这个链接库
INTERFACE:
- 当前 lib 不需要 给定链接库
- 但是,用户使用 lib 时,需要这个链接库
推荐的项目结构(文件组织结构)
变量
变量类型
类型:
- regular
- 不会被缓存的变量
- cache
- 会被缓存的变量
| |
注释:
- 缓存
- 即,被存储到 CMakeCache.txt 文件中
异同:
-D 设置
- regular 不能被设置
- cache 可以被设置
覆盖
- regular 会被 cache 覆盖
缓存数据的影响
- cache 变量,第一次执行 cmake 命令时缓存到文件中
第二次直接读取缓存数据
- 即使修改 CMakeLists.txt 文件也无效
- 只能通过 cmake -D 覆盖修改
作用域
- regular 有作用域
- cache 是全局作用域,没有局部作用域
生效时间
- CACHE set 语句只有第一次生效,后续会被跳过
无 CACHE set 语句,总是被执行
1 2 3 4 5 6 7cmake_minimum_required(VERSION 2.8) project(foo NONE) set(A "123") # 每次执行 cmake 命令时,都会被执行 set(A "456" CACHE STRING "") # 只有第一次执行 cmake 命令时,会被执行 message("A: ${A}")
regular 变量
cache 变量
参考:
特性:
CACHE 类型
- BOOL: bool
- STRING: 字符串
- PATH: 文件夹路径
- FILEPATH: 文件路径
作用
- cmake-gui 配置 cache 变量时,提供个性化的 widget
FORCE 强制修改
cache 的强制修改, 使用
FORCE1set(A "123" CACHE STRING "" FORCE)
STRINGS 枚举值
作用
- 设定用户可以选择的值
例子:
1 2 3set(FOO_CRYPTO "OpenSSL" CACHE STRING "Backend for cryptography") set_property(CACHE FOO_CRYPTO PROPERTY STRINGS "OpenSSL;Libgcrypt;WinCNG") #列表
INTERNAL 内部使用
作用
- 不被 cmake-gui 显示用来设置
特点
- 默认时 STRING 类型,设置为 FORCE
ADVANCE 高级功能
作用
- cmake -gui 高级功能配置
option 命令
作用
- 创建一个 set(name CACHE BOOL) cache 变量
- 快捷工具
删除命令
- 方法: unset(name CACHE)
- 删除 cache 在文件中的缓存
- 注意: unset(name) 对 cache 变量无效,只对 regular 变量有效
注意:
cache 变量是全局变量
- 推荐加上前缀(如 target 名称),避免名称冲突
环境变量
参考:
作用:
- 系统环境变量的处理
方法:
- 读取: 通过
$ENV{env_name}获取 设置:
set(ENV{USERNAME} "Jane Doe")注意:
- 这里的设置只是临时性的(只在 config 阶段)
- 在 build 时,环境变量,又变成了系统默认值(shell 中的设置)
- 删除:
unset(ENV{USERNAME})
特点:
可继承性
- 子进程能够获取到父进程中 CMakeLists.txt 的环境变量设置
作用域
类别
产生新作用域的情况:
add_subdirectory()
- 在子文件夹中的 CMakeLists.txt 中产生新作用域
function()
- 定义新函数
- 函数中有自己的作用域
不产生新作用域:
macro()
- 定义宏
include()
- 子模块 .cmake 文件
取消父作用域的值
unset(your_variable)
cmake listfiles 文件
参考:
类型
分类
CMakeLists.txt 文件
- 通过 add_subdirectory 被执行
*.cmake Module 文件
- 通过 cmake -P 命令行被执行
- 通过 include 命令被执行
rpath 处理(动态库搜索路径)
参考:
- cmake 处理 rpath: RPATH handling · Wiki · CMake / Community · GitLab
- conan 处理 rpath: Manage RPATHs — conan 1.43.4 documentation
动态库(.so 文件)搜索位置
RPATH
- 一个路径.so 文件存放列表
- 被 linked into 可执行文件
- 如果 RUNPATH 存在会被忽略
- 大部份 unix 支持它
- 一般它的优先级高于
LD_LIBRARY_PATH 添加方法:
ld 命令:
ld -rpath=/given/path- 注意
ld -rpath-link=dir只有链接时起作用,运行时无效,-rpath在 runtime 运行时也有效
- 注意
gcc 命令:
gcc -Wl,rpath,dir- 参考:gcc - I don't understand -Wl,-rpath -Wl, - Stack Overflow
- gcc 通过
-Wl,option_name把 link 选项传递给 linker(这里是 ld 命令)
LD_LIBRARY_PATH
环境变量
- 用户自行配置
RUNPATH
- 类似 RPATH, 但是搜索优先级在 LD_LIBRARY_PATH 之后
- 注意:只有新版 unix 支持
/etc/ld.so.conf
- ld.so 库的配置文件,额外的动态库存放目录
builtin 内建目录
- eg: /lib, /usr/lib
为什么除掉 builtin 内建目录外还有各种目录
- 用户可能安装了自己的库版本
macos 的 rpath 处理方法
参考:
- macos 中 RPATH 的搜索有限级的 DYLD_LIBRARY_PATH 之后
- RPATH 通过 @loader_path 和 @executable_path 来定义
cmake 与 rpath
cmake 中 rpath 有两种使用场景:
build tree 编译位置
- cmake 编译 target 产生的位置,运行 target 时,rpath 指向 build tree 中设置的动态库路径
install location 安装位置
- 安装后,动态库的路径改变了,因此 rpath 也要改变
注意:
- 安装时 rpath 的 cmake 自动修改是通过 install() 命令触发的,因此手动复制 target, 不会触发 rpath 改变
与 rpath 相关的 cmake 变量 (在顶层 CMakeLists.txt 中设置):
CMAKE_INSTALL_RPATH- 安装后的 rpath 设置(这里我把它归类到,安装时 rpath)
- 默认为空: ""
关于 $ORIGIN 的处理:
- 参考:CMP0095 — CMake 3.24.1 Documentation
在 v3.15 及以前需要转义:
set(CMAKE_INSTALL_RPATH "\\\${ORIGIN}/../lib")- 为什么需要转义:因为在 ld.so 中就是 ${ORIGIN} 或 $ORIGIN, cmake 中变量也是这种写法, 不做转义处理,cmake 会把 ${ORIGIN} 转换为空串 ""
在 v3.16 及以后不需要转义:
set(CMAKE_INSTALL_RPATH "${ORIGIN}/../lib")- 这是因为,cmake 添加了针对这种情况的特殊处理
- 在 v3.24.1 及以后通过
cmake_policy(set CMP0095 NEW)来支持新型写法(默认使用旧式写法)
CMAKE_BUILD_RPATH- build tree 中 target 使用的运行时 rpath,
编译时rpath,(v3.8 新特性) - 注意:它在 install location 不起作用(无效)
- build tree 中 target 使用的运行时 rpath,
CMAKE_BUILD_RPATH_USE_ORIGIN是否允许 build tree 中 target 的 rpath 使用 ${ORIGIN}, (v3.14 新特性)
- cmake 传统上,build tree rpath 使用的是绝对路径
- 默认不支持:FALSE
- 好处:允许把编译的代码直接移动的别的地方
- 注意:它不影响 BUILD_RPATH 设置内容
CMAKE_SKIP_BUILD_RPATH- 是否跳过
编译时 rpath - 默认:不跳过,即添加编译时 rpath, FALSE
- 是否跳过
CMAKE_BUILD_WITH_INSTALL_RPATH- 是否把 install location rpath 安装时 rpath 编译进去
- 默认:不把
安装时 rpath编译进去,即 FALSE
CMAKE_INSTALL_RPATH_USE_LINK_PATH- 安装时 rpath, 是否使用搜索到的 rpath:
- 默认:不使用自动搜索到的 rpath, FALSE
- 注意:上面的选项默认值都是
否("" 或 FALSE)
适用默认设置,不修改 rpath
| |
rpath 实际使用方法:
- 通过 LD_LIBRARY_PATH
- 通过默认的 lib path(即 builtin rpath)
- 因此,这样就需要配置好 LD_LIBRARY_PATH 或者把依赖的动态库(.so)安装到指定的默认路径
完全决定路径 rpath, 不依赖 LD_LIBRARY_PATH
| |
rpath 实际使用情况:
编译时,使用的是自动寻找到的 rpath
- 可以在 build tree 中执行 executable
安装时,会自动更改 rpath 到 CMAKE_INSTALL_PREFIX
- 可以在安装完成后执行 executable
- 但是这里不包括用户通过 add_library() 新创建的 .so 文件
需要用户使用 install 命令安装到相应的位置:
install(TARGETS bar DESTINATION lib)- 即:
${CMAKE_INSTALL_PREFIX}/lib
完全不使用 rpath
CMAKE_SKIP_RPATH, 设置为 True 即可,它会导致其他 rpath 相关设置自动被忽略
不同 target 不同 rpath
- 通过 set_target_properties() 实现
- 上面使用的都是全局变量,省略 CMAKE_ 前缀就是 target 的私有属性
相关属性:
INSTALL_RPATH
- 安装后的 rpath 设置(这里我把它归类到,安装时 rpath)
- 默认为空: ""
SKIP_BUILD_RPATH
- 是否跳过
编译时 rpath - 默认:不跳过,即添加编译时 rpath, FALSE
- 是否跳过
BUILD_WITH_INSTALL_RPATH
- 是否把 install location rpath 安装时 rpath 编译进去
- 默认:不把
安装时 rpath编译进去,即 FALSE
INSTALL_RPATH_USE_LINK_PATH
- 安装时 rpath, 是否使用搜索到的 rpath:
- 默认:不使用自动搜索到的 rpath, FALSE
注意:
- 上面的选项默认值都是
否("" 或 FALSE) - 这些 target 属性都是默认通过对应的 CMAKE_ 开头变量初始化的
- 上面的选项默认值都是
cmake 处理 $ORIGIN
cmake 和 Windows 中的 rpath
参考:
宗旨:
- Windows 中不存在 rpath
- Windows 会在 executable 所在位置寻找 .dll 文件
cmake 和 conan rpath 处理
conan 默认设置跳过 rpath 设置:
conan_basic_setup()会设置 set(CMAKE_SKIP_RPATH 1) (在 macos 中)
跳过 conan 默认 rpath 设置方法:
conan_basic_setup(KEEP_RPATHS)
设置 conan 支持 rpath 方法:
配置 conan_basic_setup
1 2include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) conan_basic_setup(KEEP_RPATHS)配置 cmake 的 rpath 设置
1 2 3 4 5 6 7if (APPLE) set(CMAKE_INSTALL_RPATH "@executable_path/../lib") else() set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib") endif() set(CMAKE_BUILD_WITH_INSTALL_RPATH ON)- 解释: 把 install location 的 rpath 编译进 target
- 目的: 让 target 知道在哪里找 .dll 或 .so 文件
设置 conanfile.txt, 处理 .so 或 .dll 方法
1 2 3 4 5 6 7[requires] poco/1.9.4 [imports] bin, *.dll -> ./bin # Copies all dll files from packages bin folder to my "bin" folder lib, *.dylib* -> ./lib # Copies all dylib files from packages lib folder to my "lib" folder lib, *.so* -> ./lib # Copies all so files from packages lib folder to my "lib" folder- 解释: 把 .so 文件复制到
./lib文件夹下,一般是 build/lib
- 解释: 把 .so 文件复制到
总结:
- conan 不能自动让 target 知道 shared library 在哪里
- conan 只能把对应的.so 或 .dll 文件复制到给定的目标位置
- 用户需要 hard code 硬编码,指定出安装的目标位置(rpath)
面向对象命令(面向 target)
命令列表:
target_include_directories()
- 指定 target 的头文件目录
target_link_libraries()
- 执行 target 需要链接的
链接库
- 执行 target 需要链接的
target_compile_definitions()
- C 语言 preprocessor 预处理器定义指令,eg:
#define PI
- C 语言 preprocessor 预处理器定义指令,eg:
target_compile_options()
- 传递给编译器的选项,例如传递给 gcc 命令
- 值通过 ";" 分号分割
target_compile_definitions()
- 用来添加预处理器指令
- 指令可以有值,也可以没有
- 添加的
-D定义前缀会被 cmake 自身忽略 eg:
1 2 3 4 5 6 7 8# * 下面的四种方法效果等价 target_compile_definitions(foo PUBLIC FOO) # --> #define FOO target_compile_definitions(foo PUBLIC -DFOO) # -D removed target_compile_definitions(foo PUBLIC "" FOO) # "" ignored target_compile_definitions(foo PUBLIC -D FOO) # -D becomes "", then ignored # * 添加 define 的值 target_compile_definitions(foo PUBLIC FOO=1) # --> #define FOO 1
target_compile_options()
- 定义的是传递给编译器(eg: gcc)的命令选项
eg:
1target_compile_options(my_target PUBLIC -Wall -Wextra -pedantic -Werror)
interface 类属性
参考:
作用:
- 通过有 INTERFACE_ 开头的属性,向 consumer 传递相应属性
eg:
- my_lib 有 INTERFACE_POSITION_INDEPENDENT_CODE 属性
- my_exe 通过 add_executable(my_exe PUBLIC my_lib) 语句继承了 my_lib
- 这样,my_exe 就会获得 POSITION_INDEPENDENT_CODE 属性,(通过 my_lib 传递过来的)
继承触发语句
add_executable()eg: add_executable(my_exe PUBLIC my_lib)
- my_exe 从 my_lib 继承属性
add_library()
属性特点
- 当前 target 不会被 interface 类属性直接影响
使用者(consumer)会被影响(以
target_link_libraries()):如果 consumer 是通过
PUBLIC链接的,consumer 会也拥有这个属性- 受影响
如果 consumer 是通过
PRIVATE链接,consumer 不会有这个属性- 不受影响
如果 consumer 是通过
INTERFACE链接的,consumer 不会有这个属性,只做属性传递部分受影响
- 中继这个属性,但是不会拥有
- 如果有
孙子 consumer, consumer 会把这个属性传递给孙子 consumer
相关 target 属性
参考:
以
INTERFACE_开头的 target 属性eg:
INCLUDE_DIRECTORIESVs.INTERFACE_INCLUDE_DIRECTORIESCOMPILE_OPTIONSVs.INTERFACE_COMPILE_DEFINITIONS
设置工具
类似
target_link_libraries () + INTERFACE- eg:
target_link_libraries(my_target INTERFACE my_lib)
- eg:
类似
set_property() + INTERFACE_ 开头属性- eg:
set_property(TARGET my_target PROPERTY INTERFACE_LINK_LIBRARIES my_lib)
- eg:
设置冲突问题(兼容性)
参考:
为什么可能冲突:
- 可以通过
set_property() + POSITION_INDEPENDENT_CODE直接设置一个属性 - 也可以通过 INTERFACE_POSITION_INDEPENDENT_CODE 继承获得 POSITION_INDEPENDENT_CODE
- 如果
两个来源的值不同,就会导致冲突
cmake 对这种冲突的处理方式:
- target 本身属性和依赖 lib 传递的 INTERFACE_ 属性冲突,报 diagnostic
- target 给定属性的的多个依赖 lib 之间的 INTERFACE_ 属性冲突,直接报错 error
兼容性处理方法
方法:
- 通过把相应的属性注册到一个列表中,来根据这个列表的名称,决定处理兼容性的方式
处理工具列表
COMPATIBLE_INTERFACE_BOOL:
布耳类型- 放入这个列表的属性,要保证不同依赖的这个属性的设定值,取值一致
- 兼容性规则:要么都是 ON,要么都是 OFF
- 不通过检查处理:报 diagnostic
COMPATIBLE_INTERFACE_STRING:
字符串类型- 检查规则: 取值一致的字符串
COMPATIBLE_INTERFACE_NUMBER_MAX:
数值--最大值- 检查规则:在 generate time(生成期), 取最大值
COMPATIBLE_INTERFACE_NUMBER_MIN:
数值--最小值- 检查规则:在 generate time(生成期), 取最小值
举例:
字符串类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14add_library(lib1Version2 SHARED lib1_v2.cpp) set_property(TARGET lib1Version2 PROPERTY INTERFACE_LIB_VERSION 2) set_property(TARGET lib1Version2 APPEND PROPERTY # 添加到字符串兼容类型 COMPATIBLE_INTERFACE_STRING LIB_VERSION ) add_library(lib1Version3 SHARED lib1_v3.cpp) set_property(TARGET lib1Version3 PROPERTY INTERFACE_LIB_VERSION 3) add_executable(exe1 exe1.cpp) target_link_libraries(exe1 lib1Version2) # LIB_VERSION will be "2", 没有冲突时 add_executable(exe2 exe2.cpp) target_link_libraries(exe2 lib1Version2 lib1Version3) # Diagnostic, 冲突时数值--最大值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16add_library(lib1Version2 SHARED lib1_v2.cpp) set_property(TARGET lib1Version2 PROPERTY INTERFACE_CONTAINER_SIZE_REQUIRED 200) set_property(TARGET lib1Version2 APPEND PROPERTY # 添加到兼容类型 COMPATIBLE_INTERFACE_NUMBER_MAX CONTAINER_SIZE_REQUIRED ) add_library(lib1Version3 SHARED lib1_v3.cpp) set_property(TARGET lib1Version3 PROPERTY INTERFACE_CONTAINER_SIZE_REQUIRED 1000) add_executable(exe1 exe1.cpp) # CONTAINER_SIZE_REQUIRED will be "200" target_link_libraries(exe1 lib1Version2) add_executable(exe2 exe2.cpp) # CONTAINER_SIZE_REQUIRED will be "1000" target_link_libraries(exe2 lib1Version2 lib1Version3) # 产生冲突,取最大值
Debug 方法, INTERFACE_ 属性来源
参考:
方法:
- 通过设置
CMAKE_DEBUG_TARGET_PROPERTIES列表实现 - 把要 debug 的属性放入这个列表
例子:
| |
install() 命令
参考:
作用:
- 用来说明一个项目(其中的文件等资源)是如何安装的
用来生成安装脚本,用来在
安装时运行, 执行文件安装- 脚本被
make install调用 cmake installation module 触发 - vscode 通过 INSTALL target 触发
- 脚本被
接口类型
install(TARGETS ...)- 通过 add_library() 或 add_executable() 创建的 target
install(FILES ...)- 头文件,或任意文件
- 用来安装通用文件
install(PROGRAMS ...)任意可执行文件
- 注意:这里不是 target, 是实实在在的本地文件
与 install(FILES …) 接口的区别:
- 它会设置文件的可执行属性
- 例如:shell 脚本,非 cmake 项目编译生成的可执行文件等
install(DIRECTORIES ...)- 文件夹的安装
- 用来安装资源文件
- 例如:包含图片的文件夹,数据文件夹
install(SCRIPT ...)- 安装过程中执行的 cmake 脚本程序
指定要执行的 .cmake 文件
- eg:
install(SCRIPT message.cmake)
- eg:
install(CODE ...)- cmake 脚本程序
指定要执行的 cmake 代码,通过字符串指定
- eg:
install(CODE "MESSAGE(\"Installing My Project\")")
- eg:
install(EXPORT ...)- 安装 cmake 脚本程序,(从安装目录到到外部项目)
额外接口参数
DESTINATION安装位置
- 绝对路径:就是指定的安装位置
- 相对路径:相对与
CMAKE_INSTALL_PREFIX
PERMISSIONS安装文件权限
- 用来覆盖默认权限
- 注意:不同安装命令有默认权限
可选值
- OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE, GROUP_EXECUTE, WORLD_READ, WORLD_WRITE, WORLD_EXECUTE, SETUID, and SETGID.
CONFIGURATIONS安装配置
- 举例:Release, Debug
- 用来指定安装规则
install rule使用范围 - 例如 Release 配置只能用来在 CMAKE_BUILD_TYPE 是 release 类型时起作用
COMPONENT安装规则分类(组件)
举例:
Runtime: 运行需要的组件
- 注意:一个组件,可以是多个文件
- Development: 编译组件
- Document: man page 和 help files 等文档相关文件
- 作用:通过把 install rule 拆分成多个 component 组件,可以一次只安装部分组件或单个组件
注意:
- 组件名称不是 cmake 指定的,由项目(用户)自己指定
OPTIONAL可选规则
- 类似 python None, 用来处理 target 或文件不存在文件
- 不存在,也可以不报错
DIRECTORY 接口--文件名匹配
| |
匹配工具
PATTERN
- 通配符匹配
REGEX
- 正则匹配
排除工具:
EXCLUDE
- 用来反向匹配,排除不需要的文件名
调用例子
参考 cmake install 官方: Installing Files — Mastering CMake
打包 -- cpack 和 cmake package
参考:
- Packaging With CPack — Mastering CMake
- cpack-generators(7) — CMake 3.24.2 Documentation
- Home · Wiki · CMake / Community · GitLab
总体流程:
CMakeLists.txt 配置
- add_executable()等脚本配置
- install() 命令设置安装规则
- include(CPack) 命令,包含 CPack 模块
编译程序
- cmake -S . -B build
- cmake –build .
打包
cmake –build <build_directory> –target package
- 打包二进制可执行程序
cmake –build <build_directory> –target package_source
- 源码打包
命令行调用方法
make 命令法
1 2 3make package make package_sourcecpack 命令法
1 2 3 4 5cd <build_directory> cpack --config CPackConfig.cmake # 打包二进制可执行程序 cpack --config CPackSourceConfig.cmake # 源码打包cmake –target 命令法
1 2 3cmake --build <build_directory> --target package # 打包二进制可执行程序 cmake --build <build_directory> --target package_source # 源码打包
Windows + NSIS
参考:
相关变量
icon
- 安装包图标:CPACK_NSIS_MUI_ICON
卸载工具图标:CPACK_NSIS_MUI_UNIICON
- 即:uninstall.exe
安装向导标题栏名称前缀:CPACK_NSIS_INSTALLED_ICON_NAME
- 用于安装和卸载向导 wizard 标题栏
安装位置
- 推荐(默认)安装文件夹名称:CPACK_PACKAGE_INSTALL_DIRECTORY 或者 CPACK_NSIS_PACKAGE_NAME
是否允许用户修改 Windows 的 PATH 变量
- CPACK_NSIS_MODIFY_PATH
- 类型:Bool
向导文字背景图片
- CPACK_PACKAGE_ICON
协议(证书)文件
- CPACK_RESOURCE_FILE_LICENSE
开始菜单快捷方式
- 参考:Packaging With CPack — Mastering CMake
分类:
可执行文件类型:CPACK_PACKAGE_EXECUTABLES
- eg:
set (CPACK_PACKAGE_EXECUTABLES "cmake-gui" "CMake" ) 注意:
- 值是成对的
- 第一个值是可执行文件的名称,eg: cmake-gui
- 第二个值是快捷方式的名称,eg: CMake
- eg:
url 或到安装目录的链接:CPACK_NSIS_MENU_LINKS
- eg:
set (CPACK_NSIS_MENU_LINK "doc/cmake-${VERSION_MAJOR}.${VERSION_MINOR}/cmake-gui.html" "cmake-gui Help" "http://www.cmake.org" "CMake Web Site") 注意:
- 值是成对的
- 链接是目录时,使用的是相对于安装目录的相对路径
- eg:
桌面快捷方式
- CPack — CMake 3.24.2 Documentation
- CPACK_CREATE_DESKTOP_LINKS
- eg:
set (CPACK_CREATE_DESKTOP_LINKS cmake-gui) 注意:
- 它与开始菜单快捷方式 CPACK_PACKAGE_EXECUTABLES 相关联,使用的值是 CPACK_PACKAGE_EXECUTABLES 中设置过的
- 它是一个 list 类型,可以设置多个列表
FAQ
源码打包特别大问题
可能原因:
- 把 build 文件夹也打包进去了
递归压缩问题
- 把临时文件夹 build/_CPack_Packages 递归打包了
解决方法:
- 使用 CPACK_SOURCE_IGNORE_FILES
举例:
1 2set(CPACK_SOURCE_IGNORE_FILES "/CVS/;/\\\\\\\\.svn/;\\\\\\\\.swp$;\\\\\\\\.#;/#;/build/") include(CPack)- 注:这里跳过了 build 文件夹
add_subdirectory()
FAQ
添加失败 – 不是当前文件夹的子目录
ctest 测试
参考:
相关命令:
include(CTest)enable_testing()注意:
- 需要在项目根目录下的 CMakeListst.txt 中调用
- 默认内部调用
include(CTest) - 行为受变量
BUILD_TESTING影响
add_test()
例子:
| |
制作 cmake 模块 – FindMyPackage.cmake
文章作者
上次更新 2023-02-01 (9aed3e4)