Cmake: Setting "warnings as errors" for Release only

Does anyone know how to set werror for release configs only?

I’ve tried:

set_target_properties(${TARGET_NAME} PROPERTIES
    COMPILE_WARNING_AS_ERROR ON)

which works for all configs.

These however:

set_target_properties(${TARGET_NAME} PROPERTIES
    COMPILE_WARNING_AS_ERROR[variant=Release] ON)

set_target_properties(${TARGET_NAME} PROPERTIES
    $<$<CONFIG:Release>COMPILE_WARNING_AS_ERROR ON>)

don’t work at all though.

1 Like

Could you wrap the one you’ve tried in a conditional? Like

if (CMAKE_BUILD_TYPE STREQUAL “Release”)
set_target_properties(${TARGET_NAME} PROPERTIES
COMPILE_WARNING_AS_ERROR ON)
endif()

1 Like

This would only work for single-configuration generators.

I don’t think that target property supports generator expressions, so to get the desired behavior you’d probably have to pass the actual compiler flags in generator expressions, something like:

get_cmake_property (debug_configs DEBUG_CONFIGURATIONS)

if(NOT debug_configs)
  set (debug_configs Debug)
endif()

list (JOIN debug_configs "," debug_configs)

set (config_is_debug "$<CONFIG:${debug_configs}>")
set (config_is_release "$<NOT:${config_is_debug}>")

if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")
  target_compile_options ("${TARGET_NAME}" PRIVATE
    $<IF:${config_is_release},-Werror>)
endif()

Honestly, my real answer is to solve the underlying problem in another way. I personally don’t use -Werror and I don’t think it makes sense to use it for only certain configurations.

2 Likes

The goal is to have CI fail if there are warnings. There is a cmake command line option to disable warnings as error, but not the opposite.

2 Likes

Why only for the Release configuration though? It would be much easier to turn on warning-as-error for all configurations.

You could also use some tool to parse the output of the build and detect the number of warnings. CTest does this, perhaps you could run your build using the ctest client and set up the warning failing flags that way.

1 Like

I think the simplest way is to just have an option for the CI.

option (TRACKTION_CI_ENABLED "" OFF)

if (TRACKTION_CI_ENABLED)
    #set CI-only options, add warning as errors, etc
endif()

And then generate CMake with -D TRACKTION_CI_ENABLED=1

Those kind of options are better because the programmer can turn them on and off manually while debugging/testing.

1 Like

The funny thing is I thought the contentious point here would be not enabling werror for all builds, that’s considered the best practice. In reality I find it cumbersome when making quick edits and getting something running but I definitely don’t want to be committing loads of warnings, they’re almost always pointing at some potential subtle bug.

The reason I asked the question here is that it feels like cmake should offer this but it doesn’t quite.
I just added the following in my mac/linux and windows specific sections which works fine:

    target_compile_options(${TARGET_NAME} PRIVATE "$<$<CONFIG:Release>:-Werror>")

and

    target_compile_options(${TARGET_NAME} PRIVATE "$<$<CONFIG:Release>:/WX>")

so it’s only two lines in the end.


As a side note I used to handle this only in CI by passing the following to xcodebuild/make:

xcodebuild -configuration Release GCC_TREAT_WARNINGS_AS_ERRORS=NO 

and to msbuild:

msbuild.exe project.sln /p:VisualStudioVersion=17.0 /m /t:Build /p:Configuration=Release /p:TreatWarningsAsErrors=true

But as I transition to cmake --build I want to reduce the amount of platform specific stuff outside the cmake file.

I could probably do it in a cross platform way with something like:

cmake --build . -DCOMPILE_WARNING_AS_ERROR=ON

But when I thought about it, having warnings as errors when just building locally for release is probably the best practice anyway.

1 Like

Yes, by all means, enable werror. It just makes sense to reduce potential code issues.

FWIW, I turned on werror about five years ago and have never looked back. And my code is better for it.

My two cents.

I assume you’re also a fan of the “-Wall” flag too :wink:

Like Eyal suggested, you could have some option like *_LOCAL which would turn off werror just for local development. I don’t think turning it off for Debug config is really the answer, because you may also want to build the Release config locally while working on new changes.

The reason why you might want to have different settings for different configurations is quite simple:

While writing new code and experimenting with parameters, it happens very often that you comment out sections of code, but now a couple of local variables are no longer used. If warnings are always errors, then you can’t even compile to try out the experimental change until you’ve commented out the unused variables as well.

This also applies to class members, so having to change a header file could increase compile time exponentially.

It’s much more convenient to say “in debug mode, anything goes, in release mode, compile only if everything is 100% correct”.

This discussion reminds me very much of StackOverflow, where instead of offering a solution, often people argue “don’t do that”. Not very helpful IMO.

2 Likes

I still think the best answer is to not handle “fail the build if there are warnings” in the compiler.

For one, you should be running your tests in both debug & release while developing locally, I think it’s shortsighted to completely ignore the release configuration while making local changes.

Second, although this is possible to implement in CMake, as discussed in this thread, it’s still compiler-specific. I personally think the best solution is to run some tool/script in CI after the build to analyze the logs and detect the number of warnings.

Again: instead of helping, we get “don’t do that”. I want to also compile locally, on my machine, in the IDE, and still get errors while building a release version. So I can find potential mistakes and fix them before even pushing the change to the repo.

I think people have to accept that other people have different workflows. We’ve found something that works for our small team. We want to continue doing that.

I still think enabling werror for release is the simplest way to get CI failures for our builds.

As I said, werror for debug builds would work in an ideal world but for reasons @reFX mentioned and many work, it’s just not pragmatic.

Sure, maybe analysing compiler output using other tools is one approach but again, thinking in terms of dev time resources, I’d have to set up ctest to run our tests, which I’ve not even looked in to yet.

The main reason for moving to cmake was so we can use other ides like VS Code or CLion (which are supported on platforms like Linux) and keep our existing VS/Xcode workflows. The other feature of cmake is more control over the builds, PJ just doesn’t seem to cut it anymore, at least for us.

So that’s one, self contained task with a clear deliverable. To then say, right but if you want your CI to work the same way you also need to run everything through ctest, parse the output with some other tools and report failures in a whole new way is a chunk of time we just don’t have budget for right now.

Doing what I need was 4 lines of cmake.
If someone really wants to build locally without werror, it’s easy enough to turn that off. But that’s like 1% of builds if that. I’d go as far as saying almost all development is done in debug builds (with sanitizers) unless you’re profiling, which is a specialised task and perhaps one you could disable werror for whilst performing.

3 Likes

That’s a large part of my job. Often the developers of some algorithms build only the debug version, but I build mainly the release version, because I want to make sure performance is good and it’s the version we actually ship, so I want to make sure that debug and release behave the same; the optimizer sometimes does strange things.

It’s a simple fact that different teams have different requirements for different reasons. There is no one-size-fits-all solution here.

reFX’s contribution to the discussion is interesting I think in that it points to the different “cultures” in the build framework communities… I mean, there is a reason that Meson got developed, and it’s because they didn’t like the excess of what CMake had become. If you work with Meson a bit you get the feeling it is a lot leaner and neater, and I am pretty sure you could do this without any issue in a simple way.

With CMake a lot of “don’t do that” got baked into how it works, which is why it might actually be the case there’s no easy way to set up what the OP is looking for.

IMO CMake took off like a rocket during the 2010s, it was definitely a lot less popular before then, but it still has the original culture of the people who worked on it baked into the core. (FWIW I feel about JUCE the same way in this respect, but in the case of JUCE I take it as a positive! the JUCE APIs have a ‘human’ quality to them that make them easier to work with than other audio frameworks that are more dry and technical)

So what I think Ben is pointing at is that if you use other logical levels (that work for multi-Config generators), you can get a similar result but definitely a much more convoluted CMakeLists.txt file (which is honestly what you see in any significantly larger CMake-based FOSS project). The complexity of CMake is baked into the nature of CMake.

2 Likes

Yeah maybe, I do feel with this it’s just a feature they haven’t added because maybe no one wanted it. As you say, it’s a large project so they only have limited resources (but it is FOSS and they do merge PRs).

And as I said, there is a way to do it in cmake which isn’t so bad, it’s just I thought there might be a more canonical cmake way. There isn’t so I just live with it.

I’ve not looked in to Meson but have you seen the xmake project? It looks nice and lean but I bet you run in to a lack of feature support fairly quickly (take this opinion with a pinch of salt as I’ve not looked too deeply in to it).

At a glance it seems to fit nicely between CMake and Meson in terms of extremity. It felt like Meson dev was fueled out of frustration with the complexity of CMake. For me I understood how to work with each build system once I understood the thought process of the respective primary developer (team)s.

Right now CMake is doing everything I need and I invested heavily into learning it these past few years so it will take a mountain to get me to budge (the Meson work I did was mainly in porting a WebRTC audio processing module from Meson to CMake build… working with Bazel was out of the question there).