Search code examples
cmakecudanvcc

CMake: pass list of compiler flags through NVCC


I am trying to compile some CUDA and I wish to display compiler warnings. Equivalent to:

g++ fish.cpp -Wall -Wextra

Except NVCC doesn't understand these, and you have to pass them through:

nvcc fish.cu --compiler-options -Wall --compiler-options -Wextra
nvcc fish.cu --compiler-options "-Wall -Wextra"

(I favour the latter form, but ultimately, it doesn't really matter.)

Given this CMakeLists.txt (a very cut-down example):

cmake_minimum_required(VERSION 3.9)
project(test_project LANGUAGES CUDA CXX)

list(APPEND cxx_warning_flags "-Wall" "-Wextra") # ... maybe others

add_compile_options("$<$<COMPILE_LANGUAGE:CUDA>:--compiler-options ${cxx_warning_flags}>")
add_executable(test_cuda fish.cu)

But this expands to:

nvcc "--compiler-options  -Wall" -Wextra   ...

which is obviously wrong. (Omitting the quotes around the generator expression just lands us in broken expansion hell.)

... skip ahead several thousand iterations of Monte Carlo programming ...

I've arrived at this gem:

set( temp ${cxx_warning_flags} )
string (REPLACE ";" " " temp "${temp}")
set( temp2 "--compiler-options \"${temp}\"" )
message( "${temp2}" )

which prints out the encouraging-looking

--compiler-options "-Wall -Wextra"

But then

add_compile_options("$<$<COMPILE_LANGUAGE:CUDA>:${temp2}>")

expands to:

nvcc "--compiler-options \"-Wall -Wextra\""   ...

I'm at a loss; am I onto a dead end here? Or have I missed some crucial combination of punctuation?


Solution

  • I'm answering my own question, since I've found a few solutions that work, but I'm still interested to hear if there is a better (read: cleaner, more canonical) way.

    TL;DR:

    foreach(flag IN LISTS cxx_warning_flags)
        add_compile_options("$<$<COMPILE_LANGUAGE:CUDA>:--compiler-options=${flag}>")
    endforeach()
    

    Blow-by-blow account:

    I tried this:

    foreach(flag IN LISTS cxx_warning_flags)
        add_compile_options("$<$<COMPILE_LANGUAGE:CUDA>:--compiler-options ${flag}>")
    endforeach()
    

    but that still gives

    nvcc  "--compiler-options -Wall" "--compiler-options -Wextra"   
    nvcc fatal   : Unknown option '-compiler-options -Wall'
    

    Adding in a temporary however:

    foreach(flag IN LISTS cxx_warning_flags)
        set( temp --compiler-options ${flag}) # no quotes
        add_compile_options("$<$<COMPILE_LANGUAGE:CUDA>:${temp}>")
    endforeach()
    

    Gives a new outcome:

    nvcc  --compiler-options -Wall -Wextra   ...
    nvcc fatal   : Unknown option 'Wextra'
    

    What I assume is happening here is that CMake is combining the repeated --compiler-options flags, but I'm just speculating.

    So, I tried eliminating the spaces using an equals:

    foreach(flag IN LISTS cxx_warning_flags)
        add_compile_options("$<$<COMPILE_LANGUAGE:CUDA>:--compiler-options=${flag}>")
    endforeach()
    

    Hurrah! We have a winner:

    nvcc  --compiler-options=-Wall --compiler-options=-Wextra  ...
    

    Epilogue:

    Can we do it without the loop?

    add_compile_options("$<$<COMPILE_LANGUAGE:CUDA>:--compiler-options=${cxx_warning_flags}>")
    

    doesn't work (--compiler-options=-Wall -Wextra), but:

    string (REPLACE ";" " " temp "${cxx_warning_flags}")
    add_compile_options("$<$<COMPILE_LANGUAGE:CUDA>:--compiler-options=${temp}>")
    

    does work ("--compiler-options=-Wall -Wextra").

    I'm slightly surprised about this last option, but I guess it makes sense. On balance, I think the looping option is clearest in its intention.


    EDIT: In Confusing flags passed to MSVC through NVCC with CMake, I spent a lot of time discovering that it might be better to use:

    add_compile_options("$<$<COMPILE_LANGUAGE:CUDA>:-Xcompiler=${flag}>")
    

    since CMake appears to do some rationalisation of flags to remove duplicates and ambiguity, but does not realise that --compiler-options is the same as its favoured -Xcompiler.