Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Процесс сборки Visual Studio определяется рядом файлов MSBuild .targets
, импортируемых в файл проекта. Эти импорты являются неявными, если вы используете SDK, как это обычно делают проекты Visual Studio. Один из этих импортированных файлов Microsoft.Common.targets можно расширить, чтобы разрешить выполнять пользовательские задачи в нескольких точках процесса сборки. В этой статье объясняется три метода, которые можно использовать для расширения процесса сборки Visual Studio:
Создайте пользовательскую цель и укажите, когда она должна выполняться с помощью атрибутов
BeforeTargets
иAfterTargets
.Переопределите свойства,
DependsOn
определенные в общих целевых объектах.Переопределите определенные предопределенные целевые объекты, определенные в общих целевых объектах (Microsoft.Common.targets или файлы, импортируемые им).
AfterTargets и BeforeTargets
Вы можете использовать AfterTargets
и BeforeTargets
атрибуты в пользовательском целевом объекте, чтобы указать, когда он должен выполняться.
В следующем примере показано, как использовать AfterTargets
атрибут для добавления пользовательского целевого объекта, который выполняет что-то с выходными файлами. В этом случае он копирует выходные файлы в новую папку CustomOutput. В примере также показано, как очистить файлы, созданные пользовательской операцией сборки с целевым объектом, CustomClean
с помощью BeforeTargets
атрибута и указать, что пользовательская операция очистки выполняется до целевого CoreClean
объекта.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<_OutputCopyLocation>$(OutputPath)..\..\CustomOutput\</_OutputCopyLocation>
</PropertyGroup>
<Target Name="CustomAfterBuild" AfterTargets="Build">
<ItemGroup>
<_FilesToCopy Include="$(OutputPath)**\*"/>
</ItemGroup>
<Message Text="_FilesToCopy: @(_FilesToCopy)" Importance="high"/>
<Message Text="DestFiles:
@(_FilesToCopy->'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>
<Copy SourceFiles="@(_FilesToCopy)"
DestinationFiles=
"@(_FilesToCopy->'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>
</Target>
<Target Name="CustomClean" BeforeTargets="CoreClean">
<Message Text="Inside Custom Clean" Importance="high"/>
<ItemGroup>
<_CustomFilesToDelete Include="$(_OutputCopyLocation)**\*"/>
</ItemGroup>
<Delete Files='@(_CustomFilesToDelete)'/>
</Target>
</Project>
Предупреждение
Не забудьте использовать имена, отличные от предопределенных целей (например, пользовательская цель сборки CustomAfterBuild
, а не AfterBuild
), так как эти предопределенные цели переопределяются импортом SDK, который также их определяет. См. таблицу в конце этой статьи для списка предопределенных целевых объектов.
Расширение свойств DependsOn
Другим способом расширения процесса сборки является использование DependsOn
свойств (например, BuildDependsOn
для указания целевых объектов, которые должны выполняться до стандартного целевого объекта).
Этот метод предпочтительнее, чем переопределение предопределённых целей, что будет рассмотрено в следующем разделе. Переопределение предопределенных целевых объектов — это старый метод, который по-прежнему поддерживается, но, так как MSBuild оценивает определение целевых объектов последовательно, невозможно запретить другому проекту, который импортирует проект, переопределить уже переопределенные целевые объекты. Например, последний AfterBuild
целевой объект, определенный в файле проекта, после импорта всех остальных проектов, будет тем, который используется во время сборки.
Вы можете защититься от непреднамеренных переопределений целевых объектов, переопределив DependsOn
свойства, используемые в атрибутах во DependsOnTargets
всех общих целевых объектах. Например, целевой Build
DependsOnTargets
объект содержит значение атрибута "$(BuildDependsOn)"
. Подумайте:
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>
Этот фрагмент XML указывает, что перед запуском целевого Build
объекта все целевые объекты, указанные в свойстве BuildDependsOn
, должны выполняться сначала. Свойство BuildDependsOn
определяется следующим образом:
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);
BeforeBuild;
CoreBuild;
AfterBuild
</BuildDependsOn>
</PropertyGroup>
Это значение свойства можно переопределить, объявив другое свойство с именем BuildDependsOn
в конце файла проекта. В проекте в формате SDK это означает, что необходимо использовать явные импорты. См. неявные и явные импорты, чтобы можно было поместить DependsOn
свойство после последнего импорта. Включив предыдущее BuildDependsOn
свойство в новое свойство, можно добавить новые целевые объекты в начало и конец целевого списка. Рассмотрим пример.
<PropertyGroup>
<BuildDependsOn>
MyCustomTarget1;
$(BuildDependsOn);
MyCustomTarget2
</BuildDependsOn>
</PropertyGroup>
<Target Name="MyCustomTarget1">
<Message Text="Running MyCustomTarget1..."/>
</Target>
<Target Name="MyCustomTarget2">
<Message Text="Running MyCustomTarget2..."/>
</Target>
Проекты, которые импортируют ваш файл проекта, могут расширить эти свойства без перезаписи внесенных настроек.
Переопределение свойства DependsOn
Определите предопределенное
DependsOn
свойство в общих целевых объектах, которые необходимо переопределить. В следующей таблице приведен список часто переопределенныхDependsOn
свойств.Определите еще один экземпляр свойства или свойств в конце файла проекта. Включите исходное свойство, например
$(BuildDependsOn)
, в новое свойство.Определите пользовательские целевые объекты до или после определения свойства.
Создайте файл проекта.
Свойства DependsOn, которые обычно переопределяются
Название свойства | Добавленные целевые объекты выполняются до этой точки: |
---|---|
BuildDependsOn |
Основная точка входа для сборки. Переопределите это свойство, если вы хотите вставить пользовательские целевые объекты до или после всего процесса сборки. |
RebuildDependsOn |
Rebuild
|
RunDependsOn |
Выполнение окончательных выходных данных сборки (если это .EXE) |
CompileDependsOn |
Компиляция (Compile целевой объект). Переопределите это свойство, если вы хотите вставить пользовательские процессы до или после шага компиляции. |
CreateSatelliteAssembliesDependsOn |
Создание спутниковых сборок |
CleanDependsOn |
Цель Clean (Удаление всех промежуточных и конечных выходных данных сборки). Переопределите это свойство, если вы хотите очистить выходные данные из пользовательского процесса сборки. |
PostBuildEventDependsOn |
Целевой PostBuildEvent объект |
PublishBuildDependsOn |
Сборка публикации |
ResolveAssemblyReferencesDependsOn |
Целевая ResolveAssemblyReferences задача (поиск транзитивного замыкания зависимостей для заданной зависимости). См. ResolveAssemblyReference . |
Пример: BuildDependsOn и CleanDependsOn
Следующий пример аналогичен BeforeTargets
и AfterTargets
примеру, но показывает, как достичь аналогичной функциональности. Он расширяет сборку, используя BuildDependsOn
для добавления вашей задачи CustomAfterBuild
, которая копирует выходные файлы после сборки, а также добавляет соответствующую задачу CustomClean
с помощью CleanDependsOn
.
В этом примере это проект в стиле SDK. Как упоминалось в заметке о проектах в стиле SDK, описанных ранее в этой статье, вместо атрибута, используемого Visual Studio при создании файлов проекта, следует использовать метод Sdk
ручного импорта.
<Project>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk"/>
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk"/>
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);CustomAfterBuild
</BuildDependsOn>
<CleanDependsOn>
$(CleanDependsOn);CustomClean
</CleanDependsOn>
<_OutputCopyLocation>$(OutputPath)..\..\CustomOutput\</_OutputCopyLocation>
</PropertyGroup>
<Target Name="CustomAfterBuild">
<ItemGroup>
<_FilesToCopy Include="$(OutputPath)**\*"/>
</ItemGroup>
<Message Importance="high" Text="_FilesToCopy: @(_FilesToCopy)"/>
<Message Text="DestFiles:
@(_FilesToCopy->'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>
<Copy SourceFiles="@(_FilesToCopy)"
DestinationFiles="@(_FilesToCopy->'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>
</Target>
<Target Name="CustomClean">
<Message Importance="high" Text="Inside Custom Clean"/>
<ItemGroup>
<_CustomFilesToDelete Include="$(_OutputCopyLocation)**\*"/>
</ItemGroup>
<Delete Files="@(_CustomFilesToDelete)"/>
</Target>
</Project>
Порядок элементов важен. Элементы BuildDependsOn
и CleanDependsOn
должны появляться после импорта файла целей стандартного пакета SDK.
Переопределить предопределенные целевые показатели
Общие .targets
файлы содержат набор предопределенных пустых целевых объектов, которые вызываются до и после некоторых основных целевых объектов в процессе сборки. Например, MSBuild вызывает цель BeforeBuild
перед основной целью CoreBuild
, а цель AfterBuild
после цели CoreBuild
. По умолчанию пустые целевые объекты в общих целевых объектах ничего не делают, но их поведение по умолчанию можно переопределить, определив нужные целевые объекты в файле проекта. Методы, описанные ранее в этой статье, предпочтительны, но вы можете столкнуться с более старым кодом, использующим этот метод.
Если в проекте используется пакет SDK (например Microsoft.Net.Sdk
), необходимо внести изменения с неявного на явный импорт, как описано в явном и неявном импорте.
Переопределить предопределенную цель
Если проект использует
Sdk
атрибут, измените его на явный синтаксис импорта. См. явные и неявные импорты.Определите предопределенный целевой объект в общих целевых объектах, которые необходимо переопределить. В следующей таблице приведен полный список целевых объектов, которые можно безопасно переопределить.
Определите целевой объект или целевые объекты в конце файла проекта, непосредственно перед тегом
</Project>
и после явного импорта пакета SDK. Рассмотрим пример.<Project> <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" /> ... <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" /> <Target Name="BeforeBuild"> <!-- Insert tasks to run before build here --> </Target> <Target Name="AfterBuild"> <!-- Insert tasks to run after build here --> </Target> </Project>
Обратите внимание, что
Sdk
атрибут на элементе верхнего уровняProject
был удален.Создайте файл проекта.
Таблица предопределенных целевых объектов
В следующей таблице показаны все цели из общего списка целей, которые можно переопределить.
Имя целевого объекта | Описание |
---|---|
BeforeCompile , AfterCompile |
Задачи, вставляемые в один из этих целевых объектов, выполняются до или после завершения компиляции ядра. Большинство настроек выполняются в одном из этих двух целевых объектов. |
BeforeBuild , AfterBuild |
Задачи, вставляемые в одну из этих целевых задач, запускаются до или после всего остального в сборке.
Заметка:BeforeBuild и AfterBuild целевые объекты уже определены в комментариях в конце большинства файлов проекта, что позволяет легко добавлять события до и после сборки в файл проекта. |
BeforeRebuild , AfterRebuild |
Задачи, вставляемые в один из этих целевых объектов, выполняются до или после вызова основных функций перестроения. Порядок выполнения целевого объекта в Microsoft.Common.target: BeforeRebuild , Clean Build , а затем AfterRebuild . |
BeforeClean , AfterClean |
Задачи, вставляемые в один из этих целевых объектов, выполняются до или после вызова основных функций очистки. |
BeforePublish , AfterPublish |
Задачи, вставляемые в один из этих целевых объектов, выполняются до или после вызова основных функций публикации. |
BeforeResolveReferences , AfterResolveReferences |
Задачи, вставляемые в один из этих целевых объектов, выполняются до или после устранения ссылок на сборки. |
BeforeResGen , AfterResGen |
Задачи, вставляемые в один из этих целевых объектов, выполняются до или после создания ресурсов. |
В системе сборки и пакете SDK для .NET есть еще много целевых объектов, см. целевые объекты MSBuild — пакет SDK и целевые объекты сборки по умолчанию.
Лучшие практики для настройки пользовательских целей
Свойства DependsOnTargets
и BeforeTargets
могут указывать, что задача должна выполняться перед другой задачей, но оба они необходимы в разных сценариях. Они отличаются тем, в каком целевом объекте указывается требование зависимостей. Вы можете контролировать только свои собственные цели и не можете безопасно изменять системные цели или другие импортированные цели, поэтому это ограничивает ваш выбор методов.
При создании пользовательского целевого объекта следуйте этим общим рекомендациям, чтобы убедиться, что целевой объект выполняется в указанном порядке.
Используйте атрибут
DependsOnTargets
, чтобы указать задачи, которые необходимо выполнить до начала выполнения вашей цели. Для цепочки целевых объектов, которые вы управляете, каждый целевой объект может указать предыдущий элемент цепочки вDependsOnTargets
.Используйте
BeforeTargets
для любого целевого объекта, который вы не контролируете и который необходимо выполнить заранее (например,BeforeTargets="PrepareForBuild"
для целевого объекта, который должен выполняться на раннем этапе сборки).Используйте
AfterTargets
для любого целевого объекта, который не контролируется, что гарантирует доступность необходимых выходных данных. Например, укажитеAfterTargets="ResolveReferences"
для чего-то, что изменит список ссылок.Их можно использовать в сочетании. Например:
DependsOnTargets="GenerateAssemblyInfo" BeforeTargets="BeforeCompile"
.
Явные и неявные импорты
Проекты, созданные Visual Studio, обычно используют Sdk
атрибут в элементе проекта. Эти типы проектов называются проектами в стиле SDK.
См. Используйте SDK проектов MSBuild. Ниже приведен пример:
<Project Sdk="Microsoft.Net.Sdk">
Когда проект использует Sdk
атрибут, два импорта неявно добавляются, один в начале файла проекта и один в конце.
Неявные импорты эквивалентны инструкции импорта в виде первой строки в файле проекта, после элемента Project
.
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
и следующая инструкция импорта в качестве последней строки в файле проекта:
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
Этот синтаксис называется явным импортом пакета SDK. При использовании этого явного синтаксиса следует опустить Sdk
атрибут в элементе проекта.
Неявный импорт пакета SDK эквивалентен импорту определенных "common" .props
или .targets
файлов, которые являются типичной конструкцией в старых файлах проекта, например:
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
и
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
Все старые ссылки должны быть заменены явным синтаксисом пакета SDK, показанным ранее в этом разделе.
С помощью явного синтаксиса пакета SDK можно добавить собственный код до первого импорта или после окончательного импорта пакета SDK. Это означает, что вы можете изменить поведение, задав свойства перед первым импортом, который вступит в силу в импортированном .props
файле, и вы можете переопределить целевой объект, определенный в одном из файлов ПАКЕТА SDK .targets
после окончательного импорта. С помощью этого метода можно переопределить BeforeBuild
или AfterBuild
как описано далее.
Дальнейшие шаги
Существует гораздо больше возможностей для настройки сборки с помощью MSBuild. См. Настройте вашу сборку.