Расширение процесса сборки Visual Studio

Процесс сборки Visual Studio определяется рядом файлов .targets MSBuild, которые импортируются в файл проекта. Один из этих импортированных файлов — Microsoft.Common.targets — можно расширить, чтобы выполнять настраиваемые задачи в нескольких точках в процессе сборки. В этой статье описаны два метода, которые можно использовать для расширения процесса сборки Visual Studio:

  • Переопределение конкретных предварительно заданных целевых объектов, определенных в списке стандартных целевых объектов (в файле Microsoft.Common.targets или импортируемых им файлах).

  • Переопределение свойств DependsOn, определенных в списке стандартных целевых объектов.

Переопределение предопределенных целевых объектов

Список стандартных целевых объектов содержит набор предопределенных пустых целевых объектов, которые вызываются до и после некоторых основных целевых объектов в процессе сборки. Например, MSBuild вызывает целевой объект BeforeBuild перед основным целевым объектом CoreBuild, а целевой объект AfterBuild — после целевого объекта CoreBuild. По умолчанию пустые целевые объекты в списке стандартных целевых объектов не выполняют никаких действий, но их поведение по умолчанию можно переопределить, определив нужные целевые объекты в файле проекта, куда импортируются стандартные целевые объекты. Переопределяя предварительно заданные целевые объекты, вы можете использовать задачи MSBuild, чтобы получить больший контроль над процессом сборки.

Примечание.

В проектах в стиле SDK имеется неявная директива импорта целевых объектов после последней строки в файле проекта. Это означает, что вы не можете переопределить целевые объекты по умолчанию, если не указать импорт вручную, как описано в разделе "Практическое руководство. Использование пакетов SDK для проекта MSBuild".

Переопределение предопределенного целевого объекта

  1. Выберите в списке стандартных целевых объектов предварительно заданный целевой объект, который требуется переопределить. Приведенная ниже таблица содержит полный список целевых объектов, которые можно безопасно переопределить.

  2. Определите один или несколько целевых объектов в конце файла проекта, прямо перед тегом </Project>. Например:

    <Project>
        ...
        <Target Name="BeforeBuild">
            <!-- Insert tasks to run before build here -->
        </Target>
        <Target Name="AfterBuild">
            <!-- Insert tasks to run after build here -->
        </Target>
    </Project>
    
  3. Выполните сборку файла проекта.

В приведенной ниже таблице представлены все стандартные целевые объекты, которые можно безопасно переопределить.

Имя целевого объекта Description
BeforeCompile, AfterCompile Задачи, добавленные в один из этих целевых объектов, выполняются до или после основной компиляции. Основная часть настроек выполняется в одном из этих двух целевых объектов.
BeforeBuild, AfterBuild Задачи, добавленные в один из этих целевых объектов, выполняются до или после остальной части сборки. Примечание. Целевые объекты BeforeBuild и AfterBuild уже определены в комментариях в конце большинства файлов проекта, поэтому вы можете легко добавлять события до и после сборки в файл проекта.
BeforeRebuild, AfterRebuild Задачи, добавленные в один из этих целевых объектов, выполняются до или после вызова основной функции перестроения. В Microsoft.Common.targets действует следующий порядок выполнения целевых объектов: BeforeRebuild, Clean, Build, а затем AfterRebuild.
BeforeClean, AfterClean Задачи, добавленные в один из этих целевых объектов, выполняются до или после вызова основной функции очистки.
BeforePublish, AfterPublish Задачи, добавленные в один из этих целевых объектов, выполняются до или после вызова основной функции публикация.
BeforeResolveReferences, AfterResolveReferences Задачи, добавленные в один из этих целевых объектов, выполняются до или после разрешения ссылок на сборки.
BeforeResGen, AfterResGen Задачи, добавленные в один из этих целевых объектов, выполняются до или после создания ресурсов.

Пример: 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, в которой они определены. Вы не видите операцию импорта целевого файла, который переопределяет эти целевые объекты, но она неявно добавляется в конец файла проекта при использовании метода атрибута Sdk для ссылки на пакет SDK.

Переопределение свойств DependsOn

Переопределение предопределенных целевых объектов позволяет легко расширить процесс сборки, но так как MSBuild оценивает определение целевых объектов последовательно, не существует способа запретить другому проекту, который импортирует ваш проект, переопределить целевые объекты, которые вы уже переопределили. Например, последний целевой объект AfterBuild, определенный в файле проекта, после импорта всех остальных проектов будет использоваться в процессе сборки.

Вы можете предусмотреть защиту от нежелательных переопределений целевых объектов, переопределив свойства DependsOn, которые используются в атрибутах DependsOnTargets по всему списку стандартных целевых объектов. Например, целевой объект Build содержит значение атрибута DependsOnTargets, равное "$(BuildDependsOn)". Необходимо учесть следующие моменты.

<Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>

Этот фрагмент XML-кода указывает, что перед выполнением целевого объекта Build должны быть выполнены все целевые объекты, указанные в свойстве BuildDependsOn. Свойство BuildDependsOn определено следующим образом:

<PropertyGroup>
    <BuildDependsOn>
        BeforeBuild;
        CoreBuild;
        AfterBuild
    </BuildDependsOn>
</PropertyGroup>

Значение этого свойства можно переопределить, объявив другое свойство с именем BuildDependsOn в конце файла проекта. Включив предыдущее свойство 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

  1. Выберите в списке стандартных целевых объектов предварительно заданное свойство DependsOn, которое требуется переопределить. Приведенная ниже таблица содержит список часто переопределяемых свойств DependsOn.

  2. Определите еще один экземпляр свойства или свойств в конце файла проекта. Включите исходное свойство, например $(BuildDependsOn), в новое свойство.

  3. Определите пользовательские целевые объекты до или после определения свойства.

  4. Выполните сборку файла проекта.

Часто переопределяемые свойства DependsOn

Имя свойства Description
BuildDependsOn Свойство, которое следует переопределить, если нужно вставить пользовательские целевые объекты до или после всего процесса сборки.
CleanDependsOn Свойство, которое следует переопределить, если нужно убрать выходные данные из пользовательского процесса сборки.
CompileDependsOn Свойство, которое следует переопределить, если нужно вставить пользовательские процессы до или после этапа компиляции.

Пример: BuildDependsOn и CleanDependsOn

Приведенный ниже пример похож на пример с атрибутами BeforeTargets и AfterTargets, и в нем решается аналогичная задача. В нем сборка расширяется путем добавления с помощью атрибута BuildDependsOn собственной задачи CustomAfterBuild, которая копирует выходные файлы после сборки, а также добавления соответствующей задачи CustomClean с помощью CleanDependsOn.

В этом примере проект имеет стиль пакета SDK. Как упоминалось в примечании о проектах в стиле пакета SDK ранее в этой статье, вместо атрибута Sdk, применяемого средой Visual Studio при создании файлов проекта, необходимо использовать импорт вручную.

<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-&gt;'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>

    <Copy SourceFiles="@(_FilesToCopy)"
          DestinationFiles="@(_FilesToCopy-&gt;'$(_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.