Цель статьи — рассказать немного о MSBuild, показать что такое таргеты и таски в MSBuild, научить работать с файлом .csproj, дать полезные ссылки. Если у вас будет более подходящее название для статьи, то буду рад обсудить в комментариях.
MSBuild устроен таким образом, что сборка проекта разбита на несколько этапов.
Target — это некоторый этап (событие), происходящее во время сборки проекта. Можно использовать стандартные таргеты, либо определять собственные.
Task — это некоторая задача, которая может выполняться на определенном этапе. Можно использовать стандартные таски или создавать собственные.
Цитата из документации о таргетах (https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-targets):
Для работы MSBuild Microsoft определил ряд стандартных таргетов (в файлах Microsoft.Common.targets, Microsoft.CSharp.targets и т.д.). Определено огромное множество различных таргетов, но в данной статье мы не будем на этом подробно останавливаться. Некоторые стандартные таргеты (упорядочены):
Таргеты BeforeBuild и AfterBuild специально созданы для переопределения и их можно использовать. Остальные таргеты из списка не рекомендую использовать, чтобы ничего не сломалось.
Для более подробного просмотра списка таргетов можно использовать параметр /pp:. Благодаря этому параметру будет сформирован файл, в который будут включены все импорты (включая файлы .targets). В нем можно найти множество таргетов и переменных (спасибо aikixd за подсказку).
Для примеров необходимо:
Во всех примерах этой статьи понадобится редактировать файл MSBuildExample.csproj. Каждый пример подразумевает удаление кода предыдущего примера и добавление нового. Код необходимо добавлять в конец файла .csproj до последней строчки, содержащей закрывающий тег Project.
![image](https://habrastorage.org/r/w1560/webt/v0/3p/lk/v03plkzfc9yzxfeob1o0qo3e9as.png)
Внимание! В файле .csproj регистр букв важен.
Для запуска примера необходимо запускать build в среде разработки Visual Studio. Для некоторых примеров потребуется выбирать solution конфигурацию.
![image](https://habrastorage.org/r/w1560/webt/a0/t9/pp/a0t9pp5lfae_svzlzfhxyzs811i.png)
Результат будет выводиться в окно Output в Visual Studio (внизу). Если его нет, то откройте его через пункты меню View => Output.
![image](https://habrastorage.org/r/w1560/webt/s3/38/gs/s338gsmzlabresjzg_xtn7cewfg.png)
Для примеров будем использовать таск Message, который будет выводить информацию в окно Output в Visual Studio. Как говорилось ранее есть стандартные таргеты BeforeBuild и AfterBuild, воспользуемся ими. Про подготовку читать в разделе Подготовка окружения для примеров.
Как видно, был выполнен task Message, который вывел указанный нами текст в момент BeforeBuild и AfterBuild в окно Output в Visual Studio.
При определении таргета с одним и тем же именем он перезаписывается!
Вывело только второе сообщение, потому что использовали таргеты с одним именем и он был перезаписан вторым значением.
Если таргетов BeforeBuild и AfterBuild недостаточно или нужно, чтобы таски выполнялись на другом этапе жизненного цикла сборки, то можно определить собственный таргет. Для этих целей есть параметры BeforeTargets и AfterTargets.
Было определено два собственных таргета — MyCustomBeforeTarget и MyCustomAfterTarget.
Таргет MyCustomBeforeTarget выполняется до таргета BeforeBuild, потому что мы указали:
Таргет MyCustomAfterTarget выполняется после таргета BeforeBuild, потому что мы указали:
В данной статье не рассматривается как можно писать собственные таски, но прежде чем писать таск, ознакомьтесь со списком тасков, предоставляемых Microsoft.
Рассмотрим несколько примеров использования тасков и макросов.
В файле .csproj можно использовать ряд стандартных макросов, их список можно найти здесь https://msdn.microsoft.com/en-us/library/c02as0cs.aspx и здесь https://msdn.microsoft.com/en-us/library/bb629394.aspx. Рассмотрим некоторые полезные макросы:
Для определения собственных параметров использовать PropertyGroup . Пример определения собственной переменной можно найти в разделе таски в MSBuild.
Меню
- Основные понятия — что такое target и task в MSBuild
- Жизненный цикл сборки MSBuild — какая последовательность вызова таргетов
- Подготовка окружения для примеров
- Таргеты в MSBuild — пример использования некоторых таргетов MSBuild
- Создание собственного таргета MSBuild
- Таски в MSBuild — пример использования некоторых тасков MSBuild
- Переменные и макросы в .csproj
- Ссылки
Основные понятия (Меню)
MSBuild устроен таким образом, что сборка проекта разбита на несколько этапов.
Target — это некоторый этап (событие), происходящее во время сборки проекта. Можно использовать стандартные таргеты, либо определять собственные.
Task — это некоторая задача, которая может выполняться на определенном этапе. Можно использовать стандартные таски или создавать собственные.
Цитата из документации о таргетах (https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-targets):
Targets group tasks together in a particular order and allow the build process to be factored into smaller units.
For example, one target may delete all files in the output directory to prepare for the build, while another
compiles the inputs for the project and places them in the empty directory.
Жизненный цикл сборки MSBuild (Меню)
Для работы MSBuild Microsoft определил ряд стандартных таргетов (в файлах Microsoft.Common.targets, Microsoft.CSharp.targets и т.д.). Определено огромное множество различных таргетов, но в данной статье мы не будем на этом подробно останавливаться. Некоторые стандартные таргеты (упорядочены):
Список таргетов (спойлер)
- BeforeRebuild
- Clean
- BeforeBuild
- BuildOnlySettings
- PrepareForBuild
- PreBuildEvent
- ResolveReferences
- PrepareResources
- ResolveKeySource
- Compile
- UnmanagedUnregistration
- GenerateSerializationAssemblies
- CreateSatelliteAssemblies
- GenerateManifests
- GetTargetPath
- PrepareForRun
- UnmanagedRegistration
- IncrementalClean
- PostBuildEvent
- AfterBuild
- AfterRebuild
Таргеты BeforeBuild и AfterBuild специально созданы для переопределения и их можно использовать. Остальные таргеты из списка не рекомендую использовать, чтобы ничего не сломалось.
Для более подробного просмотра списка таргетов можно использовать параметр /pp:. Благодаря этому параметру будет сформирован файл, в который будут включены все импорты (включая файлы .targets). В нем можно найти множество таргетов и переменных (спасибо aikixd за подсказку).
Подготовка окружения для примеров (Меню)
Для примеров необходимо:
- Установленная среда разработки Visual Studio
- Создать проект типа Console Application с именем MSBuildExample
- Открыть папку проекта и найти там файл MSBuildExample.csproj
- Открыть файл MSBuildExample.csproj в блокноте или другом редакторе
Во всех примерах этой статьи понадобится редактировать файл MSBuildExample.csproj. Каждый пример подразумевает удаление кода предыдущего примера и добавление нового. Код необходимо добавлять в конец файла .csproj до последней строчки, содержащей закрывающий тег Project.
![image](https://habrastorage.org/webt/v0/3p/lk/v03plkzfc9yzxfeob1o0qo3e9as.png)
Внимание! В файле .csproj регистр букв важен.
Для запуска примера необходимо запускать build в среде разработки Visual Studio. Для некоторых примеров потребуется выбирать solution конфигурацию.
![image](https://habrastorage.org/webt/a0/t9/pp/a0t9pp5lfae_svzlzfhxyzs811i.png)
Результат будет выводиться в окно Output в Visual Studio (внизу). Если его нет, то откройте его через пункты меню View => Output.
![image](https://habrastorage.org/webt/s3/38/gs/s338gsmzlabresjzg_xtn7cewfg.png)
Таргеты в MSBuild (Меню)
Для примеров будем использовать таск Message, который будет выводить информацию в окно Output в Visual Studio. Как говорилось ранее есть стандартные таргеты BeforeBuild и AfterBuild, воспользуемся ими. Про подготовку читать в разделе Подготовка окружения для примеров.
Пример использования таргетов (спойлер)
Код примера:
Результат выполнения (лишнее исключено):
<Target Name="AfterBuild">
<Message Text="AfterBuild event" Importance="high"></Message>
</Target>
<Target Name="BeforeBuild">
<Message Text="BeforeBuild event" Importance="high"></Message>
</Target>
Результат выполнения (лишнее исключено):
…
BeforeBuild event
…
AfterBuild event
…
Как видно, был выполнен task Message, который вывел указанный нами текст в момент BeforeBuild и AfterBuild в окно Output в Visual Studio.
При определении таргета с одним и тем же именем он перезаписывается!
Пример перезаписи таргета (спойлер)
Код примера:
Результат выполнения (лишнее исключено):
<Target Name="BeforeBuild">
<Message Text="First message" Importance="high"></Message>
</Target>
<Target Name="BeforeBuild">
<Message Text="Second message" Importance="high"></Message>
</Target>
Результат выполнения (лишнее исключено):
…
Second message
…
Вывело только второе сообщение, потому что использовали таргеты с одним именем и он был перезаписан вторым значением.
Создание собственного таргета MSBuild (Меню)
Если таргетов BeforeBuild и AfterBuild недостаточно или нужно, чтобы таски выполнялись на другом этапе жизненного цикла сборки, то можно определить собственный таргет. Для этих целей есть параметры BeforeTargets и AfterTargets.
Пример определения собственных таргетов (спойлер)
Код примера:
Результат выполнения (лишнее исключено):
<Target Name="BeforeBuild">
<Message Text="BeforeBuild event" Importance="high"></Message>
</Target>
<Target Name="MyCustomBeforeTarget" BeforeTargets="BeforeBuild">
<Message Text="MyCustomBeforeTarget event" Importance="high"></Message>
</Target>
<Target Name="MyCustomAfterTarget" AfterTargets="BeforeBuild">
<Message Text="MyCustomAfterTarget event" Importance="high"></Message>
</Target>
Результат выполнения (лишнее исключено):
…
MyCustomBeforeTarget event
BeforeBuild event
MyCustomAfterTarget event
…
Было определено два собственных таргета — MyCustomBeforeTarget и MyCustomAfterTarget.
Таргет MyCustomBeforeTarget выполняется до таргета BeforeBuild, потому что мы указали:
BeforeTargets="BeforeBuild"
Таргет MyCustomAfterTarget выполняется после таргета BeforeBuild, потому что мы указали:
AfterTargets="BeforeBuild"
Таски в MSBuild (Меню)
В данной статье не рассматривается как можно писать собственные таски, но прежде чем писать таск, ознакомьтесь со списком тасков, предоставляемых Microsoft.
Рассмотрим несколько примеров использования тасков и макросов.
Параметр Condition (спойлер)
Параметр Condition присутствует у всех тасков. Цитата из документации docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-task-reference:
Если будет выбрана solution конфигурация Debug, то результат будет выглядеть так (лишнее исключено):
Информацию о синтаксисе условий можно прочитать там https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-conditions
A Boolean expression that the MSBuild engine uses to determine whether this task will be executed.Код примера:
<Target Name="BeforeBuild">
<Message Text="Current configuration is Debug" Condition="'$(Configuration)' == 'Debug'" Importance="high"></Message>
<Message Text="Current configuration is Release" Condition="'$(Configuration)' == 'Release'" Importance="high"></Message>
</Target>
Если будет выбрана solution конфигурация Debug, то результат будет выглядеть так (лишнее исключено):
…Если будет выбрана solution конфигурация Release, то результат будет выглядеть так (лишнее исключено):
Current configuration is Debug
...
…Информацию о макросе $(Configuration) и других макросах можете найти в разделе переменные и макросы в .csproj.
Current configuration is Release
…
Информацию о синтаксисе условий можно прочитать там https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-conditions
Определение переменной в csproj (спойлер)
В примере выше у нас есть текст, который можно унифицировать. Чтобы исключить дублирование вынесем в отдельную переменную текст сообщения.
Код примера:
Для определения собственной переменной используется элемент PropertyGroup.
Код примера:
<PropertyGroup>
<MessageText>Current configuration is $(Configuration)</MessageText>
</PropertyGroup>
<Target Name="BeforeBuild">
<Message Text="$(MessageText)" Condition="'$(Configuration)' == 'Debug'" Importance="high"></Message>
<Message Text="$(MessageText)" Condition="'$(Configuration)' == 'Release'" Importance="high"></Message>
</Target>
Для определения собственной переменной используется элемент PropertyGroup.
Проверка существования файла, выдача ошибки (спойлер)
В данном примере сделаем таск, который проверяет создан ли файл App.Debug.config. Если он не создан, то выдаем ошибку. В случае ошибки билд будет остановлен и ошибка будет отображена как ошибки компиляции в окне Error List.
Используем для этого таск Error и уже знакомый нам параметр Condition.
Код примера:
Результат:
![image](https://habrastorage.org/r/w1560/webt/fh/kg/xa/fhkgxawfnlrr-1wmbspyzfufq8m.png)
В условии Exists используется относительный путь от папки, в которой находится файл .csproj. Для обращения к папке выше текущей использовать '../'. Если нужно обратиться к вложенной папке, то использовать формат '[DirectoryName]/App.Debug.config'.
Используем для этого таск Error и уже знакомый нам параметр Condition.
Код примера:
<Target Name="BeforeBuild">
<Error Condition="!Exists('App.Debug.config')" Text="File App.Debug.config not found"></Error>
</Target>
Результат:
![image](https://habrastorage.org/webt/fh/kg/xa/fhkgxawfnlrr-1wmbspyzfufq8m.png)
В условии Exists используется относительный путь от папки, в которой находится файл .csproj. Для обращения к папке выше текущей использовать '../'. Если нужно обратиться к вложенной папке, то использовать формат '[DirectoryName]/App.Debug.config'.
Копирование файлов (спойлер)
В данном примере будем использовать таск Copy. С помощью таска скопируем файл App.config в папку bin/[Configuration]/Config в два файла App.config и App.test.config.
Код примера:
Свойство SourceFiles — массив файлов, которые необходимо скачать. Указывать без кавычек, через точку с запятой.
Свойство DestinationFiles — массив файлов куда будут копироваться файлы. Указывать без кавычек, через точку с запятой.
Подробнее о макросе $(OutputPath) читать в разделе переменные и макросы в .csproj.
Код примера:
<Target Name="BeforeBuild">
<Copy SourceFiles="App.config;App.config" DestinationFiles="$(OutputPath)/Test/App.config;$(OutputPath)/Test/App.test.config"></Copy>
</Target>
Свойство SourceFiles — массив файлов, которые необходимо скачать. Указывать без кавычек, через точку с запятой.
Свойство DestinationFiles — массив файлов куда будут копироваться файлы. Указывать без кавычек, через точку с запятой.
Подробнее о макросе $(OutputPath) читать в разделе переменные и макросы в .csproj.
Переменные и макросы в .csproj (Меню)
В файле .csproj можно использовать ряд стандартных макросов, их список можно найти здесь https://msdn.microsoft.com/en-us/library/c02as0cs.aspx и здесь https://msdn.microsoft.com/en-us/library/bb629394.aspx. Рассмотрим некоторые полезные макросы:
- $(MSBuildToolsPath) — указывает на путь к папке MSBuild. Например, C:\Program Files (x86)\MSBuild\14.0\Bin. Данный макрос при комбинировании пути использовать со слешем. Например, $(MSBuildToolsPath)\Microsoft.Web.Publishing.Tasks.dll. Иначе он может некорректно формировать путь и выдавать ошибку, что файл не найден.
- $(OutputPath) — относительный путь к выходной папке. Например, bin\Stage. Данный макрос использовать со слешем, например, $(OutputPath)\$(TargetFileName).config.
- $(TargetFileName) — имя выходного файла вместе с расширением. Например, MSBuildExample.exe. Расширение и формат имени выходного файла может отличаться от различных типов проектов. С помощью этого макроса можно безопасно определить какое будет имя у файла конфига. Может быть полезно для трасформаций конфигов.
- $(Configuration) — имя текущей конфигурации. Например, Release, Debug
- $(IntermediateOutputPath) — путь к папке obj. Например, obj\Stage.
Для определения собственных параметров использовать PropertyGroup . Пример определения собственной переменной можно найти в разделе таски в MSBuild.
Ссылки (Меню)
- документация по таргетам
- документация по таскам
- список тасков, предоставляемых Microsoft
- условия
- информация о макросах
- информация о переменных
- информация о процессе билда
- порядок выполнения таргетов
- информация о таргет файлах
- еще немного информации о таргетах и процессе билда. Там же ссылки на серию статей про трансформации