|
| 1 | +--- |
| 2 | +id: debian-ubuntu |
| 3 | +title: Debian / Ubuntu packaging |
| 4 | +--- |
| 5 | + |
| 6 | +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. |
| 7 | + |
| 8 | +Debian and Ubuntu related distros have their applications packaged in `.deb` files, that can be installed via `sudo apt install ./your_package.deb`. |
| 9 | + |
| 10 | +## Tutorial |
| 11 | + |
| 12 | +In this tutorial, we will use the `dpkg-deb` tool to compile your `.deb` package. |
| 13 | + |
| 14 | +### 1) Organize program files in a staging folder |
| 15 | + |
| 16 | +Debian packages follow this basic structure: |
| 17 | + |
| 18 | +```sh |
| 19 | +./staging_folder/ |
| 20 | +├── DEBIAN |
| 21 | +│ └── control # package control file |
| 22 | +└── usr |
| 23 | + ├── bin |
| 24 | + │ └── myprogram # starter script |
| 25 | + ├── lib |
| 26 | + │ └── myprogram |
| 27 | + │ ├── libHarfBuzzSharp.so # Avalonia native library |
| 28 | + │ ├── libSkiaSharp.so # Avalonia native library |
| 29 | + │ ├── other_native_library_1.so |
| 30 | + │ ├── myprogram_executable # main executable |
| 31 | + │ ├── myprogram.dll |
| 32 | + │ ├── my_other_dll.dll |
| 33 | + │ ├── ... # all files from dotnet publish |
| 34 | + └── share |
| 35 | + ├── applications |
| 36 | + │ └── MyProgram.desktop # desktop shortcut file |
| 37 | + ├── icons |
| 38 | + │ └── hicolor |
| 39 | + │ ├── ... # other resolution icons (optional) |
| 40 | + └── pixmaps |
| 41 | + └── myprogram.png # main application icon |
| 42 | +``` |
| 43 | + |
| 44 | +Meaning of each folder: |
| 45 | + |
| 46 | +* `DEBIAN`: contains the `control` file. |
| 47 | +* `/usr/bin/`: contains the starter script (recommended for starting your program via command-line). |
| 48 | +* `/usr/lib/myprogram/`: where all files generated by `dotnet publish` go into. |
| 49 | +* `/usr/share/applications/`: folder for the desktop shortcut. |
| 50 | +* `/usr/share/pixmaps/` and `/usr/share/icons/hicolor/**`: folders for application icons. |
| 51 | + |
| 52 | +:::info |
| 53 | + |
| 54 | +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. |
| 55 | + |
| 56 | +::: |
| 57 | + |
| 58 | +### 2) Make the `control` file |
| 59 | + |
| 60 | +The `control` file goes inside the `DEBIAN` folder. |
| 61 | + |
| 62 | +This file describes general aspects of your program, such as its name, version, category, dependencies, maintainer, processor architecture and licenses. [Debian docs](https://www.debian.org/doc/debian-policy/ch-controlfields.html) have a more thorough description of all possible fields in the file. |
| 63 | + |
| 64 | +:::tip[Author's comment] |
| 65 | + |
| 66 | +Don't worry too much about filling all possible fields, most aren't required. This tutorial is to make a "good-enough" Debian package. |
| 67 | + |
| 68 | +::: |
| 69 | + |
| 70 | +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: ...*. Avalonia required dependencies are: `libx11-6, libice6, libsm6, libfontconfig1`. Overall, all .NET and Avalonia dependencies are required, plus any others specific of your app. |
| 71 | + |
| 72 | +Below is a simple example of a `control` file. |
| 73 | + |
| 74 | +``` |
| 75 | +Package: myprogram |
| 76 | +Version: 3.1.0 |
| 77 | +Section: devel |
| 78 | +Priority: optional |
| 79 | +Architecture: amd64 |
| 80 | +Installed-Size: 68279 |
| 81 | +Depends: libx11-6, libice6, libsm6, libfontconfig1, ca-certificates, tzdata, libc6, libgcc1, libgssapi-krb5-2, libstdc++6, zlib1g, libssl1.0.0 | libssl1.0.2 | libssl1.1 | libssl3, libicu | libicu72 | libicu71 | libicu70 | libicu69 | libicu68 | libicu67 | libicu66 | libicu65 | libicu63 | libicu60 | libicu57 | libicu55 | libicu52 |
| 82 | +Maintainer: Ken Lee <[email protected]> |
| 83 | +Homepage: https://github.com/kenlee/myprogram |
| 84 | +Description: This is MyProgram, great for doing X. |
| 85 | +Copyright: 2022-2024 Ken Lee <[email protected]> |
| 86 | +``` |
| 87 | + |
| 88 | +### 3) Make the starter script |
| 89 | + |
| 90 | +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. |
| 91 | + |
| 92 | +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. |
| 93 | + |
| 94 | +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`. |
| 95 | + |
| 96 | +Example of starter script: |
| 97 | + |
| 98 | +```sh |
| 99 | +#!/bin/bash |
| 100 | +# use exec to not have the wrapper script staying as a separate process |
| 101 | +# "$@" to pass command line arguments to the app |
| 102 | +exec /usr/lib/myprogram/myprogram_executable "$@" |
| 103 | +``` |
| 104 | + |
| 105 | +### 4) Make the desktop shortcut |
| 106 | + |
| 107 | +The desktop shortcut file follows the [freedesktop specification](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#recognized-keys). Arch Linux Wiki also has [good related information](https://wiki.archlinux.org/title/Desktop_entries). |
| 108 | + |
| 109 | +Below is an example of a desktop shortcut file. |
| 110 | + |
| 111 | +``` |
| 112 | +[Desktop Entry] |
| 113 | +Name=MyProgram |
| 114 | +Comment=MyProgram, great for doing X |
| 115 | +Icon=myprogram |
| 116 | +Exec=myprogram |
| 117 | +StartupWMClass=myprogram |
| 118 | +Terminal=false |
| 119 | +Type=Application |
| 120 | +Categories=Development |
| 121 | +GenericName=MyProgram |
| 122 | +Keywords=keyword1; keyword2; keyword3 |
| 123 | +``` |
| 124 | + |
| 125 | +:::tip |
| 126 | + |
| 127 | +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**. |
| 128 | + |
| 129 | +::: |
| 130 | + |
| 131 | +### 5) Add hicolor icons (optional) |
| 132 | + |
| 133 | +Hicolor icons follow a folder structure like below. |
| 134 | + |
| 135 | +[This blog post](https://martin.hoppenheit.info/blog/2016/where-to-put-application-icons-on-linux/) advises us to put icons on both `hicolor` and `pixmaps` directories, according to [Debian Menu System docs](https://www.debian.org/doc/packaging-manuals/menu.html/ch3.html#s3.7) and [FreeDesktop docs](https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-0.11.html#install_icons). |
| 136 | + |
| 137 | +``` |
| 138 | +├── icons |
| 139 | +│ └── hicolor |
| 140 | +│ ├── 128x128 |
| 141 | +│ │ └── apps |
| 142 | +│ │ └── myprogram.png |
| 143 | +│ ├── 16x16 |
| 144 | +│ │ └── apps |
| 145 | +│ │ └── myprogram.png |
| 146 | +│ ├── 256x256 |
| 147 | +│ │ └── apps |
| 148 | +│ │ └── myprogram.png |
| 149 | +│ ├── 32x32 |
| 150 | +│ │ └── apps |
| 151 | +│ │ └── myprogram.png |
| 152 | +│ ├── 48x48 |
| 153 | +│ │ └── apps |
| 154 | +│ │ └── myprogram.png |
| 155 | +│ ├── 512x512 |
| 156 | +│ │ └── apps |
| 157 | +│ │ └── myprogram.png |
| 158 | +│ ├── 64x64 |
| 159 | +│ │ └── apps |
| 160 | +│ │ └── myprogram.png |
| 161 | +│ └── scalable |
| 162 | +│ └── apps |
| 163 | +│ └── myprogram.svg |
| 164 | +``` |
| 165 | + |
| 166 | +### 6) Compile the `.deb` package |
| 167 | + |
| 168 | +```sh |
| 169 | +# for x64 architectures, the suggested suffix is amd64. |
| 170 | +dpkg-deb --root-owner-group --build ./staging_folder/ "./myprogram_${versionName}_amd64.deb" |
| 171 | +``` |
| 172 | + |
| 173 | +## Example of a Linux shell script for the entire process |
| 174 | + |
| 175 | +```bash |
| 176 | +#!/bin/bash |
| 177 | + |
| 178 | +# Clean-up |
| 179 | +rm -rf ./out/ |
| 180 | +rm -rf ./staging_folder/ |
| 181 | + |
| 182 | +# .NET publish |
| 183 | +# self-contained is recommended, so final users won't need to install .NET |
| 184 | +dotnet publish "./src/MyProgram.Desktop/MyProgram.Desktop.csproj" \ |
| 185 | + --verbosity quiet \ |
| 186 | + --nologo \ |
| 187 | + --configuration Release \ |
| 188 | + --self-contained true \ |
| 189 | + --runtime linux-x64 \ |
| 190 | + --output "./out/linux-x64" |
| 191 | + |
| 192 | +# Staging directory |
| 193 | +mkdir staging_folder |
| 194 | + |
| 195 | +# Debian control file |
| 196 | +mkdir ./staging_folder/DEBIAN |
| 197 | +cp ./src/MyProgram.Desktop.Debian/control ./staging_folder/DEBIAN |
| 198 | + |
| 199 | +# Starter script |
| 200 | +mkdir ./staging_folder/usr |
| 201 | +mkdir ./staging_folder/usr/bin |
| 202 | +cp ./src/MyProgram.Desktop.Debian/myprogram.sh ./staging_folder/usr/bin/myprogram |
| 203 | +chmod +x ./staging_folder/usr/bin/myprogram # set executable permissions to starter script |
| 204 | + |
| 205 | +# Other files |
| 206 | +mkdir ./staging_folder/usr/lib |
| 207 | +mkdir ./staging_folder/usr/lib/myprogram |
| 208 | +cp -f -a ./out/linux-x64/. ./staging_folder/usr/lib/myprogram/ # copies all files from publish dir |
| 209 | +chmod -R a+rX ./staging_folder/usr/lib/myprogram/ # set read permissions to all files |
| 210 | +chmod +x ./staging_folder/usr/lib/myprogram/myprogram_executable # set executable permissions to main executable |
| 211 | + |
| 212 | +# Desktop shortcut |
| 213 | +mkdir ./staging_folder/usr/share |
| 214 | +mkdir ./staging_folder/usr/share/applications |
| 215 | +cp ./src/MyProgram.Desktop.Debian/MyProgram.desktop ./staging_folder/usr/share/applications/MyProgram.desktop |
| 216 | + |
| 217 | +# Desktop icon |
| 218 | +# A 1024px x 1024px PNG, like VS Code uses for its icon |
| 219 | +mkdir ./staging_folder/usr/share/pixmaps |
| 220 | +cp ./src/MyProgram.Desktop.Debian/myprogram_icon_1024px.png ./staging_folder/usr/share/pixmaps/myprogram.png |
| 221 | + |
| 222 | +# Hicolor icons |
| 223 | +mkdir ./staging_folder/usr/share/icons |
| 224 | +mkdir ./staging_folder/usr/share/icons/hicolor |
| 225 | +mkdir ./staging_folder/usr/share/icons/hicolor/scalable |
| 226 | +mkdir ./staging_folder/usr/share/icons/hicolor/scalable/apps |
| 227 | +cp ./misc/myprogram_logo.svg ./staging_folder/usr/share/icons/hicolor/scalable/apps/myprogram.svg |
| 228 | + |
| 229 | +# Make .deb file |
| 230 | +dpkg-deb --root-owner-group --build ./staging_folder/ ./myprogram_3.1.0_amd64.deb |
| 231 | +``` |
| 232 | + |
| 233 | +## To install |
| 234 | + |
| 235 | +```sh |
| 236 | +sudo apt install ./myprogram_3.1.0_amd64.deb |
| 237 | +``` |
| 238 | + |
| 239 | +## To uninstall / remove |
| 240 | + |
| 241 | +```sh |
| 242 | +sudo apt remove myprogram |
| 243 | +``` |
| 244 | + |
| 245 | +## Third-party packaging tools for Debian / Ubuntu |
| 246 | + |
| 247 | +* https://github.com/quamotion/dotnet-packaging |
| 248 | +* https://github.com/SuperJMN/DotnetPackaging |
| 249 | +* https://github.com/kuiperzone/PupNet-Deploy |
0 commit comments