The page is an overview of Starlark, formerly known as Skylark, the language used in Bazel. For a complete list of functions and types, check Starlark's API reference.
For more information about the language, see Starlark's GitHub repo.
Starlark's syntax is inspired by Python3. This is valid syntax in 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's semantics can differ from Python, but behavioral differences are rare, except for cases where Starlark raises an error. The following Python types are supported:
Starlark favors immutability. Two mutable data structures are available: lists and dicts. Changes to mutable data-structures, such as appending a value to a list or deleting an entry in a dictionary are valid only for objects created in the current context. After a context finishes, its values become immutable.
This is because Bazel builds use parallel execution. During a build, each .bzl
file and each BUILD
file get their own execution context. Each rule is also analyzed in its own context.
Let's go through an example with the file 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
Bazel creates var
when foo.bzl
loads. var
is thus part of foo.bzl
's context. When fct()
runs, it does so within the context of foo.bzl
. After evaluation for foo.bzl
completes, the environment contains an immutable entry, var
, with the value [5]
.
When another bar.bzl
loads symbols from foo.bzl
, loaded values remain immutable. For this reason, the following code in bar.bzl
is illegal:
# `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
Global variables defined in bzl
files cannot be changed outside of the bzl
file that defined them. Just like the above example using bzl
files, values returned by rules are immutable.
BUILD
files register targets via making calls to rules. .bzl
files provide definitions for constants, rules, macros, and functions.
Native functions and native rules are global symbols in BUILD
files. bzl
files need to load them using the native
module.
There are two syntactic restrictions in BUILD
files: 1) declaring functions is illegal, and 2) *args
and **kwargs
arguments are not allowed.
Global variables are immutable.
for
statements are not allowed at the top-level. Use them within functions instead. In BUILD files, you may use list comprehensions.
if
statements are not allowed at the top-level. However, if
expressions can be used: first = data[0] if len(data) > 0 else None
.
Deterministic order for iterating through Dictionaries.
Recursion is not allowed.
Int type is limited to 32-bit signed integers. Overflows will throw an error.
Modifying a collection during iteration is an error.
Except for equality tests, comparison operators <
, <=
, >=
, >
, etc. are not defined across value types. In short: 5 < 'foo'
will throw an error and 5 == "5"
will return false.
In tuples, a trailing comma is valid only when the tuple is between parentheses, e.g. write (1,)
instead of 1,
.
Dictionary literals cannot have duplicated keys. For example, this is an error: {"a": 4, "b": 7, "a": 1}
.
Strings are represented with double-quotes (e.g. when you call repr).
Strings aren't iterable.
The following Python features are not supported:
+
operator).1 < x < 5
).class
(see struct
function).import
(see load
statement).while
, yield
.lambda
and nested functions.is
(use ==
instead).try
, raise
, except
, finally
(see fail
for fatal errors).global
, nonlocal
.