Skip to content

Commit e5e900d

Browse files
ruby : handle build options on installation (#3206)
* 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
1 parent 4d18e52 commit e5e900d

File tree

9 files changed

+77
-242
lines changed

9 files changed

+77
-242
lines changed

bindings/ruby/README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,21 @@ or,
2424

2525
$ gem install whispercpp -- --enable-ggml-cuda
2626

27-
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.
27+
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:
28+
29+
Boolean options:
30+
31+
* `-DGGML_BLAS=1` -> `--enable-ggml-blas`
32+
* `-DWHISER_COREML=OFF` -> `--disable-whisper-coreml`
33+
34+
Argument options:
35+
36+
* `-DGGML_CUDA_COMPRESSION_MODE=size` -> `--ggml-cuda-compression-mode=size`
37+
38+
Combination:
39+
40+
* `-DGGML_CUDA=1 -DCMAKE_CUDA_ARCHITECTURES="86"` -> `--enable-ggml-cuda --cmake_cuda-architectures="86"`
41+
2842
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`.
2943
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"`.
3044

bindings/ruby/ext/dependencies.rb

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
require "tsort"
22

33
class Dependencies
4+
include TSort
5+
46
def initialize(cmake, options)
57
@cmake = cmake
68
@options = options
9+
@static_lib_shape = nil
10+
@nodes = {}
11+
@graph = Hash.new {|h, k| h[k] = []}
712

813
generate_dot
9-
@libs = parse_dot
14+
parse_dot
15+
end
16+
17+
def libs
18+
tsort.filter_map {|node|
19+
label, shape = @nodes[node]
20+
shape == @static_lib_shape ? label : nil
21+
}.reverse.collect {|lib| "lib#{lib}.a"}
1022
end
1123

1224
def to_s
13-
@libs.join(" ")
25+
libs.join(" ")
1426
end
1527

1628
private
@@ -20,42 +32,38 @@ def dot_path
2032
end
2133

2234
def generate_dot
23-
system @cmake, "-S", "sources", "-B", "build", "--graphviz", dot_path, "-D", "BUILD_SHARED_LIBS=OFF", @options.to_s, exception: true
35+
args = ["-S", "sources", "-B", "build", "--graphviz", dot_path, "-D", "BUILD_SHARED_LIBS=OFF"]
36+
args << @options.to_s unless @options.to_s.empty?
37+
system @cmake, *args, exception: true
2438
end
2539

2640
def parse_dot
27-
static_lib_shape = nil
28-
nodes = {}
29-
depends = Hash.new {|h, k| h[k] = []}
30-
31-
class << depends
32-
include TSort
33-
alias tsort_each_node each_key
34-
def tsort_each_child(node, &block)
35-
fetch(node, []).each(&block)
36-
end
37-
end
38-
3941
File.open(dot_path).each_line do |line|
4042
case line
4143
when /\[\s*label\s*=\s*"Static Library"\s*,\s*shape\s*=\s*(?<shape>\w+)\s*\]/
42-
static_lib_shape = $~[:shape]
44+
@static_lib_shape = $~[:shape]
4345
when /\A\s*"(?<node>\w+)"\s*\[\s*label\s*=\s*"(?<label>\S+)"\s*,\s*shape\s*=\s*(?<shape>\w+)\s*\]\s*;\s*\z/
4446
node = $~[:node]
4547
label = $~[:label]
4648
shape = $~[:shape]
47-
nodes[node] = [label, shape]
49+
@nodes[node] = [label, shape]
4850
when /\A\s*"(?<depender>\w+)"\s*->\s*"(?<dependee>\w+)"/
4951
depender = $~[:depender]
5052
dependee = $~[:dependee]
51-
depends[depender] ||= []
52-
depends[depender] << dependee
53+
@graph[depender] << dependee
5354
end
5455
end
55-
depends.tsort.filter_map {|node|
56-
label, shape = nodes[node]
57-
shape == static_lib_shape ? label : nil
58-
}.collect {|lib| "lib#{lib}.a"}
59-
.reverse
56+
end
57+
58+
def tsort_each_node
59+
@nodes.each_key do |node|
60+
yield node
61+
end
62+
end
63+
64+
def tsort_each_child(node)
65+
@graph[node].each do |child|
66+
yield child
67+
end
6068
end
6169
end

bindings/ruby/ext/extconf.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
require_relative "dependencies"
44

55
cmake = find_executable("cmake") || abort
6-
options = Options.new
6+
options = Options.new(cmake)
77
have_library("gomp") rescue nil
88
libs = Dependencies.new(cmake, options)
99

bindings/ruby/ext/options.rb

Lines changed: 8 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,11 @@
11
class Options
2-
def initialize
2+
def initialize(cmake="cmake")
3+
@cmake = cmake
34
@options = {}
4-
@pending_options = []
5-
@ignored_options = []
65

76
configure
87
end
98

10-
def help
11-
@options
12-
.collect_concat {|name, (type, value)|
13-
option = option_name(name)
14-
if type == :bool
15-
["--enable-#{option}", "--disable-#{option}"]
16-
else
17-
"--#{option}=#{type.upcase}"
18-
end
19-
}
20-
.join($/)
21-
end
22-
239
def to_s
2410
@options
2511
.reject {|name, (type, value)| value.nil?}
@@ -32,7 +18,7 @@ def cmake_options
3218

3319
output = nil
3420
Dir.chdir __dir__ do
35-
output = `cmake -S sources -B build -L`
21+
output = `#{@cmake.shellescape} -S sources -B build -L`
3622
end
3723
started = false
3824
@cmake_options = output.lines.filter_map {|line|
@@ -47,175 +33,17 @@ def cmake_options
4733
}
4834
end
4935

50-
def missing_options
51-
cmake_options.collect {|name, type, value| name} -
52-
@options.keys - @pending_options - @ignored_options
53-
end
54-
55-
def extra_options
56-
@options.keys + @pending_options + @ignored_options -
57-
cmake_options.collect {|name, type, value| name}
58-
end
59-
6036
private
6137

6238
def configure
63-
filepath "ACCELERATE_FRAMEWORK"
64-
ignored "BUILD_SHARED_LIBS"
65-
ignored "BUILD_TESTING"
66-
ignored "CMAKE_BUILD_TYPE"
67-
ignored "CMAKE_INSTALL_PREFIX"
68-
string "CMAKE_OSX_ARCHITECTURES"
69-
ignored "CMAKE_OSX_DEPLOYMENT_TARGET"
70-
string "CMAKE_OSX_SYSROOT"
71-
filepath "FOUNDATION_LIBRARY"
72-
bool "GGML_ACCELERATE"
73-
bool "GGML_ALL_WARNINGS_3RD_PARTY"
74-
bool "GGML_AMX_BF16"
75-
bool "GGML_AMX_INT8"
76-
bool "GGML_AMX_TILE"
77-
bool "GGML_AVX"
78-
bool "GGML_AVX2"
79-
bool "GGML_AVX512"
80-
bool "GGML_AVX512_BF16"
81-
bool "GGML_AVX512_VBMI"
82-
bool "GGML_AVX512_VNNI"
83-
bool "GGML_AVX_VNNI"
84-
ignored "GGML_BACKEND_DL"
85-
ignored "GGML_BIN_INSTALL_DIR"
86-
bool "GGML_BLAS"
87-
string "GGML_BLAS_VENDOR"
88-
bool "GGML_BMI2"
89-
ignored "GGML_BUILD_EXAMPLES"
90-
ignored "GGML_BUILD_TESTS"
91-
bool "GGML_CCACHE"
92-
filepath "GGML_CCACHE_FOUND"
93-
bool "GGML_CPU"
94-
bool "GGML_CPU_AARCH64"
95-
ignored "GGML_CPU_ALL_VARIANTS"
96-
string "GGML_CPU_ARM_ARCH"
97-
bool "GGML_CPU_HBM"
98-
bool "GGML_CPU_KLEIDIAI"
99-
string "GGML_CPU_POWERPC_CPUTYPE"
100-
bool "GGML_CUDA"
101-
string "GGML_CUDA_COMPRESSION_MODE"
102-
bool "GGML_CUDA_F16"
103-
bool "GGML_CUDA_FA"
104-
bool "GGML_CUDA_FA_ALL_QUANTS"
105-
bool "GGML_CUDA_FORCE_CUBLAS"
106-
bool "GGML_CUDA_FORCE_MMQ"
107-
ignored "GGML_CUDA_GRAPHS"
108-
bool "GGML_CUDA_NO_PEER_COPY"
109-
bool "GGML_CUDA_NO_VMM"
110-
string "GGML_CUDA_PEER_MAX_BATCH_SIZE"
111-
bool "GGML_F16C"
112-
bool "GGML_FMA"
113-
bool "GGML_GPROF"
114-
bool "GGML_HIP"
115-
bool "GGML_HIP_GRAPHS"
116-
bool "GGML_HIP_NO_VMM"
117-
bool "GGML_HIP_ROCWMMA_FATTN"
118-
ignored "GGML_INCLUDE_INSTALL_DIR"
119-
bool "GGML_KOMPUTE"
120-
bool "GGML_LASX"
121-
ignored "GGML_LIB_INSTALL_DIR"
122-
ignored "GGML_LLAMAFILE"
123-
bool "GGML_LSX"
124-
bool "GGML_LTO"
125-
bool "GGML_METAL"
126-
bool "GGML_METAL_EMBED_LIBRARY"
127-
string "GGML_METAL_MACOSX_VERSION_MIN"
128-
bool "GGML_METAL_NDEBUG"
129-
bool "GGML_METAL_SHADER_DEBUG"
130-
string "GGML_METAL_STD"
131-
bool "GGML_METAL_USE_BF16"
132-
bool "GGML_MUSA"
133-
bool "GGML_NATIVE"
134-
bool "GGML_OPENCL"
135-
bool "GGML_OPENCL_EMBED_KERNELS"
136-
bool "GGML_OPENCL_PROFILING"
137-
string "GGML_OPENCL_TARGET_VERSION"
138-
bool "GGML_OPENCL_USE_ADRENO_KERNELS"
139-
bool "GGML_OPENMP"
140-
bool "GGML_RPC"
141-
bool "GGML_RVV"
142-
bool "GGML_RV_ZFH"
143-
pending "GGML_SCCACHE_FOUND"
144-
string "GGML_SCHED_MAX_COPIES"
145-
bool "GGML_SSE42"
146-
ignored "GGML_STATIC"
147-
bool "GGML_SYCL"
148-
string "GGML_SYCL_DEVICE_ARCH"
149-
bool "GGML_SYCL_F16"
150-
bool "GGML_SYCL_GRAPH"
151-
string "GGML_SYCL_TARGET"
152-
bool "GGML_SYCL_DNN"
153-
bool "GGML_VULKAN"
154-
bool "GGML_VULKAN_CHECK_RESULTS"
155-
bool "GGML_VULKAN_DEBUG"
156-
bool "GGML_VULKAN_MEMORY_DEBUG"
157-
bool "GGML_VULKAN_PERF"
158-
ignored "GGML_VULKAN_RUN_TESTS"
159-
filepath "GGML_VULKAN_SHADERS_GEN_TOOLCHAIN"
160-
bool "GGML_VULKAN_SHADER_DEBUG_INFO"
161-
pending "GGML_VULKAN_VALIDATE"
162-
bool "GGML_VXE"
163-
bool "GGML_XTHEADVECTOR"
164-
filepath "GIT_EXE"
165-
filepath "MATH_LIBRARY"
166-
filepath "METALKIT_FRAMEWORK"
167-
filepath "METAL_FRAMEWORK"
168-
bool "WHISPER_ALL_WARNINGS"
169-
bool "WHISPER_ALL_WARNINGS_3RD_PARTY"
170-
ignored "WHISPER_BIN_INSTALL_DIR"
171-
ignored "WHISPER_BUILD_EXAMPLES"
172-
ignored "WHISPER_BUILD_SERVER"
173-
ignored"WHISPER_BUILD_TESTS"
174-
bool "WHISPER_COREML"
175-
bool "WHISPER_COREML_ALLOW_FALLBACK"
176-
ignored "WHISPER_CURL"
177-
bool "WHISPER_FATAL_WARNINGS"
178-
ignored "WHISPER_FFMPEG"
179-
ignored "WHISPER_INCLUDE_INSTALL_DIR"
180-
ignored "WHISPER_LIB_INSTALL_DIR"
181-
bool "WHISPER_OPENVINO"
182-
bool "WHISPER_SANITIZE_ADDRESS"
183-
bool "WHISPER_SANITIZE_THREAD"
184-
bool "WHISPER_SANITIZE_UNDEFINED"
185-
ignored "WHISPER_SDL2"
186-
pending "WHISPER_USE_SYSTEM_GGML"
39+
cmake_options.each do |name, type, default_value|
40+
option = option_name(name)
41+
value = type == "BOOL" ? enable_config(option) : arg_config("--#{option}")
42+
@options[name] = [type, value]
43+
end
18744
end
18845

18946
def option_name(name)
19047
name.downcase.gsub("_", "-")
19148
end
192-
193-
def bool(name)
194-
option = option_name(name)
195-
value = enable_config(option)
196-
@options[name] = [:bool, value]
197-
end
198-
199-
def string(name, type=:string)
200-
option = "--#{option_name(name)}"
201-
value = arg_config(option)
202-
raise "String expected for #{option}" if value == true || value&.empty?
203-
@options[name] = [type, value]
204-
end
205-
206-
def path(name)
207-
string(name, :path)
208-
end
209-
210-
def filepath(name)
211-
string(name, :filepath)
212-
end
213-
214-
def pending(name)
215-
@pending_options << name
216-
end
217-
218-
def ignored(name)
219-
@ignored_options << name
220-
end
22149
end

bindings/ruby/ext/ruby_whisper_vad_params.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ ruby_whisper_vad_params_initialize(int argc, VALUE *argv, VALUE self)
249249
rb_get_kwargs(kw_hash, param_names, 0, NUM_PARAMS, values);
250250

251251
for (i = 0; i < NUM_PARAMS; i++) {
252-
id= param_names[i];
252+
id = param_names[i];
253253
value = values[i];
254254
if (value == Qundef) {
255255
continue;

bindings/ruby/extsources.rb

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
require "pathname"
2+
3+
root = Pathname("..")/".."
14
ignored_dirs = %w[
25
.devops
6+
.github
7+
ci
38
examples/wchess/wchess.wasm
49
examples/whisper.android
510
examples/whisper.android.java
@@ -9,26 +14,27 @@
914
models
1015
samples
1116
scripts
12-
]
17+
].collect {|dir| root/dir}
1318
ignored_files = %w[
1419
AUTHORS
1520
Makefile
1621
README.md
1722
README_sycl.md
1823
.gitignore
1924
.gitmodules
25+
.dockerignore
2026
whisper.nvim
2127
twitch.sh
2228
yt-wsp.sh
29+
close-issue.yml
2330
]
2431

2532
EXTSOURCES =
26-
`git ls-files -z ../..`.split("\x0")
27-
.select {|file|
28-
basename = File.basename(file)
29-
30-
ignored_dirs.all? {|dir| !file.start_with?("../../#{dir}")} &&
31-
!ignored_files.include?(basename) &&
32-
(file.start_with?("../..") || file.start_with?("../javascript")) &&
33-
(!file.start_with?("../../.github/") || basename == "bindings-ruby.yml")
33+
`git ls-files -z #{root}`.split("\x0")
34+
.collect {|file| Pathname(file)}
35+
.reject {|file|
36+
ignored_dirs.any? {|dir| file.descend.any? {|desc| desc == dir}} ||
37+
ignored_files.include?(file.basename.to_path) ||
38+
(file.descend.to_a[1] != root && file.descend.to_a[1] != Pathname("..")/"javascript")
3439
}
40+
.collect(&:to_path)

0 commit comments

Comments
 (0)