Avalonia Linux programs can be executed in most Linux distros by double-clicking the executable or by starting it from the terminal. Nevertheless, for a better user experience, it is recommended to have the program installed, so the user can start it via desktop shortcut, that exists in desktop environments such as GNOME and KDE, or via command-line, by having the program added to PATH.
Debian and Ubuntu related distros have their applications packaged in .deb files, that can be installed via sudo apt install ./your_package.deb.
Tutorial
In this tutorial, we will use the dpkg-deb tool to compile your .deb package.
1) Organize program files in a staging folder
Debian packages follow this basic structure:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<span class="token-line"><span class="token plain">./staging_folder/</span> </span><span class="token-line"><span class="token plain">├── DEBIAN</span> </span><span class="token-line"><span class="token plain">│ └── control </span><span class="token comment"># package control file</span> </span><span class="token-line"><span class="token plain">└── usr</span> </span><span class="token-line"><span class="token plain"> ├── bin</span> </span><span class="token-line"><span class="token plain"> │ └── myprogram </span><span class="token comment"># starter script</span> </span><span class="token-line"><span class="token plain"> ├── lib</span> </span><span class="token-line"><span class="token plain"> │ └── myprogram</span> </span><span class="token-line"><span class="token plain"> │ ├── libHarfBuzzSharp.so </span><span class="token comment"># Avalonia native library</span> </span><span class="token-line"><span class="token plain"> │ ├── libSkiaSharp.so </span><span class="token comment"># Avalonia native library</span> </span><span class="token-line"><span class="token plain"> │ ├── other_native_library_1.so</span> </span><span class="token-line"><span class="token plain"> │ ├── myprogram_executable </span><span class="token comment"># main executable</span> </span><span class="token-line"><span class="token plain"> │ ├── myprogram.dll </span> </span><span class="token-line"><span class="token plain"> │ ├── my_other_dll.dll </span> </span><span class="token-line"><span class="token plain"> │ ├── </span><span class="token punctuation">..</span><span class="token plain">. </span><span class="token comment"># all files from dotnet publish</span> </span><span class="token-line"><span class="token plain"> └── share</span> </span><span class="token-line"><span class="token plain"> ├── applications</span> </span><span class="token-line"><span class="token plain"> │ └── MyProgram.desktop </span><span class="token comment"># desktop shortcut file</span> </span><span class="token-line"><span class="token plain"> ├── icons</span> </span><span class="token-line"><span class="token plain"> │ └── hicolor</span> </span><span class="token-line"><span class="token plain"> │ ├── </span><span class="token punctuation">..</span><span class="token plain">. </span><span class="token comment"># other resolution icons (optional)</span> </span><span class="token-line"><span class="token plain"> └── pixmaps</span> </span><span class="token-line"><span class="token plain"> └── myprogram.png </span><span class="token comment"># main application icon</span> </span> |
Meaning of each folder:
DEBIAN: contains thecontrolfile./usr/bin/: contains the starter script (recommended for starting your program via command-line)./usr/lib/myprogram/: where all files generated bydotnet publishgo into./usr/share/applications/: folder for the desktop shortcut./usr/share/pixmaps/and/usr/share/icons/hicolor/**: folders for application icons.
The /usr/share/icons/hicolor/** are optional, as in your app icon will probably show up on desktop even without those images, however, it is recommended to have them for better resolution.
2) Make the control file
The control file goes inside the DEBIAN folder.
This file describes general aspects of your program, such as its name, version, category, dependencies, maintainer, processor architecture and licenses. Debian docs have a more thorough description of all possible fields in the file.
Don’t worry too much about filling all possible fields, most aren’t required. This tutorial is to make a “good-enough” Debian package.
The .NET dependencies can be listed by running apt show dotnet-runtime-deps-8.0 (suffix changes for other .NET versions); they will appear on the line starting with Depends: …. You can also check them in the .NET Core repo.
Avalonia required dependencies are: libx11-6, libice6, libsm6, libfontconfig1.
Overall, all .NET and Avalonia dependencies are required, plus any others specific of your app.
Below is a simple example of a control file.
|
1 2 3 4 5 6 7 8 9 10 11 12 |
<span class="token-line"><span class="token key atrule">Package</span><span class="token punctuation">:</span><span class="token plain"> myprogram</span> </span><span class="token-line"><span class="token key atrule">Version</span><span class="token punctuation">:</span><span class="token plain"> 3.1.0</span> </span><span class="token-line"><span class="token key atrule">Section</span><span class="token punctuation">:</span><span class="token plain"> devel</span> </span><span class="token-line"><span class="token key atrule">Priority</span><span class="token punctuation">:</span><span class="token plain"> optional</span> </span><span class="token-line"><span class="token key atrule">Architecture</span><span class="token punctuation">:</span><span class="token plain"> amd64</span> </span><span class="token-line"><span class="token key atrule">Installed-Size</span><span class="token punctuation">:</span> <span class="token number">68279</span> </span><span class="token-line"><span class="token key atrule">Depends</span><span class="token punctuation">:</span><span class="token plain"> libx11</span><span class="token punctuation">-</span><span class="token number">6</span><span class="token punctuation">,</span><span class="token plain"> libice6</span><span class="token punctuation">,</span><span class="token plain"> libsm6</span><span class="token punctuation">,</span><span class="token plain"> libfontconfig1</span><span class="token punctuation">,</span><span class="token plain"> ca</span><span class="token punctuation">-</span><span class="token plain">certificates</span><span class="token punctuation">,</span><span class="token plain"> tzdata</span><span class="token punctuation">,</span><span class="token plain"> libc6</span><span class="token punctuation">,</span><span class="token plain"> libgcc1 </span><span class="token punctuation">|</span><span class="token plain"> libgcc</span><span class="token punctuation">-</span><span class="token plain">s1</span><span class="token punctuation">,</span><span class="token plain"> libgssapi</span><span class="token punctuation">-</span><span class="token plain">krb5</span><span class="token punctuation">-</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token plain"> libstdc++6</span><span class="token punctuation">,</span><span class="token plain"> zlib1g</span><span class="token punctuation">,</span><span class="token plain"> libssl1.0.0 </span><span class="token punctuation">|</span><span class="token plain"> libssl1.0.2 </span><span class="token punctuation">|</span><span class="token plain"> libssl1.1 </span><span class="token punctuation">|</span><span class="token plain"> libssl3</span><span class="token punctuation">,</span><span class="token plain"> libicu </span><span class="token punctuation">|</span><span class="token plain"> libicu74 </span><span class="token punctuation">|</span><span class="token plain"> libicu72 </span><span class="token punctuation">|</span><span class="token plain"> libicu71 </span><span class="token punctuation">|</span><span class="token plain"> libicu70 </span><span class="token punctuation">|</span><span class="token plain"> libicu69 </span><span class="token punctuation">|</span><span class="token plain"> libicu68 </span><span class="token punctuation">|</span><span class="token plain"> libicu67 </span><span class="token punctuation">|</span><span class="token plain"> libicu66 </span><span class="token punctuation">|</span><span class="token plain"> libicu65 </span><span class="token punctuation">|</span><span class="token plain"> libicu63 </span><span class="token punctuation">|</span><span class="token plain"> libicu60 </span><span class="token punctuation">|</span><span class="token plain"> libicu57 </span><span class="token punctuation">|</span><span class="token plain"> libicu55 </span><span class="token punctuation">|</span><span class="token plain"> libicu52</span> </span><span class="token-line"><span class="token key atrule">Maintainer</span><span class="token punctuation">:</span><span class="token plain"> Ken Lee <kenlee@outlook.com</span><span class="token punctuation">></span> </span><span class="token-line"><span class="token key atrule">Homepage</span><span class="token punctuation">:</span><span class="token plain"> https</span><span class="token punctuation">:</span><span class="token plain">//github.com/kenlee/myprogram</span> </span><span class="token-line"><span class="token key atrule">Description</span><span class="token punctuation">:</span><span class="token plain"> This is MyProgram</span><span class="token punctuation">,</span><span class="token plain"> great for doing X.</span> </span><span class="token-line"><span class="token key atrule">Copyright</span><span class="token punctuation">:</span><span class="token plain"> 2022</span><span class="token punctuation">-</span><span class="token plain">2024 Ken Lee <kenlee@outlook.com</span><span class="token punctuation">></span> </span> |
3) Make the starter script
This step is recommended for two reasons: first, to reduce the complexity of the desktop shortcut, and second, to make your app runnable from Terminal.
The starter script file name should preferrably be myprogram (without .sh extension), so whenever your user types “myprogram” on the Terminal, he / she will start your program.
The myprogram_executable file usually has the same name as its .NET project, e.g., if your Avalonia .csproj project is named MyProgram.Desktop, then the main executable generated by dotnet publish will be MyProgram.Desktop.
Example of starter script:
|
1 2 3 4 5 |
<span class="token-line"><span class="token shebang important">#!/bin/bash</span> </span><span class="token-line"><span class="token comment"># use exec to not have the wrapper script staying as a separate process</span> </span><span class="token-line"><span class="token comment"># "$@" to pass command line arguments to the app</span> </span><span class="token-line"><span class="token builtin class-name">exec</span><span class="token plain"> /usr/lib/myprogram/myprogram_executable </span><span class="token string">"</span><span class="token string variable">$@</span><span class="token string">"</span> </span> |
4) Make the desktop shortcut
The desktop shortcut file follows the freedesktop specification. Arch Linux Wiki also has good related information.
Below is an example of a desktop shortcut file.
|
1 2 3 4 5 6 7 8 9 10 11 12 |
<span class="token-line"><span class="token plain">[Desktop Entry]</span> </span><span class="token-line"><span class="token plain">Name=MyProgram</span> </span><span class="token-line"><span class="token plain">Comment=MyProgram, great for doing X</span> </span><span class="token-line"><span class="token plain">Icon=myprogram</span> </span><span class="token-line"><span class="token plain">Exec=myprogram</span> </span><span class="token-line"><span class="token plain">StartupWMClass=myprogram</span> </span><span class="token-line"><span class="token plain">Terminal=false</span> </span><span class="token-line"><span class="token plain">Type=Application</span> </span><span class="token-line"><span class="token plain">Categories=Development</span> </span><span class="token-line"><span class="token plain">GenericName=MyProgram</span> </span><span class="token-line"><span class="token plain">Keywords=keyword1; keyword2; keyword3</span> </span> |
If your app is supposed to open files, append %F at the end of the Exec line, after myprogram; if it’s supposed to open URLs, then append %U.
5) Add hicolor icons (optional)
Hicolor icons follow a folder structure like below.
This blog post advises us to put icons on both hicolor and pixmaps directories, according to Debian Menu System docs and FreeDesktop docs.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<span class="token-line"><span class="token plain">├── icons</span> </span><span class="token-line"><span class="token plain">│ └── hicolor</span> </span><span class="token-line"><span class="token plain">│ ├── 128x128</span> </span><span class="token-line"><span class="token plain">│ │ └── apps</span> </span><span class="token-line"><span class="token plain">│ │ └── myprogram.png</span> </span><span class="token-line"><span class="token plain">│ ├── 16x16</span> </span><span class="token-line"><span class="token plain">│ │ └── apps</span> </span><span class="token-line"><span class="token plain">│ │ └── myprogram.png</span> </span><span class="token-line"><span class="token plain">│ ├── 256x256</span> </span><span class="token-line"><span class="token plain">│ │ └── apps</span> </span><span class="token-line"><span class="token plain">│ │ └── myprogram.png</span> </span><span class="token-line"><span class="token plain">│ ├── 32x32</span> </span><span class="token-line"><span class="token plain">│ │ └── apps</span> </span><span class="token-line"><span class="token plain">│ │ └── myprogram.png</span> </span><span class="token-line"><span class="token plain">│ ├── 48x48</span> </span><span class="token-line"><span class="token plain">│ │ └── apps</span> </span><span class="token-line"><span class="token plain">│ │ └── myprogram.png</span> </span><span class="token-line"><span class="token plain">│ ├── 512x512</span> </span><span class="token-line"><span class="token plain">│ │ └── apps</span> </span><span class="token-line"><span class="token plain">│ │ └── myprogram.png</span> </span><span class="token-line"><span class="token plain">│ ├── 64x64</span> </span><span class="token-line"><span class="token plain">│ │ └── apps</span> </span><span class="token-line"><span class="token plain">│ │ └── myprogram.png</span> </span><span class="token-line"><span class="token plain">│ └── scalable</span> </span><span class="token-line"><span class="token plain">│ └── apps</span> </span><span class="token-line"><span class="token plain">│ └── myprogram.svg</span> </span> |
6) Compile the .deb package
|
1 2 3 |
<span class="token-line"><span class="token comment"># for x64 architectures, the suggested suffix is amd64.</span> </span><span class="token-line"><span class="token plain">dpkg-deb --root-owner-group </span><span class="token parameter variable">--build</span><span class="token plain"> ./staging_folder/ </span><span class="token string">"./myprogram_</span><span class="token string variable">${versionName}</span><span class="token string">_amd64.deb"</span> </span> |
Example of a Linux shell script for the entire process
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
<span class="token-line"><span class="token shebang important">#!/bin/bash</span> </span><span class="token-line"> </span><span class="token-line"><span class="token comment"># Clean-up</span> </span><span class="token-line"><span class="token function">rm</span> <span class="token parameter variable">-rf</span><span class="token plain"> ./out/</span> </span><span class="token-line"><span class="token function">rm</span> <span class="token parameter variable">-rf</span><span class="token plain"> ./staging_folder/</span> </span><span class="token-line"> </span><span class="token-line"><span class="token comment"># .NET publish</span> </span><span class="token-line"><span class="token comment"># self-contained is recommended, so final users won't need to install .NET</span> </span><span class="token-line"><span class="token plain">dotnet publish </span><span class="token string">"./src/MyProgram.Desktop/MyProgram.Desktop.csproj"</span> <span class="token punctuation">\</span> </span><span class="token-line"> <span class="token parameter variable">--verbosity</span><span class="token plain"> quiet </span><span class="token punctuation">\</span> </span><span class="token-line"> <span class="token parameter variable">--nologo</span> <span class="token punctuation">\</span> </span><span class="token-line"> <span class="token parameter variable">--configuration</span><span class="token plain"> Release </span><span class="token punctuation">\</span> </span><span class="token-line"><span class="token plain"> --self-contained </span><span class="token boolean">true</span> <span class="token punctuation">\</span> </span><span class="token-line"> <span class="token parameter variable">--runtime</span><span class="token plain"> linux-x64 </span><span class="token punctuation">\</span> </span><span class="token-line"> <span class="token parameter variable">--output</span> <span class="token string">"./out/linux-x64"</span> </span><span class="token-line"> </span><span class="token-line"><span class="token comment"># Staging directory</span> </span><span class="token-line"><span class="token function">mkdir</span><span class="token plain"> staging_folder</span> </span><span class="token-line"> </span><span class="token-line"><span class="token comment"># Debian control file</span> </span><span class="token-line"><span class="token function">mkdir</span><span class="token plain"> ./staging_folder/DEBIAN</span> </span><span class="token-line"><span class="token function">cp</span><span class="token plain"> ./src/MyProgram.Desktop.Debian/control ./staging_folder/DEBIAN</span> </span><span class="token-line"> </span><span class="token-line"><span class="token comment"># Starter script</span> </span><span class="token-line"><span class="token function">mkdir</span><span class="token plain"> ./staging_folder/usr</span> </span><span class="token-line"><span class="token function">mkdir</span><span class="token plain"> ./staging_folder/usr/bin</span> </span><span class="token-line"><span class="token function">cp</span><span class="token plain"> ./src/MyProgram.Desktop.Debian/myprogram.sh ./staging_folder/usr/bin/myprogram</span> </span><span class="token-line"><span class="token function">chmod</span><span class="token plain"> +x ./staging_folder/usr/bin/myprogram </span><span class="token comment"># set executable permissions to starter script</span> </span><span class="token-line"> </span><span class="token-line"><span class="token comment"># Other files</span> </span><span class="token-line"><span class="token function">mkdir</span><span class="token plain"> ./staging_folder/usr/lib</span> </span><span class="token-line"><span class="token function">mkdir</span><span class="token plain"> ./staging_folder/usr/lib/myprogram</span> </span><span class="token-line"><span class="token function">cp</span> <span class="token parameter variable">-f</span> <span class="token parameter variable">-a</span><span class="token plain"> ./out/linux-x64/. ./staging_folder/usr/lib/myprogram/ </span><span class="token comment"># copies all files from publish dir</span> </span><span class="token-line"><span class="token function">chmod</span> <span class="token parameter variable">-R</span><span class="token plain"> a+rX ./staging_folder/usr/lib/myprogram/ </span><span class="token comment"># set read permissions to all files</span> </span><span class="token-line"><span class="token function">chmod</span><span class="token plain"> +x ./staging_folder/usr/lib/myprogram/myprogram_executable </span><span class="token comment"># set executable permissions to main executable</span> </span><span class="token-line"> </span><span class="token-line"><span class="token comment"># Desktop shortcut</span> </span><span class="token-line"><span class="token function">mkdir</span><span class="token plain"> ./staging_folder/usr/share</span> </span><span class="token-line"><span class="token function">mkdir</span><span class="token plain"> ./staging_folder/usr/share/applications</span> </span><span class="token-line"><span class="token function">cp</span><span class="token plain"> ./src/MyProgram.Desktop.Debian/MyProgram.desktop ./staging_folder/usr/share/applications/MyProgram.desktop</span> </span><span class="token-line"> </span><span class="token-line"><span class="token comment"># Desktop icon</span> </span><span class="token-line"><span class="token comment"># A 1024px x 1024px PNG, like VS Code uses for its icon</span> </span><span class="token-line"><span class="token function">mkdir</span><span class="token plain"> ./staging_folder/usr/share/pixmaps</span> </span><span class="token-line"><span class="token function">cp</span><span class="token plain"> ./src/MyProgram.Desktop.Debian/myprogram_icon_1024px.png ./staging_folder/usr/share/pixmaps/myprogram.png</span> </span><span class="token-line"> </span><span class="token-line"><span class="token comment"># Hicolor icons</span> </span><span class="token-line"><span class="token function">mkdir</span><span class="token plain"> ./staging_folder/usr/share/icons</span> </span><span class="token-line"><span class="token function">mkdir</span><span class="token plain"> ./staging_folder/usr/share/icons/hicolor</span> </span><span class="token-line"><span class="token function">mkdir</span><span class="token plain"> ./staging_folder/usr/share/icons/hicolor/scalable</span> </span><span class="token-line"><span class="token function">mkdir</span><span class="token plain"> ./staging_folder/usr/share/icons/hicolor/scalable/apps</span> </span><span class="token-line"><span class="token function">cp</span><span class="token plain"> ./misc/myprogram_logo.svg ./staging_folder/usr/share/icons/hicolor/scalable/apps/myprogram.svg</span> </span><span class="token-line"> </span><span class="token-line"><span class="token comment"># Make .deb file</span> </span><span class="token-line"><span class="token plain">dpkg-deb --root-owner-group </span><span class="token parameter variable">--build</span><span class="token plain"> ./staging_folder/ ./myprogram_3.1.0_amd64.deb</span> </span> |
To install
|
1 2 |
<span class="token-line"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span><span class="token plain"> ./myprogram_3.1.0_amd64.deb</span> </span> |
To uninstall / remove
|
1 |
<span class="token-line"><span class="token function">sudo</span> <span class="token function">apt</span><span class="token plain"> remove myprogram</span></span> |