Strictly appending to CMAKE_<LANG>_FLAGS

I haven’t been able to identify any mechanism to specify strictly additional compiler flags from a commandline cmake execution. I usually just use -D CMAKE_CXX_FLAGS=foo which in practice works in most cases. However, I have recently encountered an issue using Visual Studio generators where doing so results in the exclusion of CMake-internal default definitions, in particular WIN32.

This is a follow-up to what was originally filed as a GitLab issue. The message closing the issue mentions that the resulting CMAKE_CXX_FLAGS can be “interactively edited after-the-fact in ccmake or cmake-gui” which while true, doesn’t cover our usage of successive executions of a CI job in a non-interactive environment.

Depending on per-execution external criteria (i.e., not encapsulable in CMakeLists.txt) I’m seeking to provide or inhibit compiler flags such as warning-as-error (/WX). This seems to be a case of wanting an “append to default flags” option (without requiring knowledge of what the default flags are), but I haven’t been able to locate such a thing.

Explored alternatives:

  • CXXFLAGS environment variable does yield the desired behavior. This seems to me like inconsistent behavior (possible bug?) for CMAKE_CXX_FLAGS and CXXFLAGS. As I understand the documentation, the environment variable is used to initialize the CMake variable, so using either should yield the same result.

    In either case however, our particular CI jobs in some cases use incremental build areas, which makes the environment variable approach insufficient (since it’s only used during the first configuration).

  • CMAKE_<LANG>_FLAGS_INIT gives the desired behavior, but since I’m not using it from a toolchain file, I’m reluctant to rely on it. It is also relevant for the first configuration and so is similarly insufficient as the environment variable alternative.

I’d certainly argue that the best solution is to ensure that needed definitions are added within the project CMakeLists.txt. Unfortunately, that isn’t reasonable when using with 3rd party libraries. In particular many have a habit of assuming WIN32 (as opposed to _WIN32) since it’s added by default by Visual Studio (and the CMake Visual Studio generators) which leads to sloppy authoring of CMakeLists.txt.

Bump, in particular for feedback on the behavior difference between CXXFLAGS and -D CMAKE_CXX_FLAGS. Based on my understanding this would appear to be a bug (either functionality and/or documentation).

I don’t think there’s a mechanism to give extra flags from the command line here. A new feature might be required. @brad.king?

I’d argue against a general-purpose feature addition here, but since WIN32 has special handling, perhaps a targeted special case for just that entry? It’s a slippery slope though going down that path.

As I originally caveated, “I’d certainly argue that the best solution is to ensure that needed definitions are added within the project CMakeLists.txt.” For the particular project which drove and surfaced this issue we’ve since done this more proper thing of using target_compile_definitions() for focused setting of the variable. I’d suggest two separate potential tasks here:

  • Use consistent behavior between specifying CXXFLAGS via environment variable vs. -D CMAKE_CXX_FLAGS for a clean build area. The fact that the environment variable path gave the desired (but arguably incorrect) behavior was misleading.

  • Consider maybe, possibly, , providing special cases handling for WIN32 in this situation as well? I only raise this as being possibly beneficial since it’s already a special case, so being consistent and pervasive with that special case would be nice.

As I explained in the linked CMake Issue 23956, the difference between CMAKE_CXX_FLAGS and CXXFLAGS is intentional. The flags are stored in the CMAKE_CXX_FLAGS cache entry, and the default value for that is taken from a combination of builtin flags and the CXXFLAGS environment variable. By specifying -DCMAKE_CXX_FLAGS=... one says “use my flags instead of the defaults”.

CXXFLAGS is the official way to say “use default flags plus these”.

our particular CI jobs in some cases use incremental build areas, which makes the environment variable approach insufficient (since it’s only used during the first configuration).

Please explain that use case in more detail. Are you trying to append to the current CMAKE_CXX_FLAGS value in an existing CMakeCache.txt, reconfigure, and build again?

Note that anything depending on the WIN32 symbol can just use the actually-provided-by-Windows _WIN32 spelling. The WIN32 spelling was added in the old days of CMake and hasn’t been able to be removed because of compatibility concerns. I’d love a policy to remove it though :slight_smile: .

Precisely - we have jobs in CI (Jenkins in this particular case, but also GitLab CI) for quick-turn checks of multiple branches which use ccache, incremental builds, and provide user-facing options/parameters which internally result in changing effective C++ compiler options (e.g. warning-as-error, optimization level, etc.). A single job builds with all of MSVC, gcc, and clang, so we convert the single job parameter into the compiler-specific options. There is obviously a separate build area for each compiler, but multiple build areas are driven by a single job with distinct parallel stages.

We want to be able to switch branches and compilation options, then on a subsequent incremental build minimize needed rebuilds as well as use applicable results from ccache’s cache (typically only branch churn in subsets of the overall codebase). This works fine now that we’ve added the tailored application of WIN32 on targets where needed. However due to the WIN32 default vs. non-default special casing it seemed excessively complicated or at least non-intuitive. In particular, using CXXFLAGS was insufficient since it is only considered on initial build area creation, and not subsequent reexecutions of cmake with different options for our incremental builds.

Fair point, and I’d even mentioned (somewhere?) that it may be a documentation clarification. The usage of CMAKE_CXX_FLAGS overriding the env var is completely sensical.

This is the crux of what I didn’t find clear in the documentation and may perhaps be worth a tweak. The docs simply indicate that CXXFLAGS is used “to determine CXX default compilation flags, after which the value for CXXFLAGS is stored in the cache as CMAKE_CXX_FLAGS”. To my reading:

  • there isn’t a clear distinction that the behavior is different. The implications behind “to determine CXX flags” are at best subtle. I read “to determine” (now knowing incorrectly) as “determines” or “specifies”, rather than “merged with/appended to possibly other internal flags” indicating it’s only part of the resultant whole.
  • This is further emphasized and seemingly reinforced by “the value for CXXFLAGS is stored in the cache as CMAKE_CXX_FLAGS” rather then something like “the resultant determined value (including CXXFLAGS) is store in the cache…”
  • The combination of the above points seemed to falsely reinforce my understanding that the usage of CXXFLAGS and CMAKE_CXX_FLAGS were equivalent, with the former only serving as an alternate environment-based mechanism to seed the latter.

Hopefully this helps clarify and make sense of my perspective when working through it. If it’d be useful I’d be happy to draft a doc patch.

I opened CMake MR 7804 to clarify the documentation.

You should be able to achieve your use case with:

$ env CXXFLAGS='-new -flags' cmake -U CMAKE_CXX_FLAGS ...

The -U CMAKE_CXX_FLAGS flag will undefine the cache entry so that it will be re-initialized using the new CXXFLAGS environment variable value in combination with the builtin defaults.

Looks great, thanks!

Yep, indeed I had that used internally for a brief bit but it felt like cheating or subverting the intended system. The doc updates I think will help clarify and endorse that as valid to others in the future, thanks!