Створення додатку — це лише половина справи. Щоб ваш додаток потрапив до користувачів, його потрібно правильно зібрати, запакувати та розповсюдити. Кожна платформа має свої конвенції та очікування щодо формату розповсюдження: Windows користувачі очікують MSI або MSIX інсталятори, macOS користувачі звикли до DMG образів з .app bundles, а Linux користувачі цінують AppImage, Flatpak або пакети для своїх дистрибутивів.
У цій статті ми детально розглянемо весь процес підготовки Avalonia-додатку до розповсюдження: від базової збірки через dotnet publish до створення platform-specific інсталяторів, від вибору між self-contained та framework-dependent deployment до оптимізації розміру через trimming та AOT compilation. Ми також обговоримо автоматичні оновлення, code signing для різних платформ, та налаштування CI/CD pipeline для автоматичної збірки на всіх підтримуваних платформах.
Правильне пакування та розгортання — це не просто технічна задача, а важлива частина user experience. Користувач повинен мати можливість легко встановити ваш додаток, отримувати автоматичні оновлення, та бути впевненим у безпеці (через code signing). Давайте розберемося, як це зробити правильно для кожної платформи.
Основний інструмент для збірки .NET додатків — команда dotnet publish. Вона компілює додаток, збирає всі залежності та готує його до розповсюдження.
dotnet publish -c Release
Ця команда:
bin/Release/net8.0/publish/Проте для production deployment потрібно більше налаштувань.
Щоб зібрати додаток для конкретної платформи, використовуйте параметр -r (runtime):
# Windows 64-bit
dotnet publish -c Release -r win-x64
# Windows ARM64 (Surface Pro X, Windows on ARM)
dotnet publish -c Release -r win-arm64
# Linux 64-bit
dotnet publish -c Release -r linux-x64
# Linux ARM64 (Raspberry Pi 4, ARM servers)
dotnet publish -c Release -r linux-arm64
# macOS Intel (x64)
dotnet publish -c Release -r osx-x64
# macOS Apple Silicon (M1/M2/M3)
dotnet publish -c Release -r osx-arm64
Повний список RID доступний у .NET RID Catalog.
dotnet --info
RID: osx-arm64
win-x64 (обов'язково), win-arm64 (опціонально)linux-x64 (обов'язково), linux-arm64 (для ARM серверів/Raspberry Pi)osx-x64 та osx-arm64 (обидва обов'язкові для підтримки Intel та Apple Silicon)За замовчуванням dotnet publish створює output у bin/Release/net8.0/{RID}/publish/. Для зручності можна вказати власну папку:
dotnet publish -c Release -r win-x64 -o ./dist/windows
dotnet publish -c Release -r linux-x64 -o ./dist/linux
dotnet publish -c Release -r osx-arm64 -o ./dist/macos
Це зручно для CI/CD pipelines та для організації artifacts.
Один з найважливіших виборів при deployment — це тип розгортання: self-contained або framework-dependent.
Що це: Додаток вимагає встановленого .NET Runtime на машині користувача.
Переваги:
Недоліки:
Збірка:
dotnet publish -c Release -r win-x64 --self-contained false
Або у .csproj:
<PropertyGroup>
<SelfContained>false</SelfContained>
</PropertyGroup>
Розмір: ~3-10 MB (залежно від кількості залежностей)
Що це: Додаток включає .NET Runtime, не вимагає встановленого runtime на машині користувача.
Переваги:
Недоліки:
Збірка:
dotnet publish -c Release -r win-x64 --self-contained true
Або у .csproj:
<PropertyGroup>
<SelfContained>true</SelfContained>
</PropertyGroup>
Розмір: ~60-100 MB (залежно від платформи та trimming)
| Характеристика | Framework-dependent | Self-contained |
|---|---|---|
| Розмір | 3-10 MB | 60-100 MB |
| Вимоги | .NET Runtime встановлений | Немає |
| Складність установки | Середня (потрібен runtime) | Низька (просто запустити) |
| Security updates | Автоматичні (через runtime) | Вручну (rebuild додатку) |
| Контроль версії | Низький | Високий |
| Цільова аудиторія | Розробники, IT professionals | Звичайні користувачі |
Framework-dependent — якщо:
Self-contained — якщо:
Рекомендація: Для більшості desktop-додатків рекомендується self-contained deployment. Користувачі очікують, що додаток "просто працює" без додаткових кроків.
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true
Self-contained deployment включає весь .NET Runtime, навіть якщо ваш додаток використовує тільки малу його частину. Trimming дозволяє видалити невикористаний код та зменшити розмір.
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
</PropertyGroup>
Або через CLI:
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishTrimmed=true
Trimmer аналізує ваш код та визначає, які частини .NET Runtime та бібліотек використовуються. Невикористаний код видаляється з output.
Приклад:
link (агресивний):
<TrimMode>link</TrimMode>
Видаляє невикористані типи та методи з усіх assemblies (включаючи ваш код та NuGet пакети).
copyused (консервативний):
<TrimMode>copyused</TrimMode>
Видаляє тільки повністю невикористані assemblies, але не видаляє код всередині assemblies.
Рекомендація: Використовуйте link для максимальної економії, але ретельно тестуйте додаток.
Trimming може зламати додаток, якщо код використовує reflection, dynamic loading або інші runtime features:
// Цей код може зламатися після trimming
var type = Type.GetType("MyNamespace.MyClass");
var instance = Activator.CreateInstance(type);
Trimmer не може визначити, що MyClass використовується, і може видалити його.
Рішення 1: Trim warnings
Увімкніть trim warnings:
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<TrimmerSingleWarn>false</TrimmerSingleWarn>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
</PropertyGroup>
Компілятор покаже warnings для потенційно проблемного коду.
Рішення 2: DynamicallyAccessedMembers attribute
Вкажіть, які члени типу використовуються через reflection:
public void CreateInstance(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
Type type)
{
var instance = Activator.CreateInstance(type);
}
Рішення 3: Trim root assemblies
Вкажіть assemblies, які не повинні триматися:
<ItemGroup>
<TrimmerRootAssembly Include="MyApp.Core" />
</ItemGroup>
Avalonia підтримує trimming, але потрібні додаткові налаштування:
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
<!-- Avalonia-specific -->
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<!-- Не тримати Avalonia assemblies -->
<TrimmableAssembly Include="Avalonia.Base" />
<TrimmableAssembly Include="Avalonia.Controls" />
<TrimmableAssembly Include="Avalonia.Markup.Xaml" />
</ItemGroup>
Важливо: Використовуйте Compiled Bindings замість Reflection Bindings для сумісності з trimming.
dotnet publish -p:PublishTrimmed=truedotnet publish -p:TrimmerSingleWarn=falseNativeAOT (Ahead-of-Time compilation) компілює .NET код у нативний машинний код до запуску. Це дає кілька переваг:
Переваги:
Недоліки:
<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>
dotnet publish -c Release -r win-x64 -p:PublishAot=true
На момент написання статті (2024) Avalonia має експериментальну підтримку NativeAOT. Основні проблеми:
Рекомендація: Для production використовуйте trimming замість NativeAOT. NativeAOT для Avalonia ще не готовий для широкого використання.
Слідкуйте за Avalonia GitHub для оновлень щодо NativeAOT підтримки.
Кросплатформна розробка з Avalonia
Створення додатків, що працюють на Windows, Linux, macOS, Mobile та WebAssembly: структура проєкту, platform-specific код, native API та deployment
Розгортання WPF застосунків
Підготовка WPF-додатку для розповсюдження на Windows: ClickOnce, MSIX, MSI інсталятори, Squirrel для auto-updates, code signing та best practices