lda-ruby 0.3.9 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -13
- data/CHANGELOG.md +16 -0
- data/Gemfile +9 -0
- data/README.md +126 -3
- data/VERSION.yml +3 -3
- data/docs/modernization-handoff.md +233 -0
- data/docs/porting-strategy.md +148 -0
- data/docs/precompiled-platform-policy.md +81 -0
- data/docs/precompiled-target-evaluation.md +67 -0
- data/docs/release-runbook.md +192 -0
- data/docs/rust-orchestration-guardrails.md +50 -0
- data/ext/lda-ruby/cokus.c +10 -11
- data/ext/lda-ruby/cokus.h +3 -3
- data/ext/lda-ruby/extconf.rb +10 -6
- data/ext/lda-ruby/lda-inference.c +23 -7
- data/ext/lda-ruby/utils.c +8 -0
- data/ext/lda-ruby-rust/Cargo.toml +12 -0
- data/ext/lda-ruby-rust/README.md +73 -0
- data/ext/lda-ruby-rust/extconf.rb +135 -0
- data/ext/lda-ruby-rust/include/strings.h +35 -0
- data/ext/lda-ruby-rust/src/lib.rs +1263 -0
- data/lda-ruby.gemspec +0 -0
- data/lib/lda-ruby/backends/base.rb +133 -0
- data/lib/lda-ruby/backends/native.rb +158 -0
- data/lib/lda-ruby/backends/pure_ruby.rb +675 -0
- data/lib/lda-ruby/backends/rust.rb +607 -0
- data/lib/lda-ruby/backends.rb +58 -0
- data/lib/lda-ruby/corpus/corpus.rb +17 -15
- data/lib/lda-ruby/corpus/data_corpus.rb +2 -2
- data/lib/lda-ruby/corpus/directory_corpus.rb +2 -2
- data/lib/lda-ruby/corpus/text_corpus.rb +2 -2
- data/lib/lda-ruby/document/document.rb +6 -6
- data/lib/lda-ruby/document/text_document.rb +5 -4
- data/lib/lda-ruby/rust_build_policy.rb +21 -0
- data/lib/lda-ruby/version.rb +5 -0
- data/lib/lda-ruby.rb +293 -48
- data/test/backend_compatibility_test.rb +146 -0
- data/test/backends_selection_test.rb +100 -0
- data/test/benchmark_scripts_test.rb +23 -0
- data/test/gemspec_test.rb +27 -0
- data/test/lda_ruby_test.rb +49 -11
- data/test/packaged_gem_smoke_test.rb +33 -0
- data/test/pure_ruby_orchestration_test.rb +109 -0
- data/test/release_scripts_test.rb +93 -0
- data/test/rust_build_policy_test.rb +23 -0
- data/test/rust_orchestration_test.rb +911 -0
- data/test/simple_pipeline_test.rb +22 -0
- data/test/simple_yaml.rb +1 -7
- data/test/test_helper.rb +5 -6
- metadata +54 -38
- data/Rakefile +0 -61
- data/ext/lda-ruby/Makefile +0 -181
- data/test/data/.gitignore +0 -2
- data/test/simple_test.rb +0 -26
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "rbconfig"
|
|
5
|
+
|
|
6
|
+
require_relative "../../lib/lda-ruby/rust_build_policy"
|
|
7
|
+
|
|
8
|
+
module Lda
|
|
9
|
+
module RustExtensionBuild
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
def run
|
|
13
|
+
policy = RustBuildPolicy.resolve
|
|
14
|
+
puts("Rust extension build policy: #{policy} (#{RustBuildPolicy::ENV_KEY})")
|
|
15
|
+
|
|
16
|
+
case policy
|
|
17
|
+
when RustBuildPolicy::NEVER
|
|
18
|
+
puts("Skipping Rust extension build (policy=#{RustBuildPolicy::NEVER}).")
|
|
19
|
+
when RustBuildPolicy::ALWAYS
|
|
20
|
+
ensure_cargo_available!
|
|
21
|
+
build_and_stage!
|
|
22
|
+
else
|
|
23
|
+
if cargo_available?
|
|
24
|
+
build_and_stage!
|
|
25
|
+
else
|
|
26
|
+
puts("cargo not found; skipping Rust extension build (policy=#{RustBuildPolicy::AUTO}).")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
write_noop_makefile
|
|
31
|
+
rescue StandardError => e
|
|
32
|
+
if policy == RustBuildPolicy::ALWAYS
|
|
33
|
+
abort("Rust extension build failed with #{RustBuildPolicy::ENV_KEY}=#{RustBuildPolicy::ALWAYS}: #{e.message}")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
warn("Rust extension build skipped after error in auto mode: #{e.message}")
|
|
37
|
+
write_noop_makefile
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def ensure_cargo_available!
|
|
41
|
+
return if cargo_available?
|
|
42
|
+
|
|
43
|
+
abort("cargo not found in PATH but #{RustBuildPolicy::ENV_KEY}=#{RustBuildPolicy::ALWAYS} was requested.")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def cargo_available?
|
|
47
|
+
cargo = ENV.fetch("CARGO", "cargo")
|
|
48
|
+
system(cargo, "--version", out: File::NULL, err: File::NULL)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def build_and_stage!
|
|
52
|
+
cargo = ENV.fetch("CARGO", "cargo")
|
|
53
|
+
Dir.chdir(__dir__) do
|
|
54
|
+
env = rust_build_env
|
|
55
|
+
success =
|
|
56
|
+
if env.empty?
|
|
57
|
+
system(cargo, "build", "--release")
|
|
58
|
+
else
|
|
59
|
+
system(env, cargo, "build", "--release")
|
|
60
|
+
end
|
|
61
|
+
success or raise "cargo build --release failed"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
source = rust_cdylib_source
|
|
65
|
+
raise "Rust extension artifact not found at #{rust_cdylib_candidates.join(', ')}" unless source
|
|
66
|
+
|
|
67
|
+
destination = File.expand_path("../../lib/lda_ruby_rust.#{RbConfig::CONFIG.fetch('DLEXT')}", __dir__)
|
|
68
|
+
FileUtils.cp(source, destination)
|
|
69
|
+
puts("Staged Rust extension to #{destination}")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def rust_cdylib_source
|
|
73
|
+
rust_cdylib_candidates.find { |path| File.exist?(path) }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def rust_cdylib_candidates
|
|
77
|
+
rust_cdylib_filenames.map { |filename| File.join(__dir__, "target", "release", filename) }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def rust_cdylib_filenames
|
|
81
|
+
host_os = RbConfig::CONFIG.fetch("host_os")
|
|
82
|
+
case host_os
|
|
83
|
+
when /mswin|mingw|cygwin/
|
|
84
|
+
# On Windows cargo may emit either prefixed or unprefixed DLL names.
|
|
85
|
+
["lda_ruby_rust.dll", "liblda_ruby_rust.dll"]
|
|
86
|
+
else
|
|
87
|
+
extension =
|
|
88
|
+
case host_os
|
|
89
|
+
when /darwin/
|
|
90
|
+
"dylib"
|
|
91
|
+
else
|
|
92
|
+
"so"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
["liblda_ruby_rust.#{extension}"]
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def rust_build_env
|
|
100
|
+
host_os = RbConfig::CONFIG.fetch("host_os")
|
|
101
|
+
return {} unless host_os.match?(/darwin/)
|
|
102
|
+
|
|
103
|
+
dynamic_lookup_flag = "-C link-arg=-Wl,-undefined,dynamic_lookup"
|
|
104
|
+
existing = ENV.fetch("RUSTFLAGS", "")
|
|
105
|
+
merged =
|
|
106
|
+
if existing.include?(dynamic_lookup_flag)
|
|
107
|
+
existing
|
|
108
|
+
else
|
|
109
|
+
[existing, dynamic_lookup_flag].reject(&:empty?).join(" ")
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
{ "RUSTFLAGS" => merged }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def write_noop_makefile
|
|
116
|
+
File.write(
|
|
117
|
+
File.join(__dir__, "Makefile"),
|
|
118
|
+
<<~MAKEFILE
|
|
119
|
+
all:
|
|
120
|
+
\t@echo "Rust extension handled by extconf.rb"
|
|
121
|
+
|
|
122
|
+
install:
|
|
123
|
+
\t@echo "Rust extension handled by extconf.rb"
|
|
124
|
+
|
|
125
|
+
clean:
|
|
126
|
+
\t@true
|
|
127
|
+
|
|
128
|
+
distclean: clean
|
|
129
|
+
MAKEFILE
|
|
130
|
+
)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
Lda::RustExtensionBuild.run
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#ifndef LDA_RUBY_BINDGEN_STRINGS_H
|
|
2
|
+
#define LDA_RUBY_BINDGEN_STRINGS_H
|
|
3
|
+
|
|
4
|
+
#include <string.h>
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* RubyInstaller headers may include <strings.h> on Windows, but Clang-based
|
|
8
|
+
* bindgen runs can miss that header in this environment. Provide compatibility
|
|
9
|
+
* aliases for bindgen preprocessing.
|
|
10
|
+
*/
|
|
11
|
+
#if defined(_WIN32) && !defined(__MINGW32__)
|
|
12
|
+
#ifndef bzero
|
|
13
|
+
#define bzero(ptr, size) memset((ptr), 0, (size))
|
|
14
|
+
#endif
|
|
15
|
+
#ifndef bcmp
|
|
16
|
+
#define bcmp(a, b, n) memcmp((a), (b), (n))
|
|
17
|
+
#endif
|
|
18
|
+
#ifndef bcopy
|
|
19
|
+
#define bcopy(src, dst, n) memmove((dst), (src), (n))
|
|
20
|
+
#endif
|
|
21
|
+
#ifndef index
|
|
22
|
+
#define index strchr
|
|
23
|
+
#endif
|
|
24
|
+
#ifndef rindex
|
|
25
|
+
#define rindex strrchr
|
|
26
|
+
#endif
|
|
27
|
+
#ifndef strcasecmp
|
|
28
|
+
#define strcasecmp _stricmp
|
|
29
|
+
#endif
|
|
30
|
+
#ifndef strncasecmp
|
|
31
|
+
#define strncasecmp _strnicmp
|
|
32
|
+
#endif
|
|
33
|
+
#endif
|
|
34
|
+
|
|
35
|
+
#endif
|