Skip to content

一步步教你在 Windows 上构建 dotnet 系应用的 UOS 软件安装包

Updated: at 12:43,Created: at 07:35

本文将详细指导大家如何逐步为 dotnet 系列应用创建满足 UOS 统信系统软件安装包的要求。在这里,我们所说的 dotnet 系列应用是指那些能够在 Linux 平台上构建 UI 框架的应用,包括但不限于 CPF 应用、UNO 应用、Avalonia 应用等

基于 dotnet 系的自发布自包含的能力,可以将 dotnet 系的应用进行发布为独立应用,无需框架依赖,如此即可执行 deb 包的二进制打包过程,从而很方便对接上 UOS 统信系统软件安装包的要求

整体步骤可以分为三个大步骤:

本文接下来将按照步骤顺序一步步告诉大家如何在 Windows 上为 dotnet 系的应用创建符合要求的 UOS 统信系统软件安装包。本文写于 2023.12.25 如果你阅读本文的时间距离编写时间过远,可能本文有些知识不正确

步骤一 发布应用

无论是 CPF 应用还是 UNO 应用或者 Avalonia 应用,在进行构建符合要求的 UOS 软件安装包的第一步都是对应用项目进行发布,且为了后续打包和分发的方便,十分推荐发布为独立应用。这些应用的大概的发布命令行如下,请根据你的实际需求进行更改

dotnet publish -c release -r linux-x64 --self-contained true

完成发布之后,咱即可拿发布出来的文件夹进行制作符合要求的 UOS 软件安装包。为了减少大家的学习成本,本文将一步步进行,本文在编写过程中,将会先略去在本文所没有用到的知识点。但请大家放心的是,按照本文的方法是可以构建出一个符合要求的 UOS 软件安装包,只有一些扩展可选部分的功能被本文略过

在进入步骤二之前,我推荐将发布的文件夹拷贝到 UOS 系统上测试一下,确保应用本身发布出来是能够正常运行

步骤二 组织文件结构

本文属于尽可能多的手动化打包过程,所使用的工具只有文本编辑器以及 WSL (适用于 Linux 的 Windows 子系统) 和 dpkg 工具。本文这里使用 WSL 仅仅只是为了使用 dpkg 工具而已,尽量使用官方推荐的打包工具。事实上,咱是完全可以将此过程在 Windows 上完成的。但为了减少引入额外的工具,本文依然使用 dpkg 工具进行打包,在本文后面,大家可以看到 dpkg 工具的戏份也只有一句话

2.1 准备打包环境

在开始之前,需要先准备好打包环境

为什么在 WSL 里面要选用 Debian 版呢?请自行搜搜 uos 和 debian 关系 你就知道咯

2.2 准备文件结构

开始之前的准备工作完成之后,接下来就可以开始正式的打包大业了。先来明确一下咱的目标:现在咱手头上有一个 Debian 的 WSL 工具,也有使用 UI 框架的应用进行独立发布之后的文件夹,意味着其二进制文件可以直接在 UOS 上双击运行。咱需要做的就是将独立发布之后的文件夹打进安装包里面,再搭配上 UOS 规范要求的图标、应用信息等即可

根据 应用打包规范 文档中心-统信UOS生态社区 文档,咱可以了解到符合 UOS 规范要求的安装包其实也是一个 deb 包。与之不同的只有文件结构组织形式,以及部分配置内容不相同而已

根据 如何构建符合要求的 UOS 软件安装包 - VVavE 文档,可以看到在 UOS 规范下的安装包的文件结构组织形式如下

├─DEBIAN
│ control
└─opt
└─apps
└─org.dotnetcampust.exampleapplication
│ info
├─entries
│ ├─applications
│ │ org.dotnetcampust.exampleapplication.desktop
│ │
│ └─icons
│ └─hicolor
│ └─24x24
│ └─apps
│ org.dotnetcampust.exampleapplication.png
└─files
└─bin
│ ExampleApplication
│ ExampleApplication.deps.json
│ ExampleApplication.dll
│ ExampleApplication.pdb
│ ExampleApplication.runtimeconfig.json
│ ExampleApplication.uprimarker
│ WindowsBase.dll
│ Microsoft.CSharp.dll
│ ...

再次感谢 VVavE 潮汐博客 本文接下来将有大量知识内容来源于此

本文接下来将按照文件夹组织的顺序,告诉大家如何进行组织

2.2.1 DEBIAN 文件夹

安装包的文件结构组织的根目录包含了 DEBIANopt 两个文件夹。其中的 DEBIAN/ 文件夹是构建过程文件夹,包含软件包构建过程相关的控制文件。在本文这里,只需放入 control 构建控制文件即可,此文件完全遵照 Debian 官方规范

为了方便大家理解,本文将对 UnoFileDownloader 下载器 进行打包作为例子,方便大家了解手动打包的细节

在开始制作安装包的时候,咱还需要给应用进行命名,即对 AppID 应用的唯一标识进行命名。在 UOS 里采用和 Android 系统类似的规范,应用商店只接受使用域名倒置规则命名的应用。请务必使用厂商的倒置域名+产品名作为应用包名,如 com.example.demo 格式,前半部分为厂商域名倒置,后半部分为产品名,如果使用非拥有者的域名作为前缀,可能会引起该域名拥有者进行申诉,导致软件被申诉下架或者删除

这里必须特别强调,只允许小写字母。且在本文下方出现的任何 AppIDappid${appid} 等占位符里,都需要采用此应用的唯一标识字符串,请务必确保手工打包时,各处命名都是使用相同的字符串

本文这里对 UnoFileDownloader 下载器 命名为 org.dotnetcampust.unofiledownloader 应用。尽管看起来有些不习惯且可读性较差,但 UOS 官方规范就是这样,只有遵守咯

先创建工作文件夹,接下来咱的所有文件组织结构都在此工作文件夹进行。如我这里创建的是 C:\lindexi\Work 文件夹

在工作文件夹里面先创建 DEBIAN 文件夹,请确保大小写哦,在 Linux 上是大小写敏感的

进入 DEBIAN 文件夹,再创建名为 control 的文本文件。接着打开文本编辑器,编辑 control 文件。此 control 文件要求使用 UTF-8 格式编码,且是不带 BOM 头的。所谓 BOM 头就是在文件的前两个字节里面写入编码信息,这是在 Windows 上常用的方式,原本 BOM 头是用来指示编码大小端的,后续被大家更开森用在了识别区分文件的编码上了。使用 SublimteText 编辑时,默认的配置就是不带 BOM 头的

按照 Debian 的规范,编写 control 的文件的内容,如以下例子

Package: org.dotnetcampust.unofiledownloader
Version: 1.1.3
Section: utils
Priority: optional
Architecture: amd64
Multi-Arch: foreign
Build-Depends: debhelper (>=9)
Standards-Version: 3.9.6
Maintainer: lindexi<lindexi_gd@outlook.com>
Homepage: https://blog.lindexi.com/
Description: 下载器.

以上的 control 的各个字段/属性的说明如下,以下内容来自于 从零开始制作 deb 文件 - hzSomthing 博客

DEBIAN/ 文件夹里面还可以包含其他的很多文件,只是本例子这里没有需求用到,就此略过。如感兴趣,请参阅 Debian 新维护者手册

2.2.2 opt 文件夹

完成 control 文件编写之后,接下来创建 /opt/apps/${appid}/ 文件夹,请将 ${appid} 替换为你的 AppID 值,此文件夹就是应用根目录文件夹

在本文这里也就是创建 C:\lindexi\Work\opt\apps\org.dotnetcampust.unofiledownloader\ 文件夹,请特别小心文件命名的小写问题

应用根目录下面应包含 filesentries 两个目录和一个 info 文件。文件结构如下

└─opt
└─apps
└─org.dotnetcampust.exampleapplication
│ info
├─entries
│ ├─applications
│ │ org.dotnetcampust.exampleapplication.desktop
│ │
│ └─icons
│ └─hicolor
│ └─24x24
│ └─apps
│ org.dotnetcampust.exampleapplication.png
└─files
└─bin
│ UnoFileDownloader.Skia.Gtk
│ ...

以下继续按照此顺序逐个为大家介绍

2.2.2.1 files 文件夹

先创建 files 文件夹,按照规范上所述,可以在 files 文件夹里面塞入任意的文件,也包括应用的可执行文件。规范上建议将可执行文件放入到 files/bin 文件夹,咱是听话的开发者,于是将 dotnet publish -c release -r linux-x64 --self-contained true 的输出文件夹(我的是 X:\lindexi\Code\dotnetCampus.FileDownloader\src\UnoFileDownloader\UnoFileDownloader\UnoFileDownloader.Skia.Gtk\bin\Release\net8.0\linux-x64\publish\ 文件夹)的文件全部拷贝(移动)到 files/bin 文件夹里面

2.2.2.2 entries 文件夹

再创建 entries 文件夹,在 entries 文件夹里面应该放入的内容是桌面/开始菜单的图标以及应用启动信息。在 entries 文件夹里面再创建 applications 文件夹,在 applications 文件夹里面创建 AppId.desktop 文本文件。同理请将 AppId.desktop 的 AppId 换成你的应用标识符,在本文例子里面,将创建的是 C:\lindexi\Work\opt\apps\org.dotnetcampust.unofiledownloader\entries\applications\org.dotnetcampust.unofiledownloader.desktop 文件

接下来打开文本编辑器,编辑 .desktop 文本文件,同理要求此文件采用 UTF-8 编码格式。以下是 org.dotnetcampust.unofiledownloader.desktop 文件例子内容,此文件当前使用的是标准的 desktop 格式,相关标准及其他字段可以参考 https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.1.html 文档或 desktop文件规范 - DeepinWiki 文档

[Desktop Entry]
Categories=Network
Name=UnoFileDownloader
Name[zh_CN]=简单的下载器
Keywords=deepin;uniontech;downloader
Keywords[zh_CN]=深度;统信;下载器
Comment=The UnoFileDownloader can download file.
Comment[zh_CN]=可以简单下载文件。
Exec=/opt/apps/org.dotnetcampust.unofiledownloader/files/bin/UnoFileDownloader.Skia.Gtk
Icon=org.dotnetcampust.unofiledownloader
Type=Application
Terminal=false
StartupNotify=true
MimeType=audio/aac;application/aac;

以下是从官方文档 里抄到的 Desktop 文件中重点字段的解释

Categories 应用类型可参考以下填写值来设置

除上述分类外,填写其他分类将会被归为“其他应用”

完成 .desktop 文本文件,接下来再创建 icons 文件夹用来存放应用图标。图标可以使用 svg 矢量格式和 png 非矢量格式。尽管官方文档说的是非矢量格式建议使用 png 格式,然而实际是只能使用png格式,我试过 jpg 等其他格式都不生效,都会被替换为默认的应用图标,将 jpg 改后缀为 png 也是不行的

矢量格式图标只需放一份,路径是 entries/icons/hicolor/scalable/apps/${appid}.svg 请在放入图标之后,拷贝其路径与本文档进行对比,看是否文件夹层级正确

如果使用非矢量格式,请按照 分辨率 来放置图标,如:

entries/icons/hicolor/24x24/apps/${appid}.png
entries/icons/hicolor/16x16/apps/${appid}.png

同样,在放入图标之后,请拷贝其路径与本文档进行对比。我在制作安装包时,就因为少了一层 hicolor 文件夹,调查了半天才发现是文件夹层级不对

2.2.2.3 info 文件

完成应用根目录下面包含的 entriesfiles 两个目录之后,接下来开始编写 info 文件。此文件是应用的描述文件,用于dde桌面环境安装软件包时自动配置程序的安装文件,使用 JSON 格式,内容一般如下

{
"appid": "org.dotnetcampust.unofiledownloader",
"name": "UnoFileDownloader",
"version": "1.1.3",
"arch": ["amd64", "arm64"],
"permissions":
{
"autostart": false,
"notification": false,
"trayicon": false,
"clipboard": false,
"account": false,
"bluetooth": false,
"camera": false,
"audio_record": false,
"installed_apps": false
}
}

这里需要额外提醒大家,在 UOS 的官方文档提供的 info 文件例子里面,其文件内容的 json 格式是错误的,在 permissions 属性和下一个属性之间少了一个逗号,如果想要拷贝官方的例子,还请自行确保 json 格式正确

写到这里,文件结构就完成了,本例子完成之后的文件结构如下

C:\lindexi\Work
├─DEBIAN
│ control
└─opt
└─apps
└─org.dotnetcampust.unofiledownloader
│ info
├─entries
│ ├─applications
│ │ org.dotnetcampust.unofiledownloader.desktop
│ │
│ └─icons
│ └─hicolor
│ └─24x24
│ └─apps
│ org.dotnetcampust.unofiledownloader.png
└─files
└─bin
│ UnoFileDownloader.dll
│ UnoFileDownloader.pdb
│ UnoFileDownloader.Skia.Gtk
│ UnoFileDownloader.Skia.Gtk.deps.json
│ UnoFileDownloader.Skia.Gtk.dll
│ UnoFileDownloader.Skia.Gtk.pdb
│ UnoFileDownloader.Skia.Gtk.runtimeconfig.json
│ UnoFileDownloader.uprimarker
│ WindowsBase.dll
│ Microsoft.CSharp.dll
│ ...

完成了文件结构,下面将进入打包环境,开始创建 deb 包

步骤三 打包 deb 文件

本例子打包采用的是 Debian 的 WSL 里的 dpkg 工具进行创建 deb 文件,开始之前请确保准备好 Debian 环境。下载地址:https://www.microsoft.com/store/productId/9MSVKQC78PK6?ocid=pdpshare

先使用 cd 命令进入工作路径,如本文例子使用的 C:\lindexi\Work 文件夹

cd /mnt/c/lindexi/Work

进入其他盘符请使用 /mnt/[盘符] 的方式。值得一提的是 WSL 里面对中文支持还不错,可以放心进入中文文件夹

进入之后使用 ls 命令确保进入正确的文件夹,预期输出如下

lindexi@DESKTOP-51A5UGG:/mnt/c/lindexi/Work$ ls
DEBIAN opt

接下来输入 dpkg-deb -b . 命令进行打包

lindexi@DESKTOP-51A5UGG:/mnt/c/lindexi/Work$ dpkg-deb -b . UnoFileDownloader.deb
dpkg-deb: building package 'org.dotnetcampust.unofiledownloader' in 'UnoFileDownloader.deb'.

如此拿到的 UnoFileDownloader.deb 包就是可在 UOS 上使用的安装包文件

文件权限

在 WSL 里面 dpkg-deb 打包时,如果和我一样采用的是挂载某个磁盘路径,且磁盘是 NTFS 格式时,可能会遇到如下错误提示

dpkg-deb: error: control directory has bad permissions 777 (must be >=0755 and <=0775)

以上错误提示即使使用 chmod -R 0755 DEBIAN/controlchmod -R 0755 DEBIAN 命令也是没有用的。因为 wsl 如果进入的是 mnt 的 NTFS 磁盘格式下,是无法通过 chmod 设置 0755 权限的,详细请参阅 https://www.askingbox.com/question/error-message-dpkg-deb-error-control-directory-has-bad-permissions-777

For NTFS drives, this value defaults to 000, which equates to the rights of 777 and is too much here. Therefore, we change the value to umask=022, which equals the right of 755.

解决方法是在 WSL 配置里面,设置 umask=22 从而将权限转换为 755 的方式,具体步骤如下

进入 Debian 命令行,先提权,再创建或编辑 /etc/wsl.conf 文件

lindexi@DESKTOP-51A5UGG: sudo su
lindexi@DESKTOP-51A5UGG: vi /etc/wsl.conf

/etc/wsl.conf 文件配置 umask=22 的值,完成配置之后的文件内容大概如下,如果本身就不存在 /etc/wsl.conf 文件,则可以完全替换为下面内容,详细请参阅 https://unix.stackexchange.com/questions/252244/dpkg-deb-error-control-directory-has-bad-permissions

[automount]
enabled = true
root = /mnt/
options = "metadata,umask=22,fmask=11"
mountFsTab = false

配置完成之后,需要重启 WSL 才能生效。重启 WSL 需要使用命令行(新开一个终端)输入 wsl --shutdown 命令进行关闭,随后再启动 Debian 命令行即可完成重启

这里请不要使用网上乱抄的关闭 LxssManager 服务的方法

或者保险一些,自己重启电脑也可以

重启完成之后,再输入 dpkg-deb -b . 命令进行打包

签名

通过以上步骤构建所拿到的 deb 包是还没带签名的,想要进行签名,请参阅 开发者调试签名 文档中心-统信UOS生态社区

签名步骤需要进入到 UOS 系统上进行,对于开发环境来说,即使没有签名的 deb 包也是可以安装的。也就是签名这一步是可选的

在开始签名之前,请到 https://www.chinauos.com/ 注册开发者账号

接着打开统信应用商店,搜索“证书工具”,安装证书工具

安装完成之后,进入 UOS 的命令行,可使用快捷键 ctrl+alt+T 进入,输入 cert-tool -username="UOS帐号" -password="UOS密码" 进行初始化,如下面命令行

cert-tool -username=lindexi -password=123123123

完成初始化之后接下来即可对未签名的 deb 包进行签名,命令行格式如下

deepin-elf-sign-deb [deb-file]

请将 [deb-file] 替换为要签名的deb包路径

如本文例子的命令行如下

deepin-elf-sign-deb UnoFileDownloader.deb

输入之后慢慢等签名,最后看到一句成功的警告就是完成签名了

Signed successfully!

签名完成的 deb 包将会放在待签名的 deb 包目录下创建 signed_deb 目录里面,咱可以进入此 signed_deb 文件夹看一下签名完成之后的 deb 文件

为了确保签名已经成功,可以使用 deepin-deb-verify 命令验证签名

先使用 cd 命令进入到 signed_deb 文件夹,再使用如下格式命令验证签名

deepin-deb-verify [签名的 deb 包]

请将 [签名的 deb 包] 替换为你实际的 deb 包,在本例子里面输入的是以下命令

deepin-deb-verify UnoFileDownloader.deb

如输出 [INFO] signature verified! 就是证明签名成功了

接下来可以试试双击此签名完成的 deb 进行安装

常见问题

dpkg 提示 missing final newline 错误

原因是 /DEBIAN/control 文件里面最后一行少了空行

dpkg 提示 missing ‘Package’ field 错误

可能是编辑 /DEBIAN/control 文件时,编辑器写入 BOM 头

可以使用二进制查看器,看一下 /DEBIAN/control 文件开头两个字节是否 BOM 头,如 EF BB BF 开头就是 UTF-8 BOM 头。详细关于 BOM 头,请看 BOM(字节顺序标记(ByteOrderMark))_百度百科

解决方法是换个文本编辑器或手动干掉 BOM 信息

dpkg 提示 No such file or directory 错误

详细错误信息是

dpkg-deb: error: failed to open package info file '/DEBIAN/control' for reading: No such file or directory

请确保进入了正确的文件夹,工作路径正确

开始菜单图标空白或是默认应用图标

原因是图标文件错误,或者是文件路径错误

比如我少了一层 hicolor 文件夹,以及将 512x512 写成 512*512 文件夹

推荐放入图标之后,拷贝路径和本文档进行对比,了解是否放错文件夹

确保图标文件的格式在非矢量格式下使用 .png 格式,无论是后缀名还是图片格式本身,特别是从网上下载的图标。如不确定的话,可使用 MediaInfo 或 Windows 自带图片查看工具了解其文件格式

矢量 svg 图标请确保 svg 使用的是简单的格式,而不是超级复杂的 svg 格式

应用启动闪退

打包之前,推荐先将发布的输出文件夹拷贝到 UOS 上测试运行情况,先确保发布二进制文件本身可以正常运行

如二进制本身可以运行但开始菜单点击无法启动或无法找到安装的应用,请检查 info 文件格式、字段以及字段值的内容是否符合要求,以及 desktop 文件是否正确

打开控制台调试

如果期望在打包之后运行安装的应用程序进行调试,要看到输出控制台,可以在打包过程中编辑 .desktop 文本文件,设置 Terminal 为 true 的值。如此将会在启动程序时显示控制台

自动化工具

请参阅 Packaging.DebUOS 专门为 dotnet 应用制作 UOS 安装包

如果有其他不明白的,还请加入 810052083 群进行交流

更多国产软件开发请参阅 博客导航

参考文档

How to Create a Simple Debian Package - Baeldung on Linux

如何製作「deb檔(Debian Package)」 Ubuntu Basic Skill

A Unix Packager For MS-Windows Systems - Microsoft Windows Packager

从零开始制作 deb 文件 - hzSomthing

https://www.debian.org/doc/manuals/maint-guide/

如何构建符合要求的 UOS 软件安装包 - VVavE

mass1ve-err0r/wpkg: A kinda dpkg-deb for Windows 没有用

kuiperzone/PupNet-Deploy: PupNet Deploy is a cross-platform deployment utility which packages your .NET project as a ready-to-ship installation file in a single step.

quamotion/dotnet-packaging: Extensions for the .NET Core CLI which help packaging and publishing .NET Core applications

应用打包规范 文档中心-统信UOS生态社区

源码打包为deb文档中心-统信UOS生态社区

公网deb包转uos的deb包 文档中心-统信UOS生态社区 uos打包——公网deb包转uos的deb包-CSDN博客

开发者调试签名 文档中心-统信UOS生态社区

安装 WSL 的 Debian 系统

https://www.askingbox.com/question/error-message-dpkg-deb-error-control-directory-has-bad-permissions-777

https://unix.stackexchange.com/questions/252244/dpkg-deb-error-control-directory-has-bad-permissions

Automatically Configuring WSL - Windows Command Line


知识共享许可协议

原文链接: http://blog.lindexi.com/post/%E4%B8%80%E6%AD%A5%E6%AD%A5%E6%95%99%E4%BD%A0%E5%9C%A8-Windows-%E4%B8%8A%E6%9E%84%E5%BB%BA-dotnet-%E7%B3%BB%E5%BA%94%E7%94%A8%E7%9A%84-UOS-%E8%BD%AF%E4%BB%B6%E5%AE%89%E8%A3%85%E5%8C%85

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。 欢迎转载、使用、重新发布,但务必保留文章署名 林德熙 (包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我 联系