Skip to content

Commit 8507a62

Browse files
vgvassilevclaude
andcommitted
[ci] Build ROOT against the CppInterOp PR.
ROOT (root-project/root) carries an in-tree copy of CppInterOp under `interpreter/CppInterOp` that it builds and links Cling against. Breaking changes that diverge from what ROOT relies on currently surface only after merge, when the next CppInterOp bump through ROOT's vendored copy lands and the ROOT CI breaks. Add a PR-time integration job that overrides ROOT's bundled CppInterOp with the PR's tree and builds ROOT minimally, so the breakage is visible on the PR that introduces it. The job's matrix row pins the same axes as `ubu24-x86-gcc14-cling-llvm20-cppyy` in main.yml: same OS, same host compiler, same clang-runtime, same `root-llvm-tag`. The cache key expression is copied verbatim from main.yml's restore step, so on a normal run the job free-rides the LLVM/Cling artifact the cppyy cling row populated and skips the ~30 minute LLVM build. On a cache miss it falls through to the existing Build_LLVM action; a workspace-root symlink keeps the patches at the path Build_LLVM and hashFiles both expect. ROOT system dependencies (LibLZMA, libssl, X11 headers, etc.) follow the documented Ubuntu set from https://root.cern/install/build_from_source/. The ubuntu-24.04 runner image also ships `llvm-17-dev`, which would otherwise win ROOT's `find_package(LLVM)` -- `interpreter/CMakeLists.txt:63` clobbers any incoming `-DLLVM_DIR` hint with an empty path inside the build tree, so resolution falls back to standard search and `/usr/lib/llvm-17` ranks above CMAKE_PREFIX_PATH. We purge the llvm-17 packages and pass `-DCMAKE_PREFIX_PATH=$GITHUB_WORKSPACE/llvm-project/build` so the fallback resolves to our cached LLVM 20. ROOT itself is built with `-Dminimal=ON -Dtesting=OFF -Dfail-on-missing=ON` (the latter mirrors the nixpkgs ROOT recipe -- a silently disabled feature becomes a configure-time error). Cling is rebuilt from ROOT's bundled `interpreter/cling` sources; those sources are what ROOT's own version of CppInterOp is wired against, so letting ROOT do that rebuild keeps the integration honest. Triggers are `pull_request` and `workflow_dispatch`; no schedule or push, no `continue-on-error` -- a failure here is the desired signal. The `pull_request:` triggers on the other workflows (CI, WASM, Build and Deploy, clang-format, clang-tidy-review, Markdown linter) are commented out on this branch so the ROOT integration job has the runner pool to itself while we iterate. Each comment carries an `XXX(ci-root-integration)` marker so the trigger lines get restored before this branch is merged. Two ergonomic fixes ride along. `Install_Dependencies` now passes `-y` to every apt-get call so it works on catthehacker/nektos-act images that lack the GHA `Assume-Yes` config. The matrix value `cling: 'On'` is quoted so YAML 1.1 parsers (notably act) read it as the string "On" rather than boolean true, keeping `Save_PR_Info` on the same code path as real CI when run locally. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 1deecd0 commit 8507a62

7 files changed

Lines changed: 254 additions & 23 deletions

File tree

.github/actions/Miscellaneous/Install_Dependencies/action.yml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,17 @@ runs:
3030
if: runner.os == 'Linux' && runner.environment != 'self-hosted'
3131
shell: bash
3232
run: |
33-
# Install deps
33+
# Install deps. -y is required on environments without an
34+
# `Assume-Yes` apt config (e.g. nektos/act runners).
3435
sudo apt-get update
35-
sudo apt-get install valgrind ninja-build
36-
sudo apt-get install git g++ debhelper devscripts gnupg python3 doxygen graphviz python3-sphinx
36+
sudo apt-get install -y valgrind ninja-build
37+
sudo apt-get install -y git g++ debhelper devscripts gnupg python3 doxygen graphviz python3-sphinx
3738
sudo apt-get install -y libc6-dbg
38-
sudo apt autoremove
39-
sudo apt clean
39+
sudo apt-get autoremove -y
40+
sudo apt-get clean
4041
# Install libraries used by the cppyy test suite
41-
sudo apt install libeigen3-dev
42-
sudo apt install libboost-all-dev
42+
sudo apt-get install -y libeigen3-dev
43+
sudo apt-get install -y libboost-all-dev
4344
4445
4546
- name: Install dependencies on Windows

.github/workflows/clang-format.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
name: clang-format
22

33
on:
4-
pull_request:
5-
paths:
6-
- '**.h'
7-
- '**.cpp'
4+
# XXX(ci-root-integration): disabled on this branch; restore before landing.
5+
# pull_request:
6+
# paths:
7+
# - '**.h'
8+
# - '**.cpp'
9+
workflow_dispatch:
810

911
concurrency:
1012
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}

.github/workflows/clang-tidy-review.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
name: clang-tidy-review
22

33
on:
4-
pull_request:
5-
paths:
6-
- '**.h'
7-
- '**.cpp'
4+
# XXX(ci-root-integration): disabled on this branch; restore before landing.
5+
# pull_request:
6+
# paths:
7+
# - '**.h'
8+
# - '**.cpp'
9+
workflow_dispatch:
810

911
concurrency:
1012
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}

.github/workflows/emscripten.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
name: WASM
22
on:
3-
pull_request:
4-
branches: [main]
3+
# XXX(ci-root-integration): pull_request disabled on this branch so the
4+
# ROOT integration workflow gets the runner pool to itself while we
5+
# iterate. Restore before landing.
6+
# pull_request:
7+
# branches: [main]
58
push:
69
branches: [main]
710
release:

.github/workflows/main.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
name: CI
22

33
on:
4-
pull_request:
5-
branches: [main]
4+
# XXX(ci-root-integration): pull_request disabled on this branch so the
5+
# ROOT integration workflow gets the runner pool to itself while we
6+
# iterate. Restore before landing.
7+
# pull_request:
8+
# branches: [main]
69
push:
710
branches: [main]
811
release:

.github/workflows/markdown-linter.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
name: Markdown-Linter
33

44
on:
5-
pull_request:
6-
branches: [main]
7-
paths:
8-
- '**.md'
5+
# XXX(ci-root-integration): disabled on this branch; restore before landing.
6+
# pull_request:
7+
# branches: [main]
8+
# paths:
9+
# - '**.md'
10+
workflow_dispatch:
911

1012
concurrency:
1113
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}

.github/workflows/root.yml

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
name: ROOT integration
2+
3+
# Build ROOT (root-project/root master) against the CppInterOp tree
4+
# carried by the PR, by overwriting `interpreter/CppInterOp` in ROOT
5+
# with our checkout. Any breaking change CppInterOp introduces that
6+
# ROOT relies on shows up here as a configure or compile error.
7+
#
8+
# The job pins to the same axes as `main.yml`'s
9+
# ubu24-x86-gcc14-cling-llvm20-cppyy row so the LLVM/Cling cache key
10+
# resolves to the same string and the artifact is shared.
11+
12+
on:
13+
pull_request:
14+
branches: [main]
15+
workflow_dispatch:
16+
17+
concurrency:
18+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
19+
cancel-in-progress: true
20+
21+
jobs:
22+
build-root:
23+
name: ${{ matrix.name }}
24+
runs-on: ${{ matrix.os }}
25+
env:
26+
# Match main.yml's job-level CLING_VERSION so Save_PR_Info computes
27+
# CLING_HASH against the same git tag (refs/tags/v1.3) and the cache
28+
# key prefix matches.
29+
CLING_VERSION: '1.3'
30+
strategy:
31+
fail-fast: false
32+
matrix:
33+
# Mirror main.yml's `ubu24-x86-gcc14-cling-llvm20-cppyy` row's
34+
# cache-key inputs: same OS, compiler, clang-runtime,
35+
# root-llvm-tag. cppyy is set Off to keep the row's identity
36+
# distinct in CI logs while leaving the cache key bit-identical
37+
# to the cppyy variant (cppyy isn't in the cache key).
38+
include:
39+
# cling is quoted so YAML parsers that read `On` as a boolean
40+
# (e.g. nektos/act) still produce the string "On" that
41+
# Save_PR_Info compares against; without the quotes act takes
42+
# the else-branch and computes CLING_HASH=Repl, breaking the
43+
# cache-key alignment with main.yml's cling row.
44+
- { name: ubu24-x86-gcc14-cling-llvm20-root, os: ubuntu-24.04, compiler: gcc-14, clang-runtime: '20', cling: 'On', root-llvm-tag: 'cling-llvm20-20260119-01' }
45+
46+
steps:
47+
- name: Checkout CppInterOp PR
48+
uses: actions/checkout@v6
49+
with:
50+
path: cppinterop
51+
52+
- name: Stage CppInterOp's patches at workspace root
53+
shell: bash
54+
run: |
55+
# main.yml's cache key uses `hashFiles('patches/llvm/clang{0}-*.patch')`,
56+
# which resolves relative to the workspace root. With the PR checked
57+
# out into `cppinterop/`, the symlink lets `hashFiles` find the same
58+
# files at the same path, keeping the cache key bit-identical to
59+
# the cling row in main.yml. Build_LLVM's cling-side `git apply
60+
# ../patches/llvm/cling1.2-LookupHelper.patch` also resolves through
61+
# the symlink on cache-miss runs.
62+
ln -s cppinterop/patches patches
63+
64+
- name: Set up Python
65+
uses: actions/setup-python@v6
66+
with:
67+
python-version: '3.14'
68+
69+
- name: Save PR Info
70+
uses: ./cppinterop/.github/actions/Miscellaneous/Save_PR_Info
71+
72+
- name: Restore cached LLVM-${{ matrix.clang-runtime }} and Cling build
73+
uses: actions/cache/restore@v4
74+
id: cache
75+
with:
76+
path: |
77+
llvm-project
78+
cling
79+
# Verbatim copy of main.yml's cache key so this job hits the cache
80+
# the cppyy cling row populates.
81+
key: ${{ env.CLING_HASH }}-${{ runner.os }}-${{ matrix.os }}-${{ matrix.compiler }}-clang-${{ matrix.clang-runtime }}-${{ matrix.root-llvm-tag || 'none' }}.x-patch-${{ hashFiles(format('patches/llvm/clang{0}-*.patch', matrix.clang-runtime)) || 'none' }}${{ matrix.oop-jit == 'On' && '-oop' || '' }}${{ matrix.sanitizer || '' }}
82+
83+
- name: Setup default Build Type
84+
uses: ./cppinterop/.github/actions/Miscellaneous/Select_Default_Build_Type
85+
86+
- name: Setup compiler
87+
uses: ./cppinterop/.github/actions/Miscellaneous/Setup_Compiler
88+
89+
- name: Install dependencies
90+
uses: ./cppinterop/.github/actions/Miscellaneous/Install_Dependencies
91+
92+
- name: Build LLVM-${{ matrix.clang-runtime }} and Cling on cache miss
93+
uses: ./cppinterop/.github/actions/Build_LLVM
94+
with:
95+
cache-hit: ${{ steps.cache.outputs.cache-hit }}
96+
97+
- name: Cache LLVM-${{ matrix.clang-runtime }} and Cling build
98+
uses: actions/cache/save@v4
99+
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
100+
with:
101+
path: |
102+
llvm-project
103+
cling
104+
key: ${{ steps.cache.outputs.cache-primary-key }}
105+
106+
- name: Verify cached LLVM/Clang layout
107+
shell: bash
108+
run: |
109+
# Sanity-check the artifact ROOT will consume. Whether we hit the
110+
# cache or rebuilt via Build_LLVM, both `LLVMConfig.cmake` and
111+
# `ClangConfig.cmake` must be in place at the expected build-tree
112+
# locations or the ROOT configure step will silently fall back to
113+
# system paths (which is how we previously got `Found LLVM 17`).
114+
# Also assert that `lib/clang/<version>` exists -- ROOT's
115+
# `core/clingutils/CMakeLists.txt:95` globs that directory and
116+
# expects exactly one entry.
117+
BUILD="$GITHUB_WORKSPACE/llvm-project/build"
118+
fail=0
119+
for f in \
120+
"$BUILD/lib/cmake/llvm/LLVMConfig.cmake" \
121+
"$BUILD/lib/cmake/clang/ClangConfig.cmake"; do
122+
if [[ ! -f "$f" ]]; then
123+
echo "::error::missing $f"
124+
fail=1
125+
else
126+
echo "ok: $f"
127+
fi
128+
done
129+
if [[ ! -d "$BUILD/lib/clang" ]]; then
130+
echo "::error::missing $BUILD/lib/clang"
131+
fail=1
132+
else
133+
versions=$(ls "$BUILD/lib/clang" 2>/dev/null | tr '\n' ' ')
134+
echo "lib/clang/ entries: ${versions:-<empty>}"
135+
# ROOT requires exactly one entry in this directory.
136+
count=$(ls "$BUILD/lib/clang" 2>/dev/null | wc -l)
137+
if [[ "$count" -ne 1 ]]; then
138+
echo "::error::expected exactly 1 entry under $BUILD/lib/clang, found $count"
139+
fail=1
140+
fi
141+
fi
142+
exit $fail
143+
144+
- name: Install ROOT system dependencies
145+
shell: bash
146+
run: |
147+
# Per https://root.cern/install/build_from_source/. Even with
148+
# `-Dminimal=ON`, ROOT still requires LibLZMA, libssl, X11 dev
149+
# headers, etc. Install the documented Ubuntu set rather than
150+
# opting in to ROOT's `-Dbuiltin_*` fallbacks (slower build).
151+
sudo apt-get update
152+
sudo apt-get install -y --no-install-recommends \
153+
dpkg-dev binutils \
154+
libx11-dev libxpm-dev libxft-dev libxext-dev \
155+
libssl-dev liblzma-dev libpcre3-dev libpcre2-dev \
156+
libxxhash-dev libzstd-dev libtbb-dev libxml2-dev \
157+
nlohmann-json3-dev
158+
# The ubuntu-24.04 runner image ships llvm-17 dev packages,
159+
# whose `/usr/lib/llvm-17/lib/cmake/llvm` is found by ROOT's
160+
# `find_package(LLVM)` before our `-DLLVM_DIR` hint takes
161+
# effect (ROOT's interpreter subtree re-invokes find_package
162+
# in nested scopes). Purge them so only our cached LLVM 20
163+
# remains visible.
164+
sudo apt-get purge -y 'llvm-17*' 'clang-17*' 'libclang-17*' 'libllvm17*' || true
165+
sudo apt-get autoremove -y
166+
167+
- name: Substitute the PR's CppInterOp into ROOT
168+
shell: bash
169+
run: |
170+
git clone --depth=1 --branch master https://github.com/root-project/root.git
171+
# ROOT bundles a copy of CppInterOp under interpreter/CppInterOp.
172+
# Replace it with the PR's tree so the integration build exercises
173+
# this PR's headers, sources, and CMake glue.
174+
rm -rf root/interpreter/CppInterOp
175+
cp -a cppinterop root/interpreter/CppInterOp
176+
# Strip the nested `.git` so ROOT's CMake doesn't get confused by
177+
# a sub-repository inside its own source tree.
178+
rm -rf root/interpreter/CppInterOp/.git
179+
180+
- name: Configure ROOT
181+
shell: bash
182+
run: |
183+
mkdir root-build
184+
cd root-build
185+
# ROOT uses our cached external LLVM and Clang (builtin_llvm /
186+
# builtin_clang off) but rebuilds cling from its own bundled
187+
# sources under interpreter/cling -- the bundled sources are
188+
# what ROOT's own version of CppInterOp is wired against, so
189+
# letting ROOT do that rebuild keeps the integration honest.
190+
# `-DLLVM_DIR` would be wiped by `interpreter/CMakeLists.txt:63`
191+
# (`set(LLVM_DIR "${CMAKE_CURRENT_BINARY_DIR}/llvm-project/llvm")`)
192+
# before ROOT's `find_package(LLVM REQUIRED CONFIG)` runs, so we
193+
# don't pass it. Resolution happens via CMAKE_PREFIX_PATH instead.
194+
# `fail-on-missing=ON` mirrors the nixpkgs ROOT recipe so any
195+
# silently-disabled feature surfaces as a configure-time error.
196+
# `-DCLANG_INSTALL_PREFIX` is required because our cached LLVM
197+
# ships its build-tree `ClangConfig.cmake`, whose @CLANG_CONFIG_CODE@
198+
# substitution is empty (only the install-tree variant emits the
199+
# `find_prefix_from_config` snippet). Without this, ROOT's
200+
# `core/clingutils/CMakeLists.txt:87` resolves
201+
# `${CLANG_INSTALL_PREFIX}/lib/clang` to `/lib/clang` and globs the
202+
# runner's residual system clangs at `/usr/lib/clang/*`.
203+
cmake -G Ninja \
204+
-DCMAKE_BUILD_TYPE=Release \
205+
-Dminimal=ON \
206+
-Dtesting=OFF \
207+
-Dfail-on-missing=ON \
208+
-Dbuiltin_llvm=OFF \
209+
-Dbuiltin_clang=OFF \
210+
-DCMAKE_PREFIX_PATH="$GITHUB_WORKSPACE/llvm-project/build" \
211+
-DClang_DIR="$GITHUB_WORKSPACE/llvm-project/build/lib/cmake/clang" \
212+
-DCLANG_INSTALL_PREFIX="$GITHUB_WORKSPACE/llvm-project/build" \
213+
../root
214+
215+
- name: Build ROOT
216+
shell: bash
217+
run: |
218+
cmake --build root-build --parallel ${{ env.ncpus }}

0 commit comments

Comments
 (0)