@@ -77,6 +77,96 @@ def __init__(self):
7777 os .makedirs (self .llama_dir , exist_ok = True )
7878 self ._cached_cuda_architectures : Optional [str ] = None
7979
80+ def _check_cuda_toolkit_available (self ) -> Tuple [bool , Optional [str ], Optional [str ]]:
81+ """
82+ Check if CUDA Toolkit is available on the system.
83+
84+ Returns:
85+ Tuple of (is_available, cuda_root, error_message)
86+ - is_available: True if CUDA Toolkit is found
87+ - cuda_root: Path to CUDA root directory if found, None otherwise
88+ - error_message: Error message if not available, None otherwise
89+ """
90+ env = os .environ .copy ()
91+ possible_cuda_roots = [
92+ env .get ("CUDA_PATH" ),
93+ env .get ("CUDA_HOME" ),
94+ "/usr/local/cuda" ,
95+ "/usr/local/cuda-12.9" ,
96+ "/usr/local/cuda-12.8" ,
97+ "/usr/local/cuda-12.7" ,
98+ "/usr/local/cuda-12.6" ,
99+ "/usr/local/cuda-12.5" ,
100+ "/usr/local/cuda-12.4" ,
101+ "/usr/local/cuda-12.3" ,
102+ "/usr/local/cuda-12.2" ,
103+ "/usr/local/cuda-12.1" ,
104+ "/usr/local/cuda-12.0" ,
105+ "/usr/local/cuda-11.9" ,
106+ "/usr/local/cuda-11.8" ,
107+ ]
108+
109+ # Filter out None values and check if paths exist
110+ for cuda_root in possible_cuda_roots :
111+ if not cuda_root or not os .path .exists (cuda_root ):
112+ continue
113+
114+ # Check for nvcc compiler
115+ nvcc_path = os .path .join (cuda_root , "bin" , "nvcc" )
116+ if not os .path .exists (nvcc_path ):
117+ # On Windows, nvcc might be in a different location
118+ if os .name == 'nt' :
119+ nvcc_path = os .path .join (cuda_root , "bin" , "nvcc.exe" )
120+ if not os .path .exists (nvcc_path ):
121+ continue
122+ else :
123+ continue
124+
125+ # Check for CUDA libraries
126+ lib_dirs = ["lib64" , "lib" ]
127+ has_libs = False
128+ for lib_dir in lib_dirs :
129+ lib_path = os .path .join (cuda_root , lib_dir )
130+ if os .path .exists (lib_path ):
131+ # Check for at least one CUDA library file
132+ try :
133+ lib_files = os .listdir (lib_path )
134+ if any ("cudart" in f or "cublas" in f or "curand" in f for f in lib_files ):
135+ has_libs = True
136+ break
137+ except OSError :
138+ pass
139+
140+ if has_libs :
141+ return (True , cuda_root , None )
142+
143+ # Try to find nvcc in PATH as a fallback
144+ try :
145+ result = subprocess .run (
146+ ["nvcc" , "--version" ],
147+ capture_output = True ,
148+ text = True ,
149+ timeout = 5
150+ )
151+ if result .returncode == 0 :
152+ # nvcc found in PATH, try to determine CUDA root
153+ nvcc_path = shutil .which ("nvcc" )
154+ if nvcc_path :
155+ # nvcc is typically in <CUDA_ROOT>/bin/nvcc
156+ potential_root = os .path .dirname (os .path .dirname (nvcc_path ))
157+ if os .path .exists (potential_root ):
158+ return (True , potential_root , None )
159+ except (subprocess .TimeoutExpired , FileNotFoundError , OSError ):
160+ pass
161+
162+ error_msg = (
163+ "CUDA Toolkit not found. Please either:\n "
164+ "1. Install CUDA Toolkit from https://developer.nvidia.com/cuda-downloads\n "
165+ "2. Set CUDA_PATH environment variable to your CUDA installation directory\n "
166+ "3. Disable CUDA in build configuration (set enable_cuda: false)"
167+ )
168+ return (False , None , error_msg )
169+
80170 async def _detect_cuda_architectures (self ) -> Optional [str ]:
81171 """
82172 Determine CUDA architectures for the current environment by querying GPU capabilities.
@@ -757,6 +847,39 @@ async def build_source(self, commit_sha: str, patches: List[str] = None, build_c
757847 build_config .enable_backend_dl = True
758848 build_config .normalize ()
759849
850+ # Validate CUDA Toolkit availability if CUDA is enabled
851+ if build_config .enable_cuda :
852+ cuda_available , cuda_root , cuda_error = self ._check_cuda_toolkit_available ()
853+ if not cuda_available :
854+ # Check if CUDA installer is available
855+ try :
856+ from backend .cuda_installer import get_cuda_installer
857+ installer = get_cuda_installer ()
858+ installer_status = installer .status ()
859+ if not installer_status .get ("installed" ):
860+ error_msg = (
861+ f"CUDA build requested but CUDA Toolkit not found.\n \n "
862+ f"{ cuda_error } \n \n "
863+ f"You can install CUDA Toolkit using the CUDA installer in the LlamaCpp Manager, "
864+ f"or install it manually from https://developer.nvidia.com/cuda-downloads"
865+ )
866+ else :
867+ error_msg = f"CUDA build requested but CUDA Toolkit not found.\n \n { cuda_error } "
868+ except ImportError :
869+ error_msg = f"CUDA build requested but CUDA Toolkit not found.\n \n { cuda_error } "
870+
871+ logger .error (error_msg )
872+ if websocket_manager and task_id :
873+ await websocket_manager .send_build_progress (
874+ task_id = task_id ,
875+ stage = "configure" ,
876+ progress = 60 ,
877+ message = "CUDA Toolkit validation failed" ,
878+ log_lines = [error_msg ]
879+ )
880+ raise Exception (error_msg )
881+ logger .info (f"CUDA Toolkit found at: { cuda_root } " )
882+
760883 # Build CMake arguments
761884 cmake_args = ["cmake" , ".." ]
762885
@@ -818,35 +941,44 @@ def set_flag(flag: str, value: bool):
818941
819942 # Ensure CUDA toolchain paths are available when CUDA build requested
820943 if build_config .enable_cuda :
821- possible_cuda_roots = [
822- env .get ("CUDA_PATH" ),
823- "/usr/local/cuda" ,
824- "/usr/local/cuda-12.9"
825- ]
826- cuda_root = next (
827- (root for root in possible_cuda_roots if root and os .path .exists (root )),
828- None ,
829- )
830-
944+ # Use the validated CUDA root from earlier check
945+ cuda_available , cuda_root , _ = self ._check_cuda_toolkit_available ()
946+
831947 if cuda_root :
832- nvcc_path = os .path .join (cuda_root , "bin" , "nvcc" )
948+ # Set CUDA_PATH for CMake
949+ env ["CUDA_PATH" ] = cuda_root
950+ if not env .get ("CUDA_HOME" ):
951+ env ["CUDA_HOME" ] = cuda_root
952+
953+ # Set CUDACXX if nvcc is found
954+ nvcc_name = "nvcc.exe" if os .name == 'nt' else "nvcc"
955+ nvcc_path = os .path .join (cuda_root , "bin" , nvcc_name )
833956 if os .path .exists (nvcc_path ) and not env .get ("CUDACXX" ):
834957 env ["CUDACXX" ] = nvcc_path
958+
835959 # Ensure PATH includes CUDA bin for CMake detection
836960 cuda_bin = os .path .join (cuda_root , "bin" )
837961 current_path = env .get ("PATH" , "" )
838962 path_entries = [entry for entry in current_path .split (os .pathsep ) if entry ]
839963 if cuda_bin not in path_entries :
840964 env ["PATH" ] = os .pathsep .join ([cuda_bin ] + path_entries ) if current_path else cuda_bin
965+
841966 # Ensure LD_LIBRARY_PATH includes CUDA libs (Linux)
842- for lib_dir in ("lib64" , "lib" ):
843- full_dir = os .path .join (cuda_root , lib_dir )
844- if os .path .exists (full_dir ):
845- current_ld = env .get ("LD_LIBRARY_PATH" , "" )
846- ld_paths = [entry for entry in current_ld .split (os .pathsep ) if entry ]
847- if full_dir not in ld_paths :
848- ld_paths .insert (0 , full_dir )
849- env ["LD_LIBRARY_PATH" ] = os .pathsep .join (ld_paths )
967+ # On Windows, PATH is used for DLLs
968+ if os .name != 'nt' :
969+ for lib_dir in ("lib64" , "lib" ):
970+ full_dir = os .path .join (cuda_root , lib_dir )
971+ if os .path .exists (full_dir ):
972+ current_ld = env .get ("LD_LIBRARY_PATH" , "" )
973+ ld_paths = [entry for entry in current_ld .split (os .pathsep ) if entry ]
974+ if full_dir not in ld_paths :
975+ ld_paths .insert (0 , full_dir )
976+ env ["LD_LIBRARY_PATH" ] = os .pathsep .join (ld_paths )
977+
978+ logger .info (f"Configured CUDA environment: CUDA_PATH={ cuda_root } , CUDACXX={ env .get ('CUDACXX' , 'not set' )} " )
979+ else :
980+ # This shouldn't happen if validation passed, but handle it gracefully
981+ logger .warning ("CUDA was validated but root path not found during CMake configuration" )
850982
851983 cmake_process = await asyncio .create_subprocess_exec (
852984 * cmake_args ,
@@ -864,7 +996,23 @@ def set_flag(flag: str, value: bool):
864996 if cmake_process .returncode != 0 :
865997 error_msg = cmake_stderr .decode ().strip ()
866998 logger .warning (f"CMake configuration failed: { error_msg } " )
867- raise Exception (f"CMake configuration failed: { error_msg } " )
999+
1000+ # Provide more helpful error messages for CUDA-related failures
1001+ if build_config .enable_cuda and ("CUDA" in error_msg .upper () or "cuda" in error_msg .lower ()):
1002+ enhanced_error = (
1003+ f"CMake configuration failed with CUDA error:\n \n { error_msg } \n \n "
1004+ "This usually means:\n "
1005+ "1. CUDA Toolkit is not properly installed\n "
1006+ "2. CUDA_PATH environment variable is not set correctly\n "
1007+ "3. CMake cannot find CUDA compiler (nvcc)\n \n "
1008+ "To fix this:\n "
1009+ "- Install CUDA Toolkit from https://developer.nvidia.com/cuda-downloads\n "
1010+ "- Set CUDA_PATH to your CUDA installation directory\n "
1011+ "- Or disable CUDA in build configuration (set enable_cuda: false)"
1012+ )
1013+ raise Exception (enhanced_error )
1014+ else :
1015+ raise Exception (f"CMake configuration failed: { error_msg } " )
8681016
8691017 logger .info ("CMake configuration completed successfully" )
8701018
0 commit comments