# Compile

mruby uses Rake to compile and cross-compile all libraries and
binaries.

## Prerequisites

To compile mruby out of the source code you need the following tools:
* C Compiler (e.g. `gcc` or `clang`)
* Linker (e.g. `gcc` or `clang`)
* Archive utility (e.g. `ar`)
* Parser generator (e.g. `bison`)
* Ruby 2.0 or later (e.g. `ruby` or `jruby`)

Note that `bison` bundled with MacOS is too old to compile `mruby`.
Try `brew install bison` and follow the instuction shown to update
the `$PATH` to compile `mruby`.

Optional:
* GIT (to update mruby source and integrate mrbgems easier)
* C++ compiler (to use GEMs which include \*.cpp, \*.cxx, \*.cc)
* Assembler (to use GEMs which include \*.asm)

## Usage

Inside of the root directory of the mruby source a file exists
called *build_config.rb*. This file contains the build configuration
of mruby and looks like this for example:
```ruby
MRuby::Build.new do |conf|
  toolchain :gcc
end
```

All tools necessary to compile mruby can be set or modified here. In case
you want to maintain an additional *build_config.rb* you can define a
customized path using the *$MRUBY_CONFIG* environment variable.

To compile just call `rake` inside of the mruby source root. To
generate and execute the test tools call `rake test`. To clean
all build files call `rake clean`. To see full command line on
build, call `rake -v`.

## Build Configuration

Inside of the *build_config.rb* the following options can be configured
based on your environment.

### Toolchains

The mruby build system already contains a set of toolchain templates which
configure the build environment for specific compiler infrastructures.

#### GCC

Toolchain configuration for the GNU C Compiler.
```ruby
toolchain :gcc
```

#### clang

Toolchain configuration for the LLVM C Compiler clang. Mainly equal to the
GCC toolchain.
```ruby
toolchain :clang
```

#### Visual Studio 2010, 2012 and 2013

Toolchain configuration for Visual Studio on Windows. If you use the
[Visual Studio Command Prompt](http://msdn.microsoft.com/en-us/library/ms229859\(v=vs.110\).aspx),
you normally do not have to specify this manually, since it gets automatically detected by our build process.
```ruby
toolchain :visualcpp
```

#### Android

Toolchain configuration for Android.
```ruby
toolchain :android
```

Requires the custom standalone Android NDK and the toolchain path
in `ANDROID_STANDALONE_TOOLCHAIN`.

### Binaries

It is possible to select which tools should be compiled during the compilation
process. The following tools can be selected:
* mruby (mruby interpreter)
* mirb (mruby interactive shell)

To select them declare conf.gem as follows:
```ruby
conf.gem "#{root}/mrbgems/mruby-bin-mruby"
conf.gem "#{root}/mrbgems/mruby-bin-mirb"
```

### File Separator

Some environments require a different file separator character. It is possible to
set the character via `conf.file_separator`.
```ruby
conf.file_separator = '/'
```

### C Compiler

Configuration of the C compiler binary, flags and include paths.
```ruby
conf.cc do |cc|
  cc.command = ...
  cc.flags = ...
  cc.include_paths = ...
  cc.defines = ...
  cc.option_include_path = ...
  cc.option_define = ...
  cc.compile_options = ...
end
```

C Compiler has header searcher to detect installed library.

If you need a include path of header file use `search_header_path`:
```ruby
# Searches ```iconv.h```.
# If found it will return include path of the header file.
# Otherwise it will return nil .
fail 'iconv.h not found' unless conf.cc.search_header_path 'iconv.h'
```

If you need a full file name of header file use `search_header`:
```ruby
# Searches ```iconv.h```.
# If found it will return full path of the header file.
# Otherwise it will return nil .
iconv_h = conf.cc.search_header 'iconv.h'
print "iconv.h found: #{iconv_h}\n"
```

Header searcher uses compiler's `include_paths` by default.
When you are using GCC toolchain (including clang toolchain since its base is gcc toolchain)
it will use compiler specific include paths too. (For example `/usr/local/include`, `/usr/include`)

If you need a special header search paths define a singleton method `header_search_paths` to C compiler:
```ruby
def conf.cc.header_search_paths
  ['/opt/local/include'] + include_paths
end
```

### Linker

Configuration of the Linker binary, flags and library paths.
```ruby
conf.linker do |linker|
  linker.command = ...
  linker.flags = ...
  linker.flags_before_libraries = ...
  linker.libraries = ...
  linker.flags_after_libraries = ...
  linker.library_paths = ....
  linker.option_library = ...
  linker.option_library_path = ...
  linker.link_options = ...
end
```

### Archiver

Configuration of the Archiver binary and flags.
```ruby
conf.archiver do |archiver|
  archiver.command = ...
  archiver.archive_options = ...
end
```

### Parser Generator

Configuration of the Parser Generator binary and flags.
```ruby
conf.yacc do |yacc|
  yacc.command = ...
  yacc.compile_options = ...
end
```

### GPerf

Configuration of the GPerf binary and flags.
```ruby
conf.gperf do |gperf|
  gperf.command = ...
  gperf.compile_options = ...
end
```

### File Extensions
```ruby
conf.exts do |exts|
  exts.object = ...
  exts.executable = ...
  exts.library = ...
end
```

### Mrbgems

Integrate GEMs in the build process.
```ruby
# Integrate GEM with additional configuration
conf.gem 'path/to/gem' do |g|
  g.cc.flags << ...
end

# Integrate GEM without additional configuration
conf.gem 'path/to/another/gem'
```

See doc/mrbgems/README.md for more option about mrbgems.

### Mrbtest

Configuration Mrbtest build process.

If you want mrbtest.a only, You should set `conf.build_mrbtest_lib_only`
```ruby
conf.build_mrbtest_lib_only
```

### Bintest

Tests for mrbgem tools using CRuby.
To have bintests place \*.rb scripts to `bintest/` directory of mrbgems.
See `mruby-bin-*/bintest/*.rb` if you need examples.
If you want a temporary files use `tempfile` module of CRuby instead of `/tmp/`.

You can enable it with following:
```ruby
conf.enable_bintest
```

### C++ ABI

By default, mruby uses setjmp/longjmp to implement its
exceptions. But it doesn't release C++ stack object
correctly. To support mrbgems written in C++, mruby can be
configured to use C++ exception.

There are two levels of C++ exception handling. The one is
`enable_cxx_exception` that enables C++ exception, but
uses C ABI. The other is `enable_cxx_abi` where all
files are compiled by C++ compiler.

When you mix C++ code, C++ exception would be enabled automatically.
If you need to enable C++ exception explicitly add the following:
```ruby
conf.enable_cxx_exception
```

#### C++ exception disabling.

If your compiler does not support C++ and you want to ensure
you don't use mrbgem written in C++, you can explicitly disable
C++ exception, add following:
```ruby
conf.disable_cxx_exception
```
and you will get an error when you try to use C++ gem.
Note that it must be called before `enable_cxx_exception` or `gem` method.

### Debugging mode

To enable debugging mode add the following:
```ruby
conf.enable_debug
```

When debugging mode is enabled
* Macro `MRB_DEBUG` would be defined.
	* Which means `mrb_assert()` macro is enabled.
* Debug information of irep would be generated by `mrbc`.
	* Because `-g` flag would be added to `mrbc` runner.
    * You can have better backtrace of mruby scripts with this.

## Cross-Compilation

mruby can also be cross-compiled from one platform to another. To
achieve this the *build_config.rb* needs to contain an instance of
`MRuby::CrossBuild`. This instance defines the compilation
tools and flags for the target platform. An example could look
like this:
```ruby
MRuby::CrossBuild.new('32bit') do |conf|
  toolchain :gcc

  conf.cc.flags << "-m32"
  conf.linker.flags << "-m32"
end
```

All configuration options of `MRuby::Build` can also be used
in `MRuby::CrossBuild`.

### Mrbtest in Cross-Compilation

In cross compilation, you can run `mrbtest` on emulator if
you have it by changing configuration of test runner.
```ruby
conf.test_runner do |t|
  t.command = ... # set emulator. this value must be non nil or false
  t.flags = ... # set flags of emulator

  def t.run(bin) # override `run` if you need to change the behavior of it
    ... # `bin` is the full path of mrbtest
  end
end
```

## Build process

During the build process the directory *build* will be created in the
root directory. The structure of this directory will look like this:

	+- build
	   |
	   +-  host
	       |
	       +- bin          <- Binaries (mirb, mrbc and mruby)
	       |
	       +- lib          <- Libraries (libmruby.a and libmruby_core.a)
	       |
	       +- mrblib
	       |
	       +- src
	       |
	       +- test         <- mrbtest tool
	       |
	       +- tools
	          |
	          +- mirb
	          |
	          +- mrbc
	          |
	          +- mruby

The compilation workflow will look like this:
* compile all files under *src* (object files will be stored
in *build/host/src*)
* generate parser grammar out of *src/parse.y* (generated
result will be stored in *build/host/src/y.tab.c*)
* compile  *build/host/src/y.tab.c* to  *build/host/src/y.tab.o*
* create *build/host/lib/libmruby_core.a* out of all object files (C only)
* create `build/host/bin/mrbc` by compiling *tools/mrbc/mrbc.c* and
linking with *build/host/lib/libmruby_core.a*
* create *build/host/mrblib/mrblib.c* by compiling all \*.rb files
under *mrblib* with `build/host/bin/mrbc`
* compile *build/host/mrblib/mrblib.c* to *build/host/mrblib/mrblib.o*
* create *build/host/lib/libmruby.a* out of all object files (C and Ruby)
* create `build/host/bin/mruby` by compiling *mrbgems/mruby-bin-mruby/tools/mruby/mruby.c* and
linking with *build/host/lib/libmruby.a*
* create `build/host/bin/mirb` by compiling *mrbgems/mruby-bin-mirb/tools/mirb/mirb.c* and
linking with *build/host/lib/libmruby.a*

```
 _____    _____    ______    ____    ____    _____    _____    ____
| CC  |->|GEN  |->|AR    |->|CC  |->|CC  |->|AR   |->|CC   |->|CC  |
| *.c |  |y.tab|  |core.a|  |mrbc|  |*.rb|  |lib.a|  |mruby|  |mirb|
 -----    -----    ------    ----    ----    -----    -----    ----
```

### Cross-Compilation

In case of a cross-compilation to *i386* the *build* directory structure looks
like this:

	+- build
	   |
	   +-  host
	   |   |
	   |   +- bin           <- Native Binaries
	   |   |
	   |   +- lib           <- Native Libraries
	   |   |
	   |   +- mrblib
	   |   |
	   |   +- src
	   |   |
	   |   +- test          <- Native mrbtest tool
	   |   |
	   |   +- tools
	   |      |
	   |      +- mirb
	   |      |
	   |      +- mrbc
	   |      |
	   |      +- mruby
	   +- i386
	      |
	      +- bin            <- Cross-compiled Binaries
	      |
	      +- lib            <- Cross-compiled Libraries
	      |
	      +- mrblib
	      |
	      +- src
	      |
	      +- test           <- Cross-compiled mrbtest tool
	      |
	      +- tools
	         |
	         +- mirb
	         |
	         +- mrbc
	         |
	         +- mruby

An extra directory is created for the target platform. In case you
compile for *i386* a directory called *i386* is created under the
build directory.

The cross compilation workflow starts in the same way as the normal
compilation by compiling all *native* libraries and binaries.
Afterwards the cross compilation process proceeds like this:
* cross-compile all files under *src* (object files will be stored
in *build/i386/src*)
* generate parser grammar out of *src/parse.y* (generated
result will be stored in *build/i386/src/y.tab.c*)
* cross-compile *build/i386/src/y.tab.c* to *build/i386/src/y.tab.o*
* create *build/i386/mrblib/mrblib.c* by compiling all \*.rb files
under *mrblib* with the native `build/host/bin/mrbc`
* cross-compile *build/host/mrblib/mrblib.c* to *build/host/mrblib/mrblib.o*
* create *build/i386/lib/libmruby.a* out of all object files (C and Ruby)
* create `build/i386/bin/mruby` by cross-compiling *mrbgems/mruby-bin-mruby/tools/mruby/mruby.c* and
linking with *build/i386/lib/libmruby.a*
* create `build/i386/bin/mirb` by cross-compiling *mrbgems/mruby-bin-mirb/tools/mirb/mirb.c* and
linking with *build/i386/lib/libmruby.a*
* create *build/i386/lib/libmruby_core.a* out of all object files (C only)
* create `build/i386/bin/mrbc` by cross-compiling *tools/mrbc/mrbc.c* and
linking with *build/i386/lib/libmruby_core.a*

```
 _______________________________________________________________
|              Native Compilation for Host System               |
|  _____      ______      _____      ____      ____      _____  |
| | CC  | -> |AR    | -> |GEN  | -> |CC  | -> |CC  | -> |AR   | |
| | *.c |    |core.a|    |y.tab|    |mrbc|    |*.rb|    |lib.a| |
|  -----      ------      -----      ----      ----      -----  |
 ---------------------------------------------------------------
                                ||
                               \||/
                                \/
 ________________________________________________________________
|             Cross Compilation for Target System                |
|  _____      _____      _____      ____      ______      _____  |
| | CC  | -> |AR   | -> |CC   | -> |CC  | -> |AR    | -> |CC   | |
| | *.c |    |lib.a|    |mruby|    |mirb|    |core.a|    |mrbc | |
|  -----      -----      -----      ----      ------      -----  |
 ----------------------------------------------------------------
```

## Build Configuration Examples

### Minimal Library

To build a minimal mruby library you need to use the Cross Compiling
feature due to the reason that there are functions (e.g. stdio) which
can't be disabled for the main build.

```ruby
MRuby::CrossBuild.new('Minimal') do |conf|
  toolchain :gcc

  conf.cc.defines = %w(MRB_DISABLE_STDIO)
  conf.bins = []
end
```

This configuration defines a cross compile build called 'Minimal' which
is using the GCC and compiles for the host machine. It also disables
all usages of stdio and doesn't compile any binaries (e.g. mrbc).

## Test Environment

mruby's build process includes a test environment. In case you start the testing
of mruby, a native binary called `mrbtest` will be generated and executed.
This binary contains all test cases which are defined under *test/t*. In case
of a cross-compilation an additional cross-compiled *mrbtest* binary is
generated. You can copy this binary and run on your target system.
