| --- | |
| layout: documentation | |
| title: Starlark Build Configurations | |
| --- | |
| # Starlark Build Configurations | |
| - [Overview](#overview) | |
| - [Current Status](#current-status) | |
| - [User-defined Build Settings](#user-defined-build-settings) | |
| - [Build Settings and Select](#build-settings-and-select) | |
| - [User-defined Transitions](#user-defined-transitions) | |
| - [Integration With Platforms and Toolchains](#integration-with-platforms-and-toolchains) | |
| - [Also See](#also-see) | |
| ## Overview | |
| Starlark build configuration is Bazel's API for customizing how your project | |
| builds. | |
| This makes it possible to: | |
| * define custom flags for your project, obsoleting the need for | |
| [`--define`](../configurable-attributes.html#custom-keys) | |
| * write | |
| [transitions](lib/transition.html#transition) to configure deps in different configurations | |
| than their parents (e.g. `--compilation_mode=opt` or `--cpu=arm`) | |
| * bake better defaults into rules (e.g. automatically build `//my:android_app` | |
| with a specified SDK) | |
| and more, all completely from .bzl files (no Bazel release required). | |
| <!-- [TOC] --> | |
| ## Current Status | |
| As of Q4'19, everything documented here works but | |
| may have memory and performance consequences as we work on scaling concerns. | |
| Related issues: | |
| * [#5574](https://github.com/bazelbuild/bazel/issues/5574) - Starlark support | |
| for custom configuration transitions | |
| * [#5575](https://github.com/bazelbuild/bazel/issues/5575) - Starlark support | |
| for multi-arch ("fat") binaries | |
| * [#5577](https://github.com/bazelbuild/bazel/issues/5577) - Starlark support | |
| for custom build flags | |
| * [#5578](https://github.com/bazelbuild/bazel/issues/5578) - Configuration | |
| doesn't block native to Skylark rules migration | |
| ## User-defined Build Settings | |
| A build setting is a single piece of | |
| [configuration](rules.html#configurations) | |
| information. Think of a configuration as a key/value map. Setting `--cpu=ppc` | |
| and `--copt="-DFoo"` produces a configuration that looks like | |
| `{cpu: ppc, copt: "-DFoo"}`. Each entry is a build setting. | |
| Traditional flags like `cpu` and `copt` are native settings i.e. | |
| their keys are defined and their values are set inside native bazel java code. | |
| Bazel users can only read and write them via the command line | |
| and other APIs maintained natively. Changing native flags, and the APIs | |
| that expose them, requires a bazel release. User-defined build | |
| settings are defined in `.bzl` files (and thus, don't need a bazel release to | |
| register changes). They also can be set via the command line | |
| (if they're designated as `flags`, see more below), but can also be | |
| set via [user-defined transitions](#user-defined-transitions). | |
| ### Defining Build Settings | |
| #### The `build_setting` `rule()` Parameter | |
| Build settings are rules like any other rule and are differentiated using the | |
| Starlark `rule()` function's `build_setting` | |
| [attribute](lib/globals.html#rule.build_setting). | |
| ```python | |
| # example/buildsettings/build_settings.bzl | |
| string_flag = rule( | |
| implementation = _impl, | |
| build_setting = config.string(flag = True) | |
| ) | |
| ``` | |
| The `build_setting` attribute takes a function that designates the type of the | |
| build setting. The type is limited to a set of basic Starlark types like | |
| `bool` and `string`. See the `config` module | |
| [documentation](lib/config.html) for details. More complicated typing can be done in the rule's | |
| implementation function. More on this below. | |
| The `config` function also takes an optional boolean parameter, `flag`, which is | |
| set to false by default. if `flag` is set to true, the build setting can be set | |
| on the command line by users as well as internally by rule writers via default | |
| values and | |
| [transitions](lib/transition.html#transition). | |
| Not all settings should be settable by users. For example if you as a rule | |
| writer have some debug mode that you'd like to turn on inside test rules, | |
| you don't want to give users the ability to indiscriminately turn on that | |
| feature inside other non-test rules. | |
| #### Using `ctx.build_setting_value` | |
| Like all rules, build setting rules have [implementation functions](rules.html#implementation-function). | |
| The basic Starlark-type value of the build settings can be accessed via the | |
| `ctx.build_setting_value` method. This method is only available to | |
| [`ctx`](lib/ctx.html) objects of build setting rules. These implementation methods can directly | |
| forward the build settings value or do additional work on it, like type checking | |
| or more complex struct creation. Here's how you would implement an `enum`-typed | |
| build setting: | |
| ```python | |
| # example/buildsettings/build_settings.bzl | |
| TemperatureProvider = provider(fields = ['type']) | |
| temperatures = ["HOT", "LUKEWARM", "ICED"] | |
| def _impl(ctx): | |
| raw_temperature = ctx.build_setting_value | |
| if raw_temperature not in temperatures: | |
| fail(str(ctx.label) + " build setting allowed to take values {" | |
| + ", ".join(temperatures) + "} but was set to unallowed value " | |
| + raw_temperature) | |
| return TemperatureProvider(type = raw_temperature) | |
| temperature = rule( | |
| implementation = _impl, | |
| build_setting = config.string(flag = True) | |
| ) | |
| ``` | |
| Note: if a rule depends on a build setting, it will receive whatever providers | |
| the build setting implementation function returns, like any other dependency. | |
| But all other references to the value of the build setting (e.g. in transitions) | |
| will see its basic Starlark-typed value, not this post implementation function | |
| value. | |
| #### Instantiating Build Settings | |
| Rules defined with the `build_setting` parameter have an implicit mandatory | |
| `build_setting_default` attribute. This attribute takes on the same type as | |
| declared by the `build_setting` param. | |
| ```python | |
| # example/buildsettings/build_settings.bzl | |
| FlavorProvider = provider(fields = ['type']) | |
| def _impl(ctx): | |
| return FlavorProvider(type = ctx.build_setting_value) | |
| flavor = rule( | |
| implementation = _impl, | |
| build_setting = config.string(flag = True) | |
| ) | |
| ``` | |
| ```python | |
| # example/buildsettings/BUILD | |
| load("//example/buildsettings:build_settings.bzl", "flavor") | |
| flavor( | |
| name = "favorite_flavor", | |
| build_setting_default = "APPLE" | |
| ) | |
| ``` | |
| A collection of the most common build setting rules can be found in | |
| [skylib](https://github.com/bazelbuild/bazel-skylib/blob/master/rules/common_settings.bzl). | |
| ### Using Build Settings | |
| #### Depending on Build Settings | |
| If a target would like to read a piece of configuration information, it can | |
| directly depend on the build setting via a regular attribute dependency. | |
| ```python | |
| # example/rules.bzl | |
| load("//example/buildsettings:build_settings.bzl", "FlavorProvider") | |
| def _rule_impl(ctx): | |
| if ctx.attr.flavor[FlavorProvider].type == "ORANGE": | |
| ... | |
| drink_rule = rule( | |
| implementation = _rule_impl, | |
| attrs = { | |
| "flavor": attr.label() | |
| } | |
| ) | |
| ``` | |
| ```python | |
| # example/BUILD | |
| load("//example:rules.bzl", "drink_rule") | |
| load("//example/buildsettings:build_settings.bzl", "flavor") | |
| flavor( | |
| name = "favorite_flavor", | |
| build_setting_default = "APPLE" | |
| ) | |
| drink_rule( | |
| name = "my_drink", | |
| flavor = ":favorite_flavor", | |
| ) | |
| ``` | |
| Languages may wish to create a canonical set of build settings which all rules | |
| for that language depend on. Though the native concept of `fragments` no longer | |
| exists as a hardcoded object in Starlark configuration world, one way to | |
| translate this concept would be to use sets of common implicit attributes. For | |
| example: | |
| ```python | |
| # kotlin/rules.bzl | |
| _KOTLIN_CONFIG = { | |
| "_compiler": attr.label(default = "//kotlin/config:compiler-flag"), | |
| "_mode": attr.label(default = "//kotlin/config:mode-flag"), | |
| ... | |
| } | |
| ... | |
| kotlin_library = rule( | |
| implementation = _rule_impl, | |
| attrs = dicts.add({ | |
| "library-attr": attr.string() | |
| }, _KOTLIN_CONFIG) | |
| ) | |
| kotlin_binary = rule( | |
| implementation = _binary_impl, | |
| attrs = dicts.add({ | |
| "binary-attr": attr.label() | |
| }, _KOTLIN_CONFIG) | |
| ``` | |
| #### Settings Build Settings on the command line | |
| Build settings are set on the command line like any other flag. Boolean build | |
| settings understand no-prefixes and both equals and space syntaxes are supported. | |
| The name of build settings is their full target path: | |
| ```shell | |
| $ bazel build //my/target --//example:favorite_flavor="PAMPLEMOUSSE" | |
| ``` | |
| There are plans to implement shorthand mapping of flag labels so users don't | |
| need to use their entire target path each time i.e.: | |
| ```shell | |
| $ bazel build //my/target --cpu=k8 --noboolean_flag | |
| ``` | |
| instead of | |
| ```shell | |
| $ bazel build //my/target --//third_party/bazel/src/main:cpu=k8 --no//my/project:boolean_flag | |
| ``` | |
| ### Label-typed Build Settings | |
| Unlike other build settings, label-typed settings cannot be defined using the | |
| `build_setting` rule parameter. Instead, bazel has two built-in rules: | |
| `label_flag` and `label_setting`. These rules forward the providers of the | |
| actual target to which the build setting is set. `label_flag` and | |
| `label_setting` can be read/written by transitions and `label_flag` can be set | |
| by the user like other `build_setting` rules can. Their only difference is they | |
| can't customely defined. | |
| Label-typed settings will eventually replace the functionality of late-bound | |
| defaults. Late-bound default attributes are Label-typed attributes whose | |
| final values can be affected by configuration. In Starlark, this will replace | |
| the [`configuration_field`](lib/globals.html#configuration_field) | |
| API. | |
| ```python | |
| # example/rules.bzl | |
| MyProvider = provider(field = ["my_field"]) | |
| def _dep_impl(ctx): | |
| return MyProvider(my_field = "yeehaw") | |
| dep_rule = rule( | |
| implementation = _dep_impl | |
| ) | |
| def _parent_impl(ctx): | |
| if ctx.attr.my_field_provider[MyProvider] == "cowabunga": | |
| ... | |
| parent_rule = rule( | |
| implementation = _parent_impl, | |
| attrs = { "my_field_provider": attr.label() } | |
| ) | |
| ``` | |
| ```python | |
| # example/BUILD | |
| load("//example:rules.bzl", "dep_rule", "parent_rule") | |
| dep_rule(name = "dep") | |
| parent_rule(name = "parent", my_field_provider = ":my_field_provider") | |
| label_flag( | |
| name = "my_field_provider", | |
| build_setting_default = ":dep" | |
| ) | |
| ``` | |
| TODO(bazel-team): Expand supported build setting types. | |
| ## Build Settings and Select | |
| Users can configure attributes on build settings by using | |
| [`select()`](../be/functions.html#select). Build setting targets can be passed to the `flag_values` attribute of | |
| `config_setting`. The value to match to the configuration is passed as a | |
| `String` then parsed to the type of the build setting for matching. | |
| ```python | |
| config_setting( | |
| name = "my_config", | |
| flag_values = { | |
| "//example:favorite_flavor": "MANGO" | |
| } | |
| ) | |
| ``` | |
| ## User-defined Transitions | |
| A configuration | |
| [transition](lib/transition.html#transition) | |
| is how we change configuration of | |
| configured targets in the build graph. | |
| ### Defining Transitions in Starlark | |
| Transitions define configuration changes between rules. For example, a request | |
| like "compile my dependency for a different CPU than its parent" is handled by a | |
| transition. | |
| Formally, a transition is a function from an input configuration to one or more | |
| output configurations. Most transitions are 1:1 e.g. "override the input | |
| configuration with `--cpu=ppc`". 1:2+ transitions can also exist but come | |
| with special restrictions. | |
| In Starlark, transitions are defined much like rules, with a defining | |
| `transition()` | |
| [function](lib/transition.html#transition) | |
| and an implementation function. | |
| ```python | |
| # example/transitions/transitions.bzl | |
| def _impl(settings, attr): | |
| _ignore = (settings, attr) | |
| return {"//example:favorite_flavor" : "MINT"} | |
| hot_chocolate_transition = transition( | |
| implementation = _impl, | |
| inputs = [], | |
| outputs = ["//example:favorite_flavor"] | |
| ) | |
| ``` | |
| The `transition()` function takes in an implementation function, a set of | |
| build settings to read(`inputs`), and a set of build settings to write | |
| (`outputs`). The implementation function has two parameters, `settings` and | |
| `attr`. `settings` is a dictionary {`String`:`Object`} of all settings declared | |
| in the `inputs` parameter to `transition()`. | |
| `attr` is a dictionary of attributes and values of the rule to which the | |
| transition is attached. When attached as an | |
| [outgoing edge transition](#outgoing-edge-transitions), the values of these | |
| attributes are all configured i.e. post-select() resolution. When attached as | |
| an [incoming edge transition](#incoming-edge-transitions), `attr` does not | |
| include any attributes that use a selector to resolve their value. If an | |
| incoming edge transition on `--foo` reads attribute `bar` and then also | |
| selects on `--foo` to set attribute `bar`, then there's a chance for the | |
| incoming edge transition to read the wrong value of `bar` in the transition. | |
| Note: since transitions are attached to rule definitions and `select()`s are | |
| attached to rule instantiations (i.e. targets), errors related to `select()`s on | |
| read attributes will pop up when users create targets rather than when rules are | |
| written. It may be worth taking extra care to communicate to rule users which | |
| attributes they should be wary of selecting on or taking other precautions. | |
| The implementation function must return a dictionary (or list of | |
| dictionaries, in the case of | |
| transitions with multiple output configurations) | |
| of new build settings values to apply. The returned dictionary keyset(s) must | |
| contain exactly the set of build settings passed to the `outputs` | |
| parameter of the transition function. This is true even if a build setting is | |
| not actually changed over the course of the transition - its original value must | |
| be explicitly passed through in the returned dictionary. | |
| #### Defining 1:2+ Transitions | |
| [Outgoing edge transition](#outgoing-edge-transitions) can map a single input | |
| configuration to two or more output configurations. These are defined in | |
| Starlark by returning a list of dictionaries in the transition implementation | |
| function. | |
| ```python | |
| # example/transitions/transitions.bzl | |
| def _impl(settings, attr): | |
| _ignore = (settings, attr) | |
| return [ | |
| {"//example:favorite_flavor" : "LATTE"}, | |
| {"//example:favorite_flavor" : "MOCHA"}, | |
| ] | |
| coffee_transition = transition( | |
| implementation = _impl, | |
| inputs = [], | |
| outputs = ["//example:favorite_flavor"] | |
| ) | |
| ``` | |
| ### Attaching Transitions | |
| Transitions can be attached in two places: incoming edges and outgoing edges. | |
| Effectively this means rules can transition their own configuration (incoming | |
| edge transition) and transition their dependencies' configurations (outgoing | |
| edge transition). | |
| #### Incoming Edge Transitions | |
| Incoming edge transitions are activated by attaching a `transition` object | |
| (created by `transition()`) to `rule()`'s `cfg` parameter: | |
| ```python | |
| # example/rules.bzl | |
| load("example/transitions:transitions.bzl", "hot_chocolate_transition") | |
| drink_rule = rule( | |
| implementation = _impl, | |
| cfg = hot_chocolate_transition, | |
| ... | |
| ``` | |
| Incoming edge transitions must be 1:1 transitions. | |
| ### Outgoing Edge Transitions | |
| Outgoing edge transitions are activated by attaching a `transition` object | |
| (created by `transition()`) to an attribute's `cfg` parameter: | |
| ```python | |
| # example/rules.bzl | |
| load("example/transitions:transitions.bzl", "coffee_transition") | |
| drink_rule = rule( | |
| implementation = _impl, | |
| attrs = { "dep": attr.label(cfg = coffee_transition)} | |
| ... | |
| ``` | |
| Outgoing edge transitions can be 1:1 or 1:2+. | |
| ### Transitions on Native Options | |
| WARNING: This feature will be deprecated soon. Use at your own risk. | |
| Starlark transitions can also declare reads and writes on native options via | |
| a special prefix to the option name. | |
| ```python | |
| # example/transitions/transitions.bzl | |
| def _impl(settings, attr): | |
| _ignore = (settings, attr) | |
| return {"//command_line_option:cpu": "k8"} | |
| cpu_transition = transition( | |
| implementation = _impl, | |
| inputs = [], | |
| outputs = ["//command_line_option:cpu"] | |
| ``` | |
| ### Accessing Attributes with Transitions | |
| When [attaching a transition to an outgoing edge](#outgoing-edge-transitions) | |
| (regardless of whether the transition is a 1:1 or 1:2+ transition) access to | |
| values of that attribute in the rule implementation changes. Access through | |
| `ctx.attr` is forced to be a list if it isn't already. The order of elements in | |
| this list is unspecified. | |
| ```python | |
| # example/transitions/rules.bzl | |
| def _transition_impl(settings, attr): | |
| return {"//example:favorite_flavor" : "LATTE"}, | |
| coffee_transition = transition( | |
| implementation = _transition_impl, | |
| inputs = [], | |
| outputs = ["//example:favorite_flavor"] | |
| ) | |
| def _rule_impl(ctx): | |
| # Note: List access even though "dep" is not declared as list | |
| transitioned_dep = ctx.attr.dep[0] | |
| # Note: Access doesn't change, other_deps was already a list | |
| for other dep in ctx.attr.other_deps: | |
| # ... | |
| coffee_rule = rule( | |
| implementation = _rule_impl, | |
| attrs = { | |
| "dep": attr.label(cfg = coffee_transition) | |
| "other_deps": attr.label_list(cfg = coffee_transition) | |
| }) | |
| ``` | |
| Access to the value of a single branch of a 1:2+ | |
| [has not been implemented yet](https://github.com/bazelbuild/bazel/issues/8633). | |
| ## Integration with Platforms and Toolchains | |
| Many native flags today, like `--cpu` and `--crosstool_top` are related to | |
| toolchain resolution. In the future, explicit transitions on these types of | |
| flags will likely be replaced by transitioning on the | |
| [target platform](../platforms.html) | |
| ## Also See: | |
| * [Starlark Build Configuration](https://docs.google.com/document/d/1vc8v-kXjvgZOdQdnxPTaV0rrLxtP2XwnD2tAZlYJOqw/edit?usp=sharing) | |
| * [Bazel Configurability Roadmap](https://bazel.build/roadmaps/configuration.html) |