# 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`) - Ruby 2.5 or later (e.g. `ruby` or `jruby`) Optional: - Git (to update mruby source and integrate mrbgems easier) - C++ compiler (to use mrbgems which include `*.cpp`, `*.cxx`, `*.cc`) - Bison (to compile `mrbgems/mruby-compiler/core/parse.y`) - gperf (to compile `mrbgems/mruby-compiler/core/keywords`) Note that `bison` bundled with macOS is too old to compile `mruby`. Try `brew install bison` and follow the instruction shown to update the `$PATH` to compile `mruby`. We also encourage you to upgrade `ruby` on macOS in similar manner. ## Build To compile `mruby` with the default build configuration, just invoke `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`. You can specify your own configuration file by the `MRUBY_CONFIG` environment variable (you can use `CONFIG` for shorthand for `MRUBY_CONFIG`). If the path doesn't exist, `build_config/${MRUBY_CONFIG}.rb` is used. The default configuration is defined in the `build_config/default.rb` file. Those build configuration files contain the build configuration of mruby, for example: ```ruby MRuby::Build.new do |conf| conf.toolchain :gcc end ``` All tools necessary to compile mruby can be set or modified here. ## Build Configuration We wish you submit a pull-request to `build_config/PLATFORM.rb`, once you created a new configuration for a new platform. Inside the configuration file, 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 conf.toolchain :gcc ``` #### clang Toolchain configuration for the LLVM C Compiler clang. Mainly equal to the GCC toolchain. ```ruby conf.toolchain :clang ``` #### Visual Studio 2010, 2012 and 2013 Toolchain configuration for Visual Studio on Windows. If you use the [Visual Studio Command Prompt](), you normally do not have to specify this manually, since it gets automatically detected by our build process. ```ruby conf.toolchain :visualcpp ``` #### Android Toolchain configuration for Android. ```ruby conf.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. For example, - `mruby` - `mirb` The configuration are done via `mrbgems`. See `Mrbgems` section. ### 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 = '/' ``` ### Name of library directory In some environments, the `libmruby.a` file requires a different directory name than `lib`. You can be changed to any name by the `conf.libdir_name` accessor. ```ruby conf.libdir_name = 'lib64' ``` Alternatively, it can be changed via the environment variable `MRUBY_SYSTEM_LIBDIR_NAME` when the `rake` command is run. ```console $ export MRUBY_SYSTEM_LIBDIR_NAME=lib64 $ rake clean all ``` NOTES: - This environment variable `MRUBY_SYSTEM_LIBDIR_NAME` does not affect `MRuby::CrossBuild`. In other words, if you want to change it for `MRuby::CrossBuild`, you must set it with `MRuby::CrossBuild#libdir_name=`. - If you want to switch this environment variable `MRUBY_SYSTEM_LIBDIR_NAME`, you must do `rake clean`. A bad usage example is shown below. ```console $ rake clean all $ rake MRUBY_SYSTEM_LIBDIR_NAME=lib64 install ``` ### 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 an 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 filename 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 ``` ### Preallocated Symbols Preallocated symbols are always enabled. Symbol IDs used in C source code (via `MRB_SYM()` etc.) are resolved to compile-time constants during the build process. ### Mrbgems `mruby` comes with the (sort of) packaging system named `mrbgems`. To specify `gem`, you can use `conf.gem` in the configuration file. ```ruby # Integrate a bundled Gem you see in `mrbgems` directory conf.gem :core => 'mruby-something' # Integrate a Gem from GitHub conf.gem :github => 'someone/mruby-another' # Integrate a mruby binary Gem conf.gem :core => 'mruby-bin-mruby' # Integrate a interactive mruby binary Gem conf.gem :core => 'mruby-bin-mirb' # Integrate GemBox (set of Gems) conf.gembox "default" ``` A GemBox is a set of Gems defined in `mrbgems/default.gembox` for example. It's just a set of `mrbgem` configurations. There is a `RubyGem` (gem for CRuby) named `mgem` that help you to manage `mrbgems`. Try `gem install mgem`. `mgem` can show you the list of registered `mrbgems`. See [doc/guides/mrbgems.md](mrbgems.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 cross-compilation, the build configuration 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| 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`. You can find examples under the `build_config` directory. ### Mrbtest in Cross-Compilation In cross compilation, you can run `mrbtest` on an 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 `build` directory will be created in the root directory. The structure of this directory will look like this: ``` +- build | +- host | +- LEGAL <- License description | +- bin <- Binaries (mirb, mrbc and mruby) | +- lib <- Libraries (libmruby.a and libmruby_core.a) | +- mrbc <- Minimal mrbc place | +- mrbgems <- Compilation result from mrbgems | +- mrblib <- Compilation result from mrblib | +- src <- Compilation result from C sources ``` The compilation workflow will look like this: - compile minimal `mrbc` from `src` and `mrblib` sources - compile all files under `src` (object files will be stored in `build/host/mrbc/src`) - compile `mruby-compiler` gem - create `build/host/mrbc/lib/libmruby_core.a` out of all object files (C only) - create `build/host/mrbc/bin/mrbc` via `mruby-bin-mrbc` gem - compile all files under `src` and store result in `build/host/src` - create `build/host/mrblib/mrblib.c` by compiling all `*.rb` files under `mrblib` with `build/host/mrbc/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) - compile (normal) mrbgems specified in the configuration file - create `build/host/lib/libmruby.a` from object files from gems and `libmruby_core.a` - create binary commands according to binary gems (e.g. `mirb` and `mruby`) - copy binaries under `build/host/bin` to `bin` directory ``` _____ _____ ______ ____ ____ _____ _____ ____ | 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 | | | +- mrbgems | | | +- src | +- i386 | +- bin <- Cross-compiled Binaries | +- include <- Header Directory | +- lib <- Cross-compiled Libraries | +- mrbgems | +- mrblib | +- src ``` 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, except for we don't have `host/mrbc` directory (`host` directory itself works as placeholder for `mrbc`). Afterwards the cross compilation process proceeds like this: - cross-compile all files under `src` and store result in `build/i386/src` - create `build/i386/lib/libmruby_core.a` out of C object files - create `build/i386/mrblib/mrblib.c` by compiling all `*.rb` files under `mrblib` with native `build/host/bin/mrbc` - cross-compile `build/i386/mrblib/mrblib.c` to `build/i386/mrblib/mrblib.o` - create `build/i386/lib/libmruby.a` from object files from gems and `libmruby_core.a` - create binary commands according to binary gems (e.g. `mirb` and `mruby`) - copy binaries under `build/host/bin` to `bin` directory ``` _______________________________________________________________ | 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| conf.toolchain :gcc conf.cc.defines << 'MRB_NO_STDIO' 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. ## Embedding `mruby` in Your Application After the build, you will get `libmruby.a`. You can link it to your application. For compiler options and library path, you can use `mruby-config` command for convenience. `mruby-config` command prints the configuration used for `libmruby.a`. ```console $ mruby-config --help Usage: mruby-config [switches] switches: --cc print compiler name --cflags print flags passed to compiler --ld print linker name --ldflags print flags passed to linker --ldflags-before-libs print flags passed to linker before linked libraries --libs print linked libraries --libmruby-path print libmruby path --help print this help ``` For example, when you have a C source file (`c.c`) and try to compile and link it with `libmruby.a`, you can run the following command, ``` `mruby-config --cc --cflags` c.c `mruby-config --ldflags --libs` ``` When you use `make`, add following lines in `Makefile` ``` MRB_CONFIG = CFLAGS = `$(MRB_CONFIG) --cflags` LDFLAGS = `$(MRB_CONFIG) --ldflags` LIBS = `$(MRB_CONFIG) --libs` ``` ## Install To install the files in the `bin`, `include` and `lib` directories generated by the "host" build target into a system directory, do the following: ```console $ rake install ``` If there are multiple build targets in the build configuration file, to install the products of all build targets, do the following: ```console $ rake install:full ``` To install only one of several build targets, e.g., the "its-mine" build target, do the following: ```console $ rake install:full:its-mine ``` To install only the executable files, do the following: ```console $ rake install_bin # only "host" build target $ rake install:bin # all build targets $ rake install:bin:its-mine # only "its-mine" build target ``` ### Installation Directory The installation directory is `/usr/local` for the "host" build target and `/usr/local/mruby/` for the others. To change them, you can set the environment variable `PREFIX` or use `MRuby::Build#install_prefix = dir` in your build configuration file. The `PREFIX` environment variable affects all build targets and changes the `/usr/local` part. The `MRuby::Build#install_prefix` can be set for each individual build target. In this case, the environment variable `PREFIX` is ignored. Also, if the environment variable `DESTDIR` is set, it will prepend to the path obtained by `install_prefix` to determine the final write directory. This is intended for temporary file expansion by the user's package work. --- To summarize: - The default value of the environment variable `PREFIX` is `/usr/local`. - For the "host" build target, the default value of `MRuby::Build#install_prefix` is ``. - For a build target other than "host", the default value of `MRuby::Build#install_prefix` is `/mruby/`. - If the environment variable `DESTDIR` is set, the actual write directory is `/`. ### Excluded files In some cases there are files that you do not want to install. In such cases, add a file path filter to the array object `MRuby::Build#install_excludes` to exclude them. The following is an object that can be defined as a file path filter. The `path` variable that appears is a relative path based on `MRuby::Build#build_dir`. - string objects: files matched by `string.match?(path)` are excluded. - regexp object: files matched by `regexp.match?(path)` are excluded. - proc object: files which return true with `proc.call(path)` are excluded. ```ruby # exclude bin/mrbc conf.install_excludes << exefile("bin/mrbc") # exclude all files under lib/ directory conf.install_excludes << %r(^lib/) # exclude bin/mrbtest, but in this case it is recommended to use string instead of proc conf.install_excludes << proc { |path| path == exefile("bin/mrbtest") } ``` By default, it contains only a proc object to exclude `libmruby_core`. ## Tips - If you see compilation troubles, try `rake clean` first.