# C Toolchain in Kleaf

## Toolchain declaration

Source: [BUILD.bazel](BUILD.bazel)

The following toolchains are declared:

*   Default toolchains
*   Versioned toolchains
*   User toolchains, provided via command line flags

### Default toolchains

[Source: `BUILD.bazel`](BUILD.bazel)

Default toolchains, named `{target_os}_{target_cpu}_clang_toolchain`,
are the fallback toolchains when a C toolchain is requested without
any version information.

These toolchains are marked "target_compatible_with":
*   the corresponding `@platforms//os:{target_os}`
*   the corresponding `@platforms//cpu:{target_cpu}`

These toolchains are marked "exec_compatible_with":
*   `@platforms//os:linux`
*   `@platforms//cpu:x86_64`

These toolchains have `cc_toolchain.compiler` set to `CLANG_VERSION` from
`@kernel_toolchain_info//:dict.bzl`.

### Versioned toolchains

[Source: `BUILD.bazel`](BUILD.bazel)

Versioned toolchains, named
`{version}_{target_os}_{target_cpu}_clang_toolchain`,
are the toolchains bearing extra version information.

These toolchains are marked "target_compatible_with":
*   the corresponding `@platforms//os:{target_os}`
*   the corresponding `@platforms//cpu:{target_cpu}`
*   the corresponding `//prebuilts/clang/host/linux-x86/kleaf:{version}`

These toolchains are marked "exec_compatible_with":
*   `@platforms//os:linux`
*   `@platforms//cpu:x86_64`
*   the corresponding `//prebuilts/clang/host/linux-x86/kleaf:{version}`

These toolchains have `cc_toolchain.compiler` set to the corresponding
`{version}`.

### User toolchains

[Source: `clang_toolchain_repository.bzl`](clang_toolchain_repository.bzl)

User toolchains,
`@kleaf_clang_toolchain//:1_user_{target_os}_{target_cpu}_clang_toolchain`,
are the toolchains provided by the user from the command line.

These toolchains are marked "target_compatible_with":
*   the corresponding `@platforms//os:{target_os}`
*   the corresponding `@platforms//cpu:{target_cpu}`

These toolchains are marked "exec_compatible_with":
*   `@platforms//os:linux`
*   `@platforms//cpu:x86_64`

These toolchains have `cc_toolchain.compiler` set to
`"kleaf_clang_toolchain_skip_version_check"` (an implementation detail).

## Toolchain registration

[Source: `clang_toolchain_repository.bzl`](clang_toolchain_repository.bzl)

Toolchains are registered in the following order:

1.  If `--user_clang_toolchain` is set, the user toolchains are registered.
    Otherwise, fake user toolchains are registered.
2.  The versioned toolchains
3.  The default toolchains

## Toolchain resolution

The following assumes that the reader is familar with
[Bazel's toolchain resolution process](https://bazel.build/extending/toolchains#toolchain-resolution).

Each toolchain registered through Bazel can specify a list of compatible
constraint values with `exec_compatible_with` and `target_compatible_with`. A
toolchain matches a platform when the toolchain’s constraint values are a
**SUBSET** of the platform's constraint values.

To determine the toolchain for a
[build target](https://bazel.build/extending/rules#target_instantiation),
Bazel does the following.

1.  The platform of the build target is determined. By default, the platform of
    of the build target is the
    [target platform](https://bazel.build/extending/platforms), subject to
    transitions. If the build target is built in an `exec` configuration (e.g.
    when building as one of the
    [`tools` of a `genrule`](https://bazel.build/reference/be/general#genrule.tools),
    or as
    [a dependency with `cfg="exec"`](https://bazel.build/extending/rules#configurations), the platform of the build target is the
    [execution platform](https://bazel.build/extending/platforms), subject to
    transitions.
2.  Bazel looks for a toolchain such that the toolchain's constraint values
    are a **SUBSET** of the constraint values of the platform of the build
    target.

For a build without any flags or transitions, the execution platform is
`@local_config_platform//:host`. For Kleaf, this usually contains two
constraint values: (`linux`, `x86_64`).

For a build without any flags or transitions, Bazel uses
["single-platform builds"](https://bazel.build/extending/platforms)
by default, so the target platform is the same as the execution platform with
two constraint values: (`linux`, `x86_64`).

In Kleaf, if a target is built with `--config=android_{cpu}`, or is wrapped in
an `android_filegroup` with a given `cpu`, the target platform has two
constraint values (`android`, `{cpu}`).

### Example: cc\_* rules

For a build without any flags or transitions, the platform of the build target
has two constrants: (`linux`, `x86_64`). The toolchains are looked up in
the following order:

1.  If `--user_clang_toolchain` is set, `1_user_linux_x86_64_clang_toolchain`
    is returned because its constraint values (`linux`, `x86_64`) are a subset
    of the platform's constraint values (`linux`, `x86_64`). Otherwise, if
    `--user_clang_toolchain` is not set, Bazel continues checking the following
    toolchains.
2.  The versioned toolchains are skipped because their constraint values
    (`{version}`, `{target_os}`, `{target_cpu}`) are not a subset of
    the platform's constraint values (`linux`, `x86_64`).
3.  The default toolchain `linux_x86_64_clang_toolchain` is returned because
    its constraint values (`linux`, `x86_64`) are a subset
    of the platform's constraint values (`linux`, `x86_64`)

If a `cc_*` target is built with `--config=android_arm64`, the platform of the
build target has two constrants: (`android`, `arm64`). The toolchains are looked
up in the following order:

1.  If `--user_clang_toolchain` is set, `1_user_android_arm64_clang_toolchain`
    is returned because its constraint values (`android`, `arm64`) are a subset
    of the platform's constraint values (`android`, `arm64`). Otherwise, if
    `--user_clang_toolchain` is not set, Bazel continues checking the following
    toolchains.
2.  The versioned toolchains are skipped because their constraint values
    (`{version}`, `{target_os}`, `{target_cpu}`) are not a subset of
    the platform's constraint values (`android`, `arm64`).
3.  The default toolchain `android_arm64_clang_toolchain` is returned because
    its constraint values (`android`, `arm64`) are a subset
    of the platform's constraint values (`android`, `arm64`)

If a `cc_*` target is wrapped in an `android_filegroup` target with
`cpu="arm64"`, **when the `android_filegroup` target is built**, a transition is
applied on the `cc_*` target so its platform has constraint values
(`android`, `arm64`). The toolchain resolution process is the same as with
`--config=android_arm64.`

### Example: kernel\_* rules without toolchain\_version

If a `kernel_build` does not specify `toolchain_version`:

*   Its execution platform has constraint values (`linux`, `x86_64`)
*   Its target platform has constraint values (`android`, `{target_cpu}`) where
    `{target_cpu}` is specified in `kernel_build.arch`.

When `kernel_toolchains` looks up the toolchains for the execution platform
and the target platform, respectively, the process is similar to the one
for [`cc_*` rules](#example-cc_-rules). That is:

*   If `--user_clang_toolchain` is set, the user toolchains are returned:
    *   `user_linux_x86_64_clang_toolchain` is resolved for the
        execution platform
    *   `user_android_{target_cpu}_clang_toolchain` is resolved for the
        target platform
*   Otherwise, the default toolchains are returned:
    *   `linux_x86_64_clang_toolchain` is resolved for the
        execution platform
    *   `android_{target_cpu}_clang_toolchain` is resolved for the
        target platform

### Example: kernel\_* rules with toolchain\_version

This is unusual. You are recommended to not set `kernel_build.toolchain_version`
to use the default toolchains.

If a `kernel_build` does specify `toolchain_version`:

*   Its execution platform has constraint values
    (`linux`, `x86_64`, `{toolchain_version}`)
*   Its target platform has constraint values
    (`android`, `{target_cpu}`, `{toolchain_version}`) where
    `{target_cpu}` is specified in `kernel_build.arch`.

When `kernel_toolchains` looks up the toolchains for the execution platform:

*   If `--user_clang_toolchain` is set, the user toolchain
    `1_user_linux_x86_64_clang_toolchain` is returned because its constraint
    values (`linux`, `x86_64`) are a subset of the execution platform's
    constraint values (`linux`, `x86_64`, `{toolchain_version}`). However,
    `kernel_toolchains` rejects the user toolchain because the version
    `"unknown"` does not match the declared value,
    `kernel_build.toolchain_version`, resulting in a build error. You should
    delete `kernel_build.toolchain_version`, and try again.
*   If `--user_clang_toolchain` is not set, the matching versioned toolchain,
    `{version}_linux_x86_64_clang_toolchain` is returned because
    its constraint values (`linux`, `x86_64` `{toolchain_version}`) are a subset
    of the execution platform's constraint values
    (`linux`, `x86_64`, `{toolchain_version}`).
*   If no matching versioned toolchain is found, the default toolchain is
    returned. However, `kernel_toolchains` rejects the default toolchain
    because the version does not match the declared value,
    `kernel_build.toolchain_version`, resulting in a build error as expected.

The same goes for the target platform:

*   If `--user_clang_toolchain` is set, the user toolchain
    `1_user_android_{target_cpu}_clang_toolchain` is returned because its
    constraint values (`android`, `{target_cpu}`) are a subset of the target
    platform's constraint values
    (`android`, `{target_cpu}`, `{toolchain_version}`).
    `kernel_toolchains` accepts the user toolchain with a warning because
    the version of the user toolchain
    `"kleaf_clang_toolchain_skip_version_check"` does not match the declared value, `kernel_build.toolchain_version`.
*   If `--user_clang_toolchain` is not set, the matching versioned toolchain,
    `{version}_android_{target_cpu}_clang_toolchain` is returned because
    its constraint values (`android`, `{target_cpu}` `{toolchain_version}`) are a subset of the target platform's constraint values
    (`android`, `{target_cpu}`, `{toolchain_version}`).
*   If no matching versioned toolchain is found, the default toolchain is
    returned. However, `kernel_toolchains` rejects the default toolchain
    because the version does not match the declared value,
    `kernel_build.toolchain_version`, resulting in a build error as expected.

## Caveats

To use the user toolchains, the following is expected from the workspace:

*   An environment variable, `KLEAF_USER_CLANG_TOOLCHAIN_PATH`, must
    be set to the path to the user clang toolchain. This is set by
    Kleaf's Bazel wrapper, `bazel.py`, when `--user_clang_toolchain` is set.
