当前位置: 首页 > news >正文

通过重写预定义的 Target 来扩展 MSBuild / Visual Studio 的编译过程

MSBuild 的编译过程提供了一些可以被重写的 Target,通过重写这些 Target 可以扩展 MSBuild 的编译过程。


本文内容

      • 重写预定义的 Target
        • `BeforeCompile`, `AfterCompile`
        • `BeforeBuild`, `AfterBuild`
        • `BeforeRebuild`, `AfterRebuild`
        • `BeforeClean`, `AfterClean`
        • `BeforePublish`, `AfterPublish`
        • `BeforeResolveReference`, `AfterResolveReferences`
        • `BeforeResGen`, `AfterResGen`
      • 通过改写 DependsOn 的值扩展编译
        • 参考资料

重写预定义的 Target

有这些预定义的 Target 可以重写:

  • BeforeCompile, AfterCompile
  • BeforeBuild, AfterBuild
  • BeforeRebuild, AfterRebuild
  • BeforeClean, AfterClean
  • BeforePublish, AfterPublish
  • BeforeResolveReference, AfterResolveReferences
  • BeforeResGen, AfterResGen

你可以在 Microsoft.NET.Sdk 中找到各种富有创意的 Target 用来扩展,以上这些也是 Microsoft.NET.Sdk 的一部分,在那个文件夹的 Microsoft.Common.targets 或者 Microsoft.Common.CurrentVersion.targets 中。

而写法是这样的:

<Project>
    ...
    <Target Name="BeforeResGen">
        <!-- 这里可以写在生成资源之前执行的 Task 或者修改属性和集合。 -->
    </Target>
    <Target Name="AfterCompile">
        <!-- 这里可以写在 C# 文件以及各种资源文件编译之后执行的 Task 或者修改属性和集合。 -->
    </Target>
</Project>

是的,相比于你全新定义一个 Target 来说,你不需要去写 BeforeTargets 或者 AfterTargets。

那么以上那些 Target 都是什么时机呢?

BeforeCompile, AfterCompile

在 C# 文件以及各种资源文件被编译成 dll 的之前或之后执行。你可以在之前执行以便修改要编译的 C# 文件或者资源文件,你也可以在编译之后做一些其他的操作。

由于我们可以在 BeforeCompile 这个时机修改源码,所以我们很多关于代码级别的重新定义都可以在这个时机去完成。

BeforeBuild, AfterBuild

在整个编译之前或者之后执行。对于普通的编译来说,一般来说不会有比 BeforeBuild 更前以及比 AfterBuild 更后的时机了,不过如果有其他 Import 进来的 Target 或者通过 NuGet 自动引入进来的其他 Target 也使用了类似这样的时机,那么你就不一定比他们更靠前或者靠后。

BeforeRebuild, AfterRebuild

如果编译时采用了 /t:Rebuild 方案,也就是重新编译,那么 BeforeRebuild 和 AfterRebuild 就会被触发。一旦触发,会比前面更加提前和靠后。

执行顺序为:BeforeRebuild -> Clean -> Build -> AfterRebuild

BeforeClean, AfterClean

在清理开始和结束时执行。如果是重新编译,那么也会有 Clean 的过程。顺序见上面。

BeforePublish, AfterPublish

在发布之前执行和发布之后执行。对应到 Visual Studio 右键菜单中的发布按钮。

BeforeResolveReference, AfterResolveReferences

在程序集的引用被解析之前和之后执行。你可以通过重写这两个时机的 Target 来修改程序集的引用关系或者利用引用执行一些其他操作。

BeforeResGen, AfterResGen

在资源被生成之前和之后执行。

通过改写 DependsOn 的值扩展编译

有这些预定义的 DependsOn 可以改写:

  • BuildDependsOn
  • CleanDependsOn
  • CompileDependsOn

这几个属性的时机跟上面是一样的,你可以直接通过阅读上面一节中对应名字的 Target 的解释来获得这几个属性所对应的时机。

而这几个属性影响编译过程的写法是这样的:

<PropertyGroup>
    <BuildDependsOn>WalterlvDemoTarget1;$(BuildDependsOn);WalterlvDemoTarget1</BuildDependsOn>
</PropertyGroup>
<Target Name="WalterlvDemoTarget1">  
    <Message Text="正在运行 WalterlvDemoTarget1……"/>  
</Target>  
<Target Name="WalterlvDemoTarget1">  
    <Message Text="正在运行 WalterlvDemoTarget2……"/>  
</Target>

更推荐使用 DependsOn 属性的改写而不是像本文第一节那样直接重写 Target,是因为一个 Target 的重写很容易被不同的开发小伙伴覆盖。比如一个小伙伴在一处代码里面写了一个 Target,但另一个小伙伴不知道,在另一个地方也写了相同名字的 Target,那么这两个 Target 也会相互覆盖,导致其中的一个失效。

虽然同名的属性跟 Target 一样的会被覆盖,但是我们可以通过在改写属性的值的时候同时获取这个属性之前设置的值,可以把以前的值保留下来。

正如上面的例子那样,我们通过写了两个新的 Target 的名字,分别叠加到 $(BuildDependsOn) 这个属性原有值的两边,使得我们可以在编译前后执行两个不同的 Target。如果有其他的小伙伴使用了相同的方式去改写这个属性的值,那么它获取原有值的时候就会把这里已经赋过的值放入到它新的值的中间。也就是说,一个也不会丢。


参考资料

  • Extend the build process - Visual Studio - Microsoft Docs
  • c# - Determine if MSBuild CoreCompile will run and call custom target - Stack Overflow

我的博客会首发于 https://walterlv.com/,而 CSDN 和博客园仅从其中摘选发布,而且一旦发布了就不再更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://blog.csdn.net/wpwalter),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

相关文章:

  • .NET 中 GetHashCode 的哈希值有多大概率会相同(哈希碰撞)
  • C# 中委托实例的命名规则
  • 在 Target 中获取项目引用的所有依赖(dll/NuGet/Project)的路径
  • 让 MSBuild Target 支持 Clean
  • C#/.NET 如何确认一个路径是否是合法的文件路径
  • 不使用 U 盘等任何工具全新安装 Windows 操作系统
  • C# 永远不会返回的方法真的不会返回
  • CentOS 的终端中如何搜索文件
  • 如何在命令行中监听用户输入文本的改变?
  • 使用 Xamarin 开发 iOS 键盘扩展(含网络访问)
  • 使用 Xamarin 开发 iOS 应用中需要注意的若干个问题
  • .NET/C# 编译期间能确定的相同字符串,在运行期间是相同的实例
  • 使用 Xamarin 在 iOS 真机上部署应用进行调试
  • 在 Snoop 中使用 PowerShell 脚本进行更高级的 UI 调试
  • WPF 支持的多线程 UI 并不是线程安全的
  • 【译】React性能工程(下) -- 深入研究React性能调试
  • C语言笔记(第一章:C语言编程)
  • iOS筛选菜单、分段选择器、导航栏、悬浮窗、转场动画、启动视频等源码
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • Java 多线程编程之:notify 和 wait 用法
  • Js基础知识(四) - js运行原理与机制
  • k8s如何管理Pod
  • OSS Web直传 (文件图片)
  • Xmanager 远程桌面 CentOS 7
  • zookeeper系列(七)实战分布式命名服务
  • 关于extract.autodesk.io的一些说明
  • 看完九篇字体系列的文章,你还觉得我是在说字体?
  • 力扣(LeetCode)965
  • 前端每日实战:61# 视频演示如何用纯 CSS 创作一只咖啡壶
  • 前端之React实战:创建跨平台的项目架构
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • ​力扣解法汇总1802. 有界数组中指定下标处的最大值
  • #14vue3生成表单并跳转到外部地址的方式
  • (Python) SOAP Web Service (HTTP POST)
  • (ZT)北大教授朱青生给学生的一封信:大学,更是一个科学的保证
  • (超详细)语音信号处理之特征提取
  • (附表设计)不是我吹!超级全面的权限系统设计方案面世了
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (附源码)ssm高校升本考试管理系统 毕业设计 201631
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (一)spring cloud微服务分布式云架构 - Spring Cloud简介
  • (原创)攻击方式学习之(4) - 拒绝服务(DOS/DDOS/DRDOS)
  • (原創) 人會胖會瘦,都是自我要求的結果 (日記)
  • (原創) 系統分析和系統設計有什麼差別? (OO)
  • (转)http协议
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • .helper勒索病毒的最新威胁:如何恢复您的数据?
  • .java 9 找不到符号_java找不到符号
  • .net Application的目录
  • .net Signalr 使用笔记
  • .net 写了一个支持重试、熔断和超时策略的 HttpClient 实例池
  • .NET/C# 反射的的性能数据,以及高性能开发建议(反射获取 Attribute 和反射调用方法)
  • .NET成年了,然后呢?
  • .NET开发不可不知、不可不用的辅助类(三)(报表导出---终结版)
  • .vollhavhelp-V-XXXXXXXX勒索病毒的最新威胁:如何恢复您的数据?