CMake per-language option flags

CMake can set compiler flags that encompass three broad scopes. We set global and per-language options near the beginning of the top-level CMakeLists.txt, before any targets are declared, to avoid confusion about scope. COMPILE_DEFINITIONS works in a similar fashion.

By default, add_compile_options() is global 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.

Per-target options are set by target_compile_options.

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.

project(Foo
LANGUAGES C Fortran)

if(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
  add_compile_options(
  -Wall
  $<$<COMPILE_LANGUAGE:Fortran>:-fimplicit-none>
  $<$<AND:$<COMPILE_LANGUAGE:Fortran>,$<CONFIG:Debug,RelWithDebInfo>>:-Werror=array-bounds>
  )
endif()

add_library(old OBJECT legacy.f old.f)
# these options apply only to target "old"
target_compile_options(old PRIVATE -w -fno-implicit-none)

add_executable(main main.f90 $<TARGET_OBJECTS:old>)