CUDA as a language issues
I've been taking a significant package over to optionally using CUDA as a language in CMake 3.8+ (Windows support is not available for the package). When doing so, I ran into several issues. Here's a description of the process, then the issues.
To make the two procedures similar enough to support concurrently, I copied the FindCUDA module from CMake 3.9 into a git repository and changed the pair of absolute links to relative ones. Then, in my package, I optionally include the git submodule if CMake < 3.8. (It should work back as far as 3.6 or 3.4, maybe even further.) The reason I absolutely had to have 3.9 version? The ability to use PRIVATE
/PUBLIC
/INTERFACE
! I converted away from using global includes/etc anywhere, except for CXX_STANDARD
settings.
Then, I set up the new CUDA language. This is where the issues came up. Of course, it's already known, but the FindCUDA package is still needed to see if CUDA is available. (Edit: the VERSION of CUDA is available at CMAKE_CUDA_COMPILER_VERSION
), and to get the arch selection. The arch selection script should be modified to use the new language support (it almost is stand-alone), and then it could be wrapped with something like this:
function(CUDA_DETECT_ARCH)
set(oneValueArgs NVCC_FLAGS READABLE)
cmake_parse_arguments(
CDA
""
"${oneValueArgs}"
""
${ARGN})
cuda_select_nvcc_arch_flags(CDA_val)
if(NOT CDA_FLAGS STREQUAL "")
string(REPLACE ";" " " CDA_flags_val "${CDA_val}")
set(${CDA_NVCC_FLAGS} "${CDA_flags_val}" PARENT_SCOPE)
endif()
if(NOT CDA_READABLE STREQUAL "")
set(${CDA_READABLE} "${CDA_val_readable}" PARENT_SCOPE)
endif()
endfunction()
That way, it would still be easy to maintain CUDA ARCH names and versions in one place. This also fixes the output of cuda_select_nvcc_arch_flags
for CUDA as a language.
Second, unlike the older method, CUDA as a language has an awful time understanding compiler flags. If, for example, you have a flag on an INTERFACE target, it does not get wrapped with -Xcompiler
. A possible solution, and the one that was suggested at GTC 2017, was to use the COMPILE_LANGUAGE
generator expression. Which, according to the docs, does not work on Windows (Is it still impossible to fix that?).
Even when using this, most libraries, including most of the find_modules, are not set up for this. So, I use the following pair of macros to "fix" the targets for CUDA:
function(CUDA_CONVERT_FLAGS EXISTING_TARGET)
get_property(old_flags TARGET ${EXISTING_TARGET} PROPERTY INTERFACE_COMPILE_OPTIONS)
if(NOT "${old_flags}" STREQUAL "")
string(REPLACE ";" "," CUDA_flags "${old_flags}")
set_property(TARGET ${EXISTING_TARGET} PROPERTY INTERFACE_COMPILE_OPTIONS
"$<$<BUILD_INTERFACE:$<COMPILE_LANGUAGE:CXX>>:${old_flags}>$<$<BUILD_INTERFACE:$<COMPILE_LANGUAGE:CUDA>>:-Xcompiler=${CUDA_flags}>"
)
endif()
endfunction()
function(CUDA_PROTECT_FLAGS EXISTING_TARGET)
get_property(old_flags TARGET ${EXISTING_TARGET} PROPERTY INTERFACE_COMPILE_OPTIONS)
if(NOT "${old_flags}" STREQUAL "")
set_property(TARGET ${EXISTING_TARGET} PROPERTY INTERFACE_COMPILE_OPTIONS
"$<$<BUILD_INTERFACE:$<COMPILE_LANGUAGE:CXX>>:${old_flags}>"
)
endif()
endfunction()
But, this has a problem: if the target is an ALIAS target, it won't work. Is there a way to resolve an ALIAS target?
Wishlist for CUDA.cmake
-
CUDA detection -
CUDA ARCH detection script modified to support either form of CUDA -
CUDA ARCH detection wrapper with better output for CUDA language -
Windows compatibale target solution -
CONVERT/PROTECT flags solution -
Working on ALIAS (and IMPORTED?) targets
I've started an initial version of CUDA.cmake here. If you'd like to see the library's CMake, it's here. I haven't tackled fixing some the edge cases yet, like the MPI target with CUDA.