Skip to content
Merged
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
155 changes: 155 additions & 0 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,31 @@ jobs:
steps:
- uses: actions/checkout@v7

# The standard library is left to the image here, so the only record of
# which one a run used is this log. Clang selects the newest GCC
# installation it finds rather than the distribution's default alias, so
# that record cannot be derived from the image name either. Reported, not
# asserted on: these two jobs exist to build against whatever the image
# provides, so a change of release is news, not a failure.
#
# The driver's output is captured before it is filtered. Whenever the
# marker cannot be reported — the driver failed, or it stopped printing
# that line — the captured output goes to the log rather than being
# dropped, so the log never claims the driver said nothing when it said
# something else. Reading the capture directly also leaves no pipeline
# whose exit status would hinge on whether pipefail is set, which this
# step does not set. No branch fails the step.
- name: Report toolchain versions
run: |
clang-${{ matrix.toolchain.clang }} --version
if ! probe=$(clang++-${{ matrix.toolchain.clang }} -std=c++23 \
-E -x c++ -v /dev/null 2>&1); then
printf '%s\n' "$probe"
echo "(the driver exited non-zero; no GCC installation selection recorded)"
elif ! grep -m1 'Selected GCC installation:' <<< "$probe"; then
printf '%s\n' "$probe"
echo "(the driver reported no GCC installation selection)"
fi
cmake --version

# No -stdlib override: the default standard library is what most Clang
Expand All @@ -81,6 +103,139 @@ jobs:
- name: Test
run: ./build/test/dross_test --gtest_color=yes

# The jobs above cover only the libstdc++ that comes with the runner image
# (15 on ubuntu-26.04), but the declared range promises the older releases a
# consumer may still be sitting on. 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 the rest of the range is pinned here instead of being left to
# best effort. The pairing is what needs the evidence: these releases predate
# both Clang versions, and -Werror is public, so a diagnostic or a header
# difference on this axis reaches consumers.
#
# Like libc++ below, these headers come from the distribution's own
# repository (universe), so no external source enters a required gate.
build-ubuntu-clang-libstdcxx:
name: Ubuntu Clang ${{ matrix.clang }} libstdc++ ${{ matrix.libstdcxx }} (${{ matrix.build_type }})
runs-on: ubuntu-26.04
strategy:
fail-fast: false
matrix:
build_type: [Debug, Release]
clang: [20, 22]
# 15 is the runner default, already covered by the jobs above.
libstdcxx: [13, 14]

env:
# x86_64-linux-gnu is hardcoded: ubuntu-26.04 is an x86-64 runner today,
# so that is the only triple whose GCC installation this job has to name.
GCC_INSTALL_DIR: /usr/lib/gcc/x86_64-linux-gnu/${{ matrix.libstdcxx }}
LIBSTDCXX_HEADERS: /usr/include/c++/${{ matrix.libstdcxx }}

steps:
- uses: actions/checkout@v7

- name: Install libstdc++ ${{ matrix.libstdcxx }}
run: |
sudo apt-get update
sudo apt-get install -y libstdc++-${{ matrix.libstdcxx }}-dev

- name: Report toolchain versions
run: |
clang-${{ matrix.clang }} --version
cmake --version

# This job's name claims a specific libstdc++, and a green build is not
# evidence for that claim: were --gcc-install-dir to resolve to another
# release than the one pinned, the build would still succeed, just not
# against what the name says. So the include search path is read back and
# its entries are canonicalised in order until one of them falls under
# /usr/include/c++. That one is where the standard library headers come
# from, and it is the only entry compared against the pinned release.
#
# The directory check and the search path check do not subsume each other:
# the directory comes from libgcc-N-dev while the headers come from
# libstdc++-N-dev, so the directory can exist with no headers behind it —
# in which case Clang drops the standard library entries entirely and the
# comparison below reports that rather than a wrong version.
#
# What this does not cover is the flag going missing from the configure
# step below: this check would still pass, and the build would quietly fall
# back to the runner default.
- name: Verify the pinned libstdc++ is the one Clang selects
run: |
set -euo pipefail

if [ ! -d "$GCC_INSTALL_DIR" ]; then
echo "$GCC_INSTALL_DIR does not exist: installing libstdc++-${{ matrix.libstdcxx }}-dev did not bring in the GCC installation this job pins." >&2
exit 1
fi

# The driver's own output is the only diagnosis available when it
# refuses the pinned directory — a GCC installation it will not accept
# exists as far as the check above is concerned — so print it rather
# than letting the failed assignment end the step in silence.
if ! search=$(clang++-${{ matrix.clang }} -std=c++23 \
"--gcc-install-dir=$GCC_INSTALL_DIR" -E -x c++ -v /dev/null 2>&1); then
echo "The driver failed with --gcc-install-dir=$GCC_INSTALL_DIR; its output follows." >&2
printf '%s\n' "$search" >&2
exit 1
fi
entries=$(printf '%s\n' "$search" | awk '
/search starts here:/ { inside = 1; next }
/End of search list/ { inside = 0 }
inside && NF { sub(/^[ \t]+/, ""); print }')
if [ -z "$entries" ]; then
echo "No include search path in the driver output below; the marker Clang prints may have changed." >&2
printf '%s\n' "$search" >&2
exit 1
fi
printf '%s\n' "$entries"

# Clang prints these relative to the GCC installation, as
# .../13/../../../../include/c++/13, so they are canonicalised before
# being compared.
selected=
while read -r entry; do
resolved=$(readlink -m "$entry")
case "$resolved" in
/usr/include/c++/*)
selected=$resolved
break
;;
esac
done <<< "$entries"

if [ "$selected" != "$LIBSTDCXX_HEADERS" ]; then
echo "Expected $LIBSTDCXX_HEADERS to come first, found ${selected:-no standard library headers at all}." >&2
exit 1
fi
echo "$selected comes first in the include search path, as pinned."

# --gcc-install-dir has to reach the link as well as the compile: it
# selects the GCC installation the driver takes its startup files and its
# libstdc++ from. CMAKE_CXX_FLAGS alone already carries it to both link
# lines — the build file CMake writes puts it on the shared library's and
# on the test executable's — but that is CMake filling in language flags
# for its own reasons, not a promise about linking. Naming the linker
# variables states the requirement instead of resting on it. The libc++
# jobs below set CMAKE_EXE_LINKER_FLAGS as well; they set no
# CMAKE_SHARED_LINKER_FLAGS.
- name: Configure CMake
run: |
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
-DCMAKE_C_COMPILER=clang-${{ matrix.clang }} \
-DCMAKE_CXX_COMPILER=clang++-${{ matrix.clang }} \
-DCMAKE_CXX_FLAGS="--gcc-install-dir=$GCC_INSTALL_DIR" \
-DCMAKE_EXE_LINKER_FLAGS="--gcc-install-dir=$GCC_INSTALL_DIR" \
-DCMAKE_SHARED_LINKER_FLAGS="--gcc-install-dir=$GCC_INSTALL_DIR"

- name: Build
run: cmake --build build --config ${{ matrix.build_type }} -j"$(nproc)"

- name: Test
run: ./build/test/dross_test --gtest_color=yes

# The requirements say either standard library works across the whole Clang
# range, so libc++ is verified at both ends rather than left to best effort.
# Covering only the lower bound would leave the upper end of the declared
Expand Down
45 changes: 32 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,38 @@

### Requirements

- **C++23** compatible compiler, verified in CI as:
- Linux: GCC 13–15, or Clang 20–22 with either libstdc++ or libc++
- macOS: the Apple Clang shipped with macOS 15 or 26
- Newer versions are best effort: the nightly toolchain watch tracks the
newest versioned GCC available once the toolchain PPA is in place, and
the specific Clang release next in line to enter this range
- Within the declared range, the required Linux jobs build GCC 13/15 and
Clang 20/22, with the Clang jobs pairing against the libstdc++ present
on Ubuntu 26.04 or against libc++. Clang against the older libstdc++
releases available on Ubuntu 24.04 (13 and 14) is not yet verified;
verification for that combination is to be added, and this sentence
will be removed once it is. GCC 14 and Clang 21 are inside the declared
range but are not built by a required job.
- **A compiler configured for C++23 or later.** The public headers use C++23,
so C++17 and C++20 are outside the supported range. C++26 consumers are best
effort: no required job builds one, so neither compiling these headers as
C++26 nor the ABI and ODR compatibility of linking such a consumer against a
C++23 build of the library is verified.
- **A supported compiler and standard library pairing.** On Linux those are:
- GCC 13–15 with the libstdc++ it is paired with (13, 14 or 15)
- Clang 20–22 with libstdc++ 13, 14 or 15
- Clang 20–22 with libc++ 20 or 22

The version in each pairing is the version of the standard library headers
the compiler builds against. The shared runtime a resulting binary loads
comes from the system's own runtime package, which is versioned and updated
separately.

GCC with libc++ is not one of them, because upstream does not support that
pairing: GCC has no `-stdlib` option to select libc++ with in the first
place. It would be worth revisiting if GCC gained an equivalent option, or
if libc++ started supporting GCC officially. On macOS the compiler is the
Apple Clang shipped with macOS 15 or 26, and the standard library is not a
separate axis there, because libc++ comes with the OS toolchain.
- **What the required Linux jobs build.** GCC 13 and 15, each against the
libstdc++ paired with it, and Clang 20 and 22 against libstdc++ 13, 14 and
15 (15 being the release Ubuntu 26.04 provides) as well as against libc++ 20
and 22. Every libstdc++ release in the supported range is therefore covered
in the Clang pairings; among the GCC ones only 13 and 15 are, since the
libstdc++ version follows the compiler version there. GCC 14 and Clang 21
are inside the declared range but are not built by a required job. On macOS
the required jobs build with the Apple Clang of macOS 15 and 26. Newer
versions are best effort: the nightly toolchain watch tracks the newest
versioned GCC available once the toolchain PPA is in place, and the specific
Clang release next in line to enter this range.
- **CMake 3.20+**

### Installation
Expand Down
40 changes: 30 additions & 10 deletions docs/sphinx/source/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,36 @@ Development Setup
Build Requirements
~~~~~~~~~~~~~~~~~~

- C++23 compatible compiler: on Linux, GCC 13-15, or Clang 20-22 with either
libstdc++ or libc++; on macOS, the Apple Clang shipped with macOS 15 or 26.
Newer versions are best effort.
- Within that range, the required Linux jobs currently build GCC 13/15 and
Clang 20/22, with Clang built against the libstdc++ present on Ubuntu
26.04 or against libc++. Clang against the older libstdc++ releases
available on Ubuntu 24.04 (13 and 14) is not yet built by a required job;
verification for that combination is to be added, and this sentence will
be removed once it lands. GCC 14 and Clang 21 are inside the declared
range but are not built by a required job either.
- A compiler configured for C++23 or later. The public headers use C++23, so
C++17 and C++20 are outside the supported range. C++26 consumers are best
effort: no required job builds one, so neither compiling these headers as
C++26 nor the ABI and ODR compatibility of linking such a consumer against a
C++23 build of the library is verified.
- A supported compiler and standard library pairing. On Linux those are:

- GCC 13-15 with the libstdc++ it is paired with (13, 14 or 15)
- Clang 20-22 with libstdc++ 13, 14 or 15
- Clang 20-22 with libc++ 20 or 22

The version in each pairing is the version of the standard library headers
the compiler builds against. The shared runtime a resulting binary loads
comes from the system's own runtime package, which is versioned and updated
separately.

GCC with libc++ is not one of them, because upstream does not support that
pairing: GCC has no ``-stdlib`` option to select libc++ with in the first
place. It would be worth revisiting if GCC gained an equivalent option, or
if libc++ started supporting GCC officially. On macOS the compiler is the
Apple Clang shipped with macOS 15 or 26, and the standard library is not a
separate axis there, because libc++ comes with the OS toolchain.
- The required Linux jobs build GCC 13/15, each against the libstdc++ paired
with it, and Clang 20/22 against libstdc++ 13, 14 and 15 (15 being the
release Ubuntu 26.04 provides) as well as against libc++ 20 and 22. Every
libstdc++ release in the supported range is therefore covered in the Clang
pairings; among the GCC ones only 13 and 15 are, since the libstdc++ version
follows the compiler version there. GCC 14 and Clang 21 are inside the
declared range but are not built by a required job either. Newer versions
are best effort.
- CMake 3.20 or later
- Git

Expand Down
55 changes: 39 additions & 16 deletions docs/sphinx/source/getting-started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,52 @@ System Requirements

To build and use dross, you need:

- **C++ Compiler**: Supporting C++23 standard

- On Linux: GCC 13 through 15, or Clang 20 through 22 with either libstdc++
or libc++
- On macOS: the Apple Clang shipped with macOS 15 or 26
- **C++ Standard**: C++23 or later in your own project

The public headers use C++23, so C++17 and C++20 are outside the supported
range. C++26 consumers are best effort: no required job builds one, so
neither compiling these headers as C++26 nor the ABI and ODR compatibility
of linking such a consumer against a C++23 build of the library is verified.

- **C++ Compiler and Standard Library**: on Linux, one of these pairings

- GCC 13 through 15 with the libstdc++ it is paired with (13, 14 or 15)
- Clang 20 through 22 with libstdc++ 13, 14 or 15
- Clang 20 through 22 with libc++ 20 or 22

The version in each pairing is the version of the standard library headers
the compiler builds against. The shared runtime a resulting binary loads
comes from the system's own runtime package, which is versioned and updated
separately.

GCC with libc++ is not one of them, because upstream does not support that
pairing: GCC has no ``-stdlib`` option to select libc++ with in the first
place. It would be worth revisiting if GCC gained an equivalent option, or
if libc++ started supporting GCC officially. On macOS the compiler is the
Apple Clang shipped with macOS 15 or 26, and the standard library is not a
separate axis there, because libc++ comes with the OS toolchain.

The required Linux build matrix builds GCC 13 and 15, each against the
libstdc++ paired with it, and Clang 20 and 22 (not the intermediate 21)
against libstdc++ 13, 14 and 15 — 15 being the release Ubuntu 26.04
provides — as well as against libc++ 20 and 22. Every libstdc++ release in
the supported range is therefore covered in the Clang pairings; among the
GCC ones only 13 and 15 are, since the libstdc++ version follows the
compiler version there. GCC 14 and Clang 21 sit inside the declared range
the same way, without a required job of their own.

Newer versions are best effort: GCC is exercised by the nightly toolchain
watch tracking the newest versioned GCC available once the toolchain PPA
is in place, and Clang by the same watch tracking the specific release
next in line to enter this range, rather than by the required build
matrix.

The Clang lower bound is higher than the GCC one because older Clang
releases cannot compile this library's C++23 ``std::expected`` usage against
the libstdc++ they are paired with on Ubuntu 24.04 — installing the
libstdc++ 14 headers alongside them does not change that either. From Clang
20 onwards both standard libraries are supported, but the required build
matrix currently exercises only Clang 20 and 22 (not the intermediate 21),
and only against the libstdc++ present on Ubuntu 26.04 or against libc++
— not against the older libstdc++ releases available on Ubuntu 24.04
(13 and 14). Verification for that lower-bound combination is to be added;
this paragraph will be trimmed once it is. GCC 14 and Clang 21 sit inside
the declared range the same way, without a required job of their own.
The Clang lower bound is higher than the GCC one because Clang releases
older than 20 cannot compile this library's C++23 ``std::expected`` usage
against the libstdc++ they are paired with on Ubuntu 24.04 — and installing
the libstdc++ 14 headers alongside those older releases does not change that
either. From Clang 20 onwards both standard libraries work, which is why
every pairing listed above starts there.

- **Build System**: CMake 3.20 or later
- **Operating System**: Linux or macOS
Expand Down
Loading