CLI приложения #22. От проекта к готовому приложению
В предыдущих постах мы научили наше CLI-приложение многому: принимать команды, работать с файлами, выводить таблицы, показывать прогресс и даже корректно завершаться по Ctrl+C.
Но есть один важный нюанс: до сих пор мы запускали приложение через dotnet run. То есть, чтобы воспользоваться программой, нам нужны исходники, установленный SDK и терминал, открытый в папке проекта.
Для разработки это нормально. А вот готовый инструмент нужно распространять в собранном виде. Мы же не собираем, например, docker cli из исходников?
Начнём с публикации.
В .NET для этого есть команда dotnet publish. Она собирает приложение и подготавливает файлы, необходимые для его запуска.
Из папки проекта выполняем:
dotnet publish -c Release -o ./publish
Здесь:
-c Release — сборка с оптимизациями, не Debug;
-o ./publish — папка, куда попадёт результат.
Внутри появятся исполняемый файл, DLL и необходимые зависимости. Если проект называется MyApp, то на Windows получим MyApp.exe.
Уже можно запустить:
./publish/MyApp.exe --help
И никакого dotnet run!
Важно!
По умолчанию такая сборка зависит от установленного на компьютере .NET Runtime подходящей версии. SDK уже не нужен, а вот среда выполнения нужна. Это называется framework-dependent deployment. А если мы хотим отправить приложение человеку, у которого .NET вообще не установлен или установлена старая или несовместимая версия?
Решение есть: Self-contained — всё необходимое с собой
Соберём приложение вместе со средой выполнения:
dotnet publish -c Release -r win-x64 --self-contained true -o ./publish/win-x64
Параметр -r win-x64 указывает, для какой системы собираем приложение.
Например:
win-x64 — Windows x64;
linux-x64 — Linux x64;
osx-arm64 — macOS на Apple Silicon.
Теперь приложение можно запускать без установленного .NET Runtime. Правда, размер сборки заметно увеличится: вместе с нашим кодом придётся доставлять и саму среду выполнения. И файлов в папке будет немало. А нельзя ли всё это собрать в один файл? Можно, и для этого существует Single-file — один исполняемый файл:
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o ./publish/single
В результате основное приложение и его управляемые зависимости будут упакованы в один .exe.
В зависимости от проекта рядом могут остаться дополнительные файлы, например символы отладки или некоторые нативные зависимости. Но в простом CLI вполне реально получить один исполняемый файл, который достаточно скопировать и запустить.
Красота!
Только не забывайте: сборка для Windows не запустится на Linux. Для каждой ОС и архитектуры потребуется свой вариант.
А как запускать приложение из любой папки?
Пока что приходится указывать путь:
C:\Tools\MyApp.exe --help
Чтобы вызывать его просто по имени:
myapp --help
нужно добавить папку с исполняемым файлом в переменную окружения PATH.
Windows и другие ОС используют её для поиска программ, которые мы запускаем из терминала без указания полного пути. После добавления папки в PATH и открытия нового терминала команда станет доступна откуда угодно. Вот теперь наше приложение уже больше похоже на привычные git, docker и прочие консольные инструменты.
Итого.
Мы получили три варианта публикации:
- Обычная сборка — компактнее, но требует установленного .NET Runtime.
-
Self-contained— работает без отдельно установленного Runtime, но занимает больше места. -
Single-file— позволяет упаковать приложение в один исполняемый файл. При этом может быть как self-contained, так и зависимым отRuntime.
Что выбрать? Зависит от задачи. Для собственного компьютера может хватить обычной сборки, а для распространения среди пользователей удобнее self-contained single-file.
Но есть ещё один интересный вариант, который мы сегодня не рассмотрели.
.NET позволяет распространять CLI-приложения как инструменты, устанавливаемые одной командой:
dotnet tool install --global <имя-пакета>
Без ручного копирования файлов и настройки PATH для каждого приложения. А ещё такие инструменты можно обновлять и удалять штатными командами.
В следующей части попробуем упаковать наше приложение как .NET tool и установить его из локального NuGet-пакета. А затем разберём, как сделать его доступным другим разработчикам. Ну и отдельно поговорим про Native AOT — возможность собрать .NET-приложение непосредственно в машинный код, без JIT-компиляции при запуске.