在CoreBuild之前运行目标?

5

我正在将一个自定义的 .tt 模板生成目标添加到我的项目中,在运行 CoreBuild 之前进行,有两种方法可以实现:

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

并且

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

如果我的目标在项目构建之前就需要运行,而该项目依赖于它,那么使用AfterTargets="BeforeBuild"会更好吗?我看到有人使用前者来生成文本模板,但是这似乎是不可靠的,因为它可能在太晚的CoreBuild之后运行。还是说AfterTargets="BeforeBuild"仍然保证在核心构建之前运行?
我还看到过BeforeTargets="BeforeBuild",可以更早地构建。那么,在这里放置`.tt文本生成目标会更合适吗?
3个回答

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

标签。

BeforeTargets="CoreCompile" DependsOnTargets="PrepareForBuild"

这是 .NET SDK(.NET Core/Standard 项目的新样式 csproj)用于自动生成 AssemblyInfo.cs 的操作
它使用以下注释来解释原因:

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

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

4

更新2

根据Microsoft的文档

这是目标构建顺序

  1. 运行InitialTargets目标。
  2. 通过/target开关在命令行上指定的目标将被运行。如果您在命令行上未指定目标,则将运行DefaultTargets目标。如果两者都不存在,则运行遇到的第一个目标。
  3. 评估目标的条件属性。如果存在Condition属性并且计算结果为false,则不会执行该目标,并且对构建没有进一步影响。
  4. 在执行目标之前,运行其DependsOnTargets目标。
  5. 在执行目标之前,运行任何在BeforeTargets属性中列出它的目标。
  6. 在执行目标之前,比较其Inputs属性和Outputs属性。如果MSBuild确定任何输出文件与相应的输入文件过期,则MSBuild执行该目标。否则,MSBuild跳过该目标。
  7. 在执行或跳过目标后,运行任何在AfterTargets属性中列出它的目标。
这是您问题的答案:
如果我的目标应该在项目构建之前运行,因为项目依赖于它,那么使用后者会更好吗?
不会,因为所有CoreBuild的依赖项将在你的模板生成目标之前执行,这太晚了。
或者说,为什么AfterTargets="BeforeBuild"仍然保证在核心构建之前运行?
AfterTargets="BeforeBuild"保证你的目标将按时执行,因为它将在所有CoreBuild依赖项之前执行。
我还看到过BeforeTargets="BeforeBuild",这是放置".tt文本生成目标的更好位置吗?
  • 在两种情况下,AfterTargets="BeforeBuild"BeforeTargets="BeforeBuild",您的目标将在所有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的项目时安全地扩展构建过程

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

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

如果在 CoreBuild 之前有多个目标需要运行,您可以定义一个属性,其中包含所有需要按照执行顺序调用的目标:
<PropertyGroup>
    <BeforeBuildDependsOn>
      CustomTarget1;
      CustomTarget2;
      CustomTarget3
    </BeforeBuildDependsOn>
</PropertyGroup>

<Target Name="BeforeBuild" DependsOnTargets="$(BeforeBuildDependsOn)"/>

更新:

根据@stijn提供的片段:

AfterTargets="BeforeBuild"会像这样插入/执行自定义目标:(依赖于BeforeBuild)

<BuildDependsOn>
  BeforeBuild;
       |-> Custom Target


  CoreBuild;
  AfterBuild
</BuildDependsOn>

BeforeTargets="CoreBuild"会在CoreBuild之前插入/执行自定义操作:

<BuildDependsOn>
  BeforeBuild;


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

“模板生成目标”将在同一位置执行(在BeforeBuildCoreBuild之间,但根据不同的目标而定,因此应使用适当的目标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和targets。
这样,您将能够使用多个第三方脚本来尊重一般的工作流程。
显然,您可以根据情况使用这些选项的组合。但是,您应该遵循以下一般规则:
1. 尊重默认工作流程(BeforeBuild,CoreBuild,AfterBuild)。 2. 为您的第三方脚本包含Before/After targets,以允许最终用户在第三方脚本执行之前或之后注入任何内容。
如果您正在使用默认的Visual Studio生成的构建脚本(如.csproj,.vbproj等项目),则应考虑这些问题。如果您正在为其他语言或目的实现自己的脚本,则可以在任何地方使用BeforeTargets和AfterTargets,但为什么不遵循基于现有脚本的良好实践呢?

@Jez 我已经阅读了这个问题并给您提供了适当的答案。您在描述一个普遍情况,即希望在实际构建过程之前执行一个或多个目标,因此您可以使用 BeforeBuild 和 AfterBuild 目标。您需要了解 Microsoft MSBuild 脚本的设计和推荐做法,让我尝试解释一下... BeforeBuild 和 AfterBuild 是两个“空”目标,旨在在需要在实际构建过程之前或之后执行某些操作时进行重写,以防止破坏构建顺序... - Rolo
您可以通过内联或使用属性组覆盖这些目标,如我的示例所述。使用AfterTargets="BeforeBuild"或BeforeTargets="CoreBuild"的建议将起作用,除非您在BeforeBuild目标中有一些逻辑,否则它们之间没有真正的区别。无论如何,这不是适当的做法,它会使您的脚本难以理解和维护。如果您想自己验证,请从控制台运行msbuild并在命令末尾添加/v:diag以生成详细的MSBuild执行日志。 - Rolo
@Rolo 它们之间没有真正的区别,实际上有一个重要的区别:前者将在所有依赖的Corebuild步骤(编译等)之前运行,而后者将在之后运行(至少在我尝试过的2个VS版本中)。此外,我建议不要使用BeforeBuild/AfterBuild:它们可以被任何人覆盖,并且只提供了一个有限的单一扩展点:如果您覆盖它们,然后导入第三方代码再次执行相同操作,或反之亦然,只会调用其中一个版本。使用Before/After/DependsOn属性就不会有这个问题。 - stijn
@stijn,请审核我的更新,我认为它会澄清我的答案。 - Rolo
1
首先,在项目文件中,BeforeBuild和AfterBuild的位置很重要:它们必须放在例如Microsoft.CSharp.targets导入之后。 这个相当重要的信息在你的回答中缺失了。 其次,它适用于C#项目,但并不总是适用于其他项目类型,例如在某个时刻,微软取消了C++的BeforeBuild。 所以,总的来说,考虑到这些明显的缺点,我不确定AfterBuild和BeforeBuild是否真的是一个好的实践,因为据我所知,其他替代方案同样能够正常工作且问题较少。 - stijn
显示剩余5条评论

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"


如果您使用 AfterTargets="BeforeBuild",那么执行顺序不会是 BeforeBuild -> CoreBuild -> MyTarget 吗? - Jez
我不这么认为:据我所知,“AfterTargets”意味着“紧接在目标之后”。然而,https://msdn.microsoft.com/en-us/library/ee216359.aspx?f=255&MSPPError=-2147217396 对此也不是非常清楚。像Martin的答案一样,您可以强制实现“在X和Y之间运行”的行为。 - stijn
@Jez,使用AfterTargets、BeforeTargets或在BeforeBuild本身内部工作是安全的。请查看我对答案的更新,它解释了目标的执行顺序以及为什么使用BeforeBuild而不是CoreBuild不会出现问题。 - Rolo

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