Skip to content

Add feature-matrix toolchain configuration - #876

Merged
helly25 merged 2 commits into
bazel-contrib:masterfrom
helly25:feat/feature-toolchain-overrides
Oct 1, 2026
Merged

helly25 merged 2 commits into
bazel-contrib:masterfrom
helly25:feat/feature-toolchain-overrides

Conversation

@helly25

@helly25 helly25 commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Feature-matrix toolchain configuration

Allow per feature-matrix llvm config overrides.

AG;DR

The primary change is a generic feature matrix for one logical LLVM toolchain, not a linker-specific switch. Keep the existing configuration as the default/fallback and describe complete conditional variants with llvm.feature_override(...) in Bzlmod or llvm_feature_override(...) in WORKSPACE.

  • Match arbitrary enabled and disabled build features using one Bazel-style signed list, for example features = ["thin_lto", "-msan"]. A negative entry matches an absent or disabled feature; contradictory entries are rejected. Optionally restrict matches to target platforms. Only the specified features participate; unrelated features are ignored.
  • Override any public toolchain setting: LLVM distributions, linker selection, sysroots, flags, libraries, compatibility constraints and sandbox inputs.
  • Inherit unspecified settings. Dictionaries merge by key; supplied values replace inherited values. reset clears inherited attributes or restores schema defaults. Scalar/plural version settings supersede their inherited counterpart.
  • Select the fallback when nothing matches; fail explicitly when multiple variants match. There is no implicit ordering or stacking.
  • Register all variants through the existing toolchain repository/name, keeping compiler, linker and sandbox inputs consistent. Reuse the base LLVM archive when distribution settings are unchanged.

For example, use downloaded mold by default but select bundled LLD for Bazel ThinLTO:

llvm.toolchain(
    name = "llvm_toolchain",
    llvm_version = "23.1.2",
    linker = {"linux-x86_64": "mold", "darwin-aarch64": "auto"},
    linker_version = "latest",
)
llvm.feature_override(
    name = "llvm_toolchain",
    features = ["thin_lto"],
    linker = {"linux-x86_64": ""},
)

Selection uses configuration-level --features, and --host_features for execution tools. It does not inspect rule-local features, implied C++ features, or raw compiler flags such as -flto=thin. These boundaries are documented and tested.

Coverage and supporting changes

  • Unit tests cover arbitrary/conjunctive/negative predicates, disabled features, target filters, ambiguity, inheritance, resets, validation and archive reuse (including resetting an external LLVM root).
  • Integration fixtures exercise fallback, changed language standards/flags/sandbox inputs, actual LLVM 23-to-22 distribution switching, mold-to-LLD ThinLTO and independent host-feature selection. CI covers macOS/Linux, Bazel 9 Bzlmod, Bazel 8 WORKSPACE and Bazel 7 Bzlmod.
  • Strengthen the Linux MSan tests with compile-time instrumentation guards and a separate C test that must emit MemorySanitizer: use-of-uninitialized-value and exit with the expected status. An ordinary failure or crash cannot pass.
  • Briefly document unsupported MSan errors, ASan+UBSan compatibility, and mold LTO's need for a compatible LLVMgold.so, which the standalone mold archive does not contain.
  • Install the ICU 70 runtime required by LLVM 23's Linux LLD in the Ubuntu CI setup. Modestly increase Bazel connector attempts to 10 and timeout scaling to 1.5, leaving whole-download retry/backoff defaults unchanged.

Validation

  • Bazel 9 root suite: 20 tests passed; Bazel 8 WORKSPACE feature unit suite: 14 passed, including signed predicates, negative-only matches, explicit disablement, duplicate conditions, malformed entries and contradictions in either order.
  • Full feature-matrix integration script passed locally on macOS with Bazel 9/Bzlmod and Bazel 8/WORKSPACE, repeated after the signed-feature API change. This includes disabling an explicitly enabled excluded feature, distribution switching, fallback and independent host selection. Bazel 7 feature selection also compiled and passed before the syntax refinement.
  • Linux/amd64 container: downloaded mold, matching override/fallback, rule-local feature isolation, host-feature selection and ThinLTO with bundled LLD all passed. The already checksum-verified LLVM archive was reused for the final Linux checks.
  • Unsupported macOS MSan regression passed with Bazel 9 and LLVM 23.
  • Both Linux MSan binaries compile instrumented and reject uninstrumented builds. Native Linux CI now verifies both runtime tests: msan_libcxx_test and the diagnostic-required msan_detection_test passed on Bazel 7, 8, 9 and latest. All feature-matrix jobs and the required-checks gate also passed in CI run 36681030848, for head 6b40c5a25b54a44c10ce3c8977df64b14cc8de15. The earlier local QEMU memory limitation is no longer a verification blocker.
  • After the signed-feature API change, Linux/amd64 container checks passed for both the C++20/flags/sandbox-input override and mold-to-LLD ThinLTO selection.
  • Repository lint and git diff --check passed. Signed-feature head 9c9b069 completed all 93 CI checks successfully before rebasing.

Rebase verification

Rebased onto master after #875 merged (a699ee9). git range-diff confirms both feature-matrix commits are unchanged. Current head: 7bf9147.

  • Bazel 9.0.0 root suite: 20/20 tests passed, all executed with --nocache_test_results.
  • Full macOS feature-selection integration script passed with Bazel 9.0.0/Bzlmod and Bazel 8.6.0/WORKSPACE, including fallback, signed predicates, LLVM distribution switching, rule-local feature isolation and independent host-feature selection.
  • Fresh CI: Tests run 36804190226. Protected squash auto-merge is enabled; merging remains gated on the required checks.

Select complete toolchain variants from arbitrary enabled/disabled build features while retaining one public toolchain and an unchanged fallback. Support Bzlmod and WORKSPACE, inherit/reset every public setting, share unchanged LLVM archives, and reject ambiguous matches.

Add unit and cross-platform integration coverage, strengthen MSan instrumentation/runtime assertions, document sanitizer and mold LTO constraints, and modestly improve download resilience.

Based on PR bazel-contrib#875; native Linux CI must complete MSan runtime verification because local QEMU exhausted memory.
@helly25
helly25 force-pushed the feat/feature-toolchain-overrides branch from 9c9b069 to 7bf9147 Compare October 1, 2026 02:05
@helly25
helly25 enabled auto-merge (squash) October 1, 2026 02:05
@helly25
helly25 merged commit 3764511 into bazel-contrib:master Oct 1, 2026
93 checks passed
@helly25
helly25 deleted the feat/feature-toolchain-overrides branch October 1, 2026 03:12
helly25 added a commit that referenced this pull request Oct 1, 2026
# Enable linker-managed ThinLTO on macOS

Fix thin_lto feature on MacOS.

# AG;DR

## Problem

`--features=thin_lto` was silently ineffective on macOS: rules_cc only
defines the ThinLTO feature on its non-Darwin path. Explicit
`-flto=thin` compile and link flags work with Apple ld, but the
toolchain did not provide them.

Simply exposing the literal C++ `thin_lto` feature on Darwin is not
sufficient. It activates Bazel's distributed indexing/backend path,
which requires start/end-lib support and ELF indexing options that Apple
ld does not provide.

## Fix

- Translate configuration-level `--features=thin_lto` into
linker-managed ThinLTO on Darwin, applying `-flto=thin` to compilation
and linking without activating ELF indexing actions.
- Respect explicit `--features=-thin_lto`, including when both enable
and disable requests are present.
- Keep target and execution configurations independent: build tools use
`--host_features=thin_lto`.
- Leave Linux's existing distributed ThinLTO behavior unchanged.
- Document the macOS configuration-level scope: rule-local `features`
attributes do not select this behavior. LLVM 23 still needs the existing
native-linker override on macOS.

## Regression coverage

- Analyze C compilation, C++ compilation, and linking; assert the flags
are present only when enabled and no ELF-style indexing/backend actions
are introduced.
- Build and run a C/C++ cross-translation-unit probe with LLVM 23.1.2
and Apple ld. It contains an unresolved call that disappears only after
optimization through both static libraries, so an ordinary non-LTO build
cannot pass.
- Verify the same probe fails with the expected undefined symbol when
ThinLTO is disabled, and when only target features are enabled for an
execution-configured tool.
- Verify `--host_features=thin_lto` enables the tool independently of
disabled target ThinLTO.
- Add a required macOS CI job for Bazel 9/Bzlmod and Bazel 8/WORKSPACE,
including the corresponding `required_checks_done.needs` entry.

## Local verification

On Apple Silicon, repeated after rebasing onto merged #876 (`3764511`):

- Bazel 9.0.0 / Bzlmod / LLVM 23.1.2: full
`run_darwin_thin_lto_tests.sh` passed (six cached test results, both
expected-failure checks, and the host-feature build).
- Bazel 8.6.0 / WORKSPACE / LLVM 23.1.2: the same full regression script
passed (six cached test results, both expected-failure checks, and the
host-feature build).
- #876's feature-matrix fixture: `--features=thin_lto //:selection_test`
passed with Bazel 9 and Apple ld.
- Root Bazel 9.0.0 `bazel test //... --nocache_test_results`: all 20
tests executed and passed.
- `trunk check --no-fix` and the commit/push hooks passed.

The Linux distributed ThinLTO fixture also passed in the existing
container after #876's signed-feature API update. The rebase leaves the
ThinLTO patch unchanged (`git range-diff` confirms this).

## CI cleanup follow-up during sequential merging

- [#875's post-merge Ubuntu/Bazel 9
job](https://github.com/bazel-contrib/toolchains_llvm/actions/runs/36803938052/job/110184071511)
passed its native tests, then failed deleting the shared repo contents
cache before the WebAssembly phase: `Directory not empty`.
- Its log shows separate root-workspace and tests-workspace Bazel
servers, with an orphan Java process still alive after cleanup. Change
the initial banner from `bazel version` to client-only `bazel
--version`, removing that unnecessary idle root server. [Idle servers
can run repo-cache garbage
collection](https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryOptions.java),
making this a likely source of the cleanup race. Compiler/linker
configuration and cache-cleanup error handling are unchanged.
- An isolated local probe verified that the old `version` command starts
a server, while standalone `--version` succeeds on Bazel 8.6.0 and 9.0.0
without creating an output root. The probe server was shut down; `bash
-n`, `git diff --check`, and the commit/push hooks passed.

Current head: `0d84b16` (unchanged ThinLTO patch plus the CI banner
fix). Fresh CI: [Tests run
36810263471](https://github.com/bazel-contrib/toolchains_llvm/actions/runs/36810263471).
Protected squash auto-merge is enabled; #878 will be rebased only after
this PR merges.
helly25 added a commit that referenced this pull request Oct 1, 2026
Fix the remaining xz/zstd mismatch in the single-release distribution
helper and add a history-backed changelog.

# AG;DR

## Fix

#870 changed the Python updater to retain all archive formats and made
the toolchain selector exclude zstd until Bazel can extract LLVM's
large-window archives. The shell helper still had #836's old filter,
which discarded xz whenever the same archive was available as zstd.

- Retain xz, zstd, and gzip archives and their individual checksums in
`extra_distributions.sh`.
- Leave extraction compatibility decisions in the toolchain selector;
this does not enable zstd selection.
- Correct the README's stale zstd-preference claim, updater scope, and
dependencies.

## Changelog

Add `CHANGELOG.md` with release headings and bullets, newest first.
Historical feature introductions are checked against the first
containing release tags; current changes are grouped under the version
heading `1.11.0`. Distinguish earlier functionality from later
enhancements, including Linux versus macOS ThinLTO, initial versus
expanded dynamic-libstdc++ support, and MSan introduction versus
correctness fixes.

## Regression coverage

- Execute the real shell helper against offline release metadata,
allowing only the metadata request and rejecting unexpected archive
downloads.
- Cover stable and prerelease versions, both LLVM naming schemes,
xz/zstd/gzip alternatives and individual formats, exact checksum
preservation, unrelated-asset filtering, and no-match errors.
- Extend the Python updater test to preserve all three formats and
checksums.
- Run both script suites in a Linux/macOS CI job and include it in
`required_checks_done.needs`.

## Verification

- Demonstrated the new shell regression fails before the fix in all four
stable/prerelease and naming-scheme combinations, specifically because
xz is missing.
- `python3 -m unittest discover -s utils -p '*_test.py' -v`: all 8 tests
pass on macOS.
- `USE_BAZEL_VERSION=9.0.0 bazel test //... --test_output=errors
--nocache_test_results`: all 21 tests executed and passed on the rebased
head.
- `bash -n` for both shell entry points.
- Repository lint/push hooks, `git diff --check`, and YAML validation
that the required-check gate includes every declared job and has no
unknown dependencies.

Fresh CI: [Tests run
36820892858](https://github.com/bazel-contrib/toolchains_llvm/actions/runs/36820892858).
Protected squash auto-merge is enabled; this is the final PR in the #873
→ #875 → #876 → #877 → #878 sequence. The changelog heading remains `##
1.11.0`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant