55module CudaFormula
66 def self . included ( base )
77 base . class_eval do
8+ depends_on "patchelf" => :build
89 depends_on "cmake" => :test
910 depends_on :linux
11+ keg_only :versioned_formula
1012
1113 # Default livecheck - will be overridden by setup_livecheck if CUDA_VERSION is defined
1214 livecheck do
@@ -55,22 +57,73 @@ def install
5557 "--toolkit" ,
5658 "--toolkitpath=#{ libexec } "
5759
60+ prune_nonportable_payload
61+ patch_elf_rpaths
62+
5863 # NOTE: We do not symlink lib64 or include to avoid conflicts and hacks
5964 # Dependent formulae should use CMAKE_CUDA_TOOLKIT_ROOT_DIR=#{libexec}
6065 # or set CUDA_HOME=#{libexec} to find libraries and headers
6166
67+ library_paths = [
68+ "#{ libexec } /lib64" ,
69+ *Dir [ libexec /"extras/CUPTI/lib64" ] . map ( &:to_s ) ,
70+ *Dir [ libexec /"targets/*/lib" ] . map ( &:to_s ) ,
71+ ] . uniq
72+
6273 # Create wrapper scripts for CUDA binaries
6374 # This ensures they can find cuda_runtime.h, nvcc.profile, and other dependencies
75+ wrapper_bin = libexec /"homebrew/bin"
76+ wrapper_bin . mkpath
77+
6478 Dir [ libexec /"bin/*" ] . select { |f | File . file? ( f ) && File . executable? ( f ) } . each do |exe |
6579 binary_name = File . basename ( exe )
66- ( bin /binary_name ) . write <<~EOS
80+ ( wrapper_bin /binary_name ) . write <<~EOS
6781 #!/bin/bash
6882 export CUDA_HOME="#{ libexec } "
6983 export PATH="#{ libexec } /bin:$PATH"
70- export LD_LIBRARY_PATH="#{ libexec } /lib64 :$LD_LIBRARY_PATH"
84+ export LD_LIBRARY_PATH="#{ library_paths . join ( ":" ) } :$LD_LIBRARY_PATH"
7185 exec "#{ libexec } /bin/#{ binary_name } " "$@"
7286 EOS
73- chmod 0755 , bin /binary_name
87+ chmod 0755 , wrapper_bin /binary_name
88+ end
89+ end
90+
91+ def prune_nonportable_payload
92+ # These bundled GUI/profiler/debugger/GDS tools link host libraries that
93+ # Homebrew cannot validate as portable bottle dependencies.
94+ FileUtils . rm_rf ( Dir [ libexec /"nsight-compute-*" ] )
95+ FileUtils . rm_rf ( Dir [ libexec /"nsight-systems-*" ] )
96+ FileUtils . rm_rf ( libexec /"extras/demo_suite" )
97+ FileUtils . rm_rf ( libexec /"gds" )
98+ FileUtils . rm_rf ( libexec /"libnvvp" )
99+
100+ FileUtils . rm_f ( Dir [ libexec /"bin/{ctadvisor,cuda-gdb*,cuda-uninstaller,ncu,ncu-ui,nsight*,nsys,nsys-ui}" ] )
101+ FileUtils . rm_f ( Dir [ libexec /"bin/{nvprof,nvvp}" ] )
102+ FileUtils . rm_f ( Dir [ libexec /"pkgconfig/cuobjclient-*.pc" ] )
103+ FileUtils . rm_f ( Dir [ libexec /"targets/*/lib/lib{acc,cu}inj64*" ] )
104+ FileUtils . rm_f ( Dir [ libexec /"targets/*/lib/libcufile_rdma*" ] )
105+ FileUtils . rm_f ( Dir [ libexec /"targets/*/lib/libcuobjclient*" ] )
106+ end
107+
108+ def patch_elf_rpaths
109+ library_roots = [
110+ libexec /"lib64" ,
111+ libexec /"extras/CUPTI/lib64" ,
112+ *Dir [ libexec /"targets/*/lib" ] . map { |path | Pathname . new ( path ) } ,
113+ ] . select ( &:directory? )
114+
115+ Dir [ libexec /"**/*" ] . map { |path | Pathname . new ( path ) } . select ( &:file? ) . each do |file |
116+ next unless File . open ( file , "rb" ) { |f | f . read ( 4 ) == "\x7F ELF" . b }
117+
118+ file_dir = file . dirname
119+ rpaths = library_roots . filter_map do |root |
120+ relative_path = root . relative_path_from ( file_dir ) . to_s
121+ next "$ORIGIN" if relative_path == "."
122+
123+ "$ORIGIN/#{ relative_path } "
124+ end
125+
126+ system "patchelf" , "--set-rpath" , rpaths . unshift ( "$ORIGIN" ) . uniq . join ( ":" ) , file
74127 end
75128 end
76129
@@ -80,18 +133,18 @@ def caveats
80133 #{ opt_libexec }
81134
82135 Wrapper scripts for CUDA binaries are available at:
83- #{ opt_bin } /nvcc
136+ #{ opt_libexec } /homebrew/bin /nvcc
84137
85138 These wrappers automatically set CUDA_HOME for you.
86139
87140 For CMake projects (sunshine-beta example):
88141 cuda_path = Formula["lizardbyte/homebrew/cuda@13.1"]
89- args << "-DCMAKE_CUDA_COMPILER=\# {cuda_path.opt_bin} /nvcc"
142+ args << "-DCMAKE_CUDA_COMPILER=\# {cuda_path.opt_libexec}/homebrew/bin /nvcc"
90143 args << "-DCMAKE_CUDA_TOOLKIT_ROOT_DIR=\# {cuda_path.opt_libexec}"
91144
92145 For shell/manual configuration:
93146 export CUDA_HOME=#{ opt_libexec }
94- export PATH=#{ opt_bin } :$PATH
147+ export PATH=#{ opt_libexec } /homebrew/bin :$PATH
95148 export LD_LIBRARY_PATH=#{ opt_libexec } /lib64:$LD_LIBRARY_PATH
96149
97150 This formula only installs the CUDA Toolkit (compiler and libraries).
@@ -103,8 +156,11 @@ def caveats
103156 end
104157
105158 def test
159+ cuda_nvcc = libexec /"homebrew/bin/nvcc"
160+
106161 # Test that nvcc is available and can report its version
107- assert_match version . to_s , shell_output ( "#{ bin } /nvcc --version" )
162+ cuda_release = version . to_s [ /^\d +\. \d +/ ]
163+ assert_match "release #{ cuda_release } " , shell_output ( "#{ cuda_nvcc } --version" )
108164
109165 # Test compiling a simple CUDA program
110166 ( testpath /"test.cu" ) . write <<~EOS
@@ -121,7 +177,7 @@ def test
121177 EOS
122178
123179 # Compile the test program
124- system bin / "nvcc" , "test.cu" , "-o" , "test"
180+ system cuda_nvcc , "test.cu" , "-o" , "test"
125181 assert_path_exists testpath /"test"
126182
127183 # Test that CMake can find the CUDA toolkit
@@ -139,7 +195,7 @@ def test
139195 # Try to configure the CMake project
140196 # This will fail if CMake cannot find a working CUDA compiler
141197 system "cmake" , "-S" , testpath , "-B" , testpath /"build" ,
142- "-DCMAKE_CUDA_COMPILER=#{ bin } /nvcc "
198+ "-DCMAKE_CUDA_COMPILER=#{ cuda_nvcc } "
143199
144200 # Verify CMake found the CUDA toolkit
145201 assert_path_exists testpath /"build/CMakeCache.txt"
0 commit comments