Edd Inglis
Edd Inglis

Reputation: 1105

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?

Upvotes: 7

Views: 3801

Answers (3)

Amit
Amit

Reputation: 39

I stumbeled similar issue, and what worked for me was

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

set(cxx_warning_flags "-Wall -Wextra") # ... maybe others
string(JOIN ", " cxx_warning_flags_for_cuda_cc_compiler "${cxx_warning_flags}")
string(REPLACE ";" "," cxx_warning_flags_for_cuda_cc_compiler "${cxx_warning_flags_for_cuda_cc_compiler}")

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

This will work for every CXX compiler flags which are separated by spaces. If you have a CXX flag which contains the , character, the CUDA compiler will parse it incorrectly, for that you can use \\,. For example,

set(cxx_warning_flags_for_cuda_cc_compiler "-Wall,-Wextra,-Wl\\,lib1\\,lib2\\,lib3")

Upvotes: 0

ff00005
ff00005

Reputation: 1

Does NVCC_PREPEND_FLAGS="<your nvcc flags here>" <CMake invocation> (or integrating it directly into CMake) do the trick?

I'm slightly inconfident my solution applies to your problem, it looks too complex, but AFAICT you're facing the same issue of g++ and nvcc understanding different flags and wanting to target flags at nvcc directly.

Upvotes: 0

Edd Inglis
Edd Inglis

Reputation: 1105

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.

Upvotes: 7

Related Questions