RubyScript2Exe

A Ruby Compiler

Tue May 29 20:09:00 UTC 2007
Erik Veenstra <rubyscript2exe@erikveen.dds.nl>


PDF version (A4)

1. Introduction

2. Internals
          2.1. RubyScript2Exe
          2.2. EEE

3. Usage
          3.1. Compiling the Application
          3.2. Running the Application
          3.3. From Inside your Application
                    a) RUBYSCRIPT2EXE.(dlls|bin|lib)=
                    b) RUBYSCRIPT2EXE.tempdir=
                    c) RUBYSCRIPT2EXE.tk=
                    d) RUBYSCRIPT2EXE.rubyw=
                    e) RUBYSCRIPT2EXE.strip=
                    f) RUBYSCRIPT2EXE.is_compil(ing|ed)?
                    g) RUBYSCRIPT2EXE.appdir
                    h) RUBYSCRIPT2EXE.userdir
                    i) RUBYSCRIPT2EXE.exedir
                    j) RUBYSCRIPT2EXE.executable
                    k) Information about EEE
          3.4. Tips & Tricks
                    a) Just Scanning, no Running
                    b) Logging
                    c) Hacking on Location

4. Examples
          4.1. Distributions

5. License
          5.1. License of RubyScript2Exe
          5.2. License of your Application

6. Download
          6.1. Mac OS X (Darwin)

7. Known Issues


1. Introduction

RubyScript2Exe transforms your Ruby application into a standalone, compressed Windows, Linux or Mac OS X (Darwin) executable. You can look at it as a "compiler". Not in the sense of a source-code-to-byte-code compiler, but as a "collector", for it collects all necessary files to run your application on an other machine: the Ruby application, the Ruby interpreter and the Ruby runtime library (stripped down for your application). Anyway, the result is the same: a standalone executable (application.exe). And that's what we want!

Because of the gathering of files from your own Ruby installation, RubyScript2Exe creates an executable for the platform it's being run on. No cross compile.

And when I say Windows, I mean both Windows (RubyInstaller, MinGW and MSWin32) and Cygwin. But the generated exe under Cygwin is very, very big, because its exe's are very big (static?) and it includes cygwin1.dll, so it can run on machines without Cygwin.

There is one more advantage: Because there might be some incompatibilities between the different Ruby versions, you have to test your application with every single version. Unless you distribute your version of Ruby with your application...

RubyScript2Exe can handle simple scripts, but it can handle complete directories as well. Usually, an application is more than just a program or a script. It consists of libraries, documentation, help files, configuration files, images, licenses, readmes, and so on. You can embed all of them in one single executable.

[images/rubyscript2exe2.gif]

What's the difference between RubyScript2Exe and AllInOneRuby? Well, RubyScript2Exe includes an application (your script), the Ruby VM and only parts of the ruby_lib tree (it's stripped specifically for your application). AllInOneRuby contains a complete Ruby installation: it includes no application, but it does include the Ruby VM and the complete ruby_lib tree. You can use allinoneruby.exe like ruby.exe (Windows) and allinoneruby_* like ruby (Linux, Darwin) that's already installed on your system. In other words: the executable, generated with RubyScript2Exe, is an application; the one generated with AllInOneRuby "is" Ruby.

If you like RubyScript2Exe, you might want to read Distributing Ruby Applications. It's about how I build, pack and distribute my Ruby applications. Theory and practice.

(I'm working on full support of RubyGems. The handling of require_gem and the mangling of $: are implemented and all files of a gem are embedded. I've tested just a couple of gems, not all of them. If you've troubles with a specific gem, please let me know.)


2. Internals

2.1. RubyScript2Exe

RubyScript2Exe monitors the execution of your application. This is done by running your application with a special library. After your application has finished, this special library returns all information about your application to RubyScript2Exe. RubyScript2Exe then gathers all program files and requirements (ruby.exe, rubyw.exe or ruby (and their so's, o's and dll's, determined recursively), *.rb, *.so, *.o and *.dll (and their so's, o's and dll's, determined recursively)) from your own Ruby installation. All these files, your application and an extracting program are combined into one single, compressed executable. This executable can run on a bare Windows installation, a Linux installation with a libc version >= yours or a Darwin installation. Call it a "just-in-time and temporary installation of Ruby"...

[images/rubyscript2exe1.dia.gif]

2.2. EEE

EEE stands for "Environment Embedding Executable". Well, I just had to give it a name...

EEE is the little Pascal program that packs and compresses all necessary files. It had to be written in a language that could be compiled and linked into an exe-file. Ruby wasn't an option. I use FreePascal (1.9.8 on Windows, 1.9.8 on Linux and 1.9.8 on Darwin).

EEE has two modes: packing and unpacking. When it detects an attached archive, it jumps into unpacking mode; into packing mode otherwise.

After creating the temporary directory and unpacking all files, EEE spawns the Ruby interpreter for your application.rb . At that point, EEE releases control to Ruby itself. After Ruby has finished, EEE regains control and starts cleaning up.

The difference between eee.exe and eeew.exe is the same as the difference between ruby.exe and rubyw.exe: With or without a DOS-box.

I've given EEE a page of its own, with more information then this section provides.


3. Usage

3.1. Compiling the Application

If you use the original rubyscript2exe.rb:

c:\home\erik> ruby rubyscript2exe.rb application.rb[w] [parameters]
or 
c:\home\erik> ruby rubyscript2exe.rb application[/] [parameters]

If you installed the gem, it's:

c:\home\erik> rubyscript2exe application.rb[w] [parameters]
or 
c:\home\erik> rubyscript2exe application[/] [parameters]

ParameterDescription
--rubyscript2exe-rubywAvoid the popping up of a DOS box. (It's annoying in the test period... No puts and p anymore... Only use it for distributing your application. See Logging.)
--rubyscript2exe-rubyForce the popping up of a DOS box (default).
--rubyscript2exe-nostripAvoid stripping. The binaries (ruby and *.so) on Linux and Darwin are stripped by default to reduce the size of the resulting executable.
--rubyscript2exe-straceStart the embedded application with strace (Linux only, for debugging only).
--rubyscript2exe-tk(experimental) Embed not only the Ruby bindings for TK, but TK itself as well.
--rubyscript2exe-verboseVerbose mode.
--rubyscript2exe-quietQuiet mode.

In case you want to compile a complete directory, the entry point of you application has to be init.rb. RubyScript2Exe complains if it can't find application/init.rb.

All parameters starting with --rubyscript2exe- will be deleted before the execution of application.rb.

If the extension is "rb", a DOS box will pop up. If the extension is "rbw", no DOS box will pop up. Unless it is overwritten by a parameter.

On Linux and Darwin, there's no difference between ruby and rubyw.

When using --rubyscript2exe-tk, it's probably a good idea to add exit if RUBYSCRIPT2EXE.is_compiling? (see is_compiling?) just before Tk.mainloop:

require "rubyscript2exe"
exit if RUBYSCRIPT2EXE.is_compiling?
Tk.mainloop

It is possible to change the icon of the generated executable manually, with a resource editor like Resource Hacker. If Resource Hacker is installed, in your %PATH% and therefor available from the current directory, and an icon file with the name application.ico exists in the current directory, the default icon will automatically be replaced by yours. I used Resource Hacker 3.4.0 for my tests.

3.2. Running the Application

c:\home\erik> application.exe [parameters]

ParameterDescription
--eee-listJust list the contents of the executable. (Doesn't work in combination with rubyw.)
--eee-infoJust show the information stored in the executable. (Doesn't work in combination with rubyw.)
--eee-justextractJust extract the original files from the executable into the current directory (no subdirectory!).

If one of these parameters is used, RubyScript2Exe does just that. It doesn't execute the application.

If none of these parameters is used, RubyScript2Exe executes the application with the given parameters. To be forward compatible, all parameters starting with --eee- will be deleted before the execution of the application.

The exit code of the executable is the same as the exit code of your application.

3.3. From Inside your Application

Module RUBYSCRIPT2EXE is available after doing a require "rubyscript2exe". This module is used in this section.

Yep, we've two files with the same name: the application rubyscript2exe.rb (big) and the library rubyscript2exe.rb (small). They're not the same. But, since the big application rubyscript2exe.rb is an RBA and includes the small library rubyscript2exe.rb, you can always do require "rubyscript2exe". It doesn't matter whether Ruby finds the big one or the small one. It should work either way. Funny stuff, those RBA's... ;]

This is an overview of the methods (or module variables) RUBYSCRIPT2EXE provides. They're explained in detail in the next sections.

MethodUseful at Compile-TimeUseful at Run-TimeDefault
RUBYSCRIPT2EXE.dlls=x []
RUBYSCRIPT2EXE.bin=x []
RUBYSCRIPT2EXE.lib=x []
RUBYSCRIPT2EXE.tempdir=x nil
RUBYSCRIPT2EXE.tk=x false
RUBYSCRIPT2EXE.rubyw=x false
RUBYSCRIPT2EXE.strip=x true
RUBYSCRIPT2EXE.is_compiling?x  
RUBYSCRIPT2EXE.is_compiled? x 
RUBYSCRIPT2EXE.appdir x 
RUBYSCRIPT2EXE.userdir x 
RUBYSCRIPT2EXE.exedir x 
RUBYSCRIPT2EXE.executable x 

a) RUBYSCRIPT2EXE.(dlls|bin|lib)=

The application itself (application.rb) usually doesn't need to know that it's wrapped by RubyScript2Exe. But sometimes RubyScript2Exe needs to know something about the application. Instead of introducing separate configuration files, I simply abuse application.rb as a configuration file...

Sometimes, you want to embed an additional DLL in the executable. That's easily done by using RUBYSCRIPT2EXE.dlls= in your application:

require "rubyscript2exe"
RUBYSCRIPT2EXE.dlls = ["a.dll", "b.dll", "c.dll"]

(You can also do this: RUBYSCRIPT2EXE.dlls << "a.dll")

At the end of the tracing of your application, the mentioned DLL's are copied from the directory in which the application was started, if they exist. The DLL's on which these DLL's depend are not copied, in contrast to the dependencies of ruby.exe and its libraries, which are resolved recursively.

(Although RubyScript2Exe knows how to handle application directories, you still have to mention your personal DLL's by hand. Yes, the DLL's are embedded twice... I want to change this in the future.)

On one location, I was not supposed to change the application for this kind of things. So I did the following trick:

c:\home\erik> type dlls.rb
require "rubyscript2exe"
RUBYSCRIPT2EXE.dlls = ["some.dll", "another.dll"]

c:\home\erik> ruby -r dlls rubyscript2exe.rb application.rb

Like RUBYSCRIPT2EXE.dlls=, you can use RUBYSCRIPT2EXE.bin= as well for EXE's and (non-library) DLL's and SO's. In fact, RUBYSCRIPT2EXE.dlls= and RUBYSCRIPT2EXE.bin= are handled exactly the same. For library files (RB's, SO's and DLL's), you can use RUBYSCRIPT2EXE.lib=.

b) RUBYSCRIPT2EXE.tempdir=

Some firewalls block outbound connections to prevent viruses and other bad programs to connect to their friends, unless the program initiating the connection is "white-listed" manually. This "white-list" is based upon the full path to the executable. RubyScript2Exe installs Ruby and the application in a temporary directory in %TEMP%, before starting it. This directory is something like $HOME/.eee/eee.application.243 or %HOME%\eee\eee.application.342. The number part changes every time you start the application. This is not good if you want to "white-list" the program, because ruby.exe is started from another directory every time. To prevent this, you can set RUBYSCRIPT2EXE.tempdir= to the directory name that will be created in %TEMP%:

require "rubyscript2exe"
RUBYSCRIPT2EXE.tempdir = "myapplication"

Now RubyScript2Exe will use $HOME/.eee/myapplication or %HOME%\eee\myapplication every time the program is started. This has a drawback: A second instance of the program tries to install itself in the same directory. It fails to do so, because the directory already exists. It gets even worse when the first instance of the application dies unexpectedly and fails to cleanup its own temporary directory: You won't be able to start the application anymore, unless you remove the temporary directory manually or wait for the OS to do so.

(Use RUBYSCRIPT2EXE.tempdir= only when necessary! It's just a hack. Its behavior might be changed in the future. I don't know yet...)

c) RUBYSCRIPT2EXE.tk=

Embed not only the Ruby bindings for TK, but TK itself as well.

(This is considered experimental.)

d) RUBYSCRIPT2EXE.rubyw=

It's the same as compiling with --rubyscript2exe-rubyw.

e) RUBYSCRIPT2EXE.strip=

It's the same as compiling with --rubyscript2exe-nostrip (but reversed...).

f) RUBYSCRIPT2EXE.is_compil(ing|ed)?

The application is run by RubyScript2Exe on two different moments in time:

g) RUBYSCRIPT2EXE.appdir

If you want to know the full path to the directory of your (embedded) application, use RUBYSCRIPT2EXE.appdir. You can do this when the application is compiled, but even when it isn't yet compiled.

For example (not compiled):

require "rubyscript2exe"
RUBYSCRIPT2EXE.appdir             ===> C:/bin
RUBYSCRIPT2EXE.appdir("README")   ===> C:/bin/README
RUBYSCRIPT2EXE.appdir{Dir.pwd}    ===> C:/bin

For example (compiled):

require "rubyscript2exe"
RUBYSCRIPT2EXE.appdir             ===> C:/home/eee/eee.troep.exe.2/app
RUBYSCRIPT2EXE.appdir("README")   ===> C:/home/eee/eee.troep.exe.2/app/README
RUBYSCRIPT2EXE.appdir{Dir.pwd}    ===> C:/home/eee/eee.troep.exe.2/app

RUBYSCRIPT2EXE.appdir and RUBYSCRIPT2EXE.appdir("bin") are added to ENV["PATH"].

RUBYSCRIPT2EXE.appdir and RUBYSCRIPT2EXE.appdir("lib") are added to $:.

h) RUBYSCRIPT2EXE.userdir

If you want to know the full path to the directory in which the user started the application, use RUBYSCRIPT2EXE.userdir. You can do this when the application is compiled, but even when it isn't yet compiled.

For example (not compiled or compiled):

require "rubyscript2exe"
RUBYSCRIPT2EXE.userdir              ===> C:/work
RUBYSCRIPT2EXE.userdir("app.cfg")   ===> C:/work/app.cfg
RUBYSCRIPT2EXE.userdir{Dir.pwd}     ===> C:/work

(Actually, when running the application uncompiled, this is the directory (Dir.pwd) in which the applications requires rubyscript2exe.rb, which isn't necessarily the directory in which the user started the application.)

i) RUBYSCRIPT2EXE.exedir

If you want to know the full path to the directory in which your executable resides, use RUBYSCRIPT2EXE.exedir. You can do this when the application is compiled, but even when it isn't yet compiled.

For example (not compiled or compiled):

require "rubyscript2exe"
RUBYSCRIPT2EXE.exedir              ===> C:/bin
RUBYSCRIPT2EXE.exedir("app.cfg")   ===> C:/bin/app.cfg
RUBYSCRIPT2EXE.exedir{Dir.pwd}     ===> C:/bin

(Actually, when running the application uncompiled, this is the directory of the main script. Literally: File.dirname(File.expand_path($0)).)

j) RUBYSCRIPT2EXE.executable

If you want to know the full path to the executable, use RUBYSCRIPT2EXE.executable. You can do this when the application is compiled, but even when it isn't yet compiled.

For example (not compiled):

require "rubyscript2exe"
RUBYSCRIPT2EXE.executable   ===> C:/bin/app.rb

For example (compiled):

require "rubyscript2exe"
RUBYSCRIPT2EXE.executable   ===> C:/bin/app.exe

(Actually, when running the application uncompiled, this is the main script. Literally: File.expand_path($0).)

k) Information about EEE

In your application, you can access some information about the environment EEE sets up before spawning your application:

ConstantSet toReplaced by
RUBYSCRIPT2EXE::APPEXEFilename of the generated executable.RUBYSCRIPT2EXE.executable and RUBYSCRIPT2EXE.exedir
RUBYSCRIPT2EXE::EEEEXEeee.exe or eeew.exe or eee_linux or eee_darwin. 
RUBYSCRIPT2EXE::TEMPDIRTemporary directory in which the application resides.RUBYSCRIPT2EXE.appdir
RUBYSCRIPT2EXE::PARMSParameters from the command line. 
RUBYSCRIPT2EXE::QUOTEDPARMSQuoted parameters from the command line. 

(Use these constants only when necessary. Don't consider them "stable"...)

3.4. Tips & Tricks

a) Just Scanning, no Running

RubyScript2Exe runs the application (in a child process) and gathers the require-d files. It's not necessary to run the complete application when all require-s are done in the first couple of statements. You might as well exit right after the require statements:

require "rubyscript2exe"
exit if RUBYSCRIPT2EXE.is_compiling?

Sometimes, one or more require-s are done later on, deep down in a library (e.g. when connecting to a database in DBI). It's not a good idea to do the above trick under this kind of circumstances. You'll miss some libraries...

b) Logging

When using --rubyscript2exe-rubyw, the application runs without a console. This is nice for an application with a GUI. But, although you're a good programmer, sometimes the applications simply dies. If there's no console, there's no back-trace as well. I usually add one of the following lines to the top of my application, even before the require statements:

$stdout = $stderr = File.new("/path/to/temp/application.log", "w")
or
$stdout = $stderr = File.new("/path/to/temp/application.#{Process.pid}.log", "w")

c) Hacking on Location

You can extract, modify and "compile" on location, if you want to.

First, extract the executable:

c:\home\erik> application.exe --eee-justextract

This creates the directory bin (with the files ruby.exe, rubyw.exe and *.dll), the directory lib (with the dependencies *.rb and *.so), the directory app (with the file application.rb (your script) or the application directory) and the files app.eee and eee.exe (or eeew.exe) in the current directory.

It's possible to run your application again with:

c:\home\erik> bin\ruby.exe -r .\bootstrap.rb -T1 empty.rb .\app\application.rb

If the application does a Dir.chdir, try this:

c:\home\erik> bin\ruby.exe -r .\bootstrap.rb -T1 empty.rb
                                            c:\full\path\to\app\application.rb

After hacking app.eee, if necessary, you can "compile" your application again with:

c:\home\erik> eee.exe app.eee newapplication.exe
or
c:\home\erik> eeew.exe app.eee newapplication.exe

On Linux, it's pretty much the same.

$ ./application_linux --eee-justextract

$ export PATH=./bin:$PATH
$ export LD_LIBRARY_PATH=./bin:$LD_LIBRARY_PATH
$ chmod +x ./bin/*
$ ./bin/ruby -r ./bootstrap.rb -T1 empty.rb ./app/application.rb

$ ./eee_linux app.eee newapplication_linux

4. Examples

4.1. Distributions

I ran RubyScript2Exe with four different Ruby distributions (Ruby 1.8.1) on Windows and two versions of Ruby (1.6.7 and 1.8.2) on Linux:

DistributionSize (bytes)
Cygwin1287227
RubyInstaller641840
MinGW428898
MSWin32467110
Linux, Ruby 1.6.7551858
Linux, Ruby 1.8.2574015

The details can be found here.

The test script was nothing more than a little Hello World thing (And the require "rbconfig was just an extra test item...):

require "rbconfig"

puts "Hello World!"

5. License

5.1. License of RubyScript2Exe

RubyScript2Exe, Copyright (C) 2003 Erik Veenstra <rubyscript2exe@erikveen.dds.nl>

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License (GPL), version 2, as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. See the GNU General Public License (GPL) for more details.

You should have received a copy of the GNU General Public License (GPL) along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

The full text of the license can be found here.

5.2. License of your Application

Whatever...


6. Download

Current version is 0.5.3 (29.05.2007). It's a stable release.

Tested on:

You only need rubyscript2exe.rb . It's the current version, packed as an RBA (Ruby Archive, built by Tar2RubyScript) and works on both Windows and Linux. You can download rubyscript2exe.tar.gz if you want to play with the internals of RubyScript2Exe. RubyScript2Exe is available as rubyscript2exe.gem as well.

Send me reports of all bugs and glitches you find. Propositions for enhancements are welcome, too. This helps us to make our software better.

A change log and older versions can be found here. A generated log file can be found here.

RubyScript2Exe is available on SourceForge.net and on RubyForge .

6.1. Mac OS X (Darwin)

I included (experimental) support for Darwin. The Ruby code in the above mentioned packages is able to handle Darwin, but the packages don't include EEE for Darwin. (They would be too big...) For now, you have to compile it yourself:

  1. Get eee.pas from the archive.
  2. Download the compiler.
  3. Compile (fpc -Xs -B eee.pas).
  4. Rename eee to eee_darwin.

(I've put a precompiled eee_darwin on my site, but it may be newer than (and therefor incompatible with) the released Ruby code.)

RubyScript2Exe searches for eee_darwin (or eee_linux or eee.exe or eeew.exe) in 3 locations:

  1. In rubyscript2exe.rb (or rubyscript2exe/ when using rubyscript2exe.tar.gz).
  2. In the directory in which rubyscript2exe.rb is located.
  3. In the current directory.

This means that you can simply put eee_darwin in the same directory as rubyscript2exe.rb (location 2) or in the current directory (location 3).

If you want to repackage RubyScript2Exe (location 1) with an embedded eee_darwin, do this:

  1. Extract rubyscript2exe.tar.gz, or extract rubyscript2exe.rb (ruby rubyscript2exe.rb --tar2rubyscript-justextract)
  2. Copy eee_darwin to rubyscript2exe/.
  3. Recreate rubyscript2exe.rb (ruby tar2rubyscript.rb rubyscript2exe/) (optional)

7. Known Issues


 RSS 

SourceForge.net Logo

RubyForge

Nedstat Basic - Free web site statistics

aior all allinone allinoneruby applications archive bin browser code codesnippet codesnippets compile compiler computer computerlanguage dialog dialogs distribute distributing distributingrubyapplications distribution eee eee-file eeefile erik erikveen erikveenstra exe executable exerb file graphical graphicaluserinterface gui gz html http httpserver iloveruby interface jar jit just justintime lang language one pack package packaging packing packingrubyapplications programming programminglanguage rar rb rb2bin rb2exe rba rbarchive rbtobin rbtoexe rbw ruby ruby2bin ruby2exe rubyapplications rubyarchive rubycompiler rubyscript rubyscript2 rubyscript2exe rubyscripts rubyscripttoexe rubytobin rubytoexe rubyweb rubywebdialog rubywebdialogs script scripts server snippet snippets t2rb t2rs tar tar2rb tar2rbscript tar2rs tar2rscript time ui user userinterface veenstra web webbrowser webdialog webdialogs window windowinbrowser windows wrap wrapper wxruby zip