Preserving options with spaces in add_compile_options

Hello,

I have a CMake project with several targets and I wish to set global compile options through add_compile_options . These are the flags that I need to set:

ifort icc
-qno-opt-dynamic-align -qno-opt-dynamic-align
-convert big_endian -fp-model precise
-assume byterecl -O2
-ftz -debug minimal
-traceback
-assume realloc_lhs
-fp-model source
-O2
-debug minimal
-free

First I validated the flags via icc and ifort (v19.0.5.281). No compiler warnings generated and compilation is successful.

ifort -qno-opt-dynamic-align -convert big_endian -assume byterecl -ftz -traceback -assume realloc_lhs -fp-model source -O2 -debug minimal -free -o hello_F90.out hello.F90
icc -qno-opt-dynamic-align -fp-model precise -O2 -debug minimal -o hello_C.out hello.c

Next, I created a test project which tries to achieve the same result through add_compile_options.

cmake_minimum_required (VERSION 3.14)

project (helloworld LANGUAGES C Fortran)

# CASE 1: unquoted flags, space-separated
set(fflags -qno-opt-dynamic-align -convert big_endian -assume byterecl -ftz -traceback -assume realloc_lhs -fp-model source -O2 -debug minimal -free)
set(cflags -qno-opt-dynamic-align -fp-model precise -O2 -debug minimal)
add_compile_options("$<$<COMPILE_LANGUAGE:Fortran>:${fflags}>")
add_compile_options("$<$<COMPILE_LANGUAGE:C>:${cflags}>")

add_library(helloworld STATIC hello.c hello.F90)

Triggering the build on my Bash terminal,

$ cmake -S . -B bld -DCMAKE_C_COMPILER=icc -DCMAKE_Fortran_COMPILER=ifort
-- The C compiler identification is Intel 19.0.5.20190815
-- The Fortran compiler identification is Intel 19.0.5.20190815
-- Check for working C compiler: /opt/software/icc/2019.5.281-GCC-8.3.0/compilers_and_libraries_2019.5.281/linux/bin/intel64/icc
-- Check for working C compiler: /opt/software/icc/2019.5.281-GCC-8.3.0/compilers_and_libraries_2019.5.281/linux/bin/intel64/icc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working Fortran compiler: /opt/software/ifort/2019.5.281-GCC-8.3.0/compilers_and_libraries_2019.5.281/linux/bin/intel64/ifort
-- Check for working Fortran compiler: /opt/software/ifort/2019.5.281-GCC-8.3.0/compilers_and_libraries_2019.5.281/linux/bin/intel64/ifort  -- works
-- Detecting Fortran compiler ABI info
-- Detecting Fortran compiler ABI info - done
-- Checking whether /opt/software/ifort/2019.5.281-GCC-8.3.0/compilers_and_libraries_2019.5.281/linux/bin/intel64/ifort supports Fortran 90
-- Checking whether /opt/software/ifort/2019.5.281-GCC-8.3.0/compilers_and_libraries_2019.5.281/linux/bin/intel64/ifort supports Fortran 90 -- yes
-- Configuring done
-- Generating done
-- Build files have been written to: /home/projectuser/cmake-hello/bld
$ cmake --build bld
Scanning dependencies of target helloworld
[ 33%] Building C object CMakeFiles/helloworld.dir/hello.c.o
[ 66%] Building Fortran object CMakeFiles/helloworld.dir/hello.F90.o
ifort: error #10236: File not found:  'realloc_lhs'
gmake[2]: *** [CMakeFiles/helloworld.dir/hello.F90.o] Error 1
gmake[2]: *** Deleting file `CMakeFiles/helloworld.dir/hello.F90.o'
gmake[1]: *** [CMakeFiles/helloworld.dir/all] Error 2
gmake: *** [all] Error 2

I get the error File not found: 'realloc_lhs'. Inspecting bld/CMakeFiles/helloworld.dir/flags.make reveals the flags used:

C_FLAGS =   -qno-opt-dynamic-align -fp-model precise -O2 -debug minimal
Fortran_FLAGS =   -qno-opt-dynamic-align -convert big_endian -assume byterecl -ftz -traceback realloc_lhs -fp-model source -O2 -debug minimal -free

I tried different variations of setting compiler flags and was able to successfully compile the project, but the compiler warnings suggest that some flags were not recognized.

  • CASE 2: Enclosed in quotes, space-separated

    • Syntax
      set(fflags "-qno-opt-dynamic-align -convert big_endian -assume byterecl -ftz -traceback -assume realloc_lhs -fp-model source -O2 -debug minimal -free")
      set(cflags "-qno-opt-dynamic-align -fp-model precise -O2 -debug minimal")
      
    • Result
      $ cmake --build bld
      [ 33%] Building C object CMakeFiles/helloworld.dir/hello.c.o
      icc: command line warning #10006: ignoring unknown option '-qno-opt-dynamic-align -fp-model precise -O2 -debug minimal'
      [ 66%] Building Fortran object CMakeFiles/helloworld.dir/hello.F90.o
      ifort: command line warning #10006: ignoring unknown option '-qno-opt-dynamic-align -convert big_endian -assume byterecl -ftz -traceback -assume realloc_lhs -fp-model source -O2 -debug minimal -free'
      [100%] Linking Fortran static library libhelloworld.a
      [100%] Built target helloworld
      
    • bld/CMakeFiles/helloworld.dir/flags.make
      C_FLAGS =   "-qno-opt-dynamic-align -fp-model precise -O2 -debug minimal"
      Fortran_FLAGS =   "-qno-opt-dynamic-align -convert big_endian -assume byterecl -ftz -traceback -assume realloc_lhs -fp-model source -O2 -debug minimal -free"
      
  • CASE 3: Enclosed in quotes, semicolon separated

    • Syntax
      set(fflags "-qno-opt-dynamic-align;-convert big_endian;-assume byterecl;-ftz;-traceback;-assume realloc_lhs;-fp-model source;-O2;-debug minimal;-free")
      set(cflags "-qno-opt-dynamic-align;-fp-model precise;-O2;-debug minimal")
      
    • Result
      $ cmake --build bld
      Scanning dependencies of target helloworld
      [ 33%] Building C object CMakeFiles/helloworld.dir/hello.c.o
      icc: command line warning #10159: invalid argument for option '-fp-model'
      icc: command line warning #10006: ignoring unknown option '-debug minimal'
      [ 66%] Building Fortran object CMakeFiles/helloworld.dir/hello.F90.o
      ifort: command line warning #10006: ignoring unknown option '-convert big_endian'
      ifort: command line warning #10006: ignoring unknown option '-assume byterecl'
      ifort: command line warning #10006: ignoring unknown option '-assume realloc_lhs'
      ifort: command line warning #10159: invalid argument for option '-fp-model'
      ifort: command line warning #10006: ignoring unknown option '-debug minimal'
      [100%] Linking Fortran static library libhelloworld.a
      [100%] Built target helloworld
      
    • bld/CMakeFiles/helloworld.dir/flags.make
      C_FLAGS =   -qno-opt-dynamic-align "-fp-model precise" -O2 "-debug minimal"`
      Fortran_FLAGS =   -qno-opt-dynamic-align "-convert big_endian" "-assume byterecl" -ftz -traceback "-assume realloc_lhs" "-fp-model source" -O2 "-debug minimal" -free`
      
  • CASE 4: Key-value pair options enclosed in quotes, space-separated

    • Syntax
      set(fflags -qno-opt-dynamic-align "-convert big_endian" "-assume byterecl" -ftz -traceback "-assume realloc_lhs" "-fp-model source" -O2 "-debug minimal" -free)
      set(cflags -qno-opt-dynamic-align "-fp-model precise" -O2 "-debug minimal")
      
    • Result: same as CASE 3
    • bld/CMakeFiles/helloworld.dir/flags.make: same as CASE 3

To summarize this post, how can I pass a compiler option of the form -key value to add_compiler_options (minimum CMake v3.14)? Note that I don’t wish to use target_compile_options since my goaI is to “enforce” compiler-dependent flags on all my targets.

If it helps, these are the Hello World programs that I used for testing.

  • C
    #include <stdio.h>
    
    int main() {
       printf("Hello World!");
       return 0;
    }
    
  • Fortran
    program hello
      print *, 'Hello, World!'
    end program hello
    

To pass “compound” options, you have to use SHELL: prefix (see add_compile_options, Arguments paragraph).

For example:

add_compile_options("SHELL:-assume realloc_lhs" "SHELL:-assume byterecl")

Thank you Marc! I can’t believe I missed this on the CMake docs (I’m drowned by a lot of docs that I need to look up for my project).

I would suggest to make this use case more transparent via an example instead of burying it in a block of text which is more likely to be overlooked. Anyway I’d also better RTFM more carefully next time; important thing this is now solved. Thanks again!

To complete this post, either of the methods below can successfully compile my project.

  1. All flags wrapped with "SHELL:"
set(fflags "SHELL:-qno-opt-dynamic-align -convert big_endian -assume byterecl -ftz -traceback -assume realloc_lhs -fp-model source -O2 -debug minimal -free")
set(cflags "SHELL:-qno-opt-dynamic-align -fp-model precise -O2 -debug minimal")
add_compile_options("$<$<COMPILE_LANGUAGE:Fortran>:${fflags}>")
add_compile_options("$<$<COMPILE_LANGUAGE:C>:${cflags}>")
  1. Compound flags separated from single-token flags and then wrapped with "SHELL:"
set(fflags -O2 -ftz -free -traceback -qno-opt-dynamic-align "SHELL:-convert big_endian -assume byterecl -assume realloc_lhs -fp-model source -debug minimal")
set(cflags -O2 -qno-opt-dynamic-align "SHELL:-fp-model precise -debug minimal")
add_compile_options("$<$<COMPILE_LANGUAGE:Fortran>:${fflags}>")
add_compile_options("$<$<COMPILE_LANGUAGE:C>:${cflags}>")

But option 1. is not a so good approach because it will prevent CMake to de-duplicate options.

Wouldn’t de-duplication take effect only when add_compile_options is used without the SHELL: prefix? My thought is once SHELL: is used then the compile line is treated as-is and no further post-processing is done on the flags.

Sorry I got confused, you meant that option 1 would prevent CMake to de-duplicate options.. I need to clarify then that my use case is only valid for multiple unique flags. I will update the accepted answer.

Not exactly. All options are treated for de-duplication even when SHELL: is used. The effect of SHELL: is to take is argument as one option and this option will be considered for de-duplication. For example, for "SHELL:-assume realloc_lhs", CMake will consider "-assume realloc_lhs" for de-duplication.

So specify all options as part of one SHELL: will have as result to consider all your options as one option for de-duplication step.

The effect of SHELL: is to take is argument as one option and this option will be considered for de-duplication.

Thanks for the clarification. IMO de-duplication under the context of SHELL: usage isn’t transparent in the docs. Anyway while de-duplication is useful there might be times when I don’t want my flags to be modified in any way, e.g. one case might be replicating compile warnings/errors from users of our software. I realize this could be already out of scope of add_compile_options and setting CMAKE_<LANG>_FLAGS instead might be better suited to this purpose.

Is there a way to make SHELL: work when the second argument is a full path containing spaces and needs embedded quotes to be handled correctly by the compiler argument processing code?

e.g. this doesn’t work:

add_compile_options(
    "SHELL:/analyze:ruleset \"${CFW_VS_RULESETS_PATH}/SecurityRules.ruleset\""
    "SHELL:/analyze:ruleset \"${CFW_VS_RULESETS_PATH}/NativeMinimumRules.ruleset\""
    "SHELL:/analyze:ruleset \"${CFW_VS_RULESETS_PATH}/NativeRecommendedRules.ruleset\""
)