Skip to content

Commit d9f780f

Browse files
committed
Improve documentation about build configuration
This commit rearranges existing documentation and adds more explanation, including a comprehensive overview of the various parts (platforms, modifiers and transitions), and how they are relevant for end users. In particular, a lot of information was moved from `rule_authors/` to `concepts/`, as build configuration is important for end users. A lot of information was also deduplicated across pages.
1 parent 3319740 commit d9f780f

File tree

8 files changed

+533
-602
lines changed

8 files changed

+533
-602
lines changed

docs/concepts/configurations.md

Lines changed: 325 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,318 @@ id: configurations
33
title: Configurations
44
---
55

6-
For rule authors see also: [Configurations](../rule_authors/configurations.md)
7-
8-
When building a target, buck always builds it in a particular "configuration."
9-
The configuration typically includes information like the target os, target
10-
arch, sanitizers, opt level, etc. One way to understand the effect that a
11-
configuration has is via the `cquery` and `uquery` commands. The cquery command
12-
will compute the appropriate configuration for a target and display a version of
13-
that target's attributes with the configuration applied. The `uquery` command
6+
Build configurations are how Buck models building the same target in
7+
different ways. This can include (but is not limited to):
8+
9+
- Target architecture
10+
- Target OS
11+
- Optimization level/build mode
12+
- Compiler type/version
13+
- Language version (e.g. C++ standard version or Rust edition)
14+
- Sanitizers
15+
- Passing arbitrary flags to build tools
16+
17+
When building a target, Buck always builds it in a particular
18+
configuration. Build configurations are also sometimes called
19+
"platforms". While technically separate, those two concepts are almost
20+
identical.
21+
22+
Build configurations are [composed](../api/build/Configuration) of a set
23+
of constraints and a set of values.
24+
25+
## Configuration constraints
26+
27+
Configuration constraints are enum-like constructs. Here is an example
28+
definitions:
29+
30+
```python
31+
# //config/BUCK
32+
33+
constraint(
34+
name = "build_mode",
35+
default = "debug",
36+
values = [
37+
"debug",
38+
"release",
39+
],
40+
)
41+
```
42+
43+
Constraint values can also be grouped into larger logical pieces.
44+
Assuming that we have also defined other constraints:
45+
46+
```python
47+
config_setting(
48+
name = "dev",
49+
constraint_values = [
50+
":build_mode[debug]",
51+
":compiler[clang_21]",
52+
":asan[enabled]",
53+
],
54+
)
55+
```
56+
57+
Note that the prelude defines some generic constraints, e.g. under
58+
`prelude//os:` and `prelude//cpu:`, which you might want to consider
59+
using for interoperability.
60+
61+
Once defined, this constraint can used in various ways, such as:
62+
63+
- Being passed on the command line to run a build in debug or release
64+
mode.
65+
- Being "selected on" so that building in debug vs release mode has
66+
different effects.
67+
- Being used for constraining compatibility (e.g. "this target can only
68+
be built in release mode" for a benchmark).
69+
- Being used to "transition" part of the build (e.g. "this target and
70+
its dependencies are always built in release mode, regardless of the
71+
dependent")
72+
73+
## Configuration values
74+
75+
Configurations can also include values taken from the buckconfig:
76+
77+
```python
78+
config_setting(
79+
name = "fastmode_enabled",
80+
values = {
81+
"build.fastmode": "true",
82+
},
83+
)
84+
```
85+
86+
This setting will be satisfied if the associated buckconfig matches,
87+
i.e. if the user passes `build.fastmode=true` via the `-c`/`--config`
88+
CLI flag, or if the following is set in the cell's `.buckconfig` file:
89+
90+
```ini
91+
[build]
92+
fastmode = true
93+
```
94+
95+
## Using configuration: `select()`
96+
97+
Configurations can be used to change the build behavior based on which
98+
value is currently active:
99+
100+
```cpp
101+
cxx_binary(
102+
name = "bin",
103+
srcs = ["main.cpp"],
104+
compiler_flags = select({
105+
"//config:build_mode[debug]": ["-O0", "-g"],
106+
"//config:build_mode[release]": ["-O3"],
107+
}),
108+
)
109+
```
110+
111+
The above example is simplistic, and build mode compiler flags would
112+
typically be set at the toolchain level, rather than per-target, but it
113+
shows how build constraints can be used to change a build's behavior.
114+
115+
`select()` can appear in almost all attributes, and it can be composed
116+
with other collection types. For example, the following is valid:
117+
118+
```python
119+
cxx_library(
120+
name = "lib",
121+
exported_deps = [
122+
"//common:lib",
123+
] + select({
124+
"//config:os[linux]": ["//linux:lib"],
125+
"//config:os[mac]": ["//mac:lib"],
126+
# `DEFAULT` is a special value that is always available.
127+
# In this case, we do not link against any extra libraries.
128+
"DEFAULT": [],
129+
}),
130+
)
131+
```
132+
133+
If multiple conditions of the `select()` match, then the select will be resolved
134+
to the "most refined" of the conditions that match. A set of constraints (as in
135+
a `config_setting`) is said to "refine" another if it is a superset of that
136+
other's constraints. The "most refined" of a set is then the condition that
137+
refines all the others. If there is no "most refined" condition of the matching
138+
ones, it is an error.
139+
140+
Note that `select()` is resolved during configuration. This happens
141+
after the evaluation of the BUCK file is completed, and so Starlark code
142+
run during BUCK file evaluation does not have access to the resolved
143+
value. This can make it difficult to have macros that do extensive
144+
modification or inspection of attributes (which should be done in rules
145+
instead). However, some functions
146+
([`select_map`](../api/build/#select_map) and
147+
[`select_test`](../api/build/#select_map)) allow performing
148+
limited operations on these objects.
149+
150+
## Using configuration: compatibility
151+
152+
Constraints can also be used to limit target compatibility. For example,
153+
assuming that our repo supports C++20, C++23 and C++26:
154+
155+
```python
156+
# Reflection is only available starting with C++26, so we require it.
157+
cxx_library(
158+
name = "uses_reflection",
159+
exported_headers = ["foo.h"],
160+
target_compatible_with = ["//:cxx_standard[26]"]
161+
)
162+
163+
# Deducing this is not available in C++20, so we make it incompatible.
164+
cxx_library(
165+
name = "uses_deducing_this",
166+
exported_headers = ["foo.h"],
167+
target_compatible_with = select({
168+
"//:cxx_standard[20]": ["prelude//:none"],
169+
"DEFAULT": [],
170+
})
171+
)
172+
```
173+
174+
Target platform compatibility is transitive, all _dependents_ of an incompatible
175+
target are incompatible. In other words, a node is compatible if and only if the
176+
node itself and all of its transitive dependencies are compatible.
177+
178+
When trying to build a target with the wrong configuration (we will see
179+
how shortly), the build will just fail (unless
180+
`--skip-incompatible-targets` is passed).
181+
182+
When trying to build a set of targets using a
183+
[pattern](./target_pattern)) (e.g. `//some/package:` or
184+
`//some/package/...`), Buck will simply ignore incompatible targets.
185+
186+
See the [reference
187+
documentation](../api/build/Select/#target_compatible_with) for more
188+
information.
189+
190+
## Changing the build configuration
191+
192+
The build configuration is determined as follows:
193+
194+
1. A base platform is resolved:
195+
1. If the user passed `--target-platforms` via the CLI, use that.
196+
2. Else, if the target being built has a `default_target_platform`
197+
attribute, use that. Note that since it is used to determine the
198+
configuration, it is one of the few attributes that are not
199+
`select`able.
200+
3. Else, use the default (`parser.target_platform_detector_spec` in
201+
the `.buckconfig` file).
202+
2. [Configuration modifiers](./modifiers.md) are applied. Those are a
203+
lightweight way to add ad-hoc constraints to an existing
204+
configuration (e.g. "build with the default configuration/platform,
205+
except with a different compiler").
206+
3. [Configuration transitions](./transitions.md) are applied. Those
207+
allow changing the configuration of parts of the build graph based on
208+
arbitrary logic (e.g. "this part of the build graph should always be
209+
built in release mode").
210+
211+
The target platform resolution is not applied to all nodes in the graph.
212+
Once the top-level nodes have been configured via the target platform
213+
resolution, the configuration is propagated to dependencies (possibly
214+
altered by transitions).
215+
216+
For example:
217+
218+
```sh
219+
# Build this target with the default configuration.
220+
buck2 build :my_target
221+
# Build it with an entirely different configuration.
222+
buck2 build :my_target --target-platforms //my/other:platform
223+
# Build it with the default configuration, plus release mode.
224+
buck2 build :my_target?release
225+
```
226+
227+
See the [configurations for author](../rule_authors/configurations.md)
228+
page for information on how to define a platform.
229+
230+
Other example:
231+
232+
```python
233+
java_binary(
234+
name = "cats",
235+
default_target_platform = "//platforms:windows-arm64-dev",
236+
deps = ["//libs:foo"],
237+
)
238+
239+
java_binary(
240+
name = "dogs",
241+
default_target_platform = "//platforms:mac-x86-dev",
242+
deps = ["//libs:foo"],
243+
)
244+
245+
java_library(
246+
name = "foo",
247+
deps = [
248+
"//libs:common",
249+
] + select({
250+
"//constraints:x86": ["//libs:x86"],
251+
"//constraints:mac-arm64": ["//libs:mac-arm64"],
252+
"//constraints:windows-arm64": ["//libs:win-arm64"],
253+
"DEFAULT": ["//libs:generic"],
254+
})
255+
```
256+
257+
When running `buck2 build //binaries:cats //binaries:dogs`, the
258+
`//binaries:cats` binary will be built in the `//platforms:windows-arm64-dev`
259+
configuration and the `//binaries:dogs` binary will be built in the
260+
`//platforms:mac-x86-dev` configuration.
261+
262+
Each of those binaries depend on `//libs:foo`, but they will get
263+
different versions of it as the binaries' configurations will each be
264+
passed down to their dependencies. For `//binaries:cats`, its resolved
265+
dependencies will include `//libs:win-arm64` and for `//binaries:dogs`,
266+
it would contain `//libs:x86`.
267+
268+
Note that `//libs:common` will be built twice, once for each
269+
configuration.
270+
271+
When running `buck2 build //binaries:cats //binaries:dogs --target-platforms
272+
//platforms:mac-x86-opt`, both `//binaries:cats` and `//binaries:dogs` will
273+
be built in the `//platforms:mac-x86-opt` configuration, use the same
274+
dependencies, which would only be built once.
275+
276+
## Configurations and output paths
277+
278+
Since a target may appear within a build in multiple different configurations,
279+
output paths cannot be derived based on just targets (as multiple actions would
280+
map to the same outputs). For this reason, the target and the configuration are
281+
encoded into output paths. The configuration is currently represented as a hash
282+
of its values (a "hashed buck-out").
283+
284+
## Target platform vs execution platform
285+
286+
Buck distinguishes two kinds of targets: "regular" ones, and the ones
287+
used as build tools. The rationale is that it is common to want
288+
different build configurations for those. For example, it is typical to
289+
want build tools (e.g. a compiler) to be built/run in release mode, even
290+
when building debug targets.
291+
292+
For this reason, Buck requires both _target_ platforms and _execution_
293+
platforms to be defined. The execution platforms are specified via the
294+
`build.execution_platforms` value in `.buckconfig`.
295+
296+
## Platform resolution
297+
298+
## Queries
299+
300+
### Getting configuration constraints from its hash
301+
302+
Build configurations are uniquely identified by their hash, which is not
303+
human friendly.
304+
305+
To determine what constraints are part of a configuration, run `buck2
306+
cquery //...` sot that Buck will discover all existing configurations,
307+
then run `buck2 audit configurations`.
308+
309+
This will list all available configurations and print their composing
310+
contraints.
311+
312+
### `cquery` and `uquery`
313+
314+
One way to understand the effect that a configuration has is via the
315+
`cquery` and `uquery` commands. The `cquery` command will compute the
316+
appropriate configuration for a target and display a version of that
317+
target's attributes with the configuration applied. The `uquery` command
14318
will not apply a configuration.
15319

16320
Here is a heavily trimmed version of the outputs of invoking `uquery` and
@@ -89,3 +393,16 @@ while the `nix` dependency is needed only for Linux. In `cquery` that
89393
distinction has been resolved; because the target has been configured for Linux,
90394
the `nix` dependency is present and indistinguishable from any other, while the
91395
`common-path` dependency is gone.
396+
397+
## Execution groups
398+
399+
Execution groups are a future feature that will allow a rule to perform
400+
execution platform resolution multiple times and then specify in which of the
401+
resolved platforms each action runs in.
402+
403+
## See also
404+
405+
- [Configuration modifiers](./modifiers.md)
406+
- [Configuration transitions](./transitions.md)
407+
- [Configurations for rule authors](../rule_authors/configurations.md)
408+
- [Configuration transitions for rule authors](../rule_authors/configuration_transitions.md)

docs/concepts/modifiers.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ title: Configuration modifiers
55

66
Modifiers (also referred to as configuration modifiers) are a feature that lets
77
users add [constraints](../../rule_authors/configurations) to individual
8-
directories, target definitions and individual `buck2` invocations. They are the
9-
recommended to customize build configuration.
8+
directories, target definitions and individual `buck2` invocations.
9+
10+
They are the recommended to customize build configurations when building
11+
targets directly. If you need to customize parts of your build graph
12+
(e.g. always build some specific dependencies in release mode),
13+
[configuration transitions](transitions.md) are more appropriate.
1014

1115
## (Open-Source Only) Getting started with modifiers
1216

0 commit comments

Comments
 (0)