为什么要使用构建系统?

本页介绍了什么是构建系统、它们的作用、为什么您应该使用构建系统,以及为什么随着您的组织开始扩展,编译器和构建脚本并不是最佳选择。适用于在构建系统方面没有太多经验的开发者。

什么是构建系统?

从根本上讲,所有构建系统都有一个简单明了的用途:将工程师编写的源代码转换为可供机器读取的可执行二进制文件。构建系统不仅仅适用于人工编写的代码,还支持机器自动创建 build,无论是用于测试还是发布到生产环境。在拥有数千名工程师的组织中,大多数构建都是自动触发的,而不是由工程师直接触发的,这种情况很常见。

我不能直接使用编译器吗?

对构建系统的需求可能不会立即显现出来。大多数工程师在学习编码时不使用构建系统:大多数工程师都先直接从命令行调用 gccjavac 等工具,或者在集成开发环境 (IDE) 中调用等效工具。只要所有源代码都位于同一目录中,如下所示的命令就能正常运行:

javac *.java

这会指示 Java 编译器将当前目录中的每个 Java 源文件转换为二进制类文件。在最简单的情况下,只需这样做即可。

不过,代码一扩大,复杂性就会随之而来。javac 足够智能,可以查看当前目录的子目录,以查找要导入的代码。但无法找到存储在文件系统的其他部分(可能是由多个项目共享的库)中的代码。它也只知道如何构建 Java 代码大型系统通常涉及使用各种编程语言编写的不同部分,这些部分之间存在复杂的依赖关系网,这意味着任何单一语言的编译器都无法构建整个系统。

一旦您要处理多种语言或多个编译单元的代码,构建代码就不再是一项单步流程。现在,您必须评估代码所依赖的组件,并以正确的顺序构建这些部分,并且可能为每个部分使用不同的工具集。如果任何依赖项发生变化,您必须重复此过程,以免依赖于过时的二进制文件。对于中等大小的代码库,此过程很快就会变得繁琐且容易出错。

此外,编译器也不知道如何处理外部依赖项,例如 Java 中的第三方 JAR 文件。如果没有构建系统,您可以通过以下方式进行管理:从互联网下载依赖项,将其粘贴到硬盘上的 lib 文件夹中,并将编译器配置为从该目录读取库。随着时间的推移,这些外部依赖项的更新、版本和来源难以维护。

Shell 脚本呢?

假设您的爱好项目一开始非常简单,只需使用编译器就能构建,但您开始遇到上述一些问题。也许您仍然认为自己不需要构建系统,并且可以使用一些简单的 Shell 脚本来自动处理繁琐的部分,这些脚本会按正确的顺序进行构建。这在短期内会有所帮助,但很快您就会遇到更多问题:

  • 这会很繁琐。随着系统变得越来越复杂,您在构建脚本上花费的时间与编写实际代码的时间差不多。调试 shell 脚本很麻烦,因为越来越多的黑客会层层叠加。

  • 速度很慢。为确保您不会意外依赖于过时库,您可以让 build 脚本在每次运行时按顺序构建每个依赖项。您可以考虑添加一些逻辑来检测哪些部分需要重新构建,但这听起来极其复杂且容易出错。或者,您考虑每次都指定哪些部分需要重新构建,但后来又回到了原状。

  • 好消息:是时候发布了!最好先弄清楚您需要传递给 jar 命令的所有参数,以便生成最终 build。并记住如何上传该文件并将其推送到中央存储库。构建和推送文档更新,并向用户发送通知。嗯,可能需要使用其他脚本...

  • 惨了!您的硬盘崩溃了,现在您需要重新创建整个系统。您很聪明,将所有源文件都纳入了版本控制,但下载的库呢?您能否重新找到它们,并确保它们与首次下载时是相同的版本?您的脚本可能依赖于在特定位置安装特定工具 - 您能否恢复相同的环境,以便脚本再次正常运行?您很久以前为使编译器正常运行而后忘记的那些环境变量如何?

  • 尽管存在问题,但您的项目非常成功,您可以开始招聘更多工程师了。现在,您已意识到,之前的问题并不难事,每次有新开发者加入您的团队时,您都需要经历同样痛苦的引导过程。即使您已尽最大努力,每个人的系统也仍存在细微差异。通常情况下,适合一个人计算机的内容并不一定适合另一个人,并且每次需要花几个小时调试工具路径或库版本才能找出差异之处。

  • 您决定需要实现构建系统的自动化。从理论上讲,只需购买一台新电脑并将其设置为使用 cron 每天晚上运行构建脚本,即可轻松实现此目的。您仍然需要经历痛苦的设置过程,但现在您无法受益于人脑检测和解决小问题。现在,每天早上上班时,您都会发现昨晚的 build 失败了,因为昨天有位开发者进行了一项更改,该更改在其系统上有效,但在自动化 build 系统上无效。每次都是一个简单的修复,但问题非常频繁,最终您每天都要花费大量时间来发现和应用这些简单的修复。

  • 随着项目的增长,构建速度会越来越慢。有一天,在等待构建完成时,您悲伤地凝视着正在休假的同事的空闲桌面,并希望有办法利用所有浪费的计算能力。

您遇到了典型的规模问题。如果单个开发者在最多一两周的时间里编写了最多几百行代码(这可能是刚从大学毕业的初级开发者的全部体验),那么就只需要一个编译器。脚本或许可以帮您更进一步。但是,一旦您需要协调多个开发者及其机器,即使是完美的构建脚本也不足以应付,因为很难考虑到这些机器之间的细微差异。至此,这种简单的方法就打破了常规,是时候投资一个真实的构建系统了。