3

I have an interface library cxx_opts in cmake that defines a bunch of flags and a binary main that uses the cxx_opts library.

cmake_minimum_required(VERSION 3.16)
project(test LANGUAGES CXX)
    
add_library(cxx_opts INTERFACE)
target_compile_options(cxx_opts INTERFACE
  -Wall -Wpedantic -Wextra -Werror
)
    
add_executable(main main.cpp)
target_link_libraries(main PRIVATE cxx_opts)
target_compile_options(main PRIVATE -Wno-unused-variable)

For completeness, my main.cpp is super simple:

int main() {
    int unused_variable{};
}

I then configure this project and generate a compilation database with this command:

cmake -D CMAKE_EXPORT_COMPILE_COMMANDS=ON  -S . -B build

If I now build this code, I'm getting an error:

cmake --build build
main.cpp:2:9: error: unused variable 'unused_variable' [-Werror,-Wunused-variable]
    2 |     int unused_variable{};
      |         ^~~~~~~~~~~~~~~

I expect that because I'm adding a -Wno-unused-variable flag to the main target the warning should not trigger, and yet it does and is treated as an error.

If I now inspect the compilation database generated as a result of the configuration step (cleaned up from unimportant stuff), I'm seeing that the order of the flags is wrong:

[
{
  "directory": "test_project/build",
  "command": "c++ -Wno-unused-variable -Wall -Wpedantic -Wextra -Werror -o CMakeFiles/main.dir/main.cpp.o -c test_project/main.cpp",
  "file": "test_project/main.cpp"
}
]

Note how the -Wno-unused-variable appears before the -Werror flag, which causes the behavior I'm seeing.

Now with all of the above in mind, I do not understand why CMake is ordering these flags like it does. I also am an avid CMake user and was under the impression that this should not ever happen. From here, the CMake docs seem to say the following:

If BEFORE is specified, the content will be prepended to the property instead of being appended.

Repeated calls for the same append items in the order called.

To test the above statement I actively added the -Wno-unused-variable flag to the cxx_opts target directly with the same results, it still appears before the rest. So something actively reorders flags in the resulting command.

target_compile_options(cxx_opts INTERFACE
  -Wall -Wpedantic -Wextra -Werror
)
target_compile_options(cxx_opts INTERFACE -Wno-unused-variable)

The error here happens on my Mac, so maybe it is Mac-related? Does anyone know what is happening here and what I can do to correct the order of the flags?

UPD: After the comments by @Tsyvarev it seems that we can do this instead that should guarantee the order of the flags (I'm not 100% sure of this yet):

cmake_minimum_required(VERSION 3.16)
project(test LANGUAGES CXX)

add_library(cxx_opts INTERFACE)
target_compile_options(cxx_opts INTERFACE
  -Wall -Wpedantic -Wextra -Werror
)

add_library(custom_flags INTERFACE)
target_compile_options(custom_flags INTERFACE -Wno-unused-variable)

add_executable(main main.cpp)
target_link_libraries(main PRIVATE cxx_opts custom_flags)

Note how we have to create another target custom_flags and link it after the cxx_opts for the flags from custom_flags target to appear after the ones from cxx_opts target. Is there a better (more idiomatic) way?

8
  • "Does anyone know what is happening here" - CMake doesn't guarantee any order between compiler flags added via target_compile_options and flags obtained via usage requirement (from linked libraries). Actually, the reverse order is expected: target_compile_options(main PRIVATE -Wno-unused-variable) adds items to COMPILE_OPTIONS property immediately, but call to target_link_libraries defers propagating usage requirements from linked libraries until the very end of configuration process (after all CMakeLists.txt are processed). Commented Feb 17 at 15:21
  • It is better to avoid list in INTERFACE_COMPILE_OPTIONS (and other INTERFACE_*) those options which may be not suitable for consuming targets. And do not add to that lists those options which are not actually needed for use the library itself. Commented Feb 17 at 15:24
  • Thanks a lot for this explanation. This, however, would not explain why the order of flags is wrong if I make two calls to target_compile_options with the same target cxx_opts. The order of flags is still reversed. Commented Feb 17 at 18:12
  • 1
    In simple words: While CMakeLists.txt is processed, a target property COMPILE_OPTIONS is populated only by target_compile_options command for that target. When the last line of your CMakeLists.txt is executed, the property contains only -Wno-unused-variable. Only at the end of the configuration process the property COMPILE_OPTIONS is enhanced with values from the linked libraries. So values -Wall, -Wpedantic, -Wextra and -Werror from the library cxx_opts are appended to the end of the list. Commented Feb 17 at 19:50
  • Ok, that kinda makes sense to me, thanks for the explanation! Now that shakes my world view a bit. I was under the assumption that using these target-specific commands is the right way to go. But then there is no way to override flags in downstream targets. What is the way then to propagate flags between targets in a meaningful manner in modern CMake? Commented Feb 17 at 21:36

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.