CMake per-language option flags

CMake can set per-language compiler flags scoped to directory or target. The directory scope propagates to targets in the directory and subdirectories. Per-language options that are intended for all targets in the project are often set near the beginning of the top-level CMakeLists.txt, before any targets are declared. The same concepts and scope apply to compile definitions and linker flags.

By default, add_compile_options() and target_compile_options() add flags for all target languages. Multi-language targets (for example, a target that has C and C++ source files) would have the flags applied to all languages. In general, this may cause problems if the flags are not appropriate for all languages.

The options are restricted by compiler, language, build configuration, etc. using CMake generator expressions. For example, $<COMPILE_LANGUAGE:Fortran> is used to restrict options to Fortran targets. A stanza like $<AND:$<COMPILE_LANGUAGE:Fortran>,$<CONFIG:Debug,RelWithDebInfo>> sets flags for Fortran targets in the debug and RelWithDebInfo build configurations.

Note: where using FetchContent, the main project may desire -Wall but this may cause megabytes of warnings from a legacy Fetched project. A solution is to put add_compile_options() in each of the parent project directories and/or use target_compile_options() in the parent project.

This example is for a C and Fortran project, where some flags apply to C and Fortran, and other flags are Fortran-specific. It’s often useful to use a convenience function to make the code more readable, say in a file “AddOptions.cmake”:

function(add_compile_lang_options lang)

  set(args "")

  foreach(arg ${ARGN})
    string(APPEND args ${arg}$<SEMICOLON>)
  endforeach()

  add_compile_options($<$<COMPILE_LANGUAGE:${lang}>:${args}>)
endfunction()
project(Demo LANGUAGES C Fortran)

include(AddOptions.cmake)

add_compile_lang_options(Fortran -Wall -fimplicit-none)

# more advanced example to add debug-only, compiler-specific and language-specific options:

add_compile_options(
  $<$<AND:$<COMPILE_LANG_AND_ID:Fortran,GNU>,$<CONFIG:Debug,RelWithDebInfo>>:-Werror=array-bounds>
)