【问题标题】:How should I structure my MSBuild script我应该如何构建我的 MSBuild 脚本
【发布时间】:2013-02-07 22:22:05
【问题描述】:

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

【问题讨论】:

    标签: msbuild


    【解决方案1】:

    如果您有兴趣,在我的MSBuild Book 中有一节介绍如何在 MSBuild 中创建可重用元素。不过,我也会在这里给出一些 cmets。此内容与书中的内容不同。

    在创建 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 确实是为了允许用户注入要在之前/之前执行的目标而创建的。在他们不拥有的目标之后,并且创建者没有使用上述模式。

    【讨论】:

    • 嗨,Sayed,我刚刚浏览了Microsoft.Common.targets 文件,并产生了通过属性定义依赖关系的想法。对我来说,为了获得 4.0 现在提供的功能,它非常冗长和笨拙。我同意它更明确,您应该始终在 4.0 中将它用于困难的工作流程(如所述)!也许我不够清楚,但问题是在 LOCAL 与 SERVER 构建中,有一些目标必须流入预定义的工作流程。也许我会尝试属性方法,但我很怀疑。
    • 我看到你刚刚在博客上写过这个 :) 如果你有兴趣,我想向你展示一个我遵循自定义规则的小样本 :) 顺便说一句,我认为在某种程度上,我们对构建构建脚本的结构有一个共同的理解,尤其是 whathow 部分。
    • 顺便说一句,还有 Mike Fourie(你可能认识他 ;))在他的 post 中指出 BeforeTargetsAfterTargets 可以产生更多可重复使用的目标。
    • 嗨@Matthias,是的,我知道迈克他是个好人。我读了他的帖子,但对我来说,他并没有像他强烈主张之前/之后的目标而不是 DependsOnTargets 那样。我的印象是他更喜欢 DependsOnTargets,但谁知道呢。如果您想发布您的示例作为要点,我会看看它。
    • 我可以这样做吗:AfterTargets="Build" DependsOnTargets="Build" 用于从未显式调用但仍需要在Build 之后运行(但不是在Clean 之后)的目标?
    【解决方案2】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-29
      • 1970-01-01
      • 2012-07-27
      相关资源
      最近更新 更多