为 MSBuild 编写任务

任务包含在 MSBuild 目标中,并提供在生成过程中运行的代码。 MSBuild 包含典型任务的库,还可以创建自己的任务。 有关 MSBuild 包含的任务库的详细信息,请参阅 MSBuild 任务参考

先决条件

使用 MSBuild 生成的 Visual Studio 项目。

任务

MSBuild 任务的示例包括 Copy、复制一个或多个文件 、创建目录的 MakeDir 以及编译 C# 源代码文件的 Csc。 每个任务都实现为 .NET 类,该类实现 ITaskMicrosoft.Build.Framework.dll 程序集中定义的接口。

实现任务时,可以使用以下任一方法:

  • 直接实现 ITask 接口。

  • 从 Microsoft.Build.Utilities.dll 程序集中定义的帮助程序类 Task 中派生类Task 实现了 ITask 并提供了某些 ITask 成员的默认实现。 记录也更容易。

在这两种情况下,都必须向命名的类添加一个方法 Execute,该方法在任务运行时调用。 此方法不采用任何参数并返回 Boolean 值:如果任务成功,则返回 true;如果任务失败,则返回 false。 以下示例演示一个任务,该任务不执行任何作、成功完成并返回 true

using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace MyTasks
{
    public class SimpleTask : Task
    {
        public override bool Execute()
        {
            return true;
        }
    }
}

以下 MSBuild 项目文件运行前面的任务:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Target Name="MyTarget">
        <SimpleTask />
    </Target>
</Project>

任务运行时,如果在任务类上创建 .NET 属性,则它们还可以从项目文件接收输入。 MSBuild 在调用任务的 Execute 方法之前立即设置这些属性。 若要创建字符串属性,请使用任务代码,例如以下示例:

using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace MyTasks
{
    public class SimpleTask : Task
    {
        public override bool Execute()
        {
            return true;
        }

        public string MyProperty { get; set; }
    }
}

以下项目文件运行此任务,并将其设置为 MyProperty 给定值。

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <Target Name="MyTarget">
      <SimpleTask MyProperty="Value for MyProperty" />
   </Target>
</Project>

MSBuild 如何调用任务

当 MSBuild 调用任务时,它首先实例化任务类,然后为项目文件中的任务元素中设置的任务参数调用该对象的属性库。 如果任务元素未指定参数,或者元素中指定的表达式的计算结果为空字符串,则不会调用属性 setter。

例如,在以下项目中,只调用 Input3 的 setter。

<Project>
 <Target Name="InvokeCustomTask">
  <CustomTask Input1=""
              Input2="$(PropertyThatIsNotDefined)"
              Input3="value3" />
 </Target>
</Project>

任务不应依赖于参数属性 setter 调用的任何相对顺序。

任务参数类型

MSBuild 本机处理类型 stringboolITaskItemITaskItem[] 的属性。 如果一个任务接受不同类型的参数,MSBuild 会调用 ChangeTypestring 转换为目标类型,并扩展所有属性和项引用。 如果任何输入参数的转换失败,MSBuild 将发出错误,并且不调用任务 Execute() 的方法。

注册任务

若要运行任务,MSBuild 必须知道如何查找并运行包含任务类的程序集。 任务是使用 UsingTask 元素 (MSBuild) 注册的。

如果您的任务具有特定于运行时的依赖项,则必须通过指示其UsingTask元素中的ArchitectureRuntime属性来指示 MSBuild 在特定环境中运行该任务。 有关详细信息,请参阅 UsingTask 属性和任务参数

MSBuild 文件 Microsoft.Common.tasks 是一个项目文件,其中列出了 UsingTask 注册 MSBuild 提供的所有任务的元素。 当 MSBuild 生成任何项目时,将自动包含此文件。 如果在 Microsoft.Common.tasks 中注册的任务也在当前项目文件中注册,则当前项目文件优先,因此可以使用自己的同名任务替代默认任务。

提示

可以通过查看其 Microsoft.Common.tasks 文件的内容,查看随特定版本的 MSBuild 一起提供的任务列表。

要求设置任务属性

可以将某些任务属性标记为必需,因此运行任务的任何项目文件都必须设置这些属性的值或生成失败。 将 [Required] 属性应用于任务中的 .NET 属性,如下所示:

[Required]
public string RequiredProperty { get; set; }

[Required] 属性由 RequiredAttribute 命名空间中的 Microsoft.Build.Framework 定义。

从任务引发事件

如果您的任务派生自 Task 帮助程序类,则可以在 Task 类上使用以下任何帮助程序方法来引发所有已注册记录器捕获和显示的事件:

public override bool Execute()
{
    Log.LogError("messageResource1", "1", "2", "3");
    Log.LogWarning("messageResource2");
    Log.LogMessage(MessageImportance.High, "messageResource3");
    ...
}

如果任务直接实现 ITask ,仍可以引发此类事件,但必须使用 IBuildEngine 接口。 以下示例演示实现 ITask 并引发自定义事件的任务。

public class SimpleTask : ITask
{
    public IBuildEngine BuildEngine { get; set; }

    public override bool Execute()
    {
        TaskEventArgs taskEvent =
            new TaskEventArgs(BuildEventCategory.Custom,
            BuildEventImportance.High, "Important Message",
           "SimpleTask");
        BuildEngine.LogBuildEvent(taskEvent);
        return true;
    }
}

打包任务

分发任务的建议方法是在 NuGet 包中。 包需要捆绑所有依赖项。 有关指导你创建自定义任务的教程,请参阅 “创建 NuGet 包”。

示例 1

以下 C# 类演示从 Task 辅助类派生的任务。 此任务返回 true,指示它已成功。

using System;
using Microsoft.Build.Utilities;

namespace SimpleTask1
{
    public class SimpleTask1: Task
    {
        public override bool Execute()
        {
            // This is where the task would presumably do its work.
            return true;
        }
    }
}

示例 2

以下 C# 类演示实现 ITask 接口的任务。 此任务返回 true,指示它已成功。

using System;
using Microsoft.Build.Framework;

namespace SimpleTask2
{
    public class SimpleTask2: ITask
    {
        //When implementing the ITask interface, it's necessary to
        //implement a BuildEngine property of type
        //Microsoft.Build.Framework.IBuildEngine. This is done for
        //you if you derive from the Task class.
        public IBuildEngine BuildEngine { get; set; }

        // When implementing the ITask interface, it's necessary to
        // implement a HostObject property of type object.
        // This is done for you if you derive from the Task class.
        public object HostObject { get; set; }

        public bool Execute()
        {
            // This is where the task does its work.
            return true;
        }
    }
}

示例 3

此 C# 类演示一个从 Task 帮助器类派生的任务。 该任务具有必需的字符串属性,并触发一个事件,该事件由所有已注册的记录器显示。

using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace SimpleTask3
{
    public class SimpleTask3 : Task
    {
        private string myProperty;

        // The [Required] attribute indicates a required property.
        // If a project file invokes this task without passing a value
        // to this property, the build will fail immediately.
        [Required]
        public string MyProperty
        {
            get
            {
                return myProperty;
            }
            set
            {
                myProperty = value;
            }
        }

        public override bool Execute()
        {
            // Log a high-importance comment
            Log.LogMessage(MessageImportance.High,
                "The task was passed \"" + myProperty + "\".");
            return true;
        }
    }
}

示例 4

以下示例显示了调用上一个示例任务 SimpleTask3的项目文件。

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <UsingTask TaskName="SimpleTask3.SimpleTask3"
        AssemblyFile="SimpleTask3\bin\debug\simpletask3.dll"/>

    <Target Name="MyTarget">
        <SimpleTask3 MyProperty="Hello!"/>
    </Target>
</Project>