Commit 5035086

2026-09-07 17:57:13
feat: build the Rust binary via ext/extconf.rb, not bin/setup
gem install elelem-ratatui now builds the binary automatically (spec.extensions), matching elelem-llama's cmake precedent. Previously bin/setup ran cargo directly, which only worked for a cloned checkout. extconf.rb builds into ext/elelem/ratatui/native/, and Session::BINARY points there instead of rust/target/release. bin/setup now just runs bundle install + rake compile for local dev. Claude-Session: https://claude.ai/code/session_01DUuj4amvRrPxDnHPHBkvz5
1 parent 86628e0
Changed files (6)
bin/setup
@@ -4,8 +4,4 @@ IFS=$'\n\t'
 set -vx
 
 bundle install
-
-which cargo >/dev/null || { echo "elelem-ratatui: cargo not found. Install Rust: https://rustup.rs" >&2; exit 1; }
-
-cd "$(dirname "$0")/../rust"
-cargo build --release
+bundle exec rake compile
ext/elelem/ratatui/extconf.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require "fileutils"
+
+EXT_DIR  = __dir__
+GEM_ROOT = File.expand_path("../../..", EXT_DIR)
+RUST_DIR = File.join(GEM_ROOT, "rust")
+NATIVE   = File.join(EXT_DIR, "native")
+
+def run(*cmd)
+  warn "elelem-ratatui: + #{cmd.join(' ')}"
+  system(*cmd) || abort("elelem-ratatui: build step failed: #{cmd.join(' ')}")
+end
+
+def which(cmd)
+  ENV["PATH"].to_s.split(File::PATH_SEPARATOR).any? do |dir|
+    path = File.join(dir, cmd)
+    File.executable?(path) && !File.directory?(path)
+  end
+end
+
+abort "elelem-ratatui: cargo not found. Install Rust: https://rustup.rs" unless which("cargo")
+
+run("cargo", "build", "--release", "--manifest-path", File.join(RUST_DIR, "Cargo.toml"))
+
+FileUtils.mkdir_p(NATIVE)
+FileUtils.cp(File.join(RUST_DIR, "target", "release", "elelem-ratatui"), File.join(NATIVE, "elelem-ratatui"))
+
+File.write(File.join(EXT_DIR, "Makefile"), <<~MAKE)
+  all:
+  clean:
+  install:
+  .PHONY: all clean install
+MAKE
+
+warn "elelem-ratatui: built binary into #{NATIVE}"
lib/elelem/ratatui/session.rb
@@ -3,7 +3,7 @@
 module Elelem
   module Ratatui
     class Session
-      BINARY = File.expand_path("../../../rust/target/release/elelem-ratatui", __dir__)
+      BINARY = File.expand_path("../../../ext/elelem/ratatui/native/elelem-ratatui", __dir__)
 
       def self.memoized
         @memoized ||= new.tap { |session| at_exit { session.close } }
.gitignore
@@ -7,3 +7,6 @@
 /spec/reports/
 /tmp/
 /rust/target/
+/ext/elelem/ratatui/native/
+/ext/elelem/ratatui/Makefile
+*.gem
elelem-ratatui.gemspec
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
       (f == gemspec) || f.start_with?(*%w[bin/ Gemfile .gitignore rust/target/])
     end
   end
+  spec.extensions = ["ext/elelem/ratatui/extconf.rb"]
   spec.require_paths = ["lib"]
 
   spec.add_dependency "elelem", "~> 0.12"
Rakefile
@@ -5,4 +5,9 @@ require "rspec/core/rake_task"
 
 RSpec::Core::RakeTask.new(:spec)
 
+desc "Build the Rust binary (the same build `gem install` runs)"
+task :compile do
+  ruby "ext/elelem/ratatui/extconf.rb"
+end
+
 task default: %i[spec]