3

最近,我开始学习 MSBuild,以便灵活地为本地和服务器构建(CI、Nightly、Weekly)构建脚本。根据我的经验,我知道构建脚本可能非常尴尬。即使在我的公司有一些指导,了解所有目标以及它们如何协同工作也是一种痛苦。当然,这是一个长期的过程:你需要一些东西,你没有足够的时间,你开始变得懒惰和凌乱。但我问自己,如何构建一个 MSBuild 脚本以便于扩展性和可读性?尤其是目标DependsOnTargets、BeforeTargets、AfterTargets之间的三个关系,对我的脚下射击很有用。

4

2 回答 2

7

如果您有兴趣,在我的MSBuild 书中,我有一节介绍如何在 MSBuild 中创建可重用元素。不过,我也会在这里发表一些评论。这个内容与书中的内容不同。

创建 MSBuild 文件时,您应该小心区分whathow。为了稍微解释一下,让我们看看托管 VS 项目是如何开箱即用的(这是可重用元素的一个很好的模型)。

当您创建 C#/VB 项目时,您将获得一个 .csproj 文件,该 .csproj 文件主要包含属性和项。您不会在该文件中找到单个目标。这些文件包含要构建的内容(以及与构建相关的一些设置)。在项目文件的底部,您将找到一个导入语句。这个导入带来了项目是如何构建的。

导入的文件包括:

  • Microsoft.CSharp/VisualBasic/FSharp.targets
  • Microsoft.common.targets

在这种情况下,Microsoft.common.targets 定义了所有托管语言的整体构建过程。然后 Microsoft.CSharp.targets(或其他特定语言的 .targets 文件之一)填补了如何调用特定语言特定工具的空白。

DependsOnTargets 与 Before/AfterTargets

在您上面的回答中,您声明“我建议避免使用 DependsOnTargets,除非确实有必要,例如两个”。我不同意这一点。这是我对 DependsOnTargets 与 Before/AfterTargets 的看法。

使用 DependsOnTargets 时

  • 当您尝试创建要执行的目标工作流时
  • 当一个目标在没有其他目标首先执行的情况下无法运行时
  • 当您需要根据所需操作在特定步骤注入不同目标时

使用 Before/AfterTargets 时

  • 当您不拥有目标所在的文件并且它没有可以扩展的 DependsOnTargets 属性时
  • 无论何时执行,您都希望目标在特定目标之前/之后执行

稍微梳理一下差异,请考虑 Web 项目。对于 Web 项目,.csproj/.vbproj 负责两个工作流程:

  1. 建造
  2. 发布

如果我想在构建目标之前将目标添加到要执行的目标列表中,我可以仅为发布场景动态更新 BuildDependsOn 属性。您不能使用 Before/AfterTargets 执行此操作。

在理想的世界中,每个目标都将具有以下 DependsOnTargets。

  • 所有目标都有一个 DependsOnTargets 属性,该属性由属性提供
  • 每个 DependsOnTargets 总是将现有值添加到属性定义中

例如

<MyTargetDependsOn>
    $(MyTargetDependsOn);
    Target1;
    Target2
</MyTargetDependsOn>

不幸的是,许多目标不遵循这种模式,因此 DependsOnTargets 在许多情况下都死在了水中。

当我创作 MSBuild 脚本时,我总是使用 DependsOnTargets ,除非我有充分的理由选择使用 Before/AfterTargets。我觉得(我对设计的真正原因没有洞察力,因为我当时不在 Microsoft 工作)Before/AfterTargets 的真正创建是为了允许用户在他们没有的目标之前/之后注入要执行的目标自己的,创作者没有使用上面的模式。

于 2013-02-12T02:13:00.150 回答
0

好吧,正如我已经说过的,我有自己的经历:)

  1. 每个目标都进入一个文件,就像在面向对象编程中一样:)公共属性和项目组也应该保留在它们自己的文件中(例如,包含所有要构建的项目的Projects.conf ;包含所有工具路径的Environment.conf和扩展包)。正如您将看到的,这对于不同的构建集(如本地和服务器构建)非常方便。
  2. 定义您需要的所有路径、文件和属性。不要遵循 DRY 原则。这是很难的事情,但根据我的经验,很难根据已经定义的价值观来构建新的价值观。因此,如果您有一个包含.csproj.nuspec文件的项目,请不要费心显式声明它们。
  3. 声明一个初始化目标。解释如下。
  4. 声明一些在构建过程中必须处理的主要目标,如Clean、Build、Test、Package、Deploy、Archive等。使它们相互依赖(DependsOnTargets)。例如,测试打包取决于构建目标。它们都应该取决于Init目标。
  5. 健全性检查是规则 1 的例外。它们属于需要它们的同一个文件。例如,PackageCheck目标将监视.nuspec文件是否存在,并将Init声明为AfterTargets。这允许快速失败构建。
  6. 所有其他目标将使用BeforeTargets、AfterTargets进行排序。我建议避免使用DependsOnTargets,除非确实有必要,例如如果必须在Build之前处理两个目标,但彼此之间也有顺序。
  7. 在服务器和本地构建脚本中,只需导入您需要的所有内容以及应该是公共 API 的内容。正如我所说,每个目标都进入一个文件 (1)。现在,如果您有一个UpdateAssemblyVersion目标,则应在AfterTargets (5)中声明Build 。您可以通过简单地添加/删除导入来激活/停用该目标。
  8. 利用MSBuildExtensionPack。他们真的很有帮助!但也不要害怕编写自己的任务

所以这些是我的规则。我将不胜感激任何意见或补充!

于 2013-02-07T22:22:05.440 回答