Verify Clang against the older libstdc++ releases in the declared range - #24
Merged
Merged
Conversation
The required jobs built Clang only against the libstdc++ that ships with the runner image, so 13 and 14 went unverified even though the declared range promises them. Which libstdc++ Clang uses is an axis of its own -- it does not follow from the Clang version the way it does for GCC -- so pin those two releases in a job of their own, leaving the default jobs to build without a flag or a package step. A green build is not evidence for the release a job claims, so the include search path is read back and its first standard library entry compared against the pin. Settle the contract documents accordingly: state the C++ standard, the compiler and the standard library as three axes, list the pairings that hold instead of a product of two axes, and record that GCC with libc++ is not among them because upstream has no -stdlib option to select it with. Coverage is now stated per quadrant: every libstdc++ release in range is covered in the Clang pairings, while the GCC ones cover 13 and 15 only.
The check added with the pinned jobs dies without printing anything when the driver refuses the directory it was given: the assignment fails under set -e with the driver's output already captured, so a run keeps only its exit code. Report it instead. The awk there also took the first field of each search path entry, which turns a path containing a space into a wrong comparison rather than an error. The default jobs name no standard library at all, and Clang picks the newest GCC installation it finds rather than the distribution's alias, so nothing in a run recorded which release that arm built against even though the documents name it. Report the selection there too, without asserting on it -- those jobs exist to build against whatever the image provides, so a change of release is news rather than a failure. The documents now also say that the version in each pairing is the one whose headers are compiled against, the runtime a binary loads being versioned separately, and the configure step names the shared linker variable rather than resting on CMake carrying the flag there on its own.
The observation line added with the pinned jobs filtered the driver's output through grep, so a driver that failed left nothing behind: its diagnostics went into the pipe and were dropped, and the fallback text claimed the driver had reported no selection -- a statement about what the driver said, made in the one case where nothing it said survived. That is the same disappearance this branch had just removed from the pinned check, reintroduced one step away. Capture first, then read the capture. Whenever the marker cannot be reported, whether because the driver failed or because it stopped printing that line, the captured output reaches the log instead of being dropped. Reading the capture with a here-string rather than a pipe also removes the step's dependence on pipefail being unset, which is what the current behaviour rests on. Two comments elsewhere claimed more than the code does. The configure step called its linker variables the same shape the libc++ jobs use, though those name no shared-linker variable. The pin check said no other entry is examined, when what it means is that the entries are walked in order and only the first one under the standard library prefix is compared.
One of them said this workflow does not set pipefail, while the pinned check sets it eighty lines further down. The claim holds for the step, whose default shell is the one that has no pipefail, and not for the file; shell options do not carry between steps, so narrowing the scope keeps the reasoning and drops the part a reader would find contradicted. The other explained why the libc++ jobs name their linker variable. That is an attribution those jobs do not make anywhere; their comments cover why both ends are built and where the packages come from. What can be read off them is which variables they set, so the comment now says that and stops there.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The required Linux jobs built Clang only against the libstdc++ that ships with
the runner image, so the two older releases the declared range promises — 13
and 14 — went unverified. This adds jobs that pin them, and settles the contract
documents around what the matrix actually covers.
Why this axis needs jobs of its own
Which libstdc++ Clang uses does not follow from the Clang version the way it
does for GCC: the two come from different upstreams, and neither of them tests
the pairing. Both breakages this project has hit on that axis —
std::expectedfirst,
std::views::splitlater — happened there.-Werroris public, so aheader difference or a newly added diagnostic on this axis reaches consumers.
The jobs
Six configurations, each in Debug and Release:
--gcc-install-dir--gcc-install-dirlibstdc++-13-devandlibstdc++-14-devcome from the distribution's ownrepository (universe), so no external source enters a required gate. Build-system
jobs go from 20 to 28.
A green build is not evidence for the release a job's name claims: were the pin
to resolve to another release, the build would still succeed, just not against
what the name says. Each pinned job therefore reads the include search path back
and compares its first standard library entry against the pin. The default jobs
report which installation the driver selected without asserting on it — they
exist to build against whatever the image provides, so a change of release there
is news rather than a failure.
Measured
Ubuntu 26.04, along the path CI takes (configure, build, run the tests):
The lower bound holds against every declared release, so it stays at 20.
Documents
The three files that publish the support range now state three axes — the C++
standard a consumer builds with, the compiler, and the standard library — and
list the pairings that hold instead of a product of two axes. GCC with libc++ is
noted as a pairing upstream does not support, along with what would make it
worth revisiting. Coverage is stated per quadrant: every libstdc++ release in
range is covered in the Clang pairings, while the GCC ones cover 13 and 15,
since the libstdc++ version follows the compiler version there. The provisional
notes that called this combination unverified are gone.
The version in each pairing is the version of the headers built against; the
runtime a binary loads comes from the system's runtime package, which is
versioned separately. The documents now say so, since the pin does not reach the
loader.