Cross Compile Ruby for the Orange Pi Zero

Compiling software directly on the Orange Pi Zero can take some time. CPU, Memory and IO are limiting factors on embedded devices. We can however do the heavy lifting on another more powerful machine. In this example I show how to cross compile Ruby for the Orange Pi Zero. This example shows how to build a statically linked Ruby binary. By statically linking, you avoid having to maintain additional shared libraries.

For the build I used Ubuntu Xenial 16.04 running inside a LXC container. You could use anything running Ubuntu Xenial 16.04 be it a virtual machine, workstation or like myself, an LXC container.

Download the build tools

In addition to the cross compiling toolchain, Ruby is also a required dependency to build Ruby. The reason for this is that the build process runs some Ruby scripts, which checks if dependencies like readline and gdbm are available.

# apt-get install gcc-multilib-arm-linux-gnueabihf build-essential autoconf bison wget ruby

Download the source packages

Download the source packages into your /usr/src directory. I used wget but you could use curl if you prefer.

# cd /usr/src
# wget ftp://ftp.gnu.org/gnu/ncurses/ncurses-6.0.tar.gz
# wget ftp://ftp.gnu.org/gnu/readline/readline-6.3.tar.gz
# wget ftp://ftp.gnu.org/gnu/gdbm/gdbm-1.13.tar.gz
# wget http://zlib.net/zlib-1.2.11.tar.gz
# wget ftp://sourceware.org/pub/libffi/libffi-3.2.1.tar.gz
# wget https://www.openssl.org/source/openssl-1.1.0g.tar.gz
# wget https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.2.tar.gz

Extract the source packages and symlink

# cd /usr/src
# tar xvfz ncurses-6.0.tar.gz
# tar xvfz readline-6.3.tar.gz
# tar xvfz gdbm-1.13.tar.gz
# tar xvfz zlib-1.2.11.tar.gz
# tar xvfz libffi-3.2.1.tar.gz
# tar xvfz openssl-1.1.0g.tar.gz
# tar xvfz ruby-2.4.2.tar.gz

# ln -s ncurses-6.0 ncurses
# ln -s readline-6.3 readline
# ln -s gdbm-1.13 gdbm
# ln -s zlib-1.2.11 zlib
# ln -s libffi-3.2.1 libffi
# ln -s openssl-1.1.0g openssl
# ln -s ruby-2.4.2 ruby

Cross Compile ncurses

# cd /usr/src/ncurses
# CC=arm-linux-gnueabihf-gcc CPPFLAGS="-P" ./configure --host=arm-linux-gnueabihf --without-cxx-binding
# make
# make install DESTDIR=`pwd`/target

Cross Compile readline

You must run autoconf before building readline. I ran into the following error otherwise configure: error: cannot run test program while cross compiling You also need to tell the compiler and linker where the ncurses dependency can be found. In this case ncurses was installed to /usr/src/ncurses/target

# cd /usr/src/readline
# autoconf
# CC=arm-linux-gnueabihf-gcc CFLAGS="-I/usr/src/ncurses/target/usr/include" LDFLAGS="-L/usr/src/ncurses/target/usr/lib" ./configure --host=arm-linux-gnueabihf --enable-static --disable-shared
# make
# make install DESTDIR=`pwd`/target

Cross Compile gdbm

# cd /usr/src/gdbm
# CC=arm-linux-gnueabihf-gcc CFLAGS="-I/usr/src/readline/target/usr/local/include -I/usr/src/ncurses/target/usr/include" LDFLAGS="-L/usr/src/readline/target/usr/local/lib -L/usr/src/ncurses/target/usr/lib" ./configure --host=arm-linux-gnueabihf --enable-static --disable-shared
# make
# make install DESTDIR=`pwd`/target

Cross Compile zlib

# cd /usr/src/zlib
# CC=arm-linux-gnueabihf-gcc ./configure --static
# make
# make install DESTDIR=`pwd`/target

Cross Compile libffi

# cd /usr/src/libffi
# CC=arm-linux-gnueabihf-gcc ./configure --host=arm-linux-gnueabihf --enable-static --disable-shared
# make
# make install DESTDIR=`pwd`/target

Cross Compile openssl

# cd /usr/src/openssl
# CC=arm-linux-gnueabihf-gcc ./Configure -lpthread linux-armv4 no-shared
# make
# make install DESTDIR=`pwd`/target

Cross Compile Ruby

You will notice the ./configure command for Ruby is quite a lot longer than the others. You must tell the compiler and linker where to find the static libraries and headers for the dependencies we have built. As we chose not to install them into the system, each built dependency can be found under /target in the respective directory.

# cd /usr/src/ruby
# CC=arm-linux-gnueabihf-gcc CFLAGS="-I/usr/src/ncurses/target/usr/include -I/usr/src/zlib/target/usr/local/include -I/usr/src/gdbm/target/usr/local/include -I/usr/src/libffi/target/usr/local/include -I/usr/src/openssl/target/usr/local/include -I/usr/src/readline/target/usr/local/include" LDFLAGS="-L/usr/src/ncurses/target/usr/lib -L/usr/src/zlib/target/usr/local/lib -L/usr/src/gdbm/target/usr/local/lib -L/usr/src/libffi/target/usr/local/lib -L/usr/src/openssl/target/usr/local/lib -L/usr/src/readline/target/usr/local/lib" ./configure --host=arm-linux-gnueabihf --disable-install-doc --disable-shared --enable-static --with-static-linked-ext --without-dbm --with-gdbm
# make
# make install DESTDIR=`pwd`/target

Redistribute

All being well Ruby should now be compiled and static linked against the dependencies. Now create an archive of this build to distribute to you Orange Pi Zero

# tar cvfz /usr/src/ruby-2.4.2-static.tar.gz -C /usr/src/ruby/target/ .

Once the archive is built, transfer it to your Orange Pi Zero. I like to use scp for this, but use what works best for you. If you are using the scp example, ensure you replace the IP address with the one of your Orange Pi.

# scp /usr/src/ruby-2.4.2-static.tar.gz root@10.10.10.10:~

Install

Now login to your Orange Pi via SSH and look in the /root directory. You will see the ruby-2.4.2-static.tar.gz file. To install the cross compiled ruby do the following.

# tar cvfz /root/ruby-2.4.2-static.tar.gz -C /

Testing

Ruby is now ready to use, you can test it by running ruby -v. You could also inspect the Ruby binary to see which libraries are linked against it.

# ruby -v
ruby 2.4.2p198 (2017-09-14 revision 59899) [arm-linux-eabihf]
# ldd `which ruby`
libpthread.so.0 => /lib/arm-linux-gnueabihf/libpthread.so.0 (0xb6ee7000) libpthread.so.0 => /lib/arm-linux-gnueabihf/libpthread.so.0 (0xb6ee7000) libdl.so.2 => /lib/arm-linux-gnueabihf/libdl.so.2 (0xb6ed4000) libcrypt.so.1 => /lib/arm-linux-gnueabihf/libcrypt.so.1 (0xb6e95000) libm.so.6 => /lib/arm-linux-gnueabihf/libm.so.6 (0xb6e1d000) libgcc_s.so.1 => /lib/arm-linux-gnueabihf/libgcc_s.so.1 (0xb6df5000) libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0xb6d09000) /lib/ld-linux-armhf.so.3 (0xb6f0b000)

Summary

In this guide you have managed to cross compile ruby, its required dependencies and get it running on your Orange Pi Zero. I hope you found this useful, if you encountered an issue please leave a comment and I will do my best to help you.

Ruby On Rails Dynamic Button Text With Form Partial

Recently I have been working on a project using Ruby on Rails. I came across a requirement to Dynamically change the button text on a form partial.

When using a form partial, you are able to use the same block of HTML / ERB code for both create and edit. However the button text for both controller methods would be the same.

The way I have got round this is to do the following.

<%= f.submit @product.persisted? ? "Update" : "Create" %>

In my example, the form that I am working with either creates or updates a Product. By using a shorthand if statement I am able to change the text on the button by checking if the object is already persisted.

When editing an object it will already be persisted to the database, when creating a new object it will not yet be persisted.

This is just one way of achieving the desired functionality, have you come across any other ways? Please leave a comment I would be interested to hear your feedback.