Skip to content

Commit 885cb83

Browse files
authored
Intro examples for auto-configured builds / PROJECT.scl (#635)
For bazelbuild/bazel#24839.
1 parent c6700e2 commit 885cb83

File tree

18 files changed

+507
-1
lines changed

18 files changed

+507
-1
lines changed

.bazelci/configurations.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,17 @@ configurations: &configurations
2929
# These targets demonstrate failures: they're never meant to build.
3030
- "-//read_attr_in_transition:will-break"
3131
- "-//cc_binary_selectable_copts:app_forgets_to_set_features"
32-
# These targest are not supposed to build at the top level: only as
32+
# These targets are not supposed to build at the top level: only as
3333
# dependencies of other targets.
3434
- "-//cc_binary_selectable_copts:lib"
3535
- "-//cc_binary_selectable_copts:app_forgets_to_set_features_native_binary"
3636
- "-//cc_binary_selectable_copts:app_with_feature1_native_binary"
3737
- "-//cc_binary_selectable_copts:app_with_feature2_native_binary"
38+
# Checking in examples before supported by Bazel 9.0. Also note these examples
39+
# auto-set flags, so building /... may trigger targets that set confliciting
40+
# flags and fail the build. We could mitigate this by having dedicated test
41+
# sets for compatible target combingations.
42+
- "-//auto_configured_builds/..."
3843
# TODO: why is this crashing bazel?
3944
- "-//cc_test/..."
4045
# test_targets:
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
platform(
2+
name = "myplatform",
3+
constraint_values = [
4+
"@platforms//os:linux",
5+
"@platforms//cpu:arm",
6+
],
7+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
bazel_dep(name = "platforms", version = "1.0.0")
2+
bazel_dep(name = "bazel_skylib", version = "1.8.2")
3+
4+
local_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "local_repository")
5+
local_repository(name = "custom_flags", path = "custom_flags_impl")
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
## Auto-configured builds examples
2+
**Requirements: Bazel 9.0 or newer.**
3+
4+
*Auto-configured builds* is a Bazel 9.0+ feature that lets project owners declare which build flags should apply to their project's targets.
5+
6+
This makes it possible to write
7+
8+
$ bazel test //foo/...
9+
10+
and bazel automatically adds whatever flags are appropriate for `foo`'s builds.
11+
12+
This is similar to [bazelrc](https://bazel.build/run/bazelrc) with the crucial difference that bazelrc files are *user-registered*, not *project-registered*. That means `bazelrc`-added flags depend on who does the build and which `bazelrcs` they've registered, not which targets they're building.
13+
14+
The features described here make it easier for *anyone* to write `$ bazel test //foo/...` - a project dev, external contributor, library maintainer, IDE, AI agent, or CI system - and consistently get the same results with the same flags regardless of how they've set up their workspace or if they've ever built the project before. Project owners can then ensure everyone builds with correct, project-approved flags.
15+
16+
More info at https://github.com/bazelbuild/bazel/issues/24839.
17+
18+
### `PROJECT.scl`
19+
Flag settings are declared in a file called `PROJECT.scl`. This lives in your source repository next to your `BUILD` files.
20+
21+
`$ bazel test //foo/bar/baz:all` looks for a `PROJECT.scl` in `foo/bar/baz/` to find the project's flag settings. If that file doesn't exist, it looks in `foo/bar/`, then `foo/`, and so on until it either finds a match or determines the project has no flag settings.
22+
23+
This also applies to `bazel build` and `bazel cquery`.
24+
25+
### Project-wide settings with warnings for unexpected flags
26+
[warn](warn) is an example that sets two sets of flags for a project. The first set, `default_config`, triggers by default. The second, `debug_config`, can be set with `--scl_config=debug_config`. If the user sets any other flags, bazel emits a warning that the build is non-canonical.
27+
28+
*Default flags:*
29+
```sh
30+
$ bazel build //warn:all
31+
INFO: Reading project settings from //warn:PROJECT.scl.
32+
INFO: Applying flags from the config 'default_config' defined in //warn:PROJECT.scl: [--platforms=//:myplatform,
33+
--compilation_mode=opt, --@custom_flags//:project_flag="custom flag value"]
34+
INFO: Found 2 targets...
35+
INFO: Build completed successfully, 3 total actions
36+
```
37+
38+
*Non-default supported flags:*
39+
```sh
40+
$ bazel build //warn:all --scl_config=debug_config
41+
INFO: Reading project settings from //warn:PROJECT.scl.
42+
INFO: Applying flags from the config 'debug_config' defined in //warn:PROJECT.scl: [--platforms=//:myplatform,
43+
--compilation_mode=dbg, --@custom_flags//:project_flag="debug value"]
44+
INFO: Found 2 targets...
45+
INFO: Build completed successfully, 3 total actions
46+
```
47+
48+
*Unexpected flags:*
49+
```sh
50+
$ bazel build //warn:all --copt=abc
51+
INFO: Reading project settings from //warn:PROJECT.scl.
52+
WARNING: This build uses a project file (//warn:PROJECT.scl), but also sets output-affecting flags in the command
53+
line or user bazelrc: ['--copt=abc']. Please consider removing these flags.
54+
INFO: Applying flags from the config 'default_config' defined in //warn:PROJECT.scl: [--platforms=//:myplatform,
55+
--compilation_mode=opt, --@custom_flags//:project_flag="custom flag value"
56+
INFO: Found 2 targets...
57+
INFO: Build completed successfully, 3 total actions
58+
```
59+
60+
### Project-wide settings with incompatible flag checks
61+
[compatible](compatible) is an example that sets two sets of flags for a project. It lets the user set other, unrelated flags with a warning but fails the build if the user sets contradictory flags.
62+
63+
*Default flags:*
64+
```sh
65+
$ bazel build //compatible:all
66+
INFO: Reading project settings from //compatible:PROJECT.scl.
67+
INFO: Applying flags from the config 'default_config' defined in //compatible:PROJECT.scl:
68+
[--platforms=//:myplatform, --compilation_mode=opt, --@custom_flags//:project_flag="custom flag value"]
69+
INFO: Found 2 targets...
70+
INFO: Build completed successfully, 3 total actions
71+
```
72+
73+
*Unexpected non-conflicting flag:*
74+
```sh
75+
$ bazel build //compatible:all --copt=abc
76+
INFO: Reading project settings from //compatible:PROJECT.scl.
77+
WARNING: This build uses a project file (//compatible:PROJECT.scl), but also sets output-affecting flags in
78+
the command line or user bazelrc: ['--copt=abc']. Please consider removing these flags.
79+
INFO: Applying flags from the config 'default_config' defined in //compatible:PROJECT.scl:
80+
[--platforms=//:myplatform, --compilation_mode=opt, --@custom_flags//:project_flag="custom flag value"]
81+
INFO: Build completed successfully, 3 total actions
82+
```
83+
84+
*Unexpected conflicting flag:*
85+
```sh
86+
$ bazel build //compatible:all --compilation_mode=fastbuild
87+
INFO: Reading project settings from //compatible:PROJECT.scl.
88+
ERROR: Cannot parse options: This build uses a project file (//compatible:PROJECT.scl) that does not allow
89+
conflicting flags in the command line or user bazelrc. Found ['--compilation_mode=fastbuild']. Please remove
90+
these flags or disable project file resolution via --noenforce_project_configs.
91+
ERROR: Build did NOT complete successfully
92+
```
93+
94+
### Project-wide settings with strict checks
95+
[strict](strict) is an example that sets two sets of flags for a project. It fails the build if the user sets any flags to any values different than the project settings. This is the strictest form of flag checking. It ensures all builds use pre-approved, canonical flags.
96+
97+
*Default flags:*
98+
```sh
99+
$ bazel build //strict:all
100+
INFO: Reading project settings from //strict:PROJECT.scl.
101+
INFO: Applying flags from the config 'default_config' defined in //strict:PROJECT.scl: [--platforms=//:myplatform,
102+
--compilation_mode=opt, --@custom_flags//:project_flag="custom flag value"]
103+
INFO: Found 2 targets...
104+
INFO: Build completed successfully, 3 total actions
105+
```
106+
107+
*Unexpected non-conflicting flag:*
108+
```sh
109+
$ bazel build //strict:all --copt=abc
110+
INFO: Reading project settings from //strict:PROJECT.scl.
111+
ERROR: Cannot parse options: This build uses a project file (//strict:PROJECT.scl) that does not allow
112+
output-affeccting flags in the command line or user bazelrc. Found ['--copt=abc']. Please remove these flags or
113+
disable project file resolution via --noenforce_project_configs.
114+
ERROR: Build did NOT complete successfully
115+
```
116+
117+
### Per-target flag settings
118+
[target_specific](target_specific) sets different default flags for different targets. This example applies the [warn](warn) enforcement policy.
119+
120+
*`//target_specific:one`:*
121+
```sh
122+
$ bazel build //target_specific:one
123+
INFO: Reading project settings from //target_specific:PROJECT.scl.
124+
INFO: Applying flags from the config 'default_config_for_target_one' defined in //target_specific:PROJECT.scl:
125+
[--platforms=//:myplatform, --@custom_flags//:project_flag="settings for target one"]
126+
INFO: Found 1 target...
127+
INFO: Build completed successfully, 1 total action
128+
```
129+
130+
*`//target_specific:two`:*
131+
```sh
132+
$ bazel build //target_specific:two
133+
INFO: Reading project settings from //target_specific:PROJECT.scl.
134+
INFO: Applying flags from the config 'default_config_for_target_two' defined in //target_specific:PROJECT.scl:
135+
[--platforms=//:myplatform, --@custom_flags//:project_flag="settings for target two"]
136+
INFO: Found 1 target...
137+
INFO: Build completed successfully, 1 total action
138+
```
139+
140+
**Note:** `$ bazel build //target_specific:all` fails because no common set of flags applies to both targets. You can resolve this either by explicitly setting `--scl_config` or disabling project flags with `--noenforce_project_configs`. Comment on https://github.com/bazelbuild/bazel/issues/24839 if you're interested in more automatic behavior.
141+
142+
### Aliases
143+
[alias](alias) is a project with different disjoint directories. You can refer them all to the same souce of flag truth with `PROJECT.scl` aliases.
144+
145+
*Main project definition in `alias/project_main`:*
146+
```sh
147+
$ bazel build //alias/project_main:main
148+
INFO: Reading project settings from //alias/project_main:PROJECT.scl.
149+
INFO: Applying flags from the config 'default_config' defined in //alias/project_main:PROJECT.scl:
150+
[--platforms=//:myplatform, --compilation_mode=opt, --@custom_flags//:project_flag="custom flag value"]
151+
INFO: Found 1 target...
152+
INFO: Build completed successfully, 1 total action
153+
```
154+
155+
*`alias/project_lib` is a different directory but part of the same project:*
156+
```sh
157+
$ cat alias/project_lib/PROJECT.scl
158+
project = {
159+
"actual": "//alias/project_main:PROJECT.scl",
160+
}
161+
```
162+
163+
```sh
164+
$ bazel build //alias/project_lib:lib
165+
INFO: Reading project settings from //alias/project_main:PROJECT.scl.
166+
INFO: Applying flags from the config 'default_config' defined in //alias/project_main:PROJECT.scl:
167+
[--platforms=//:myplatform, --compilation_mode=opt, --@custom_flags//:project_flag="custom flag value"]
168+
INFO: Found 1 target...
169+
INFO: Build completed successfully, 1 total action
170+
```
171+
172+
**Note:** In this example both directories have the same parent directory. You can alternatively move the project definition to `alias/PROJECT.scl` for the same effect. This also requires an `alias/BUILD` file, which may be empty.
173+
174+
### Questions?
175+
This is an evolving feature. These examples don't cover everything.
176+
177+
Comment at https://github.com/bazelbuild/bazel/issues/24839 with more questions and requests.
178+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
genrule(
2+
name = "lib",
3+
srcs = [],
4+
outs = ["lib.out"],
5+
cmd = "echo lib > $@",
6+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# This package is part of the //alias/project_main project. Use that
2+
# package's PROJECT.scl.
3+
project = {
4+
"actual": "//alias/project_main:PROJECT.scl",
5+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
genrule(
2+
name = "main",
3+
srcs = [],
4+
outs = ["main.out"],
5+
cmd = "echo main > $@",
6+
)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# A project file that automatically sets build flags for all targets in the
2+
# project and warns if the user sets any other flags.
3+
load(
4+
"@bazel_tools//src/main/protobuf/project:project_proto.scl",
5+
"buildable_unit_pb2",
6+
"project_pb2",
7+
)
8+
9+
project = project_pb2.Project.create(
10+
# "WARN" means bazel emits a warning if the user sets flags to different
11+
# values than they're set here. This can be used as a signal to determine if
12+
# the project is built canonically.
13+
#
14+
# "WARN" is the default value. You can omit the below line and keep the same behavior.
15+
enforcement_policy = "WARN",
16+
buildable_units = [
17+
# Since this buildable unit sets "is_default = True", these flags apply
18+
# to any target in this package or its subpackages by default. You can
19+
# also request these flags explicitly with "--scl_config=default_config".
20+
buildable_unit_pb2.BuildableUnit.create(
21+
name = "default_config",
22+
flags = [
23+
"--platforms=//:myplatform",
24+
"--compilation_mode=opt",
25+
'--@custom_flags//:project_flag="custom flag value"',
26+
],
27+
is_default = True,
28+
# If a build sets an unknown --scl_config value or omits
29+
# --scl_config on a project with no default config, bazel errors
30+
# with a summary of valid configurations. The "description" field
31+
# tells bazel how to describe this configuration.
32+
description = "this project's default flags",
33+
),
34+
],
35+
)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
genrule(
2+
name = "one",
3+
srcs = [],
4+
outs = ["one.out"],
5+
cmd = "echo one > $@",
6+
)
7+
8+
genrule(
9+
name = "two",
10+
srcs = [],
11+
outs = ["two.out"],
12+
cmd = "echo two > $@",
13+
)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# A project file that automatically sets build flags for all targets in the
2+
# project and fails if the users set any flags that conflict with the project's
3+
# flags.
4+
#
5+
# For example:
6+
# --compilation_mode=dbg conflicts with --compilation_mode=opt
7+
# --compilation_mode=dbg does not conflict with --//some:unrelated_flag=1
8+
load(
9+
"@bazel_tools//src/main/protobuf/project:project_proto.scl",
10+
"buildable_unit_pb2",
11+
"project_pb2",
12+
)
13+
14+
project = project_pb2.Project.create(
15+
# "COMPATIBLE" means bazel errors if the user sets flags that conflict with
16+
# project's flags. However, bazel permits other user flags that don't
17+
# directly conflict. See top of this file for examples.
18+
enforcement_policy = "COMPATIBLE",
19+
buildable_units = [
20+
# Since this buildable unit sets "is_default = True", these flags apply
21+
# to any target in this package or its subpackages by default. You can
22+
# also request these flags explicitly with "--scl_config=default_config".
23+
buildable_unit_pb2.BuildableUnit.create(
24+
name = "default_config",
25+
flags = [
26+
"--platforms=//:myplatform",
27+
"--compilation_mode=opt",
28+
'--@custom_flags//:project_flag="custom flag value"',
29+
],
30+
is_default = True,
31+
# If a build sets an unknown --scl_config value or omits
32+
# --scl_config on a project with no default config, bazel errors
33+
# with a summary of valid configurations. The "description" field
34+
# tells bazel how to describe this configuration.
35+
description = "this project's default flags",
36+
),
37+
38+
# A different flag configuration. Since this doesn't set "is_default
39+
# True", you have to request it explicitly with "--scl_config=debug_config".
40+
buildable_unit_pb2.BuildableUnit.create(
41+
name = "debug_config",
42+
flags = [
43+
"--platforms=//:myplatform",
44+
"--compilation_mode=dbg",
45+
'--@custom_flags//:project_flag="debug value"',
46+
],
47+
description = "debug configuration for developers",
48+
),
49+
],
50+
)

0 commit comments

Comments
 (0)