我们如何意外地为OCaml构建了一个更好的构建系统
构建系统是开发者工具箱中最重要的工具之一。简单来说,它负责从一堆不同的源文件中生成可运行的程序,调用编译器、设置并执行测试套件等。由于开发者每天都要与它打交道,因此它必须快速——但同时也需要具备灵活性。
大约在2012年,我们对当时OCaml的标准构建系统OMake感到不满,于是决定自己构建一个新的系统,我们称之为Jenga。Jenga在我们内部运行得很好,我们认为更广泛的社区可能也会觉得它有用。于是我们决定开源Jenga,希望其他人试用后会喜欢它,甚至可能为它贡献代码。开源Jenga也让我们更容易开源我们的代码。
然而,现实是:几乎没有人真正想用Jenga。一方面,它不支持Windows系统;另一方面,使用Jenga实际上意味着要采用“Jane Street式”的OCaml构建方式。由于外部用户的接受度太低,我们最终决定不再开源Jenga。于是,我们又回到了原点。
到了2016年,我们受够了这种情况,决定开发一个简单的跨平台工具,名为Jbuilder。这个工具允许外部用户构建我们的代码,而不必完全采用Jenga,同时也让我们免于将构建系统重写为当时新兴的OCaml构建标准——OCamlbuild。
Jbuilder能够理解Jenga用于构建配置的jbuild
文件,并按拓扑顺序执行所有所需的编译命令。它并不是传统意义上的构建系统:每次都会重新执行所有命令,而不是只重新执行那些输入发生变化的命令。
Jbuilder大受欢迎,最终成为“Dune”
然后,奇怪的事情发生了。人们非常喜欢Jbuilder。他们不仅用它来构建我们的包,还用它来构建自己的项目。起初我们并不理解这一点,毕竟Jbuilder并不是一个真正的构建系统,它只是一个简单的兼容性工具。
最终我们意识到,Jbuilder的吸引力在于它的速度。事实证明,Jbuilder比其他选项快得多,编译我们的项目时速度比OCamlbuild快了大约5倍。再加上它的跨平台性和易于修改的特性,这些因素让它成为了早期用户和贡献者的首选。
于是,我们与OCaml Labs(以及现在的Tarides)合作,开始将Jbuilder打造成一个真正的构建系统,添加更多功能,使其成为更广泛的开源社区的有用工具。
然而,我们遇到了另一个问题:名字。
原来,Borland已经有一个名为“JBuilder”的Java IDE。虽然这个系统早已停止维护,但我们还是费了一番功夫找到版权所有者,询问是否可以使用这个名字。结果,对方不同意。
于是我们决定换个新名字。我们进行了一些社区调研,最终“Dune”胜出。
与此同时,Dune的受欢迎程度迅速飙升。人们开始认真使用它,而我们发现自己陷入了一个有点荒谬的境地:现在我们需要维护和支持两个完整的构建系统。
Jenga vs. Dune
我们逐渐意识到,Dune是更好的系统——它经过重新设计,对大多数用户的构建速度更快,拥有更广泛的用户群体,API和用户体验也更好。这让我们不得不面对一个问题:“我们什么时候将Jane Street的代码迁移到Dune上?”考虑到这个工具的起源,这个问题显得有点荒谬,但事实就是如此。
答案总是“明年”,因为构建系统团队已经忙于应对我们不断增长的代码库(2016年Dune刚起步时,我们有400万行OCaml代码;如今我们有6500万行OCaml代码,外加500万行Python代码)。迁移到Dune的任务如此艰巨,以至于我们一直没有完全启动这个项目。不过,我们还是乐观地估计,未来6到12个月内可能会完成迁移。
直到去