28、CPack中的许可证管理:许可证文件包含、许可证选择、许可证显示

许可证管理这件事,说实话,很多开发者一开始都不太在意。我以前也是这样,觉得代码写出来能用就行,许可证随便选一个。直到有一次,我打包了一个开源项目,结果用户反馈说找不到许可证文件,差点惹上法律麻烦。嗯,从那以后,我再也不敢忽视CPack里的许可证配置了。

今天我们就来聊聊,CPack里许可证到底怎么管。说白了就三件事:选什么许可证、怎么把许可证文件塞进安装包、用户安装时怎么看到它

核心要点:CPack的许可证管理涉及三个层面——许可证选择(指定协议类型)、许可证包含(将LICENSE文件打包)、许可证显示(安装过程中展示协议)。

许可证选择:别随便选一个

CPack本身不替你选许可证,它只是把你在CMakeLists.txt里指定的许可证信息打包进去。我个人习惯在项目根目录放一个LICENSE文件,然后在CMake里这样配置:

# 设置项目许可证
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")

# 或者指定许可证简称
set(CPACK_PACKAGE_LICENSE "MIT")

这里要注意,CPACK_RESOURCE_FILE_LICENSE指向的是你本地的许可证文件路径。而CPACK_PACKAGE_LICENSE只是一个字符串标识,告诉用户你的项目用的是哪种协议。

我曾经见过有人把这两个搞混,结果打包出来的安装包里,许可证文件内容是空的,但界面上却写着"GPL-3.0"。你说这多尴尬?

变量名 作用 示例值
CPACK_RESOURCE_FILE_LICENSE 指定许可证文件的路径(会被打包进安装包) ${CMAKE_SOURCE_DIR}/LICENSE
CPACK_PACKAGE_LICENSE 设置许可证的简称或标识 MITApache-2.0

许可证文件包含:把LICENSE塞进包里

这一步其实很简单,但很多人会忘。你想想看,用户下载了你的安装包,打开一看,没有许可证文件,他敢用吗?

正确的做法是,在install()命令里把许可证文件一起安装:

install(
  FILES LICENSE
  DESTINATION share/doc/${PROJECT_NAME}
  COMPONENT Runtime
)

然后CPack在打包时,会自动把这个文件包含进去。不过要注意,CPACK_RESOURCE_FILE_LICENSE这个变量指向的文件,是安装过程中显示的许可证,不是安装到目标目录的那个文件。两者可以不同,但我建议保持一致。

小技巧:如果你的项目用了多个第三方库,每个库都有自己的许可证,我建议在项目根目录建一个LICENSES/文件夹,把所有许可证文件放进去,然后在CMake里统一安装。

# 安装所有第三方许可证
install(
  DIRECTORY LICENSES/
  DESTINATION share/doc/${PROJECT_NAME}/licenses
  COMPONENT Runtime
)

许可证显示:让用户看到协议

不同平台的安装包,显示许可证的方式不一样。我挑几个常用的说说:

  • NSIS(Windows安装包):安装过程中会弹出一个窗口,显示许可证文本。用户必须点击"我同意"才能继续。
  • DMG(macOS磁盘映像):通常不会强制显示,但许可证文件会放在包内。
  • DEB/RPM(Linux包):安装时不会弹窗,但包管理器会记录许可证信息。

对于NSIS这种需要交互的安装器,CPack会自动读取CPACK_RESOURCE_FILE_LICENSE指向的文件,并显示在安装向导中。你不需要额外写代码。

注意:NSIS对许可证文件的编码有要求。我曾经遇到过一个问题——许可证文件是UTF-8编码,但NSIS只认ANSI,结果安装时显示乱码。解决方案是:把LICENSE文件另存为ANSI编码,或者用CPACK_NSIS_LICENSE_OVERRIDE变量指定一个兼容的文件。

# 针对NSIS的特殊处理
if(WIN32)
  set(CPACK_NSIS_LICENSE_OVERRIDE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE_ANSI.txt")
endif()

知识体系结构图

下面这张图,我把许可证管理的整个流程梳理了一下。你可以看到,从选择许可证到最终显示给用户,中间涉及哪些环节:

CPack许可证管理流程 许可证选择 CPACK_PACKAGE_LICENSE 许可证文件包含 CPACK_RESOURCE_FILE_LICENSE 许可证显示 安装向导/包信息 各阶段关键点: 1. 许可证选择:在CMakeLists.txt中设置 CPACK_PACKAGE_LICENSE 变量 2. 文件包含:将LICENSE文件通过 install() 命令安装到目标目录 3. 许可证显示:不同打包器(NSIS/DEB/DMG)显示方式不同 常见问题: • NSIS安装包许可证乱码 → 检查文件编码(需ANSI) • 许可证文件未打包 → 检查 install() 命令是否包含LICENSE

避坑指南:我踩过的几个坑

做CPack打包这些年,我在许可证管理上栽过好几次跟头。分享出来,希望大家别重蹈覆辙:

  • 许可证文件路径写错:我曾经把CPACK_RESOURCE_FILE_LICENSE指向了一个不存在的路径,结果CPack直接报错退出。后来我习惯在CMake里加一个判断:if(NOT EXISTS ${CPACK_RESOURCE_FILE_LICENSE}),提前检查文件是否存在。
  • 多许可证项目处理不当:如果你的项目用了MIT、Apache、GPL三种协议的库,千万别只放一个LICENSE文件。我建议在安装目录下建一个licenses/子目录,把每个库的许可证单独放进去。
  • 忘记更新许可证:项目升级时,如果换了许可证协议,记得同步更新CMake里的配置。我见过有人项目从MIT改成了GPL,但CPACK_PACKAGE_LICENSE还写着MIT,这要是被用户发现,麻烦就大了。

我的个人习惯:每次发布新版本前,我都会跑一遍cpack --config CPackConfig.cmake,然后手动检查生成的安装包里是否包含许可证文件。虽然麻烦点,但心里踏实。

总结一下

许可证管理说白了就三步:选对协议、放对文件、显示正确。CPack提供了CPACK_RESOURCE_FILE_LICENSECPACK_PACKAGE_LICENSE两个变量来帮你完成这些事。不同平台的显示方式有差异,但核心逻辑是一样的。

嗯,如果你在配置过程中遇到问题,不妨先检查一下许可证文件的编码和路径。这两个地方最容易出岔子。


公众号:蓝海资料掘金营,微信deep3321