Создание собственного типа сборки (Build Action) в Visual Studio 2013
Visual Studio и MSBuild имеют болльше возможности для расширения. Одной из таких возможностей является возможность определения каким именно способом будет обрабатываться конкретный файл в проекте. Здесь я покажу каким образом можно создавать собственные типы сборки и для чего это может быть нужно.
Я не буду пояснять синтаксис MSBuild, поэтому если вы не знакомы с ним, то перед прочтением предлагаю ознакомиться.
Разрабатывая приложения в Visual Studio, наверное, каждый сталкивался с параметром Build Action
в окне свойств файла.

В зависимости от этого параметра, MSBuild корректирует своё поведение относительно того, что именно нужно сделать с конкретным файлом. Например, мы можем пометить файл как ресурс или указать, что изображение будет экраном приветствия.
Представленные типы могут изменяться в зависимости от типа проекта. Например, типы сборки для консольного приложения могут отличаться от таковых для приложения ASP.NET.
При разработке своих приложений может также потребоваться добавить новый тип сборки и дополнить его некоторым поведением. Это может быть полезно в случаях, когда нам нужно выполнить какой-либо пре- или пост-процессинг. Например, для web-приложений это может быть процессинг LESS/SASS или сборка JS-файлов при помощи GruntJS.
Существует множество других способ сделать то же самое. Например, использовать pre- и post-build events, которые можно задать в свойствах проекта. Однако, приведенное решение по моему мнению гораздо удобнее использовать повторно и переносить на новые проекты без боли (например, через Nuget).
Создаем новый тип сборки
Чтобы создать новый Build Type необходимо открыть файл проекта (.csproj
и др.) и добавить секцию AvailableItemName
. Предположим, мы будем создавать новый тип сборки для компиляции LESS, тогда это может выглядеть так:
<ItemGroup>
<AvailableItemName Include="LessBuild" />
</ItemGroup>
Добавление этого кода в файл проекта достаточно для того, чтобы новый тип сборки появился в списке.

Если мы выберем какой-либо файл в проекте и зададим соответствующий тип сборки, то определение этого файла в проекте изменится следующим образом:
<ItemGroup>
<LessBuild Include="StyleSheet1.less" />
</ItemGroup>
Если мы попробуем собрать проект с новым типом сборки, то проект успешно соберется, но с ресурсом, для которого этот тип сборки задан, ничего не произойдет. Для этого нам нужно сделать ещё кое-что.
Задаем поведение для нового типа сборки
При сборке ресурса нам может потребоваться запускать какую-то стороннюю утилиту или задачу MSBuild. Обработка нового типа сборки сводится к созданию и запуску нового Target
в проекте MSBuild.
<Target Name="LessBuildTarget" AfterTargets="AfterBuild" Condition="'@(LessBuild)' != ''">
</Target>
Рассмотрим подробнее как это работает.
Target
запускается каждый раз после сборки проекта из-за параметраAfterTargets="AfterBuild"
. Если вам необходимо что-то запустить перед сборкой проекта, то используйте вместо этогоBeforeTargets="BeforeBuild"
.- Если в проекте присутствуют файлы, для которых задан соответствующий тип сборки, то переменная
@(LessBuild)
будет содержать их имена. ПараметрCondition
предотвращает запуск скрипта, если в проекте нет ни одного файла с соответствующим типом сборки.
После того, как входная точка создана, остается обработать соответствующие файлы. Переменная @(LessBuild)
в этом случае является строкой, в которой перечислены все файлы проекта с соответствующим типом сборки, перечисленные через точку с запятой. Получим из этой строки список файлов:
<Target Name="LessBuildTarget" AfterTargets="AfterBuild" Condition="'@(LessBuild)' != ''">
<ItemGroup>
<_FilesToProcess Include="@(LessBuild)"/>
</ItemGroup>
</Target>
И теперь нам остается только обработать эти файлы, запустив какую-то внешнюю утилиту, задачу MSBuild или как-то ещё.
<Target Name="LessBuildTarget" AfterTargets="AfterBuild" Condition="'@(LessBuild)' != ''">
<ItemGroup>
<_FilesToProcess Include="@(LessBuild)"/>
</ItemGroup>
<Exec Command="lesscompile.exe %(_FilesToProcess.FullPath)" />
</Target>
Разумеется, этот кусок скрипта следует переписать несколько иначе, исходя из текущих потребностей (например, обрабатывая это в несколько шагов). В данном случае я намеренно не хочу усложнять, чтобы идея осталась понятной.
В итоге, мы имеем достаточно универсальный инструмент, который позволяет интегрировать сборку наших приложений с различными внешними инструментами и утилитами. Надеюсь, это сделает вашу жизнь проще.
Добавить комментарий