跳到主要内容

CMake CPack 打包

CPack 是 CMake 自带的打包工具,可以将项目打包成各种格式的安装包,如 DEB、RPM、NSIS、DragNDrop 等。本章将详细介绍如何使用 CPack 进行软件打包和分发。

CPack 简介

什么是 CPack?

CPack 是 CMake 套件中的打包程序,它可以:

  1. 生成安装包:支持多种安装包格式
  2. 跨平台分发:为不同平台创建原生安装包
  3. 自动安装配置:处理文件安装、依赖关系等
  4. 版本管理:自动生成版本信息

支持的打包格式

格式平台说明
DEBLinux (Debian/Ubuntu)Debian 软件包
RPMLinux (Red Hat/Fedora)RPM 软件包
TGZ跨平台压缩包
ZIP跨平台ZIP 压缩包
NSISWindowsWindows 安装程序
WIXWindowsWindows Installer (MSI)
DragNDropmacOSDMG 磁盘镜像
BundlemacOSmacOS 应用程序包
PKGmacOSmacOS 安装包

基本使用

启用 CPack

CMakeLists.txt 末尾添加:

cmake_minimum_required(VERSION 3.15)
project(MyApp VERSION 1.0.0)

add_executable(myapp src/main.cpp)

# 安装规则
install(TARGETS myapp RUNTIME DESTINATION bin)

# 启用 CPack
include(CPack)

打包基本信息

设置包的基本信息:

# 项目信息
set(CPACK_PACKAGE_NAME "MyApp")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A sample application")
set(CPACK_PACKAGE_VENDOR "MyCompany")
set(CPACK_PACKAGE_CONTACT "[email protected]")

# 安装目录结构
set(CPACK_PACKAGING_INSTALL_PREFIX "/opt/myapp")

include(CPack)

生成安装包

# 配置项目
cmake -B build

# 构建
cmake --build build

# 打包
cd build
cpack

# 指定生成器
cpack -G DEB
cpack -G TGZ

常用配置变量

基本信息

# 包名称
set(CPACK_PACKAGE_NAME "MyApp")

# 版本号
set(CPACK_PACKAGE_VERSION_MAJOR 1)
set(CPACK_PACKAGE_VERSION_MINOR 0)
set(CPACK_PACKAGE_VERSION_PATCH 0)
# 或直接使用
set(CPACK_PACKAGE_VERSION "1.0.0")

# 描述
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "My Application")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README.md")

# 供应商
set(CPACK_PACKAGE_VENDOR "MyCompany")
set(CPACK_PACKAGE_CONTACT "[email protected]")

# 图标
set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/resources/icon.png")

文件和目录

# 安装前缀
set(CPACK_PACKAGING_INSTALL_PREFIX "/usr/local")

# 资源文件目录
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md")
set(CPACK_RESOURCE_FILE_WELCOME "${CMAKE_SOURCE_DIR}/WELCOME.txt")

# 输出目录
set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}/packages")

压缩设置

# 压缩格式
set(CPACK_ARCHIVE_COMPONENT_INSTALL ON)

# 压缩级别
set(CPACK_ARCHIVE_THREADS 0) # 使用所有 CPU 核心

Linux 打包

DEB 包(Debian/Ubuntu)

# DEB 包配置
set(CPACK_GENERATOR "DEB")

# 包信息
set(CPACK_DEBIAN_PACKAGE_NAME "myapp")
set(CPACK_DEBIAN_PACKAGE_VERSION ${PROJECT_VERSION})
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Developer <[email protected]>")
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "My Application
This is a longer description of my application.
It can span multiple lines.")

# 依赖关系
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.17), libstdc++6 (>= 4.9)")

# 推荐和建议依赖
set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "myapp-plugins")
set(CPACK_DEBIAN_PACKAGE_SUGGESTS "myapp-doc")

# 包分类
set(CPACK_DEBIAN_PACKAGE_SECTION "utils")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")

# 控制文件
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA
"${CMAKE_SOURCE_DIR}/debian/postinst"
"${CMAKE_SOURCE_DIR}/debian/prerm"
)

include(CPack)

维护脚本

debian/postinst(安装后脚本):

#!/bin/sh
set -e

# 创建配置目录
mkdir -p /etc/myapp

# 设置权限
chmod 755 /usr/bin/myapp

echo "MyApp has been installed successfully!"

debian/prerm(卸载前脚本):

#!/bin/sh
set -e

echo "Preparing to remove MyApp..."

RPM 包(Red Hat/Fedora)

# RPM 包配置
set(CPACK_GENERATOR "RPM")

# 包信息
set(CPACK_RPM_PACKAGE_NAME "myapp")
set(CPACK_RPM_PACKAGE_VERSION ${PROJECT_VERSION})
set(CPACK_RPM_PACKAGE_RELEASE "1")
set(CPACK_RPM_PACKAGE_ARCHITECTURE "x86_64")
set(CPACK_RPM_PACKAGE_SUMMARY "My Application")
set(CPACK_RPM_PACKAGE_DESCRIPTION "A longer description
of my application.")

# 许可证
set(CPACK_RPM_PACKAGE_LICENSE "MIT")

# 分组
set(CPACK_RPM_PACKAGE_GROUP "Applications/Utilities")

# 依赖关系
set(CPACK_RPM_PACKAGE_REQUIRES "glibc >= 2.17, libstdc++ >= 4.9")

# 指定文件
set(CPACK_RPM_PACKAGE_RELOCATABLE ON)

# 安装前/后脚本
set(CPACK_RPM_PRE_INSTALL_SCRIPT_FILE "${CMAKE_SOURCE_DIR}/rpm/preinst")
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CMAKE_SOURCE_DIR}/rpm/postinst")
set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${CMAKE_SOURCE_DIR}/rpm/prerm")
set(CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE "${CMAKE_SOURCE_DIR}/rpm/postrm")

include(CPack)

Windows 打包

NSIS 安装程序

# NSIS 配置
set(CPACK_GENERATOR "NSIS")

# 安装程序信息
set(CPACK_NSIS_PACKAGE_NAME "MyApp")
set(CPACK_NSIS_DISPLAY_NAME "My Application")
set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64")
set(CPACK_NSIS_INSTALLER_ICON_CODE "")
set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/resources/icon.ico")
set(CPACK_NSIS_MUI_UNIICON "${CMAKE_SOURCE_DIR}/resources/uninstall.ico")

# 安装选项
set(CPACK_NSIS_MODIFY_PATH ON) # 添加到 PATH
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)

# 创建桌面快捷方式
set(CPACK_NSIS_CREATE_ICONS_EXTRA
"CreateShortCut '$DESKTOP\\\\MyApp.lnk' '$INSTDIR\\\\bin\\\\myapp.exe'"
)

# 开始菜单
set(CPACK_NSIS_MENU_LINKS
"bin/myapp.exe" "MyApp"
"https://example.com" "Website"
)

include(CPack)

WIX(MSI 安装程序)

# WIX 配置
set(CPACK_GENERATOR "WIX")

# 基本信息
set(CPACK_WIX_PRODUCT_GUID "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE")
set(CPACK_WIX_UPGRADE_GUID "FFFFFFFF-GGGG-HHHH-IIII-JJJJJJJJJJJJ")
set(CPACK_WIX_PRODUCT_ICON "${CMAKE_SOURCE_DIR}/resources/icon.ico")

# 安装目录
set(CPACK_WIX_INSTALL_DIR_SCOPE "perMachine")

# UI 设置
set(CPACK_WIX_UI_BANNER "${CMAKE_SOURCE_DIR}/resources/banner.bmp")
set(CPACK_WIX_UI_DIALOG "${CMAKE_SOURCE_DIR}/resources/dialog.bmp")

include(CPack)

macOS 打包

DMG(磁盘镜像)

# DMG 配置
set(CPACK_GENERATOR "DragNDrop")

# 基本信息
set(CPACK_DMG_FORMAT "UDBZ") # 压缩格式
set(CPACK_DMG_VOLUME_NAME "MyApp")

# 背景图片
set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/resources/background.png")

# 图标位置
set(CPACK_DMG_DS_STORE "${CMAKE_SOURCE_DIR}/resources/DS_Store")

include(CPack)

Bundle(应用程序包)

# 创建 macOS Bundle
add_executable(myapp MACOSX_BUNDLE src/main.cpp)

# 设置 Bundle 属性
set_target_properties(myapp PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/Info.plist.in"
MACOSX_BUNDLE_GUI_IDENTIFIER "com.mycompany.myapp"
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}
MACOSX_BUNDLE_BUNDLE_NAME "MyApp"
MACOSX_BUNDLE_ICON_FILE "icon.icns"
)

# 安装 Bundle
install(TARGETS myapp
BUNDLE DESTINATION .
)

# 打包设置
set(CPACK_GENERATOR "Bundle")
set(CPACK_BUNDLE_NAME "MyApp")
set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/resources/icon.icns")
set(CPACK_BUNDLE_PLIST "${CMAKE_SOURCE_DIR}/Info.plist")

include(CPack)

组件化打包

定义组件

# 定义安装组件
install(TARGETS myapp
RUNTIME DESTINATION bin
COMPONENT Runtime
)

install(TARGETS mylib
LIBRARY DESTINATION lib
COMPONENT Development
)

install(FILES include/mylib.h
DESTINATION include
COMPONENT Development
)

install(DIRECTORY docs/
DESTINATION share/doc/myapp
COMPONENT Documentation
)

# 组件配置
set(CPACK_COMPONENTS_ALL Runtime Development Documentation)

set(CPACK_COMPONENT_Runtime_DISPLAY_NAME "Runtime")
set(CPACK_COMPONENT_Runtime_DESCRIPTION "Runtime libraries and executables")
set(CPACK_COMPONENT_Runtime_REQUIRED ON)

set(CPACK_COMPONENT_Development_DISPLAY_NAME "Development")
set(CPACK_COMPONENT_Development_DESCRIPTION "Headers and libraries for development")
set(CPACK_COMPONENT_Development_DEPENDS Runtime)

set(CPACK_COMPONENT_Documentation_DISPLAY_NAME "Documentation")
set(CPACK_COMPONENT_Documentation_DESCRIPTION "User documentation")

include(CPack)

组件组

# 定义组件组
set(CPACK_COMPONENT_RUNTIME_GROUP "Base")
set(CPACK_COMPONENT_DEVELOPMENT_GROUP "Development")
set(CPACK_COMPONENT_DOCUMENTATION_GROUP "Extras")

# 组配置
set(CPACK_COMPONENT_GROUP_BASE_DISPLAY_NAME "Base Installation")
set(CPACK_COMPONENT_GROUP_BASE_DESCRIPTION "Essential components")
set(CPACK_COMPONENT_GROUP_BASE_EXPANDED ON)

set(CPACK_COMPONENT_GROUP_DEVELOPMENT_DISPLAY_NAME "Development Files")
set(CPACK_COMPONENT_GROUP_DEVELOPMENT_DESCRIPTION "Files for developers")

set(CPACK_COMPONENT_GROUP_EXTRAS_DISPLAY_NAME "Extra Components")
set(CPACK_COMPONENT_GROUP_EXTRAS_DESCRIPTION "Optional components")

多配置打包

同时生成多种格式

# 同时生成多个格式
set(CPACK_GENERATOR "DEB;RPM;TGZ")

# 或者使用命令行
# cpack -G "DEB;RPM;TGZ"

条件化配置

# 根据平台选择打包格式
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
find_program(DPKG_FOUND dpkg)
if(DPKG_FOUND)
set(CPACK_GENERATOR "DEB")
else()
set(CPACK_GENERATOR "RPM")
endif()
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(CPACK_GENERATOR "NSIS")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(CPACK_GENERATOR "DragNDrop")
endif()

include(CPack)

完整示例

跨平台项目打包

cmake_minimum_required(VERSION 3.15)
project(MyApp VERSION 1.0.0 LANGUAGES CXX)

# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 可执行文件
add_executable(myapp src/main.cpp)

# 安装规则
install(TARGETS myapp
RUNTIME DESTINATION bin
COMPONENT Runtime
)

install(FILES README.md LICENSE
DESTINATION share/doc/myapp
COMPONENT Documentation
)

install(DIRECTORY config/
DESTINATION etc/myapp
COMPONENT Runtime
)

# CPack 通用配置
set(CPACK_PACKAGE_NAME "myapp")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "My Application - A sample project")
set(CPACK_PACKAGE_VENDOR "MyCompany")
set(CPACK_PACKAGE_CONTACT "[email protected]")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md")

# 平台特定配置
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(CPACK_GENERATOR "DEB;RPM;TGZ")

# DEB 配置
set(CPACK_DEBIAN_PACKAGE_NAME "myapp")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "MyCompany <[email protected]>")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.17)")
set(CPACK_DEBIAN_PACKAGE_SECTION "utils")

# RPM 配置
set(CPACK_RPM_PACKAGE_NAME "myapp")
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_GROUP "Applications/Utilities")

elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(CPACK_GENERATOR "NSIS;ZIP")

# NSIS 配置
set(CPACK_NSIS_DISPLAY_NAME "MyApp ${PROJECT_VERSION}")
set(CPACK_NSIS_MODIFY_PATH ON)

elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(CPACK_GENERATOR "DragNDrop;TGZ")

set(CPACK_DMG_VOLUME_NAME "MyApp")
endif()

# 组件配置
set(CPACK_COMPONENTS_ALL Runtime Documentation)
set(CPACK_COMPONENT_Runtime_REQUIRED ON)
set(CPACK_COMPONENT_Documentation_DISPLAY_NAME "Documentation")

include(CPack)

命令行使用

基本命令

# 生成默认格式的包
cpack

# 指定生成器
cpack -G DEB
cpack -G "DEB;RPM"

# 指定配置
cpack -C Release

# 指定输出目录
cpack -B ./packages

# 详细输出
cpack -V

# 打包特定组件
cpack -C Release -G DEB --component Runtime

使用预设

# 使用预设打包
cpack --preset myapp-deb

调试和验证

验证 DEB 包

# 查看包信息
dpkg-deb --info myapp-1.0.0-Linux.deb

# 解压包内容
dpkg-deb -x myapp-1.0.0-Linux.deb ./extracted

# 安装测试
sudo dpkg -i myapp-1.0.0-Linux.deb

验证 RPM 包

# 查看包信息
rpm -qip myapp-1.0.0-Linux.rpm

# 查看文件列表
rpm -qlp myapp-1.0.0-Linux.rpm

# 安装测试
sudo rpm -ivh myapp-1.0.0-Linux.rpm

查看 ZIP/TGZ 内容

# ZIP
unzip -l myapp-1.0.0-Linux.zip

# TGZ
tar -tvzf myapp-1.0.0-Linux.tar.gz

常见问题

问题 1:包中缺少文件

确保安装规则正确:

# 检查安装规则
install(TARGETS myapp RUNTIME DESTINATION bin)
install(FILES ${HEADERS} DESTINATION include)

问题 2:依赖关系不正确

手动指定依赖:

# DEB
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libstdc++6, libc6")

# RPM
set(CPACK_RPM_PACKAGE_REQUIRES "libstdc++, glibc")

问题 3:安装路径错误

检查安装前缀:

# 设置正确的安装前缀
set(CMAKE_INSTALL_PREFIX "/usr")
set(CPACK_PACKAGING_INSTALL_PREFIX "/usr")

小结

本章我们学习了:

  1. CPack 基础:基本配置和打包流程
  2. Linux 打包:DEB 和 RPM 包配置
  3. Windows 打包:NSIS 和 WIX 安装程序
  4. macOS 打包:DMG 和 Bundle
  5. 组件化打包:分离安装组件
  6. 调试和验证:验证打包结果

CPack 让软件分发变得简单,是 CMake 项目发布的重要组成部分。

练习

  1. 为一个简单项目创建 DEB 和 RPM 包
  2. 创建 Windows NSIS 安装程序
  3. 实现组件化安装,分离运行时和开发文件
  4. 添加自定义安装脚本(postinst、prerm)

参考资源