161

How to set the warning level for a project (not the whole solution) using CMake? Should work on Visual Studio and GCC.

I found various options but most seem either not to work or are not consistent with the documentation.

usr1234567
  • 21,601
  • 16
  • 108
  • 128
Wernight
  • 36,122
  • 25
  • 118
  • 131
  • It sounds like you are coming from a Visual Studio background. I don't have a deep background in that area, but I believe VS _projects_ are CMake _targets_ and VS _solutions_ are similar to CMake _projects_. Since this is a question about CMake, it might help other readers if you use CMake terminology instead of VS terminology. – starball Sep 30 '22 at 20:01

7 Answers7

161

In modern CMake, the following works well:

if(MSVC)
  target_compile_options(${TARGET_NAME} PRIVATE /W4 /WX)
else()
  target_compile_options(${TARGET_NAME} PRIVATE -Wall -Wextra -Wpedantic -Werror)
endif()

My colleague suggested an alternative version:

target_compile_options(${TARGET_NAME} PRIVATE
  $<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
  $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic -Werror>
)

Replace ${TARGET_NAME} with the actual target name. -Werror is optional, it turns all warnings into errors.

Or use add_compile_options(...) if you want to apply it to all targets as suggested by @aldo in the comments.

Also, be sure to understand the difference between PRIVATE and PUBLIC (public options will be inherited by targets that depend on the given target).

As @davidfong notes in the comments, since CMake v3.24, there is the CMAKE_COMPILE_WARNING_AS_ERROR variable that switches on treating compile warings as errors. In case it is set inside CMakeLists.txt, the user can still turn it off with the --compile-no-warning-as-error cmake flag. In case you want to add warning-as-error manually, add /WX in Windows and -Werror elsewhere to target_compile_options.

mrts
  • 16,697
  • 8
  • 89
  • 72
  • 35
    Or simply `add_compile_options(...)` if you want to apply it to all targets. – aldo Oct 03 '18 at 00:55
  • 5
    @aldo the problem with `add_compile_options()` is that the warnings will propagate to targets added via `add_subdirectory()`. If you include external libraries this way you may get lots of warnings if that library was designed with different warning level. – trozen Oct 25 '19 at 13:10
  • 6
    Please do not add `-Werror` unconditionally. `-Werror` is great for the development, but the final user may use more recent compiler with new warnings. You don't want to break his process. – Jérôme Pouiller Feb 23 '21 at 09:05
  • 4
    Not to be pedantic, but for the sake of uniformity, the 'pedantic' flag should be called "-Wpedantic". – Raleigh L. Aug 20 '21 at 18:26
  • 1
    @RaleighL. thanks, fixed - pedantic for the win! – mrts Aug 22 '21 at 20:11
  • This works but produces lots of D9025 overriding '/W3' with '/W4 warnings. – Dan M. Aug 27 '23 at 20:35
107

UPDATE: This answer predates the Modern CMake era. Every sane CMake user should refrain from fiddling with CMAKE_CXX_FLAGS directly and call the target_compile_options command instead. Check the mrts' answer which presents the recommended best practice.

You can do something similar to this:

if(MSVC)
  # Force to always compile with W4
  if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
    string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
  else()
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
  endif()
elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
  # Update if necessary
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic")
endif()
mloskot
  • 37,086
  • 11
  • 109
  • 136
  • Notice that new versions of Visual Studio (at least 2013) support `/Wall` flag (which is named `EnableAllWarnings`). It produces even more warnings than `/W4`. However from my experience it produces way too much warnings. – Adam Badura May 23 '16 at 23:30
  • 13
    `/Wall` is usable if you want to follow a 'subtractive' strategy for warnings, just like clang's `-Weverything`. Instead of selecting warnings to enable, you enable everything and then select specific warnings to disable. – bames53 Sep 07 '16 at 16:59
  • 1
    Note by "Modern CMake" you mean Cmake version 3.20 or better when it no longer forces the default build flags to include ``/W3 /GR``. – Chuck Walbourn Nov 12 '22 at 15:29
  • @ChuckWalbourn and you would still need some fiddling if something else sets the "default" warning level (i.e. top level "add_compile_options"). – Dan M. Aug 27 '23 at 23:45
28

Some CMake modules I've written include experimental cross-platfrom warning suppression:

sugar_generate_warning_flags(
    target_compile_options
    target_properties
    ENABLE conversion
    TREAT_AS_ERRORS ALL
)

set_target_properties(
    foo
    PROPERTIES
    ${target_properties}
    COMPILE_OPTIONS
    "${target_compile_options}"
)

Result for Xcode:

  • Set CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION Xcode attribute (aka build settings -> warnings -> suspicious implicit conversions -> YES)
  • Add compiler flag: -Werror

Makefile gcc and clang:

  • Add compiler flags: -Wconversion, -Werror

Visual studio:

  • Add compiler flags: /WX, /w14244

Links

xaxxon
  • 19,189
  • 5
  • 50
  • 80
  • 2
    it is a shame cmake does not provide this functionality – Slava Jan 28 '16 at 18:08
  • 3
    Good news. Sorry to post it here and not in the cmake mailing-list, but without level this will be useless, There are just too many warnings to list them all explicitely. If you want to unify it one way to do thatis two separate cmake_level - unified set of warnings, based for instance on clang, and native_level with the meaning specific for a compiler. One of them can be probably shortened to level. Sorry if I did not really follow the conversation and got something wrong – Slava Apr 30 '16 at 11:07
  • 1
    @void.pointer raises a valid point. Your proposed answer reads: *"**I'm** planning to add this feature"*. It doesn't say, that you did some cursory research and now hope for someone else to do the heavy lifting for you. If you don't want to be attributed with the implementation (and questions on its progress), you need to edit your answer and disassociate yourself from the task you haven't made any progress on in well over a year. – IInspectable Aug 06 '17 at 14:10
  • *"Over a year later, still no progress."* - Now that *is* a valid point. More than a year have passed, with **zero** progress. That is a very strong indication of an abandoned project. If you want to prove us wrong, show us some progress. That hasn't happened, but your proposed answer still suggests, that the feature is just about to get added to CMake. Why make all the fuss about a feature that won't be available in years? That's not helpful at all. Either show some progress, or edit your answer to be less misleading. – IInspectable Aug 07 '17 at 12:48
  • Alright then, would you mind sharing your road map, so that future visitors will know, when this feature becomes available? – IInspectable Aug 07 '17 at 15:04
  • 6
    You don't appear to understand. If you suggest that you are going to implement a feature, then you need to implement that feature in due time. Failing that, you are asked to remove that promise from your proposed answer. You have shown zero commitment to implement said feature, so don't claim otherwise. I understand that it is big. I also understand that you may not be capable of pulling this off. I'm simply asking you to make your answer reflect that. – IInspectable Aug 07 '17 at 16:27
  • removed promise to implement this from answer. – xaxxon Jan 24 '18 at 14:57
20

As per Cmake 3.27.4 documentation:

if (MSVC)
    # warning level 4
    add_compile_options(/W4)
else()
    # additional warnings
    add_compile_options(-Wall -Wextra -Wpedantic)
endif()

GCC and Clang share these flags, so this should cover all 3.

Jay
  • 2,535
  • 3
  • 32
  • 44
  • 1
    Don't use this. Instead, use target_compile_options(). Referring to the latest document seems to be "correct", but it's an ancient entry merely for backward-compatibility. – caoanan Apr 29 '20 at 08:45
  • 5
    @caoanan The documentation mentions nothing of backward-compatibility for this. `add_compile_options` is directory-wide, whereas `target_compile_options` is just for a single target. – Signal Jun 10 '20 at 01:00
9

Here is the best solution I found so far (including a compiler check):

if(CMAKE_BUILD_TOOL MATCHES "(msdev|devenv|nmake)")
    add_definitions(/W2)
endif()

The GCC equivalent is -Wall (untested).

Wernight
  • 36,122
  • 25
  • 118
  • 131
  • 9
    The warning flag for GCC would be `-Wall` and maybe `-Wextra` as detailed at http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html – Milliams Mar 04 '10 at 14:19
  • 1
    The list I use is `-W -Wall -Wextra -pedantic`. `-Wextra` IIRC replaced `-W` in a later version of GCC, but I leave both for compatibilities sake. – Jimmio92 Sep 26 '16 at 22:21
  • 3
    That's not the intended purpose of [add_definitions](https://cmake.org/cmake/help/latest/command/add_definitions.html) (*"it is intended to add preprocessor definitions"*). It's not just a best practices recommendation either. Arguments passed to this command will show up in the generated build scripts invoking tools that do not expect them (e.g. the resource compiler). – IInspectable Nov 06 '18 at 09:39
  • 2
    That's not a "compiler check", it's a build tool check. – Thomas Dec 20 '19 at 13:46
3
if(MSVC)
    string(REGEX REPLACE "/W[1-3]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
endif()

If you use target_compile_options - cmake will try to use double /W* flag, which will give warning by compiler.

TarmoPikaro
  • 4,723
  • 2
  • 50
  • 62
  • Thanks for this. I was naively using the `add_compile_options` only to get tons of warnings that `/W3` is being overriden with `/W4`. The fact tha CMake is not addressing this rudimentary option (setting warning level) is beyond belief. – Resurrection Apr 25 '20 at 18:16
1

How to set the warning level for a project (not the whole solution) using CMake?

(I assume this to mean a CMake target, and not a CMake project.)

I found various options but most seem either not to work or are not consistent with the documentation.

Kitware's APIs may be trying to deter you from making your build system brittle and error-prone. The special-casing encouraged by other answers to this question violate at least two important principles of modern CMake build systems...

Firstly, prefer not to specify toolchain-specific details in CMakeLists.txt files. It makes the build system brittle. For example, if a new warning appears in a future version of the toolchain, the compiler will emit an error and your user may need to hack your project in order to build the target.

Instead, write toolchain-agnostic CMakeLists.txt files and preserve the user's ability to customise as they see fit. Ideally, your project should build everywhere with vanilla toolchain configuration - even if that doesn't enable your preferred warnings by default.

Secondly, if you intend to link binaries together, flags should be consistent. This reduces the risk of incompatibility which could result in an ill-formed program. However, warning flags are unlikely to affect code generation, so it may be safe to vary these between the targets you link together.

So... if you wish to specify flags per toolchain and if you absolutely must have different flags for different targets, use custom variables:

# CMakeLists.txt
project(my_project)

add_executable(my_target source_file.cpp)
target_compile_options(my_target PRIVATE "${MY_PROJECT_ELEVATED_WARNING_FLAGS}")

There are many ways to set these variables, such as CMakeCache.txt, a toolchain file, and via CMAKE_PROJECT_INCLUDE_BEFORE. But the simplest way is on the command line during configuration, for GCC

cmake -DMY_PROJECT_ELEVATED_WARNING_FLAGS:STRING="-Wall;-Wextra;-Wpedantic;-Werror" <path-to-project>

for MSVC

cmake -DMY_PROJECT_ELEVATED_WARNING_FLAGS:STRING="/W4;/WX" <path-to-project>
John McFarlane
  • 5,528
  • 4
  • 34
  • 38