Thursday 25 August 2011

Cross compiling ports for ARM under FreeBSD

So this is something I've been working on and off for the last few months, but now I think I found the probably most elegant solution, although it's not working for every single port so far.
I managed to compile important ports like net/mpd5 and www/thttpd as well as some others, but failed for now with net/gateway6 or net-p2p/transmission-daemon.
But some tweaking might fix that.

But in general the following steps will get you to your cross-compiled ports:

1) Get the cross-compiler and tools:

This step is quite easy to achieve. Check out the source tree into /usr/devel (or whichever folder you prefer) and run

make TARGET=arm KERNCONF="" kernel-toolchain toolchain

This will place the cross-compiler, all the necessary tools and the libraries into /usr/obj/arm.arm/usr/devel/

2) Create the cross-compile environment

I started an extra shell (bash), in order not to mess up my current environment, and exported the following env variables:

export CC=/usr/obj/arm.arm/usr/devel/tmp/usr/bin/gcc
export CPP=/usr/obj/arm.arm/usr/devel/tmp/usr/bin/cpp
export CXX=/usr/obj/arm.arm/usr/devel/tmp/usr/bin/g++
export AS=/usr/obj/arm.arm/usr/devel/tmp/usr/bin/as
export NM=/usr/obj/arm.arm/usr/devel/tmp/usr/bin/nm
export RANLIB=/usr/obj/arm.arm/usr/devel/tmp/usr/bin/gnu-ranlib
export LD=/usr/obj/arm.arm/usr/devel/tmp/usr/bin/ld
export OBJCOPY=/usr/obj/arm.arm/usr/devel/tmp/usr/bin/objcopy
export SIZE=/usr/obj/arm.arm/usr/devel/tmp/usr/bin/size
export AR=/usr/obj/arm.arm/usr/devel/tmp/usr/bin/gnu-ar
export STRIPBIN=/usr/obj/arm.arm/usr/devel/tmp/usr/bin/strip
export MACHINE=arm

export MACHINE_ARCH=arm

3) Compile a port:

First create two folders, one into which to install the ports, one to use as working directory for the port. In my case /home/arm/portinstall and /home/arm/portwork. This keeps things clean.

Then cd into a ports directory and issue the following command:

make PREFIX=/home/arm/portinstall WRKDIRPREFIX=/home/arm/portwork CONFIGURE_ARGS+="--host=arm --prefix=/home/arm/portinstall" LDFLAGS+="-rpath=/usr/obj/arm.arm/usr/devel/tmp/lib:/usr/obj/arm.arm/usr/devel/tmp/usr/lib:/home/arm/portinstall/lib:/usr/obj/arm.arm/usr/devel/lib -L/usr/obj/arm.arm/usr/devel/tmp/lib -L/usr/obj/arm.arm/usr/devel/tmp/usr/lib -L/home/arm/portinstall/lib -L/usr/obj/arm.arm/usr/devel/lib" NO_PKG_REGISTER=1 clean install

The command explained:
PREFIX sets the installation prefix for the port
WRKDIRPREFIX sets the working directory
CONFIGURE_ARGS+= are additional arguments passed to the configure script, which does not always observe the environment variables. Therefore prefix and host are declared again. For ftp/curl for example you also need to pass --without-random.
LDFLAGS sets the library search paths. As you're cross-compiling you need to make sure that the program links to the correct library, the ARM one not the one of the host you're compiling on.
This is fixed using the -L flags and the -rpath flag. Experience has shown you need both.
NO_PKG_REGISTER needs to be set in order to avoid the package being registered for the local system. If you cross-compile a port that is not already also installed in the current system, you also don't want the system to think it has been installed, where it's actually not, as it's been cross-compiled.

The information in this post is based on the outdated information found here

Some ports might need additional tweaking, by passing additional options to LDFLAGS or CONFIGURE_ARGS.
Ports might also need additional compilers or make tools (like gmake) and might try to install them as part of the ports installation process. You have to make sure that they are installed on the local system beforehand, as the cross-compiler will generate ARM binaries otherwise, and the cross-compilation of the main port will fail.

An other possibility is to create a separate jail in which you do the cross compilation and install the ports in the default directory instead of using the PREFIX directive. It's a bit more messy as all ARM and non ARM ports are installed in the same location, but you can use a

make package

and then install the package on the destination machine.
The biggest problem in this situation though are the ports which have the same port as build dependency and run-time dependency. In that case you'd need an ARM and a non ARM port in the same place, which won't work..
Having not personally tried the jails solution, I can not suggest a proper workaround for this problem.

[edit]
Aleksandr Rybalko also has some hints about cross-compiling which can be found here
Specifically point 2 in that blog deals with dependencies.

Installing Matlab 2011a on FreeBSD

After having had to move my Linux box from my desk due to Occupational Health and Safety issues (It seems that 5 PC's around you are just too many), I had to install Matlab on my main PCBSD 8.2 box.
A scary task after having failed with 2009b, and 2010a a while ago (on a box running 8-CURRENT though).

Now it seems to work just fine. All you need is the Linux compatibility layer and libraries and tweak some of the files:

For the installation:

Edit the "installer" file and replace

#!/bin/sh
with
#!/compat/linux/bin/sh

Then install Matlab, muck around with the license stuff, and cd to the installation folder.

In the installation folder edit file "bin/matlab" and replace

#!/bin/sh
with
#!/compat/linux/bin/sh

as before. Then edit "bin/util/oscheck.sh" and again replace

#!/bin/sh
with
#!/compat/linux/bin/sh

Then start Matlab by simply typing "matlab"

It worked, and the java interface still crashes when trying to work with multiple directories containing lots (about 10000 or more) of files, like it did with 2010b under Linux.
Well, gotta live with that.