为什么选择构建系统?

报告问题 查看源代码

本页面讨论了什么是构建系统、它们的用途,为什么您应该使用构建系统,以及为什么编译器和构建脚本不是您的组织开始扩展时的最佳选择。本单元的目标受众是对构建系统没有太多经验的开发者。

什么是构建系统?

从本质上讲,所有构建系统都具有直接目的:它们将工程师编写的源代码转换为可由机器读取的可执行二进制文件。构建系统不仅仅适用于人工编写的代码,还允许机器自动创建 build(无论是用于测试还是用于发布正式版)。在拥有数千名工程师的组织中,通常大部分构建是自动触发的,而不是直接由工程师触发的。

不能直接使用编译器吗?

对构建系统的需求可能不那么显而易见。大多数工程师在学习编码时不会使用构建系统:大多数工程师会先直接从命令行调用 gccjavac 等工具,或在集成开发环境 (IDE) 中调用同类工具。只要所有源代码都位于同一目录中,就可以使用以下命令:

javac *.java

此命令指示 Java 编译器获取当前目录中的每个 Java 源文件并将其转换为二进制类文件。最简单的方式就是使用它。

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

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

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

那 Shell 脚本呢?

假设您的爱好项目最初足够简单,您只需使用编译器就可以构建它,但您开始遇到前面所述的一些问题。也许您仍然认为自己不需要构建系统,并且可以使用一些简单的 shell 脚本自动处理繁琐的部分,这些脚本负责按照正确的顺序构建内容。这种做法有一段时间会解决问题,但很快您就会遇到更多问题:

  • 这会变得很无聊。随着系统变得越来越复杂,您开始处理构建脚本的时间将几乎与处理实际代码的时间一样多。调试 shell 脚本很麻烦,越来越多的黑客相互叠加。

  • 很慢为了确保您不会意外依赖于过时的库,请让构建脚本在每次运行它时按顺序构建每个依赖项。您需要考虑添加一些逻辑来检测哪些部分需要重新构建,但这听起来非常复杂,而且容易导致脚本出错。也可以考虑每次都需要重新构建哪些部分,然后返回方形部分。

  • 好消息:是时候发布了!最好弄清楚您需要将所有参数传递给 jar 命令才能构建最终 build。还记得如何上传并推送到中央代码库。构建和推送文档更新,并向用户发送通知。可能还需要其他脚本...

  • 灾难!您的硬盘发生崩溃,现在您需要重新创建整个系统。您很聪明,可以确保所有源文件都处于版本控制中,但是您下载的这些库呢?您能否再次找到这些版本,并确保它们与首次下载时的版本相同。您的脚本可能依赖于在特定位置安装的特定工具 - 您能否恢复相同的环境以恢复这些脚本?很久之前,您设置了那么多让这些环境变量正常运行的编译器,现在却忘记了,

  • 尽管存在这些问题,但您的项目仍然非常成功,您可以开始聘请更多工程师。现在您意识到,先前的问题不会发生灾难。每次新开发者加入您的团队时,您都需要经历同样的痛苦的引导过程。尽管您已经尽了最大努力,但每个人的系统仍存在细微差别。通常,对一个用户计算机有效的方法不适用于另一个人的机器,并且每次都需要花费数小时的调试工具路径或库版本才能找出差异所在。

  • 您决定需要自动执行构建系统。从理论上讲,这就像获取一台新计算机,并将其设置为每晚使用 Cron 运行构建脚本一样简单。您仍然需要经历痛苦的设置过程,但现在您无法利用人脑来检测和解决小问题。现在,当您每天早上上班时,都会看到昨晚的 build 失败了,因为昨日有开发者做出了一项更改,该更改对其系统有效,但对自动化构建系统无效。每次都是简单的修复,但结果经常如此,您最终每天会花大量时间来发现和应用这些简单的修复。

  • 随着项目的发展,构建会变得越来越慢。有一天,在等待构建完成时,您会怀着悲伤的目光凝视着正在休假的同事的空闲桌面,并希望有一种方法可以充分利用浪费的计算能力。

您遇到了常见的扩容问题。如果某个开发者至多花费了一两周时间来编写多达几百行代码(到目前为止可能是刚从大学毕业的初级开发者)。而脚本可能会让您更进一步。但是,一旦您需要协调多个开发者及其机器,就算再有完美的构建脚本也是远远不够的,因为它很难区分这些机器中的细微差别。此时,这种简单的方法已经失效,是时候投资部署真正的构建系统了。