【问题标题】:How to always execute a target in MSBuild如何始终在 MSBuild 中执行目标
【发布时间】:2010-09-16 21:09:22
【问题描述】:

我有一个 MSBuild 文件,它在编译应用程序之前操作 AssemblyInfo 文件。在构建结束时,它会恢复 AssemblyInfo 文件。它通过备份文件、对其进行操作,然后在构建时间后恢复文件来完成此操作。

这工作得很好,除非在构建过程中发生错误。然后它不会恢复原始文件。有没有办法告诉 MSBuild 在构建结束时执行目标,无论它是成功还是失败?

【问题讨论】:

  • MSBuild 中没有内置这样的功能。您能否更好地解释您正在做什么,以便其他人可以提供一些替代方案?
  • 当然...基本上我要做的是:1.) 将 assemblyinfo.cs 复制到备份文件 (assemblyinfo.cs.original) 2.) 编辑 assemblyinfo.cs 以包含有关版本控制的信息。版本信息来自自定义 MSBuild 任务。 3.) 构建应用程序和/或程序集 4.) 将 assemblyInfo.cs 替换为原始文件 (assemblyinfo.cs.original) 如果在构建过程中发生任何错误,我希望执行第 4 步。

标签: msbuild


【解决方案1】:

根据您对原始问题的最后评论,我将采用另一种方法,而忘记您当前采用的方法。您应该知道您的版本信息不必在 AssemblyInfo.cs 文件中。它可以在任何代码文件中,只要您只定义了一个属性 AssemblyVersion 和 AssemblyFileVersion 即可。话虽如此,我要做的是按照以下步骤操作:

  1. 从 AssemblyInfo.cs 中删除 AssemblyVersion 和 AssemblyFileVersion
  2. 创建一个新文件,将其命名为您想要的任何名称,在我的例子中,我将它放在 Properties\VersionInfo.cs 中。 不要将此文件添加到项目中。
  3. 编辑项目文件以将该文件包含到文件列表中,以便仅在需要时编译

让我们稍微扩展一下#3。构建 .NET 项目时,项目本身就是一个 MSBuild 文件。在该文件中,您将找到一个声明为 Compile 的项目。这是将发送到编译器进行编译的文件列表。您可以从该列表中动态包含/排除文件。在您的情况下,仅当您在构建服务器上构建(或您定义的任何其他条件)时才希望包含 VersionInfo.cs 文件。对于此示例,我将条件定义为项目是否在发布模式下构建。因此,对于发布模式,VersionInfo.cs 将被发送到编译器,而对于其他构建则不会。以下是 VersionInfo.cs 的内容

VersionInfo.cs

[assembly: System.Reflection.AssemblyVersion("1.2.3.4")]
[assembly: System.Reflection.AssemblyFileVersion("1.2.3.4")]

为了将它连接到构建过程中,您必须编辑项目文件。在该文件中,您将找到一个元素(可能超过 1 个,具体取决于项目类型)。您应该在此处添加类似于以下内容的目标。

<Target Name="BeforeCompile">
  <ItemGroup Condition=" '$(Configuration)'=='Release' ">
    <Compile Include="Properties\VersionInfo.cs" />
  </ItemGroup>
</Target>

我在这里所做的是定义一个目标,BeforeCompile,这是一个众所周知的目标,您可以覆盖它。有关其他类似目标的信息,请参阅此 MSDN article。基本上,这是一个在调用编译器之前总是会被调用的目标。在此目标中,仅当 Configuration 属性设置为 release 时,我才将 VersionInfo.cs 添加到 Compile 项。您可以将该属性定义为您想要的任何内容。例如,如果您将 TFS 作为构建服务器,那么它可能是,

<Target Name="BeforeCompile">
  <ItemGroup Condition=" '$(TeamFoundationServerUrl)'!='' ">
    <Compile Include="Properties\VersionInfo.cs" />
  </ItemGroup>
</Target>

因为我们知道 TeamFoundationServerUrl 只有在通过 TFS 构建时才定义。

如果您是从命令行构建,那么类似这样

<Target Name="BeforeCompile">
  <ItemGroup Condition=" '$(IncludeVersionInfo)'=='true' ">
    <Compile Include="Properties\VersionInfo.cs" />
  </ItemGroup>
</Target>

当您构建项目时,只需执行 msbuild.exe YourProject.proj /p:IncludeVersion=true。注意:这在构建解决方案时不起作用。

【讨论】:

  • 很有趣,但我不确定这如何解决问题。让我扩展一下我在做什么。当我希望发送一个版本进行测试时,我会在 subversion 中创建一个分支来指定版本号。然后,我的自定义 MSBuild 任务会读取工作副本所在的分支(以及其他信息,例如修订号等)。然后它会修改 assemblyinfo.cs 以嵌入此类信息。失败时,它会恢复文件的原始版本。这样做的原因是我的正则表达式“知道”在哪里修改信息。
  • 更重要的是,我不会以这种方式创建未提交更改的无限循环。如果我提交版本 1.0.0.0,由于只是构建项目,我没有更改文件。相反,该文件仅在我需要时(在构建期间)进行更改。我不希望我的开发人员必须记住手动更改此信息。这个想法是我可以查看任何文件并确切地知道它来自哪里以及它是如何构建的。因此,如果有人在 prod 服务器上发布垃圾,我知道该怪谁以及我需要修改哪个版本的代码。
  • +1 我喜欢这个解决方案。太糟糕了 Cruisecontrol 不会自动定义这样的属性。
【解决方案2】:

换个问题怎么样:

  • 将“模板”AssemblyInfo.cs.template 添加到版本控制中,该模板代表您的“理想”AssemblyInfo.cs,其中带有正则表达式挂钩
  • 在构建之前,将模板复制到真实环境并应用您的正则表达式
  • 为 AssemblyInfo.cs 添加某种颠覆忽略(我不是 svn 专家,但我很确定有一种方法可以告诉它忽略某些文件)
  • 如果您的开发人员需要添加一些通常会出现在 AssemblyInfo.cs 中的自定义项(例如 InternalsVisibleTo),然后让他们将其添加到 不同 .cs 文件中已登记入住。

作为进一步的改进,将 Sayed 的解决方案与我的解决方案结合起来,并从实际的 AssemblyInfo.cs 中删除版本信息内容,并拥有一个已签入的 VersionInfo.cs.template,它在 BeforeBuild 中创建一个 VersionInfo.cs。

【讨论】:

  • 有趣的想法...我需要考虑一下,但我认为这可能更接近可行的解决方案。如果成功,则在“模板文件”中编译。如果失败,模板文件会保留在那里,但我要保护的文件不会更改。
  • 我很想听听你最后的想法。祝你好运!
【解决方案3】:

我从未使用过它,但从文档看来,OnError Element 对您想要实现的目标很有用。

导致一个或多个目标执行, 如果 ContinueOnError 属性是 失败的任务为 false。

【讨论】:

  • 我已经将 OnError 元素用于我的自定义目标/任务。这非常有效。问题是当错误发生在我控制的目标之外时。所以我想要一个超级捕获所有可以作为任何任务的 OnError 的东西。
  • @Jason,在这种情况下,我很抱歉我无法提供任何进一步的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-22
  • 2010-11-09
  • 2023-01-13
  • 1970-01-01
相关资源
最近更新 更多