☰
统信UOS下Avalonia跨平台应用打包deb安装包全流程
2026/9/26 5:21:16 网站建设 项目流程

做 Avalonia 跨平台桌面开发的朋友,迟早会碰上一个现实问题:程序在 Windows 上跑得好好的,换成统信 UOS 就不知道怎么发给用户了。你总不能让人家先装 .NET SDK,再把 publish 出来的几十个文件手动复制到固定目录吧?最省心的方案就是打一个 .deb 安装包,让用户拿到手就能双击安装,装完启动器里出现图标,也能被系统统一管理、随时卸载。这篇文章就把我在统信 UOS 上把 Avalonia 程序打包成 deb 安装包的全过程讲一遍,包含发布模式怎么选、deb 内部结构怎么回事、control 文件怎么写、安装脚本怎么配,以及装完后常见的几个坑怎么排。适合已经能用 Avalonia 做出程序、正准备向统信或 deepin 环境分发的开发者,也适合团队里负责出包运维的同学参考。

1. 为什么要在统信系统上打 deb 包,以及准备工作

1.1 先认识一下 .deb 包到底是什么

Deb 是 Debian 系发行版通用的软件包格式,统信 UOS、deepin、Ubuntu 用的都是这一套。它的本质是一个 ar 归档文件,里面拆成了三块:debian-binary 记录格式版本,control.tar.gz 放包的控制信息,data.tar.gz 放实际要安装到系统的文件。安装的时候 dpkg 会把 data 部分解压到系统对应目录,然后执行 control 里的安装脚本,整个过程对用户来说就是双击一下的事。

有人可能会问:为什么不直接丢个 tar.gz 给用户?因为 tar.gz 只做了文件归档,依赖关系、桌面入口、图标注册、软件中心识别这些统统没有。而 deb 包是系统原生支持的格式,装上之后会被 dpkg 数据库记录,可以在统信的应用商店或者软件中心里被识别,卸载也干净。对政企内网来说,这种可追踪、可统一管理的分发方式比散装压缩包靠谱得多。

另外,deb 包还能声明依赖关系。比如你的 Avalonia 程序依赖 libfontconfig1、libgl1 这些系统库,写进 Depends 后,用户安装时如果缺了依赖,源里能自动拉取补齐,不会出现装上之后一启动就报缺库的尴尬。

1.2 打包前的环境检查与工具准备

统信的默认源里自带 dpkg 和 dpkg-deb,这两个是打包的核心工具,一般来说不需要额外安装。工作之前我习惯先看一眼系统信息和架构,命令如下:

cat /etc/os-version uname -m dpkg-deb --version dotnet --info

架构很重要,统信 UOS 主要是 x86_64,但也存在 arm64、龙芯等版本。Avalonia 发布的时候 runtime identifier 要对得上,比如 x86_64 对应linux-x64,arm64 对应linux-arm64。我这边常用的环境是统信 UOS 20 专业版 (x86_64),打包出来的包名带 amd64 后缀。

如果统信系统上没有安装 .NET SDK,直接去 dotnet 官网下载对应架构的 tar.gz 包解压就行,不需要走系统源。解压之后把 dotnet 路径加进 PATH,再执行dotnet --info验证。这个环节踩过坑的人都知道,arm64 的机器下载 x64 的 SDK 是跑不起来的,务必确认架构配对。

提示:打包全过程建议用普通用户执行,然后通过 sudo 做安装验证,不要在 root 环境下直接操作文件。普通用户环境更贴近最终用户的使用状态,能提前暴露权限问题。

2. Avalonia 应用的 Linux 发布姿势

2.1 自包含还是框架依赖,我为什么选自包含

Avalonia 程序在 Linux 上的发布模式有两个方向:framework-dependent(框架依赖)和 self-contained(自包含)。框架依赖模式生成的包小,通常只有几 MB,但目标机器上必须装有对应版本的 .NET 运行时;自包含模式把整个 .NET 运行时也编进发布目录,体积会重不少,但目标机器上不需要预装任何 .NET 组件。

我在统信 UOS 上分发,一律建议自包含。原因很简单:统信系统本身自带的 dotnet 往往不是最新版本,不同机器上的运行时版本参差不齐。你辛辛苦苦写好的程序,不能把运行环境寄托在用户机器的运气上。自包含模式缺点是包大,一个 Avalonia 程序打出来通常有 70~100MB,但这年头硬盘和带宽都不差,换来的是"装上就能跑"的确定性。

2.2 发布命令与输出目录

发布命令我常用这条:

dotnet publish -c Release -r linux-x64 --self-contained true -o ./out/publish

参数逐个说:-c Release是发布 Release 配置,-r linux-x64是运行时标识,--self-contained true强制自包含,-o指定输出目录。执行完之后,./out/publish下会有一个可执行的二进制文件(比如 MyApp),旁边跟着一大串 .dll、.json 文件,它们都要进安装包。

这里有个细节值得注意:不要轻易开启单文件发布(PublishSingleFile)。Avalonia 涉及 Skia 渲染等原生库,单文件模式下部分原生库的释放逻辑在 Linux 上容易出现临时文件残留问题,我见过多次装上之后莫名其妙起不来、或者个别功能失灵的反馈。老老实实用目录形式发布,虽然文件多,但稳定,出问题也好排查。

2.3 Avalonia 在 Linux 上离不开哪些系统库

Avalonia 是托管 UI 框架,但底层渲染和窗口管理还是绕不开系统原生库。打包之前心里要有这张依赖清单:

  • libfontconfig1:字体配置与匹配,缺失则界面文字渲染异常
  • libfreetype6:字体光栅化,直接关系到文字显示
  • libice6、libsm6:X11 会话管理相关
  • libgl1 / mesa:OpenGL 渲染路径,缺失会导致启动直接崩掉
  • libgtk-3-0:部分文件对话框和系统集成功能需要 GTK

这些库最好写进 deb 的 Depends 字段,这样安装时统信会自动拉取补齐。别贪图省事不写依赖,等用户反馈"装完打不开"再去排查,成本比写一行依赖高得多。

3. .deb 包的结构解析与文件规范

3.1 deb 包打包前的目录长什么样

虽然 deb 安装包本身是 ar 归档,但我们手工打包时只需要组织一个"过渡目录"(staging directory),dpkg-deb 会自动把它压成标准格式。这个过渡目录结构大概是这样的:

MyApp_deb/ ├── DEBIAN/ │ ├── control │ ├── postinst │ └── postrm └── usr/ ├── lib/ │ └── myapp/ │ ├── MyApp │ └── ...(dll、json 等发布文件) └── share/ ├── applications/ │ └── myapp.desktop └── icons/ └── hicolor/ └── 512x512/ └── apps/ └── myapp.png

DEBIAN 目录(注意全大写)存放控制信息,不会安装到系统里去;usr 目录下的内容会原样铺到系统根目录的对应位置。也就是说,usr/lib/myapp/MyApp最终会出现在系统的/usr/lib/myapp/MyApp。

3.2 control 文件字段逐个说清楚

control 文件是整个包的身份证明,写错一点 dpkg 都会认账。我贴一个常用的模板:

Package: myapp Version: 1.0.0 Architecture: amd64 Maintainer: Your Name <you@example.com> Installed-Size: 10240 Depends: libfontconfig1, libice6, libsm6, libgl1, libgtk-3-0 Section: utils Priority: optional Description: My Avalonia Application A cross-platform Avalonia desktop application for UOS.

字段很容易出问题,逐个说:

  • Package:包名,只能用小写英文字母、数字和连字符。我用 MyApp 的驼峰写法打包时就踩过坑,dpkg 直接报 invalid characters。
  • Version:版本号,建议和项目 tag 对齐,方便追溯。
  • Architecture:只能写amd64,不能写x86_64,这是 dpkg 的架构命名,写错了装上会提示架构不匹配。
  • Installed-Size:单位是 KB,估算发布目录总大小就行,写大点没坏处。
  • Depends:逗号分隔的依赖包列表,建议把 2.3 节的库都写进去。
  • Description:第一行是简短描述,后续行必须以一个空格开头作为详细说明,缩进错了 control 文件就解析失败。

注意:control 文件必须用 UTF-8 编码保存,换行用 LF。Windows 上编辑过再拷到 Linux 容易带上 CRLF,dpkg 会报奇怪的语法错误。

3.3 安装脚本 postinst 与 postrm 的职责划分

postinst 在系统文件全部复制完成之后执行,体积不大但作用很关键。它通常干三件事:给主程序加执行权限、在 /usr/local/bin 下建一个软链方便命令行调用、刷新桌面数据库和图标缓存。postrm 是卸载时执行的反向脚本,主要清理软链。两个脚本都要以#!/bin/bash开头,并且文件要有可执行权限,否则 dpkg 会无视它们。

还要在 postinst 里注意一点:脚本执行失败会直接导致整个安装事务回滚,所以开头加set -e很有必要。刷新图标缓存这种操作偶尔会因环境问题失败,可以在末尾加上|| true兜底,别让一个缓存刷新失败毁掉整个安装流程。

4. 全流程实操:从 publish 到 dpkg -i

4.1 建立打包工作目录并拷贝发布文件

现在开始动真格的。先建一个干净的目录作为打包根,然后把第二步 publish 出来的所有文件完整拷贝进去。

mkdir -p MyApp_deb/usr/lib/myapp mkdir -p MyApp_deb/usr/share/applications mkdir -p MyApp_deb/usr/share/icons/hicolor/512x512/apps cp -a ./out/publish/* MyApp_deb/usr/lib/myapp/ chmod +x MyApp_deb/usr/lib/myapp/MyApp

拷贝务必用-a保留文件属性,因为部分文件可能带只读标记,丢了权限后面会引发奇怪的问题。主程序和 .so 文件尤其要保证可执行权限。

4.2 写 .desktop 桌面入口文件

统信 DDE 桌面环境通过 .desktop 文件识别应用图标,这一步不做,装完包启动器里什么都看不到。文件内容如下:

[Desktop Entry] Type=Application Name=MyApp Comment=My Avalonia Application Exec=/usr/lib/myapp/MyApp Icon=/usr/share/icons/hicolor/512x512/apps/myapp.png Terminal=false Categories=Utility;Office; StartupNotify=true

Exec 一定要写绝对路径,别写相对路径,也别指望系统会自动搜 PATH。Icon 同样用绝对路径指向 deb 里带进去的图标文件。图标建议用 512x512 的 PNG,开发环境下直接拿应用图标导出成 png 放到指定目录即可。Categories 可以按需写,Utility;Office;这组合在统信启动器里能被正常归类和搜索到。

4.3 亲手写 control 和安装脚本

两个目录结构准备好后,开始写 DEBIAN 下的三个文件。control 直接用 3.2 节的模板,把包名和版本改掉就行,not going to repeat。postinst 我一般这么写:

#!/bin/bash set -e chmod +x /usr/lib/myapp/MyApp ln -sf /usr/lib/myapp/MyApp /usr/local/bin/myapp update-desktop-database /usr/share/applications/ >/dev/null 2>&1 || true gtk-update-icon-cache /usr/share/icons/hicolor/ >/dev/null 2>&1 || true

postrm 就干一件事:

#!/bin/bash set -e rm -f /usr/local/bin/myapp

写完之后别忘给这两个脚本加执行权限:

chmod +x MyApp_deb/DEBIAN/postinst MyApp_deb/DEBIAN/postrm

这一步漏了,安装时 postinst 脚本不会执行,届时你会发现装完包没有可执行按钮、启动器里也没有图标。

4.4 用 dpkg-deb 打包并验证内容

目录结构完整、脚本权限到位之后,执行打包命令:

dpkg-deb --build --root-owner-group MyApp_deb MyApp_1.0.0_amd64.deb

--root-owner-group很重要,它把包内文件的 owner 强制设为 root,不然打出来的包里文件 owner 是你当前用户的 UID,其他用户装上之后可能出现文件属主混乱。打包完成后用下面两条命令检查内容:

dpkg-deb --info MyApp_1.0.0_amd64.deb dpkg-deb -c MyApp_1.0.0_amd64.deb

--info看 control 信息,-c列出内含文件清单。我习惯在正式发布之前把这两步输出都过一遍,确认版本号、依赖、关键文件都在。

5. 安装测试与常见问题排查

5.1 安装卸载验证流程

打包不是终点,装上能用才是终点。测试机上执行安装:

sudo dpkg -i MyApp_1.0.0_amd64.deb

如果系统源里有缺失依赖,dpkg 会提示依赖未满足,这时执行sudo apt --fix-broken install自动补齐,然后重新 dpkg -i。装完确认包已注册:

dpkg -l | grep myapp

然后去启动器里搜索 MyApp,点开跑一跑,确认主流程正常。卸载测试也建议跑一遍:

sudo dpkg -r myapp

卸载后检查/usr/local/bin/myapp软链是否被清理干净,桌面图标是否消失。来回迭代验证没问题,这个包才能对外发。

5.2 我踩过的六个坑

打包这事儿看着简单,实际操作里坑一个接一个。我把最有代表性的六个列出来:

  1. Package 字段用了大写字母:dpkg 直接报"package name has invalid characters"。包名永远用小写。
  2. .desktop 文件里 Exec 写的相对路径:启动器图标点了没反应,改成绝对路径立刻好。
  3. 忘了给 MyApp 主程序加执行权限:安装正常,但双击无反应,命令行直接提示 Permission denied。
  4. control 文件 Windows 编辑带 CRLF:dpkg --build 不报错,安装时提示"invalid control file"。写完后用file命令看一眼格式最保险。
  5. 架构写成 x86_64:安装提示 architecture mismatch,改成 amd64 才通过。
  6. 图标路径不匹配:图标实际在 512x512 目录,.desktop 却指向其他尺寸,启动器里只显示默认齿轮图标。

5.3 装上之后启动黑屏或者闪退怎么办

这个问题在统信 UOS 上遇到得不算少,通常和图形环境或者渲染库有关。统信 DDE 默认可能跑在 X11,也可能跑在 Wayland,而 Avalonia 程序在不同后端下的表现并不一致。如果启动黑屏,先检查系统是否装了 mesa 等 OpenGL 库,缺了直接补上:

sudo apt install mesa-utils libgl1

如果补了图形库还黑屏,可以试试在 .desktop 文件的 Exec 行后面追加环境变量强制 X11 后端:

Exec=env DISPLAY=:0 /usr/lib/myapp/MyApp

这个变量对部分用户环境有效,但最有效的方法是在程序里手动指定 Avalonia 的 backend 优先级。在 Main 函数里可以使用 AppBuilder 配置 X11 优先:

.UseX11()

这样至少能保证在没有 Wayland 支持的情况下程序能落到 X11 路径,不至于一启动就黑。

结尾再分享一个习惯:我实际用这套流程出过几次包之后,就把 publish 和打包整个过程写成了一个 build-deb.sh 脚本放在仓库的 build 目录下,每次发版只需要改版本号和包名,一条命令出包。包里文件多了之后,还可以顺便在脚本里把 md5sums 生成出来放进 DEBIAN 目录,方便 dpkg 完整性校验。用 dpkg-deb 手工打 deb 包并不难,难的是把流程规范化和自动化。把这套沉淀下来,后续不管出多少个统信版本,都是改个版本号的事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询