大家好,我是小康。
还记得上篇文章的内容吗?我们聊了 CMake 的生成器表达式和代码生成与自定义命令,用 CMake 解决了不少开发中的繁琐问题。但今天,我们要继续往下深挖,搞懂两个重磅话题:包管理与安装配置和最佳实践与常见问题。
你是不是也遇到过这些情况:
- 写了一个很棒的库,却不知道怎么优雅地让别人用起来;
- 分享项目后,别人总是抱怨“找不到依赖”;
- 明明配置好了 CMakeLists.txt,却总踩一些奇怪的“坑”。
别急,这篇文章会带你一步步搞定这些问题,拿走你开发路上的绊脚石!
一张思维导图帮你快速了解 CMake 的全貌:
友情提醒:原创不易,如果觉得内容对你有帮助,别忘了点赞、收藏,关注支持一下!非常感谢!🌟
微信搜索 【跟着小康学编程】,关注我公众号,持续分享计算机编程硬核技术文章。
14. CMake 的包管理与安装配置:优雅分发你的库
当你写好一个库,下一步就是让别人方便地用起来。这不仅是拷贝头文件和库文件的问题,还包括如何自动解决依赖,如何简化用户的使用流程。
CMake 提供了强大的包管理和安装配置功能,通过合理利用这些机制,你可以实现一套专业的分发方案,让别人用 find_package()
一键找到你的库。那么,我们就通过一个实战例子,手把手讲解如何完成一个完整的安装配置。
场景还原:你开发了一个数学库
假设你写了一个简单的数学库 MathLib
,能做加法和减法。代码结构如下:
makefile
MathLib/
├── CMakeLists.txt # 顶层 CMake 配置文件
├── include/
│ └── MathLib.h # 头文件
├── src/
│ └── MathLib.cpp # 源文件
└── main.cpp # 示例程序(供测试)
目标很简单:
- 把库和头文件“打包”到一个地方;
- 让别人通过
find_package(MathLib)
一键找到并用上你的库; - 确保这个库安装后,用起来顺畅无坑。
1. 项目初始化:写 CMakeLists.txt
首先,告诉 CMake 这是一个库项目,并设置头文件路径:
cmake_minimum_required(VERSION 3.14)
project(MathLib VERSION 1.0.0)
# 定义静态库目标
add_library(MathLib STATIC src/MathLib.cpp)
# 设置头文件路径
target_include_directories(MathLib PUBLIC
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include> # 构建时用
$<INSTALL_INTERFACE:include> # 安装后用
)
解读:
add_library(MathLib STATIC src/MathLib.cpp)
:告诉 CMake 我们要生成一个叫MathLib
的静态库;target_include_directories
:设置头文件路径。BUILD_INTERFACE
表示构建阶段使用include/
文件夹;INSTALL_INTERFACE
表示安装后,库和头文件会被复制到用户指定的安装路径,比如 /usr/local/include 或自定义的 /path/to/install/include。
简单说,这样用户无论是开发中用还是安装后用,都能找到头文件。
2. 安装库和头文件
现在库已经能在项目中正常编译了,但为了让别人用起来更方便,我们需要做一个“安装包”。安装的目标就是把库和头文件整理好,放到一个用户容易找到的位置。
在 CMakeLists.txt
中,我们需要添加安装规则:
# 安装库文件
install(TARGETS MathLib
EXPORT MathLibTargets # 导出目标配置
ARCHIVE DESTINATION lib # 静态库安装到 lib 目录
LIBRARY DESTINATION lib # 动态库(如果有)安装到 lib 目录
RUNTIME DESTINATION bin # 可执行文件(如果有)安装到 bin 目录
INCLUDES DESTINATION include # 安装头文件路径
)
# 安装头文件
install(DIRECTORY include/ DESTINATION include)
这段代码在干啥?
1.install(TARGETS MathLib ...)
这里定义了库文件的安装规则,比如:
- 静态库(
libMathLib.a
)会被安装到lib/
目录; - 如果是动态库(
libMathLib.so
),也会被放到lib/
; - 可执行文件(如果有)会被放到
bin/
; - 头文件的路径会同时导出,方便用户找到。
2.install(DIRECTORY include/ DESTINATION include)
这一行简单直接:把 include/
目录中的头文件复制到安装路径的 include/
下。
安装路径在哪里?
CMake 默认会把这些文件安装到一个“根路径”下,这个根路径是你运行安装命令时指定的 --prefix
。比如,如果你执行了以下命令:
cmake --install build --prefix /path/to/install
最终的安装结果会是这样的:
/path/to/install/
├── lib/ # 库文件
│ └── libMathLib.a
├── include/ # 头文件
│ └── MathLib.h
注意:
lib/
和include/
是install()
定义的相对路径;--prefix
指定了安装的根目录,最终路径是两者拼接而成。
这一步做完后,库和头文件就准备好分发了。
3. 生成配置文件
为了让用户用 find_package(MathLib)
一键找到库,我们需要生成两个配置文件:
- MathLibTargets.cmake:由 CMake 自动生成,用来记录库的详细信息;比如:
- 库文件在哪里(
libMathLib.a
或libMathLib.so
); - 头文件路径(
include/MathLib.h
在哪里)。
- MathLibConfig.cmake: 这个文件需要你手写,它的作用是引入
MathLibTargets.cmake
。用户调用find_package(MathLib)
时,CMake 会先加载MathLibConfig.cmake
,再通过它找到库的详细信息。
在 CMakeLists.txt
中添加以下内容:
# 导出 MathLibTargets.cmake
install(EXPORT MathLibTargets
FILE MathLibTargets.cmake # 导出的文件名
NAMESPACE MathLib:: # 命名空间,用户会用 MathLib::MathLib 访问你的库
DESTINATION lib/cmake/MathLib # 安装到的路径
)
# 安装 MathLibConfig.cmake
install(FILES cmake/MathLibConfig.cmake DESTINATION lib/cmake/MathLib)
这段代码在做什么?