.NET 在编译时执行其他任务

为什么我会写这篇文档,因为我在看官方文档过程中,踩了一些坑,怎么都实现不了!
所以在这里总结一下,避免跟我一样的小白遇到一些坑。

如何运行我们的任务
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net5.0-windows</TargetFramework>
    <UseWPF>true</UseWPF>
  </PropertyGroup>

  <!-- 如何在编译过程中执行这个任务呢? -->
  <Target Name="MyTask">
	  <MakeDir Directories="HelloWorld"></MakeDir>
  </Target>

</Project>

上述的项目配置文件中,是不会允许 MyTask 这个任务的,那我们如何让它执行起来呢?
有三种办法:

  • Project 元素中指定 InitialTargets 属性值为 MyTask
  • Project 元素中指定 DefaultTargets 属性值为 MyTask
  • 使用命令编译 msbuild -target:MyTask

以上三种办法都可以让这个命令允许起来,当然还有其他办法,就是指定MyTask任务在编译任务完成之后执行,这样也是可以执行的。
不过这里有个坑?
如果你是在 Visual Studio 2022 中编码,当 InitialTargets 设置值以后为什么会一直执行 MyTask 任务,这是 VS 每隔一段时间编译一下项目,如果不这样
的话就不能实时的检查我们项目配置是否出错,所以才会导致 InitialTargets 设置的任务一直会被执行。

更多信息需要参考 Target 的生成顺序:
https://docs.microsoft.com/zh-cn/visualstudio/msbuild/target-build-order?view=vs-2022

如何编写我们自己的任务

新建一个类库,生成一个类型

using Microsoft.Build.Utilities;
using System;
using System.IO;

namespace CustomTask
{
    public class CreateFilesTask : Task
    {
        public override bool Execute()
        {
            File.Create(Path.Combine(Environment.CurrentDirectory, Path.ChangeExtension(Path.GetRandomFileName(), "txt")));
            Console.WriteLine("创建文件成功!");
            return true;
        }
    }
}

这里有一个坑,就是类库项目的目标框架一定要兼容 MSBuild 版本,否则在其他项目中使用任务时会一直报错找不到 运行时
具体要看程序集 using Microsoft.Build.Utilities.Core 的依赖,以及 Msbuild 版本是否兼容。

在我的这个测试例程中类库的项目文件为

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Library</OutputType>

    <!-- 这里很重要,我试过切换成 net472,net5.0-windows,net48等等各种框架,在其他项目中使用任务时就一直报错找不到 net5.0 的运行时(怎么可能,我电脑本来就装了 .NET5 环境运行时的),当时我的一脸懵逼!百思不得其解
    后来,水哥跟我说,可能是 TargetFramework 版本与 MSBuild 不兼容的问题,叫我切换成 standard ,然后就可以运行了。 我离成功就差了那么一点点,哎!-->
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

  <!-- ITask, Task 这两个类所在的程序集,Task 实现了 ITask,它并不是 Threading 中的 Task  哦! -->
  <ItemGroup>
    <PackageReference Include="Microsoft.Build.Framework" Version="17.0.0" />
    <PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.0.0" />
  </ItemGroup>

</Project>
至于为什么会出现以上找不到运行时的问题呢?

微软MVP林德熙跟我说,具体看你的 TargetFramework 版本的问题,以及你使用的编译命令:

MSBuild
Dotnet MSBuild 

这两个命令是有区别的,

  • dotnet msbuild 命令允许访问功能完备的 MSBuild。该命令与仅适用于 SDK 样式项目的现有 MSBuild 命令行客户端具有完全相同的功能。
  • 至于 msbuild 命令,也是菜鸡,正在读文档呢。
    总而言之,如果 msbuild 编译命令不通过,那么就换成 dotnet msbuild。

如果需要在任务中定义我们自己的属性,需要使用 Required 标记属性,例如

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

namespace CustomTask
{
    public class CreateFilesTask : Task
    {
        // 使用特性标记的属性,这样就可以在 XML 中去设置它。
        [Required]
        public string FileName { get; set; }

        public override bool Execute()
        {
            if (string.IsNullOrWhiteSpace(FileName))
            {
                File.Create(Path.Combine(Environment.CurrentDirectory, Path.ChangeExtension(Path.GetRandomFileName(), "txt")));
            }
            else
            {
                if (Path.HasExtension(FileName))
                {
                    File.Create(Path.Combine(Environment.CurrentDirectory, FileName));
                }
                else
                {
                    File.Create(Path.Combine(Environment.CurrentDirectory, Path.ChangeExtension(FileName, "txt")));
                }
            }

            Console.WriteLine("创建文件成功!");
            return true;
        }
    }
}

至此,我们的任务类库就搭建好了!

接下来,随便创建要给Demo,项目配置如下:

 <!-- DefaultTargets="MyTask" 指定默认执行的目标,当然你也可用命令行 msbuild -target:MyTask 执行该任务 -->
<Project Sdk="Microsoft.NET.Sdk" DefaultTargets="MyTask">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net5.0-windows</TargetFramework>
    <UseWPF>true</UseWPF>
  </PropertyGroup>

  <!--
      https://docs.microsoft.com/zh-cn/visualstudio/msbuild/task-writing?view=vs-2022 任务写入链接
      https://docs.microsoft.com/zh-cn/visualstudio/msbuild/taskbody-element-msbuild?view=vs-2022 UsingTask元素链接
  -->
  <!--将 Task 元素中引用的任务映射到包含该任务实现的程序集。-->
  <UsingTask AssemblyFile="D:\Temp\CompilerTest\CustomTask\bin\Debug\netstandard2.0\CustomTask.dll" 
             TaskName="CustomTask.CreateFilesTask" />

  <ItemGroup>
    <PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.0.0" />
  </ItemGroup>

  <!--<Target Name="MyTask">
	  <MakeDir Directories="HelloWorld"></MakeDir>
  </Target>-->
  
  <!-- 定义我们需要执行的目标 -->
  <Target Name="CreateNewDir">
    <MakeDir Directories="newdir" />
  </Target>

</Project>

至此,编译项目,此时会在项目下随机的创建一个文本文件。

结束语

这篇文章的初衷是,虽然我按照官方文档的教程进行操作,但是还是实现不了官方文档中的效果,所以在这里总结了一些踩的坑,避免下一个人继续踩我踩过的坑。
更多的链接,请看
任务写入,如何自定义一个任务:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/task-writing?view=vs-2022
msbuild 自带的目标可以在这篇文章里查看,在默认生成目标处:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/msbuild-targets?view=vs-2022
目标生成顺序:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/target-build-order?view=vs-2022
msbuild 项目文件架构引用,描述的是 msbuild 中用到的元素,比如 Item,ItemGroup,UsingTask 等等:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/msbuild-project-file-schema-reference?view=vs-2022
任务类的链接,比如 Csc,Delete,MakeDir等等等:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/msbuild-task-reference?view=vs-2022

最后在附加一个例子吧,当作练手

<Project Sdk="Microsoft.NET.Sdk" DefaultTargets="CreateNewDir">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net5.0-windows</TargetFramework>
    <UseWPF>true</UseWPF>
  </PropertyGroup>

  <UsingTask AssemblyFile="D:\Temp\CompilerTest\CustomTask\bin\Debug\netstandard2.0\CustomTask.dll" TaskName="CustomTask.CreateFilesTask" />

  <ItemGroup>
    <PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.0.0" />
  </ItemGroup>

  <Target Name="MyTask" AfterTargets="CreateNewDir">
	  <MakeDir Directories="TempDirectory"></MakeDir>
  </Target>

  <Target Name="CreateNewDir">
	  <CreateFilesTask FileName="HelloWorld" />
  </Target>

</Project>

这个例子执行完 CreateNewDir 任务之后回去执行 MyTask 任务,此时目录下会创建一个 TempDirectory 空目录和一个 HelloWorld.txt 文件。

posted @   RafaelLxf  阅读(95)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示