Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
RoslynCodeTaskFactory использует кроссплатформенные компиляторы Roslyn для создания сборок задач в памяти для использования в качестве встроенных задач.
RoslynCodeTaskFactory задачи предназначены для .NET Standard и могут работать в средах выполнения .NET Framework и .NET Core, а также на других платформах, таких как Linux и macOS.
Замечание
RoslynCodeTaskFactory доступен только в MSBuild 15.8 и выше. Версии MSBuild следуют версиям Visual Studio, поэтому RoslynCodeTaskFactory доступна в Visual Studio 2017 версии 15.8 и выше.
Структура встроенной задачи с Помощью RoslynCodeTaskFactory
RoslynCodeTaskFactory Встроенные задачи объявляются с помощью UsingTask элемента. Встроенная задача и UsingTask элемент, содержащий его, обычно включаются в файл и импортируются в .targets другие файлы проекта по мере необходимости. Ниже приведена базовая встроенная задача. Обратите внимание, что это ничего не делает.
<Project>
<!-- 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>
</Project>
Элемент UsingTask в примере содержит три атрибута, описывающие задачу и встроенную фабрику задач, которая компилирует ее.
Атрибут
TaskNameназывает задачу в данном случаеDoNothing.Атрибут
TaskFactoryименует класс, реализующий встроенную фабрику задач.Атрибут
AssemblyFileпредоставляет расположение встроенной фабрики задач. Кроме того, атрибут можно использоватьAssemblyNameдля указания полного имени класса встроенной фабрики задач, который обычно находится в глобальном кэше сборок (GAC).
Остальные элементы 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, вам не нужно беспокоиться о выходе зарезервированных символов, например "<" или ">".
Кроме того, атрибут элемента можно использовать SourceCode для указания расположения файла, содержащего код для задачи. Код в исходном файле должен иметь тип, указанный атрибутом Type .
Source Если атрибут присутствует, значение Type по умолчанию равноClass. Если Source он отсутствует, значение по умолчанию равно Fragment.
Замечание
Если вы определяете класс задач в исходном файле, имя класса должно согласиться с TaskName атрибутом соответствующего элемента UsingTask .
Всем привет
Ниже приведена более надежная встроенная задача.RoslynCodeTaskFactory Задача HelloWorld отображает "Hello, world!" на устройстве ведения журнала ошибок по умолчанию, которое обычно является системной консолью или окном вывода Visual Studio. Элемент Reference в примере включается только для иллюстрации.
<Project>
<!-- This simple inline task displays "Hello, world!" -->
<UsingTask
TaskName="HelloWorld"
TaskFactory="RoslynCodeTaskFactory"
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>
<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, то свойства автоматически создаются для каждого параметра. В RoslynCodeTaskFactory, если Code элемент имеет Type атрибут Class, то не нужно указывать ParameterGroupзначение , так как он выводится из исходного кода (это разница от CodeTaskFactory). В противном случае свойства должны быть явно объявлены в исходном коде задачи и должны точно соответствовать их определениям параметров.
Пример
Следующие встроенные журналы задач записывают некоторые сообщения и возвращают строку.
<Project>
<UsingTask TaskName="MySample"
TaskFactory="RoslynCodeTaskFactory"
AssemblyFile="$(MSBuildBinPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<Parameter1 ParameterType="System.String" Required="true" />
<Parameter2 ParameterType="System.String" />
<Parameter3 ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<Using Namespace="System" />
<Code Type="Fragment" Language="cs">
<![CDATA[
Log.LogMessage(MessageImportance.High, "Hello from an inline task created by Roslyn!");
Log.LogMessageFromText($"Parameter1: '{Parameter1}'", MessageImportance.High);
Log.LogMessageFromText($"Parameter2: '{Parameter2}'", MessageImportance.High);
Parameter3 = "A value from the Roslyn CodeTaskFactory";
]]>
</Code>
</Task>
</UsingTask>
<Target Name="Demo">
<MySample Parameter1="A value for parameter 1" Parameter2="A value for parameter 2">
<Output TaskParameter="Parameter3" PropertyName="NewProperty" />
</MySample>
<Message Text="NewProperty: '$(NewProperty)'" />
</Target>
</Project>
Эти встроенные задачи могут объединять пути и получать имя файла.
<Project>
<UsingTask TaskName="PathCombine"
TaskFactory="RoslynCodeTaskFactory"
AssemblyFile="$(MSBuildBinPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<Paths ParameterType="System.String[]" Required="true" />
<Combined ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<Using Namespace="System" />
<Code Type="Fragment" Language="cs">
<![CDATA[
Combined = Path.Combine(Paths);
]]>
</Code>
</Task>
</UsingTask>
<UsingTask TaskName="PathGetFileName"
TaskFactory="RoslynCodeTaskFactory"
AssemblyFile="$(MSBuildBinPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<Path ParameterType="System.String" Required="true" />
<FileName ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<Using Namespace="System" />
<Code Type="Fragment" Language="cs">
<![CDATA[
FileName = System.IO.Path.GetFileName(Path);
]]>
</Code>
</Task>
</UsingTask>
<Target Name="Demo">
<PathCombine Paths="$(Temp);MyFolder;$([System.Guid]::NewGuid()).txt">
<Output TaskParameter="Combined" PropertyName="MyCombinedPaths" />
</PathCombine>
<Message Text="Combined Paths: '$(MyCombinedPaths)'" />
<PathGetFileName Path="$(MyCombinedPaths)">
<Output TaskParameter="FileName" PropertyName="MyFileName" />
</PathGetFileName>
<Message Text="File name: '$(MyFileName)'" />
</Target>
</Project>
Обеспечение обратной совместимости
RoslynCodeTaskFactory впервые стало доступно в MSBuild версии 15.8. Предположим, вы хотите поддерживать предыдущие версии Visual Studio и MSBuild, если RoslynCodeTaskFactory она недоступна, но CodeTaskFactory вы хотите использовать тот же скрипт сборки. Можно использовать Choose конструкцию, которая использует $(MSBuildVersion) свойство для выбора во время сборки, следует ли использовать RoslynCodeTaskFactory или вернуться CodeTaskFactoryобратно, как показано в следующем примере:
<Project Sdk="Microsoft.NET.Sdk" DefaultTargets="RunTask">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<Choose>
<When Condition=" '$(MSBuildVersion.Substring(0,2))' >= 16 Or
('$(MSBuildVersion.Substring(0,2))' == 15 And '$(MSBuildVersion.Substring(3,1))' >= 8)">
<PropertyGroup>
<TaskFactory>RoslynCodeTaskFactory</TaskFactory>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup>
<TaskFactory>CodeTaskFactory</TaskFactory>
</PropertyGroup>
</Otherwise>
</Choose>
<UsingTask
TaskName="HelloWorld"
TaskFactory="$(TaskFactory)"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup />
<Task>
<Using Namespace="System"/>
<Using Namespace="System.IO"/>
<Code Type="Fragment" Language="cs">
<![CDATA[
Log.LogError("Using RoslynCodeTaskFactory");
]]>
</Code>
</Task>
</UsingTask>
<Target Name="RunTask" AfterTargets="Build">
<Message Text="MSBuildVersion: $(MSBuildVersion)"/>
<Message Text="TaskFactory: $(TaskFactory)"/>
<HelloWorld />
</Target>
</Project>