让我尝试一一解决这些问题......
问:*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 中,您编写构建过程的整个逻辑entirely in F#。这意味着您可以使用库、抽象、高阶函数、特殊类型——真正的高级语言的所有优点。您可以使用 glob 模式指定源文件列表,使用 FscHelper 调用 F# 编译器,使用 XUnitHelper 运行测试,使用 Paket helper、deploy to Azure 甚至 notify your teammates on Slack 打包和发布 - 所有这些都无需离开舒适的功能。
但有一个问题:构建脚本无法通过工具进行分析。
因为它是一个真正的程序,而不是一种数据格式,IDE 不知道如何从中提取源文件列表,或者如何添加新文件;包管理器不知道在哪里插入对包的引用,等等。
另一方面,MSBuild 文件是可分析性的定义:它们是 XML,并且对于去往何处有严格的标准。
于是它变成了:我们使用 MSBuild 指定一些严格但简单的属性,然后使用 FAKE 脚本来“驱动”更高级别的构建逻辑 - 例如运行测试、生成文档、发布、部署等. 而不是指定文件列表并直接调用 F# 编译器,我们的构建脚本只是调用 MSBuild。
问:为什么 Paket 需要两个引用和依赖?
简而言之:因为我们通常会同时处理多个编译单元(又名“项目”)(我们称这样的项目系统为“解决方案”),我们不喜欢当一个解决方案下的不同项目使用相同包的不同版本时:冲突比比皆是,我们的头脑爆炸了。所以我们为整个解决方案指定一个包列表(在“依赖项”中),然后每个项目都可以选择它实际使用的包(在“引用”中)。
长篇大论:@rmunn's excellent answer 更详细地讨论了这一点。
也很有趣:“本机”.NET 包管理器 NuGet 实际上没有这个属性。 NuGet 下的每个项目都有自己的、完全独立的包列表。而这种令人头疼的版本冲突确实会发生。在大型解决方案上,常常令人沮丧。这是 Paket 优越的众多原因之一。