我刚刚在
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从未被调用。