Module: Ignis::Platform

Defined in:
lib/nnw/platform.rb

Overview

Cross-platform detection and path resolution for Ignis.

Windows is the primary target (the whole point — Ruby HPC without Python’s 20-30% overhead). Linux support exists for multi-GPU bare-metal testing on Lambda / RunPod / Azure instances.

Usage:

Ignis::Platform.windows?          # => true on dev box
Ignis::Platform.cuda_lib('cudart') # => 'cudart64_130.dll' or 'libcudart.so.13'
Ignis::Platform.cuda_bin_path      # => 'C:/Program Files/.../bin' or '/usr/local/cuda/lib64'

Constant Summary collapse

CUDA_VERSION_MAJOR =

CUDA Paths


13
CUDA_VERSION_MINOR =
0
WIN_CUDA_ROOT =

Windows CUDA root

File.join('C:', 'Program Files', 'NVIDIA GPU Computing Toolkit', 'CUDA', "v#{CUDA_VERSION_MAJOR}.#{CUDA_VERSION_MINOR}").freeze
WIN_CUDA_BIN =
File.join(WIN_CUDA_ROOT, 'bin').freeze
LINUX_CUDA_ROOT =

Linux CUDA root (standard install or Lambda Stack)

"/usr/local/cuda-#{CUDA_VERSION_MAJOR}.#{CUDA_VERSION_MINOR}".freeze
LINUX_CUDA_LIB =
File.join(LINUX_CUDA_ROOT, 'lib64').freeze
LINUX_CUDA_ALT =
'/usr/local/cuda'.freeze
LINUX_CUDA_ALT_LIB =
File.join(LINUX_CUDA_ALT, 'lib64').freeze
WIN_DLL_SUBDIRS =

Candidate sub-directories that hold the CUDA redistributable DLLs under a toolkit root. CUDA 13 on Windows moved them from bin\ to binx64, so both must be probed.

[File.join('bin', 'x64'), 'bin'].freeze
WIN_LIB_PATTERNS =

Windows DLL patterns

{
  cuda_runtime: 'cudart64_*.dll',
  cublas:       'cublas64_*.dll',
  cublaslt:     'cublasLt64_*.dll',
  cufft:        'cufft64_*.dll',
  curand:       'curand64_*.dll',
  cusparse:     'cusparse64_*.dll',
  cusolver:     'cusolver64_*.dll',
  cudnn:        'cudnn64_*.dll',
  nvrtc:        'nvrtc64_*.dll',
  cutensor:     'cutensor.dll',
  cudss:        'cudss64_*.dll',
  mathdx:       'mathdx64_0.dll',
  cuda_driver:  'nvcuda.dll'
}.freeze
LINUX_LIB_PATTERNS =

Linux .so patterns

{
  cuda_runtime: 'libcudart.so*',
  cublas:       'libcublas.so*',
  cublaslt:     'libcublasLt.so*',
  cufft:        'libcufft.so*',
  curand:       'libcurand.so*',
  cusparse:     'libcusparse.so*',
  cusolver:     'libcusolver.so*',
  cudnn:        'libcudnn.so*',
  nvrtc:        'libnvrtc.so*',
  cutensor:     'libcutensor.so*',
  cudss:        'libcudss.so*',
  mathdx:       'libmathdx.so*',
  cuda_driver:  'libcuda.so*'
}.freeze

Class Method Summary collapse

Class Method Details

.cuda_bin_pathString

Returns CUDA binary/library directory for the current OS.

Returns:

  • (String)

    CUDA binary/library directory for the current OS



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/nnw/platform.rb', line 108

def self.cuda_bin_path
  if windows?
    win_cuda_bin
  else
    # Prefer versioned path, fall back to symlinked /usr/local/cuda
    if Dir.exist?(LINUX_CUDA_LIB)
      LINUX_CUDA_LIB
    elsif Dir.exist?(LINUX_CUDA_ALT_LIB)
      LINUX_CUDA_ALT_LIB
    else
      # Lambda Stack / system CUDA — libraries on LD_LIBRARY_PATH
      '/usr/lib/x86_64-linux-gnu'
    end
  end
end

.cuda_lib_pattern(key) ⇒ String

Maps library keys to their platform-specific filenames. Windows uses DLLs, Linux uses shared objects.

Parameters:

  • key (Symbol)

    library key

Returns:

  • (String)

    platform-specific glob pattern



142
143
144
145
146
147
148
# File 'lib/nnw/platform.rb', line 142

def self.cuda_lib_pattern(key)
  if windows?
    WIN_LIB_PATTERNS[key] || raise(ArgumentError, "Unknown CUDA library: #{key}")
  else
    LINUX_LIB_PATTERNS[key] || raise(ArgumentError, "Unknown CUDA library: #{key}")
  end
end

.cuda_rootString

Returns CUDA root directory.

Returns:

  • (String)

    CUDA root directory



125
126
127
128
129
130
131
# File 'lib/nnw/platform.rb', line 125

def self.cuda_root
  if windows?
    win_cuda_root
  else
    Dir.exist?(LINUX_CUDA_ROOT) ? LINUX_CUDA_ROOT : LINUX_CUDA_ALT
  end
end

.cudart_pathString

Resolve the CUDA runtime library path directly.

Returns:

  • (String)

    path to cudart



167
168
169
170
171
172
173
174
175
# File 'lib/nnw/platform.rb', line 167

def self.cudart_path
  if windows?
    found = find_cuda_lib(:cuda_runtime)
    found || File.join(WIN_CUDA_BIN, "cudart64_#{CUDA_VERSION_MAJOR}0.dll")
  else
    found = find_cuda_lib(:cuda_runtime)
    found || "libcudart.so.#{CUDA_VERSION_MAJOR}"
  end
end

.custom_lib_pathsHash{Symbol => String}

Returns custom search paths per library.

Returns:

  • (Hash{Symbol => String})

    custom search paths per library



192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/nnw/platform.rb', line 192

def self.custom_lib_paths
  if windows?
    {
      cutensor: 'C:/Program Files/NVIDIA cuTENSOR/v2.4/bin/13',
      cudss:    'C:/Program Files/NVIDIA cuDSS/v0.7/bin/13'
    }
  else
    {
      cutensor: '/usr/local/cutensor/lib',
      cudss:    '/usr/local/cudss/lib'
    }
  end
end

.find_cuda_lib(key) ⇒ String?

Resolve the full path to a specific CUDA library.

Parameters:

  • key (Symbol)

    library key (e.g. :cuda_runtime, :cublas)

Returns:

  • (String, nil)

    full path or nil



153
154
155
156
157
158
159
160
161
162
163
# File 'lib/nnw/platform.rb', line 153

def self.find_cuda_lib(key)
  pattern = cuda_lib_pattern(key)
  search_paths = cuda_search_paths(key)

  search_paths.each do |dir|
    matches = Dir.glob(File.join(dir, pattern))
    return matches.max if matches.any?
  end

  nil
end

.infoHash

Returns platform summary for debugging.

Returns:

  • (Hash)

    platform summary for debugging



233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/nnw/platform.rb', line 233

def self.info
  {
    os: os,
    ruby_platform: RUBY_PLATFORM,
    cuda_version: "#{CUDA_VERSION_MAJOR}.#{CUDA_VERSION_MINOR}",
    cuda_root: cuda_root,
    cuda_bin: cuda_bin_path,
    cudart: cudart_path,
    kernel32: kernel32_available?,
    shared_lib_ext: shared_lib_ext
  }
end

.kernel32_available?Boolean

Returns true if Kernel32 (Windows DLL path management) is available.

Returns:

  • (Boolean)

    true if Kernel32 (Windows DLL path management) is available



211
212
213
# File 'lib/nnw/platform.rb', line 211

def self.kernel32_available?
  windows?
end

.linux?Boolean

Returns true if running on Linux.

Returns:

  • (Boolean)

    true if running on Linux



25
26
27
# File 'lib/nnw/platform.rb', line 25

def self.linux?
  RUBY_PLATFORM.match?(/linux/i)
end

.macos?Boolean

Returns true if running on macOS (unlikely for GPU work).

Returns:

  • (Boolean)

    true if running on macOS (unlikely for GPU work)



30
31
32
# File 'lib/nnw/platform.rb', line 30

def self.macos?
  RUBY_PLATFORM.match?(/darwin/i)
end

.osSymbol

Returns :windows, :linux, or :macos.

Returns:

  • (Symbol)

    :windows, :linux, or :macos



35
36
37
38
39
40
41
42
43
44
45
# File 'lib/nnw/platform.rb', line 35

def self.os
  if windows?
    :windows
  elsif linux?
    :linux
  elsif macos?
    :macos
  else
    :unknown
  end
end

.path_separatorString

Returns path separator for the OS.

Returns:

  • (String)

    path separator for the OS



178
179
180
# File 'lib/nnw/platform.rb', line 178

def self.path_separator
  windows? ? ';' : ':'
end

.shared_lib_extString

Returns shared library extension.

Returns:

  • (String)

    shared library extension



183
184
185
# File 'lib/nnw/platform.rb', line 183

def self.shared_lib_ext
  windows? ? '.dll' : '.so'
end

.win_cuda_binString

Resolve the directory that actually contains the CUDA redistributable DLLs (binx64 on CUDA 13, bin on older layouts). Memoized.

Returns:

  • (String)


99
100
101
102
103
104
105
# File 'lib/nnw/platform.rb', line 99

def self.win_cuda_bin
  @win_cuda_bin ||= begin
    root = win_cuda_root
    sub = WIN_DLL_SUBDIRS.find { |s| Dir.glob(File.join(root, s, 'cudart64_*.dll')).any? }
    File.join(root, sub || 'bin')
  end
end

.win_cuda_rootString

Discover the installed Windows CUDA toolkit root.

The compiled-in v#MAJOR.#MINOR default is only a last resort: real installs vary (v13.0 / v13.1 / v13.2 / …). We prefer the CUDA_PATH env var set by the NVIDIA installer, then the highest-versioned vX.Y directory that actually contains a CUDA runtime DLL. Memoized.

Returns:

  • (String)


76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/nnw/platform.rb', line 76

def self.win_cuda_root
  @win_cuda_root ||= begin
    candidates = []
    env_root = ENV['CUDA_PATH']
    candidates << env_root if env_root && !env_root.empty?

    base = File.join('C:', 'Program Files', 'NVIDIA GPU Computing Toolkit', 'CUDA')
    versioned = Dir.glob(File.join(base, 'v*')).select { |d| File.directory?(d) }
    versioned = versioned.sort_by do |d|
      (m = d.match(/v(\d+)\.(\d+)/)) ? [m[1].to_i, m[2].to_i] : [0, 0]
    end.reverse # highest version first (v13.2 before v13.0)
    candidates.concat(versioned)

    chosen = candidates.find do |root|
      WIN_DLL_SUBDIRS.any? { |sub| Dir.glob(File.join(root, sub, 'cudart64_*.dll')).any? }
    end
    chosen || WIN_CUDA_ROOT
  end
end

.windows?Boolean

Returns true if running on Windows.

Returns:

  • (Boolean)

    true if running on Windows



20
21
22
# File 'lib/nnw/platform.rb', line 20

def self.windows?
  RUBY_PLATFORM.match?(/mswin|mingw|cygwin/i) || (defined?(FFI) && FFI::Platform::IS_WINDOWS)
end

.wnais_native_libString

Returns expected path to WNAIS native AOT binary.

Returns:

  • (String)

    expected path to WNAIS native AOT binary



220
221
222
223
224
225
226
# File 'lib/nnw/platform.rb', line 220

def self.wnais_native_lib
  if windows?
    'Wnais.Core.dll'
  else
    'Wnais.Core.so'
  end
end