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

每次都要重新编译?太慢!让跨平台的 MSBuild/dotnet build 的 Target 支持差量编译

如果你干预到了项目的编译过程,可能就需要考虑到差量编译了。不然——当你的项目大起来的时候,就会感受到每次都重新编译时,每次重复调试的过程都要进行漫长等待时的绝望和无奈。

如果你正遭遇差量编译失效,每次都要重新编译的问题,那么阅读本文应该能够帮助你解决问题。


msbuild.exedotnet build 编译项目的方式是一样的,只不过前者使用完整的 .NET Framework,而后者使用 .NET Core。所以后面我们说到 Target 的差量编译的时候,就不再区分这两者了。

一个差量编译的例子

先看一个 Target 的例子,这里例子来源于我的另一篇文章如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包 - 吕毅。在例子中,我没有加入任何的差量编译支持。

<Target Name="WalterlvDemo" BeforeTargets="CoreCompile">
  <DemoTool IntermediateOutputPath="$(IntermediateOutputPath)">
    <Output TaskParameter="AdditionalCompileFile" PropertyName="WalterlvDemo_AdditionalCompileFile" />
  </DemoTool>

  <ItemGroup>
    <Compile Include="$(WalterlvDemo_AdditionalCompileFile)" />
  </ItemGroup>
</Target>

上述例子的作用是在编译期间执行一个名为 DemoToolTask,在 Task 执行结束之后,将生成的临时文件 $(WalterlvDemo_AdditionalCompileFile) 加入编译。

如果你觉得上面的写法非常陌生,或者说不清楚那个 Target 节点的作用,建议先阅读:

  • 理解 C# 项目 csproj 文件格式的本质和编译流程 - 吕毅
  • 如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包 - 吕毅

差量编译的关键

每一个 Target 都有 InputsOutputs 属性,可以设置,也可以不用设置。

当没有指定时,MSBuild 会认定为此 Target 在每次编译时都会执行;当指定时,MSBuild 会认定为此 Target 需要进行差量执行。不存在只指定其中一个而不指定另一个的情况——MSBuild 直接会提示此 Target 没有正确指定 InputsOutputs

InputsOutputs 的格式都是一组用 ; 分隔的字符串,每一项都是一个文件的路径。不过不用特别考虑如何使用 ; 拼接,因为当我们使用 @ 符号时,收集到的每一项便是使用 ; 分隔的。例如 @(Compile) 表示在 <ItemGroup> 中每一个 Compile 类型的节点。如果不清楚 <ItemGroup><Compile> 的作用,建议建议先阅读理解 C# 项目 csproj 文件格式的本质和编译流程 - 吕毅。

假设我们指定 Inputs@(Compile)Outputs 指定为某个 xxx.exe 生成的临时文件的位置(在 如何创建一个基于命令行工具的跨平台的 NuGet 工具包 一文中,我假定为了 $(IntermediateOutputPath)Doubi.cs),那么 MSBuild 就会在执行此 Target 之前检查所有这些输入输出文件。如果所有 <Compile> 节点中对应的文件都没有改变,而且 $(IntermediateOutputPath)Doubi.cs 存在且没改变,那么此 Target 将不需要执行。任何一个文件不满足此条件,则 Target 都将重新执行。

现在,回到我们刚开始的例子,你觉得如何设置 InputsOutputs 可以获得最佳的差量编译效果呢?答案是——Inputs 设置为空字符串(因为我们没有输入文件),Outputs 设置为 $(WalterlvDemo_AdditionalCompileFile) 应该有的值(特别注意:我指的是这个属性对应的值,而不是属性本身——因为此属性的值在编译之前不能确认,也就无法进行差量分析)。

不是所有的 Target 都适合差量编译

注意!不是所有的 Target 都适合设置 InputsOutputs 属性

在本文前面的例子中,我们的 Target 是有明确的输入和输出文件的;然而有些 Target 是没有输入输出文件的——他们的输出依赖于其他 Target 的输出。

例如我们有另一个 <Target>,它的作用是生成一个属性的值,或者一组文件的名字;而另外一个 <Target> 使用这个属性的值和这组文件。典型的例子如我在如何创建一个基于命令行工具的跨平台的 NuGet 工具包 中写的那个 NuGet 工具。

<Target Name="WalterlvDemo" BeforeTargets="CoreCompile">
  <Exec Command="dotnet $(NuGetWalterlvToolPath) -i $(IntermediateOutputPath)Doubi.cs" />
</Target>

<Target Name="WalterlvDemoUseResult" AfterTargets="WalterlvDemo" BeforeTargets="CoreCompile">
  <ItemGroup>
    <Compile Include="$(IntermediateOutputPath)Doubi.cs" />
  </ItemGroup>
</Target>

WalterlvDemo 生成文件,而 WalterlvDemoUseResult 使用文件。这时,WalterlvDemo 适合使用差量编译,而 WalterlvDemoUseResult 却不适合!

因为前者已经生成了文件,如果不执行,文件依然存在;但后者一旦不执行,那么我们就会少一个编译的文件。这将导致后续名为 CoreCompile 的 Target 执行时,发现少了一个文件,将重新执行编译。

所以前者的 Inputs 指定为空字符串,Outputs 指定为 $(IntermediateOutputPath)Doubi.cs;但是后者不应该指定 InputsOutputs

相关文章:

  • C# 中那些可以被重载的操作符,以及使用它们的那些丧心病狂的语法糖
  • 神器如 dnSpy,无需源码也能修改 .NET 程序
  • Roslyn 入门:使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码
  • .NET Standard / dotnet-core / net472 —— .NET 究竟应该如何大小写?
  • 微软 Windows 系统检测网络连通性(用于显示感叹号)竟然是通过访问一个特殊网址来实现的
  • 如何根据一个绝对文件路径生成一个相对文件路径
  • 如何使用 MSBuild Target(Exec)中的控制台输出
  • C#/.NET 中推荐的 Dispose 模式的实现
  • Windows 10 四月更新,文件夹名称也能区分大小写?
  • WPF 中使用附加属性,将任意 UI 元素或控件裁剪成圆形(椭圆)
  • XML 的 XPath 语法
  • .NET 使用 XPath 来读写 XML 文件
  • 像黑客一样!Chrome 完全键盘操作指南(原生快捷键 + Vimium 插件)
  • 如何在 .NET 库的代码中判断当前程序运行在 Debug 下还是 Release 下
  • 在制作跨平台的 NuGet 工具包时,如何将工具(exe/dll)的所有依赖一并放入包中
  • __proto__ 和 prototype的关系
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • classpath对获取配置文件的影响
  • Java 内存分配及垃圾回收机制初探
  • Node 版本管理
  • Shell编程
  • weex踩坑之旅第一弹 ~ 搭建具有入口文件的weex脚手架
  • 工作中总结前端开发流程--vue项目
  • 浅谈Golang中select的用法
  • 通过来模仿稀土掘金个人页面的布局来学习使用CoordinatorLayout
  • 网络应用优化——时延与带宽
  • 微信小程序实战练习(仿五洲到家微信版)
  • 文本多行溢出显示...之最后一行不到行尾的解决
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 学习HTTP相关知识笔记
  • python最赚钱的4个方向,你最心动的是哪个?
  • 回归生活:清理微信公众号
  • ​学习一下,什么是预包装食品?​
  • #define与typedef区别
  • (cljs/run-at (JSVM. :browser) 搭建刚好可用的开发环境!)
  • (笔试题)分解质因式
  • (六)c52学习之旅-独立按键
  • (原创)Stanford Machine Learning (by Andrew NG) --- (week 9) Anomaly DetectionRecommender Systems...
  • (转)关于pipe()的详细解析
  • .net core 客户端缓存、服务器端响应缓存、服务器内存缓存
  • .NET Framework 3.5中序列化成JSON数据及JSON数据的反序列化,以及jQuery的调用JSON
  • .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)
  • .net6解除文件上传限制。Multipart body length limit 16384 exceeded
  • .NET开发人员必知的八个网站
  • .NET微信公众号开发-2.0创建自定义菜单
  • [ IO.File ] FileSystemWatcher
  • [ vulhub漏洞复现篇 ] Jetty WEB-INF 文件读取复现CVE-2021-34429
  • [20160807][系统设计的三次迭代]
  • [20161214]如何确定dbid.txt
  • [BZOJ 1040] 骑士
  • [C/C++]_[初级]_[关于编译时出现有符号-无符号不匹配的警告-sizeof使用注意事项]
  • [C++]运行时,如何确保一个对象是只读的
  • [codeforces]Levko and Permutation
  • [CSAWQual 2019]Web_Unagi ---不会编程的崽
  • [CVPR2021]Birds of a Feather: Capturing Avian Shape Models from Images