专栏首页walterlv - 吕毅的博客每次都要重新编译?太慢!让跨平台的 MSBuild/dotnet build 的 Target 支持差量编译

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

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

发布于 2018-05-14 07:46 更新于 2018-07-28 09:52

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

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


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

一个差量编译的例子

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

<Target Name="WalterlvDemo" BeforeTargets="CoreCompile"
        Inputs="$(MSBuildAllProjects);@(Compile)" Outputs="$(IntermediateOutputPath)Doubi.cs">
  <Exec Command="dotnet walterlv-tool.dll $(IntermediateOutputPath)Doubi.cs" />
</Target>

上述例子的作用是在编译期间执行一个名为 walterlv-tool.dll 的 .NET Core 应用,在命令执行结束之后,将生成一份新的代码文件 $(IntermediateOutputPath)Doubi.cs 并加入编译。

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

差量编译的关键

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

  • 当两者都没有指定时,MSBuild 会认定为此 Target 在每次编译时都会执行
  • 当两者都指定时,MSBuild 会认定为此 Target 需要进行差量执行
  • 不能只指定其中的一个而不指定另一个(MSBuild 直接会提示此 Target 没有正确指定 InputsOutputs

另外,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 都将重新执行。

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

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

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

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

<Target Name="WalterlvDemo" BeforeTargets="CoreCompile"
        Inputs="$(MSBuildAllProjects);@(Compile)" Outputs="$(IntermediateOutputPath)Doubi.cs">
  <Exec Command="dotnet walterlv-tool.dll $(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

本文会经常更新,请阅读原文: https://walterlv.com/post/msbuild-incremental-build.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 编写 Target 检测 MSBuild / dotnet build 此次编译是否是差量编译

    MSBuild 或 Roslyn 编译项目时均支持差量编译,毕竟为了性能。我在 每次都要重新编译?太慢!让跨平台的 MSBuild/dotnet bui...

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

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

    walterlv
  • 编写 MSBuild 内联编译任务(Task)用于获取当前编译环境下的所有编译目标(Target)

    我之前写过一些改变 MSBuild 编译过程的一些博客,包括利用 Microsoft.NET.Sdk 中各种自带的 Task 来执行各种各样的编...

    walterlv
  • 【六一儿童节】回忆一下“孩子们的游戏”!(码农版)

    今天,我们来回忆一下程序员版的儿童小游戏。六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。DL作为牛客的资深元老,自然也准备了一些小游戏。...

    深度学习技术前沿公众号博主
  • Vijos P1113 不高兴的津津【模拟】

    不高兴的津津 描述 津津上初中了。妈妈认为津津应该更加用功学习,所以津津除了上学之外,还要参加妈妈为她报名的各科复习班。另外每周妈妈还会送她去学习朗诵、舞蹈和钢...

    Angel_Kitty
  • C#版(打败97.89%的提交) - Leetcode 202. 快乐数 - 题解

    Leetcode 202.Happy Number 在线提交: https://leetcode-cn.com/problems/happy-number/...

    Enjoy233
  • 微信跳一跳技术手段高分秘籍:不仅可以用 Python 刷分,竟然还可以直接改分

    如果你每次都能挑到各自的正中间的话,可以 + 2 分,如果连着跳到中间会 + 4、+6、+8、+10…… 跳到污水井盖上面,停留 2 秒,等到下水道声音响起直接...

    企鹅号小编
  • 剑指offer No.4 重建二叉树

    输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6...

    week
  • .NET Core 3.0之深入源码理解Kestrel的集成与应用(二)

    前一篇文章主要介绍了.NET Core继承Kestrel的目的、运行方式以及相关的使用,接下来将进一步从源码角度探讨.NET Core 3.0中关于Kestre...

    Edison.Ma
  • Win7无法设置背景图片的快速解决办法

    不知道怎么回事,win7电脑突然连个性化设置背景图片的按钮都没了。真操蛋~~~满屏的黑色背景图案,看着实在是不爽。

    跟着阿笨一起玩NET

扫码关注云+社区

领取腾讯云代金券