最近,我开始学习MSBuild,以便为本地和服务器构建(CI,每夜构建,每周构建)创建灵活的构建脚本。基于我的经验,我知道构建脚本可能非常尴尬。即使在我的公司有一些指导,也很难了解所有目标及其如何相互配合。当然,这是一个长期的过程:你需要某些东西,但没有足够的时间,你开始变得懒惰和混乱。但我问自己,如何构建一个易于扩展和可读性高的MSBuild脚本呢?特别是目标之间的三种关系DependsOnTargets、BeforeTargets和AfterTargets非常容易让人掉进陷阱。
最近,我开始学习MSBuild,以便为本地和服务器构建(CI,每夜构建,每周构建)创建灵活的构建脚本。基于我的经验,我知道构建脚本可能非常尴尬。即使在我的公司有一些指导,也很难了解所有目标及其如何相互配合。当然,这是一个长期的过程:你需要某些东西,但没有足够的时间,你开始变得懒惰和混乱。但我问自己,如何构建一个易于扩展和可读性高的MSBuild脚本呢?特别是目标之间的三种关系DependsOnTargets、BeforeTargets和AfterTargets非常容易让人掉进陷阱。
在我的MSBuild Book中,有一个基于如何在MSBuild中创建可重用元素的部分,如果您感兴趣的话。不过我也会在这里提供一些评论,这个内容与书中的不同。
创建MSBuild文件时,应该注意隔离what (什么)和how (如何)。为了解释一下,让我们来看看默认情况下托管VS项目的工作方式(这是可重用元素的一个很好的模型)。
当你创建一个C#/VB项目时,你会得到一个.csproj文件,这个.csproj文件主要包含属性和项。在那个文件中,你找不到一个目标。这些文件包含what将被构建(以及与构建相关的一些设置)。在项目文件的底部,你会发现一个导入语句。这个导入语句引入了How项目被构建。
被导入的文件包括:
在这种情况下,Microsoft.common.targets定义了所有管理语言的总体构建过程。然后,Microsoft.CSharp.targets (或其他语言特定的 .targets 文件之一) 填补了如何调用特定语言特定工具的空白。
在您上面的回答中,您说:“我建议避免使用DependsOnTargets,除非真的真的有必要,例如如果有两个”。我不同意这个观点。以下是我的看法,关于DependsOnTargets 和 Before/AfterTargets 的区别。
使用DependsOnTargets 当
使用Before/AfterTargets 当
为了更好地理解差异,请考虑Web项目。对于Web项目,.csproj/.vbproj 处理两个工作流程:
如果我想要将目标添加到Build目标之前要执行的目标列表中,我可以动态更新BuildDependsOn属性,仅适用于发布场景。你不能使用Before/AfterTargets来做到这一点。
在理想情况下,每个目标都应该具有DependsOnTargets方面的以下特征:
例如:
<MyTargetDependsOn>
$(MyTargetDependsOn);
Target1;
Target2
</MyTargetDependsOn>
不幸的是,许多目标并未遵循这种模式,因此在许多情况下,DependsOnTargets无法使用。
当我编写MSBuild脚本时,我总是使用DependsOnTargets,除非我有充分的理由选择使用Before/AfterTargets。我觉得(我对设计的真正原因没有深入了解,因为我当时不在微软)Before/AfterTargets的确是为了让用户可以在不拥有某个目标的情况下插入要执行的目标而创建的,而创建者没有使用上述模式。
Microsoft.Common.targets
文件时想到了通过属性定义依赖关系的想法。对我来说,为了获得4.0现在提供的内容,这种方法非常冗长和hacky。我同意它更加明确,对于像描述的那样的困难工作流程,你应该始终使用它,即使在4.0中也是如此!也许我没有表达清楚,但问题在于LOCAL与SERVER构建中有一些目标必须流入预定义的工作流程中。也许我会尝试属性方法,但我非常怀疑。 - MatthiasAfterTargets="Build" DependsOnTargets="Build"
吗?但是这些目标仍然需要在Build
之后运行(但不是在Clean
之后)? - Steven Liekens