[.NET, C#] Nuke. Быстрый старт
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
После прочтения статьи "Как готовить Cake, используя только Frosting" мне пришла в голову мысль: "Какой большой проект для такого простого процесса сборки". После этого я и решил написать мини-статью про аналог Cake — Nuke.Если я правильно понимаю историю проекта, Nuke появился как более простой и удобный аналог Cake, Psake и Fake. Автору Nuke хотелось писать скрипты сборки именно на C#, поэтому ему не подошли Psake и Fake. Судя по всему, когда создавался Nuke, не существовал Frosting, и все Cake скрипты были странными файлами с DSL на основе C#, которые можно было писать и отлаживать только в VS Code. Скрипт сборки Nuke изначально задумывался как обычное консольное приложение, которое легко можно писать и отлаживать в любой IDE. Давайте создадим простейший скрипт сборки на Nuke и соберём с его помощью приложение!1. Создаём приложениеСоздадим новое приложение в папке src репозитория. Это не обязательно, но далее станет понятно, почему удобнее использовать для этого отдельную папку.
2. Устанавливаем Global Tool
dotnet tool install Nuke.GlobalTool --global
3. Создаём проект со скриптом сборкиДля этого необходимо выполнить команду:
nuke :setup
Это запустит консольный конфигуратор проекта сборки. Привожу анимацию его работы из документации Nuke.
Давайте посмотрим, что изменилось в репозитории:
Если не трогать настройки по умолчанию, то скрипт установки nuke создаст проект сборки в отдельной папке build репозитория. Таким образом, скрипты сборки лежат в build, а исходники в src. Те же настройки по умолчанию предполагают использование папки output в корне репозитория в качестве выходной директории для сборки.Разберём изменения по отдельности:
- .nuke — файл-маркер, по которому nuke определяет корневую папку
- build.* — бутстрапперы для запуска сборки. При необходимости могут и .net установить, если его нет на машине, на которой происходит сборка
- _build.csproj — файл проекта сборки. Имеет такое странное название для того, чтобы всегда быть первым в списке проектов в Solution Explorer
- .editorconfig и _build.csproj.DotSettings — просто настройки стиля
А теперь — самое интересное:3.1. Изменения в sln
Проект _build был добавлен в решение. Но был добавлен очень хитрым способом. Он отображается в IDE, его можно собрать, но если выполнить сборку всего решения, то мы увидим следующий вывод:
1>------ Rebuild All started: Project: Demo, Configuration: Debug Any CPU ------
2>------ Skipped Rebuild All: Project: _build, Configuration: Debug Any CPU ------
2>Project not selected to build for this solution configuration
1>Demo -> C:\Users\buldo\source\repos\nuke-example\src\Demo\Demo\bin\Debug\netcoreapp3.1\Demo.dll
========== Rebuild All: 1 succeeded, 0 failed, 1 skipped ==========
То есть при пересборке решения проект _build не собирается. Действительно, если бы скрипт сборки при работе вызывал ещё и пересборку самого себя, это могло бы привести к проблемам.3.2. Build.csПриведу полный код сгенерированного файла и объясню основные моменты.
[CheckBuildProjectConfigurations]
[ShutdownDotNetAfterServerBuild]
class Build : NukeBuild
{
public static int Main () => Execute<Build>(x => x.Compile);
[Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")]
readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release;
[Solution] readonly Solution Solution;
[GitRepository] readonly GitRepository GitRepository;
[GitVersion] readonly GitVersion GitVersion;
AbsolutePath SourceDirectory => RootDirectory / "src";
AbsolutePath OutputDirectory => RootDirectory / "output";
Target Clean => _ => _
.Before(Restore)
.Executes(() =>
{
SourceDirectory.GlobDirectories("**/bin", "**/obj").ForEach(DeleteDirectory);
EnsureCleanDirectory(OutputDirectory);
});
Target Restore => _ => _
.Executes(() =>
{
DotNetRestore(s => s
.SetProjectFile(Solution));
});
Target Compile => _ => _
.DependsOn(Restore)
.Executes(() =>
{
DotNetBuild(s => s
.SetProjectFile(Solution)
.SetConfiguration(Configuration)
.SetAssemblyVersion(GitVersion.AssemblySemVer)
.SetFileVersion(GitVersion.AssemblySemFileVer)
.SetInformationalVersion(GitVersion.InformationalVersion)
.EnableNoRestore());
});
}
Данный класс является точкой входа в приложения. В методе Main запускается сборка и выбирается цель сборки по умолчанию.Цели сборки представлены свойствами типа Target. Синтаксис с кучей стрелочек сначала пугает, но потом привыкаешь. Для целей сборки можно задавать зависимости (DependsOn()), явный порядок выполнения (Before(), After()), условия выполнения (как статические, так и динамические), а главное — указывать с помощью Executes(), что вообще делает этот Target.Следующая важная вещь для начинающего пользователя Nuke — параметры сборки. Достаточно просто пометить поле атрибутом [Parameter], и можно передавать значение этого поля через командную строку. В данном случае — конфигурацию сборки Debug или Release. Механизм очень гибок — можно сделать параметр необходимым, и без него сборка не запустится. Также параметр можно делать необходимым только для конкретного Target.Nuke предоставляет необходимые абстракции над решениями и проектами. Несколько решений в проекте? Нет проблем. Добавьте новое свойство типа Solution и пометьте его атрибутом [Solution] — Nuke найдёт решение, распарсит его и предоставит доступ его содержимому.Также в этом файле можно заметить пример расширения Nuke — модуль для работы с git установлен отдельным пакетом. С помощью полей GitRepository и GitVersion можно легко оперировать состоянием репозитория. Например, ориентируясь на имя ветки (master/не master), выбрать — ставить или нет preview метку при сборке nuget пакета.4. Запуск сборкиСборка запускается с помощью бутстраппера:
.\build.ps1 [targets] [arguments]
В данном случае для того, чтобы собрать приложение, необходимо выполнить следующую команду:
.\build.ps1 --target Compile --configuration release
На выходе получаем лог сборки, завершающийся удобной статистикой:
═══════════════════════════════════════
Target Status Duration
───────────────────────────────────────
Restore Executed 0:01
Compile Executed 0:03
───────────────────────────────────────
Total 0:04
═══════════════════════════════════════
Build succeeded on 29.12.2020 21:38:10.
На самом деле, если установлен NET 5, сборка скорее всего завершится ошибкой. Волшебный фикс — заменить атрибут поля GitVersion на [GitVersion(Framework = "netcoreapp3.1")].5. Делаем что-то полезноеПо самому первому скриншоту статьи можно было заметить, что был создан проект на Avalonia. Так давайте соберём наше приложение, да так, чтобы оно было в виде одного исполняемого файла, включающего рантайм. Это значит, что придётся собирать несколько исполняемых файлов.Далее я приведу код цели сборки с подробными комментариями:
Target Publish => _ => _
.Executes(() =>
{
var rids = new[] {"win-x64", "linux-x64"}; // Перечисляем RID'ы, для которых собираем приложение
DotNetPublish(s => s // Теперь вызываем dotnet publish
.SetAssemblyVersion(GitVersion.AssemblySemVer)
.SetFileVersion(GitVersion.AssemblySemFileVer)
.SetInformationalVersion(GitVersion.InformationalVersion)
.SetProject(Solution.GetProject("Demo")) // Для dotnet publish желательно указывать проект
.SetPublishSingleFile(true) // Собираем в один файл
.SetSelfContained(true) // Вместе с рантаймом
.SetConfiguration(Configuration) // Для определённой конфигурации
.CombineWith(rids, (s, rid) => s // Но нам нужны разные комбинации параметров
.SetRuntime(rid) // Устанавливаем RID
.SetOutput(OutputDirectory/rid))); // Делаем так, чтобы сборки с разными RID попали в разные папки
});
ЗаключениеNuke — удобная система сборки. Она проста и минималистична в начале вашего проекта, но достаточно мощна для того, чтобы развиваться вместе с ним. Ссылки
===========
Источник:
habr.com
===========
Похожие новости:
- [Open source, Git, Системы управления версиями, Системы сборки, DevOps] Вышел релиз GitLab 13.7 с проверяющими для мерж-реквестов и автоматическим откатом при сбое
- [Программирование, C#] Модифицируем паттерн Filter с помощью обобщенных лямбда-выражений (перевод)
- [.NET, C#, DevOps] Как установить файл конфигурации в .Net Core Console app для нескольких сред разработки при запуске Docker-контейнера
- [.NET] Когда программисту нечего делать или оптимизируем код при помощи Linq.Expression
- [Разработка игр, C#, Unity, Дизайн игр] Гравитационная комната в Unity 3D
- [Программирование, C#, Графический дизайн] Создание Dockers в Corel Draw
- [.NET, Системы сборки] xUnit тестирование в TeamCity
- [Программирование, .NET, Amazon Web Services, C#, DevOps] Nuke: настраиваем сборку и публикацию .NET-проекта
- [.NET, Разработка игр, Unity, CGI (графика), AR и VR] Поговорим про градиенты в Unity
- [Программирование, C#] Поиск, устранение и предупреждение утечек памяти в C# .NET: 8 лучших практик (перевод)
Теги для поиска: #_.net, #_c#, #_.net, #_nuke, #_ci/cd, #_c#, #_.net, #_c#
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 12:11
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
После прочтения статьи "Как готовить Cake, используя только Frosting" мне пришла в голову мысль: "Какой большой проект для такого простого процесса сборки". После этого я и решил написать мини-статью про аналог Cake — Nuke.Если я правильно понимаю историю проекта, Nuke появился как более простой и удобный аналог Cake, Psake и Fake. Автору Nuke хотелось писать скрипты сборки именно на C#, поэтому ему не подошли Psake и Fake. Судя по всему, когда создавался Nuke, не существовал Frosting, и все Cake скрипты были странными файлами с DSL на основе C#, которые можно было писать и отлаживать только в VS Code. Скрипт сборки Nuke изначально задумывался как обычное консольное приложение, которое легко можно писать и отлаживать в любой IDE. Давайте создадим простейший скрипт сборки на Nuke и соберём с его помощью приложение!1. Создаём приложениеСоздадим новое приложение в папке src репозитория. Это не обязательно, но далее станет понятно, почему удобнее использовать для этого отдельную папку. 2. Устанавливаем Global Tool dotnet tool install Nuke.GlobalTool --global
nuke :setup
Давайте посмотрим, что изменилось в репозитории: Если не трогать настройки по умолчанию, то скрипт установки nuke создаст проект сборки в отдельной папке build репозитория. Таким образом, скрипты сборки лежат в build, а исходники в src. Те же настройки по умолчанию предполагают использование папки output в корне репозитория в качестве выходной директории для сборки.Разберём изменения по отдельности:
Проект _build был добавлен в решение. Но был добавлен очень хитрым способом. Он отображается в IDE, его можно собрать, но если выполнить сборку всего решения, то мы увидим следующий вывод: 1>------ Rebuild All started: Project: Demo, Configuration: Debug Any CPU ------
2>------ Skipped Rebuild All: Project: _build, Configuration: Debug Any CPU ------ 2>Project not selected to build for this solution configuration 1>Demo -> C:\Users\buldo\source\repos\nuke-example\src\Demo\Demo\bin\Debug\netcoreapp3.1\Demo.dll ========== Rebuild All: 1 succeeded, 0 failed, 1 skipped ========== [CheckBuildProjectConfigurations]
[ShutdownDotNetAfterServerBuild] class Build : NukeBuild { public static int Main () => Execute<Build>(x => x.Compile); [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release; [Solution] readonly Solution Solution; [GitRepository] readonly GitRepository GitRepository; [GitVersion] readonly GitVersion GitVersion; AbsolutePath SourceDirectory => RootDirectory / "src"; AbsolutePath OutputDirectory => RootDirectory / "output"; Target Clean => _ => _ .Before(Restore) .Executes(() => { SourceDirectory.GlobDirectories("**/bin", "**/obj").ForEach(DeleteDirectory); EnsureCleanDirectory(OutputDirectory); }); Target Restore => _ => _ .Executes(() => { DotNetRestore(s => s .SetProjectFile(Solution)); }); Target Compile => _ => _ .DependsOn(Restore) .Executes(() => { DotNetBuild(s => s .SetProjectFile(Solution) .SetConfiguration(Configuration) .SetAssemblyVersion(GitVersion.AssemblySemVer) .SetFileVersion(GitVersion.AssemblySemFileVer) .SetInformationalVersion(GitVersion.InformationalVersion) .EnableNoRestore()); }); } .\build.ps1 [targets] [arguments]
.\build.ps1 --target Compile --configuration release
═══════════════════════════════════════
Target Status Duration ─────────────────────────────────────── Restore Executed 0:01 Compile Executed 0:03 ─────────────────────────────────────── Total 0:04 ═══════════════════════════════════════ Build succeeded on 29.12.2020 21:38:10. Target Publish => _ => _
.Executes(() => { var rids = new[] {"win-x64", "linux-x64"}; // Перечисляем RID'ы, для которых собираем приложение DotNetPublish(s => s // Теперь вызываем dotnet publish .SetAssemblyVersion(GitVersion.AssemblySemVer) .SetFileVersion(GitVersion.AssemblySemFileVer) .SetInformationalVersion(GitVersion.InformationalVersion) .SetProject(Solution.GetProject("Demo")) // Для dotnet publish желательно указывать проект .SetPublishSingleFile(true) // Собираем в один файл .SetSelfContained(true) // Вместе с рантаймом .SetConfiguration(Configuration) // Для определённой конфигурации .CombineWith(rids, (s, rid) => s // Но нам нужны разные комбинации параметров .SetRuntime(rid) // Устанавливаем RID .SetOutput(OutputDirectory/rid))); // Делаем так, чтобы сборки с разными RID попали в разные папки }); =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 12:11
Часовой пояс: UTC + 5