starlark 设计一瞥

英文原文地址:https://blog.bazel.build/2017/03/21/design-of-skylark.html

本文主要介绍 starlark (旧名:skylark)的设计原理,Bazel 用 starlark 语言构建工程。

简史

N年前,Google使用 Makefile 来管理代码。正如坊间传言,Makefile 管理代码太多时,就有点 hold 不住了。救急的解决办法是用 python 脚本来生成 Makefile,编译过程中用到的 python 函数都存储在 BUILD 文件中。但是这样还是太慢,瓶颈在 Make。

Blaze(后来以 Bazel 开源)项目开始于2006年。Blaze 用一个简单的解释器来读取 BUILD 文件(只支持函数调用,列表解析和变量分配)。Blaze 不能直接解析 BUILD 文件,通过 Python 解释器将用户的 BUILD 文件预处理为一个简化的 BUILD 文件,简化后的 BUILD 文件供 Blaze 使用。

这样处理起来简便并且允许开发者创建自己的宏。但是代码维护,性能,安全性又出现了很多问题。因为Blaze 本身不能解析 BUILD 文件,导致编译变得越来越复杂。

Bazel 经过重新设计,剔除了 Python 预处理的步骤。我们保留了 Python 的语法来移植基础代码。效果看上去不错:很多人喜欢我们的 BUILD 文件语法,并对一些构建工具进行了适配,比如 Buck, Pants 和 Please。

设计要求

我们决定将构建过程和扩展功能(宏和规则)分开描述,构建过程的描述放在 BUILD 文件里,然后扩展功能的描述放在 .bzl 文件里,但是他们仍然采用同样的解释器。我们希望代码易读且易于维护,希望成千上万的工程师采用 Bazel,大部分工程师对编译工具内部不熟悉,也不愿意花时间来学习一门新语言。所以 BUILD 文件就需要简明扼要,便于使用。

语言其他要求:

         1. 可以在 JVM 上运行。因为Bazel 是用 Java写的,本语言需要与 Bazel 分享数据结构。

         2. 使用 Python 语法,保留我们的基础代码。

         3. 确保确定性和密封性。我们必须保证代码的执行始终都能产生一致的结果。比如,我们禁止访问 I/O、日期和时间,以便确保字典的迭代。

         4. 确保线程安全。我们需要 BUILD 文件并行执行。执行的代码需要线程安全来保证结果的确定性。

关于性能方面,一个典型的 BUILD 文件很简单,可以很快地被执行。在大部分情况下,直接执行代码比先编译一下再执行要快。

并行性

Starlark 如何处理并行是它的一大特点。在 Bazel 中,一个大的工程需要处理很多的 BUILD 文件,所以我们需要并行加载它们。每个 BUILD 文件可能会用到很多扩展功能,扩展功能又可能会用到很多文件。这就是说我们需要准备好一张依赖性图表。

Bazel 首先会并行分析这张依赖表的各分支。依次加载它们各自依赖的文件,也就是解析各个 BUILD 和 .bzl 文件,不关心 load 语句的顺序。

每个文件几乎同时加载。当文件被解析后,它的定义(全局变量和函数)都被缓存了。其他任意文件都可以通过这个缓存访问其中元素。

由于多个线程可能会同时访问一个变量,所以我们需要严格地保证线程安全。解决方法很简单:当我们缓存了一个文件的定义,会把这些定义先“冷冻”起来,设置成只读,比如,你可以反复使用一个数组,但是不能修改其中的元素。但是你可以对这个数组做备份,然后进行修改。

你可能感兴趣的:(starlark 设计一瞥)