Ruby で書かれたスクリプトを Ruby の入っていない Windows 環境に向けて配布するツールとしては、OCRA という gem が有名です。
しかし OCRA にはいくつかの欠点1があったため、Neri という gem を作成しました。このたび ver 1.0 を公開し、今までより多くの Ruby スクリプトを実行ファイルに変換できるようになったので、Neri を使って Ruby スクリプトを実行ファイルに変換するチュートリアルを書いてみたいと思います。
Neri の目的
Neri は、ちょっとした CUI スクリプトや、あるいは DXRuby や Gosu を用いたゲーム、LibUI(あるいはそのラッパーである Glimmer DSL for LibUI)を用いた GUI プログラムなどを実行ファイルに変換し、Ruby の無い Windows 環境でも容易に動作させられるようにすることが目的です。
多くのライブラリを用いた大規模で複雑なプログラム(例えば Rails)を実行ファイル化することには向かないので、ご了承ください。
実行環境
Neri が動作するのは、Ruby Installer を用いてインストールされた Ruby 環境だけです。Windows 以外の環境(含 WSL)では動作しません。
また、.exe ファイルを作成するには Devkit 付きの Ruby Installer が必要です。ただし .bat ファイルを作成する機能もあり、それを使うのであれば Devkit は必ずしも必要ではありません。
この記事は「DXRuby で作ったゲームを .exe 化する」という状況を例にして書いていきます。Ruby Installer with Devkit および DXRuby はインストール済みなものとします。Neri は以下のように gem コマンドからインストールしてください。
gem install neri
DXRuby は、この記事を書いている 2022年01月09日現在、Ruby 最新版である 3.1 には対応していません。(2.5~3.0 に対応。) また、64bit(x64) 版 Ruby では不安定な動作も多いので、32bit(x86) 版を使用することをオススメします。 (※ なお、Neri は Ruby のバージョンや 32/64bit に関係なく動作します。) なお、最近の多くの環境では DXRuby を使用するには d3dx9_40.dll が必要になります。その導入方法については下記リンク先を御覧ください。(d3dx9_40.dll は、インストールしている Ruby のフォルダの中の bin フォルダにコピーすることをオススメします。)
サンプルプログラム
以下のようなファイル構成になっています。
-
image
フォルダ:画像ファイルの入ったフォルダ -
src
フォルダ:メイン以外のスクリプトの入ったフォルダ -
data
フォルダ:その他のファイルの入ったフォルダ -
star.ico
:実行ファイルのアイコン -
ほしあつめ.rb
:メインの実行スクリプト
Neri を用いて実行ファイルを作成する
まず最初に一度、
ridk enable
として開発環境にパスを通しておく必要があります。
基本
neri ほしあつめ.rb
基本は上のように、neri
コマンドに続けて実行スクリプトを指定するだけです。
するとまずは一度スクリプトが実行されゲームが起動するので、× をクリックして終了してしまいましょう。
これで実行ファイル ほしあつめ.exe
と、実行に必要なファイルを収めた system
フォルダが作成されます。実際に .exe ファイルを実行して、ゲームが動作することを確かめてください。
アイコンを設定する
neri --icon star.ico ほしあつめ.rb
と、--icon
オプションを使用することで .exe ファイルにアイコンを設定することができます。
実行に必要なファイルをデータファイルにまとめる
ソースコード (.rb) をまとめる
今のままでは、配布するにあたっては
-
data
フォルダ -
image
フォルダ -
src
フォルダ -
system
フォルダ ほしあつめ.exe
をすべてまとめて配布する必要があります。実際にはここにさらに Readme とかも追加することになるでしょう。ちょっとゴチャゴチャしている感がありますね。
また、ソースコードが(ほしあつめ.rb
も src
フォルダ内のものも)そのままになってしまっているため、Ruby がちょっと分かる人であれば簡単に書き換えて改造することができてしまいます。
そこで、Neri にはそうしたファイルをデータファイルにまとめる機能があるので、それを使って配布するファイルを減らし、簡単には改造できないようにすることができます。
neri --icon star.ico ほしあつめ.rb src
2こうすると、system
フォルダ内に ほしあつめ.dat
というファイルが作成されます。src
内のファイル(および ほしあつめ.rb
)はこのデータファイルに入っているのですが、require
等でそのまま読み込めるようになっています。つまりファイルを隠蔽したにもかかわらず、ソースコードを書き換える必要なくそのまま動作させることができます。
画像・音声ファイルをまとめる (DXRuby 使用時)
さらに画像(あるいは音声)ファイルについても、DXRuby を使っている場合はソースコードを書き換えることなしに、ファイルをデータファイルに入れてしまうことができます。つまり
neri --icon star.ico ほしあつめ.rb src image
とすれば src
と image
フォルダ内のファイルがデータファイルに入るので、配布に必要なものは以下だけになります。
-
data
フォルダ -
system
フォルダ ほしあつめ.exe
それ以外のファイルをまとめる
まだ data
フォルダが残っているので、これもまとめてしまいたいところです。しかし残念ながらソースコードや画像ファイル以外のファイルは、そのまま
neri --icon star.ico ほしあつめ.rb src image data
のようにしてデータファイルに追加しただけでは、うまく動作しません。
上のコマンドで作成した .exe ファイルを実行してみてもゲーム画面はおろかエラーメッセージも何も表示されないので、このままでは原因がわかりにくいですね。そうした場合には --bat
オプションを使って .exe ではなく .bat を作ると、エラーメッセージが表示されるようになるので原因がわかりやすくなるでしょう。
neri --bat --icon star.ico ほしあつめ.rb src image data
作成された ほしあつめ.bat
を実行すると、ほしあつめ.rb:27:in 'read': No such file or directory @ rb_sysopen - data/secret.txt (Errno::ENOENT)
とのエラーで、data/secret.txt
を読み込めていないことがわかります。こうした場合はソースコードにも手を入れる必要があります。
ほしあつめ.rb
に、まずは一番上に
require "neri"
を追加しましょう。こうするとデータファイル内のデータを扱うためのメソッドが使えるようになります。
そして、25行目の
SECRET_MESSAGE = File.read("data/secret.txt", encoding: "utf-8")
を、以下のように変更します。
SECRET_MESSAGE = Neri.file_read("data/secret.txt", "utf-8")
Neri.file_read
は、データファイルがあるときにはそちらから、無ければ実際のファイルからデータを読み込みます。そのため普通に Ruby から実行した際にも、Neri で作った .exe ファイルを実行した際にも、同じ様にデータを読み込むことができます。
この上で
neri --icon star.ico ほしあつめ.rb src image data
と data
フォルダ内のファイルもデータファイルに入れておけば、配布に必要なものは
-
system
フォルダ ほしあつめ.exe
の 2 つだけになりました。非常にすっきりしましたね。
データファイルを暗号化する
しかし、今のままでは問題があります。data/secret.txt
は、10 点以上の点を取った人だけに見せたい秘密のメッセージです。しかし無理やりデータファイルを開いてみると、この秘密のメッセージがそのまま保存されてしまっていることがわかります。
それが気になるのであれば、--encryption-key
オプションを使って暗号化しましょう。3
neri --encryption-key tekitounakey --icon star.ico ほしあつめ.rb src image data
こうすればデータファイルを開いても、何が書いてあるのかわからなくなります。
出力先を別フォルダにする
デフォルトでは Neri はカレントフォルダに実行ファイル等を作成しますが、--output-dir
オプションを使って別のフォルダ内に作成するようにしてみましょう。
neri --output-dir ../starcollector --encryption-key tekitounakey --icon star.ico ほしあつめ.rb src image data
設定ファイルを作る
先程のような長いコマンドを何度も入力するのは大変です。そこで neri.config
というファイルを作っておくと、そこから設定を読み込んでくれます。
--output-dir ../starcollector
--encryption-key tekitounakey
--icon star.ico
ほしあつめ.rb
src
image
data
こうしておけば、
neri
だけで実行ファイルを作ってくれます。
一部の設定だけを neri.config
に書いておき、残りの設定を Neri 実行時に指定することもできます。
いちいち × をクリックして終了しなくても済むようにする
今の状態では、実行ファイルを作成するたびにいちいちゲームが起動してしまい、そのたびに × をクリックして終了する必要があります。
実行ファイル作成時には module NeriBuild
が定義されているので、これの有無をチェックし 有るなら早期に4終了させてしまえば、いちいち × をクリックする必要がなくなります。
exit if defined? NeriBuild
LibUI を使ったスクリプトを .exe 化する
GUI ライブラリ LibUI を使ったスクリプトも問題なく .exe 化できることを確かめています。LibUI の Usage にある以下のサンプル(※ ただし 1 行目を追加)を Neri で .exe 化できることを確かめてみてください。
require 'rbconfig'
require 'libui'
UI = LibUI
UI.init
main_window = UI.new_window('hello world', 200, 100, 1)
button = UI.new_button('Button')
UI.button_on_clicked(button) do
UI.msg_box(main_window, 'Information', 'You clicked the button')
end
UI.window_on_closing(main_window) do
puts 'Bye Bye'
UI.control_destroy(main_window)
UI.quit
0
end
UI.window_set_child(main_window, button)
UI.control_show(main_window)
UI.main
UI.quit
1 行目の require 'rbconfig' が無いと、スクリプトを実行した際は問題なく動作しますが、Neri で作った .exe ファイルを実行しようとするとうまくいきません。 Neri はデフォルトでは軽量化のために Rubygems を取り除いてしまうのですが、Rubygems が RbConfig を require しており、そして現行バージョン(0.0.13)の LibUI は RbConfig を(require 無しで)使用しているためです。 今後のバージョンでは、この点が改善され require 'rbconfig' が不要になるかもしれません。
最後に
この Neri は実は 3 年ほど完全に放置してしまっていたプロジェクトだったのですが、GitHub でプルリクを送ってくださった方がいて、それで 3 年ぶりにやる気を出して正式版の公開にまでこぎつけることができました。この場を借りて御礼申し上げます。
-
作成当時は OCRA には「日本語ユーザー名の環境では実行できない」という欠点があったのですが、現在は解消されたようです。ただ、x64 版の Ruby を使っていると libssp-0.dll や libgmp-10.dll がデフォルトでは同梱されずエラーになることがあるようで、その場合 ↩
ocra foo.rb --dll ruby_builtin_dlls\libssp-0.dll --dll ruby_builtin_dlls\libgmp-10.dll
のように明示的に dll を追加する必要があります。(以下参照)
Neri はその点を(力技ですが)対処しているので、x64 環境でも問題なく実行ファイルを作成できます。
-
実行するスクリプトは、(オプションをのぞいて)一番最初に書く必要があります。つまり ↩
neri --icon star.ico src ほしあつめ.rb
と順番を変えるとエラーになります。オプションは後ろに書いても大丈夫なので
neri ほしあつめ.rb src --icon star.ico
でも大丈夫です。
-
暗号化と言っても簡易的なものなので、つよつよエンジニアの手にかかれば解読可能です。重要なデータの暗号化には使わないでください。 ↩
-
一通りライブラリのロードが終わってからにする必要があります。 ↩