第13章 安装与打包:install()命令,CPack配置,生成安装包
说实话,很多C++开发者写CMakeLists.txt时,只关心怎么编译。安装和打包?那是最后才想的事。我以前也这样,直到有次交付项目,对方问我「怎么没有安装脚本?」——场面一度很尴尬。
这一章,我们就来搞定CMake的安装与打包。说白了,就是让你的项目能被make install,还能一键生成deb、rpm、NSIS安装包。
13.1 install() 命令:把文件放到该放的地方
install()是CMake里最核心的安装命令。它的作用很简单:告诉构建系统,编译完成后,哪些文件要复制到安装目录。
基本语法长这样:
install(TARGETS target_name
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static)
这里要注意:
- RUNTIME:可执行文件,放
bin - LIBRARY:共享库(.so/.dylib),放
lib - ARCHIVE:静态库(.a/.lib),放
lib/static
我习惯把这三个分开写,清晰一点:
install(TARGETS myapp
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static)
GNUInstallDirs模块,可以写成DESTINATION ${CMAKE_INSTALL_BINDIR},这样更符合Linux标准。
13.2 安装头文件和配置文件
除了二进制文件,头文件和配置文件也得装。用install(DIRECTORY ...):
install(DIRECTORY include/
DESTINATION include
FILES_MATCHING
PATTERN "*.h"
PATTERN "*.hpp")
嗯,这里有个坑:include/后面的斜杠很重要。有斜杠表示复制目录内容,没有斜杠会复制目录本身。我曾经因为这个,把整个include目录装到了/usr/local/include/include下面……
13.3 安装脚本和CMake模块
有时候需要安装一些辅助脚本或CMake模块文件:
install(FILES cmake/MyConfig.cmake
DESTINATION lib/cmake/MyProject)
这样别人用find_package(MyProject)时就能找到你的配置了。
13.4 CPack:一键生成安装包
CPack是CMake自带的打包工具。你只需要在CMakeLists.txt里设置几个变量,然后运行cpack命令,就能生成deb、rpm、NSIS等格式的安装包。
基本配置:
set(CPACK_PACKAGE_NAME "MyProject")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A useful tool")
set(CPACK_PACKAGE_VENDOR "My Company")
include(CPack)
就这么简单?对,就这么简单。但实际项目中,你还需要指定更多信息:
| 变量名 | 说明 | 示例值 |
|---|---|---|
| CPACK_PACKAGE_CONTACT | 维护者邮箱 | support@mycompany.com |
| CPACK_PACKAGE_LICENSE | 许可证 | MIT |
| CPACK_PACKAGE_FILE_NAME | 安装包文件名 | MyProject-1.0.0-Linux |
| CPACK_PACKAGE_INSTALL_DIRECTORY | 安装路径(Windows) | MyProject |
13.5 生成deb包
Debian/Ubuntu用户最熟悉的就是.deb了。CPack生成deb包需要额外设置:
set(CPACK_GENERATOR "DEB")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "me@example.com")
set(CPACK_DEBIAN_PACKAGE_SECTION "devel")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libssl-dev, libcurl4")
然后运行:
cpack -G DEB
就会生成一个.deb文件。你可以用dpkg -i安装它。
libssl-dev,结果用户安装后运行时崩溃,排查了半天才发现是缺了OpenSSL。
13.6 生成rpm包
Red Hat/CentOS/Fedora用户需要.rpm。配置类似:
set(CPACK_GENERATOR "RPM")
set(CPACK_RPM_PACKAGE_RELEASE "1")
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_GROUP "Development/Tools")
运行:
cpack -G RPM
生成.rpm文件。注意,rpm打包需要rpmbuild工具,确保系统已安装。
13.7 生成NSIS安装包(Windows)
Windows用户习惯双击.exe安装。NSIS(Nullsoft Scriptable Install System)就是干这个的。
set(CPACK_GENERATOR "NSIS")
set(CPACK_NSIS_DISPLAY_NAME "MyProject")
set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES")
set(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\MyProject.lnk' '$INSTDIR\\\\bin\\\\myapp.exe'")
运行:
cpack -G NSIS
生成一个.exe安装包。Windows用户双击就能安装,还能自动创建开始菜单快捷方式。
13.8 多平台打包策略
实际项目中,我通常这样组织:
# 通用设置
set(CPACK_PACKAGE_NAME "MyProject")
set(CPACK_PACKAGE_VERSION "1.0.0")
# 平台相关
if(WIN32)
set(CPACK_GENERATOR "NSIS")
set(CPACK_NSIS_DISPLAY_NAME "MyProject")
elseif(APPLE)
set(CPACK_GENERATOR "DragNDrop") # 生成.dmg
else()
set(CPACK_GENERATOR "DEB;RPM")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "me@example.com")
set(CPACK_RPM_PACKAGE_RELEASE "1")
endif()
include(CPack)
这样,在Linux上运行cpack会生成deb和rpm,在Windows上生成NSIS,在macOS上生成dmg。一套代码,到处打包。
13.9 知识体系图
下面这张图总结了安装与打包的核心流程:
13.10 避坑指南
最后,分享几个我踩过的坑:
- 路径问题:
install(DIRECTORY ...)的斜杠一定要小心。多一个少一个,结果天差地别。 - 依赖缺失:打包时一定要声明所有运行时依赖。尤其是动态链接的库,漏一个就崩。
- 权限问题:Linux下安装到
/usr/local需要root权限。建议在CMakeLists.txt里加个提示。 - 版本号:
CPACK_PACKAGE_VERSION和项目版本号保持一致。我习惯用project()里的版本号自动赋值。
install()负责把文件放到正确位置- CPack负责把安装好的文件打包成安装包
- 不同平台用不同的Generator:DEB、RPM、NSIS
- 依赖关系一定要写清楚,否则用户安装后无法运行
好了,安装与打包就讲到这里。下一章我们会聊聊测试——enable_testing()和add_test(),让你的项目更健壮。
公众号:蓝海资料掘金营,微信deep3321