在CoreBuild之前运行目标?

时间:2017-05-18 09:32:01

标签: visual-studio msbuild

我在我的项目中添加自定义.tt模板生成目标,以便在CoreBuild之前运行,并且似乎有两种方法:

<Project...>
    <Target Name="TransformOnBuild" AfterTargets="BeforeBuild">
</Project>

<Project...>
    <Target Name="TransformOnBuild" BeforeTargets="CoreBuild">
</Project>

如果我的目标应该在我的项目构建之前运行,因为项目依赖于它,那么使用后者会更好吗?我已经看到前者曾经做过像生成文本模板这样的事情,但这似乎是一种不可靠的方法,因为它可能会在CoreBuild之后运行,这为时已晚。或者,为什么AfterTargets="BeforeBuild"仍然可以保证在核心构建之前运行?

我也见过BeforeTargets="BeforeBuild"甚至会更早建立。这是放置`.tt文本生成目标的更好的地方吗?

3 个答案:

答案 0 :(得分:4)

更新2

基于documentation from Microsoft

这是目标构建顺序

  1. 运行InitialTargets目标。
  2. 运行/ target开关在命令行上指定的目标。如果在命令行上未指定目标,则运行DefaultTargets目标。如果两者都不存在,那么遇到遇到的第一个目标。
  3. 评估目标的Condition属性。如果Condition属性存在且计算结果为false,则不执行目标,并且对构建没有进一步的影响。
  4. 在执行目标之前,会运行其DependsOnTargets目标。
  5. 在执行目标之前,会运行在BeforeTargets属性中列出目标的任何目标。
  6. 在执行目标之前,会比较其Inputs属性和Outputs属性。如果MSBuild确定任何输出文件相对于相应的输入文件已过期,则MSBuild将执行目标。否则,MSBuild会跳过目标。
  7. 执行或跳过目标后,将运行在AfterTargets属性中列出目标的任何目标。
  8. 以下是您问题的答案:

    如果我的目标应该在我的项目构建之前运行,因为项目依赖它,那么使用后者会更好吗?

    • 不,因为 CoreBuild 的所有依赖项都将在模板生成目标之前执行,而且为时已晚。

    或者有什么理由为什么AfterTargets =&#34; BeforeBuild&#34;仍然保证在核心构建之前运行?

    • AfterTargets =&#34; BeforeBuild&#34; 保证您的目标将按时执行,因为它将在所有 CoreBuild 依赖项之前执行。

    我还看过 BeforeTargets =&#34; BeforeBuild&#34; ,它们甚至会更早建立。这是放置`.tt文本生成目标的更好的地方吗?

    • 在两种情况下 AfterTargets =&#34; BeforeBuild&#34; BeforeTargets =&#34; BeforeBuild&#34; 您的目标将在所有<之前执行< em> CoreBuild 依赖项,但在这两种情况下,您仍有可能影响模板生成目标的结果,具体取决于您必须在 BeforeBuild 中执行的操作。如果您对此进行了控制,则可以安全地使用这些选项。

    在CoreBuild之前运行目标?

    > there appear to be 2 ways of doing it.
    
        There are more options to achieve this. Please review below.
    

    为此,您应该使用特定的内置目标(BeforeBuild或AfterBuild)。这是Microsoft在使用依赖于Microsoft.Common.targets的项目时safely extend the build process提供的机制

    如果在CoreBuild之前只有一个目标要运行,则可以执行以下操作:

    <Target Name="BeforeBuild">
        <!-- add your tasks here -->
    </Target>
    

    如果在CoreBuild之前要运行多个目标,则可以定义一个属性,其中包含需要按所需执行顺序调用的所有目标:

    <PropertyGroup>
        <BeforeBuildDependsOn>
          CustomTarget1;
          CustomTarget2;
          CustomTarget3
        </BeforeBuildDependsOn>
    </PropertyGroup>
    
    <Target Name="BeforeBuild" DependsOnTargets="$(BeforeBuildDependsOn)"/>
    

    <强>更新

    基于@stijn提供的片段:

    AfterTargets =&#34; BeforeBuild&#34;将插入/执行自定义目标,如下所示:(取决于BeforeBuild

    <BuildDependsOn>
      BeforeBuild;
           |-> Custom Target
    
    
      CoreBuild;
      AfterBuild
    </BuildDependsOn>
    

    BeforeTargets =&#34; CoreBuild&#34; 将插入/执行此类自定义(取决于CoreBuild):

    <BuildDependsOn>
      BeforeBuild;
    
    
           |-> Custom Target
      CoreBuild;
      AfterBuild
    </BuildDependsOn>
    

    所以&#34;模板生成目标&#34;将在同一个地方执行(在 BeforeBuild CoreBuild 之间,但是根据不同的目标,这就是为什么要使用的适当的目标应该是 BeforeBuild 内联或依赖。

    现在关于第三方问题评论,BeforeBuild / AfterBuild目标是针对最终用户的,第三方提供商应该实现他们的脚本而不影响基本工作流程。这些是第三方应该使用的一些选项,以避免破坏常规流程:

    将此视为基础:

    <PropertyGroup>
        <BuildDependsOn>
          BeforeBuild;
          CoreBuild;
          AfterBuild
        </BuildDependsOn>
    </PropertyGroup>
    
    <Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>
    

    选项1:这将在BeforeBuild之前注入您的自定义第三方脚本,而不会影响默认的BuildDependsOn序列,这仍然允许最终用户不使用BeforeBuild目标。

    <PropertyGroup>
        <BuildDependsOn>
          MyCustomThirdParty;
          $(BuildDependsOn);
        </BuildDependsOn>
    </PropertyGroup>
    
    <PropertyGroup>
        <MyCustomThirdPartyDependsOn>
          BeforeMyCustomThirdParty;
          CustomStep1;
          CustomStep2;
          CustomStep1;
          AfterMyCustomThirdParty
        </MyCustomThirdPartyDependsOn>
    </PropertyGroup>
    
    <Target Name="MyCustomThirdParty" DependsOnTargets="$(MyCustomThirdPartyDependsOn)"/>
    

    选项2:如果需要在 BeforeBuild 目标之后执行第三方脚本,可以这样做:

    <PropertyGroup>
        <BuildDependsOn>
          BeforeBuild;
          MyCustomThirdParty;
          CoreBuild;
          AfterBuild
        </BuildDependsOn>
    </PropertyGroup>
    

    注意:为了使其正常工作,您必须在Microsoft.CSharp.targets导入后添加PropertyGroup和目标。

    通过这种方式,您可以使用多个第三方脚本来完成一般工作流程。

    根据具体情况,您显然可以使用这些选项的组合。但是你应该遵循这些一般规则:

    1. 尊重默认工作流程(BeforeBuild,CoreBuild,AfterBuild)。
    2. 包含第三方脚本的前/后目标,以允许最终用户在第三方脚本执行之前或之后注入任何内容。
    3. 当您使用默认的visual studio生成的构建脚本(像.csproj,.vbproj等项目)时,应该考虑这些。如果您正在为其他语言或目的实现自己的脚本,可以在任何地方使用BeforeTargets和AfterTargets,但为什么不遵循基于现有脚本的良好实践?

答案 1 :(得分:3)

从Microsoft.Common.CurrentVersion.targets开始,Build目标基本上是:

<BuildDependsOn>
  BeforeBuild;
  CoreBuild;
  AfterBuild
</BuildDependsOn>
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>

<PropertyGroup>
  <CoreBuildDependsOn>
    PrepareForBuild;
    PreBuildEvent;
    ...
    Compile;
    ...
    PostBuildEvent
  </CoreBuildDependsOn>
</PropertyGroup>
<Target Name="CoreBuild" DependsOnTargets="$(CoreBuildDependsOn)">

因此使用BeforeTargets="CoreBuild"确实会在CoreBuild之前运行,但之后所有它的依赖目标都会运行,所以之后所有实际的构建步骤。这通常不是您想要的,而是如果您想在编译之前运行某些内容,请使用BeforeTargets="PrepareForBuild"AfterTargets="BeforeBuild"或甚至BeforeTargets="BeforeBuild"

答案 2 :(得分:3)

在@ stjin的答案的基础上,一个好的解决方案似乎正在使用

BeforeTargets="CoreCompile" DependsOnTargets="PrepareForBuild"

这就是.net sdk(.net核心/标准项目的新风格csproj)is doing for automatic AssemblyInfo.cs generation

它使用以下注释来解释原因:

  

请注意,这必须在每次调用CoreCompile之前运行   确保所有编译器运行都看到生成的程序集信息。那里   至少有一个涉及调用CoreCompile的Xaml的场景   没有其他潜在的钩子,如Compile或CoreBuild等,所以   我们直接挂钩到CoreCompile。此外,我们必须在之后运行   PrepareForBuild以确保中间目录已经存在   创建

请注意&#34;中间目录&#34; (在这种情况下为obj/[TargetFramework])是输出.cs文件放在这种情况下的位置,也可能是您想要做的。