Files
eskin-player/docs/cargo-wix-guide.md

1061 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# cargo-wix 打包与 WiX 常用修改指南
本文档说明本项目如何使用 `cargo-wix` 生成 Windows MSI 安装包,以及常见 WiX 配置的修改方法。当前项目的安装配置文件是 `wix/main.wxs`Rust 包信息来自 `Cargo.toml`
## 1. 当前项目打包方式
本项目使用:
- Rust/Cargo 构建 release 可执行文件
- `cargo-wix` 读取 `Cargo.toml``wix/main.wxs`
- WiX Toolset v3 编译并链接生成 `.msi`
当前可执行文件配置在 `Cargo.toml`
```toml
[[bin]]
name = "ESkinPlayer"
path = "src/main.rs"
```
当前 WiX 元信息:
```toml
[package.metadata.wix]
eula = false
```
当前 MSI 默认生成到:
```text
target\wix\eskin-model-player-<version>-x86_64.msi
```
例如:
```text
target\wix\eskin-model-player-5.0.0-x86_64.msi
```
## 2. 环境准备
需要安装:
1. Rust toolchain
2. WiX Toolset v3
3. cargo-wix
检查 Rust
```powershell
rustc --version
cargo --version
```
安装 `cargo-wix`
```powershell
cargo install cargo-wix
```
检查 `cargo-wix`
```powershell
cargo wix --help
```
本机常见 WiX v3 路径:
```text
C:\Program Files (x86)\WiX Toolset v3.14\bin
```
检查 WiX 工具是否存在:
```powershell
Test-Path "C:\Program Files (x86)\WiX Toolset v3.14\bin\candle.exe"
Test-Path "C:\Program Files (x86)\WiX Toolset v3.14\bin\light.exe"
```
## 3. 常用打包命令
进入项目目录:
```powershell
cd D:\eskin-player
```
清理旧的 WiX 输出:
```powershell
cargo wix clean
```
正常打包:
```powershell
cargo wix --bin-path "C:\Program Files (x86)\WiX Toolset v3.14\bin" --nocapture
```
说明:
- `--bin-path` 指定 WiX Toolset 的 `bin` 目录。
- `--nocapture` 显示 `candle.exe``light.exe` 的详细输出,排查错误时建议加上。
- 默认会先执行 `cargo build --release`
- 默认输出到 `target\wix`
查看生成的 MSI
```powershell
Get-ChildItem target\wix\*.msi
```
指定输出目录:
```powershell
cargo wix --bin-path "C:\Program Files (x86)\WiX Toolset v3.14\bin" --output dist\
```
指定输出文件名:
```powershell
cargo wix --bin-path "C:\Program Files (x86)\WiX Toolset v3.14\bin" --output dist\ESkinPlayer.msi
```
只打包,不重新构建 Rust
```powershell
cargo wix --bin-path "C:\Program Files (x86)\WiX Toolset v3.14\bin" --no-build --target-bin-dir target\release
```
打包后立即安装:
```powershell
cargo wix --bin-path "C:\Program Files (x86)\WiX Toolset v3.14\bin" --install
```
## 4. 重要文件说明
### Cargo.toml
`Cargo.toml` 决定包名、版本、描述、作者、二进制名称等。
常见字段:
```toml
[package]
name = "eskin-model-player"
version = "5.0.0"
authors = ["JOYSONQUIN"]
description = "Desktop pressure sensor visualization and playback application."
```
这些字段会影响:
- MSI 文件名
- 控制面板里的产品版本
- WiX 模板变量
修改版本号后重新打包:
```toml
version = "5.0.1"
```
Windows Installer 的升级判断依赖版本号。发布新的安装包时建议递增版本号。
### wix/main.wxs
`wix/main.wxs` 是 WiX 主配置文件。它决定:
- 安装包名称
- 安装范围
- 安装目录
- 包含哪些文件
- 是否写 PATH
- 卸载行为
- UI 样式
- 升级规则
- 图标、EULA、快捷方式等
## 5. Product 和 Package
典型结构:
```xml
<Product
Id='*'
Name='eskin-model-player'
UpgradeCode='7CEF316B-BE23-4533-B4B2-87D06D2230B5'
Manufacturer='JOYSONQUIN'
Language='1033'
Codepage='1252'
Version='$(var.Version)'>
<Package Id='*'
Keywords='Installer'
Description='Desktop pressure sensor visualization and playback application.'
Manufacturer='JOYSONQUIN'
InstallerVersion='450'
Languages='1033'
Compressed='yes'
InstallScope='perUser'
InstallPrivileges='limited'
SummaryCodepage='1252'/>
</Product>
```
关键字段:
- `Product/@Id='*'`:每次构建生成新的 ProductCode。
- `UpgradeCode`:同一个产品线必须保持稳定,不要随意修改。
- `Version='$(var.Version)'`:使用 `Cargo.toml` 的版本。
- `InstallScope`:安装范围,常见值是 `perUser``perMachine`
- `InstallPrivileges`:权限要求,`limited` 表示不要求管理员权限,`elevated` 表示需要提升权限。
## 6. per-user 和 per-machine 的区别
### 当前用户安装perUser
适合:
- 不希望安装时弹管理员权限
- 应用只给当前用户使用
- 安装到用户目录,例如 `%LOCALAPPDATA%`
- 写用户 PATH 或 HKCU 注册表
推荐配置:
```xml
<Package
...
InstallScope='perUser'
InstallPrivileges='limited'/>
```
目录使用:
```xml
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='LocalAppDataFolder'>
<Directory Id='APPLICATIONFOLDER' Name='eskin-model-player'>
...
</Directory>
</Directory>
</Directory>
```
环境变量使用用户级 PATH
```xml
<Environment
Id='PATH'
Name='PATH'
Value='[Bin]'
Permanent='no'
Part='last'
Action='set'
System='no'/>
```
注意:
- 安装到用户目录的组件需要使用 `HKCU` 注册表项作为 `KeyPath`
- 用户目录下的安装目录建议添加 `RemoveFolder`,否则 ICE64 会报错。
### 所有用户安装perMachine
适合:
- 应用需要给所有用户使用
- 安装到 `Program Files`
- 写系统 PATH
- 写 HKLM 注册表
- 公司 IT 管理员部署
推荐配置:
```xml
<Package
...
InstallScope='perMachine'
InstallPrivileges='elevated'/>
```
目录使用:
```xml
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ProgramFilesFolder'>
<Directory Id='APPLICATIONFOLDER' Name='eskin-model-player'>
...
</Directory>
</Directory>
</Directory>
```
64 位安装包通常使用:
```xml
<?if $(sys.BUILDARCH) = x64 or $(sys.BUILDARCH) = arm64 ?>
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
<?else ?>
<?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
<?endif ?>
```
然后:
```xml
<Directory Id='$(var.PlatformProgramFilesFolder)' Name='PFiles'>
```
系统 PATH
```xml
<Environment
Id='PATH'
Name='PATH'
Value='[Bin]'
Permanent='no'
Part='last'
Action='set'
System='yes'/>
```
注意:
- `perMachine` 通常需要管理员权限。
- 如果普通用户安装,会出现权限不足提示。
-`Program Files` 和系统 PATH 都需要提升权限。
## 7. 当前项目的 per-user 配置要点
当前项目已经改为当前用户安装:
```xml
InstallScope='perUser'
InstallPrivileges='limited'
```
安装目录:
```xml
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='LocalAppDataFolder'>
<Directory Id='APPLICATIONFOLDER' Name='eskin-model-player'>
<Directory Id='Bin' Name='bin'>
...
</Directory>
</Directory>
</Directory>
</Directory>
```
PATH 组件使用 HKCU registry key 作为 KeyPath
```xml
<Component Id='Path' Guid='FD230ABD-75B9-47D4-870B-CF2EA72B76BE'>
<RegistryValue
Root='HKCU'
Key='Software\JOYSONQUIN\eskin-model-player'
Name='PathComponent'
Type='integer'
Value='1'
KeyPath='yes'/>
<RemoveFolder Id='RemoveBinFolder' Directory='Bin' On='uninstall'/>
<RemoveFolder Id='RemoveApplicationFolder' Directory='APPLICATIONFOLDER' On='uninstall'/>
<Environment
Id='PATH'
Name='PATH'
Value='[Bin]'
Permanent='no'
Part='last'
Action='set'
System='no'/>
</Component>
```
程序文件组件也使用 HKCU registry key 作为 KeyPath
```xml
<Component Id='binary0' Guid='302CAFB5-6951-426B-BC5A-988C351A2CF2'>
<RegistryValue
Root='HKCU'
Key='Software\JOYSONQUIN\eskin-model-player'
Name='BinaryComponent'
Type='integer'
Value='1'
KeyPath='yes'/>
<File
Id='exe0'
Name='ESkinPlayer.exe'
DiskId='1'
Source='$(var.CargoTargetBinDir)\ESkinPlayer.exe'/>
</Component>
```
## 8. Component、File、KeyPath 的基本规则
WiX 的核心概念:
- `Directory`:安装目录。
- `Component`:安装和卸载的最小管理单元。
- `File`:要安装的文件。
- `RegistryValue`:要写入的注册表项。
- `KeyPath`Windows Installer 用来判断组件是否已安装的关键路径。
常见规则:
- 一个组件通常只放一个主要文件。
- 组件 GUID 一旦发布后不要随意修改。
- `perUser` 安装到用户目录时,建议用 `HKCU` registry key 作为 `KeyPath`
- `perMachine` 安装到 Program Files 时,文件通常可以作为 `KeyPath`
- 如果组件同时包含文件和注册表 KeyPathWiX v3 通常不能用 `Guid='*'` 自动生成 GUID需要写固定 GUID。
生成 GUID
```powershell
[guid]::NewGuid().ToString().ToUpper()
```
示例:
```xml
<Component Id='binary0' Guid='302CAFB5-6951-426B-BC5A-988C351A2CF2'>
...
</Component>
```
## 9. 添加文件
假设要安装一个 `README.txt` 到应用目录。
目录结构:
```text
wix\
main.wxs
README.txt
```
添加组件:
```xml
<Component Id='ReadmeFile' Guid='PUT-GUID-HERE'>
<RegistryValue
Root='HKCU'
Key='Software\JOYSONQUIN\eskin-model-player'
Name='ReadmeComponent'
Type='integer'
Value='1'
KeyPath='yes'/>
<File
Id='ReadmeTxt'
Name='README.txt'
DiskId='1'
Source='wix\README.txt'/>
</Component>
```
然后在 `Feature` 里引用:
```xml
<ComponentRef Id='ReadmeFile'/>
```
如果是 `perMachine` 安装,也可以简化为:
```xml
<Component Id='ReadmeFile' Guid='PUT-GUID-HERE'>
<File
Id='ReadmeTxt'
Name='README.txt'
DiskId='1'
Source='wix\README.txt'
KeyPath='yes'/>
</Component>
```
## 10. 添加资源目录
如果程序运行时需要模型、图片、配置文件等资源,建议明确安装到应用目录下。
示例:
```xml
<Directory Id='AssetsFolder' Name='assets'>
<Component Id='AssetConfig' Guid='PUT-GUID-HERE'>
<RegistryValue
Root='HKCU'
Key='Software\JOYSONQUIN\eskin-model-player'
Name='AssetConfigComponent'
Type='integer'
Value='1'
KeyPath='yes'/>
<File
Id='AssetConfigJson'
Name='config.json'
DiskId='1'
Source='assets\config.json'/>
</Component>
</Directory>
```
引用:
```xml
<ComponentRef Id='AssetConfig'/>
```
如果资源很多,不建议长期手写大量 `<File>`。可以考虑:
- 用 WiX Heat 自动收集目录
- 在构建脚本里生成 `.wxs` 片段
- 只把稳定的少量资源手写到 `main.wxs`
## 11. 修改安装目录
### 改为当前用户目录
```xml
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='LocalAppDataFolder'>
<Directory Id='APPLICATIONFOLDER' Name='eskin-model-player'>
...
</Directory>
</Directory>
</Directory>
```
实际路径通常类似:
```text
C:\Users\<user>\AppData\Local\eskin-model-player
```
### 改为 Program Files
```xml
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ProgramFilesFolder'>
<Directory Id='APPLICATIONFOLDER' Name='eskin-model-player'>
...
</Directory>
</Directory>
</Directory>
```
64 位项目推荐:
```xml
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ProgramFiles64Folder'>
<Directory Id='APPLICATIONFOLDER' Name='eskin-model-player'>
...
</Directory>
</Directory>
</Directory>
```
## 12. 是否添加 PATH
当前项目有一个可选 Feature把安装目录下的 `bin` 加入 PATH。
Feature
```xml
<Feature
Id='Environment'
Title='PATH Environment Variable'
Description='Add the install location of the [ProductName] executable to the PATH system environment variable.'
Level='1'
Absent='allow'>
<ComponentRef Id='Path'/>
</Feature>
```
如果不希望修改 PATH可以删除或注释
```xml
<ComponentRef Id='Path'/>
```
也可以把整个 `Environment` Feature 注释掉。
用户级 PATH
```xml
System='no'
```
系统级 PATH
```xml
System='yes'
```
注意:
- 用户级 PATH 不需要管理员权限。
- 系统级 PATH 需要管理员权限。
- 安装后新开的终端才能看到新的 PATH。
- 已打开的 PowerShell/CMD 通常不会自动刷新环境变量。
## 13. 添加开始菜单快捷方式
常见做法是在 `ProgramMenuFolder` 下创建快捷方式。
目录:
```xml
<Directory Id='ProgramMenuFolder'>
<Directory Id='ApplicationProgramsFolder' Name='eskin-model-player'/>
</Directory>
```
在可执行文件组件里添加:
```xml
<Shortcut
Id='ApplicationStartMenuShortcut'
Directory='ApplicationProgramsFolder'
Name='ESkinPlayer'
WorkingDirectory='Bin'
Advertise='no'
Target='[Bin]ESkinPlayer.exe'/>
<RemoveFolder Id='RemoveApplicationProgramsFolder' Directory='ApplicationProgramsFolder' On='uninstall'/>
```
如果是 per-user 安装,快捷方式组件也应该使用 HKCU registry key 作为 `KeyPath`
## 14. 添加桌面快捷方式
目录:
```xml
<Directory Id='DesktopFolder' Name='Desktop'/>
```
快捷方式:
```xml
<Shortcut
Id='ApplicationDesktopShortcut'
Directory='DesktopFolder'
Name='ESkinPlayer'
WorkingDirectory='Bin'
Advertise='no'
Target='[Bin]ESkinPlayer.exe'/>
```
桌面快捷方式不一定适合默认创建。更推荐放到可选 Feature或者只创建开始菜单快捷方式。
## 15. 修改控制面板图标
准备 `.ico` 文件,例如:
```text
wix\Product.ico
```
启用:
```xml
<Icon Id='ProductICO' SourceFile='wix\Product.ico'/>
<Property Id='ARPPRODUCTICON' Value='ProductICO'/>
```
注意:
- `.ico` 最好包含多个尺寸,例如 16、32、48、256。
- 路径相对执行打包时的项目根目录。
## 16. 添加 EULA
如果需要安装时显示许可协议:
1. 准备 RTF 文件,例如 `wix\Eula.rtf`
2. 修改 `Cargo.toml`
```toml
[package.metadata.wix]
eula = true
```
3.`wix/main.wxs` 中启用:
```xml
<WixVariable Id='WixUILicenseRtf' Value='wix\Eula.rtf'/>
```
如果当前使用 `WixUI_FeatureTree` 并跳过 EULA需要检查 `<UI>` 里的 `Publish` 逻辑,避免安装流程跳转冲突。
## 17. 修改安装 UI
当前项目使用:
```xml
<UI>
<UIRef Id='WixUI_FeatureTree'/>
...
</UI>
```
常见 UI
- `WixUI_Minimal`:最简单。
- `WixUI_InstallDir`:允许选择安装目录。
- `WixUI_FeatureTree`:允许选择 Feature。
- `WixUI_Advanced`:更完整的高级安装流程。
如果要允许用户改安装路径,常用:
```xml
<UIRef Id='WixUI_InstallDir'/>
<Property Id='WIXUI_INSTALLDIR' Value='APPLICATIONFOLDER'/>
```
## 18. 升级策略
当前项目使用:
```xml
<MajorUpgrade
Schedule='afterInstallInitialize'
DowngradeErrorMessage='A newer version of [ProductName] is already installed. Setup will now exit.'/>
```
含义:
- 安装新版本时自动卸载旧版本。
- 不允许安装更旧版本覆盖新版本。
- `UpgradeCode` 必须保持不变。
- `Version` 必须递增。
Windows Installer 版本规则:
- 通常只比较前三段版本号:`Major.Minor.Build`
- `5.0.0``5.0.1` 是升级
- 只重打同一个 `5.0.0` 可能不会触发正常升级
发布新版时建议:
```toml
version = "5.0.1"
```
## 19. 签名 MSI
未签名 MSI 在 Windows 上可能触发安全提示。正式发布建议签名。
`cargo-wix` 支持 `sign` 子命令,也可以在生成 MSI 后用 `signtool.exe`
示例:
```powershell
signtool sign /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 /a target\wix\eskin-model-player-5.0.0-x86_64.msi
```
实际签名命令取决于证书来源:
- 本机证书
- PFX 文件
- 硬件 token
- 公司代码签名服务
## 20. 常见错误与处理
### 权限不足
错误示例:
```text
You do not have sufficient privileges to complete this installation for all users of the machine.
```
常见原因:
- `InstallScope='perMachine'`
- 安装到 `ProgramFilesFolder`
- 写系统 PATH`System='yes'`
- 写 HKLM 注册表
解决:
- 以管理员身份运行 MSI
- 或改成 `perUser`
- 或安装到 `LocalAppDataFolder`
- 或把 PATH 改为 `System='no'`
### ICE38
错误示例:
```text
ICE38: Component ... installs to user profile. It must use a registry key under HKCU as its KeyPath, not a file.
```
原因:
- 组件安装到用户目录
- 但组件使用文件作为 `KeyPath`
解决:
```xml
<RegistryValue
Root='HKCU'
Key='Software\JOYSONQUIN\eskin-model-player'
Name='BinaryComponent'
Type='integer'
Value='1'
KeyPath='yes'/>
```
并移除 `File` 上的:
```xml
KeyPath='yes'
```
### ICE64
错误示例:
```text
ICE64: The directory Bin is in the user profile but is not listed in the RemoveFile table.
```
原因:
- 用户目录下创建了目录
- 卸载时没有明确清理
解决:
```xml
<RemoveFolder Id='RemoveBinFolder' Directory='Bin' On='uninstall'/>
<RemoveFolder Id='RemoveApplicationFolder' Directory='APPLICATIONFOLDER' On='uninstall'/>
```
### CNDL0230
错误示例:
```text
The Component/@Guid attribute's value '*' is not valid for this component because it does not meet the criteria for having an automatically generated guid.
```
原因:
- 组件里有文件
- 但 KeyPath 是 registry value
- WiX v3 不能自动生成这种组件 GUID
解决:
生成固定 GUID
```powershell
[guid]::NewGuid().ToString().ToUpper()
```
然后写入:
```xml
<Component Id='binary0' Guid='302CAFB5-6951-426B-BC5A-988C351A2CF2'>
```
### ICE91
警告示例:
```text
ICE91: The file 'exe0' will be installed to the per user directory ...
```
原因:
- 文件安装到用户目录
- ICE 提醒如果该包被当作 per-machine 安装,文件不会复制到每个用户的 profile
处理:
- 如果安装包明确是 `perUser`,通常可以忽略。
- 如果目标是 `perMachine`,应改用 `ProgramFilesFolder`,不要安装到用户 profile。
### 新 MSI 没有覆盖旧 MSI
常见原因:
- `Cargo.toml` 版本号没变
- `UpgradeCode` 改了
- 旧版本是 per-machine新版本是 per-user
解决:
- 发布时递增版本号
- 保持 `UpgradeCode` 不变
- 如果旧版本是 per-machine先用管理员权限卸载旧版本
## 21. 调试 MSI 安装
生成详细安装日志:
```powershell
msiexec /i target\wix\eskin-model-player-5.0.0-x86_64.msi /l*v install.log
```
静默安装:
```powershell
msiexec /i target\wix\eskin-model-player-5.0.0-x86_64.msi /qn /l*v install.log
```
卸载:
```powershell
msiexec /x target\wix\eskin-model-player-5.0.0-x86_64.msi /l*v uninstall.log
```
如果安装失败,优先查看日志里:
```text
Return value 3
```
它附近通常是真正失败原因。
## 22. 推荐发布流程
1. 更新 `Cargo.toml` 版本号。
2. 确认 `wix/main.wxs` 的安装范围符合发布目标。
3. 执行 release 构建和 MSI 打包。
4. 在干净机器或干净用户环境中测试安装。
5. 测试启动程序。
6. 测试卸载后文件、快捷方式、PATH 是否清理干净。
7. 正式发布前对 MSI 签名。
命令:
```powershell
cd D:\eskin-player
cargo wix clean
cargo wix --bin-path "C:\Program Files (x86)\WiX Toolset v3.14\bin" --nocapture
```
验证:
```powershell
Get-ChildItem target\wix\*.msi
msiexec /i target\wix\eskin-model-player-5.0.0-x86_64.msi /l*v install.log
```
## 23. 本项目常用修改清单
### 改包名
修改 `Product/@Name`
```xml
Name='eskin-model-player'
```
以及 `Cargo.toml`
```toml
name = "eskin-model-player"
```
### 改显示厂商
修改:
```xml
Manufacturer='JOYSONQUIN'
```
以及:
```toml
authors = ["JOYSONQUIN"]
```
### 改安装目录名
修改:
```xml
<Directory Id='APPLICATIONFOLDER' Name='eskin-model-player'>
```
### 改可执行文件名
修改 `Cargo.toml`
```toml
[[bin]]
name = "ESkinPlayer"
```
同步修改 WiX
```xml
<File
Id='exe0'
Name='ESkinPlayer.exe'
Source='$(var.CargoTargetBinDir)\ESkinPlayer.exe'/>
```
### 关闭 PATH 写入
删除或注释:
```xml
<ComponentRef Id='Path'/>
```
也可以删除整个 `Environment` Feature。
### 从 per-user 改回 per-machine
修改:
```xml
InstallScope='perMachine'
InstallPrivileges='elevated'
```
安装目录改为:
```xml
<Directory Id='$(var.PlatformProgramFilesFolder)' Name='PFiles'>
```
PATH 改为:
```xml
System='yes'
```
注意:
- 这会重新要求管理员权限。
- 如果已经发布过 per-user 版本,切换安装范围前需要测试升级和卸载路径。
## 24. 维护注意事项
- `UpgradeCode` 是产品线标识,正常发布不要改。
- 发布新版本时递增 `Cargo.toml``version`
- 已发布组件的 `Guid` 不要随意变更。
- 用户目录安装使用 `HKCU` registry key 作为 `KeyPath`
- 系统目录安装通常需要管理员权限。
- 修改 PATH 后要测试安装和卸载。
- 加文件后必须在 `Feature` 中添加 `ComponentRef`,否则不会被安装。
- WiX 报错时优先看 `candle` 还是 `light` 阶段:
- `candle`XML 结构、语法、GUID、路径等编译错误。
- `light`链接、ICE 校验、MSI 表规则问题。