让我尝试一一解决这些问题...
问:*proj 文件有什么用?
这些*proj
文件是 MSBuild 的本地语言(如果您使用的是 Mono ,则为 xbuild )。在最简单的情况下,他们只是列出所有要编译的文件以及对他们使用的其他项目的所有引用。还有一些其他属性,比如目标平台、CPU 架构等。最初的想法是一个*proj
文件产生一个编译汇编单元(又名“DLL”,大致对应于 JAR)。这在很大程度上仍然是正确的,但并非总是如此。例如,TypeScript 项目可以生成多个 JS 文件。
但是 MSBuild 的核心架构使这些文件非常灵活。它们基本上建立在插件系统(称为“任务”)上,其中每个任务都做一些特定的事情。框中包含许多任务,例如“编译 C# 文件”或“输出到日志”,但您也可以添加自己的,或以包的形式安装一些,或全局安装。另外,MSBuild 的核心允许一些技巧,比如变量(某种)、循环(某种)和分支(近似),以至于您实际上可以完全在一个*proj
文件。这一切背后的最初想法是 MSBuild 将成为整个构建过程的主要工具,不需要其他工具。对于简单的玩具项目,它有点像:当您只需要将一堆文件编译为 DLL 时,MSBuild 可以出色地完成这项工作。即使是不那么简单的项目,人们也试图完全在*proj
文件中完成整个构建。即使是现在,也有一些项目可以做到这一点。
然而,随着时间的推移,很明显用 XML 编写高级构建逻辑是如此棘手和落后,以至于它很快变得无法维护。因此出现了一堆专门用于实现高级构建逻辑的工具。这让我...
问:FAKE 不是 MSBuild 的替代品吗?
是的。嗯,不,实际上不是。也许。有点儿。
正如我上面所说,可以在 MSBuild 中编写整个构建逻辑:编译、复制、压缩(在需要时)、捆绑(如果需要)、打包(在适当时)和发布(在合法时:-)。但是编写起来非常尴尬,并且很快变得无法维护。
输入 FAKE:在 FAKE 中,您完全在 F# 中编写构建过程的整个逻辑。这意味着您可以使用库、抽象、高阶函数、特殊类型——真正的高级语言的所有优点。您可以使用 glob 模式指定源文件列表,使用 FscHelper 调用 F# 编译器,使用XUnitHelper运行测试,使用Paket helper打包和发布,部署到 Azure,甚至在 Slack 上通知您的队友——所有这些都无需离开舒适的功能上的好处。
但是有一个问题:构建脚本无法通过工具进行分析。
因为它是一个真正的程序,而不是一种数据格式,IDE 不知道如何从中提取源文件列表,或者如何添加新文件;包管理器不知道在哪里插入对包的引用,等等。
另一方面,MSBuild 文件是可分析性的定义:它们是 XML,并且对于去往何处有严格的标准。
于是它变成了:我们使用 MSBuild 指定一些严格但简单的属性,然后使用 FAKE 脚本来“驱动”更高级别的构建逻辑——例如运行测试、生成文档、发布、部署等。指定文件列表并直接调用 F# 编译器,我们的构建脚本只调用 MSBuild。
问:为什么 Paket 需要引用和依赖?
简而言之:因为我们通常一次处理多个编译单元(又名“项目”)(我们将这样的项目系统称为“解决方案”),并且当一个解决方案下的不同项目使用不同版本时,我们不喜欢它相同的包裹:冲突比比皆是,我们的头脑爆炸了。因此,我们为整个解决方案指定一个包列表(在“依赖项”中),然后每个项目都可以选择它实际使用的那些(在“引用”中)。
长篇大论:@rmunn 的出色回答更详细地讨论了这一点。
同样有趣的是:“本机”.NET 包管理器 NuGet 实际上没有这个属性。NuGet 下的每个项目都有自己的、完全独立的包列表。而这种令人头疼的版本冲突确实会发生。在大型解决方案上,常常令人沮丧。这是Paket优越的众多原因之一。