15、安装与打包:install命令与CPack打包
说到CMake的安装与打包,我其实挺有感触的。早期做C++项目时,编译完了一堆二进制文件,怎么发给别人用?手动拷贝?写个shell脚本?都不是长久之计。CMake的install命令和CPack工具,就是专门解决这个问题的。
说白了,install告诉CMake「哪些文件要装到哪个目录」,而CPack则负责「把这些文件打包成安装包」。两者配合,就能实现一键生成安装程序。
15.1 install命令:核心三件套
install命令有几种常用形式,我工作中最常用的就是TARGETS、FILES和DIRECTORIES。咱们一个一个看。
15.1.1 安装目标:install(TARGETS ...)
这是最常用的。你想把编译出来的库或可执行文件装到系统里,就用它。
install(TARGETS myapp
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static
)
这里要注意:RUNTIME对应可执行文件(Windows下还有DLL),LIBRARY对应共享库(.so/.dylib),ARCHIVE对应静态库(.a/.lib)。
lib/static 目录,避免和共享库混在一起。曾经有个项目,同事把静态库和共享库都丢到同一个lib目录,结果链接时老是找错文件,排查了半天。
15.1.2 安装文件:install(FILES ...)
头文件、配置文件、脚本等,用这个。
install(FILES
include/mylib.h
include/mylib_config.h
DESTINATION include/mylib
)
嗯,这里有个坑:FILES不会保留目录结构。如果你有多个子目录的头文件,得用DIRECTORIES。
15.1.3 安装目录:install(DIRECTORY ...)
install(DIRECTORY include/
DESTINATION include
FILES_MATCHING PATTERN "*.h"
)
注意include/后面的斜杠!有斜杠表示拷贝目录内容,没有斜杠表示拷贝目录本身。我刚开始用的时候经常搞混,后来养成习惯:一律加斜杠,只拷贝内容。
15.1.4 导出目标:install(EXPORT ...)
这个稍微高级一点。如果你写了一个库,想让其他CMake项目通过find_package找到它,就需要导出。
install(TARGETS mylib
EXPORT mylibTargets
LIBRARY DESTINATION lib
PUBLIC_HEADER DESTINATION include
)
install(EXPORT mylibTargets
FILE mylibTargets.cmake
DESTINATION lib/cmake/mylib
)
这样别人就能用find_package(mylib)来引用你的库了。我在做公司内部公共库时,就是用这套方案,省去了每个人手动配置路径的麻烦。
15.2 安装路径的灵活配置
硬编码路径是不好的。CMake提供了GNUInstallDirs模块,帮你自动适配不同平台的路径。
include(GNUInstallDirs)
install(TARGETS myapp
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
在Linux上,CMAKE_INSTALL_BINDIR默认是bin;在Windows上,可能就变成.了。你想想看,这样跨平台就省心多了。
install 里显式列出所有依赖的DLL。
15.3 CPack打包:从安装到安装包
install命令定义好了安装规则,CPack就负责把这些规则打包成安装程序。你只需要在CMakeLists.txt末尾加上几行配置。
set(CPACK_PACKAGE_NAME "MyApp")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "My Awesome Application")
include(CPack)
然后运行:
cmake --build . --target package
或者:
cpack
默认会生成一个压缩包。如果你想生成其他格式,比如NSIS安装包(Windows)、DEB包(Debian)、RPM包(Red Hat),可以指定生成器。
cpack -G NSIS # Windows安装包
cpack -G DEB # Debian包
cpack -G RPM # Red Hat包
cpack -G TGZ # tar.gz压缩包
15.4 常用CPack生成器对比
| 生成器 | 适用平台 | 输出格式 | 特点 |
|---|---|---|---|
| NSIS | Windows | .exe | 交互式安装向导,支持开始菜单快捷方式 |
| DEB | Debian/Ubuntu | .deb | 系统包管理器直接安装 |
| RPM | Red Hat/Fedora | .rpm | 系统包管理器直接安装 |
| TGZ | 跨平台 | .tar.gz | 简单解压即用,无依赖 |
| ZIP | 跨平台 | .zip | Windows友好,解压即用 |
15.5 自定义CPack配置
CPack有很多可配置项。我常用的几个:
# 安装包图标
set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/icons/myapp.ico")
# 安装路径前缀(Linux下默认为/usr/local)
set(CPACK_PACKAGING_INSTALL_PREFIX "/usr")
# 依赖检测(DEB/RPM)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libssl-dev (>= 1.1)")
# 许可证文件
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE.txt")
CPACK_PACKAGING_INSTALL_PREFIX。默认是 /usr/local,但系统包管理器通常期望安装到 /usr。我曾经没改这个,结果打出来的DEB包安装到了 /usr/local,用户用 dpkg -L 都找不到文件。
15.6 知识体系总览
下面这张图总结了本章的核心逻辑:从源码到安装包,CMake帮你串联了整个过程。
15.7 实战:一个完整的例子
假设你有一个库和一个可执行文件,想打包成DEB包。完整的CMakeLists.txt大概长这样:
cmake_minimum_required(VERSION 3.15)
project(MyApp VERSION 1.0.0 LANGUAGES CXX)
add_library(mylib SHARED src/mylib.cpp)
target_include_directories(mylib PUBLIC include)
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE mylib)
# 安装规则
include(GNUInstallDirs)
install(TARGETS mylib
EXPORT mylibTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mylib
)
install(TARGETS myapp
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(EXPORT mylibTargets
FILE mylibTargets.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mylib
)
# CPack配置
set(CPACK_PACKAGE_NAME "myapp")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "My Application")
set(CPACK_PACKAGING_INSTALL_PREFIX "/usr")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.31)")
include(CPack)
然后执行:
mkdir build && cd build
cmake ..
make
cpack -G DEB
你就会得到一个myapp-1.0.0-Linux.deb文件。用户拿到后直接sudo dpkg -i就能安装。
install(TARGETS)安装编译产物,区分RUNTIME/LIBRARY/ARCHIVEinstall(FILES)安装单个文件,install(DIRECTORY)安装整个目录install(EXPORT)导出CMake配置,方便其他项目引用- CPack基于install规则生成安装包,支持NSIS/DEB/RPM/TGZ等多种格式
- 使用
GNUInstallDirs模块实现跨平台路径适配
好了,这一章的内容就到这里。安装与打包是CMake工程化的最后一步,也是把代码交付给用户的关键环节。掌握了这些,你的项目就能做到「编译完、打包好、直接发」。
公众号:蓝海资料掘金营,微信deep3321