Starlark 语言

报告问题 查看源代码

本页面简要介绍了 Starlark(以前称为 Skylark),它是 Bazel 中使用的语言。如需查看函数和类型的完整列表,请参阅 Bazel API 参考文档

如需详细了解该语言,请参阅 Starlark 的 GitHub 代码库

如需了解 Starlark 语法和行为的权威规范,请参阅 Starlark 语言规范

语法

Starlark 的语法受到 Python3 的启发。以下是 Starlark 中的有效语法:

def fizz_buzz(n):
  """Print Fizz Buzz numbers from 1 to n."""
  for i in range(1, n + 1):
    s = ""
    if i % 3 == 0:
      s += "Fizz"
    if i % 5 == 0:
      s += "Buzz"
    print(s if s else i)

fizz_buzz(20)

Starlark 的语义可能与 Python 不同,但行为差异很少见,但 Starlark 引发错误的情况除外。支持以下 Python 类型:

可变性

Starlark 支持不可变性。有两个可变的数据结构:列表字典。对可变数据结构的更改(例如向列表中附加值或删除字典中的条目)仅对在当前上下文中创建的对象有效。上下文完成后,其值将变为不可变。

这是因为 Bazel 构建使用并行执行。在构建期间,每个 .bzl 文件和每个 BUILD 文件都会获得自己的执行上下文。此外,每条规则还会在其各自的上下文中进行分析。

我们来看一个使用文件 foo.bzl 的示例:

# `foo.bzl`
var = [] # declare a list

def fct(): # declare a function
  var.append(5) # append a value to the list

fct() # execute the fct function

加载 foo.bzl 时,Bazel 会创建 var。因此,varfoo.bzl 的上下文的一部分。当 fct() 运行时,它会在 foo.bzl 的上下文中执行此操作。foo.bzl 评估完成后,环境包含一个不可变条目 var,其值为 [5]

当另一个 bar.bzlfoo.bzl 加载符号时,加载的值是不可变的。因此,bar.bzl 中的以下代码是非法的:

# `bar.bzl`
load(":foo.bzl", "var", "fct") # loads `var`, and `fct` from `./foo.bzl`

var.append(6)  # runtime error, the list stored in var is frozen

fct()          # runtime error, fct() attempts to modify a frozen list

bzl 文件中定义的全局变量无法在定义它们的 bzl 文件之外更改。与上述使用 bzl 文件的示例一样,规则返回的值是不可变的。

BUILD 和 .bzl 文件之间的区别

BUILD 文件通过调用规则来注册目标。.bzl 文件提供了常量、规则、宏和函数的定义。

原生函数原生规则BUILD 文件中的全局符号。bzl 文件需要使用 native 模块加载它们。

BUILD 文件中有两种语法限制:1) 声明函数是非法的;2) 不允许使用 *args**kwargs 参数。

与 Python 的区别

  • 全局变量是不可变的。

  • 不允许在顶级使用 for 语句。请改为在函数中使用它们。在 BUILD 文件中,您可以使用列表推导。

  • 不允许在顶级使用 if 语句。不过,可以使用 if 表达式:first = data[0] if len(data) > 0 else None

  • 迭代字典的确定顺序。

  • 不允许递归。

  • Int 类型仅限 32 位带符号的整数。溢出将会引发错误。

  • 在迭代期间修改集合会出错。

  • 除了等式测试之外,比较运算符 <<=>=> 等不会跨值类型定义。简而言之:5 < 'foo' 会抛出错误,5 == "5" 会返回 false。

  • 在元组中,尾随逗号仅在元组位于圆括号之间(当您写入 (1,) 而不是 1, 时)有效。

  • 字典字面量不能有重复的键。例如,这是一个错误:{"a": 4, "b": 7, "a": 1}

  • 字符串用英文双引号表示(例如,当您调用 repr 时)。

  • 字符串不可迭代。

不支持以下 Python 功能:

  • 隐式字符串串联(使用显式 + 运算符)。
  • 链式比较(例如 1 < x < 5)。
  • class(请参阅 struct 函数)。
  • import(请参阅 load 语句)。
  • whileyield
  • 浮点数和集类型。
  • 和生成器表达式
  • is(改用 ==)。
  • tryraiseexceptfinally(如需了解严重错误,请参阅 fail)。
  • globalnonlocal
  • 大多数内置函数和大多数方法