【问题标题】:How to run MSBuild Target BEFORE compilation but only when compilation will happen如何在编译之前运行 MSBuild 目标,但仅在编译发生时
【发布时间】:2019-12-12 15:46:51
【问题描述】:

我有一个 C# 库项目,其中包含一些使用 PowerShell 在“BeforeBuild”中创建的依赖项。

MSBuild 似乎每次都执行目标“BeforeBuild”,如果库项目本身不需要构建。

我想将构建过程配置为仅在库需要(重新)构建时运行 PowerShell 脚本。

有没有办法做到这一点?

我尝试使用目标“BeforeBuild”和“BeforeCompile”。但它似乎每次都执行。

【问题讨论】:

  • 嗨朋友,这个问题有什么更新吗?

标签: visual-studio msbuild msbuild-task incremental-build


【解决方案1】:

MSBuild 似乎每次都执行目标“BeforeBuild”,也好像库项目本身不需要构建。

如果库项目本身不需要构建,那么 msbuild 每次都执行目标是什么意思?据我所知,BeforeBuild 是构建过程中必不可少的目标之一,因此如果您构建一个项目,它将始终调用此目标。如果库项目不需要构建,您调用 msbuild 构建它的原因是什么?更多细节可能会更好:)

我想将构建过程配置为仅运行 PowerShell 如果库需要(重新)构建,则编写脚本。

有没有办法做到这一点?

我尝试使用目标“BeforeBuild”和“BeforeCompile”。但似乎 每次都执行。

BeforeBuild 和 BeforeCompile 是 msbuild 系统中的预定义目标。您可以覆盖它们以自定义构建步骤,但您无法避免在构建过程中运行它们。因此,每次构建开始时它们都会执行,这是设计的预期行为。

根据您的描述,您可以覆盖这些目标并在其中运行 power shell 脚本。这就是为什么在你的机器上它总是会调用 ps 脚本。

如果您通过 msbuild.exe 而不是 VS IDE 构建项目,这里有一个解决方法:

假设您在项目文件中覆盖了BeforeTarget。您可以添加condition 来确定是运行自定义 BeforeTarget(调用ps 脚本)还是原始 BeforeTarget。

定义一个名为RunPS的属性,并在你这样使用的BeforeTarget中添加一个条件:

  <PropertyGroup>
    <RunPS>false</RunPS>
  </PropertyGroup>

  <Target Name="BeforeBuild" Condition="$(RunPS)=='true'">
   <!--Run the ps script here-->  
  </Target>

然后你可以通过设置RunPS的值来控制是使用默认的BeforeTarget还是自定义的。

msbuild xx.csproj /p:RunPS=true在构建过程中运行ps脚本,msbuild xx.csproj运行原始BeforeTarget

如果您在 VS IDE 中构建项目,您可以创建从 Debug 或 Release 复制的新配置(CustomDebug),并将Condition="$(Configuration)=='CustomDebug'" 设置为目标。

更新:

适用于 VS 和命令行的解决方法:

请参阅this document,在 VS 中,我们可以通过从调试和发布配置中复制设置来轻松创建新配置。

1.我认为,对于您的库项目,您有正常的Debug和Release配置,您可以按照文档中的提示创建相应的自定义配置。从 Debug 复制设置并创建一个名为 PSDebug 的新配置,从 Release 复制该设置并创建一个名为 PSRelease 的新配置。

2.然后编辑xx.csproj,添加如下格式的条件:

  <Target Name="BeforeBuild" Condition="'$(Configuration)'=='PSDebug' OR '$(Configuration)'=='PSRelease'">
    <!--Run the ps script here-->
    <Message Text="showsth" Importance="high" />
  </Target>

然后在 VS IDE 中,如果您使用普通配置 Debug or Release 构建,它将使用默认的 BeforeBuild 目标。只有当你使用自定义PSDebug or PSRelease构建时,它才会调用你自定义的BeforeBuild目标。在VS中,你可以通过这个框轻松地在这些配置之间切换:

对于命令行,它也可以工作。如果您使用msbuild xx.csproj /p:Configuration=Debug 之类的命令,它会使用预定义的BeforeBuild 目标来执行此操作,如果您使用msbuild xx.csproj /p:Configuration=PSDebug 之类的命令,它将调用您的自定义目标并运行PS 脚本。

【讨论】:

  • 感谢您的建议!我知道这种行为是“设计使然”!但我不能使用你的解决方案,因为它也需要在 VS 中工作。
  • @IngoKarstein 查看我的更新,因为它是设计行为,我们没有直接控制它的 VS 选项。但是 vs 允许我们从 Debug 和 Release 中创建新的配置,所以我们可以通过自定义项目文件来实现行为。希望它有所帮助:)
【解决方案2】:

您无法真正预测编译是否会发生,因为您无法知道在目标运行后目标可能会或可能不会做什么。但是您也许可以根据构建目录的当前状态来预测您的任务是否需要运行。这就是您在 MsBuild 中实现增量构建的方式,这听起来就像您真正追求的那样。

所有目标都有一个可选的输入和输出参数。这两个用于计算自上次构建以来是否有任何输入发生变化,并基于此 MsBuild 决定是否运行您的目标或是否可以跳过它。

这需要一些知识,根据构建中 ItemGroups 和属性的当前状态来预测目标的结果。

当没有指定输入和输出时,MsBuild 无法判断你的目标是否会影响任何东西,因此它会运行它来确定。

The docs provide a good explanation on how to enable incremental building of your custom targets.

要预测输出,您需要编写某种转换。或者,您会写出某种标记文件(如codeanalysis.lastsucceeded)并使用它进行比较。增量构建将仅查看所有输入的最后更改日期并将其与所有输出进行比较。因此,您需要将运行或不运行的需要表达为对该数据的操作。

基本结构:

<target name="RunMyPowerShell" 
    beforetargets="Compile" Inputs="@(Content)" 
    Outputs"@(Content->'%(Filename).translated.content')">
    ...
    <exec Command="powershell .\mypsfile.ps1" />
    ...
</targets>

作为BeforeTargets="Build" 的替代方案,您可以覆盖BuildDependsOn 属性:

<PropertyGroup>
    <CompileDependsOn>
        RunMyPowerShell;
        $(CompileDependsOn)
    </CompileDependsOn>
</PropertyGroup>

如果您以前没有深入研究过这个领域,那么 MsBuild 的所有错综复杂都会让人不知所措,要完成这项工作,您需要掌握很多东西。它还可能需要更改 PowerShell 脚本的功能,以使其输出足够可预测,让 MsBuild 发挥它的魔力。

或者,您可以根据某些表达式在目标上添加条件,但考虑到构建文件的解析、计算和执行方式,很难弄清楚如何正确地做到这一点。

如果你把这些事情做对了,就会有巨大的性能提升。

另见:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-05-10
    • 1970-01-01
    • 2020-03-22
    • 1970-01-01
    • 2012-11-09
    • 2015-12-15
    • 2022-11-28
    • 2012-11-04
    相关资源
    最近更新 更多