Skip to content

Commit f0d2bfb

Browse files
ruby : make Ruby bindings installed with build options (#3056)
* Fix signature of URI.new7s return value * Use path instead of string | _ToPath * Add document comment to RBS * Remove unnecessary build flags * Remove unnecessary line * Remove files have become unnecessary * Make gem install accept build options for whisper.cpp * Add instraction for build options in README * Add methods for check to Options * Test build options * Rename: configs -> options * Add assert_installed assertion * Use assert_installed * Remove unused attribute * Extract dependency check logic as Dependencies class * Update README * Add WHISPER_FFMPEG option * Test extra build options only on local test * Bump version to 1.3.2 [skip ci]
1 parent 170b2fa commit f0d2bfb

11 files changed

+535
-88
lines changed

bindings/ruby/README.md

+12
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ If bundler is not being used to manage dependencies, install the gem by executin
1616

1717
$ gem install whispercpp
1818

19+
You can pass build options for whisper.cpp, for instance:
20+
21+
$ bundle config build.whispercpp --enable-ggml-cuda
22+
23+
or,
24+
25+
$ gem install whispercpp -- --enable-ggml-cuda
26+
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.
28+
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`.
29+
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"`.
30+
1931
Usage
2032
-----
2133

bindings/ruby/ext/cpu.mk

-13
This file was deleted.

bindings/ruby/ext/dependencies.rb

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
require "tsort"
2+
3+
class Dependencies
4+
def initialize(cmake, options)
5+
@cmake = cmake
6+
@options = options
7+
8+
generate_dot
9+
@libs = parse_dot
10+
end
11+
12+
def to_s
13+
@libs.join(" ")
14+
end
15+
16+
private
17+
18+
def dot_path
19+
File.join(__dir__, "build", "whisper.cpp.dot")
20+
end
21+
22+
def generate_dot
23+
system @cmake, "-S", "sources", "-B", "build", "--graphviz", dot_path, "-D", "BUILD_SHARED_LIBS=OFF", @options.to_s, exception: true
24+
end
25+
26+
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+
39+
File.open(dot_path).each_line do |line|
40+
case line
41+
when /\[\s*label\s*=\s*"Static Library"\s*,\s*shape\s*=\s*(?<shape>\w+)\s*\]/
42+
static_lib_shape = $~[:shape]
43+
when /\A\s*"(?<node>\w+)"\s*\[\s*label\s*=\s*"(?<label>\S+)"\s*,\s*shape\s*=\s*(?<shape>\w+)\s*\]\s*;\s*\z/
44+
node = $~[:node]
45+
label = $~[:label]
46+
shape = $~[:shape]
47+
nodes[node] = [label, shape]
48+
when /\A\s*"(?<depender>\w+)"\s*->\s*"(?<dependee>\w+)"/
49+
depender = $~[:depender]
50+
dependee = $~[:dependee]
51+
depends[depender] ||= []
52+
depends[depender] << dependee
53+
end
54+
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
60+
end
61+
end

bindings/ruby/ext/extconf.rb

+5-44
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,12 @@
11
require "mkmf"
2-
require "tsort"
3-
4-
# TODO: options such as CoreML
2+
require_relative "options"
3+
require_relative "dependencies"
54

65
cmake = find_executable("cmake") || abort
7-
6+
options = Options.new
87
have_library("gomp") rescue nil
8+
libs = Dependencies.new(cmake, options)
99

10-
prefix = File.join("build", "whisper.cpp.dot")
11-
system cmake, "-S", "sources", "-B", "build", "--graphviz", prefix, "-D", "BUILD_SHARED_LIBS=OFF", exception: true
12-
13-
static_lib_shape = nil
14-
nodes = {}
15-
depends = {}
16-
class << depends
17-
include TSort
18-
alias tsort_each_node each_key
19-
def tsort_each_child(node, &block)
20-
fetch(node, []).each(&block)
21-
end
22-
end
23-
File.open(File.join("build", "whisper.cpp.dot")).each_line do |line|
24-
case line
25-
when /\[\s*label\s*=\s*"Static Library"\s*,\s*shape\s*=\s*(?<shape>\w+)\s*\]/
26-
static_lib_shape = $~[:shape]
27-
when /\A\s*"(?<node>\w+)"\s*\[\s*label\s*=\s*"(?<label>\S+)"\s*,\s*shape\s*=\s*(?<shape>\w+)\s*\]\s*;\s*\z/
28-
node = $~[:node]
29-
label = $~[:label]
30-
shape = $~[:shape]
31-
nodes[node] = [label, shape]
32-
when /\A\s*"(?<depender>\w+)"\s*->\s*"(?<dependee>\w+)"/
33-
depender = $~[:depender]
34-
dependee = $~[:dependee]
35-
depends[depender] ||= []
36-
depends[depender] << dependee
37-
end
38-
end
39-
libs = depends.tsort.filter_map {|node|
40-
label, shape = nodes[node]
41-
shape == static_lib_shape ? label : nil
42-
}.collect {|lib| "lib#{lib}.a"}
43-
.reverse
44-
.join(" ")
45-
46-
$CFLAGS << " -std=c11 -fPIC"
47-
$CXXFLAGS << " -std=c++17 -O3 -DNDEBUG"
4810
$INCFLAGS << " -Isources/include -Isources/ggml/include -Isources/examples"
4911
$LOCAL_LIBS << " #{libs}"
5012
$cleanfiles << " build #{libs}"
@@ -54,8 +16,7 @@ def tsort_each_child(node, &block)
5416
$(TARGET_SO): #{libs}
5517
#{libs}: cmake-targets
5618
cmake-targets:
57-
#{"\t"}#{cmake} -S sources -B build -D BUILD_SHARED_LIBS=OFF -D CMAKE_ARCHIVE_OUTPUT_DIRECTORY=#{__dir__} -D CMAKE_POSITION_INDEPENDENT_CODE=ON
19+
#{"\t"}#{cmake} -S sources -B build -D BUILD_SHARED_LIBS=OFF -D CMAKE_ARCHIVE_OUTPUT_DIRECTORY=#{__dir__} -D CMAKE_POSITION_INDEPENDENT_CODE=ON #{options}
5820
#{"\t"}#{cmake} --build build --config Release --target common whisper
59-
#{"\t"}
6021
EOF
6122
end

bindings/ruby/ext/metal-embed.mk

-17
This file was deleted.

bindings/ruby/ext/metal.mk

-6
This file was deleted.

0 commit comments

Comments
 (0)