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元素列出要访问的命名空间。 此元素类似于usingVisual C# 中的指令。 该Namespace属性指定要包含的命名空间。
Reference 元素 Using 与语言无关。 内联任务可以用任何一种受支持的 .NET CodeDom 语言(例如 Visual Basic 或 Visual C#)编写。
注释
元素包含的 Task 元素特定于任务工厂,在本例中为代码任务工厂。
Code 元素
要出现在元素中的 Task 最后一个子元素是 Code 元素。 该 Code 元素包含或查找要编译到任务中的代码。 在元素中 Code 放置的内容取决于要编写任务的方式。
该 Language 属性指定编写代码的语言。 可接受的值为 cs C#, vb 对于 Visual Basic。
该 Type 特性指定元素 Code 中找到的代码类型。
如果值为
< a0/>,则 元素包含派生自接口的 类的代码。 如果值为 <
a0/>,则代码定义接口方法的 重写 。 如果值为,
TypeFragment则代码定义方法的内容Execute,而不是签名或return语句。
代码本身通常出现在标记和]]>标记之间<![CDATA[。 由于代码位于 CDATA 部分中,因此无需担心转义保留字符,例如“”<或“>”。
或者,可以使用 Source 元素的属性 Code 来指定包含任务代码的文件的位置。 源文件中的代码必须是由 Type 特性指定的类型。
Source如果该属性存在,则默认值为 TypeClass. 如果 Source 不存在,则默认值为 Fragment。
注释
如果在源文件中定义任务类,类名必须与相应的 UsingTask 元素的属性一致TaskName。
Hello World
下面是一个更可靠的内联任务 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>