Msbuild CoreCompile 依赖于目标

5
这是我们目前的情况:ccnet + tfs + msbuild。
问题在于,由于有很多项目,我们的构建过程太慢了。
我进行了一些调查,这就是我想做的事情。
每个项目编译完成后,它都会从输出文件夹复制到共享文件夹中。即使这个项目没有被编译,也会执行此操作。因此,我检查了msbuild的详细日志,并发现了以下信息:“Skipping target "CoreCompile" because all output files are up-to-date with respect to the input files.”。
这个验证操作在Microsoft.TeamFoundation.Build.targets中声明。问题是如何拦截这个信息,创建自己的目标,添加一些条件,并触发某种操作,如果出现“Skipping target "CoreCompile" message”,则不运行我的复制目标并跳过它,如果项目已经编译,则应该运行此复制目标。
1个回答

6
我刚刚在http://sedodream.com/2012/05/14/MSBuildHowToExecuteATargetAfterCoreCompile.aspx上发布了博客,但我已经将内容复制下来供您参考。今天我在StackOverflow上看到一个问题,涉及到这样的问题:“如何创建一个目标,当执行CoreCompile时会被执行,而当CoreCompile被跳过时不会被执行?" 下面是我的答案。

对于一般情况,这是一个棘手的问题,但在你的情况下,它相当容易解决,因为CoreCompile对这种情况有特殊的内置支持。在我讲述如何使用CoreCompile实现这一点之前,让我先解释一下它在一般情况下是如何工作的。

一般情况的解释

在MSBuild中,由于增量构建,目标会被跳过。增量构建纯粹是由目标本身的Inputs和Outputs属性驱动的。输入是目标将“使用”的文件列表,输出是目标“生成”的文件列表。我用引号是因为它是一个松散的概念,而不是一个具体的概念。为了简化,您可以将输入/输出视为文件列表。当要执行目标时,MSBuild会获取输入并比较它们的时间戳与输出的时间戳。如果所有的输出都比输入新,那么该目标将被跳过。(FYI 如果你想知道当只有一些输出过期时会发生什么,请阅读我的博客:http://sedodream.com/2010/09/23/MSBuildYouveHeardOfIncrementalBuildingButHaveYouHeardOfPartialBuilding.aspx)。

无论如何,如果您想跳过一个目标,您必须正确地制定Inputs/Outputs。在您的情况下,您希望在跳过CoreCompile时跳过您的目标,因此表面上看来,您可以简单地复制CoreCompile的Inputs/Outputs,但这是行不通的。这是因为当执行CoreCompile时,文件可能已经过期,但该目标本身会使它们更新。然后当执行您的目标时,由于它们都是最新的,它将被跳过。您需要复制Inputs/Outputs并将一个附加文件添加到inputs/outputs中,以确保在第一次通过时不会跳过您的目标。

CoreCompile的具体解决方案

如果您查看项目文件,您会发现在底部导入了Microsoft.Common.targets文件,该文件将导入特定语言的.targets文件。例如,它将导入Microsoft.CSharp.targets或Microsoft.VisualBasic.targets(如果您正在使用C#或VB)。在这些.targets文件中,您将找到CoreCompile被定义。在CoreCompile的定义中,您将在其末尾找到以下内容。
<CallTarget Targets="$(TargetsTriggeredByCompilation)" Condition="'$(TargetsTriggeredByCompilation)' != ''"/>

这将调用TargetsTriggeredByCompilation属性中定义的所有目标。因此,如果您希望在CoreCompile被执行时调用目标,则可以扩展该属性。以下是具体操作步骤。
<PropertyGroup>
  <TargetsTriggeredByCompilation>
    $(TargetsTriggeredByCompilation);
    MyCustomTarget
  </TargetsTriggeredByCompilation>
</PropertyGroup>

<Target Name="MyCustomTarget">
  <Message Text="MyCustomTarget called" Importance ="high"/>
</Target>

在这种情况下,我定义了属性TargetsTriggeredByCompilation并将MyCustomTarget附加到其中。非常重要的是,在那里包括$(TargetsTriggeredByCompilation);,如果不包括,则不会附加而是覆盖。因此,如果其他人使用此技术,您将清除其目标。
下面是一张图片,显示我进行一次构建后执行CoreCompile和MyCustomTarget。然后第二次构建时,跳过CoreCompile,MyCustomTarget从未被调用。

enter image description here


这是我添加到我的项目中的内容: <PropertyGroup> <TargetsTriggeredByCompilation> $(TargetsTriggeredByCompilation); MyCustomTarget </TargetsTriggeredByCompilation> </PropertyGroup> <Target Name="MyCustomTarget" > <Message Importance="high" Text="TestMessage:Success" /> </Target> 我甚至检查了核心编译中常见目标中是否存在此块<CallTarget Targets="$(TargetsTriggeredByCompilation)" Condition="'$(TargetsTriggeredByCompilation)' != ''"/>,并且它确实存在。 - mirakl
我进行了一些调查:只有在Microsoft.Csharp.Targets中定义这些属性TargetsTriggeredByCompilation才能正常工作,但这是不可接受的。我不能直接修改Microsoft.Csharp.Targets。 - mirakl
如果我也在Microsoft.Common.Targets中添加这些属性,它就可以工作。但是如果我需要将此属性添加到我的自定义.proj文件中,该怎么办呢? - mirakl
custom.proj的内容如下:<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <ProjectFile>TestDir/Myproject.csproj</ProjectFile> <IsWebProject>False</IsWebProject> </PropertyGroup> <Import Project="shared.proj"/> </Project> shared.proj用于我们所有的项目,它具有一些预定义的自定义属性和目标,如clean、build等。实际上,我已经尝试在shared.proj和custom.proj中添加TargetsTriggeredByCompilation属性,但它不起作用。无论如何,非常感谢您的帮助。 - mirakl
如果我们可以基于某个谓词HasTargetExecuted(CoreCompile)设置条件,那就会容易得多,如果目标被跳过则评估为false,如果运行则为true,或者甚至一个简单的属性DidCoreCompileRun之类的东西。哦好吧,感谢解释,至少现在有一种方法可以完成它。 - Samer Adra
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接