Skip to content
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
baca9d1
Add more missing distributions (all .gz).
Mar 13, 2025
ffc2fe5
Add the ne platforms test to the actions release workflow
Mar 13, 2025
e1c923d
Better naming: should be toolchain_tests not platform_tests
Mar 13, 2025
606b5e3
No switching directories for the new test
Mar 13, 2025
bfaa5de
Use var array `test_args` to pass around the erquired flag.
Mar 13, 2025
8eed849
Add ability to specify `llvm_version/s` as (semver) requirements.
Mar 15, 2025
594e53c
Add a requirements test and disable the tests in workspace mode as th…
Mar 15, 2025
e2ffc9b
Make this work for workspaces
Mar 15, 2025
9d83ae4
The dependency also needs to go into deps.bzl so run_tests.bzl works.
Mar 15, 2025
e5810a0
Update (redo) after sync.
May 11, 2025
baf774a
Example update#
May 11, 2025
1eea0e6
Readme update.
May 11, 2025
03d2540
Update configure and tests.
May 11, 2025
2c4f855
Use better requiement identification that is in line with the actual …
May 11, 2025
71912b3
Updates & tweaks
May 11, 2025
01e888e
Ensure common_test_args are present
May 11, 2025
313c56f
one test is not always supported.
May 11, 2025
d38fda7
Small improvements. Pre-filter eligible versions and ensure actual co…
May 12, 2025
d28ef85
Attribute `extra_llvm_distributions` must be a common attribute.
May 12, 2025
5bd90e9
sync
May 16, 2025
01cff84
Limit test to 20.1.3 to prevent unnecessary diffs.
May 16, 2025
01814d4
sync
Jun 5, 2025
83dc627
No more llvm_distributions_select_no_error_test
Jun 5, 2025
a68560c
Add 20.1.7 (#509)
helly25 Jun 17, 2025
1c77e92
Update dependency rules_foreign_cc (#513)
renovate[bot] Jun 26, 2025
898ae5b
Mark repository as configurable (#515)
mering Jun 27, 2025
d65c5f2
support runtime/test-time compiler invokations (#518)
TroyKomodo Jun 30, 2025
199a0bd
Fix a `_strip_prefix` call site (#520)
fmeum Jul 3, 2025
ecaa6b0
Add 20.1.8 (#526)
helly25 Jul 18, 2025
e48789b
Additional 20.1.7 distributions (#527)
helly25 Jul 18, 2025
3e26d85
Add support for `parse_headers` (#524)
fmeum Jul 21, 2025
a78ac0f
Make `wasm-ld` optional (#529)
fmeum Aug 1, 2025
01683b5
Add support for riscv32-unknown-none-elf targets (#530)
kitterion Aug 7, 2025
6dfecf4
Mark downloaded repository as reproducible (#531)
trevorgray Aug 12, 2025
fa98a33
Use debian bookworm (#541)
helly25 Aug 27, 2025
e30d0f2
Add LLVM release 21.1.0. (#539)
helly25 Aug 27, 2025
d869904
Add fastbuild_compile_flags (#536)
mering Aug 28, 2025
fa87b96
Sync back changes to presubmit.yml (#545)
fmeum Sep 4, 2025
03de120
disable module for all stdlib (#544)
xiaopeng-tranxmart Sep 8, 2025
0559d92
More 21.1.0 (#546)
helly25 Sep 8, 2025
44fa28a
First 21.1.1 distributions (#551)
helly25 Sep 14, 2025
8304da3
More 21.1.1 distributions (#552)
helly25 Sep 18, 2025
4b9fcdb
First 21.1.2 distributions
helly25 Sep 24, 2025
e4eefec
Fix order and commenting
helly25 Sep 28, 2025
6cebb70
Merge branch 'master' into feat/latest_requirements
helly25 Sep 28, 2025
bc3e517
Merge branch 'master' into feat/latest_requirements
helly25 Oct 1, 2025
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
12 changes: 9 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,19 @@ jobs:
USE_BZLMOD: ${{ matrix.bzlmod }}
run: tests/scripts/run_tests.sh
toolchain_test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
os: [macos-latest, ubuntu-latest]
bazel_version: [7.x, latest]
bzlmod: [true, false]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Test
env:
USE_BAZEL_VERSION: latest
USE_BZLMOD: true
USE_BAZEL_VERSION: ${{ matrix.bazel_version }}
USE_BZLMOD: ${{ matrix.bzlmod }}
run: tests/scripts/run_toolchain_tests.sh
external_test:
strategy:
Expand Down
1 change: 1 addition & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ module(
bazel_dep(name = "bazel_skylib", version = "1.5.0")
bazel_dep(name = "rules_cc", version = "0.0.17")
bazel_dep(name = "platforms", version = "0.0.8")
bazel_dep(name = "helly25_bzl", version = "0.1.2")

# TODO: Remove when protobuf is released with a version of rules_python that supports 8.x
bazel_dep(name = "rules_python", version = "1.0.0", dev_dependency = True)
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,25 @@ See [bazel
tutorial](https://docs.bazel.build/versions/main/tutorial/cc-toolchain-config.html)
for how CC toolchains work in general.

### Requirements

Version attributes can be requirements of the form `first`, `first:<condition>`,
`latest` or `latest:<condition>`.

In case of `latest`, the latest distribution matching the optional `condition`
will be selected.

In case of `first`, the first distribution matching the optional `condition`
will be selected.

The condition consists of a comma separated list of semver version comparisons
supporting `<`, `<=`, `>`, `>=`, `==`, `!=`. Examples:

- `latest`
- `latest:>=20.1.0`
- `latest:>17.0.4,!=19.1.7,<=20.1.0`
- `first:>=15.0.6,<16`

### Selecting Toolchains

If toolchains are registered (see Quickstart section above), you do not need to
Expand Down
9 changes: 9 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,12 @@
workspace(
name = "toolchains_llvm",
)

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
name = "helly25_bzl",
sha256 = "404f8473bcaad2e370752e57d274d2093eb87ca94cb9a597c1a3553b76743206",
strip_prefix = "bzl-0.1.2",
url = "https://github.com/helly25/bzl/releases/download/0.1.2/bzl-0.1.2.tar.gz",
)
6 changes: 5 additions & 1 deletion tests/scripts/run_toolchain_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,9 @@ targets=(
"//toolchain/..."
)

if [[ -z "${common_test_args:-}" ]]; then
common_test_args=()
fi

"${bazel}" ${TEST_MIGRATION:+"--strict"} --bazelrc=/dev/null test \
"${common_test_args[@]}" "${test_args[@]}" "${targets[@]}"
"${common_test_args[@]}" "${test_args[@]}" -- "${targets[@]}"
10 changes: 9 additions & 1 deletion toolchain/deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,12 @@ def bazel_toolchain_dependencies():
sha256 = "bc283cdfcd526a52c3201279cda4bc298652efa898b10b4db0837dc51652756f",
)

# Skip bazel_skylib_workspace because we are not using lib/unittest.bzl
# Skip bazel_skylib_workspace because we are not using lib/unittest.bzl

if not native.existing_rule("helly25_bzl"):
http_archive(
name = "helly25_bzl",
strip_prefix = "bzl-0.1.2",
url = "https://github.com/helly25/bzl/releases/download/0.1.2/bzl-0.1.2.tar.gz",
sha256 = "404f8473bcaad2e370752e57d274d2093eb87ca94cb9a597c1a3553b76743206",
)
17 changes: 15 additions & 2 deletions toolchain/internal/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
# limitations under the License.

load("@bazel_skylib//rules:diff_test.bzl", "diff_test")
load("llvm_distributions.bzl", "write_distributions")
load("llvm_distributions.bzl", "distributions_test_writer", "requirements_test_writer")

exports_files(["template.modulemap"])

write_distributions(
distributions_test_writer(
name = "llvm_distributions",
testonly = True,
output = "llvm_distributions.out.txt",
Expand All @@ -36,3 +36,16 @@ diff_test(
file1 = "llvm_distributions.golden.sel.txt",
file2 = "llvm_distributions.sel.txt",
)

requirements_test_writer(
name = "llvm_requirements_test_output",
testonly = True,
result = "llvm_requirements_test.output.txt",
visibility = ["//visibility:private"],
)

diff_test(
name = "llvm_requirements_test",
file1 = "llvm_requirements_test.golden.txt",
file2 = "llvm_requirements_test.output.txt",
)
10 changes: 10 additions & 0 deletions toolchain/internal/configure.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ load(
_supported_targets = "SUPPORTED_TARGETS",
_toolchain_tools = "toolchain_tools",
)
load("//toolchain/internal:llvm_distributions.bzl", "is_requirement", "required_llvm_release_name_rctx")
load(
"//toolchain/internal:sysroot.bzl",
_default_sysroot_path = "default_sysroot_path",
Expand Down Expand Up @@ -80,6 +81,15 @@ def llvm_config_impl(rctx):
if not toolchain_root:
fail("LLVM toolchain root missing for ({}, {})".format(os, arch))
(_key, llvm_version) = _exec_os_arch_dict_value(rctx, "llvm_versions")
if is_requirement(llvm_version):
llvm_version, distribution, error = required_llvm_release_name_rctx(rctx, llvm_version)
if error:
fail(error)
if llvm_version:
print("\nINFO: Resolved latest LLVM version to {llvm_version}: {distribution}".format(
distribution = distribution,
llvm_version = llvm_version,
)) # buildifier: disable=print
if not llvm_version:
# LLVM version missing for (os, arch)
_empty_repository(rctx)
Expand Down
181 changes: 165 additions & 16 deletions toolchain/internal/llvm_distributions.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# limitations under the License.

load("@bazel_tools//tools/build_defs/repo:utils.bzl", "read_netrc", "use_netrc")
load("@helly25_bzl//bzl/versions:versions.bzl", "versions")
load(
"//toolchain/internal:common.bzl",
"attr_dict",
Expand Down Expand Up @@ -788,7 +789,8 @@ def _get_llvm_version(rctx):

def _get_all_llvm_distributions(*, llvm_distributions, extra_llvm_distributions, parsed_llvm_version):
distributions = {}
for basename, sha256 in llvm_distributions.items():
for dist, sha256 in llvm_distributions.items() + (extra_llvm_distributions.items() if extra_llvm_distributions else []):
basename = _distribution_basename(dist)
version = _distribution_version(basename)
if parsed_llvm_version and parsed_llvm_version != version:
continue
Expand All @@ -797,16 +799,6 @@ def _get_all_llvm_distributions(*, llvm_distributions, extra_llvm_distributions,
sha256 = sha256,
version = version,
)
if extra_llvm_distributions:
for dist, sha256 in extra_llvm_distributions.items():
version = _distribution_version(dist)
if parsed_llvm_version and parsed_llvm_version != version:
continue
distributions[_distribution_basename(dist)] = struct(
distribution = dist,
sha256 = sha256,
version = version,
)
return distributions

_UBUNTU_NAMES = [
Expand Down Expand Up @@ -1069,6 +1061,10 @@ def _find_llvm_basename_list(llvm_version, all_llvm_distributions, host_info):
return []

def _find_llvm_basename_or_error(llvm_version, all_llvm_distributions, host_info):
all_llvm_distributions = _filter_llvm_distributions(
llvm_version = llvm_version,
all_llvm_distributions = all_llvm_distributions,
)
basenames = _find_llvm_basename_list(llvm_version, all_llvm_distributions, host_info)
if len(basenames) > 1:
return None, "ERROR: Multiple configurations found for version {llvm_version} on {os}/{dist_name}/{dist_version} with arch {arch}: [{basenames}].".format(
Expand All @@ -1095,8 +1091,69 @@ def _find_llvm_basename_or_error(llvm_version, all_llvm_distributions, host_info

return basenames[0], None

def _parse_version_or_requirements(version_or_requirements):
if version_or_requirements in ["latest", "first"]:
return None
for prefix in ["latest:", "first:"]:
if version_or_requirements.startswith(prefix):
return versions.parse_requirements(version_or_requirements.removeprefix(prefix))
fail("ERROR: Invalid version requirements: '{version_or_requirements}'.".format(
version_or_requirements = version_or_requirements,
))

def _get_version_from_distribution(distribution):
# We assume here that the `distribution` is a basename of the form `LLVM-<version>-...` or
# `clang+llvm-<version>-...`.
return distribution.split("-")[1]

def _get_llvm_versions(*, version_or_requirements, all_llvm_distributions):
llvm_version_dict = {}
for distribution in all_llvm_distributions.keys():
version = _get_version_from_distribution(distribution)
llvm_version_dict[_parse_version(version)] = version

return [v for k, v in sorted(llvm_version_dict.items(), reverse = version_or_requirements.startswith("latest"))]

def _required_llvm_release_name(*, version_or_requirements, all_llvm_distributions, host_info):
llvm_versions = _get_llvm_versions(version_or_requirements = version_or_requirements, all_llvm_distributions = all_llvm_distributions)
requirements = _parse_version_or_requirements(version_or_requirements)
for llvm_version in llvm_versions:
if requirements and not versions.check_all_requirements(llvm_version, requirements):
continue
basenames = _find_llvm_basename_list(llvm_version, all_llvm_distributions, host_info)
if len(basenames) == 1:
return llvm_version, basenames[0], None
return None, None, "ERROR: No matching distribution found."

def required_llvm_release_name_rctx(rctx, llvm_version):
all_llvm_distributions = _get_all_llvm_distributions(
llvm_distributions = _llvm_distributions,
extra_llvm_distributions = rctx.attr.extra_llvm_distributions,
parsed_llvm_version = _parse_version(llvm_version),
)
return _required_llvm_release_name(
version_or_requirements = llvm_version,
all_llvm_distributions = all_llvm_distributions,
host_info = host_info(rctx),
)

def is_requirement(version_or_requirement):
"""Return whether `version_or_requirement` is likely a requirement (True) or should be a version."""
for prefix in ["first:", "latest:"]:
if version_or_requirement.startswith(prefix) or version_or_requirement == prefix[:-1]:
return True
return False

def _filter_llvm_distributions(*, llvm_version, all_llvm_distributions):
"""Return (distribution: sha) entries from `all_llvm_distributions` that match `llvm_version`."""
result = {}
for k, v in all_llvm_distributions.items():
if _get_version_from_distribution(k) == llvm_version:
result[k] = v
return result

def _distribution_urls(rctx):
"""Return LLVM `urls`, `shha256` and `strip_prefix` for the given context."""
"""Return LLVM `urls`, `sha256` and `strip_prefix` for the given context."""
llvm_version = _get_llvm_version(rctx)
all_llvm_distributions = _get_all_llvm_distributions(
llvm_distributions = _llvm_distributions,
Expand All @@ -1106,7 +1163,15 @@ def _distribution_urls(rctx):
_, sha256, strip_prefix, _ = _key_attrs(rctx)

if rctx.attr.distribution == "auto":
basename, error = _find_llvm_basename_or_error(llvm_version, all_llvm_distributions, host_info(rctx))
rctx_host_info = host_info(rctx)
if is_requirement(llvm_version):
llvm_version, basename, error = _required_llvm_release_name(
version_or_requirements = llvm_version,
all_llvm_distributions = all_llvm_distributions,
host_info = rctx_host_info,
)
else:
basename, error = _find_llvm_basename_or_error(llvm_version, all_llvm_distributions, rctx_host_info)
if error:
fail(error)
dist_info = all_llvm_distributions[basename]
Expand Down Expand Up @@ -1143,7 +1208,7 @@ def _distribution_urls(rctx):

return urls, sha256, strip_prefix

def _write_distributions_impl(ctx):
def _distributions_test_writer_impl(ctx):
"""Analyze the configured versions and write to a file for test consumption.

The test generated file '<rule_name>.out' contains the following lines:
Expand Down Expand Up @@ -1353,10 +1418,94 @@ def _write_distributions_impl(ctx):
ctx.actions.write(ctx.outputs.output, "\n".join(output) + "\n")
ctx.actions.write(ctx.outputs.select, "\n".join(select) + "\n")

write_distributions = rule(
implementation = _write_distributions_impl,
distributions_test_writer = rule(
implementation = _distributions_test_writer_impl,
attrs = {
"output": attr.output(mandatory = True),
"select": attr.output(mandatory = True),
},
)

def _requirements_test_writer_impl(ctx):
"""Analyze the configured versions and write to a file for test consumption.
The test generated file '<rule_name>.out' contains the following lines:
[<arch>,<os>,<requirement>]: <llvm_distribution_basename>
"""
all_llvm_distributions = {
# In order to prevent new distributions to interfere we cut at 20.1.3.
k: v
for k, v in _llvm_distributions.items()
if _parse_version(_get_version_from_distribution(k)) <= (20, 1, 3)
}
requirement_list = [
"latest:<=20.1.0",
"latest:<=20.1.0,>17.0.4,!=19.1.7",
"latest:<20.1.0,>17.0.4,!=19.1.7",
"latest:<20.1.0,>17.0.4",
"latest:>=15.0.6,<16",
"first:>=15.0.6,<16",
"latest",
"first",
]
arch_list = [
"aarch64",
"armv7a",
"x86_64",
]
os_list = [
"darwin",
"linux",
"windows",
]
ANY_VERSION = "0" # Version does not matter, but must be a valid integer
dist_dict_list = {
"linux": [
# keep sorted
struct(name = "ubuntu", version = ANY_VERSION),
struct(name = "raspbian", version = ANY_VERSION),
struct(name = "rhel", version = ANY_VERSION),
],
}
result = []
for arch in arch_list:
for os in os_list:
dist_list = dist_dict_list.get(os, [struct(name = os, version = "")])
for dist in dist_list:
for requirement in requirement_list:
host_info = struct(
arch = arch,
os = os,
dist = dist,
)
llvm_version, basename, error = _required_llvm_release_name(
version_or_requirements = requirement,
all_llvm_distributions = all_llvm_distributions,
host_info = host_info,
)
if llvm_version and basename:
result.append("[{arch},{os}{dist_name}{dist_version},'{requirement}']: {llvm_version} = {basename}".format(
arch = arch,
os = os,
dist_name = "," + dist.name if os == "linux" else "",
dist_version = "," + dist.version if os == "linux" else "",
requirement = requirement,
llvm_version = llvm_version,
basename = basename,
))
else:
result.append("[{arch},{os},\"{requirement}\"]: {error}".format(
arch = arch,
os = os,
requirement = requirement,
llvm_version = llvm_version,
basename = basename,
error = error or "ERROR: N/A",
))
ctx.actions.write(ctx.outputs.result, "\n".join(result) + "\n")

requirements_test_writer = rule(
implementation = _requirements_test_writer_impl,
attrs = {
"result": attr.output(mandatory = True),
},
)
Loading