Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
189 changes: 189 additions & 0 deletions .github/actions/setup-llvm/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# Shared toolchain setup for jobs that compile cx: installs LLVM 23 and the
# link-time dependencies, and exports LLVMDIR/EXTRA_CMAKE_* for the configure
# step. Used by both build.yml and release.yml so releases can never drift
# onto a different toolchain than CI tested.
name: Set up LLVM 23 and link dependencies
description: Install LLVM 23 plus zlib/zstd/DIA SDK and export CMake paths

runs:
using: composite
steps:
# Cache downloaded .debs (LLVM, SDL build deps, glfw) in a
# user-owned dir; the default archive dir is root-owned and the
# cache action could not restore into it. Stale entries are safe:
# apt re-downloads anything not matching the package index.
# The lock file and partial/ dir stay excluded: apt recreates them
# as root, and tar cannot read them back as the runner user.
- name: Cache apt archives (Ubuntu)
if: runner.os == 'Linux'
uses: actions/cache@v5
with:
path: |
/tmp/apt-archives
!/tmp/apt-archives/lock
!/tmp/apt-archives/partial
key: apt-archives-llvm-23-sdl-glfw

- name: Redirect apt archives (Ubuntu)
if: runner.os == 'Linux'
shell: bash
run: |
mkdir -p /tmp/apt-archives/partial
echo 'Dir::Cache::Archives "/tmp/apt-archives";' | sudo tee /etc/apt/apt.conf.d/99-ci-cache

- name: Download LLVM (Ubuntu)
if: runner.os == 'Linux'
shell: bash
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 23 all
echo "/usr/lib/llvm-23/bin" >> $GITHUB_PATH

# Bottle downloads are content-addressed, so a static key is safe.
# Only the fetch is cached; install/link still runs (cheap, and the
# full cellar layout is too version-sensitive to restore blindly).
- name: Cache Homebrew downloads (macOS)
if: runner.os == 'macOS'
uses: actions/cache@v5
with:
path: ~/Library/Caches/Homebrew
key: homebrew-downloads-llvm-23-sdl3-glfw

- name: Download LLVM (macOS)
if: runner.os == 'macOS'
shell: bash
run: |
# Refresh Homebrew first: a stale snapshot installs an older LLVM
# bottle (e.g. 23.1.0, whose libclangDriver.a ships an LLVM-bitcode
# object that the system linker can't parse).
brew update
brew install llvm@23
echo LLVMDIR=$(brew --prefix llvm@23) >> $GITHUB_ENV

# Cache the extracted LLVM tree: re-downloading and
# double-extracting the tarball costs minutes on every Windows leg.
# Bump the key when the URL below changes.
- name: Cache LLVM (Windows)
if: runner.os == 'Windows'
uses: actions/cache@v5
with:
path: clang+llvm-23.1.1-x86_64-pc-windows-msvc
key: llvm-windows-23.1.1

- name: Download LLVM (Windows)
if: runner.os == 'Windows'
shell: bash
run: |
# Reuse the cached tree only when it is complete; a cache entry
# saved from a failed extract must trigger a re-download, not a
# build against a corrupt tree.
if [ ! -f "clang+llvm-23.1.1-x86_64-pc-windows-msvc/bin/clang.exe" ]; then
rm -rf clang+llvm-23.1.1-x86_64-pc-windows-msvc
curl -L "https://github.com/llvm/llvm-project/releases/download/llvmorg-23.1.1/clang+llvm-23.1.1-x86_64-pc-windows-msvc.tar.xz" --output llvm.tar.xz && 7z x llvm.tar.xz && 7z x llvm.tar
fi
# The sed below converts the path to a Windows-style path
echo LLVMDIR="$(echo "$PWD/clang+llvm-23.1.1-x86_64-pc-windows-msvc" | sed -e 's/^\///' -e 's/\//\\/g' -e 's/^./\0:/')" >> $GITHUB_ENV

# LLVM 22+ locates the DIA SDK via $VSINSTALLDIR (see llvm/cmake/modules/FindDIASDK.cmake),
# which is only set inside a Visual Studio developer environment.
# vs-shell is composite (no Node runtime); ilammy/msvc-dev-cmd is abandoned on node20.
- name: Set up MSVC environment (Windows)
if: runner.os == 'Windows'
uses: egor-tensin/vs-shell@v2

- name: Provide DIA SDK (Windows)
if: runner.os == 'Windows'
shell: bash
run: |
# LLVM's LLVMDebugInfoPDB target needs diaguids.lib. Its FindDIASDK
# module only looks at $VSINSTALLDIR, which may point at an install
# without it, so search wider and pass an explicit path if needed.
DIA_CMAKE_DIR=""
if [ -f "$VSINSTALLDIR/DIA SDK/lib/amd64/diaguids.lib" ]; then
echo "DIA SDK found via VSINSTALLDIR"
else
VSWHERE="C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe"
if [ -f "$VSWHERE" ]; then
mapfile -t VS_INSTALLS < <("$VSWHERE" -products '*' -property installationPath)
for INSTALL_PATH in "${VS_INSTALLS[@]}"; do
INSTALL_PATH_UNIX=$(echo "$INSTALL_PATH" | tr -d '\r' | sed -e 's/\\/\//g')
if [ -f "$INSTALL_PATH_UNIX/DIA SDK/lib/amd64/diaguids.lib" ]; then
DIA_CMAKE_DIR="$INSTALL_PATH_UNIX/DIA SDK"
break
fi
done
fi
if [ -z "$DIA_CMAKE_DIR" ]; then
# Fall back to the DIA files shipped in the Windows SDK, staged
# into the layout FindDIASDK expects.
DIA_LIB=$(ls -d "/c/Program Files (x86)/Windows Kits/10/Lib/"*"/um/x64/diaguids.lib" 2>/dev/null | sort -r | head -n 1 || true)
DIA_INC=$(echo "$DIA_LIB" | sed -e 's#/Lib/\(.*\)/um/x64/diaguids.lib#/Include/\1/um/dia2.h#')
if [ -n "$DIA_LIB" ] && [ -f "$DIA_INC" ]; then
DIA_STAGE="$RUNNER_TEMP/dia-sdk"
mkdir -p "$DIA_STAGE/include" "$DIA_STAGE/lib/amd64"
cp "$DIA_LIB" "$DIA_STAGE/lib/amd64/diaguids.lib"
cp "$DIA_INC" "$DIA_STAGE/include/dia2.h"
DIA_CMAKE_DIR=$(echo "$DIA_STAGE" | sed -e 's/\\/\//g')
fi
fi
if [ -z "$DIA_CMAKE_DIR" ]; then
echo "ERROR: DIA SDK with lib/amd64/diaguids.lib not found in VSINSTALLDIR, any VS instance, or the Windows Kits"
exit 1
fi
echo "DIA SDK found at: $DIA_CMAKE_DIR"
echo "DIA_CMAKE_DIR=$DIA_CMAKE_DIR" >> $GITHUB_ENV
fi

# LLVM 22+ links LLVMSupport against the ZLIB::ZLIB and zstd::libzstd_static
# imported targets, so findable ZLIB and zstd are required at configure
# time. Use the static triplets to match the /MT runtime used with the
# prebuilt LLVM.
# Note: vcpkg names the static libs zs/zsd, which FindZLIB only
# searches for with ZLIB_USE_STATIC_LIBS=ON. Similarly, LLVM's Findzstd
# module only defines zstd::libzstd_static for a library literally named
# zstd_static.lib, which vcpkg doesn't ship (only zstd.lib), so copy it.
# The vcpkg baseline moves with the runner image, so resolve it to
# a commit SHA for the cache key. A static key would freeze on the
# first binaries saved (actions/cache never re-saves on hit) and
# rebuild from source forever after any update. Falls back to a
# static key if the install is not a git checkout.
- name: Get vcpkg baseline (Windows)
if: runner.os == 'Windows'
id: vcpkg-baseline
shell: bash
run: echo "sha=$(git -C \"$VCPKG_INSTALLATION_ROOT\" rev-parse HEAD 2>/dev/null || echo unknown)" >> "$GITHUB_OUTPUT"

# vcpkg restores the built ports from this binary cache instead of
# compiling them; each entry carries an ABI hash, so stale hits are
# impossible.
- name: Cache vcpkg binaries (Windows)
if: runner.os == 'Windows'
uses: actions/cache@v5
with:
path: ${{ runner.temp }}/vcpkg-binary-cache
key: vcpkg-${{ steps.vcpkg-baseline.outputs.sha }}-zlib-zstd-static

- name: Install zlib and zstd (Windows)
if: runner.os == 'Windows'
shell: bash
env:
VCPKG_BINARY_SOURCES: "clear;files,${{ runner.temp }}/vcpkg-binary-cache,readwrite"
run: |
vcpkg install zlib:x64-windows-static zstd:x64-windows-static
cp "$VCPKG_INSTALLATION_ROOT/installed/x64-windows-static/lib/zstd.lib" "$VCPKG_INSTALLATION_ROOT/installed/x64-windows-static/lib/zstd_static.lib"
ls "$VCPKG_INSTALLATION_ROOT/installed/x64-windows-static/lib"
echo "EXTRA_CMAKE_PREFIX=$VCPKG_INSTALLATION_ROOT/installed/x64-windows-static" >> $GITHUB_ENV
echo "EXTRA_CMAKE_ARGS=-DZLIB_USE_STATIC_LIBS=ON" >> $GITHUB_ENV

# The lit/snippet/LSP suites spawn thousands of compiler and linker
# processes, each writing temporary objects and executables. Real-time
# Defender scanning of every one is a large Windows-only slowdown, so
# exclude the build tree, the repo, and the temp directories. The
# runner is ephemeral, so narrowing scan coverage here costs nothing.
- name: Exclude build and temp paths from Defender (Windows)
if: runner.os == 'Windows'
shell: powershell
continue-on-error: true
run: |
Add-MpPreference -ExclusionPath "${{ runner.workspace }}", "$env:GITHUB_WORKSPACE", "$env:RUNNER_TEMP", "$env:TEMP"
179 changes: 3 additions & 176 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,182 +20,8 @@ jobs:
with:
python-version: 3.12

# Cache downloaded .debs (LLVM, SDL build deps, glfw) in a
# user-owned dir; the default archive dir is root-owned and the
# cache action could not restore into it. Stale entries are safe:
# apt re-downloads anything not matching the package index.
# The lock file and partial/ dir stay excluded: apt recreates them
# as root, and tar cannot read them back as the runner user.
- name: Cache apt archives (Ubuntu)
if: matrix.os == 'ubuntu-latest'
uses: actions/cache@v5
with:
path: |
/tmp/apt-archives
!/tmp/apt-archives/lock
!/tmp/apt-archives/partial
key: apt-archives-llvm-23-sdl-glfw

- name: Redirect apt archives (Ubuntu)
if: matrix.os == 'ubuntu-latest'
run: |
mkdir -p /tmp/apt-archives/partial
echo 'Dir::Cache::Archives "/tmp/apt-archives";' | sudo tee /etc/apt/apt.conf.d/99-ci-cache

- name: Download LLVM (Ubuntu)
if: matrix.os == 'ubuntu-latest'
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 23 all
echo "/usr/lib/llvm-23/bin" >> $GITHUB_PATH

# Bottle downloads are content-addressed, so a static key is safe.
# Only the fetch is cached; install/link still runs (cheap, and the
# full cellar layout is too version-sensitive to restore blindly).
- name: Cache Homebrew downloads (macOS)
if: matrix.os == 'macos-latest'
uses: actions/cache@v5
with:
path: ~/Library/Caches/Homebrew
key: homebrew-downloads-llvm-23-sdl3-glfw

- name: Download LLVM (macOS)
if: matrix.os == 'macos-latest'
run: |
# Refresh Homebrew first: a stale snapshot installs an older LLVM
# bottle (e.g. 23.1.0, whose libclangDriver.a ships an LLVM-bitcode
# object that the system linker can't parse).
brew update
brew install llvm@23
echo LLVMDIR=$(brew --prefix llvm@23) >> $GITHUB_ENV

# Cache the extracted LLVM tree: re-downloading and
# double-extracting the tarball costs minutes on every Windows leg.
# Bump the key when the URL below changes.
- name: Cache LLVM (Windows)
if: matrix.os == 'windows-latest'
uses: actions/cache@v5
with:
path: clang+llvm-23.1.1-x86_64-pc-windows-msvc
key: llvm-windows-23.1.1

- name: Download LLVM (Windows)
if: matrix.os == 'windows-latest'
shell: bash
run: |
# Reuse the cached tree only when it is complete; a cache entry
# saved from a failed extract must trigger a re-download, not a
# build against a corrupt tree.
if [ ! -f "clang+llvm-23.1.1-x86_64-pc-windows-msvc/bin/clang.exe" ]; then
rm -rf clang+llvm-23.1.1-x86_64-pc-windows-msvc
curl -L "https://github.com/llvm/llvm-project/releases/download/llvmorg-23.1.1/clang+llvm-23.1.1-x86_64-pc-windows-msvc.tar.xz" --output llvm.tar.xz && 7z x llvm.tar.xz && 7z x llvm.tar
fi
# The sed below converts the path to a Windows-style path
echo LLVMDIR="$(echo "$PWD/clang+llvm-23.1.1-x86_64-pc-windows-msvc" | sed -e 's/^\///' -e 's/\//\\/g' -e 's/^./\0:/')" >> $GITHUB_ENV

# LLVM 22+ locates the DIA SDK via $VSINSTALLDIR (see llvm/cmake/modules/FindDIASDK.cmake),
# which is only set inside a Visual Studio developer environment.
# vs-shell is composite (no Node runtime); ilammy/msvc-dev-cmd is abandoned on node20.
- name: Set up MSVC environment (Windows)
if: matrix.os == 'windows-latest'
uses: egor-tensin/vs-shell@v2

- name: Provide DIA SDK (Windows)
if: matrix.os == 'windows-latest'
shell: bash
run: |
# LLVM's LLVMDebugInfoPDB target needs diaguids.lib. Its FindDIASDK
# module only looks at $VSINSTALLDIR, which may point at an install
# without it, so search wider and pass an explicit path if needed.
DIA_CMAKE_DIR=""
if [ -f "$VSINSTALLDIR/DIA SDK/lib/amd64/diaguids.lib" ]; then
echo "DIA SDK found via VSINSTALLDIR"
else
VSWHERE="C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe"
if [ -f "$VSWHERE" ]; then
mapfile -t VS_INSTALLS < <("$VSWHERE" -products '*' -property installationPath)
for INSTALL_PATH in "${VS_INSTALLS[@]}"; do
INSTALL_PATH_UNIX=$(echo "$INSTALL_PATH" | tr -d '\r' | sed -e 's/\\/\//g')
if [ -f "$INSTALL_PATH_UNIX/DIA SDK/lib/amd64/diaguids.lib" ]; then
DIA_CMAKE_DIR="$INSTALL_PATH_UNIX/DIA SDK"
break
fi
done
fi
if [ -z "$DIA_CMAKE_DIR" ]; then
# Fall back to the DIA files shipped in the Windows SDK, staged
# into the layout FindDIASDK expects.
DIA_LIB=$(ls -d "/c/Program Files (x86)/Windows Kits/10/Lib/"*"/um/x64/diaguids.lib" 2>/dev/null | sort -r | head -n 1 || true)
DIA_INC=$(echo "$DIA_LIB" | sed -e 's#/Lib/\(.*\)/um/x64/diaguids.lib#/Include/\1/um/dia2.h#')
if [ -n "$DIA_LIB" ] && [ -f "$DIA_INC" ]; then
DIA_STAGE="$RUNNER_TEMP/dia-sdk"
mkdir -p "$DIA_STAGE/include" "$DIA_STAGE/lib/amd64"
cp "$DIA_LIB" "$DIA_STAGE/lib/amd64/diaguids.lib"
cp "$DIA_INC" "$DIA_STAGE/include/dia2.h"
DIA_CMAKE_DIR=$(echo "$DIA_STAGE" | sed -e 's/\\/\//g')
fi
fi
if [ -z "$DIA_CMAKE_DIR" ]; then
echo "ERROR: DIA SDK with lib/amd64/diaguids.lib not found in VSINSTALLDIR, any VS instance, or the Windows Kits"
exit 1
fi
echo "DIA SDK found at: $DIA_CMAKE_DIR"
echo "DIA_CMAKE_DIR=$DIA_CMAKE_DIR" >> $GITHUB_ENV
fi

# LLVM 22+ links LLVMSupport against the ZLIB::ZLIB and zstd::libzstd_static
# imported targets, so findable ZLIB and zstd are required at configure
# time. Use the static triplets to match the /MT runtime used with the
# prebuilt LLVM.
# Note: vcpkg names the static libs zs/zsd, which FindZLIB only
# searches for with ZLIB_USE_STATIC_LIBS=ON. Similarly, LLVM's Findzstd
# module only defines zstd::libzstd_static for a library literally named
# zstd_static.lib, which vcpkg doesn't ship (only zstd.lib), so copy it.
# The vcpkg baseline moves with the runner image, so resolve it to
# a commit SHA for the cache key. A static key would freeze on the
# first binaries saved (actions/cache never re-saves on hit) and
# rebuild from source forever after any update. Falls back to a
# static key if the install is not a git checkout.
- name: Get vcpkg baseline (Windows)
if: matrix.os == 'windows-latest'
id: vcpkg-baseline
shell: bash
run: echo "sha=$(git -C \"$VCPKG_INSTALLATION_ROOT\" rev-parse HEAD 2>/dev/null || echo unknown)" >> "$GITHUB_OUTPUT"

# vcpkg restores the built ports from this binary cache instead of
# compiling them; each entry carries an ABI hash, so stale hits are
# impossible.
- name: Cache vcpkg binaries (Windows)
if: matrix.os == 'windows-latest'
uses: actions/cache@v5
with:
path: ${{ runner.temp }}/vcpkg-binary-cache
key: vcpkg-${{ steps.vcpkg-baseline.outputs.sha }}-zlib-zstd-static

- name: Install zlib and zstd (Windows)
if: matrix.os == 'windows-latest'
shell: bash
env:
VCPKG_BINARY_SOURCES: "clear;files,${{ runner.temp }}/vcpkg-binary-cache,readwrite"
run: |
vcpkg install zlib:x64-windows-static zstd:x64-windows-static
cp "$VCPKG_INSTALLATION_ROOT/installed/x64-windows-static/lib/zstd.lib" "$VCPKG_INSTALLATION_ROOT/installed/x64-windows-static/lib/zstd_static.lib"
ls "$VCPKG_INSTALLATION_ROOT/installed/x64-windows-static/lib"
echo "EXTRA_CMAKE_PREFIX=$VCPKG_INSTALLATION_ROOT/installed/x64-windows-static" >> $GITHUB_ENV
echo "EXTRA_CMAKE_ARGS=-DZLIB_USE_STATIC_LIBS=ON" >> $GITHUB_ENV

# The lit/snippet/LSP suites spawn thousands of compiler and linker
# processes, each writing temporary objects and executables. Real-time
# Defender scanning of every one is a large Windows-only slowdown, so
# exclude the build tree, the repo, and the temp directories. The
# runner is ephemeral, so narrowing scan coverage here costs nothing.
- name: Exclude build and temp paths from Defender (Windows)
if: matrix.os == 'windows-latest'
shell: powershell
continue-on-error: true
run: |
Add-MpPreference -ExclusionPath "${{ runner.workspace }}", "$env:GITHUB_WORKSPACE", "$env:RUNNER_TEMP", "$env:TEMP"
- name: Set up LLVM 23 and link dependencies
uses: ./.github/actions/setup-llvm

- name: Create Build Environment
run: cmake -E make_directory ${{runner.workspace}}/build
Expand Down Expand Up @@ -654,6 +480,7 @@ jobs:
run: |
node website/test-bench.mjs
node website/test-cx-mode.mjs
node website/test-install.mjs
node wasm/test-editor.mjs
node wasm/test-playground.mjs
node wasm/test-playground-worker.mjs
Expand Down
Loading
Loading