Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: revise flags example for clarity #315

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
1 change: 1 addition & 0 deletions flags-parsing-tutorial/.bazelversion
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
6.3.2
89 changes: 51 additions & 38 deletions flags-parsing-tutorial/README.md
Original file line number Diff line number Diff line change
@@ -1,103 +1,116 @@
Bazel flags parsing examples
========================

This provides examples for the current behavior of flag parsing. This tutorial assumes that users have basic knowledge of creating a Bazel [WORKSPACE](https://docs.bazel.build/build-ref.html#workspace) and writing a [BUILD](https://docs.bazel.build/versions/main/build-ref.html#BUILD_files) file. Users should also be familiar with [built-in](https://docs.bazel.build/versions/main/configurable-attributes.html#built-in-flags) (non-Starlark) and [user-defined](https://docs.bazel.build/versions/main/configurable-attributes.html#custom-flags) (Starlark) flags.
This provides examples for the current behavior of flag parsing. This tutorial assumes that users have basic knowledge of writing a Bazel [BUILD](https://bazel.build/concepts/build-files) file. Users should also be familiar with [built-in](https://bazel.build/docs/configurable-attributes#built-in-flags) (non-Starlark) and [user-defined](https://bazel.build/docs/configurable-attributes#custom-flags) (Starlark) flags.

Note that a broader term, [options](https://docs.bazel.build/versions/main/command-line-reference.html#option-syntax) is often used interchangeably with flags. An important distinction is that only flags can be set on the command line.
Note that a broader term, [options](https://bazel.build/reference/command-line-reference#option-syntax) is often used interchangeably with flags. An important distinction is that only flags can be set on the command line.

Terminologies
========================
Terminology
-----------
`--config`: Throughout this tutorial, users will see regular usage of `--config`. Although, it's already defined [here](https://docs.bazel.build/guide.html#bazelrc), we will repeat important points for first-time Bazel users.
* `--config` can be used to represent a group of flags with a short name following the convention `<command>:<config_name>`. For example:
`--config` can be used to represent a group of flags with a short name following the convention `<command>:<config_name>`. For example:

```
# bazelrc
# .bazelrc
build:foo --//:wibble=wibble
build:foo --//:wobble=wobble
build:foo --//:wubble=wubble
```
Here, we have defined a `--config` named `foo` expanding flags `--//:wibble, --//:wobble, --//:wubble`.

By default, flags defined in a `--config` will be ignored, unless `--config=<config_name>` is specified either on the command line or in the `bazelrc` file. Flags grouped by `--config` are expanded in place having the same priority with the `--config` expanding them. Please see example B.4.
By default, flags defined in a `--config` will be ignored, unless `--config=<config_name>` is specified either on the command line or in the `.bazelrc` file. Flags grouped by `--config` are expanded in place having the same priority with the `--config` expanding them. Please see example B.4.

Instructions
========================
------------

```
cd examples/flags-parsing-tutorial
```
In this WORKSPACE, we have:
* <b>[bazelrc](https://docs.bazel.build/guide.html#bazelrc-the-bazel-configuration-file) file</b>: This is the user-defined bazelrc where flags can be defined.
* <b>build_defs.bzl</b>: This contains the Starlark rules' implementations.
* <b>BUILD</b>: This contains rules Bazel uses to build a package.
* **[.bazelrc](https://docs.bazel.build/guide.html#bazelrc-the-bazel-configuration-file) file**: This is the user-defined bazelrc where flags can be defined.
* **build_defs.bzl**: This contains the Starlark rules' implementations.
* **BUILD**: This contains rules Bazel uses to build a package.

For each requirement below, run the following example commands and examine the outputs.

❗❗❗<i>Please note that there is an outstanding [bug](https://github.com/bazelbuild/bazel/issues/13603) concerning `--config` and Starlark flags which will be pointed out later in this README. Once a fix is released, this doc will be updated.</i>

### A. Flags on the command line take precedence over those in bazelrc. ###
### A. Flags on the command line take precedence over those in bazelrc.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sanity check: this remains accurate?

I thought there was some logic to inline --config flags into the command line.

--foo=1 --config=something_that_sets_foo --foo2

would evaluate the bazelrc setting second.

https://bazel.build/run/bazelrc supports what's stated here

Options on the command line always take precedence over those in rc files

Maybe I'm remembering outdated logic?

Without `--config`, this is true for both Starlark and non-Starlark options
```
bazel --bazelrc=./bazelrc build --//:flag=cmd :flag
bazel build --//:flag=cmd :flag
```
For user-defined (Starlark) flags, the evaluated value can be observed by adding a `DEBUG` statement as in line 6 of `build_defs.bzl`. Users should see the following `DEBUG` statement indicating that the final value for `--//:flag` is `cmd`
```
DEBUG: /my/root/examples/flags-parsing-tutorial/build_defs.bzl:6:10: evaluated value for flag: cmd
DEBUG: examples/flags-parsing-tutorial/build_defs.bzl:6:10: evaluated value for flag: cmd
```
### B. The last option on the command line takes precedence. ###
⭐ <b>Tips</b>: Use [--announce_rc](https://docs.bazel.build/user-manual.html#flag--announce_rc) to debug flag parsing.
### B. The last option on the command line takes precedence.

> [!NOTE]
> Use [--announce_rc](https://docs.bazel.build/user-manual.html#flag--announce_rc) to debug flag parsing.

#### Example B.1

<u><b>Example B.1</b></u>
```
bazel --bazelrc=./bazelrc build --config=foo -c opt --announce_rc :wibble
bazel build --config=foo -c opt --announce_rc :wibble
```
`-c (--compilation_mode)` is first expanded by `--config=foo` with value `dbg`. The flag then got overridden by the explicit `-c opt` flag, which is the last on the command line as seen above.

Since Bazel uses a different output directories for each compilation mode, users can observe the output by running `ls -l`. In the output path, `opt` should be present.

<u><b>Example B.2</b></u>
#### Example B.2

```
bazel --bazelrc=./bazelrc build -c opt --config=foo --announce_rc :wibble
bazel build -c opt --config=foo --announce_rc :wibble
```
Since `--config=foo` is last on the command line and flag `-c dbg` is expanded by `--config=foo`, flag `-c dbg` also takes the same precedence. In contrast of <b>Example B.1</b>, `dbg` should be present in the output path.

<u><b>Example B.3</b></u>
#### Example B.3

```
bazel --bazelrc=./bazelrc build --//:flag=cmd --//:flag=cmd_last :flag
bazel build --//:flag=cmd --//:flag=cmd_last :flag
```
Users should see the following `DEBUG` statement indicating that the final value for `--//:flag` is `cmd_last`.
```
DEBUG: /my/root/examples/flags-parsing-tutorial/build_defs.bzl:6:10: evaluated value for flag: cmd_last
DEBUG: examples/flags-parsing-tutorial/build_defs.bzl:6:10: evaluated value for flag: cmd_last
```
<u><b>Example B.4</b></u>

#### Example B.4

```
bazel --bazelrc=./bazelrc build --config=foo --config=bar :wibble :wobble :wubble
bazel build --config=foo --config=bar :wibble :wobble :wubble
```
Requirement <b>B</b> is also applicable for cascading `--config(s)`. `DEBUG` should show `flob` as the evaluated value for `--//:wibble`. Since `config=bar` does not expand `--//:wobble` and `--//:wubble`, their values should stay the same as defined in `--config=foo`.
```
DEBUG: /my/root/examples/flags-parsing-tutorial/build_defs.bzl:6:10: evaluated value for wibble: flob
DEBUG: /my/root/examples/flags-parsing-tutorial/build_defs.bzl:6:10: evaluated value for wubble: wubble
DEBUG: /my/root/examples/flags-parsing-tutorial/build_defs.bzl:6:10: evaluated value for wobble: wobble
DEBUG: examples/flags-parsing-tutorial/build_defs.bzl:6:10: evaluated value for wibble: flob
DEBUG: examples/flags-parsing-tutorial/build_defs.bzl:6:10: evaluated value for wubble: wubble
DEBUG: examples/flags-parsing-tutorial/build_defs.bzl:6:10: evaluated value for wobble: wobble
```
⭐ <b>Tips</b>: Since `--config` is a group of options and it can override explicit flags specified on the command line (<b>Example B.2</b>), try to have your explicit options at the end to avoid unintentional overriding. In this case, Bazel will show a <b>WARNING</b>.

<u><b>Example B.5</b></u>
> [!NOTE]
> Since `--config` is a group of options and it can override explicit flags specified on the command line (**Example B.2**), try to have your explicit options at the end to avoid unintentional overriding. In this case, Bazel will show a **WARNING**.

#### Example B.5

❗❗❗ As mentioned above, there is an outstanding [bug](https://github.com/bazelbuild/bazel/issues/13603). Currently, users cannot override Starlark flags expanded by a `--config` via an explicit flag on the command line
```
bazel --bazelrc=./bazelrc build --config=foo --//:wibble=flob :wibble
bazel build --config=foo --//:wibble=flob :wibble
```
DEBUG should show `flob` for `--//:wibble` per requirement <b>A</b>, but instead `wibble` is shown.
```
DEBUG: /Users/aranguyen/examples/flags-parsing-tutorial/build_defs.bzl:6:10: evaluated value for wibble: wibble
```
### C. Within bazelrc file, precedence depends on specificity which is defined by inheritance ###

### C. Within bazelrc file, precedence depends on specificity which is defined by inheritance

Commands such as test and release inherit flags from build. The inheriting command is said to be more specific and thus takes precedence.
```
# -c is resolved to opt, and --//:wibble resolves to flob.
bazel --bazelrc=./bazelrc test --config=baz :wibble --announce_rc
bazel test --config=baz :wibble --announce_rc
```
⭐ <b>Tips</b>: For readability, have your most common options at the top of bazelrc.

> [!NOTE]
> For readability, have your most common options at the top of bazelrc.

### D. Using `--enable_platform_specific_config` flag

If the value of `--enable_platform_specific_config` is `True`, Bazel enables host-OS-specific flags in the `bazelrc`. For example, considering the `bazelrc` in this WORKSPACE:
```
build --enable_platform_specific_config
Expand All @@ -110,7 +123,7 @@ build:openbsd --cpu=haswell
```
If the host platform (where Bazel is running) is `macos` and the `build` command is run, Bazel picks up `build:macos` lines in the `bazelrc`. In this example, `build:macos --cpu=k8` will be enabled. Try the following command and observe the output, `k8` should appear in the output path (.../bazel-out/k8-fastbuild/...).
```
bazel --bazelrc=./bazelrc build :wibble
bazel build :wibble
```
Note that Bazel will only enable flags based on the host platform, instead of execution platform or target platform. The definitions for these platforms can be found [here](https://docs.bazel.build/platforms.html).

15 changes: 14 additions & 1 deletion flags-parsing-tutorial/build_defs.bzl
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
BuildSettingInfo = provider()
"""Defines the string_flag setting.

Note that the same exists in bazel-skylib:
https://github.com/bazelbuild/bazel-skylib/blob/652c8f0b2817daaa2570b7a3b2147643210f7dc7/docs/common_settings_doc.md#string_flag
"""

BuildSettingInfo = provider(
doc = "A singleton provider that contains the raw value of a build setting",
fields = {
"value": "The value of the build setting in the current configuration. " +
"This value may come from the command line or an upstream transition, " +
"or else it will be the build setting's default.",
},
)

def _string_imp(ctx):
value = ctx.build_setting_value
Expand Down
Loading