第28章 打包与分发:CPack、WiX Toolset、DMG、AppImage

说到跨平台开发的最后一公里,打包分发绝对是个绕不开的坎儿。你想想看,代码写好了,编译通过了,测试也跑完了,结果用户拿到手不知道怎么装——这多尴尬。

我个人习惯把打包这件事分成三个维度来看:平台原生性依赖管理安装体验。Windows用户要的是双击下一步,macOS用户期待拖拽到Applications,Linux用户则希望一个AppImage搞定一切。嗯,今天我们就来把这三种平台的打包方案掰开揉碎讲清楚。

28.1 CPack:跨平台打包的瑞士军刀

CPack是CMake自带的打包工具,说白了就是给CMake项目配一个统一的打包接口。你只要在CMakeLists.txt里写好配置,CPack就能帮你生成各种平台的安装包。

先看一个最基础的CPack配置:

# CMakeLists.txt 尾部添加
include(CPack)

set(CPACK_PACKAGE_NAME "MyApp")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_PACKAGE_VENDOR "MyCompany")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A cross-platform application")

# 指定生成器
set(CPACK_GENERATOR "ZIP;TGZ")
# Windows下还可以加 NSIS 或 WIX
if(WIN32)
    list(APPEND CPACK_GENERATOR "NSIS")
endif()

然后执行:

cpack -G ZIP          # 生成zip包
cpack -G TGZ          # 生成tar.gz包
cpack -G NSIS         # Windows下生成exe安装包
cpack -G WIX          # Windows下生成msi安装包
cpack -G DragNDrop    # macOS下生成dmg包
cpack -G RPM          # Linux下生成rpm包
cpack -G DEB          # Linux下生成deb包
我的经验:CPack最妙的地方在于,你只需要维护一套打包脚本。我在一个项目里同时给Windows、macOS和Linux打包,CMakeLists.txt里用条件判断区分平台,CPack配置几乎不用改。

28.2 WiX Toolset:Windows安装包的专业方案

Windows平台的安装包,我个人最推荐WiX Toolset。它生成的是.msi文件,Windows Installer原生支持,企业环境里部署起来特别方便。

WiX用XML来描述安装过程,看一个最简单的例子:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Product Id="*" Name="MyApp" Language="1033" 
             Version="1.0.0" Manufacturer="MyCompany">
        
        <Package InstallerVersion="200" Compressed="yes" />
        
        <Directory Id="TARGETDIR" Name="SourceDir">
            <Directory Id="ProgramFiles64Folder">
                <Directory Id="INSTALLDIR" Name="MyApp" />
            </Directory>
        </Directory>
        
        <ComponentGroup Id="ProductComponents" Directory="INSTALLDIR">
            <Component Id="MainExecutable" Guid="*">
                <File Id="MyAppExe" Name="MyApp.exe" 
                      Source="$(var.BuildDir)/MyApp.exe" />
            </Component>
        </ComponentGroup>
        
        <Feature Id="MainFeature" Title="Main Feature" Level="1">
            <ComponentGroupRef Id="ProductComponents" />
        </Feature>
    </Product>
</Wix>

然后用candle和light编译:

candle MyApp.wxs -o MyApp.wixobj
light MyApp.wixobj -o MyApp.msi
避坑指南:我曾经在WiX里忘记设置GUID为"*",结果每次升级安装包都提示"已存在另一个版本"。WiX的GUID管理是个坑,建议所有Component都用"*"让WiX自动生成。

WiX还支持自定义操作(CustomAction),比如写注册表、创建快捷方式、启动服务等。我一般用WiX的Heat工具自动扫描目录生成.wxs文件,省得手写几百行XML。

28.3 DMG:macOS的优雅分发

macOS用户习惯拖拽安装。DMG(Disk Image)就是干这个的——你打开一个.dmg文件,把.app拖到Applications文件夹里,完事。

用CPack生成DMG最简单:

cpack -G DragNDrop

但如果你想自定义DMG的外观(比如背景图、窗口大小、图标位置),就得用hdiutil和osascript手动做了。我写了一个脚本:

#!/bin/bash
# 创建临时目录
mkdir -p dmg_build
cp -r MyApp.app dmg_build/

# 创建DMG
hdiutil create -srcfolder dmg_build \
               -volname "MyApp" \
               -fs HFS+ \
               -format UDZO \
               -size 200m \
               MyApp.dmg

# 挂载并设置背景(可选)
hdiutil attach MyApp.dmg
# ... 这里可以用AppleScript设置窗口位置和背景
hdiutil detach /Volumes/MyApp
我的习惯:DMG里一定要放一个指向Applications文件夹的快捷方式(别名),用户一看就知道该拖到哪儿。这个用AppleScript几行代码就能搞定。

macOS还有一个坑:代码签名。不签名的.app在Gatekeeper下会被拦截。我建议在打包脚本里加上:

codesign --force --deep --sign "Developer ID Application: YourName" MyApp.app

28.4 AppImage:Linux的便携式方案

Linux发行版太多,deb、rpm、pacman各玩各的。AppImage的好处是——一个文件,到处运行。用户下载后chmod +x就能用,不需要安装依赖。

AppImage的核心是一个AppDir目录结构,然后用appimagetool打包:

# 创建AppDir
mkdir -p MyApp.AppDir/usr/bin
cp MyApp MyApp.AppDir/usr/bin/
cp icon.png MyApp.AppDir/
cp MyApp.desktop MyApp.AppDir/

# 创建.desktop文件
cat > MyApp.AppDir/MyApp.desktop << EOF
[Desktop Entry]
Name=MyApp
Exec=MyApp
Icon=icon
Type=Application
Categories=Utility;
EOF

# 打包
appimagetool MyApp.AppDir MyApp-x86_64.AppImage

AppImage的依赖处理方式很巧妙:它会把所有需要的.so库都打包进去。但要注意,不要打包系统库(比如glibc),否则在不同发行版上反而会出问题。

我曾经踩过的坑:AppImage里打包了libstdc++.so.6,结果在Ubuntu 22.04上跑得好好的,到了CentOS 7上就报GLIBC版本不匹配。后来我改用linuxdeploy工具,它会自动排除系统库,只打包应用特有的依赖。

还有一个实用工具叫linuxdeployqt(Qt项目专用)或linuxdeploy(通用),它们能自动扫描可执行文件的依赖并复制到AppDir里:

linuxdeploy --appdir MyApp.AppDir --executable MyApp --desktop-file MyApp.desktop --icon-file icon.png

28.5 四种打包方案对比

方案 平台 输出格式 依赖处理 安装体验 推荐场景
CPack 全平台 ZIP/TGZ/NSIS/WIX/DragNDrop等 需手动配置 取决于生成器 CMake项目的统一打包入口
WiX Toolset Windows .msi 需手动配置 企业级,支持静默安装 企业环境、需要组策略部署
DMG macOS .dmg 需代码签名 拖拽安装,用户友好 macOS桌面应用分发
AppImage Linux .AppImage 自动打包应用依赖 下载即用,无需安装 跨发行版Linux分发

28.6 打包流程自动化

在实际项目中,我通常用CI/CD来自动化打包流程。以GitHub Actions为例:

# .github/workflows/package.yml
jobs:
  package:
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v3
      - name: Configure
        run: cmake -B build -DCMAKE_BUILD_TYPE=Release
      - name: Build
        run: cmake --build build --config Release
      - name: Package
        run: cpack --config build/CPackConfig.cmake
      - name: Upload
        uses: actions/upload-artifact@v3
        with:
          name: package-${{ matrix.os }}
          path: build/*.zip

这样每次提交代码,三个平台的安装包都会自动生成。我只需要去Artifacts页面下载就行。

核心要点:
  • CPack是跨平台打包的入口,统一管理生成器
  • WiX适合Windows企业级部署,注意GUID管理
  • DMG要配合代码签名,否则Gatekeeper会拦截
  • AppImage用linuxdeploy自动处理依赖,别打包系统库
  • CI/CD自动化打包,省时省力
跨平台打包分发体系 CPack(统一入口) Windows macOS Linux 通用格式 WiX Toolset → .msi NSIS → .exe DragNDrop → .dmg 代码签名 + 公证 AppImage → 便携式 DEB / RPM / 平铺包 ZIP / TGZ 压缩包 CI/CD 自动打包 一套CMake配置,CPack生成多平台安装包

打包分发这件事,说白了就是让用户用最舒服的方式拿到你的软件。Windows用户要.msi,macOS用户要.dmg,Linux用户要.AppImage——你都得准备好。CPack帮你统一管理,但每个平台的细节还得自己打磨。

我个人建议:先用CPack搭好架子,然后针对每个平台做定制优化。Windows上WiX的GUID别搞错,macOS别忘了签名,Linux用linuxdeploy自动处理依赖。把这些坑都填平了,你的软件才能顺利到达用户手里。

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