Встроенные задачи MSBuild

Задачи MSBuild обычно создаются путем компиляции класса, реализующего интерфейс ITask. Дополнительные сведения см. в разделе Задачи.

Начиная с версии 4 .NET Framework, вы можете создавать встроенные задачи в файле проекта. Для размещения задачи не нужно создавать отдельную сборку. Это упрощает как отслеживание исходного кода, так и развертывание задачи. Исходный код встроен в скрипт.

В MSBuild 15.8 добавлена служба RoslynCodeTaskFactory . Для текущей разработки обязательно используйте RoslynCodeTaskFactory, а не CodeTaskFactory. CodeTaskFactory поддерживает только версии C# до 4.0.

Структура встроенной задачи

Элемент UsingTask содержит встроенную задачу. Встроенная задача и содержащий ее элемент UsingTask обычно включены в TARGETS-файл и при необходимости импортируются в другие файлы проекта. Ниже представлен пример обычной встроенной задачи. Обратите внимание, что в нем не предусмотрено выполнение каких-либо действий.

<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- This simple inline task does nothing. -->
  <UsingTask
    TaskName="DoNothing"
    TaskFactory="CodeTaskFactory"
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" >
    <ParameterGroup />
    <Task>
      <Reference Include="" />
      <Using Namespace="" />
      <Code Type="Fragment" Language="cs">
      </Code>
    </Task>
  </UsingTask>
</Project>

Элемент UsingTask в примере включает три атрибута, описывающих задачу и фабрику встроенной задачи, компилирующую ее.

  • Атрибут TaskName содержит имя задачи. В примере используется имя — DoNothing.

  • Атрибут TaskFactory содержит класс, реализующий фабрику встроенной задачи.

  • Атрибут AssemblyFile включает расположение фабрики встроенной задачи. Вы также можете использовать атрибут AssemblyName для указания полного имени класса фабрики встроенной задачи, который обычно расположен в $(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll.

Остальные элементы задачи DoNothing пусты и приведены, чтобы показать порядок и структуру встроенной задачи. Более сложный пример представлен в этой статье далее.

  • Элемент ParameterGroup необязательный. Если он все же используется, его функция заключается в указании параметров задачи. Дополнительные сведения о входных и выходных параметрах см. в разделе Входные и выходные параметры ниже.

  • Элемент Task содержит исходный код задачи и описывает его.

  • Элемент Reference указывает ссылки на сборки .NET, используемые в коде. Это эквивалентно добавлению ссылки в проект в Visual Studio. Атрибут Include задает путь к сборке, на которую указывает ссылка.

  • Элемент Using необходим для вывода списка пространств имен, к которым нужно получить доступ. Он похож на оператор Using в Visual C#. Атрибут Namespace указывает пространство имен, которое нужно включить.

Элементы Reference и Using подходят для любого языка. Встроенные задачи можно написать на любом из поддерживаемых языков .NET CodeDom, например Visual Basic или Visual C#.

Примечание.

Элементы, содержащиеся в элементе Task, характерны для фабрики задачи, в этом случае для фабрики кода задачи.

Элемент кода

Последний дочерний элемент в элементе Task — Code. Элемент Code содержит код, который нужно скомпилировать в задачу, или определяет его местонахождение. Содержимое в элементе Code зависит от того, каким образом вы хотите написать задачу.

Атрибут Language указывает язык, на котором написан код. Допустимые значения: cs для C# и vb для Visual Basic.

Атрибут Type указывает тип кода, находящийся в элементе Code.

  • Если значением атрибута Type является Class, элемент Code содержит код для класса, производного от интерфейса ITask.

  • Если значением атрибута Type является Method, код определяет переопределение метода Execute интерфейса ITask.

  • Если значением атрибута Type является Fragment, тогда код определяет содержимое метода Execute, а не сигнатуру или оператор return.

Сам код отображается, как правило, между метками <![CDATA[ и ]]>. Так как код размещается в разделе CDATA, вы можете не беспокоиться об экранировании зарезервированных знаков, например "<" или ">".

Вы также можете использовать атрибут Source элемента Code, чтобы указать расположение файла, содержащего код для задачи. Код в исходном файле должен иметь тип, заданный атрибутом Type. Если есть атрибут Source, тогда по умолчанию значением атрибута Type является Class. Если атрибут Source отсутствует, значением по умолчанию будет Fragment.

Примечание.

При определении класса задачи в исходном файле имя класса должно быть согласовано с атрибутом TaskName соответствующего элемента UsingTask.

HelloWorld

Рассмотрим более сложную встроенную задачу. Задача HelloWorld отображает приветствие "Hello, world!" на устройстве регистрации ошибок по умолчанию. Как правило, это системная консоль или окно вывода Visual Studio. В примере элемент Reference используется просто для наглядности.

<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- This simple inline task displays "Hello, world!" -->
  <UsingTask
    TaskName="HelloWorld"
    TaskFactory="CodeTaskFactory"
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" >
    <ParameterGroup />
    <Task>
      <Reference Include="System.Xml"/>
      <Using Namespace="System"/>
      <Using Namespace="System.IO"/>
      <Code Type="Fragment" Language="cs">
<![CDATA[
// Display "Hello, world!"
Log.LogError("Hello, world!");
]]>
      </Code>
    </Task>
  </UsingTask>
</Project>

Задачу HelloWorld можно сохранить в файл с именем HelloWorld.targets, а затем вызвать его из проекта, как показано ниже.

<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="HelloWorld.targets" />
  <Target Name="Hello">
    <HelloWorld />
  </Target>
</Project>

Входные и выходные параметры

Параметры встроенной задачи являются дочерними элементами элемента ParameterGroup. Каждый параметр принимает имя элемента, который его определяет. Код, представленный ниже, определяет параметр Text.

<ParameterGroup>
  <Text />
</ParameterGroup>

Параметры могут иметь один или несколько атрибутов:

  • Атрибут Required является необязательным и по умолчанию имеет значение false. Если же используется значение true, тогда этот параметр обязателен и перед вызовом задачи ему необходимо присвоить значение.

  • Атрибут ParameterType является необязательным и по умолчанию имеет значение System.String. Вы можете его задать для любого полного типа, являющегося элементом или значением, которые можно преобразовать как в строку, так и из нее, используя System.Convert.ChangeType. Другими словами, любой тип, который можно передать во внешнюю задачу или же из нее.

  • Атрибут Output является необязательным и по умолчанию имеет значение false. Если же используется значение true, тогда этому параметру необходимо присвоить значение перед возвратом из метода Execute.

Например:

<ParameterGroup>
  <Expression Required="true" />
  <Files ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
  <Tally ParameterType="System.Int32" Output="true" />
</ParameterGroup>

определяет следующие три параметра:

  • Expression является обязательным входным параметром типа System.String.

  • Files является обязательным входным параметром списка элементов.

  • Tally является выходным параметром типа System.Int32.

Если в элементе Code значением атрибута Type является Fragment или Method, тогда свойства для каждого параметра создаются автоматически. В противном случае свойства следует явно объявить в исходном коде задачи. Кроме того, они должны в точности соответствовать определениям своих параметров.

Пример

Как показано ниже, встроенная задача заменяет каждое вхождение токена в указанном файле на указанное значение.

<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' ToolsVersion="15.0">

  <UsingTask TaskName="TokenReplace" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
    <ParameterGroup>
      <Path ParameterType="System.String" Required="true" />
      <Token ParameterType="System.String" Required="true" />
      <Replacement ParameterType="System.String" Required="true" />
    </ParameterGroup>
    <Task>
      <Code Type="Fragment" Language="cs"><![CDATA[
string content = File.ReadAllText(Path);
content = content.Replace(Token, Replacement);
File.WriteAllText(Path, content);

]]></Code>
    </Task>
  </UsingTask>

  <Target Name='Demo' >
    <TokenReplace Path="C:\Project\Target.config" Token="$MyToken$" Replacement="MyValue"/>
  </Target>
</Project>

См. также