一、安装与打包:从代码到可交付物
说实话,很多开发者写 CMake 就止步于「能编译通过」。但项目做大了,你总得把东西交给别人用吧?要么是安装到系统里,要么是打个安装包发出去。这就是我们今天要聊的——install 命令和CPack 打包。
我个人习惯是,在项目第一天就把 install 规则写好。别等到最后再补,那时候依赖关系乱成一团,你会想哭的。
1.1 install() 命令:把文件放到该放的地方
install() 是 CMake 里最核心的安装指令。它的基本语法长这样:
install(TARGETS target_name
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static)
这里要注意区分三种目标类型:
| 目标类型 | 对应文件 | 典型 DESTINATION |
|---|---|---|
| RUNTIME | 可执行文件 (.exe, 无后缀) | bin |
| LIBRARY | 共享库 (.so, .dylib, .dll) | lib |
| ARCHIVE | 静态库 (.a, .lib) 或 DLL 导入库 | lib/static |
嗯,这里有个坑。我在项目中遇到过有人把静态库也扔到 LIBRARY 里,结果安装路径全乱了。静态库请用 ARCHIVE,别问我为什么,这是 CMake 的约定。
除了安装目标,你还可以安装头文件、配置文件、脚本等:
install(FILES config.json DESTINATION etc/myapp)
install(DIRECTORY include/ DESTINATION include
FILES_MATCHING PATTERN "*.h")
1.2 安装路径的变量与 GNUInstallDirs
你可能会问:bin、lib 这些路径写死合适吗?当然不合适。不同操作系统、不同发行版,路径习惯完全不同。
CMake 提供了 GNUInstallDirs 模块,帮你自动适配:
include(GNUInstallDirs)
install(TARGETS myapp
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
这些变量在 Linux 上会展开为 bin、lib,在 macOS 上可能变成 Applications、Frameworks。说白了,就是让 CMake 替你操心平台差异。
我个人习惯是:永远不要硬编码路径。哪怕你只在 Windows 上开发,也请用变量。因为有一天你可能会把项目移植到 Linux,到时候改路径改到崩溃——我曾经就干过这种蠢事。
1.3 安装的权限与配置
有时候你需要安装一些系统服务或脚本,需要特定权限:
install(PROGRAMS start.sh DESTINATION bin
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE)
PROGRAMS 会自动添加可执行权限,但如果你要精细控制,就用 PERMISSIONS 手动指定。
1.4 CPack:一键生成安装包
install 命令只负责把文件放到构建目录的安装前缀下。要真正生成安装包(.deb、.rpm、.dmg、.exe 安装程序),你需要 CPack。
CPack 是 CMake 自带的打包工具。配置起来很简单:
# 在 CMakeLists.txt 末尾添加
include(CPack)
# 设置包信息
set(CPACK_PACKAGE_NAME "MyApp")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A wonderful application")
set(CPACK_PACKAGE_VENDOR "My Company")
然后运行:
cmake --build . --target install
cpack -G DEB # 生成 .deb 包
cpack -G RPM # 生成 .rpm 包
cpack -G NSIS # 生成 Windows 安装程序
cpack -G DragNDrop # 生成 macOS .dmg
你想想看,一行命令就能生成各种平台的安装包,是不是很爽?
1.5 CPack 的生成器与配置细节
不同的生成器有不同的配置项。这里我列几个常用的:
| 生成器 | 适用平台 | 关键变量 |
|---|---|---|
| DEB | Debian/Ubuntu | CPACK_DEBIAN_PACKAGE_MAINTAINER |
| RPM | Red Hat/Fedora | CPACK_RPM_PACKAGE_LICENSE |
| NSIS | Windows | CPACK_NSIS_DISPLAY_NAME |
| DragNDrop | macOS | CPACK_DMG_VOLUME_NAME |
举个例子,如果你要生成 .deb 包,必须设置维护者信息:
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "yourname@example.com")
不设置的话,CPack 会报错。嗯,这个坑我踩过——第一次打包时忘了设,折腾了半小时才找到原因。
1.6 安装与打包的完整流程
把上面所有知识点串起来,一个典型的流程是这样的:
- 编写 CMakeLists.txt,用 install() 定义安装规则
- 引入 GNUInstallDirs 确保路径可移植
- 在项目末尾 include(CPack) 并设置包元信息
- 构建项目:cmake --build .
- 安装到临时目录:cmake --install . --prefix /tmp/staging
- 打包:cpack -G DEB
这里我建议你始终使用 --prefix 指定一个临时安装目录,不要直接安装到系统里。否则测试打包时会把系统搞乱——我曾经把测试用的 .so 装到了 /usr/lib,结果系统库被覆盖,重装了系统才修好。
- install() 定义「安装什么」和「装到哪里」
- GNUInstallDirs 解决路径可移植性
- CPack 把安装结果打包成可分发的格式
- 始终使用临时安装目录进行测试
1.7 知识体系总览
下面这张图概括了安装与打包的完整知识结构:
这张图把今天的内容串起来了。从左到右,从 install 规则到路径配置,再到 CPack 打包,最后形成完整的交付流程。你写项目时,按这个顺序来,基本不会出错。
好了,安装与打包的核心内容就这些。记住:install 是基础,CPack 是升华。先把 install 写对,打包就是水到渠成的事。