Brickstrap is the tool that we use to create the SD card images for ev3dev. But, it turns out that it is super-useful for working with compiled languages on the EV3!

This method of cross-compiling is no longer supported. Please use Docker instead. Brickstrap still exists, but starting with v0.6.0, it only creates disk images from Docker images. If you really want to keep using the old brickstrap, you can use the 0.5.x branch or download the legacy Debian package and install it manually.

Creating a Virtual Environment

Now, create an empty directory to work in. You can name it whatever you like. I am calling it brickstrap.

mkdir brickstrap
cd brickstrap

Then, build an image. Right now there are 4 flavors of ev3dev. The -c option is the “component” definition that is used to build the image It should either be ev3 if you are using the EV3, bone if you are using BeagleBone, rpi1 for Raspberry Pi 0/1 or rpi2 for Raspberry Pi 2. The -p option is the name of the project and must be ev3dev-jessie. The create-rootfs command tells brickstrap to build a file system but to not actually create an image file. This will take 20 to 30 minutes or longer depending on the speed of your machine and Internet connection.

brickstrap -p ev3dev-jessie -c ev3 create-rootfs

Working in the Brickstrap Shell

Once brickstrap has finished creating the root filesystem, you can get a bash shell inside of the directory that was created. This is almost like working in a virtual machine except that qemu is used to run individual commands instead of the whole thing being run inside of a virtual environment.

brickstrap -p ev3dev-jessie -c ev3 shell

Now, you can install packages and run programs almost just as if you were on the actual EV3. Don’t forget to run apt-get update first! For starters, you will want to install build-essential. If you want to use any extra libraries, most have -dev packages that you will need to install. We should probably setup a non-root user, but for now, we are going to do everything as (fake)root.

apt-get install build-essential

There is a magical folder called host-rootfs inside the brickstrap shell that that gives you access to he filesystem of your host machine. You will want to work there so that you can access the files that create from outside of the brickstrap shell. This is important because some things, like ssh, will not work inside the brickstrap shell.

cd /brickstrap/host-rootfs/home/user

Sample Program

Let’s build the classic hello world.

mkdir hello
cd hello
cat > hello.c << EOF
#include <stdio.h>

int main(void) {
    printf("Hello World!\n");
    return 0;
}
EOF
gcc -o hello hello.c
./hello

Nice. It works inside the brickstrap shell. Now, lets get it onto the EV3. You need to open a new terminal (or exit the brickstrap shell) to do this. You may need to use an IP address instead of ev3dev in order to connect.

user@host:~$ cd ~/hello
user@host:~/hello$ scp hello user@ev3dev:~/
user@host:~/hello$ ssh user@ev3dev
user@ev3dev:~$ ./hello

Mounting a Remote File System

Of course, you are not going to want to copy your program like this every time you build it. So, in order to get around this, you can mount part of the filesystem of your host computer on the EV3.

You have a couple of options to do this:

Both of these options essentially do the same thing, just using a different network protocol. They make files that physically exist on your host computer available on the EV3 as if they were real files.

Now, you just need two terminals open. One running the brickstrap shell where you build your program and one connected to the EV3 so that you can run the program.

Using GDB

gdb is the GNU debugger. TODO: need to find a good link for intro to gdb.

Although it is possible to run gdb directly on the EV3, you will quickly run out of memory. To get around this, we will do remote debugging.

On your EV3, install gdbserver.

sudo apt-get install gdbserver

And in your brickstrap shell, install gdb

apt-get install gdb

Now, let’s debug our hello program. First, we need to make sure we compile with debugging symbols (thats the -g flag). You will need to copy the new executable to the EV3 too if you haven’t done the mounting a remote file system thing yet.

gcc -g -o hello hello.c

On the EV3, run gdbserver. host is the name or IP address of your host computer (or VM) and 3333 is an arbitrary TCP port.

gdbserver host:3333 hello

Then back in the brickstrap shell run gdb. target remote tells gdb to connect to your EV3. Host name resolution seems to have issues in the brickstrap shell, so you are better off using the IP address of your EV3 (192.168.0.100 in this example). And of course, the port number needs to match what you used with gdbserver.

gdb hello

This starts an interactive gdb session. You have to type in the commands on each line that starts with (gdb).

...
Reading symbols from /host-rootfs/home/david/work/brickdm/build/hello...done.
...
(gdb) target remote 192.168.0.100:3333
Remote debugging using 192.168.0.144:1234
...
(gdb) break hello.c:4
Breakpoint 1 at 0x8428: file hello.c, line 4.
(gdb) c
Continuing.

Breakpoint 1, main () at hello.c:4
4               printf("Hello World!\n");
(gdb) c
Continuing.
[Inferior 1 (process 1821) exited normally]
qemu: Unsupported syscall: 26
(gdb) q

Since gdb is running in an emulated environment using qemu, you will occasionally see errors like the unsupported syscall above. Most errors don’t seem to cause any problems, but it may limit the use of some features of gdb.

Using a “Real” Cross-Compiler

If you are compiling a larger project, you will quickly notice that while the methods above are faster than compiling on the EV3 itself, they are still slow compared to compiling on your host computer. This is because inside of the brickstrap shell, it is actually running ARM machine code using an emulator instead of running a native binary.

So, we can actually use a “real” cross-compiling toolchain to compile much faster but still use the root file system created by brickstrap as the source of header files and libraries.

I haven’t done this yet with a regular makefile, but I’ll use brickman as an example, which uses CMake for the build system.

Example using Vala and CMake 3.x

First, we will assume that you ran brickstrap as described above to create a root file system on your host computer at /home/user/work/ev3-rootfs. While we are talking about the root files system, let’s get is setup. Do this in the brickstrap shell (see above if you don’t remember how to start the brickstrap shell).

nano /etc/apt/sources.list

We need to edit source package repositories. Edit the file so that it looks like this…

deb http://cdn.debian.net/debian jessie main contrib non-free
deb-src http://cdn.debian.net/debian jessie main contrib non-free

deb http://archive.ev3dev.org/debian jessie main
deb-src http://archive.ev3dev.org/debian jessie main

Save the changes, then run…

apt-get update
apt-get build-dep brickman
apt-get install symlinks

This will install all of the package that are needed when building brickman. Now, for a very important part. Some libraries use absoloute symlinks, which is bad for us when we are cross compiling because we are accessing files in the root file system created by brickstrap from outside of the brickstrap shell. This is why we installed the symlinks package. Simply run…

symlinks -c /usr/lib/arm-linux-gnueabi

This will change any absolute links to relative links and save us a bunch of trouble later. This should be all that we need to do inside of the brickstrap shell.

You will also need to install a cross compiler toolchain and and some other build tools on your host computer (not in brickstrap shell)…

sudo apt-get install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi cmake valac pkg-config

These cross-compiler packages are only available on Ubuntu. Sorry Debian users.

And we need to download the brickman source code…

cd ~/work
git clone https://github.com/ev3dev/brickman.git

The source code will be downloaded to /home/user/work/brickman, but don’t switch to that directory. We are going to build “out of tree” (that means in another directory that is not the source code directory). Let’s make that directory now - and a couple more files too.

mkdir build-area
touch arm-linux-gnueabi.cmake
touch ev3-rootfs-cross.env

Use your favorite text editor to edit the two empty files we just created with touch. First, arm-linux-gnueabi.cmake should look like this…

set(CMAKE_SYSROOT /home/user/work/ev3-rootfs)

set(CMAKE_SYSTEM_NAME Linux)

set(CMAKE_C_COMPILER arm-linux-gnueabi-gcc)
#set(CMAKE_CXX_COMPILER arm-linux-gnueabi-g++)

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

This will tell CMake to use programs on our host computer (which is why we had to install valac and pkg-config on the host computer). But, it will look for header files and libraries inside of the root file system we created with brickstrap. And, of course, it tells us to use the cross-compiler too.

The ev3-rootfs-cross.env file needs to look like this…

SYSROOT_PATH=/home/user/work/ev3-rootfs

export PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1
export PKG_CONFIG_ALLOW_SYSTEM_LIBS=1
export PKG_CONFIG_SYSROOT_DIR=${SYSROOT_PATH}
export PKG_CONFIG_LIBDIR=${SYSROOT_PATH}/usr/lib/arm-linux-gnueabi/pkgconfig
export PKG_CONFIG_LIBDIR=${PKG_CONFIG_LIBDIR}:${SYSROOT_PATH}/usr/lib/pkgconfig
export PKG_CONFIG_LIBDIR=${PKG_CONFIG_LIBDIR}:${SYSROOT_PATH}/usr/share/pkgconfig
export PKG_CONFIG_LIBDIR=${PKG_CONFIG_LIBDIR}:${SYSROOT_PATH}/usr/local/lib/arm-linux-gnueabi/pkgconfig
export PKG_CONFIG_LIBDIR=${PKG_CONFIG_LIBDIR}:${SYSROOT_PATH}/usr/local/lib/pkgconfig
export PKG_CONFIG_LIBDIR=${PKG_CONFIG_LIBDIR}:${SYSROOT_PATH}/usr/local/share/pkgconfig

export XDG_DATA_DIRS=${SYSROOT_PATH}/usr/local/share:${SYSROOT_PATH}/usr/share

These set environment variables so that pkg-config and vala will search for files inside of our root file system instead of the usual places on the host computer. To make these actually take effect, run…

source ev3-rootfs-cross.env

Now, lets actually try to build something…

cd build-area
cmake ../brickman -DCMAKE_TOOLCHAIN_FILE=../arm-linux-gnueabi.cmake
make

If all went well, you should end up with a brickman binary that you can copy to your EV3 and run.

Example using Makefiles

I haven’t actually done this yet, so feel free to edit this page and add more info. The basic gist is that you need to have something like this…

PROGRAM = my-program
CROSS_COMPILE = arm-linux-gnueabi-
SYSROOT = /home/user/work/ev3-rootfs

CC=$(CROSS_COMPILE)gcc
LD=$(CROSS_COMPILE)ld
CFLAGS= --sysroot=$(SYSROOT) -g -I$(SYSROOT)/usr/include

all: $(PROGRAM)

LIBDIR = -L=/usr/lib/arm-linux-gnueabi
#LIBDIR = -L$(SYSROOT)/usr/lib/arm-linux-gnueabi

LIBS = -lpthread

LDFLAGS= $(LIBDIR) $(LIBS)
SOURCE = my_program.c

OBJS = $(SOURCE:.c=.o)

$(PROGRAM): $(OBJS)
    $(CC) -o $@ $(OBJS) $(LDFLAGS)

clean:
    -rm -f $(OBJS) $(PROGRAM)

The important points are that you:

Authors