So you want to try out FreeBSD on a Raspberry Pi? Go right ahead, installation images are available straight from the FTP sites. However, binary packages? Not so much. And building them on the Pi itself is not just slow, you’ll also quite likely burn through your SD card while doing it. This article introduces a way for you to use your much bigger Intel PC to build a custom package mirror for your humble Pi. Yes, that’s right. Your Intel PC can build ARM packages for Pi just fine. Here’s how.
First, get FreeBSD installed on your build system. I’m assuming you know how to do this. Whether you choose to use bare metal or a VM doesn’t matter much. Just keep in mind that a build server requires tons of resources while doing its magic. A bigger machine is always better in this case. Assuming you have FreeBSD 11.1 RELEASE (amd64) installed on your build box, now install Poudriere from ports:
#cd /usr/ports/ports-mgmt/poudriere #make install clean
Make sure you check the Qemu checkbox so you get the capability to build cross-platform package repositories. The build from ports is required because Qemu support doesn’t come with the precompiled upstream binary.
Poudriere is the scaffolding the FreeBSD project itself uses to build its own repositories of binary packages. It offers a neat, clean framework based on Jails to build all kinds for package-sets with different dependencies. The fun part is that it does this without contaminating your base system. The truly amazing bit: it will also do cross-platform builds through emulation. You can use that big fat i7 under your desk to precompile software for a whole fleet of Raspberry Pi’s. Sure, emulation is dog-slow but when you’re dealing with tiny systems like the Pi it still has its advantages.
Have a look at /usr/local/etc/poudriere.conf. This is the main configuration file for the Poudriere framework and it’s fairly well-documented within itself. Depending on whether you’re using ZFS, make sure you set the name of the pool Poudriere should use and also set a source from which to download FreeBSD distribution files. You should also disable Linux binary support, since that’s only available for Intel platforms.
Slow ARM emulation is needed because many ports still expect to be compiled natively on their target hardware. It’s a fact of life we simply have to deal with.
Before you can actually use the Qemu ARM-emulator you’ll need to tell the host kernel how it should deal with executables that were compiled for a different CPU.
#binmiscctl add armv6 \ --interpreter "/usr/local/bin/qemu-arm-static" \ --magic "\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00" \ --mask "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff" \ --size 20 \ --set-enabled
The binmiscctl utility tells the running kernel about binary formats. Without going into too much detail, the ‘magic’ numbers and the mask have to do with how a running kernel can differentiate between different flavors of executable files.
At this point you’re ready to create your ARMv6 jail for package building. At the time of writing I’m on FreeBSD 11.1-p9 so I’m pulling the current patch-level of this release from SVN.
#poudriere jail -c -j raspberry -v releng/11.1 -m svn -a arm.armv6
Now sit back and relax while your system cross-compiles the entire FreeBSD userland for ARMv6 and stuffs it into a jail. This will take a while. Don’t be fooled, though. This process is your Intel CPU executing Intel code, only to generate ARM-compatible binary files. So while building the jail is a big process, it’s much faster than the package-building process will be later. Package-building involves your Intel CPU acting like it’s an ARM, executing ARM code to generate ARM-compatible binary files “natively”.
Pull in the ports collection
While you wait for the jail to complete, you can make good use of the time to pull in a ports collection from which to build your new packages. Downloading a bunch of files hardly interferes with the CPU-heavy task of compiling FreeBSD, so go right ahead.
#poudriere ports -c -p head
This pulls in the current ports collection for use by the Poudriere framework. This is different from the local system’s ports collection which you may or may not have in-place at this point in time. If you’re using the default poudriere.conf file, you should make sure you have a local distfiles-cache directory present. If not, create the directory /usr/ports/distfiles now or change Poudriere’s configuration to point somewhere more appropriate for your case.
Make a list, check it twice
Now you’re ready to make a list of packages that you want to build. Let’s assume you want to build a simple web server based on Nginx. This would require you to build the www/nginx package and all of its runtime dependendies. Fortunately, Poudriere is intelligent enough to figure out what you need from simple instructions. Create a text file containing just the following line. I’m using /root/shoppinglist.txt for my tests.
One of the main strengths of the FreeBSD ports framework is that it allows you to easily set compile-time options for individual packages. So go ahead, configure your packages to suit your personal tastes and needs. When compiling for tiny systems like a Raspberry Pi I usually choose to disable building documentation and examples, which come with many of the packages. These just take needless space on your SD card anyway. We have the internet for documentation nowadays. Also, if you don’t plan on hooking up a display to your Pi, try disabling all X11 support. That’ll make the list of dependencies a whole lot lighter.
#poudriere options -c -f /root/shoppinglist.txt -p head
This will show you a bunch of screens allowing you to configure options for each of the dependencies. Set these to your heart’s content. When you’re done, it’s time to start building.
#poudriere bulk -j raspberry -p head -f /root/shoppinglist.txt
This is where the serious business starts. This command will check your environment and start spinning up ‘builders’. These are clones of the initial ‘raspberry’ jail, equal in number to the number of cores on your host CPU, all dedicated to building packages. You’re using CPU emulation so the process of package-building will be incredibly slow compared to native compilation. When it’s done, you’ll find a whole bunch of files in /usr/local/poudriere/data/packages. You can use these directly on your Pi. How you make this directory structure into a reliable remote package repository for use by the pkg toolbox on your Pi is the subject of the next blog post.