From c5aaff567dc4d70c4105ce3fde9bebbbf90c894f Mon Sep 17 00:00:00 2001 From: flink Date: Mon, 20 Jul 2026 21:16:49 -0400 Subject: [PATCH 1/2] Set matching CC alongside CXX in Linux CI matrix The Configure step only set CXX, so CMake's C compiler detection fell back to whatever the default happened to be on the runner, independent of which C++ compiler the matrix entry was actually testing (e.g. CXX=clang++-3.6 but CC left to detect GCC 11). Derive CC from the same matrix.cxx value the job already installs a matching compiler for. --- .github/workflows/linux.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index e9a21424..8d45e478 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -179,6 +179,13 @@ jobs: sudo apt install locales-all cmake -E make_directory ${{runner.workspace}}/build + - name: Select matching C compiler + run: | + cc=${{matrix.cxx}} + cc=${cc/clang++/clang} + cc=${cc/g++/gcc} + echo "CC=$cc" >> "$GITHUB_ENV" + - name: Configure working-directory: ${{runner.workspace}}/build env: From 5ae4721b7e1b690e42b01227e22b76e33a3b2348 Mon Sep 17 00:00:00 2001 From: hexonal Date: Wed, 22 Jul 2026 22:12:26 -0400 Subject: [PATCH 2/2] Skip c-test when the C compiler is Clang < 3.8 Setting CC from matrix.cxx means the clang++-3.6 job now configures with real clang 3.6 instead of falling back to the runner's default GCC, which exposes a compile failure in test/c-test.c: clang predates the LLVM PR16340 fix (landed in 3.8) that applies array-to-pointer decay to the controlling expression of _Generic, so fmt-c.h's FMT_MAKE_ARG dispatch never matches a string literal (char[4]) against its char*/const char* associations. It falls through to the zero-argument default association, which is then called with one argument. The guard is at configure time rather than in the CI Test step. c-test is part of the default target, so `cmake --build` compiles it before ctest ever runs -- excluding it with `ctest -E` cannot help, because the job has already failed in the Build step. Guarding the add_executable also means anyone building fmt's tests with an old clang benefits, not just this one CI job. enable_language(C) stays first and unconditional, since CMAKE_C_COMPILER_VERSION is not set before it. fmt-c itself (src/fmt-c.cc, C++) still builds on that job; only the C-consumer smoke test is skipped. --- test/CMakeLists.txt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cd7ba177..a294d9da 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -242,6 +242,14 @@ if (FMT_CUDA_TEST) endif () enable_language(C) -add_executable(c-test c-test.c) -target_link_libraries(c-test PRIVATE fmt::fmt-c) -add_test(NAME c-test COMMAND c-test) +# Clang < 3.8 doesn't apply array-to-pointer decay to the controlling +# expression of _Generic (https://llvm.org/PR16340), so fmt-c.h's argument +# dispatch doesn't match string literals there. +if (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION + VERSION_LESS 3.8) + message(STATUS "Skipping c-test: _Generic is broken in Clang < 3.8") +else () + add_executable(c-test c-test.c) + target_link_libraries(c-test PRIVATE fmt::fmt-c) + add_test(NAME c-test COMMAND c-test) +endif ()