A way to force "Check for working ${LANG} compiler" test?

Hi!

I would like to ask if there is a “proper” way to force the compiler sanity check even if it passes the ABI check?

For our package, a lot of users are trying to do -DCMAKE_CXX_COMPILER=gcc -DCMAKE_C_COMPILER=gcc, which CMake accepts initially, leading to obscure linking errors down the line. It would be nice if there was a way to force CMake to not skip the compiler check, even if the compiler is known, so that users get a better error message.

Example (Ubuntu 22.04, CMake 3.28.2): gcc is considered a working C++ compiler, but it fails the very basic try_compile:

$ cmake -S. -Bbuild/ -DCMAKE_CXX_COMPILER=gcc
-- The CXX compiler identification is GNU 11.4.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/gcc - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Trying to compile a sample C++ file
CMake Error at CMakeLists.txt:11 (message):
  Cannot compile a sample file: Change Dir:
  '/home/aland/tmp/cmake-test/build/CMakeFiles/CMakeTmp'



  Run Build Command(s): /usr/bin/cmake -E env VERBOSE=1 /usr/bin/gmake -f
  Makefile cmTC_7e4ff/fast

  /usr/bin/gmake -f CMakeFiles/cmTC_7e4ff.dir/build.make
  CMakeFiles/cmTC_7e4ff.dir/build

  gmake[1]: Entering directory
  '/home/aland/tmp/cmake-test/build/CMakeFiles/CMakeTmp'

  Building CXX object CMakeFiles/cmTC_7e4ff.dir/main.cpp.o

  /usr/bin/gcc -std=gnu++17 -o CMakeFiles/cmTC_7e4ff.dir/main.cpp.o -c
  /home/aland/tmp/cmake-test/main.cpp

  Linking CXX executable cmTC_7e4ff

  /usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_7e4ff.dir/link.txt
  --verbose=1

  /usr/bin/gcc CMakeFiles/cmTC_7e4ff.dir/main.cpp.o -o cmTC_7e4ff

  /usr/bin/ld: CMakeFiles/cmTC_7e4ff.dir/main.cpp.o: in function
  `__static_initialization_and_destruction_0(int, int)':

  main.cpp:(.text+0x3b): undefined reference to `std::ios_base::Init::Init()'

  /usr/bin/ld: main.cpp:(.text+0x56): undefined reference to
  `std::ios_base::Init::~Init()'

  collect2: error: ld returned 1 exit status

  gmake[1]: *** [CMakeFiles/cmTC_7e4ff.dir/build.make:99: cmTC_7e4ff] Error 1

  gmake[1]: Leaving directory
  '/home/aland/tmp/cmake-test/build/CMakeFiles/CMakeTmp'

  gmake: *** [Makefile:127: cmTC_7e4ff/fast] Error 2





-- Configuring incomplete, errors occurred!

Here’s CMakeLists.txt used:

cmake_minimum_required(VERSION 3.10)
project(MyProject VERSION 1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

message(STATUS "Trying to compile a sample C++ file")
try_compile(MAIN_CPP_COMPILES "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/main.cpp" OUTPUT_VARIABLE MAIN_CPP_ERROR)

if (NOT MAIN_CPP_COMPILES)
    message(FATAL_ERROR "Cannot compile a sample file: ${MAIN_CPP_ERROR}")
endif ()

main.cpp:

#include <iostream>

int main()
{
    return 0;
}

I indicate compiler selection by setting environment variables CC CXX etc.

On Unix-like OS:

export CC=gcc CXX=g++ 

This must be before the first CMake command.

From what I can tell, this will suffer from the same problem if the user sets CXX=gcc: CMake will happily accept the compiler without any diagnostics, only for it to fail at the build stage.

What I want is to have a way to force CMake to actually check that compiler works. It is always possible to add try_compile with a trivial C++ code to project’s CMakeLists.txt, but it feels like there should be a better, standardized way to check that the supplied C++ compiler is actually a C++ compiler.

Does -DCMAKE_<LANG>_ABI_COMPILED=FALSE work if passed at first configure time?

Thanks for the hint. That indeed forces the compiler check instead of skipping, but, surprisingly, gcc is still considered a working C++ compiler:

$ cmake -S. -Bbuild/ -DCMAKE_CXX_COMPILER=gcc -DCMAKE_CXX_ABI_COMPILED=FALSE
-- The CXX compiler identification is GNU 11.4.0
-- Check for working CXX compiler: /usr/bin/gcc
-- Check for working CXX compiler: /usr/bin/gcc - works
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Trying to compile a sample C++ file
CMake Error at CMakeLists.txt:11 (message):
  Cannot compile a sample file: Change Dir:
[same error message as in OP]

Looking at the source code, it appears that only compiler definition is checked, but no actual C++ symbols are used. While, for my case, the problem occurs when trying to link to the C++ standard library.

I recall that using gcc as a C++ compiler needs special flags to link with the C++ standard libraries. Are you sure you don’t want to use g++ for the CXX compiler?

I definitely want to use g++ :slight_smile: But some of our users are, by mistake, setting both C and C++ compilers to gcc, which results in rather confusing error messages mid-configure or mid-build (see text above, I don’t think it’s easy to figure out what is wrong).

So, I want to know if there is a way to obtain a good diagnostic from CMake in such a case. Since CMake already does compiler sanity checks, and accidentally setting the wrong compiler seems like an easy mistake to make, I hope that there should be some better way to do it then writing our own try_compile test to double-check the compiler sanity after CMake already deemed it working.

So far, it seems there are two problems:

  • By default, the compiler is not actually checked as long as it passes the ABI checks. That can be avoided by forcing CMAKE_CXX_ABI_COMPILED to false, per @ben.boeckel’s suggestion.
  • When the check is forced by the method above, CMake still reports “Check for working CXX compiler: /usr/bin/gcc - works” (or even “Check for working CXX compiler: /usr/bin/gfortran - works”). To me, “working CXX compiler” implies that “try_compile” a simple C++ source should succeed without any extra flags; but it does not.

FYI,

In cmake/Modules/CMakeCXXCompilerABI.cpp it has this stanza:

#ifndef __cplusplus
#  error "A C compiler has been selected for C++."
#endif

#include "CMakeCompilerABI.h"

This stanza should detect a c compiler being used for c++ when compiling this file. I believe this is the standard way of detecting c++ compilers.

But gcc has its determine language by file name suffix feature.

 -x language
           Specify explicitly the language for the following input files
           (rather than letting the compiler choose a default based on
           the file name suffix).

Which causes gcc to act like a c++ compiler for c++ source code. However gcc, when driving the link step, performs linking for c executables. Makes sense in the context that compiling and linking are two different operations.

Even if you altered the ABI tests to call a C++ standard library call, the user still could set CMAKE_TRY_COMPILE_TARGET_TYPE to STATIC_LIBRARY which turns off linking when try_compile is used. So, it wouldn’t catch the problem all the time.

Good luck!

True.

But, since CMake uses the same binary as a linker driver, I would argue that it’s reasonable to expect that both aspects are tested (in a single test or separately is less of an issue from user’s perspective).

Yeah. I’m a bit surprised that GCC did not consider that people are relying on such behavior. True, compiling and linking are separate steps, but people who use different binaries or bother with explicit linking to the C++ std library are likely to be able to differentiate between different compiler drivers for different source files.

Thanks for pointing out that possibility. But, I guess, for now we would resort to a simple try_compile check: should be robust enough to catch simple user errors.

FYI,
Warn if gcc is used as CXX_COMPILER? (#20385) · Issues · CMake / CMake · GitLab (kitware.com)

1 Like

Thanks!