Skip to content
Open
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
140 changes: 54 additions & 86 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,96 +1,55 @@
name: GitHub-hosted
name: CI

on:
push:
branches: [master, main]
pull_request:

env:
CMAKE_BUILD_PARALLEL_LEVEL: 4
permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
# PR-only: don't cancel an in-flight push validation on master.
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
build-and-test:
build-test:
name: ${{ matrix.os }}/llvm${{ matrix.llvm }}/py${{ matrix.py }}/c++${{ matrix.cxx }}${{ matrix.vg && '/vg' || '' }}${{ matrix.flavor == 'cling' && '/cling' || '' }}
strategy:
fail-fast: false
# Mirrors CppInterOp's cppyy PR cells: clang-repl on the same OS/arch/LLVM
# /valgrind combos at Python 3.14, plus a cling cell. Breadth (3.12/3.13,
# C++17, LLVM 20, arm) lives in nightly.
matrix:
include:
- os: ubuntu-24.04
llvm: 20
python: "3.12"
runtime_cxx_standard: 17
- os: ubuntu-24.04
llvm: 21
python: "3.13"
runtime_cxx_standard: 20
- os: ubuntu-24.04
llvm: 21
python: "3.14"
runtime_cxx_standard: 20
- os: macos-15
llvm: 20
python: "3.12"
runtime_cxx_standard: 20

runs-on: ${{ matrix.os }}
name: ${{ matrix.os }} / LLVM ${{ matrix.llvm }} / Py ${{ matrix.python }} / C++${{ matrix.runtime_cxx_standard }}

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}

- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: |
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/llvm-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/llvm-archive-keyring.gpg] http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-${{ matrix.llvm }} main" \
| sudo tee /etc/apt/sources.list.d/llvm.list
sudo apt-get update
sudo apt-get install -y \
llvm-${{ matrix.llvm }}-dev \
libclang-${{ matrix.llvm }}-dev \
clang-${{ matrix.llvm }} \
libpolly-${{ matrix.llvm }}-dev \
libboost-dev libeigen3-dev

- name: Install dependencies (macOS)
if: runner.os == 'macOS'
run: brew install llvm@${{ matrix.llvm }} boost eigen

- name: pip install CppJIT
run: |
pip install pytest numpy psutil
pip install numba || true
pip install . -v --config-settings=cmake.define.LLVM_DIR=${{
runner.os == 'macOS'
&& format('/opt/homebrew/opt/llvm@{0}/lib/cmake/llvm', matrix.llvm)
|| format('/usr/lib/llvm-{0}/lib/cmake/llvm', matrix.llvm)
}}

- name: Build test dictionaries
run: make -j4
working-directory: test

- name: Run tests
run: python -m pytest -ra --tb=short -q 2>&1 | tee ../test-output.txt
working-directory: test
env:
CPPINTEROP_EXTRA_INTERPRETER_ARGS: "-std=c++${{ matrix.runtime_cxx_standard }}"

- name: Upload result
if: always()
uses: actions/upload-artifact@v4
with:
name: result-${{ matrix.os }}-llvm${{ matrix.llvm }}-py${{ matrix.python }}-cpp${{ matrix.runtime_cxx_standard }}
path: test-output.txt
- { os: ubuntu-24.04, llvm: '21', flavor: system, py: '3.14', cxx: '20', vg: true }
- { os: ubuntu-24.04, llvm: '22', flavor: '', py: '3.14', cxx: '20' }
- { os: macos-26, llvm: '21', flavor: system, py: '3.14', cxx: '20' }
- { os: macos-26-intel, llvm: '21', flavor: system, py: '3.14', cxx: '20' }
# cling backend, against the cached llvm-root (cling-llvm20) recipe cell.
- { os: ubuntu-24.04, llvm: '20', flavor: cling, flavor_version: cling-llvm20, py: '3.14', cxx: '20' }
uses: compiler-research/ci-workflows/.github/workflows/cppjit.yml@main
with:
# CppInterOp is pinned in CppJIT's cmake to a commit compatible
# with the current forks.
cppjit-repo: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
cppjit-ref: ${{ github.event.pull_request.head.sha || github.sha }}
os: ${{ matrix.os }}
llvm-version: ${{ matrix.llvm }}
llvm-flavor: ${{ matrix.flavor }}
llvm-flavor-version: ${{ matrix.flavor_version || '' }}
python-version: ${{ matrix.py }}
cxx-standard: ${{ matrix.cxx }}
valgrind: ${{ matrix.vg || false }}
run-xfail-crashing-tests: true

report:
if: always() && github.event_name == 'pull_request'
needs: build-and-test
if: ${{ always() && github.event_name == 'pull_request' }}
needs: build-test
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/download-artifact@v4
Expand All @@ -105,20 +64,29 @@ jobs:

const rows = fs.readdirSync('.').filter(d => d.startsWith('result-')).sort().map(d => {
const cfg = d.replace('result-', '');
const file = `${d}/test-output.txt`;
const file = `${d}/pytest-summary.txt`;
const lines = fs.existsSync(file) ? fs.readFileSync(file, 'utf8').trim().split('\n') : [];
const result = lines.length ? lines.at(-1) : 'no output';
return `| ${cfg} | \`${result}\` |`;
});

const body = [marker, '## Test Results', '| Configuration | Result |', '|---|---|', ...rows].join('\n');
const { data: comments } = await github.rest.issues.listComments({
...context.repo, issue_number: context.issue.number,
});
const existing = comments.find(c => c.body.startsWith(marker));

if (existing) {
await github.rest.issues.updateComment({ ...context.repo, comment_id: existing.id, body });
} else {
await github.rest.issues.createComment({ ...context.repo, issue_number: context.issue.number, body });
// Run summary works even with a read-only token (fork PRs).
await core.summary.addRaw(body).write();

// PR comment needs pull-requests:write, downgraded to read-only on
// fork PRs — don't fail the job when the write errors out.
try {
const { data: comments } = await github.rest.issues.listComments({
...context.repo, issue_number: context.issue.number,
});
const existing = comments.find(c => c.body.startsWith(marker));
if (existing) {
await github.rest.issues.updateComment({ ...context.repo, comment_id: existing.id, body });
} else {
await github.rest.issues.createComment({ ...context.repo, issue_number: context.issue.number, body });
}
} catch (e) {
core.warning(`Could not post PR comment (likely a fork PR with a read-only token): ${e.message}`);
}
92 changes: 92 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: Nightly

# Exhaustive run on a schedule: the full matrix with the xfail sweeps and
# valgrind on every Linux cell, against the pinned CppInterOp (reusable
# default). On any failure a tracking issue is opened/updated. To track
# upstream drift, add a cell with `cppinterop-ref: main`; it builds CppJIT
# against CppInterOp's tip and goes red when that API drifts ahead of the
# synced forks -- the signal that the next fork re-sync is due.

on:
schedule:
- cron: '30 2 * * *'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: nightly-${{ github.workflow }}
cancel-in-progress: true

jobs:
build-test:
name: ${{ matrix.os }}/llvm${{ matrix.llvm }}/py${{ matrix.py }}/c++${{ matrix.cxx }}${{ matrix.vg && '/vg' || '' }}${{ matrix.flavor == 'cling' && '/cling' || '' }}
strategy:
fail-fast: false
matrix:
# Superset of the PR matrix + breadth (py 3.12/3.13, C++17, LLVM 20, arm)
# and one cling cell. valgrind only on LLVM 21 cells: vendored etc/ has
# clang21 supps only, so other majors would surface unsuppressed noise.
include:
- { os: ubuntu-24.04, llvm: '20', flavor: system, py: '3.12', cxx: '17' }
- { os: ubuntu-24.04, llvm: '21', flavor: system, py: '3.13', cxx: '20', vg: true }
- { os: ubuntu-24.04, llvm: '21', flavor: system, py: '3.14', cxx: '20', vg: true }
- { os: ubuntu-24.04, llvm: '22', flavor: '', py: '3.14', cxx: '20' }
- { os: ubuntu-24.04, llvm: '21', flavor: system, py: '3.12', cxx: '20', vg: true }
- { os: ubuntu-24.04-arm, llvm: '21', flavor: system, py: '3.13', cxx: '20', vg: true }
- { os: ubuntu-24.04-arm, llvm: '20', flavor: system, py: '3.12', cxx: '17' }
- { os: macos-26, llvm: '21', flavor: system, py: '3.12', cxx: '20' }
- { os: macos-26-intel, llvm: '21', flavor: system, py: '3.12', cxx: '20' }
# cling backend, against the cached llvm-root (cling-llvm20) recipe cell.
- { os: ubuntu-24.04, llvm: '20', flavor: cling, flavor_version: cling-llvm20, py: '3.13', cxx: '20' }
uses: compiler-research/ci-workflows/.github/workflows/cppjit.yml@main
with:
# For upstream-drift detection, set cppinterop-ref: main here.
os: ${{ matrix.os }}
llvm-version: ${{ matrix.llvm }}
llvm-flavor: ${{ matrix.flavor }}
llvm-flavor-version: ${{ matrix.flavor_version || '' }}
python-version: ${{ matrix.py }}
cxx-standard: ${{ matrix.cxx }}
valgrind: ${{ matrix.vg || false }}
run-xfail-crashing-tests: true

drift-issue:
needs: build-test
if: ${{ always() && needs.build-test.result == 'failure' }}
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- uses: actions/github-script@v7
with:
script: |
const marker = '<!-- cppjit-nightly-drift -->';
const run = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
const body = [
marker,
'## Nightly build/test failed',
'',
`The scheduled nightly run failed (CppJIT \`master\`, pinned CppInterOp).`,
`Investigate the failing cell; a persistent failure may mean a fork re-sync is due.`,
'',
`Run: ${run}`,
`Date: ${new Date().toISOString()}`,
].join('\n');

const existing = await github.paginate(github.rest.issues.listForRepo, {
...context.repo, state: 'open', labels: 'nightly-drift',
});
const hit = existing.find(i => (i.body || '').startsWith(marker));
if (hit) {
await github.rest.issues.createComment({ ...context.repo, issue_number: hit.number, body });
} else {
await github.rest.issues.create({
...context.repo,
title: 'Nightly: CppJIT build/test failing',
labels: ['nightly-drift'],
body,
});
}
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ include(GNUInstallDirs)
# This option won't make a lot of sense since we only ship the shared library in site-packages
# Perhaps this should permanently be OFF and users can build their own CppInterOp if they want to run the tests?
option(CPPJIT_ENABLE_CPPINTEROP_TESTS "enable CppInterOp tests" OFF)
set(CPPINTEROP_GIT_TAG "a438c13316a8c1917cb86e00d5b0d95e6aee75f5" CACHE STRING "")
# TEMPORARY (macOS sysroot CI test): fetch CppInterOp from the fork branch carrying the
# -isysroot SDK detection fix, so cppjit CI (whose setup-llvm does not export SDKROOT)
# actually exercises it. Revert to compiler-research + a pinned SHA once the CppInterOp
# PR merges. Tip at time of writing: f68231a.
set(CPPINTEROP_GIT_REPOSITORY "https://github.com/aaronj0/CppInterOp.git" CACHE STRING "")
set(CPPINTEROP_GIT_TAG "osx-sysroot-fix" CACHE STRING "")

set(Python_FIND_VIRTUALENV ONLY)
find_package(Python COMPONENTS Interpreter Development)
Expand Down
2 changes: 1 addition & 1 deletion cmake/AddCppInterOp.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function(cppjit_add_cppinterop)
endif()

ExternalProject_Add(CppInterOp
GIT_REPOSITORY https://github.com/compiler-research/CppInterOp.git
GIT_REPOSITORY ${CPPINTEROP_GIT_REPOSITORY}
GIT_TAG ${CPPINTEROP_GIT_TAG}
PREFIX "${CMAKE_BINARY_DIR}/CppInterOp"
CMAKE_ARGS ${_args}
Expand Down
17 changes: 17 additions & 0 deletions etc/clang21-valgrind.supp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
ignore_free_delete_mismatches
Memcheck:Free
fun:free
obj:*
fun:_ZNK7CppImpl7JitCall6InvokeEPvNS0_7ArgListES1_
fun:_ZL11WrapperCallPvmS_S_S_
fun:_ZN5Cppyy5CallVEPvS0_mS0_
fun:_ZL8GILCallVPvS_PN8CPyCppyy11CallContextE
fun:_ZN8CPyCppyy12_GLOBAL__N_112VoidExecutor7ExecuteEPvS2_PNS_11CallContextE
fun:_ZN8CPyCppyy9CPPMethod11ExecuteFastEPvlPNS_11CallContextE
fun:_ZN8CPyCppyy9CPPMethod7ExecuteEPvlPNS_11CallContextE
fun:_ZN8CPyCppyy11CPPFunction4CallERPNS_11CPPInstanceEPKP7_objectmS5_PNS_11CallContextE
fun:_ZN8CPyCppyy12_GLOBAL__N_1L13mp_vectorcallEPNS_11CPPOverloadEPKP7_objectmS4_
fun:_PyObject_VectorcallTstate
fun:PyObject_Vectorcall
}
6 changes: 6 additions & 0 deletions etc/clang21-valgrind_arm.supp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
ignore_free_delete_mismatches
Memcheck:Free
fun:free
obj:*
}
65 changes: 65 additions & 0 deletions etc/cppinterop-clang21-valgrind.supp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Valgrind suppressions for clang-21 builds.
#
# Tracks llvm/llvm-project#194147 ("clang-21+ reads uninitialised
# bytes in many AST/Sema paths"). The same regression class as
# clang-22; the per-major file lets each binary's specific report
# set drift without entries from one major silencing the other's
# coverage.
#
# `flavor: system` consumers (CppInterOp's llvm-21 vg row) get this
# file applied automatically by Build_and_Test_CppInterOp's loader,
# which loads `etc/clang${CLANG_VERSION}-valgrind.supp` whenever it
# exists. apt-llvm.org's clang-21 binary is what trips the reports
# below.
#
# What's *actually* in here is what fired on a real local valgrind
# run (validated via `bin/repro --save-temps` + targeted
# `--gtest_filter` re-runs against the saved container, watching the
# `used_suppression` summary). Earlier iterations carried ~17 more
# blocks targeting symbols I'd extracted from "Uninitialised value
# was created by" allocation-origin frames; valgrind's matcher only
# considers the read-site top frame, so every one of those blocks
# was dead. Removed.
#
# Drop this whole file once #194147 lands upstream and apt-llvm.org's
# clang-21 packages refresh.

# clang::FunctionProtoType::FunctionProtoType(...) is the read-site
# top frame for the bulk of #194147 reports on this binary
# (54k+ errors over 51 contexts in our wider test sample).
#
# Three patterns considered, only one fires:
# *FunctionProtoType::FunctionProtoType* -- demangled, didn't match
# _ZN5clang17FunctionProtoTypeC* -- mangled, didn't match
# *FunctionProtoType* -- single substring, MATCHES
#
# The single-substring form over-matches by design: every method on
# the FunctionProtoType class (getCallConv, getReturnType, etc.)
# would also be silenced if it ever surfaces a real bug. Tolerated
# because (a) the constructor is the only top frame seen today, (b)
# the more specific patterns demonstrably don't match valgrind's
# matcher despite reading correctly to a human, (c) dropping the
# whole file when #194147 lands restores precision. Documented so
# the next maintainer doesn't waste time on the same investigation.
{
llvm/llvm-project#194147: FunctionProtoType (any method, see comment)
Memcheck:Cond
fun:*FunctionProtoType*
...
}

# Two more #194147 patterns that fire on lower-frequency code paths
# (~14 errors each on the sampled tests). Wildcards on two distinct
# identifiers, which matches valgrind's matcher reliably.
{
llvm/llvm-project#194147: CodeGenFunction::EmitCall
Memcheck:Cond
fun:*CodeGenFunction*EmitCall*
...
}
{
llvm/llvm-project#194147: EHScopeStack::requiresLandingPad
Memcheck:Cond
fun:*EHScopeStack*requiresLandingPad*
...
}
Loading
Loading