4、CPack配置文件详解:CPackConfig.cmake文件结构、常用变量、设置包描述信息

好,咱们进入第四章。说实话,很多人在CMake里折腾半天,最后卡在打包这一步,就是因为CPackConfig.cmake没写对。这个文件说白了就是CPack的“总控台”,你告诉它包叫什么名字、版本号多少、谁来维护,它才能给你生成一个像样的安装包。

我个人习惯,每次新建项目都会先把这个文件搭好骨架。哪怕后面再改,也比最后手忙脚乱强。今天咱们就把这个文件掰开揉碎,看看里面到底有哪些门道。

4.1 CPackConfig.cmake 文件结构

这个文件其实就是一个普通的CMake脚本文件。CPack在打包时会读取它,然后根据里面的变量决定怎么干活。结构上很简单,就是一堆set()命令。

我见过有人把几百行配置全塞在一个文件里,看着就头疼。其实可以拆成几块:

  • 基础信息区:包名、版本、描述
  • 平台配置区:生成器类型、安装路径
  • 依赖与组件区:需要打包哪些库、哪些文件
  • 高级选项区:签名、压缩、许可证等

嗯,这里要注意,CPackConfig.cmake 必须和你的 CMakeLists.txt 放在同一级目录,或者通过 -C 参数指定路径。否则CPack找不到它,直接报错。

# 一个典型的 CPackConfig.cmake 骨架
set(CPACK_PACKAGE_NAME "MyApp")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_PACKAGE_VENDOR "MyCompany")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A useful application")
set(CPACK_GENERATOR "TGZ;DEB;RPM")

4.2 核心常用变量详解

CPack的变量很多,但常用的就那么十几个。我把它们分成三类,咱们一个一个看。

4.2.1 包标识变量

变量名 作用 示例值
CPACK_PACKAGE_NAME 包名,会出现在安装包文件名中 "MyApp"
CPACK_PACKAGE_VERSION 版本号,通常从CMakeLists.txt传入 "2.1.0"
CPACK_PACKAGE_VENDOR 供应商/组织名称 "MyCompany"
CPACK_PACKAGE_CONTACT 维护者邮箱 "dev@mycompany.com"

我在项目中遇到过一个问题:有人把 CPACK_PACKAGE_NAME 设成了带空格的字符串,结果生成的DEB包名直接非法。记住,包名里不要有空格,用下划线或者连字符代替。

4.2.2 版本与描述变量

版本号这块,我建议你从CMakeLists.txt里用 project() 命令定义好,然后通过 set() 传过来。这样版本号只维护一份,不会出现CMakeLists.txt里是1.0,CPackConfig里还是0.9的尴尬。

# 在 CMakeLists.txt 中
project(MyApp VERSION 1.2.3)

# 在 CPackConfig.cmake 中
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")

描述信息也很重要。很多Linux发行版的包管理器会读取这些字段:

  • CPACK_PACKAGE_DESCRIPTION_SUMMARY:一句话简介,显示在包列表里
  • CPACK_PACKAGE_DESCRIPTION_FILE:指向一个包含详细描述的文本文件
  • CPACK_RESOURCE_FILE_LICENSE:许可证文件路径
  • CPACK_RESOURCE_FILE_README:README文件路径
小技巧:CPACK_PACKAGE_DESCRIPTION_SUMMARY 不要写太长,50个字符以内最佳。DEB包的Description字段如果太长会被截断,你想想看,用户看到一半就没了,多难受。

4.2.3 生成器与路径变量

CPack支持多种打包格式,通过 CPACK_GENERATOR 指定。可以同时指定多个,用分号隔开:

set(CPACK_GENERATOR "TGZ;DEB;RPM;NSIS")

常用的生成器有:

  • TGZ:tar.gz压缩包,跨平台通用
  • DEB:Debian/Ubuntu包
  • RPM:Red Hat/Fedora包
  • NSIS:Windows安装程序
  • DragNDrop:macOS的.dmg包

安装路径相关的变量也很关键:

变量名 说明
CPACK_PACKAGE_INSTALL_DIRECTORY 安装到系统后的顶层目录名
CPACK_SET_DESTDIR 是否启用DESTDIR机制
CPACK_INSTALL_PREFIX 安装前缀,默认是 /usr/local
避坑指南:我曾经在给一个嵌入式项目打包时,忘了设置 CPACK_INSTALL_PREFIX,结果默认装到了 /usr/local,而目标系统的根目录只有50MB空间,直接撑爆了。后来我改成 /opt/myapp,问题解决。

4.3 设置包描述信息

描述信息不只是给用户看的,也是给包管理器看的。比如在Ubuntu上执行 apt show myapp,显示的内容就来自这些变量。

我一般会准备三个文件:

  1. LICENSE.txt:许可证文本,比如MIT、GPL
  2. README.md:项目简介、构建方式、使用说明
  3. ChangeLog.txt:版本变更记录

然后在CPackConfig里这样引用:

set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt")
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ChangeLog.txt")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A lightweight HTTP server for embedded systems")

对于DEB包,还有几个专属变量:

set(CPACK_DEBIAN_PACKAGE_MAINTAINER "John Doe <john@example.com>")
set(CPACK_DEBIAN_PACKAGE_SECTION "net")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://myapp.example.com")

RPM包也有对应的:

set(CPACK_RPM_PACKAGE_GROUP "Applications/Internet")
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_URL "https://myapp.example.com")
set(CPACK_RPM_PACKAGE_RELEASE "1")
核心要点:描述信息要真实、准确。别为了好看乱写版本号或者许可证类型。我记得有一次审计,发现一个包的许可证声明是GPL,但实际代码里混了闭源库,结果整个项目都得重写。嗯,这教训够深刻。

4.4 一个完整的示例

最后,给你看一个我实际项目中用过的CPackConfig.cmake。不算复杂,但该有的都有了:

# ============================================
# CPack 配置文件
# 项目:MiniWebServer
# 版本:从 CMakeLists.txt 自动获取
# ============================================

# ---- 基础信息 ----
set(CPACK_PACKAGE_NAME "MiniWebServer")
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
set(CPACK_PACKAGE_VENDOR "Example Corp")
set(CPACK_PACKAGE_CONTACT "support@example.com")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A minimal HTTP server for IoT devices")

# ---- 描述文件 ----
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")

# ---- 生成器 ----
set(CPACK_GENERATOR "TGZ;DEB")

# ---- 安装路径 ----
set(CPACK_INSTALL_PREFIX "/opt/miniwebserver")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "MiniWebServer")

# ---- DEB 专属配置 ----
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Example Corp <support@example.com>")
set(CPACK_DEBIAN_PACKAGE_SECTION "httpd")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libssl1.1, libc6 (>= 2.28)")

# ---- 组件打包(可选) ----
# set(CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE ON)

看到没?核心就是把这些变量填对。你只要照着这个模板改,基本不会出大问题。

好,这一章就到这里。下一章咱们聊聊怎么用 install() 命令把文件安排得明明白白,那是打包前最重要的一步。