I'm working on porting FreeGLUT for Android, and FreeGLUT is switching to CMake.

I'm more familiar with the GNU Autotools myself, and I'm used to cross-compile with a simple:

./configure --host=arm-linux-androideabi

Can we do that with CMake? Well, it's not well-documented, but yes :) Let's see how to do that for Android (smartphones) and MinGW (.exe for windows).


The principle is:

  • write a "toolchain" CMake script that defines the cross-compiler,

  • mention you're using a "GNU" compiler (a.k.a. GCC) so CMake automatically looks for prefixed cross-compilers tools such as arm-linux-androideabi-cpp or arm-linux-androideabi-ld.

  • pass the toolchain script to CMake using the predefined CMAKE_TOOLCHAIN_FILE variable.

  • REMOVE all the CMake-generated files when you change anything, CMake does not rebuild its cache when the toolchain script or PATH is modified, this creates misleading errors. Hence, building in a subdirectory is crucial.

Here's a simple toolchain script:

SET(CMAKE_SYSTEM_NAME Linux)  # Tell CMake we're cross-compiling
include(CMakeForceCompiler)
# Prefix detection only works with compiler id "GNU"
# CMake will look for prefixed g++, cpp, ld, etc. automatically
CMAKE_FORCE_C_COMPILER(arm-linux-androideabi-gcc GNU)
SET(ANDROID TRUE)

Note: SET(ANDROID TRUE) is not mandatory for CMake, it's just used to detect an Android build in the particular case of FreeGLUT development.

Here's how to invoke it:

PATH=/usr/src/ndk-standalone-9/bin:$PATH
cd /usr/src/freeglut-3.0.0/
mkdir cross-android/ && cd cross-android/
cmake \
  -D CMAKE_TOOLCHAIN_FILE=../android_toolchain.cmake \
  -D CMAKE_INSTALL_PREFIX=/freeglut \
  ..
make -j4
make install DESTDIR=$(pwd)

Now here's a slightly more complex toolchain for MinGW.

The main issue with MinGW is that there are at least 4 possible prefixes depending on which version and which distro you're using. So I ask the user to specify it with GNU_HOST:

SET(CMAKE_SYSTEM_NAME Windows)
include(CMakeForceCompiler)
IF("${GNU_HOST}" STREQUAL "")
    SET(GNU_HOST i586-mingw32msvc)
ENDIF()
# Prefix detection only works with compiler id "GNU"
CMAKE_FORCE_C_COMPILER(${GNU_HOST}-gcc GNU)
# CMake doesn't automatically look for prefixed 'windres', do it manually:
SET(CMAKE_RC_COMPILER ${GNU_HOST}-windres)

I wanted to print an error when GNU_HOST is not specified, but the toolchain script is loaded multiple times, and the command-line variables are not always set (CMake is not super-clean in that regard). So I defined a default, that works.

Sample invocation:

  apt-get install mingw-w64

  mkdir cross-woe/ && cd cross-woe/
  cmake \
    -D GNU_HOST=x86_64-w64-mingw32 \
    -D CMAKE_TOOLCHAIN_FILE=../mingw_cross_toolchain.cmake \
    -D CMAKE_INSTALL_PREFIX=/freeglut \
    ..
  make -j4
  make install DESTDIR=$(pwd)

I used this technique to cross-compile my projects, or other projects that use CMake. mingw-cross-env uses this technique (abeilt with explicit definition of all compiler tools) to provide pre-built windows libraries without altering the existing CMake scripts.

Do you people have tips or a better technique to cross-compile with CMake?

I've been asked where my porting work is.

You can track it at:

http://gitorious.org/freeglut/android

:)

Comment by beuc Sun Mar 4 16:39:45 2012

I am copying the MinGW file for a project of mine, thanks.

BTW, in the cmake invocation did you put the CMAKE_INSTALL_PREFIX on the root filesystem on purpose or did you mean to put the slash at the end of the path?

Thanks... have I said that already?

Antonio http://ao2.it

Comment by Anonymous Thu May 10 15:38:05 2012

Hi,

When running 'make install', CMake will install the files under CMAKE_INSTALL_PREFIX, relative to the DESTDIR parameter (if specified) or to / (if no DESTDIR). In this case, I install in the 'freeglut/' directory relative to the current directory, ready to be zipped / copied to a ms woe box.

Usually when you cross-compile you don't need to install the binary on your system, so we could skip 'make install'.

But some programs like to know where they are installed and store this directory in a variable; some others (like FreeGLUT) install their binaries and data files in a specific hierarchy; that's what I showed how to specify an install prefix.

It would also work with CMAKE_INSTALL_PREFIX as a relative path, though as I mentioned some progs may embed to full compilation path in a variable used at run-time.

Comment by beuc Sat May 19 14:02:08 2012