How to Cross Compile for Raspberry Pi

I prefer to write code on my Mac and then push it to the Pi. That way I don’t have to try to find the Pi version of development tools and editors that I like to use. It also keeps junk off the Pi leaving it lean and mean.

This is fine for interpreted languages. But when I want to write code in C or C++ this becomes a problem. If I compile code on my Mac or even in a Linux box I will run into issues. The Pi has an incompatible chipset. I have to use tools that will compile code that works on the Pi.


Pi Tools

The folks at Raspberry Pi have a repo out on GitHub that has the tools needed for cross compiling.

https://github.com/raspberrypi/tools

Here is where you can find the x64 tools that work on Linux:

https://github.com/raspberrypi/tools/tree/master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64

If you look in the bin folder you will find a number of tools. For this demo I’m going to use the gcc tool located in the subfolder at bin/arm-linux-gnueabihf-gcc.


Ubuntu

From what I’ve read a number of people have had trouble installing the tools and getting them to work on a Mac. It looked easier to get things working on Ubuntu (a popular flavor of Linux).

There are a few ways to install and run the tools on Ubuntu:

  • Create a virtual machine using free tools like VirtualBox
  • Create a Docker container

I’ve done quite a lot of work in virtual machines based on Ubuntu using VirtualBox. I wouldn’t be opposed to using it. I could install Eclipse on it and use that to build C++ apps that work on the Pi. But for this demo this seemed like overkill.

All I need to do is this:

  1. Create a C++ project
  2. Push it to Ubuntu and have it use the Pi tools to compile it
  3. Get the resulting executable and push it to the Raspberry Pi

If I edit on my Mac and have a way to get the executable back, I only need Ubuntu for the second step. All it needs to do is run a make file and give me back the result. There has to be a more efficient way then having to do this:

  • Launch VirtualBox
  • Start the Ubuntu Virtual Machine
  • Login
  • Launch Eclipse
  • Open the project
  • Edit some files
  • Compile the program
  • Push or pull the executable from the VM to the Pi or the Mac

This doesn’t even cover installing Eclipse, setting up the plugins or mapping drives, setting up git repo ssh keys, etc. I’ve also had issues with VirtualBox images imploding. So after all the hassles of setting up I would need to make sure I take snapshots and backup the image on a regular basis.

There must be an easier way.


Docker

Tools like VirtualBox are for creating virtual machines. Docker is used to create “containers.”

As explained in the InfoWorld article Containers 101: Linux container and Docker explained:

“[containers are] something that feels like a virtual machine, but sheds all the weight and startup overhead of a guest operating system.

The other thing I like about them is that they are great for running headless. You can fire them up from the command line. Then you can either set them to continue running, or do a job, then exit. You can even interact with them at the command line level.

To compile code all it needs to do is to start, compile my code and then exit.

What about the executable?

I could setup the container to push the resulting executable to a certain location. But that won’t make it very generic. If I don’t do that and I don’t leave it running, where do I get the executable? For that matter, how would a generic container know where to find my code to compile it in the first place?

Shared Volume

One of the neat tricks about Docker is you can start (run) an image with a path parameter using the run -v flag. You can tell it to map a folder on your hard-drive to a folder within the container.

For example here is a docker run command that will map the the folder ~/raspberry/hello on your Mac to a folder called /build inside the image.

docker run -it -v ~/raspberry/hello:/build mitchallen/pi-cross-compile

If you place a C++ project with a Makefile in ~/raspberry/hello on your Mac, you can compile it within an Ubuntu image using this command:

make -f /build/Makefile

That’s assuming you are using a Docker image setup to work like this, which I have. I’ll show you how you can use it too.


See how it works

To demonstrate how this works you will need to do the following:

  1. Install Docker
  2. Pull the image I created for cross compiling from their hub
  3. Use git to make a local copy of my Raspberry Hello demo
  4. Run the docker container to build the demo
  5. Copy the resulting executable to the Pi
  6. Test it on the Pi to make sure it works

Step 1: Install Docker

Go to https://docs.docker.com/engine/installation/ and install Docker.

Step 2: Pull the image

You can find the special docker image that I created for cross compiling here:

https://hub.docker.com/r/mitchallen/pi-cross-compile/

You can bring it down to your system by opening up a terminal window and issuing a docker pull command:

docker pull mitchallen/pi-cross-compile

Step 3: Clone my Raspberry Hello demo

Clone the project into ~/raspberry/hello.

mkdir ~/raspberry

cd ~/raspberry

git clone https://github.com/mitchallen/pi-hello-cross-compile.git --depth=1 hello

Step 4: Build the demo

docker run -it -v ~/raspberry/hello:/build mitchallen/pi-cross-compile

A new executable should now exist:

ls -l ~/raspberry/hello/bin/hello

Inspect the file to see if it was compiled for ARM (the Raspberry Pi chip):

file ~/raspberry/hello/bin/hello

The output should look like this:

bin/hello: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, not stripped

Step 5: Copy the executable to your Pi

To copy the executable to your Pi you will need a way of getting the file on to it. One way is to install Samba and share a folder.

Login to your Pi and do the following:

Get the latest updates

$ sudo apt-get update

Install Samba

$ sudo apt-get install samba samba-common-bin

Configure Samba

$ sudo nano /etc/samba/smb.conf 

Find the line that says read only and set it equal to no.

Create a Samba password for remote access as user pi.

$ sudo smbpasswd -a pi

Stop and restart the Samba server

$ sudo /etc/init.d/samba stop
$ sudo /etc/init.d/samba restart

Copy the file to your Pi

In Finder wait a bit and you should see your Pi show up under Shared in the left hand column. Connect as user pi using the samba password that you just created.

Copy the executable to your pi via Finder.

Step 6: Run the executable on your Pi

Login to your Pi. Find the executable and run it like this:

./hello

It should print the hello message to the console.


The Makefile

You can view the Raspberry Hello project here:

https://bitbucket.org/mitchallen/pi-hello-cross-compile

At the top of the Makefile you will see this line:

CC = /pitools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf-gcc

When I created the Docker container I set it up to clone the Raspberry Pi Tools to the folder /pitools.

If you want to create a make file to compile your project with my Docker image you will need to reference the tools like that.


Create your own container

How to create Docker containers is beyond the scope of this article. But if you would like to see how I did it you can find the Dockerfile here: https://bitbucket.org/mitchallen/pi-cross-compile.


References


Mitch Allen is a game developer and tech writer. To download his latest games and books please visit his Web site at mitchallen.com