Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Задачи MSBuild обычно создаются путем компиляции класса, реализующего ITask интерфейс. Дополнительные сведения см. в разделе Задачи.
Если вы хотите избежать затрат на создание скомпилированной задачи, можно создать встроенную задачу в файле проекта или в импортированном файле. Вам не нужно создавать отдельную сборку для выполнения задачи. Использование встроенной задачи упрощает отслеживание исходного кода и упрощает развертывание задачи. Исходный код интегрирован в файл проекта MSBuild или импортированный файл, как правило файл .targets.
Вы создаете встроенную задачу с помощью фабрики задач в коде. Для текущей разработки не забудьте использовать RoslynCodeTaskFactory, а не CodeTaskFactory.
CodeTaskFactory поддерживает только версии C# до 4.0.
Встроенные задачи предназначены для небольших задач, которые не требуют сложных зависимостей. Поддержка отладки встроенных задач ограничена. Рекомендуется создать скомпилированную задачу вместо встроенной задачи, если требуется написать более сложный код, ссылаться на пакет NuGet, запускать внешние инструменты или выполнять операции, которые могут привести к ошибкам. Кроме того, встроенные задачи компилируются каждый раз при сборке, поэтому может быть заметное влияние на производительность сборки.
Структура встроенной задачи
Встроенная задача содержится элементом UsingTask . Встроенная задача и элемент UsingTask, который его содержит, обычно включаются в файл .targets и импортируются в другие файлы проекта по мере необходимости. Ниже приведена базовая встроенная задача, которая ничего не делает, но иллюстрирует синтаксис:
<!-- This simple inline task does nothing. -->
<UsingTask
TaskName="DoNothing"
TaskFactory="RoslynCodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" >
<ParameterGroup />
<Task>
<Reference Include="" />
<Using Namespace="" />
<Code Type="Fragment" Language="cs">
</Code>
</Task>
</UsingTask>
Элемент UsingTask в примере содержит три атрибута, описывающие задачу и встроенную фабрику задач, которая компилирует ее.
Атрибут
TaskNameназывает задачу в данном случаеDoNothing.Атрибут
TaskFactoryименует класс, реализующий встроенную фабрику задач.Атрибут
AssemblyFileпредоставляет расположение встроенной фабрики задач. Кроме того, атрибут можно использоватьAssemblyNameдля указания полного имени класса встроенной фабрики задач, который обычно находится в$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll.
Остальные элементы DoNothing задачи пусты и предоставляются для иллюстрации порядка и структуры встроенной задачи. Полный пример представлен далее в этой статье.
Элемент
ParameterGroupнеобязательный. Если указано, он объявляет параметры для задачи. Дополнительные сведения о входных и выходных параметрах см. далее в этой статье.Элемент
Taskописывает и содержит исходный код задачи.Элемент
Referenceуказывает ссылки на сборки .NET, которые вы используете в коде. Использование этого элемента эквивалентно добавлению ссылки на проект в Visual Studio. АтрибутIncludeзадает путь к указанной сборке. Сборки в mscorlib, .NET Standard, Microsoft.Build.Framework и Microsoft.Build.Utilities.Core, а также некоторые сборки, на которые транзитивно ссылаются как зависимости, доступны безReference.Элемент
Usingперечисляет пространства имен, к которым требуется получить доступ. Этот элемент эквивалентен директивеusingв C#. АтрибутNamespaceуказывает пространство имен для включения. Нельзя вставить директивуusingв встроенный код, так как этот код помещается в тело метода, где директивыusingне допускаются.
Reference и Using элементы являются независимыми от языка. Встроенные задачи можно записывать в Visual Basic или 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.
<Project>
<!-- This simple inline task displays "Hello, world!" -->
<UsingTask
TaskName="HelloWorld"
TaskFactory="RoslynCodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" >
<ParameterGroup />
<Task>
<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>
<Import Project="HelloWorld.targets" />
<Target Name="Hello">
<HelloWorld />
</Target>
</Project>
Входные и выходные параметры
Встроенные параметры задачи являются дочерними элементами элемента ParameterGroup. Каждый параметр принимает имя элемента, определяющего его. Следующий код определяет параметр Text.
<ParameterGroup>
<Text />
</ParameterGroup>
Параметры могут иметь один или несколько следующих атрибутов:
-
Required— это необязательный атрибут, который включен по умолчаниюfalse. Еслиtrueпараметр является обязательным и должен быть задано значение перед вызовом задачи. -
ParameterType— это необязательный атрибут, который используется по умолчанию.System.StringЕго можно задать для любого полностью квалифицированного типа, который является элементом или значением, которое можно преобразовать в строку и обратно с помощью 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, то свойства автоматически создаются для каждого параметра. В противном случае свойства должны быть явно объявлены в исходном коде задачи и должны точно соответствовать их определениям параметров.
Отладка встроенной задачи
MSBuild создает исходный файл встроенной задачи и записывает выходные данные в текстовый файл с именем GUID в папке временных файлов AppData\Local\Temp\MSBuildTemp. Выходные данные обычно удаляются, но для сохранения этого выходного файла можно задать для переменной MSBUILDLOGCODETASKFACTORYOUTPUT среды значение 1.
Пример 1
Следующая встроенная задача заменяет каждое вхождение маркера в заданном файле заданным значением.
<Project>
<UsingTask TaskName="TokenReplace" TaskFactory="RoslynCodeTaskFactory" 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="Target.config" Token="$MyToken$" Replacement="MyValue"/>
</Target>
</Project>
Пример 2
Следующая встроенная задача создает сериализованные выходные данные. В этом примере показано использование выходного параметра и ссылки.
<Project>
<PropertyGroup>
<RoslynCodeTaskFactoryAssembly Condition="$(RoslynCodeTaskFactoryAssembly) == ''">$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll</RoslynCodeTaskFactoryAssembly>
</PropertyGroup>
<UsingTask
TaskName="MyInlineTask"
TaskFactory="RoslynCodeTaskFactory"
AssemblyFile="$(RoslynCodeTaskFactoryAssembly)">
<ParameterGroup>
<Input ParameterType="System.String" Required="true" />
<Output ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Text.Json" /> <!-- Reference an assembly -->
<Using Namespace="System.Text.Json" /> <!-- Use a namespace -->
<Code Type="Fragment" Language="cs">
<![CDATA[
Output = JsonSerializer.Serialize(new { Message = Input });
]]>
</Code>
</Task>
</UsingTask>
<Target Name="RunInlineTask">
<MyInlineTask Input="Hello, Roslyn!" >
<Output TaskParameter="Output" PropertyName="SerializedOutput" />
</MyInlineTask>
<Message Text="Serialized Output: $(SerializedOutput)" />
</Target>
</Project>