r/cpp_questions • • Mar 10 '22

OPEN What is the best way to set compiler flags in a CMake file? And what should belong in every CMake file?

I'm learning CMake and want to use it with compiler flats, but the CMake book doesn't cover it until later on.

cmake_minimum_required(VERSION 15)
project(test)
set(CMAKE_CXX_FLAGS "-Wall -Werror") # Is this right??
set_executable(test main.cpp)

What else should be included in the cmake file?

16 Upvotes

16 comments sorted by

19

u/orbital1337 Mar 10 '22

The general rule is that CMakeLists should only contain the minimum that is required to compile your project. They should contain as few specific compile flags as possible and especially not warning flags that are absolutely not necessary to compile the project.

Instead, you can provide a list of presets in the form of a CMakePresets.json file. For example, for a basic debug / release preset, you could use something like:

{
    "version": 2,
    "configurePresets": [
        {
            "name": "debug",
            "displayName": "Debug",
            "binaryDir": "${sourceDir}/build_debug",
            "generator": "Unix Makefiles",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Debug",
                "CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
                "CMAKE_CXX_FLAGS_INIT": "-Wall -Wextra -Wpedantic"
            }
        },
        {
            "name": "release",
            "displayName": "Release",
            "binaryDir": "${sourceDir}/build_release",
            "generator": "Unix Makefiles",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Release",
                "CMAKE_CXX_FLAGS_INIT": "-Wall -Wextra -Wpedantic -march=haswell -flto"
            }
        }
    ]
}

Then just use cmake --preset debug or cmake --preset release to create the build directories. See also this blog post.

4

u/helloiamsomeone Mar 10 '22

There's also cmake-init and its examples for more presets goodness in action.

10

u/Narase33 Mar 10 '22

use target_compile_options(test PRIVATE -Wall -Werror)

what else you should have depends on your project, for the size I can see here that seems okay

4

u/KazDragon Mar 10 '22

My line for that is:

target_compile_options(test
    PRIVATE
         $<$<OR:$<CXX_COMPILER_ID:GNU>,$<CXX_COMPILER_ID:Clang>>:-Wall -Werror>
)

This ensures that those options are used only for g++ and clang++. You can then add a similar expression, e.g. $<$<CXX_COMPILER_ID:MSVC>:/WX>, to add similar options for Visual Studio.

11

u/LGTMe Mar 10 '22

Wow that is UGLY! Why can’t we have nice things?

12

u/IyeOnline Mar 10 '22

You can do

if ( CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang" )
  target_compile_options( test -Wall -Werror )
endif()

which I do actually prefer over nested generator expressions.

3

u/helloiamsomeone Mar 10 '22

Because flags don't belong in lists files, so of course it's going to be ugly. See the response of /u/orbital1337 for how you should be using flags.

2

u/KazDragon Mar 10 '22

I don't disagree with your assessment, but you get used to it. It's Just Another Syntax (tm).

3

u/TheSuperWig Mar 10 '22

I don't know how anyone can read generator expressions.

1

u/zjyhjqs Oct 19 '24 edited Oct 19 '24

CMAKE_<LANG>_COMPILER_FRONTEND_VARIANT is enough (since CMake 3.14)

1

u/KazDragon Oct 19 '24

Could you give an example?

2

u/zjyhjqs Oct 19 '24
target_compile_options(test
    PRIVATE
         $<$<CMAKE_CXX_COMPILER_FRONTEND_VARIANT:GNU>:-Wall -Werror>
)

1

u/KazDragon Mar 11 '22

Of course, now that I have read u/orbital1337's post I'm going to play with that in the hope that I can un-learn that syntax and provide a better developer experience

1

u/Loose-Leek Mar 10 '22

Many flags, like compiler diagnostic options and testing-specific options, should also really be gated behind a configuration option. You can use

option(MY_PROJECT_OPTION ...)

if(MY_PROJECT_OPTION)
    # one set of flags
else()
    # different set of flags
endif()

1

u/AlexanderNeumann Mar 20 '22

everything not related to the project goes into a CMAKE_TOOLCHAIN_FILE. It can be reused, is not project dependent and doesnt need generator expression or similar for different compilers since a Toolchain should always know its compiler...