Compare commits

...

38 Commits

Author SHA1 Message Date
Nicholas Dudfield
e633922226 Merge remote-tracking branch 'origin/dev' into add-clang-matrix 2025-10-14 16:44:37 +07:00
Nicholas Dudfield
1a3210f3ab chore: remove unused Conan patching action
- Conan 2 natively supports Clang 18, no patching needed
- Action was only required for Conan 1.x settings.yml modification
2025-10-13 10:09:11 +07:00
Nicholas Dudfield
44b9dcfaee Merge branch 'dev' into add-clang-matrix - migrate to Conan 2
- Upgrade from Conan v1 to Conan v2 (>=2.0,<3)
- Convert profile configuration to Conan 2 heredoc syntax
- Add libc++ support to Conan 2 profile configuration
- Remove Conan settings patching action (Conan 2 supports Clang 18 natively!)
- Keep full compiler matrix implementation and GCC hiding logic
- Incorporate dev changes (cfg renames, database updates, etc.)
2025-10-13 10:01:20 +07:00
Nicholas Dudfield
ecf0d68a64 fix: remove clang-14-gcc13 config - confirmed incompatible
- Testing confirmed Clang 14 cannot compile GCC 13 headers
- consteval usage in <chrono> causes compilation failure
- GCC 11 workaround is necessary for Clang 14
- Back to 6 working configurations
2025-08-20 08:22:47 +07:00
Nicholas Dudfield
775b6c14a0 test: add clang-14 with gcc-13 configuration
- Test if Clang 14 actually fails with GCC 13 headers
- This will validate if our GCC 11 workaround is necessary
- Total of 7 compiler configurations now

[ci-nix-full-matrix]
2025-08-20 08:10:51 +07:00
Nicholas Dudfield
7ea6f432c9 fix: add minimal safety improvements
- Use check=True for conan config init to catch failures immediately
- Add safety check to prevent clobbering when renaming GCC directories
- Document why Clang 16 is the cutoff for --gcc-install-dir support
2025-08-19 15:55:00 +07:00
Nicholas Dudfield
22a81a050f docs: improve comments about Conan initialization
- Explain that Conan 1 uses embedded defaults until config init
- Document that settings.yml is created on-demand
- Clarify that conan config init is safe (won't overwrite)
- Remove pr-description.md from repo (moved to .niqs-ignored)
2025-08-19 14:49:28 +07:00
Nicholas Dudfield
cd252504ad fix: use renamed action and add Conan init fallback
- Update workflow to use renamed xahau-patch-conan-post-setup action
- Use 'conan config home' to dynamically find settings.yml location
- Add debug logging to see Conan home contents
- Try 'conan config init' if settings.yml doesn't exist
- This should handle fresh Conan installations
2025-08-19 14:35:52 +07:00
Nicholas Dudfield
d8ccb1db81 refactor: move Conan patching to separate action
- Create new xahau-patch-conan-clang-versions action
- Remove patching logic from dependencies action
- Call patch action after Conan initialization in main workflow
- Fixes sequencing issue where settings.yml didn't exist yet
2025-08-19 14:30:16 +07:00
Nicholas Dudfield
27ec068050 fix: initialize Conan before patching settings.yml
- Check if settings.yml exists before trying to patch it
- Run 'conan profile new default --detect' if needed
- Fixes FileNotFoundError for fresh runners without cache
2025-08-19 14:20:09 +07:00
Nicholas Dudfield
2b6c92ecb1 debug: add logging to matrix generation 2025-08-19 14:15:49 +07:00
Nicholas Dudfield
e559008fc8 fix: use Python shell for Conan settings patch
- Switch to shell: python since PyYAML is pre-installed on Ubuntu
- Move export statement before Python heredoc so env var is available
- Adjust PR matrix logic: only release/candidate PRs get full matrix
- PRs to dev now use minimal matrix (most are feature branches)

[ci-nix-full-matrix]
2025-08-19 14:05:25 +07:00
Nicholas Dudfield
7ced4de6f2 feat: add clang-18 support with generic version detection
- Add clang-18-libcxx configuration (6 compiler configs total)
- Generic Conan settings patcher: auto-detects Clang version from compiler-id
- Works for any future Clang version without code changes
- Change minimal matrix to use clang-14 instead of clang-17 (faster)
- Sort Conan version list numerically when adding new versions

[ci-nix-full-matrix]
2025-08-19 13:54:46 +07:00
Nicholas Dudfield
3571a403f6 perf: use python:3-slim container for matrix-setup job
- Reduces total job time from ~26s to ~7s (72% faster)
- Bypasses ubuntu-latest's 25s toolchain setup overhead
- Container init only takes 3-5s with GitHub's layer caching
- Based on research showing containerized jobs skip host env config
2025-08-19 12:09:12 +07:00
Nicholas Dudfield
87dab64e0c perf: use self-hosted runner for matrix-setup job
- Reduces matrix generation time from ~30s to ~3s
- No VM provisioning overhead
- Perfect for tiny, safe, read-only jobs like JSON generation
2025-08-19 11:50:20 +07:00
Nicholas Dudfield
c730e1d5f0 fix: gcc stdlib flag, add dynamic matrix with [ci-nix-full-matrix]
- GCC doesn't recognize -stdlib flag (it's Clang-specific)
- Added dynamic matrix generation for branch-based CI control
- Main branches (dev/release/candidate) run all 5 compiler configs
- Feature branches and PRs run only gcc-13 and clang-17
- [ci-nix-full-matrix] tag forces full matrix on any branch
- Uses inline Python script in matrix-setup job
2025-08-19 11:38:14 +07:00
Nicholas Dudfield
9bf2bc0420 ci: add clang-17 with native libc++ to build matrix 2025-08-19 11:07:58 +07:00
Nicholas Dudfield
be1a34c7e9 ci: add gcc-11 and reinstate gcc-13 in build matrix 2025-08-19 11:06:19 +07:00
Nicholas Dudfield
06b4344cc0 fix: use -lt 16 check for gcc hiding, update compiler-id docs 2025-08-19 10:46:55 +07:00
Nicholas Dudfield
7a11eb6c15 docs: clarify gcc hiding trick is for clang < 16 2025-08-18 21:24:57 +07:00
Nicholas Dudfield
c1a35510f4 fix: install gcc before hiding versions, add verification 2025-08-18 21:19:27 +07:00
Nicholas Dudfield
7c4b1bafeb fix: install target gcc version before hiding others 2025-08-18 21:12:19 +07:00
Nicholas Dudfield
ccfa6da70a fix: use integer renaming for hidden gcc versions 2025-08-18 21:09:49 +07:00
Nicholas Dudfield
139f1bd32b ci: use directory hiding for clang-14 and --gcc-install-dir for clang-16+ 2025-08-18 21:05:58 +07:00
Nicholas Dudfield
a3b00d57a2 docs: clarify compiler_id includes gcc version for clang builds 2025-08-18 20:44:40 +07:00
Nicholas Dudfield
b822b66825 ci: simplify gcc toolchain logic - no update-alternatives needed 2025-08-18 20:34:49 +07:00
Nicholas Dudfield
6684075a28 ci: remove gcc-14-base to prevent header conflicts 2025-08-18 20:27:49 +07:00
Nicholas Dudfield
cc28fcf190 ci: remove gcc-14 and add clang-14 with gcc-11 test
- Add clang-14-libstdcxx-gcc11 matrix entry for testing
- Remove gcc-14 to prevent Clang from finding it
- Add diagnostic to show which headers Clang will use
- Test both gcc-11 and gcc-13 with clang-14
2025-08-18 20:19:26 +07:00
Nicholas Dudfield
ddca64815d ci: simplify by using update-alternatives instead of gcc-install-dir
- Keep clang_gcc_toolchain to control which GCC to install
- Use update-alternatives to set system default GCC
- Remove complex --gcc-install-dir flag from CMAKE_CXX_FLAGS
- Clang automatically uses the default GCC's headers
2025-08-18 20:15:30 +07:00
Nicholas Dudfield
60df462bc2 fix: properly quote CMAKE_CXX_FLAGS with spaces 2025-08-18 19:41:21 +07:00
Nicholas Dudfield
4755a37cad ci: add clang_gcc_toolchain support for header compatibility
- Clang 14 uses GCC 11 toolchain to avoid GCC 14 header issues
- Clang 16 uses GCC 13 toolchain for better compatibility
- Use --gcc-install-dir to specify exact GCC version
- Fixes consteval and ranges concept errors with newer libstdc++
2025-08-18 18:51:38 +07:00
Nicholas Dudfield
df652b457b ci: include stdlib in compiler_id for cache keys
- compiler_id now includes stdlib (e.g. clang-14-libstdcxx)
- Cache keys are self-documenting without redundant stdlib param
- Actions still receive stdlib for Conan/CMake configuration
- Added documentation explaining matrix key purposes
2025-08-18 18:37:20 +07:00
Nicholas Dudfield
0651332bb3 refactor: use clear stdlib naming instead of force_libstdcpp
- Rename force_libstdcpp to stdlib with values libstdcxx/libcxx
- Make stdlib a required enum field with validation
- Update cache keys to be self-documenting (e.g. clang-14-libstdcxx)
- Cleaner, more intuitive configuration flow
2025-08-18 18:21:42 +07:00
Nicholas Dudfield
5286bae753 ci: fix cache key to include stdlib choice 2025-08-18 18:12:11 +07:00
Nicholas Dudfield
4b9ef8db22 ci: move force_libstdcpp to matrix-level property 2025-08-18 18:07:39 +07:00
Nicholas Dudfield
360546d555 ci: add clang-14 to matrix, temporarily disable gcc 2025-08-18 18:04:31 +07:00
Nicholas Dudfield
4efcbd1eaa ci: add option to use libstdc++ with clang-16 2025-08-18 18:00:51 +07:00
Nicholas Dudfield
4c90598462 ci: add clang to build matrix 2025-08-18 16:58:09 +07:00
3 changed files with 264 additions and 15 deletions

View File

@@ -21,7 +21,7 @@ inputs:
required: false
default: ''
compiler-id:
description: 'Unique identifier for compiler/version combination used for cache keys'
description: 'Unique identifier: compiler-version-stdlib[-gccversion] (e.g. clang-14-libstdcxx-gcc11, gcc-13-libstdcxx)'
required: false
default: ''
cache_version:
@@ -36,6 +36,17 @@ inputs:
description: 'Main branch name for restore keys'
required: false
default: 'dev'
stdlib:
description: 'C++ standard library to use'
required: true
type: choice
options:
- libstdcxx
- libcxx
clang_gcc_toolchain:
description: 'GCC version to use for Clang toolchain (e.g. 11, 13)'
required: false
default: ''
runs:
using: 'composite'
@@ -93,6 +104,38 @@ runs:
CCACHE_ARGS="-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache"
fi
# Configure C++ standard library if specified
# libstdcxx used for clang-14/16 to work around missing lexicographical_compare_three_way in libc++
# libcxx can be used with clang-17+ which has full C++20 support
# Note: -stdlib flag is Clang-specific, GCC always uses libstdc++
CMAKE_CXX_FLAGS=""
if [[ "${{ inputs.cxx }}" == clang* ]]; then
# Only Clang needs the -stdlib flag
if [ "${{ inputs.stdlib }}" = "libstdcxx" ]; then
CMAKE_CXX_FLAGS="-stdlib=libstdc++"
elif [ "${{ inputs.stdlib }}" = "libcxx" ]; then
CMAKE_CXX_FLAGS="-stdlib=libc++"
fi
fi
# GCC always uses libstdc++ and doesn't need/support the -stdlib flag
# Configure GCC toolchain for Clang if specified
if [ -n "${{ inputs.clang_gcc_toolchain }}" ] && [[ "${{ inputs.cxx }}" == clang* ]]; then
# Extract Clang version from compiler executable name (e.g., clang++-14 -> 14)
clang_version=$(echo "${{ inputs.cxx }}" | grep -oE '[0-9]+$')
# Clang 16+ supports --gcc-install-dir (precise path specification)
# Clang <16 only has --gcc-toolchain (uses discovery heuristics)
if [ -n "$clang_version" ] && [ "$clang_version" -ge "16" ]; then
# Clang 16+ uses --gcc-install-dir (canonical, precise)
CMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS --gcc-install-dir=/usr/lib/gcc/x86_64-linux-gnu/${{ inputs.clang_gcc_toolchain }}"
else
# Clang 14-15 uses --gcc-toolchain (deprecated but necessary)
# Note: This still uses discovery, so we hide newer GCC versions in the workflow
CMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS --gcc-toolchain=/usr"
fi
fi
# Run CMake configure
# Note: conanfile.py hardcodes 'build/generators' as the output path.
# If we're in a 'build' folder, Conan detects this and uses just 'generators/'
@@ -101,6 +144,7 @@ runs:
cmake .. \
-G "${{ inputs.generator }}" \
$CCACHE_ARGS \
${CMAKE_CXX_FLAGS:+-DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS"} \
-DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \
-DCMAKE_BUILD_TYPE=${{ inputs.configuration }}

View File

@@ -10,7 +10,7 @@ inputs:
required: false
default: '.build'
compiler-id:
description: 'Unique identifier for compiler/version combination used for cache keys'
description: 'Unique identifier: compiler-version-stdlib[-gccversion] (e.g. clang-14-libstdcxx-gcc11, gcc-13-libstdcxx)'
required: false
default: ''
cache_version:
@@ -25,6 +25,13 @@ inputs:
description: 'Main branch name for restore keys'
required: false
default: 'dev'
stdlib:
description: 'C++ standard library for Conan configuration (note: also in compiler-id)'
required: true
type: choice
options:
- libstdcxx
- libcxx
outputs:
cache-hit:
@@ -70,6 +77,7 @@ runs:
path: |
~/.conan
~/.conan2
# Note: compiler-id format is compiler-version-stdlib[-gccversion]
key: ${{ runner.os }}-conan-v${{ inputs.cache_version }}-${{ inputs.compiler-id }}-${{ hashFiles('**/conanfile.txt', '**/conanfile.py') }}-${{ inputs.configuration }}
restore-keys: |
${{ runner.os }}-conan-v${{ inputs.cache_version }}-${{ inputs.compiler-id }}-${{ hashFiles('**/conanfile.txt', '**/conanfile.py') }}-

View File

@@ -13,21 +13,146 @@ concurrency:
cancel-in-progress: true
jobs:
build-job:
matrix-setup:
runs-on: ubuntu-latest
container: python:3-slim
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Generate build matrix
id: set-matrix
shell: python
run: |
import json
import os
# Full matrix with all 6 compiler configurations
# Each configuration includes all parameters needed by the build job
full_matrix = [
{
"compiler_id": "gcc-11-libstdcxx",
"compiler": "gcc",
"cc": "gcc-11",
"cxx": "g++-11",
"compiler_version": 11,
"stdlib": "libstdcxx",
"configuration": "Debug"
},
{
"compiler_id": "gcc-13-libstdcxx",
"compiler": "gcc",
"cc": "gcc-13",
"cxx": "g++-13",
"compiler_version": 13,
"stdlib": "libstdcxx",
"configuration": "Debug"
},
{
"compiler_id": "clang-14-libstdcxx-gcc11",
"compiler": "clang",
"cc": "clang-14",
"cxx": "clang++-14",
"compiler_version": 14,
"stdlib": "libstdcxx",
"clang_gcc_toolchain": 11,
"configuration": "Debug"
},
{
"compiler_id": "clang-16-libstdcxx-gcc13",
"compiler": "clang",
"cc": "clang-16",
"cxx": "clang++-16",
"compiler_version": 16,
"stdlib": "libstdcxx",
"clang_gcc_toolchain": 13,
"configuration": "Debug"
},
{
"compiler_id": "clang-17-libcxx",
"compiler": "clang",
"cc": "clang-17",
"cxx": "clang++-17",
"compiler_version": 17,
"stdlib": "libcxx",
"configuration": "Debug"
},
{
# Clang 18 - testing if it's faster than Clang 17 with libc++
# Requires patching Conan v1 settings.yml to add version 18
"compiler_id": "clang-18-libcxx",
"compiler": "clang",
"cc": "clang-18",
"cxx": "clang++-18",
"compiler_version": 18,
"stdlib": "libcxx",
"configuration": "Debug"
}
]
# Minimal matrix for PRs and feature branches
minimal_matrix = [
full_matrix[1], # gcc-13 (middle-ground gcc)
full_matrix[2] # clang-14 (mature, stable clang)
]
# Determine which matrix to use based on the target branch
ref = "${{ github.ref }}"
base_ref = "${{ github.base_ref }}" # For PRs, this is the target branch
event_name = "${{ github.event_name }}"
commit_message = """${{ github.event.head_commit.message }}"""
pr_title = """${{ github.event.pull_request.title }}"""
# Debug logging
print(f"Event: {event_name}")
print(f"Ref: {ref}")
print(f"Base ref: {base_ref}")
print(f"PR title: {pr_title}")
print(f"Commit message: {commit_message}")
# Check for override tags in commit message or PR title
force_full = "[ci-nix-full-matrix]" in commit_message or "[ci-nix-full-matrix]" in pr_title
print(f"Force full matrix: {force_full}")
# Check if this is targeting a main branch
# For PRs: check base_ref (target branch)
# For pushes: check ref (current branch)
main_branches = ["refs/heads/dev", "refs/heads/release", "refs/heads/candidate"]
if force_full:
# Override: always use full matrix if tag is present
use_full = True
elif event_name == "pull_request":
# For PRs, base_ref is just the branch name (e.g., "dev", not "refs/heads/dev")
# Check if the PR targets release or candidate (more critical branches)
use_full = base_ref in ["release", "candidate"]
else:
# For pushes, ref is the full reference (e.g., "refs/heads/dev")
use_full = ref in main_branches
# Select the appropriate matrix
if use_full:
if force_full:
print(f"Using FULL matrix (6 configs) - forced by [ci-nix-full-matrix] tag")
else:
print(f"Using FULL matrix (6 configs) - targeting main branch")
matrix = full_matrix
else:
print(f"Using MINIMAL matrix (2 configs) - feature branch/PR")
matrix = minimal_matrix
# Output the matrix as JSON
output = json.dumps({"include": matrix})
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
f.write(f"matrix={output}\n")
build:
needs: matrix-setup
runs-on: ubuntu-latest
outputs:
artifact_name: ${{ steps.set-artifact-name.outputs.artifact_name }}
strategy:
fail-fast: false
matrix:
compiler: [gcc]
configuration: [Debug]
include:
- compiler: gcc
cc: gcc-13
cxx: g++-13
compiler_id: gcc-13
compiler_version: 13
matrix: ${{ fromJSON(needs.matrix-setup.outputs.matrix) }}
env:
build_dir: .build
# Bump this number to invalidate all caches globally.
@@ -41,8 +166,70 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y ninja-build ${{ matrix.cc }} ${{ matrix.cxx }} ccache
# Install the specific GCC version needed for Clang
if [ -n "${{ matrix.clang_gcc_toolchain }}" ]; then
echo "=== Installing GCC ${{ matrix.clang_gcc_toolchain }} for Clang ==="
sudo apt-get install -y gcc-${{ matrix.clang_gcc_toolchain }} g++-${{ matrix.clang_gcc_toolchain }} libstdc++-${{ matrix.clang_gcc_toolchain }}-dev
echo "=== GCC versions available after installation ==="
ls -la /usr/lib/gcc/x86_64-linux-gnu/ | grep -E "^d"
fi
# For Clang < 16 with --gcc-toolchain, hide newer GCC versions
# This is needed because --gcc-toolchain still picks the highest version
#
# THE GREAT GCC HIDING TRICK (for Clang < 16):
# Clang versions before 16 don't have --gcc-install-dir, only --gcc-toolchain
# which is deprecated and still uses discovery heuristics that ALWAYS pick
# the highest version number. So we play a sneaky game...
#
# We rename newer GCC versions to very low integers (1, 2, 3...) which makes
# Clang think they're ancient GCC versions. Since 11 > 3 > 2 > 1, Clang will
# pick GCC 11 over our renamed versions. It's dumb but it works!
#
# Example: GCC 12→1, GCC 13→2, GCC 14→3, so Clang picks 11 (highest number)
if [ -n "${{ matrix.clang_gcc_toolchain }}" ] && [ "${{ matrix.compiler_version }}" -lt "16" ]; then
echo "=== Hiding GCC versions newer than ${{ matrix.clang_gcc_toolchain }} for Clang < 16 ==="
target_version=${{ matrix.clang_gcc_toolchain }}
counter=1 # Start with 1 - these will be seen as "GCC version 1, 2, 3" etc
for dir in /usr/lib/gcc/x86_64-linux-gnu/*/; do
if [ -d "$dir" ]; then
version=$(basename "$dir")
# Check if version is numeric and greater than target
if [[ "$version" =~ ^[0-9]+$ ]] && [ "$version" -gt "$target_version" ]; then
echo "Hiding GCC $version -> renaming to $counter (will be seen as GCC version $counter)"
# Safety check: ensure target doesn't already exist
if [ ! -e "/usr/lib/gcc/x86_64-linux-gnu/$counter" ]; then
sudo mv "$dir" "/usr/lib/gcc/x86_64-linux-gnu/$counter"
else
echo "ERROR: Cannot rename GCC $version - /usr/lib/gcc/x86_64-linux-gnu/$counter already exists"
exit 1
fi
counter=$((counter + 1))
fi
fi
done
fi
# Verify what Clang will use
if [ -n "${{ matrix.clang_gcc_toolchain }}" ]; then
echo "=== Verifying GCC toolchain selection ==="
echo "Available GCC versions:"
ls -la /usr/lib/gcc/x86_64-linux-gnu/ | grep -E "^d.*[0-9]+$" || true
echo ""
echo "Clang's detected GCC installation:"
${{ matrix.cxx }} -v -E -x c++ /dev/null -o /dev/null 2>&1 | grep "Found candidate GCC installation" || true
fi
# Install libc++ dev packages if using libc++ (not needed for libstdc++)
if [ "${{ matrix.stdlib }}" = "libcxx" ]; then
sudo apt-get install -y libc++-${{ matrix.compiler_version }}-dev libc++abi-${{ matrix.compiler_version }}-dev
fi
# Install Conan 2
pip install --upgrade "conan>=2.0"
pip install --upgrade "conan>=2.0,<3"
- name: Configure ccache
uses: ./.github/actions/xahau-configure-ccache
@@ -57,14 +244,21 @@ jobs:
# Create the default profile directory if it doesn't exist
mkdir -p ~/.conan2/profiles
# Determine the correct libcxx based on stdlib parameter
if [ "${{ matrix.stdlib }}" = "libcxx" ]; then
LIBCXX="libc++"
else
LIBCXX="libstdc++11"
fi
# Create profile with our specific settings
cat > ~/.conan2/profiles/default <<EOF
[settings]
arch=x86_64
build_type=Release
build_type=${{ matrix.configuration }}
compiler=${{ matrix.compiler }}
compiler.cppstd=20
compiler.libcxx=libstdc++11
compiler.libcxx=${LIBCXX}
compiler.version=${{ matrix.compiler_version }}
os=Linux
@@ -99,6 +293,7 @@ jobs:
compiler-id: ${{ matrix.compiler_id }}
cache_version: ${{ env.CACHE_VERSION }}
main_branch: ${{ env.MAIN_BRANCH_NAME }}
stdlib: ${{ matrix.stdlib }}
- name: Build
uses: ./.github/actions/xahau-ga-build
@@ -111,6 +306,8 @@ jobs:
compiler-id: ${{ matrix.compiler_id }}
cache_version: ${{ env.CACHE_VERSION }}
main_branch: ${{ env.MAIN_BRANCH_NAME }}
stdlib: ${{ matrix.stdlib }}
clang_gcc_toolchain: ${{ matrix.clang_gcc_toolchain || '' }}
- name: Set artifact name
id: set-artifact-name