Merge topic 'CUDAToolkit-no-nvcc'

7cc815a2a6 CUDAToolkit: Detect CUDA SDK that don't have nvcc

Acked-by: Kitware Robot <kwrobot@kitware.com>
Tested-by: Raul Tambre <raul@tambre.ee>
Reviewed-by: Raul Tambre <raul@tambre.ee>
Merge-request: !5061
diff --git a/Help/release/dev/FindCUDAToolkit-no-nvcc.rst b/Help/release/dev/FindCUDAToolkit-no-nvcc.rst
new file mode 100644
index 0000000..e815876
--- /dev/null
+++ b/Help/release/dev/FindCUDAToolkit-no-nvcc.rst
@@ -0,0 +1,5 @@
+FindCUDAToolkit-no-nvcc
+-----------------------
+
+* The :module:`FindCUDAToolkit` module gained support for finding CUDA toolkits
+  that do not contain ``nvcc``.
diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake
index e3acc3f..1054f60 100644
--- a/Modules/FindCUDAToolkit.cmake
+++ b/Modules/FindCUDAToolkit.cmake
@@ -14,8 +14,7 @@
 Search Behavior
 ^^^^^^^^^^^^^^^
 
-Finding the CUDA Toolkit requires finding the ``nvcc`` executable, which is
-searched for in the following order:
+The CUDA Toolkit search behavior uses the following order:
 
 1. If the ``CUDA`` language has been enabled we will use the directory
    containing the compiler as the first search location for ``nvcc``.
@@ -26,13 +25,12 @@
    configuration variable are specified, the *configuration* variable takes
    precedence.
 
-   The directory specified here must be such that the executable ``nvcc`` can be
-   found underneath the directory specified by ``CUDAToolkit_ROOT``.  If
-   ``CUDAToolkit_ROOT`` is specified, but no ``nvcc`` is found underneath, this
-   package is marked as **not** found.  No subsequent search attempts are
-   performed.
+   The directory specified here must be such that the executable ``nvcc`` or
+   the appropriate ``version.txt`` file can be found underneath the specified
+   directory.
 
-3. If the CUDA_PATH environment variable is defined, it will be searched.
+3. If the CUDA_PATH environment variable is defined, it will be searched
+   for ``nvcc``.
 
 4. The user's path is searched for ``nvcc`` using :command:`find_program`.  If
    this is found, no subsequent search attempts are performed.  Users are
@@ -404,7 +402,7 @@
 
 ``CUDAToolkit_VERSION``
     The exact version of the CUDA Toolkit found (as reported by
-    ``nvcc --version``).
+    ``nvcc --version`` or ``version.txt``).
 
 ``CUDAToolkit_VERSION_MAJOR``
     The major version of the CUDA Toolkit.
@@ -434,7 +432,7 @@
 ``CUDAToolkit_TARGET_DIR``
     The path to the CUDA Toolkit directory including the target architecture
     when cross-compiling. When not cross-compiling this will be equivalent to
-    ``CUDAToolkit_ROOT_DIR``.
+    the parent directory of ``CUDAToolkit_BIN_DIR``.
 
 ``CUDAToolkit_NVCC_EXECUTABLE``
     The path to the NVIDIA CUDA compiler ``nvcc``.  Note that this path may
@@ -493,31 +491,81 @@
   set(CUDAToolkit_BIN_DIR "${CUDAToolkit_ROOT_DIR}/bin")
   set(CUDAToolkit_NVCC_EXECUTABLE "${CUDAToolkit_BIN_DIR}/nvcc${CMAKE_EXECUTABLE_SUFFIX}")
 else()
+
+  function(_CUDAToolkit_find_root_dir )
+    cmake_parse_arguments(arg "" "" "SEARCH_PATHS;FIND_FLAGS" ${ARGN})
+
+
+    if(NOT CUDAToolkit_BIN_DIR)
+      if(NOT CUDAToolkit_SENTINEL_FILE)
+        find_program(CUDAToolkit_NVCC_EXECUTABLE
+          NAMES nvcc nvcc.exe
+          PATHS ${arg_SEARCH_PATHS}
+          ${arg_FIND_FLAGS}
+        )
+      endif()
+
+      if(NOT CUDAToolkit_NVCC_EXECUTABLE)
+        find_file(CUDAToolkit_SENTINEL_FILE
+          NAMES version.txt
+          PATHS ${arg_SEARCH_PATHS}
+          NO_DEFAULT_PATH
+        )
+      endif()
+
+      if(CUDAToolkit_NVCC_EXECUTABLE)
+        get_filename_component(CUDAToolkit_BIN_DIR "${CUDAToolkit_NVCC_EXECUTABLE}" DIRECTORY)
+
+        set(CUDAToolkit_BIN_DIR "${CUDAToolkit_BIN_DIR}" CACHE PATH "" FORCE)
+        mark_as_advanced(CUDAToolkit_BIN_DIR)
+      elseif(CUDAToolkit_SENTINEL_FILE)
+        get_filename_component(CUDAToolkit_BIN_DIR ${CUDAToolkit_SENTINEL_FILE} DIRECTORY ABSOLUTE)
+        set(CUDAToolkit_BIN_DIR "${CUDAToolkit_BIN_DIR}/bin")
+
+        set(CUDAToolkit_BIN_DIR "${CUDAToolkit_BIN_DIR}" CACHE PATH "" FORCE)
+        mark_as_advanced(CUDAToolkit_BIN_DIR)
+      endif()
+    endif()
+
+    if(CUDAToolkit_BIN_DIR)
+      get_filename_component(CUDAToolkit_ROOT_DIR ${CUDAToolkit_BIN_DIR} DIRECTORY ABSOLUTE)
+      set(CUDAToolkit_ROOT_DIR "${CUDAToolkit_ROOT_DIR}" PARENT_SCOPE)
+    endif()
+
+  endfunction()
+
+  function(_CUDAToolkit_find_version_file result_variable)
+    # We first check for a non-scattered installation to prefer it over a scattered installation.
+    if(CUDAToolkit_ROOT AND EXISTS "${CUDAToolkit_ROOT}/version.txt")
+      set(${result_variable} "${CUDAToolkit_ROOT}/version.txt" PARENT_SCOPE)
+    elseif(CUDAToolkit_ROOT_DIR AND EXISTS "${CUDAToolkit_ROOT_DIR}/version.txt")
+      set(${result_variable} "${CUDAToolkit_ROOT_DIR}/version.txt" PARENT_SCOPE)
+    elseif(CMAKE_SYSROOT_LINK AND EXISTS "${CMAKE_SYSROOT_LINK}/usr/lib/cuda/version.txt")
+      set(${result_variable} "${CMAKE_SYSROOT_LINK}/usr/lib/cuda/version.txt" PARENT_SCOPE)
+    elseif(EXISTS "${CMAKE_SYSROOT}/usr/lib/cuda/version.txt")
+      set(${result_variable} "${CMAKE_SYSROOT}/usr/lib/cuda/version.txt" PARENT_SCOPE)
+    endif()
+  endfunction()
+
   # For NVCC we can easily deduce the SDK binary directory from the compiler path.
   if(CMAKE_CUDA_COMPILER_LOADED AND NOT CUDAToolkit_BIN_DIR AND CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA")
     get_filename_component(CUDAToolkit_BIN_DIR "${CMAKE_CUDA_COMPILER}" DIRECTORY)
     set(CUDAToolkit_BIN_DIR "${CUDAToolkit_BIN_DIR}" CACHE PATH "")
+    # Try language provided path first.
+    _CUDAToolkit_find_root_dir(SEARCH_PATHS "${CUDAToolkit_BIN_DIR}" FIND_FLAGS NO_DEFAULT_PATH)
     mark_as_advanced(CUDAToolkit_BIN_DIR)
   endif()
 
-  # Try language- or user-provided path first.
-  if(CUDAToolkit_BIN_DIR)
-    find_program(CUDAToolkit_NVCC_EXECUTABLE
-      NAMES nvcc nvcc.exe
-      PATHS ${CUDAToolkit_BIN_DIR}
-      NO_DEFAULT_PATH
-    )
+  # Try user provided path
+  if(NOT CUDAToolkit_ROOT_DIR AND CUDAToolkit_ROOT)
+    _CUDAToolkit_find_root_dir(SEARCH_PATHS "${CUDAToolkit_ROOT}" FIND_FLAGS PATH_SUFFIXES bin NO_DEFAULT_PATH)
+  endif()
+  if(NOT CUDAToolkit_ROOT_DIR)
+    _CUDAToolkit_find_root_dir(FIND_FLAGS PATHS "ENV CUDA_PATH" PATH_SUFFIXES bin)
   endif()
 
-  # Search using CUDAToolkit_ROOT
-  find_program(CUDAToolkit_NVCC_EXECUTABLE
-    NAMES nvcc nvcc.exe
-    PATHS ENV CUDA_PATH
-    PATH_SUFFIXES bin
-  )
-
-  # If the user specified CUDAToolkit_ROOT but nvcc could not be found, this is an error.
-  if(NOT CUDAToolkit_NVCC_EXECUTABLE AND (DEFINED CUDAToolkit_ROOT OR DEFINED ENV{CUDAToolkit_ROOT}))
+  # If the user specified CUDAToolkit_ROOT but the toolkit could not be found, this is an error.
+  if(NOT CUDAToolkit_ROOT_DIR AND (DEFINED CUDAToolkit_ROOT OR DEFINED ENV{CUDAToolkit_ROOT}))
     # Declare error messages now, print later depending on find_package args.
     set(fail_base "Could not find nvcc executable in path specified by")
     set(cuda_root_fail "${fail_base} CUDAToolkit_ROOT=${CUDAToolkit_ROOT}")
@@ -554,7 +602,7 @@
   # We will also search the default symlink location /usr/local/cuda first since
   # if CUDAToolkit_ROOT is not specified, it is assumed that the symlinked
   # directory is the desired location.
-  if(NOT CUDAToolkit_NVCC_EXECUTABLE)
+  if(NOT CUDAToolkit_ROOT_DIR)
     if(UNIX)
       if(NOT APPLE)
         set(platform_base "/usr/local/cuda-")
@@ -591,12 +639,8 @@
       list(INSERT search_paths 0 "/usr/local/cuda")
     endif()
 
-    # Now search for nvcc again using the platform default search paths.
-    find_program(CUDAToolkit_NVCC_EXECUTABLE
-      NAMES nvcc nvcc.exe
-      PATHS ${search_paths}
-      PATH_SUFFIXES bin
-    )
+    # Now search for the toolkit again using the platform default search paths.
+    _CUDAToolkit_find_root_dir(SEARCH_PATHS "${search_paths}" FIND_FLAGS PATH_SUFFIXES bin)
 
     # We are done with these variables now, cleanup for caller.
     unset(platform_base)
@@ -604,7 +648,7 @@
     unset(versions)
     unset(search_paths)
 
-    if(NOT CUDAToolkit_NVCC_EXECUTABLE)
+    if(NOT CUDAToolkit_ROOT_DIR)
       if(CUDAToolkit_FIND_REQUIRED)
         message(FATAL_ERROR "Could not find nvcc, please set CUDAToolkit_ROOT.")
       elseif(NOT CUDAToolkit_FIND_QUIETLY)
@@ -616,24 +660,12 @@
     endif()
   endif()
 
-  if(NOT CUDAToolkit_BIN_DIR AND CUDAToolkit_NVCC_EXECUTABLE)
-    get_filename_component(CUDAToolkit_BIN_DIR "${CUDAToolkit_NVCC_EXECUTABLE}" DIRECTORY)
-    set(CUDAToolkit_BIN_DIR "${CUDAToolkit_BIN_DIR}" CACHE PATH "" FORCE)
-    mark_as_advanced(CUDAToolkit_BIN_DIR)
+  _CUDAToolkit_find_version_file( _CUDAToolkit_version_file )
+  if(_CUDAToolkit_version_file)
+    # CUDAToolkit_LIBRARY_ROOT contains the device library and version file.
+    get_filename_component(CUDAToolkit_LIBRARY_ROOT "${_CUDAToolkit_version_file}" DIRECTORY ABSOLUTE)
   endif()
-
-  get_filename_component(CUDAToolkit_ROOT_DIR ${CUDAToolkit_BIN_DIR} DIRECTORY ABSOLUTE)
-
-  # CUDAToolkit_LIBRARY_ROOT contains the device library and version file.
-  # In a non-scattered installation this is equivalent to CUDAToolkit_ROOT_DIR.
-  # We first check for a non-scattered installation to prefer it over a scattered installation.
-  if(EXISTS "${CUDAToolkit_ROOT_DIR}/version.txt")
-    set(CUDAToolkit_LIBRARY_ROOT "${CUDAToolkit_ROOT_DIR}")
-  elseif(CMAKE_SYSROOT_LINK AND EXISTS "${CMAKE_SYSROOT_LINK}/usr/lib/cuda/version.txt")
-    set(CUDAToolkit_LIBRARY_ROOT "${CMAKE_SYSROOT_LINK}/usr/lib/cuda")
-  elseif(EXISTS "${CMAKE_SYSROOT}/usr/lib/cuda/version.txt")
-    set(CUDAToolkit_LIBRARY_ROOT "${CMAKE_SYSROOT}/usr/lib/cuda")
-  endif()
+  unset(_CUDAToolkit_version_file)
 endif()
 
 # Handle cross compilation
@@ -695,7 +727,7 @@
     set(CUDAToolkit_VERSION_PATCH "${CMAKE_MATCH_3}")
     set(CUDAToolkit_VERSION "${CMAKE_CUDA_COMPILER_VERSION}")
   endif()
-else()
+elseif(CUDAToolkit_NVCC_EXECUTABLE)
   # Compute the version by invoking nvcc
   execute_process(COMMAND ${CUDAToolkit_NVCC_EXECUTABLE} "--version" OUTPUT_VARIABLE NVCC_OUT)
   if(NVCC_OUT MATCHES [=[ V([0-9]+)\.([0-9]+)\.([0-9]+)]=])
@@ -705,6 +737,17 @@
     set(CUDAToolkit_VERSION  "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}")
   endif()
   unset(NVCC_OUT)
+else()
+  _CUDAToolkit_find_version_file(version_file)
+  if(version_file)
+    file(READ "${version_file}" VERSION_INFO)
+    if(VERSION_INFO MATCHES [=[CUDA Version ([0-9]+)\.([0-9]+)\.([0-9]+)]=])
+      set(CUDAToolkit_VERSION_MAJOR "${CMAKE_MATCH_1}")
+      set(CUDAToolkit_VERSION_MINOR "${CMAKE_MATCH_2}")
+      set(CUDAToolkit_VERSION_PATCH "${CMAKE_MATCH_3}")
+      set(CUDAToolkit_VERSION  "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}")
+    endif()
+  endif()
 endif()
 
 # Find the CUDA Runtime Library libcudart
@@ -721,7 +764,6 @@
   message(STATUS "Unable to find cudart library.")
 endif()
 
-unset(CUDAToolkit_ROOT_DIR)
 if(_CUDAToolkit_Pop_Prefix)
   list(REMOVE_AT CMAKE_PREFIX_PATH -1)
   unset(_CUDAToolkit_Pop_Prefix)
@@ -734,13 +776,16 @@
   REQUIRED_VARS
     CUDAToolkit_INCLUDE_DIR
     CUDA_CUDART
-    CUDAToolkit_NVCC_EXECUTABLE
+    CUDAToolkit_BIN_DIR
   VERSION_VAR
     CUDAToolkit_VERSION
 )
+
+unset(CUDAToolkit_ROOT_DIR)
 mark_as_advanced(CUDA_CUDART
                  CUDAToolkit_INCLUDE_DIR
                  CUDAToolkit_NVCC_EXECUTABLE
+                 CUDAToolkit_SENTINEL_FILE
                  )
 
 #-----------------------------------------------------------------------------