.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
文件。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具