Skip to content

Declare system LLVM as +python external instead of rebuilding it - #121

Open
TApplencourt wants to merge 11 commits into
mainfrom
improve_clang_binding_detection
Open

Declare system LLVM as +python external instead of rebuilding it#121
TApplencourt wants to merge 11 commits into
mainfrom
improve_clang_binding_detection

Conversation

@TApplencourt

Copy link
Copy Markdown
Collaborator

h2yaml needs llvm@18:+clang+python, where +python means the prefix ships clang's Python bindings (clang/cindex.py) next to libclang. spack external find picks variants from executables and never looks for the bindings, so it always records LLVM as ~python -- bindings present or not. Such an entry can never satisfy h2yaml, so a usable system LLVM goes unused and Spack builds one from source, by far the longest step of a THAPI install.

Add scripts/gen-llvm-external.py, which prints the packages.yaml config for a given LLVM prefix. If the bindings are already there it points the external at the prefix directly; otherwise it builds an overlay (symlinks to the real install, since site LLVMs are read-only) and supplies the bindings, either from where the distribution put them or by downloading them from the matching LLVM release -- they are pure Python, so nothing is compiled. It verifies clang/cindex.py is reachable before emitting, so a wrong +python claim fails during setup rather than mid-build. Stdlib only, no dependencies.

Use it from both the README and CI, replacing the sed that rewrote packages.yaml in place, and drop the separate symlink workaround it needed.

h2yaml: search for the bindings rather than assuming lib/python<spack's python>/site-packages. That path is named for whichever python LLVM was built against, which need not be the one Spack runs h2yaml with, and externals may use lib64 or Debian's dist-packages. Also stop re-joining an already-absolute libclang path onto prefix.lib, and raise a pointed error naming the searched prefix instead of failing later with a bare ModuleNotFoundError: clang.

cron.yml: keep the from-scratch install (the only job checking THAPI builds on a bare machine) and add a job exercising the fast path PR CI uses, which would otherwise go untested during a quiet week. It was also missing the env block, so SPACK_OPT expanded empty and failures produced no log.

h2yaml needs llvm@18:+clang+python, where +python means the prefix ships
clang's Python bindings (clang/cindex.py) next to libclang. `spack external
find` picks variants from executables and never looks for the bindings, so it
always records LLVM as ~python -- bindings present or not. Such an entry can
never satisfy h2yaml, so a usable system LLVM goes unused and Spack builds one
from source, by far the longest step of a THAPI install.

Add scripts/gen-llvm-external.py, which prints the packages.yaml config for a
given LLVM prefix. If the bindings are already there it points the external at
the prefix directly; otherwise it builds an overlay (symlinks to the real
install, since site LLVMs are read-only) and supplies the bindings, either from
where the distribution put them or by downloading them from the matching LLVM
release -- they are pure Python, so nothing is compiled. It verifies
clang/cindex.py is reachable before emitting, so a wrong +python claim fails
during setup rather than mid-build. Stdlib only, no dependencies.

The entry carries extra_attributes:compilers. llvm+clang provides c/cxx, so
Spack may select the external as the compiler; spack's own llvm package treats
that attribute as mandatory, and without it the build runs with SPACK_CC=None
and every compile fails with "C compiler cannot create executables".

Use it from both the README and CI, replacing the sed that rewrote
packages.yaml in place, and drop the separate symlink workaround it needed.

h2yaml: search for the bindings rather than assuming
lib/python<spack's python>/site-packages. That path is named for whichever
python LLVM was built against, which need not be the one Spack runs h2yaml
with, and externals may use lib64 or Debian's dist-packages. Also stop
re-joining an already-absolute libclang path onto prefix.lib, and raise a
pointed error naming the searched prefix instead of failing later with a bare
ModuleNotFoundError: clang.

cron.yml: keep the from-scratch install (the only job checking THAPI builds on
a bare machine) and add a job exercising the fast path PR CI uses, which would
otherwise go untested during a quiet week. It was also missing the env block,
so SPACK_OPT expanded empty and failures produced no log.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@TApplencourt
TApplencourt force-pushed the improve_clang_binding_detection branch from 0bbe6ac to 1f58414 Compare September 8, 2026 23:39
TApplencourt and others added 10 commits September 9, 2026 16:10
`spack external find` and `spack compiler find` both register the system
llvm without `+python`, shadowing the entry gen-llvm-external.py writes.
Exclude it from the former; `compiler find` has no such flag, so remove
what it added.

The mirror is created with `--autopush`, which uploads packages but never
writes an index, and spack skips an unindexed mirror during
concretization. Index it before upload so downstream jobs reuse it.

Move the cron fast-path check ahead of the install: it only needs
concretization, and running it afterwards means paying for the llvm build
it exists to catch.

upload/download-artifact v6->v7, delete-artifact v5->v6, ruff-action
v3->v4.1.0, all now on node24.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`spack spec -j` gives structured output, so version numbers and the
external marker come from jq rather than sed/awk over the rendered tree.
`.external.path` is null for a non-external node, which is what `jq -e`
needs to set its exit code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Only status lines start with the marker; it can appear anywhere inside
the json itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A spec can carry more than one llvm or python node. Selecting them
node-by-node yields a newline-separated list, which lands in the apt
sources line as "llvm-toolchain-noble-22\n22 main" and apt rejects the
file. Reduce to the distinct values and stop if more than one survives,
so an ambiguous spec is a clear error rather than a malformed repo entry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The runner pool mixes cpu generations. Spack defaults to
concretizer:targets:granularity:microarchitectures, which puts the
microarchitecture in the dag hash, so a package built on one runner does
not match the same package on another. Every consumer job was rebuilding
its whole dependency tree from source: the thapi@0.0.12 (legacy) log
reported "no binary available" for all of protobuf, abseil-cpp, elfutils,
babeltrace2 and lttng-tools, each of which the dependencies job had just
built and pushed.

Targeting x86_64_v3 keeps hashes stable across the pool. Binaries lose
cpu-specific tuning, which does not matter for a build test.

from_scratch installs without a build cache, so it keeps the default.

Also drop --concurrent-packages 2. It dates from the old installer, where
0 meant serial. The new installer, the default since v1.0, reads 0 as no
package limit and bounds concurrency with a jobserver sized to the core
count, so the flag only caps parallelism below what the hardware allows.
Both cost ~17 min in the dependency jobs and are already installed by
apt, but `spack external find` cannot see them: neither recipe declares
`executables` or `libraries`.

Add scripts/gen-external.py, which reads the version from the installed
tool and prints the packages.yaml entry, and use it for both. Same shape
as gen-llvm-external.py.

This pins PR CI to Noble's older protobuf; the weekly cron
`from_scratch` job installs with no externals and covers the current one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
line-length is 150, so the two wrapped calls fit on one line.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Same treatment as re2c and protobuf: ~6 min in the dependency jobs, apt
already ships it, and the recipe has no detectors so `spack external
find` cannot see it. Version comes from `eu-readelf --version`.

Unlike the other two this is a link dependency (of babeltrace2, and of
glib through the `elf` virtual), but `libelf.pc` lives in pkg-config's
default search path, so it does not need the PKG_CONFIG_PATH handling
that keeps libxml2 excluded from `spack external find`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant