Tuesday, February 6, 2024

[SOLVED] How can I change the number of errors GCC displays without invaliding the CMake cache?

Issue

I have a C++ project that I build using GCC and CMake.

Generally I like to compile with -fmax-errors=1. My normal workflow is to fix the first error and then rebuild since subsequent errors are often caused by the first one.

But unfortunately, with C++20, an error involving a constraint failure is often treated as multiple "errors" by GCC. In order to see why the constraint failed, I need to see more than one error.

So occasionally I like to set -fmax-errors to a higher number, probably 2, when such an error occurs.

But changing the compiler flags (by manually changing CMakeLists.txt or passing a cache variable to cmake on the command line) invalidates the CMake cache and makes the build start from scratch.

This behavior generally makes sense of course; arbitrary configuration changes could require a rebuild. But we humans understand that changing the compiler's error-formatting behavior doesn't require a rebuild. Is there a way of expressing this distinction to CMake?

Or, failing that, is there a clever way of working around this? I thought of having CMake read an environment variable at the time when the compiler is invoked (not at the time when cmake is run), but I can't find any documentation suggesting that this is actually possible.

(I could probably create a script that forwards most of its arguments to g++ but also adds -fmax-errors="$MY_COOL_ENV_VARIABLE" and then tell CMake that the script in question is the C++ compiler to build with, but I imagine that this might violate some of CMake's expectations about the "compiler.")


Solution

At the advice of Marc Glisse, I gave the approach that I hypothesized in parentheses at the end of the question a try. This in fact works quite well.

I now have a Python script called invoke_compiler.py in the top-level directory of my project. I point CMake to the script in the usual way in which one specifies a C++ compiler.

set(CMAKE_CXX_COMPILER ${PROJECT_SOURCE_DIR}/invoke_compiler.py)

Now I was actually a bit terse in the question for the sake of exposition. In actuality I regularly build this project with both GCC and Clang. So I want to be able to specify the C++ compiler to CMake when I invoke cmake.

cmake my-src-dir -DCMAKE_CXX_COMPILER=g++

So invoke_compiler.py has to take a few extra command-line flags, --my-project-cxx-compiler and --my-project-num-errors-flag.

Then the script invokes the compiler (whose executable is the one specified by --my-project-cxx-compiler), forwarding all of its command-line arguments except the extra ones mentioned above and adding f"{num_errors_flag}={os.environ.get('MY_PROJECT_NUM_ERRORS', 1)}". (The name of the compiler flag specifying the number of errors to display must itself be passed as an argument to the script because GCC and Clang call the flag --fmax-errors and --ferror-limit respectively.)

The trick is simply that the logic in CMakeLists.txt that determines which arguments to pass to invoke_compiler.py needs to execute before CMAKE_CXX_COMPILER is overwritten.

The user just interacts with CMake as usual and changes the value of the MY_PROJECT_NUM_ERRORS variable at any time in order to get a different number of errors from the compiler.



Answered By - Sam Marinelli
Answer Checked By - Katrina (WPSolving Volunteer)