Lesson 11 · Targets: the core model

Compiler flags and warnings

Add flags per target with target_compile_options. Enable warnings the documented way, per compiler, without touching third-party code.

Your code should compile with warnings enabled, but only for your code, and with flags that exist on the compiler you are using. This lesson shows the per-target way to do it.

The per-target command

target_compile_options(app PRIVATE -Wall -Wextra)

Same grammar as everything else. Attach to one target, PRIVATE so it does not leak into libraries you link.

Why

add_compile_options(-Wall) is global. It would also apply to every add_subdirectory target you do not own, including third-party code whose warnings you cannot fix. Per-target options mean your warnings, your code.

Flags are compiler-specific

-Wall is GCC and Clang. MSVC wants /W4. Writing one line that works everywhere means branching on the compiler. The official tutorial shows the pattern, checking the frontend variant, which also catches clang-cl, since it uses the MSVC frontend:

if(CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "MSVC")
    target_compile_options(app PRIVATE /W4)
elseif(CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "GNU|Clang")
    target_compile_options(app PRIVATE -Wall -Wextra)
endif()

Tip

Wrap it in an interface library (lesson 13) so every target in your project gets the same warnings with target_link_libraries(app PRIVATE warnings). That is the pattern larger projects use instead of repeating the if block.

Warnings as errors. Do not default to it

Turning warnings into errors (-Werror / /WX) makes CI fail on any warning, which sounds great. But it breaks the moment a new compiler version adds a warning to your dependencies’ headers. The docs are explicit. Projects should not turn warnings-as-errors flags on by default.

# OK for your own code, as an opt-in for CI. Not the default.
option(APP_ENABLE_WERROR "Treat warnings as errors" OFF)
if(APP_ENABLE_WERROR)
    if(MSVC)
        target_compile_options(app PRIVATE /WX)
    else()
        target_compile_options(app PRIVATE -Werror)
    endif()
endif()

Definitions

-DFOO=bar has a per-target form too:

target_compile_definitions(app PRIVATE VERSION=2 NDEBUG)

The old global add_definitions(-DVERSION=2) is the same reasoning as before. Attached to a target, it cannot leak.

Note

CMake itself leaves MSVC-style warning flags out of CMAKE_<LANG>_FLAGS by default (policy CMP0092, NEW since 3.15), precisely so that you decide per-target. If you see a tutorial setting CMAKE_CXX_FLAGS "-Wall", that is the old style.

Pitfall

Do not put compiler flags in CMAKE_CXX_FLAGS for your project. It is global, it affects every target, it persists in the build directory’s cache even after you delete the line, and it breaks the one-target-one-requirement model. Flags belong on targets.

Read the docs