构建系统的目标是保证工程师能够快速、可靠地构建代码。构建系统将工程师编写的源代码转换成可由机器读取的可执行二进制文件。
一旦我们不得不处理来自多语言或多个编译单元的代码,构建代码就不再是一个单步过程。我们需要考虑我们的代码依赖什么,并以适当的顺序构建这些片段,可能为每个片段使用不同的工具集。如果我们要改变任何依赖项,我们需要重复这个过程,以避免依赖陈旧的二进制文件。
简单的shell脚本可以自动处理那些烦琐的部分,这些脚本负责按正确的顺序构建。但脚本会变得冗长烦琐,调试脚本是一件痛苦的事。
你遇到的是一个典型的规模化扩展问题。如果你只需要在一两周内处理几百行代码,编译器就是你所需要的一切。脚本可能会带你走得更远一点。但是当需要跨多个开发人员和机器进行协作时,即使是一个完整的构建脚本也不够,因为很难解释这些机器之间的微小差异。此时,是时候投资一个真正的构建系统了。
管理自己的代码很简单,但管理它的依赖项就困难多了。有时依赖一个任务,有时依赖一个制品。有时是对代码库另一部分的内部依赖,有时是对其他团队代码或数据的外部依赖。管理依赖是构建系统最基本的工作。
基于任务的构建系统的缺点是难以并行处理、难以执行增量构建、难以维护和调试脚本。
要解决这些问题,需要从工程师手中夺走一些权力,把它放回系统中,重新认识系统的作用,不是运行任务,而是生产制品。这就是基于制品的构建系统。
基于任务的构建系统好比命令式编程,让程序员定义一系列要执行的步骤。基于制品的构建好比函数式编程,程序员描述要执行的计算,但将计算的具体执行时间和方式留给编译器。基于制品的构建系统声明清单并让系统清楚如何执行构建。
通过限制工程师的权力和灵活性,可以提高他们的生产力。构建系统通过一个高度结构化的框架限制个人选择,将决策留给自动化工具,让工程师专注于编写应用程序的有趣部分,而不是纠结于构建逻辑。
基于制品的构建将构建定义为以制品为中心而不是以任务为中心,这使得我们的构建能够规模化扩展。分布式构建系统能够利用整个计算机集群的资源提高工程师的生产力。基于制品的构建具有良好的伸缩性,对于小项目也能提升速度和正确性。
关于依赖关系管理,细粒度模块比粗粒度模块具有更好的扩展性。采用单一版本规则,所有依赖都应当显式进行版本控制。这样就能避免常见的陷阱如菱形依赖。