]> git.djapps.eu Git - pkg/ggml/sources/whisper.cpp/commitdiff
ruby : handle build options on installation (#3206)
authorKITAITI Makoto <redacted>
Thu, 29 May 2025 16:32:49 +0000 (01:32 +0900)
committerGitHub <redacted>
Thu, 29 May 2025 16:32:49 +0000 (01:32 +0900)
* Don't pass empty string to cmake command

* Refactor Dependencies

* Use found cmake path for options

* Maintain extsources.rb

* List dependent files by directory separator agnostic way

* Prepend whitespace before '='

* Handle build options on install

* Remove useless test

* Retrieve gem file name and version from spec file

* Bump version to 1.3.3

* Update date

* Add install option examples

* [skip ci]Remove unused module

bindings/ruby/README.md
bindings/ruby/ext/dependencies.rb
bindings/ruby/ext/extconf.rb
bindings/ruby/ext/options.rb
bindings/ruby/ext/ruby_whisper_vad_params.c
bindings/ruby/extsources.rb
bindings/ruby/tests/helper.rb
bindings/ruby/tests/test_package.rb
bindings/ruby/whispercpp.gemspec

index 208a89f32cf8b877c6fadc9733e0aecec85aed16..6de00fb275be082918c29e620cbd701821f78b99 100644 (file)
@@ -24,7 +24,21 @@ or,
 
     $ gem install whispercpp -- --enable-ggml-cuda
 
-See whisper.cpp's [README](https://github.com/ggml-org/whisper.cpp/blob/master/README.md) for available options. You need convert options present the README to Ruby-style options.  
+See whisper.cpp's [README](https://github.com/ggml-org/whisper.cpp/blob/master/README.md) for available options. You need convert options present the README to Ruby-style options, for example:
+
+Boolean options:
+
+* `-DGGML_BLAS=1` -> `--enable-ggml-blas`
+* `-DWHISER_COREML=OFF` -> `--disable-whisper-coreml`
+
+Argument options:
+
+* `-DGGML_CUDA_COMPRESSION_MODE=size` -> `--ggml-cuda-compression-mode=size`
+
+Combination:
+
+* `-DGGML_CUDA=1 -DCMAKE_CUDA_ARCHITECTURES="86"` -> `--enable-ggml-cuda --cmake_cuda-architectures="86"`
+
 For boolean options like `GGML_CUDA`, the README says `-DGGML_CUDA=1`. You need strip `-D`, prepend `--enable-` for `1` or `ON` (`--disable-` for `0` or `OFF`) and make it kebab-case: `--enable-ggml-cuda`.  
 For options which require arguments like `CMAKE_CUDA_ARCHITECTURES`, the README says `-DCMAKE_CUDA_ARCHITECTURES="86"`. You need strip `-D`, prepend `--`, make it kebab-case, append `=` and append argument: `--cmake-cuda-architectures="86"`.
 
index 9beb128c3ad6cda70203b5e27011da2c577f9411..79e325c9067df54e325d25f737b81b9de095c273 100644 (file)
@@ -1,16 +1,28 @@
 require "tsort"
 
 class Dependencies
+  include TSort
+
   def initialize(cmake, options)
     @cmake = cmake
     @options = options
+    @static_lib_shape = nil
+    @nodes = {}
+    @graph = Hash.new {|h, k| h[k] = []}
 
     generate_dot
-    @libs = parse_dot
+    parse_dot
+  end
+
+  def libs
+    tsort.filter_map {|node|
+      label, shape = @nodes[node]
+      shape == @static_lib_shape ? label : nil
+    }.reverse.collect {|lib| "lib#{lib}.a"}
   end
 
   def to_s
-    @libs.join(" ")
+    libs.join(" ")
   end
 
   private
@@ -20,42 +32,38 @@ class Dependencies
   end
 
   def generate_dot
-    system @cmake, "-S", "sources", "-B", "build", "--graphviz", dot_path, "-D", "BUILD_SHARED_LIBS=OFF", @options.to_s, exception: true
+    args = ["-S", "sources", "-B", "build", "--graphviz", dot_path, "-D", "BUILD_SHARED_LIBS=OFF"]
+    args << @options.to_s unless @options.to_s.empty?
+    system @cmake, *args, exception: true
   end
 
   def parse_dot
-    static_lib_shape = nil
-    nodes = {}
-    depends = Hash.new {|h, k| h[k] = []}
-
-    class << depends
-      include TSort
-      alias tsort_each_node each_key
-      def tsort_each_child(node, &block)
-        fetch(node, []).each(&block)
-      end
-    end
-
     File.open(dot_path).each_line do |line|
       case line
       when /\[\s*label\s*=\s*"Static Library"\s*,\s*shape\s*=\s*(?<shape>\w+)\s*\]/
-        static_lib_shape = $~[:shape]
+        @static_lib_shape = $~[:shape]
       when /\A\s*"(?<node>\w+)"\s*\[\s*label\s*=\s*"(?<label>\S+)"\s*,\s*shape\s*=\s*(?<shape>\w+)\s*\]\s*;\s*\z/
         node = $~[:node]
         label = $~[:label]
         shape = $~[:shape]
-        nodes[node] = [label, shape]
+        @nodes[node] = [label, shape]
       when /\A\s*"(?<depender>\w+)"\s*->\s*"(?<dependee>\w+)"/
         depender = $~[:depender]
         dependee = $~[:dependee]
-        depends[depender] ||= []
-        depends[depender] << dependee
+        @graph[depender] << dependee
       end
     end
-    depends.tsort.filter_map {|node|
-      label, shape = nodes[node]
-      shape == static_lib_shape ? label : nil
-    }.collect {|lib| "lib#{lib}.a"}
-      .reverse
+  end
+
+  def tsort_each_node
+    @nodes.each_key do |node|
+      yield node
+    end
+  end
+
+  def tsort_each_child(node)
+    @graph[node].each do |child|
+      yield child
+    end
   end
 end
index 53e2e1857fd92f62db387d60c1909ea7fa86c001..edb7b82ff97c738f24af720827398369259dabf9 100644 (file)
@@ -3,7 +3,7 @@ require_relative "options"
 require_relative "dependencies"
 
 cmake = find_executable("cmake") || abort
-options = Options.new
+options = Options.new(cmake)
 have_library("gomp") rescue nil
 libs = Dependencies.new(cmake, options)
 
index 09f27e41b90bee580c985dec4d7f9bf56142540d..46408ae8722a1d5d6b489b9a1e2a269f4f529622 100644 (file)
@@ -1,25 +1,11 @@
 class Options
-  def initialize
+  def initialize(cmake="cmake")
+    @cmake = cmake
     @options = {}
-    @pending_options = []
-    @ignored_options = []
 
     configure
   end
 
-  def help
-    @options
-      .collect_concat {|name, (type, value)|
-        option = option_name(name)
-        if type == :bool
-          ["--enable-#{option}", "--disable-#{option}"]
-        else
-          "--#{option}=#{type.upcase}"
-        end
-      }
-      .join($/)
-  end
-
   def to_s
     @options
       .reject {|name, (type, value)| value.nil?}
@@ -32,7 +18,7 @@ class Options
 
     output = nil
     Dir.chdir __dir__ do
-      output = `cmake -S sources -B build -L`
+      output = `#{@cmake.shellescape} -S sources -B build -L`
     end
     started = false
     @cmake_options = output.lines.filter_map {|line|
@@ -47,175 +33,17 @@ class Options
     }
   end
 
-  def missing_options
-    cmake_options.collect {|name, type, value| name} -
-      @options.keys - @pending_options - @ignored_options
-  end
-
-  def extra_options
-    @options.keys + @pending_options + @ignored_options -
-      cmake_options.collect {|name, type, value| name}
-  end
-
   private
 
   def configure
-    filepath "ACCELERATE_FRAMEWORK"
-    ignored "BUILD_SHARED_LIBS"
-    ignored "BUILD_TESTING"
-    ignored "CMAKE_BUILD_TYPE"
-    ignored "CMAKE_INSTALL_PREFIX"
-    string "CMAKE_OSX_ARCHITECTURES"
-    ignored "CMAKE_OSX_DEPLOYMENT_TARGET"
-    string "CMAKE_OSX_SYSROOT"
-    filepath "FOUNDATION_LIBRARY"
-    bool "GGML_ACCELERATE"
-    bool "GGML_ALL_WARNINGS_3RD_PARTY"
-    bool "GGML_AMX_BF16"
-    bool "GGML_AMX_INT8"
-    bool "GGML_AMX_TILE"
-    bool "GGML_AVX"
-    bool "GGML_AVX2"
-    bool "GGML_AVX512"
-    bool "GGML_AVX512_BF16"
-    bool "GGML_AVX512_VBMI"
-    bool "GGML_AVX512_VNNI"
-    bool "GGML_AVX_VNNI"
-    ignored "GGML_BACKEND_DL"
-    ignored "GGML_BIN_INSTALL_DIR"
-    bool "GGML_BLAS"
-    string "GGML_BLAS_VENDOR"
-    bool "GGML_BMI2"
-    ignored "GGML_BUILD_EXAMPLES"
-    ignored "GGML_BUILD_TESTS"
-    bool "GGML_CCACHE"
-    filepath "GGML_CCACHE_FOUND"
-    bool "GGML_CPU"
-    bool "GGML_CPU_AARCH64"
-    ignored "GGML_CPU_ALL_VARIANTS"
-    string "GGML_CPU_ARM_ARCH"
-    bool "GGML_CPU_HBM"
-    bool "GGML_CPU_KLEIDIAI"
-    string "GGML_CPU_POWERPC_CPUTYPE"
-    bool "GGML_CUDA"
-    string "GGML_CUDA_COMPRESSION_MODE"
-    bool "GGML_CUDA_F16"
-    bool "GGML_CUDA_FA"
-    bool "GGML_CUDA_FA_ALL_QUANTS"
-    bool "GGML_CUDA_FORCE_CUBLAS"
-    bool "GGML_CUDA_FORCE_MMQ"
-    ignored "GGML_CUDA_GRAPHS"
-    bool "GGML_CUDA_NO_PEER_COPY"
-    bool "GGML_CUDA_NO_VMM"
-    string "GGML_CUDA_PEER_MAX_BATCH_SIZE"
-    bool "GGML_F16C"
-    bool "GGML_FMA"
-    bool "GGML_GPROF"
-    bool "GGML_HIP"
-    bool "GGML_HIP_GRAPHS"
-    bool "GGML_HIP_NO_VMM"
-    bool "GGML_HIP_ROCWMMA_FATTN"
-    ignored "GGML_INCLUDE_INSTALL_DIR"
-    bool "GGML_KOMPUTE"
-    bool "GGML_LASX"
-    ignored "GGML_LIB_INSTALL_DIR"
-    ignored "GGML_LLAMAFILE"
-    bool "GGML_LSX"
-    bool "GGML_LTO"
-    bool "GGML_METAL"
-    bool "GGML_METAL_EMBED_LIBRARY"
-    string "GGML_METAL_MACOSX_VERSION_MIN"
-    bool "GGML_METAL_NDEBUG"
-    bool "GGML_METAL_SHADER_DEBUG"
-    string "GGML_METAL_STD"
-    bool "GGML_METAL_USE_BF16"
-    bool "GGML_MUSA"
-    bool "GGML_NATIVE"
-    bool "GGML_OPENCL"
-    bool "GGML_OPENCL_EMBED_KERNELS"
-    bool "GGML_OPENCL_PROFILING"
-    string "GGML_OPENCL_TARGET_VERSION"
-    bool "GGML_OPENCL_USE_ADRENO_KERNELS"
-    bool "GGML_OPENMP"
-    bool "GGML_RPC"
-    bool "GGML_RVV"
-    bool "GGML_RV_ZFH"
-    pending "GGML_SCCACHE_FOUND"
-    string "GGML_SCHED_MAX_COPIES"
-    bool "GGML_SSE42"
-    ignored "GGML_STATIC"
-    bool "GGML_SYCL"
-    string "GGML_SYCL_DEVICE_ARCH"
-    bool "GGML_SYCL_F16"
-    bool "GGML_SYCL_GRAPH"
-    string "GGML_SYCL_TARGET"
-    bool "GGML_SYCL_DNN"
-    bool "GGML_VULKAN"
-    bool "GGML_VULKAN_CHECK_RESULTS"
-    bool "GGML_VULKAN_DEBUG"
-    bool "GGML_VULKAN_MEMORY_DEBUG"
-    bool "GGML_VULKAN_PERF"
-    ignored "GGML_VULKAN_RUN_TESTS"
-    filepath "GGML_VULKAN_SHADERS_GEN_TOOLCHAIN"
-    bool "GGML_VULKAN_SHADER_DEBUG_INFO"
-    pending "GGML_VULKAN_VALIDATE"
-    bool "GGML_VXE"
-    bool "GGML_XTHEADVECTOR"
-    filepath "GIT_EXE"
-    filepath "MATH_LIBRARY"
-    filepath "METALKIT_FRAMEWORK"
-    filepath "METAL_FRAMEWORK"
-    bool "WHISPER_ALL_WARNINGS"
-    bool "WHISPER_ALL_WARNINGS_3RD_PARTY"
-    ignored "WHISPER_BIN_INSTALL_DIR"
-    ignored "WHISPER_BUILD_EXAMPLES"
-    ignored "WHISPER_BUILD_SERVER"
-    ignored"WHISPER_BUILD_TESTS"
-    bool "WHISPER_COREML"
-    bool "WHISPER_COREML_ALLOW_FALLBACK"
-    ignored "WHISPER_CURL"
-    bool "WHISPER_FATAL_WARNINGS"
-    ignored "WHISPER_FFMPEG"
-    ignored "WHISPER_INCLUDE_INSTALL_DIR"
-    ignored "WHISPER_LIB_INSTALL_DIR"
-    bool "WHISPER_OPENVINO"
-    bool "WHISPER_SANITIZE_ADDRESS"
-    bool "WHISPER_SANITIZE_THREAD"
-    bool "WHISPER_SANITIZE_UNDEFINED"
-    ignored "WHISPER_SDL2"
-    pending "WHISPER_USE_SYSTEM_GGML"
+    cmake_options.each do |name, type, default_value|
+      option = option_name(name)
+      value = type == "BOOL" ? enable_config(option) : arg_config("--#{option}")
+      @options[name] = [type, value]
+    end
   end
 
   def option_name(name)
     name.downcase.gsub("_", "-")
   end
-
-  def bool(name)
-    option = option_name(name)
-    value = enable_config(option)
-    @options[name] = [:bool, value]
-  end
-
-  def string(name, type=:string)
-    option = "--#{option_name(name)}"
-    value = arg_config(option)
-    raise "String expected for #{option}" if value == true || value&.empty?
-    @options[name] = [type, value]
-  end
-
-  def path(name)
-    string(name, :path)
-  end
-
-  def filepath(name)
-    string(name, :filepath)
-  end
-
-  def pending(name)
-    @pending_options << name
-  end
-
-  def ignored(name)
-    @ignored_options << name
-  end
 end
index be7bc465b1337d9574d7cdd3b529745589f36e18..f254bfa2138781a58e1c387d8f63b1748df7c525 100644 (file)
@@ -249,7 +249,7 @@ ruby_whisper_vad_params_initialize(int argc, VALUE *argv, VALUE self)
   rb_get_kwargs(kw_hash, param_names, 0, NUM_PARAMS, values);
 
   for (i = 0; i < NUM_PARAMS; i++) {
-    id= param_names[i];
+    id = param_names[i];
     value = values[i];
     if (value == Qundef) {
       continue;
index 08c479b61313e59084a4121f26391631f7b73946..18ae348d7100cc31f263294e2ec9f6348a9fdb50 100644 (file)
@@ -1,5 +1,10 @@
+require "pathname"
+
+root = Pathname("..")/".."
 ignored_dirs = %w[
   .devops
+  .github
+  ci
   examples/wchess/wchess.wasm
   examples/whisper.android
   examples/whisper.android.java
@@ -9,7 +14,7 @@ ignored_dirs = %w[
   models
   samples
   scripts
-]
+].collect {|dir| root/dir}
 ignored_files = %w[
   AUTHORS
   Makefile
@@ -17,18 +22,19 @@ ignored_files = %w[
   README_sycl.md
   .gitignore
   .gitmodules
+  .dockerignore
   whisper.nvim
   twitch.sh
   yt-wsp.sh
+  close-issue.yml
 ]
 
 EXTSOURCES =
-  `git ls-files -z ../..`.split("\x0")
-    .select {|file|
-      basename = File.basename(file)
-
-      ignored_dirs.all? {|dir| !file.start_with?("../../#{dir}")} &&
-        !ignored_files.include?(basename) &&
-        (file.start_with?("../..") || file.start_with?("../javascript")) &&
-        (!file.start_with?("../../.github/") || basename == "bindings-ruby.yml")
+  `git ls-files -z #{root}`.split("\x0")
+    .collect {|file| Pathname(file)}
+    .reject {|file|
+      ignored_dirs.any? {|dir| file.descend.any? {|desc| desc == dir}} ||
+        ignored_files.include?(file.basename.to_path) ||
+        (file.descend.to_a[1] != root && file.descend.to_a[1] != Pathname("..")/"javascript")
     }
+    .collect(&:to_path)
index bc5e472456500456e1bcb010f2a299ba8aa59461..389e15c9c155b92dd65062378a5a6d8e3751c6c6 100644 (file)
@@ -21,15 +21,4 @@ class TestBase < Test::Unit::TestCase
   def whisper
     self.class.whisper
   end
-
-  module BuildOptions
-    load "ext/options.rb", self
-    Options.include self
-
-    def enable_config(name)
-    end
-
-    def arg_config(name)
-    end
-  end
 end
index 00ab0a684a43b81d20bdba7fe7280fab29f9659a..be0bbe8766772a711cce8e303bda4799b14720c5 100644 (file)
@@ -18,12 +18,10 @@ class TestPackage < TestBase
     end
 
     def test_install
-      match_data = `rake -Tbuild`.match(/(whispercpp-(.+)\.gem)/)
-      filename = match_data[1]
-      version = match_data[2]
+      gemspec = Gem::Specification.load("whispercpp.gemspec")
       Dir.mktmpdir do |dir|
-        system "gem", "install", "--install-dir", dir.shellescape, "--no-document", "pkg/#{filename.shellescape}", exception: true
-        assert_installed dir, version
+        system "gem", "install", "--install-dir", dir.shellescape, "--no-document", "pkg/#{gemspec.file_name.shellescape}", exception: true
+        assert_installed dir, gemspec.version
       end
     end
 
@@ -35,12 +33,4 @@ class TestPackage < TestBase
       assert_path_not_exist File.join(dir, "gems/whispercpp-#{version}/ext/build")
     end
   end
-
-  def test_build_options
-    options = BuildOptions::Options.new
-    assert_empty options.missing_options
-    if ENV["TEST_EXTRA_OPTIONS"] == "1"
-      assert_empty options.extra_options
-    end
-  end
 end
index 44a0cb9c7ecee375d13cbaa6fc03578912020a15..59d654824aebd6d4ca38bd929fc9177d66b38e14 100644 (file)
@@ -3,8 +3,8 @@ require_relative "extsources"
 Gem::Specification.new do |s|
   s.name    = "whispercpp"
   s.authors = ["Georgi Gerganov", "Todd A. Fisher"]
-  s.version = '1.3.2'
-  s.date    = '2025-05-11'
+  s.version = '1.3.3'
+  s.date    = '2025-05-29'
   s.description = %q{High-performance inference of OpenAI's Whisper automatic speech recognition (ASR) model via Ruby}
   s.email   = 'todd.fisher@gmail.com'
   s.extra_rdoc_files = ['LICENSE', 'README.md']