-
Notifications
You must be signed in to change notification settings - Fork 96
POC fast_llvm toolchain #973
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
Draft
AlexanderLanin
wants to merge
4
commits into
main
Choose a base branch
from
fast_llvm
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| # The supported LLVM releases. Each entry maps a requested version and host | ||
| # architecture to the upstream archive Bazel downloads and verifies. | ||
| _LLVM_DISTRIBUTIONS = { | ||
| "19.1.0": { | ||
| "aarch64": { | ||
| # Official prebuilt LLVM package for 64-bit ARM Linux hosts. | ||
| "url": "https://github.com/llvm/llvm-project/releases/download/llvmorg-19.1.0/clang+llvm-19.1.0-aarch64-linux-gnu.tar.xz", | ||
| # SHA-256 pinned by toolchains_llvm for this exact upstream archive. | ||
| "sha256": "7bb54afd330fe1a1c2d4c593fa1e2dbe2abd9bf34fb3597994ff41e443cf144b", | ||
| }, | ||
| "x86_64": { | ||
| # Official prebuilt LLVM package for 64-bit x86 Linux hosts. | ||
| "url": "https://github.com/llvm/llvm-project/releases/download/llvmorg-19.1.0/LLVM-19.1.0-Linux-X64.tar.xz", | ||
| # SHA-256 of that archive. `ctx.download` rejects altered or corrupt | ||
| # downloads instead of unpacking them. | ||
| "sha256": "cee77d641690466a193d9b88c89705de1c02bbad46bde6a3b126793c0a0f2923", | ||
| }, | ||
| }, | ||
| "19.1.1": { | ||
| "aarch64": { | ||
| # Official prebuilt LLVM package for 64-bit ARM Linux hosts. | ||
| "url": "https://github.com/llvm/llvm-project/releases/download/llvmorg-19.1.1/clang+llvm-19.1.1-aarch64-linux-gnu.tar.xz", | ||
| # SHA-256 pinned by toolchains_llvm for this exact upstream archive. | ||
| "sha256": "414d2ebef10c5035e9df10a224e81b484dbe17d319373050d0c1b3b1467040d2", | ||
| }, | ||
| "x86_64": { | ||
| # Official prebuilt LLVM package for 64-bit x86 Linux hosts. | ||
| "url": "https://github.com/llvm/llvm-project/releases/download/llvmorg-19.1.1/LLVM-19.1.1-Linux-X64.tar.xz", | ||
| # SHA-256 pinned by toolchains_llvm for this exact upstream archive. | ||
| "sha256": "8204de000b6a6921f0572e038336601e3225898e9a253c8aaa43b0a5fae8a4ce", | ||
| }, | ||
| }, | ||
| "22.1.7": { | ||
| "aarch64": { | ||
| # Official prebuilt LLVM package for 64-bit ARM Linux hosts. | ||
| "url": "https://github.com/llvm/llvm-project/releases/download/llvmorg-22.1.7/LLVM-22.1.7-Linux-ARM64.tar.xz", | ||
| # SHA-256 from toolchains_llvm's distribution metadata for this | ||
| # exact upstream archive. `ctx.download` rejects altered or corrupt | ||
| # downloads. | ||
| "sha256": "118ca2d3ad9da34367e05735317854e7977db45dc4c02a32af58da64c23b8789", | ||
| }, | ||
| "x86_64": { | ||
| # Official prebuilt LLVM package for 64-bit x86 Linux hosts. | ||
| "url": "https://github.com/llvm/llvm-project/releases/download/llvmorg-22.1.7/LLVM-22.1.7-Linux-X64.tar.xz", | ||
| "sha256": "edb0522b41e261819c06ea437d249f9b8acfa413d3805bc9920eec6fb76ff830", | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| _ARCH_ALIASES = { | ||
| "aarch64": "aarch64", | ||
| "amd64": "x86_64", | ||
| "arm64": "aarch64", | ||
| "x86_64": "x86_64", | ||
| } | ||
|
|
||
| def _log(message): | ||
| print("[fast_llvm_repo] " + message) | ||
|
|
||
| # Implementation of `fast_llvm_repo`. Repository rules run while Bazel is | ||
| # preparing external dependencies, before analysing or building project code. | ||
| def _fast_llvm_repo_impl(ctx): | ||
| # Select the archive native to the host which will execute this toolchain. | ||
| host_arch = _ARCH_ALIASES.get(ctx.os.arch) | ||
| if not host_arch: | ||
| fail("Unsupported host architecture: %s" % ctx.os.arch) | ||
|
|
||
| # Look up the requested release and its host-specific archive. | ||
| distributions = _LLVM_DISTRIBUTIONS.get(ctx.attr.llvm_version) | ||
| dist = distributions.get(host_arch) if distributions else None | ||
|
|
||
| # Fail with a useful message if MODULE.bazel asks for a version that has no | ||
| # URL and checksum entry yet. | ||
| if not dist: | ||
| fail("Unsupported LLVM version/host architecture: %s/%s" % (ctx.attr.llvm_version, host_arch)) | ||
|
|
||
| # Choose a temporary file inside this external repository for the archive. | ||
| archive = ctx.path("llvm.tar.xz") | ||
|
|
||
| # Download the archive and verify its SHA-256 before using it. | ||
| _log("downloading LLVM %s for %s" % (ctx.attr.llvm_version, host_arch)) | ||
| ctx.download( | ||
| url = dist["url"], | ||
| output = archive, | ||
| sha256 = dist["sha256"], | ||
| ) | ||
| _log("download completed") | ||
|
|
||
| # Use the host's `tar` executable to unpack the xz archive. `ctx.which` | ||
| # returns None instead of guessing when `tar` is unavailable. | ||
| tar = ctx.which("tar") | ||
| if not tar: | ||
| fail("tar not found in PATH") | ||
|
|
||
| # LLVM's archive has many independently decompressible XZ blocks. Let xz | ||
| # decode them in parallel, then stream the resulting tar file directly to | ||
| # tar; materializing the 8+ GiB uncompressed archive would be wasteful. | ||
| # Keep the regular tar invocation for minimal Linux hosts without xz. | ||
| xz = ctx.which("xz") | ||
| if xz: | ||
| _log("extracting LLVM with xz -T0 and tar") | ||
| sh = ctx.which("sh") | ||
| if not sh: | ||
| fail("sh not found in PATH") | ||
| result = ctx.execute( | ||
| [ | ||
| sh, | ||
| "-c", | ||
| "set -e; \"$1\" -T0 -dc \"$2\" | \"$3\" -xf - --strip-components=1 -C \"$4\"", | ||
| "fast_llvm_repo", | ||
| xz, | ||
| archive, | ||
| tar, | ||
| ctx.path("."), | ||
| ], | ||
| # Allow the relatively large archive up to 30 minutes to unpack. | ||
| timeout = 1800, | ||
| # Do not suppress command output from Bazel's repository-rule log. | ||
| quiet = False, | ||
| ) | ||
| else: | ||
| _log("xz not found; extracting LLVM with tar fallback") | ||
| result = ctx.execute( | ||
| [ | ||
| tar, | ||
| "-xf", | ||
| archive, | ||
| "--strip-components=1", | ||
| # Extract into the external repository represented by `ctx`. | ||
| "-C", | ||
| ctx.path("."), | ||
| ], | ||
| timeout = 1800, | ||
| quiet = False, | ||
| ) | ||
|
|
||
| # Stop repository creation if extraction reported an error. | ||
| if result.return_code: | ||
| fail(result.stderr) | ||
| _log("extraction completed") | ||
|
|
||
| # The archive is no longer needed after extraction; leave only the LLVM | ||
| # distribution files in the external repository. | ||
| ctx.delete(archive) | ||
|
|
||
| # Generate the BUILD file expected by toolchains_llvm 1.7. LLVM 16 and | ||
| # later store compiler resources in a directory named by the major version. | ||
| _log("generating BUILD.bazel") | ||
| ctx.template( | ||
| "BUILD.bazel", | ||
| Label("@toolchains_llvm//toolchain:BUILD.llvm_repo.tpl"), | ||
| substitutions = {"{LLVM_VERSION}": ctx.attr.llvm_version.split(".")[0]}, | ||
| ) | ||
| _log("llvm setup completed") | ||
|
|
||
| # Public repository rule used from MODULE.bazel. Its only user-facing input | ||
| # is an LLVM version. | ||
| fast_llvm_repo = repository_rule( | ||
| implementation = _fast_llvm_repo_impl, | ||
| attrs = { | ||
| # Required version key used to select an entry in `_LLVM_DISTRIBUTIONS`. | ||
| "llvm_version": attr.string(mandatory = True), | ||
| }, | ||
| ) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use https://docs.bazel.build/versions/0.29.1/repo/http.html#http_archive
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing this out — it seems I forgot to include any documentation for this POC.
Functionally,
http_archivewould be a valid solution. The LLVM toolchain internally creates a repository for the LLVM distribution and uses Bazel’s standard download-and-extract mechanism for it. Thatdownload_and_extract()step is exactly the slow part this POC is trying to improve.The custom rule downloads the pinned archive with
ctx.download()and extracts it usingxz -T0 | tar, allowing XZ decompression to run in parallel. In my measurements, this reduced the LLVM setup time from roughly 220 seconds to 70 seconds.So the custom rule is not required for correctness. It replaces only the internally generated LLVM distribution repository, while keeping the existing toolchain configuration and BUILD layout intact. The trade-off is additional maintenance and currently limited platform coverage, which is why this is still a POC.
If the additional extraction time is acceptable, sticking with the original
toolchains_llvmsetup is indeed the simpler option.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The brittleness comes from calling host tools directly in the repository-rule.
While this makes it fast, it may also make it behave differently depending on what version is installed in the system.
Not quite sure that the benefit is high enough to warant this. After all, in 99% of the cases, the toolchain is cached. Redownload really only happens if one builds without cache or invalidates it by modifying the toolchain.
@castler what are your thoughts on this?