Skip to content

Commit dbe3ab5

Browse files
authored
Merge pull request #3 from brandonros/master
Add workflow and scripts to build LLVM prebuilts
2 parents 8b0e4e5 + 7962068 commit dbe3ab5

8 files changed

Lines changed: 456 additions & 11 deletions

File tree

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.patch text eol=lf

.github/workflows/build-llvm.yml

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
name: Build LLVM
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
llvm_version:
7+
description: LLVM version to build (e.g. 7.1.0 or 19.1.7)
8+
required: true
9+
default: 19.1.7
10+
release:
11+
description: Publish tarballs to a GitHub release tagged llvm-<version>
12+
type: boolean
13+
default: true
14+
15+
jobs:
16+
build:
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
include:
21+
- os: ubuntu-22.04
22+
artifact: linux-x86_64
23+
- os: windows-2022
24+
artifact: windows-x86_64
25+
runs-on: ${{ matrix.os }}
26+
timeout-minutes: 360
27+
28+
steps:
29+
- uses: actions/checkout@v4
30+
31+
- name: Install Linux build deps
32+
if: runner.os == 'Linux'
33+
run: |
34+
sudo apt-get update
35+
sudo apt-get install -y --no-install-recommends \
36+
build-essential cmake ninja-build python3 \
37+
libffi-dev libedit-dev libncurses5-dev libxml2-dev \
38+
zlib1g-dev xz-utils
39+
40+
- name: Build LLVM (Linux)
41+
if: runner.os == 'Linux'
42+
env:
43+
LLVM_VERSION: ${{ inputs.llvm_version }}
44+
INSTALL_PREFIX: ${{ github.workspace }}/install
45+
run: ./scripts/build-llvm-linux.sh
46+
47+
- name: Build LLVM (Windows)
48+
if: runner.os == 'Windows'
49+
shell: pwsh
50+
env:
51+
LLVM_VERSION: ${{ inputs.llvm_version }}
52+
INSTALL_PREFIX: ${{ github.workspace }}\install
53+
run: ./scripts/build-llvm-windows.ps1
54+
55+
- name: Package
56+
shell: bash
57+
env:
58+
INSTALL_PREFIX: ${{ github.workspace }}/install
59+
ARTIFACT_NAME: ${{ matrix.artifact }}
60+
run: ./scripts/package-prebuilt.sh
61+
62+
- uses: actions/upload-artifact@v4
63+
with:
64+
name: ${{ matrix.artifact }}-llvm-${{ inputs.llvm_version }}
65+
path: ${{ matrix.artifact }}.tar.xz
66+
if-no-files-found: error
67+
68+
release:
69+
if: ${{ inputs.release }}
70+
needs: build
71+
runs-on: ubuntu-22.04
72+
permissions:
73+
contents: write
74+
steps:
75+
- uses: actions/download-artifact@v4
76+
with:
77+
path: artifacts
78+
pattern: '*-llvm-${{ inputs.llvm_version }}'
79+
merge-multiple: true
80+
81+
- name: Publish release
82+
env:
83+
GH_TOKEN: ${{ github.token }}
84+
TAG: llvm-${{ inputs.llvm_version }}
85+
VERSION: ${{ inputs.llvm_version }}
86+
run: |
87+
set -euo pipefail
88+
ls -lR artifacts
89+
if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
90+
gh release upload "$TAG" artifacts/*.tar.xz \
91+
--repo "$GITHUB_REPOSITORY" --clobber
92+
else
93+
gh release create "$TAG" artifacts/*.tar.xz \
94+
--repo "$GITHUB_REPOSITORY" \
95+
--title "LLVM $VERSION" \
96+
--notes "Prebuilt LLVM $VERSION for x86_64 Linux and Windows. Built by [build-llvm.yml](../../actions/workflows/build-llvm.yml) run #$GITHUB_RUN_NUMBER."
97+
fi

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/install/
2+
/build/
3+
/llvm-src/
4+
/*.tar.xz
5+
/prebuilt/

README.md

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,70 @@
11
# Prebuilt LLVM Libraries
22

3-
This repo contains prebuilt LLVM 7.1.0 Libraries for Windows and Linux.
3+
This repository builds and publishes prebuilt LLVM libraries used by [`rustc_codegen_nvvm`](https://github.com/Rust-GPU/Rust-CUDA/tree/main/crates/rustc_codegen_nvvm). Building LLVM from source is slow and finicky — especially older versions — so we publish pinned, reproducible tarballs to GitHub releases that `rustc_codegen_nvvm`'s `build.rs` can download.
44

5-
These libraries are required by rustc_codegen_nvvm, however, LLVM is difficult to
6-
build, especially older versions of LLVM. Therefore, we ship prebuilt compressed
7-
7zip files which contain:
8-
- `include/llvm` and `include/llvm-c`, these are required for building the LLVM shim.
9-
- `lib/*`, these are the actual `.lib` files we tell rustc to include when building the codegen.
5+
The repo itself contains only the build scripts and CI; the tarballs live as release assets so cloning stays fast.
106

11-
We do not include binaries other than llvm-config because they aren't needed for us.
7+
## What's published
128

13-
The prebuilt libraries are pulled from the following locations:
14-
- linux-x86_64: LLVM prebuilt downloads 7.1.0, Ubuntu 14.04.
15-
- windows-x86_64: Built from source.
9+
Each release is tagged `llvm-<version>` (e.g. `llvm-19.1.7`) and ships two assets:
1610

17-
We do not ship mac binaries because currently we only support CUDA 11 and up, and CUDA dropped MacOS support.
11+
| Asset | Target triple |
12+
|------------------------|---------------------------|
13+
| `linux-x86_64.tar.xz` | `x86_64-unknown-linux-gnu` |
14+
| `windows-x86_64.tar.xz` | `x86_64-pc-windows-msvc` |
15+
16+
Download URLs follow the standard GitHub releases pattern:
17+
18+
```
19+
https://github.com/<owner>/<repo>/releases/download/llvm-<version>/<artifact>.tar.xz
20+
```
21+
22+
CUDA dropped macOS support before any Rust-CUDA-relevant version, so no Darwin builds are produced.
23+
24+
Each tarball expands to a single top-level directory matching the target name (`linux-x86_64/` or `windows-x86_64/`) containing:
25+
26+
```
27+
bin/
28+
llvm-config[.exe] # probed by build.rs for components, cxxflags, libs
29+
llvm-as[.exe] # used by the LLVM 19 build path to assemble libintrinsics.ll
30+
include/
31+
llvm/
32+
llvm-c/
33+
lib/
34+
*.{a,so,lib} # static archives (and dylib on Linux)
35+
```
36+
37+
`bin/cmake` and `lib/pkgconfig` are stripped because `build.rs` consumes the link-line via `llvm-config`, not `find_package(LLVM)`.
38+
39+
## Build configuration
40+
41+
Both LLVM 7 and LLVM 19 are built with the same flags:
42+
43+
- `CMAKE_BUILD_TYPE=Release`
44+
- `LLVM_TARGETS_TO_BUILD=X86;NVPTX`
45+
- `LLVM_ENABLE_ASSERTIONS=OFF`
46+
- `LLVM_ENABLE_BINDINGS=OFF`
47+
- `LLVM_INCLUDE_{EXAMPLES,TESTS,BENCHMARKS}=OFF`
48+
- Linux only: `LLVM_BUILD_LLVM_DYLIB=ON`, `LLVM_LINK_LLVM_DYLIB=ON`, `LLVM_ENABLE_{ZLIB,TERMINFO}=ON`
49+
50+
Linux builds run on `ubuntu-22.04` (glibc 2.35), giving a floor that matches the rust-cuda container baseline. Windows builds run on `windows-2022`. **LLVM 7 on Windows is compiled with `clang-cl` from the preinstalled LLVM toolchain** because MSVC v143 (the default compiler on `windows-2022`) refuses LLVM 7's older sources without source patches; LLVM 19 uses MSVC directly.
51+
52+
## How to build and publish a new version
53+
54+
Trigger [`build-llvm.yml`](.github/workflows/build-llvm.yml) from the Actions tab via **Run workflow**, supplying the LLVM version (e.g. `7.1.0` or `19.1.7`). The matrix runs on Linux and Windows runners. With the default `release: true` input, the workflow then creates (or updates) a release tagged `llvm-<version>` and uploads both tarballs as assets — re-running clobbers the existing assets, so it's safe to retry. Untick `release` to build without publishing; tarballs are still available as run artifacts on the workflow run page.
55+
56+
## How to reproduce locally
57+
58+
The CI uses these scripts directly. To rebuild a tarball locally:
59+
60+
```sh
61+
# Linux
62+
LLVM_VERSION=19.1.7 INSTALL_PREFIX="$PWD/install" ./scripts/build-llvm-linux.sh
63+
INSTALL_PREFIX="$PWD/install" ARTIFACT_NAME=linux-x86_64 ./scripts/package-prebuilt.sh
64+
65+
# Windows (PowerShell)
66+
$env:LLVM_VERSION = '19.1.7'; $env:INSTALL_PREFIX = "$PWD\install"
67+
./scripts/build-llvm-windows.ps1
68+
$env:ARTIFACT_NAME = 'windows-x86_64'
69+
bash ./scripts/package-prebuilt.sh
70+
```

scripts/build-llvm-linux.sh

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Build LLVM from source on Linux. Produces an install tree at $INSTALL_PREFIX
5+
# matching the layout that scripts/package-prebuilt.sh expects.
6+
#
7+
# Required env:
8+
# LLVM_VERSION e.g. 7.1.0 or 19.1.7
9+
# INSTALL_PREFIX absolute path; cmake --install target
10+
#
11+
# Optional env:
12+
# WORK_DIR where to extract sources (default: $PWD/llvm-src)
13+
14+
: "${LLVM_VERSION:?LLVM_VERSION must be set (e.g. 7.1.0 or 19.1.7)}"
15+
: "${INSTALL_PREFIX:?INSTALL_PREFIX must be set}"
16+
17+
ARCH=$(uname -m)
18+
case "$ARCH" in
19+
x86_64) TARGETS="X86;NVPTX" ;;
20+
aarch64) TARGETS="AArch64;NVPTX" ;;
21+
*) echo "Unsupported arch: $ARCH" >&2; exit 1 ;;
22+
esac
23+
24+
LLVM_MAJOR="${LLVM_VERSION%%.*}"
25+
26+
# LLVM 7.x ships `llvm-X.Y.Z.src.tar.xz` (just the llvm subtree, cmake root is
27+
# the extracted dir itself). LLVM 8+ ships `llvm-project-X.Y.Z.src.tar.xz`
28+
# (monorepo, cmake root is the `llvm/` subdir).
29+
if [ "$LLVM_MAJOR" -le 7 ]; then
30+
TARBALL="llvm-${LLVM_VERSION}.src.tar.xz"
31+
SRC_DIR="llvm-${LLVM_VERSION}.src"
32+
CMAKE_SRC=".."
33+
else
34+
TARBALL="llvm-project-${LLVM_VERSION}.src.tar.xz"
35+
SRC_DIR="llvm-project-${LLVM_VERSION}.src"
36+
CMAKE_SRC="../llvm"
37+
fi
38+
39+
WORK_DIR="${WORK_DIR:-$PWD/llvm-src}"
40+
mkdir -p "$WORK_DIR"
41+
cd "$WORK_DIR"
42+
43+
if [ ! -d "$SRC_DIR" ]; then
44+
curl -sSfL -O "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/${TARBALL}"
45+
tar -xf "$TARBALL"
46+
fi
47+
48+
cd "$SRC_DIR"
49+
mkdir -p build
50+
cd build
51+
52+
cmake -G Ninja \
53+
-DCMAKE_BUILD_TYPE=Release \
54+
-DLLVM_TARGETS_TO_BUILD="$TARGETS" \
55+
-DLLVM_BUILD_LLVM_DYLIB=ON \
56+
-DLLVM_LINK_LLVM_DYLIB=ON \
57+
-DLLVM_ENABLE_ASSERTIONS=OFF \
58+
-DLLVM_ENABLE_BINDINGS=OFF \
59+
-DLLVM_INCLUDE_EXAMPLES=OFF \
60+
-DLLVM_INCLUDE_TESTS=OFF \
61+
-DLLVM_INCLUDE_BENCHMARKS=OFF \
62+
-DLLVM_ENABLE_ZLIB=ON \
63+
-DLLVM_ENABLE_TERMINFO=ON \
64+
-DCMAKE_INSTALL_PREFIX="$INSTALL_PREFIX" \
65+
"$CMAKE_SRC"
66+
67+
ninja -j"$(nproc)"
68+
ninja install

scripts/build-llvm-windows.ps1

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
$ErrorActionPreference = 'Stop'
2+
3+
# Build LLVM from source on Windows. Produces an install tree at
4+
# $env:INSTALL_PREFIX matching the layout scripts/package-prebuilt.sh expects.
5+
#
6+
# Required env:
7+
# LLVM_VERSION e.g. 7.1.0 or 19.1.7
8+
# INSTALL_PREFIX absolute path; cmake --install target
9+
#
10+
# Notes on toolchain choice:
11+
# - LLVM 19+ builds cleanly with MSVC v143 (default on windows-2022).
12+
# - LLVM 7 predates a number of MSVC C++ conformance fixes and will not
13+
# compile under MSVC v143 without source patches. We sidestep that by
14+
# using clang-cl from the LLVM toolchain preinstalled on windows-2022,
15+
# which is more permissive while still producing MSVC-ABI binaries.
16+
# - LLVM_BUILD_LLVM_DYLIB is unsupported on Windows; consumers link against
17+
# the static archives that build.rs already prefers by default.
18+
19+
if (-not $env:LLVM_VERSION) { throw 'LLVM_VERSION must be set' }
20+
if (-not $env:INSTALL_PREFIX) { throw 'INSTALL_PREFIX must be set' }
21+
22+
$llvmVersion = $env:LLVM_VERSION
23+
$installPrefix = $env:INSTALL_PREFIX
24+
$llvmMajor = [int]($llvmVersion -split '\.')[0]
25+
26+
$workDir = if ($env:WORK_DIR) { $env:WORK_DIR } else { Join-Path $PWD 'llvm-src' }
27+
New-Item -ItemType Directory -Force -Path $workDir | Out-Null
28+
Set-Location $workDir
29+
30+
# Use Git for Windows' GNU tar by full path. The default `tar` on PATH is
31+
# C:\Windows\System32\tar.exe (bsdtar) which hangs on .tar.xz — see
32+
# actions/runner-images#282. Prepending Git's bin dir to PATH also lets
33+
# GNU tar find `xz` when shelling out for the -J path.
34+
$gitBin = 'C:\Program Files\Git\usr\bin'
35+
$gitTar = Join-Path $gitBin 'tar.exe'
36+
if (-not (Test-Path $gitTar)) { throw "Git for Windows tar.exe not found at $gitTar" }
37+
$env:PATH = "$gitBin;$env:PATH"
38+
39+
function Get-LlvmTarball {
40+
param([string]$name)
41+
if (Test-Path $name) { return }
42+
$url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-$llvmVersion/$name"
43+
Write-Host "Downloading $url"
44+
curl.exe -fL -o $name $url
45+
if ($LASTEXITCODE -ne 0) { throw "download failed: $name" }
46+
}
47+
48+
function Expand-LlvmTarball {
49+
param([string]$name, [string[]]$paths)
50+
if ($paths) { & $gitTar -xf $name @paths } else { & $gitTar -xf $name }
51+
if ($LASTEXITCODE -ne 0) { throw "tar extraction failed: $name" }
52+
}
53+
54+
if ($llvmMajor -le 7) {
55+
$tarball = "llvm-$llvmVersion.src.tar.xz"
56+
$srcDir = "llvm-$llvmVersion.src"
57+
$cmakeSrc = '..'
58+
} else {
59+
$tarball = "llvm-project-$llvmVersion.src.tar.xz"
60+
$srcDir = "llvm-project-$llvmVersion.src"
61+
$cmakeSrc = '../llvm'
62+
}
63+
64+
if (-not (Test-Path $srcDir)) {
65+
Get-LlvmTarball $tarball
66+
if ($llvmMajor -le 7) {
67+
Expand-LlvmTarball $tarball
68+
} else {
69+
# clang/test/Driver/Inputs has out-of-order symlinks that msys-tar
70+
# cannot create on Windows (CreateSymbolicLink requires the target's
71+
# FILE/DIR type, which it determines by stat'ing — fails when the
72+
# target hasn't been extracted yet). The build only needs llvm/,
73+
# cmake/, and third-party/, so extract just those three subtrees.
74+
Expand-LlvmTarball $tarball @("$srcDir/llvm", "$srcDir/cmake", "$srcDir/third-party")
75+
}
76+
77+
# LLVM 7 ships with uninitialized members in ManagedStaticBase, which
78+
# under modern MSVC turns the AllSubCommands ManagedStatic into a
79+
# dynamic-init that wipes the cl::opt list — making -gen-attrs and most
80+
# of llvm-tblgen's options invisible at runtime. Backport the upstream
81+
# fix (LLVM #40712 / D80433). See the patch header for full details.
82+
if ($llvmMajor -le 7) {
83+
$patch = Join-Path $gitBin 'patch.exe'
84+
if (-not (Test-Path $patch)) { throw "patch.exe not found at $patch" }
85+
$patchFile = Join-Path $PSScriptRoot 'patches\llvm7-managedstatic-msvc.patch'
86+
Push-Location $srcDir
87+
try {
88+
& $patch -p1 --binary -i $patchFile
89+
if ($LASTEXITCODE -ne 0) { throw "patch failed: $patchFile" }
90+
} finally {
91+
Pop-Location
92+
}
93+
}
94+
}
95+
96+
Set-Location $srcDir
97+
New-Item -ItemType Directory -Force -Path build | Out-Null
98+
Set-Location build
99+
100+
Write-Host "Configuring cmake"
101+
102+
$cmakeArgs = @(
103+
'-G', 'Ninja',
104+
'-DCMAKE_BUILD_TYPE=Release',
105+
'-DLLVM_TARGETS_TO_BUILD=X86;NVPTX',
106+
'-DLLVM_ENABLE_ASSERTIONS=OFF',
107+
'-DLLVM_ENABLE_BINDINGS=OFF',
108+
'-DLLVM_INCLUDE_EXAMPLES=OFF',
109+
'-DLLVM_INCLUDE_TESTS=OFF',
110+
'-DLLVM_INCLUDE_BENCHMARKS=OFF',
111+
'-DLLVM_ENABLE_ZLIB=OFF',
112+
'-DLLVM_ENABLE_TERMINFO=OFF',
113+
"-DCMAKE_INSTALL_PREFIX=$installPrefix"
114+
)
115+
116+
if ($llvmMajor -le 7) {
117+
$clangCl = 'C:/Program Files/LLVM/bin/clang-cl.exe'
118+
if (-not (Test-Path $clangCl)) {
119+
throw "clang-cl required for LLVM 7 builds was not found at $clangCl"
120+
}
121+
$cmakeArgs += @(
122+
"-DCMAKE_C_COMPILER=$clangCl",
123+
"-DCMAKE_CXX_COMPILER=$clangCl"
124+
)
125+
}
126+
127+
$cmakeArgs += $cmakeSrc
128+
129+
cmake @cmakeArgs
130+
if ($LASTEXITCODE -ne 0) { throw 'cmake configure failed' }
131+
132+
Write-Host "Building (ninja -j $env:NUMBER_OF_PROCESSORS)"
133+
ninja -j $env:NUMBER_OF_PROCESSORS
134+
if ($LASTEXITCODE -ne 0) { throw 'ninja build failed' }
135+
136+
Write-Host "Installing to $installPrefix"
137+
ninja install
138+
if ($LASTEXITCODE -ne 0) { throw 'ninja install failed' }
139+
140+
Write-Host "Done."

0 commit comments

Comments
 (0)