The Raspberry PI is an amazing mini computer, powerful and cheap, the dream of hobbyists and developers around the world. With its potential to implement embedded systems, it is common to use C/C++ for developing its code, specially when efficiency and performance is relevant. However, even if it is a powerful system for the money, it can be slow while building relatively large C/C++ libraries, so cross-building can be very convenient for developers.

This post will describe how to setup cross building to the Raspberry PI from Windows (from Linux it is also possible, and the configuration would be very similar). A library will be cross-built, a conan package will be uploaded to a server (can be a conan_server, conan.io, or Artifactory), and then such library will be installed and used from the Raspberry PI, i.e. we will build an app and link it against this cross-built library. As bonus points, this post will also explain how to bundle the source code in the package itself, so later the library can be debugged from the Raspberry PI.

Hello world library

The library that will be built and packaged is the one existing in this github repo. It is just a simple C++ “Hello world” library project, using CMake, and nothing special or conan related in it.

We will then start with a package template created with the conan new command:

$ conan new Hello/0.1@user/testing -t  # use your own user

Now, lets just replace the root conanfile.py with this one:

from conans import ConanFile, CMake
import os

class HelloConan(ConanFile):
    name = "Hello"
    version = "0.1"
    settings = "os", "compiler", "build_type", "arch"

    def source(self):
        self.run("git clone https://github.com/memsharded/hello.git")

    def build(self):
        cmake = CMake(self.settings)
        gcc_dbg_src = ""
        if self.settings.compiler == "gcc" and self.settings.build_type == "Debug":
            gcc_dbg_src =  ' -DCMAKE_CXX_FLAGS="-fdebug-prefix-map=%s/hello=src"' % os.getcwd()
        self.run('cmake hello %s %s' % (cmake.command_line, gcc_dbg_src))
        self.run("cmake --build . %s" % cmake.build_config)

    def package(self):
        self.copy("*.h", dst="include", src="hello")
        if self.settings.compiler == "gcc" and self.settings.build_type == "Debug":
            self.copy("*.cpp", dst="src", src="hello")
        self.copy("*.lib", dst="lib", keep_path=False)
        self.copy("*.a", dst="lib", keep_path=False)

    def package_info(self):
        self.cpp_info.libs = ["hello"]

It is very similar to the one created by the template, but with two minor differences. First, as we want to be able to debug the packages in the Raspberry PI, it is necessary to define the gcc flag debug-prefix-map, so it points to the relative source folder src instead of the original one. Because we are going to debug in a different machine, where the original absolute source path will make no sense. Se we just define the flag to CMake (conditionally for the gcc and Debug settings):

if self.settings.compiler == "gcc" and self.settings.build_type == "Debug":
    gcc_dbg_src =  ' -DCMAKE_CXX_FLAGS="-fdebug-prefix-map=%s/hello=src"' % os.getcwd()

Then, we just copy the sources *.cpp to the final package (only for the same settings too.)

if self.settings.compiler == "gcc" and self.settings.build_type == "Debug":
    self.copy("*.cpp", dst="src", src="hello")

This package recipe could be tested natively, by just running:

$ conan test_package
Hello world!

Setting the cross-build toolchain

For this example, we are going to use the SysProg toolchain. We are using the 4.6.3 toolchain, with complete sysroot, which is very convenient. We download the tool, install it in C:\SysGCC\Raspberry and add C:/SysGCC/Raspberry/bin/ to the system PATH.

Now we could just specify cross compilers to the conan command as arguments, like conan test_package -e CXX=some_gcc_compiler, but we can make it easier using a conan profile. So we create a file in <userhome>/.conan/profiles/rpi_gcc46 with the following:

[settings]
    os: Linux
    compiler: gcc
    compiler.version: 4.6
    compiler.libcxx: libstdc++
    build_type: Debug
    arch: armv6
[env]
    CC=arm-linux-gnueabihf-gcc
    CXX=arm-linux-gnueabihf-g++

Note the armv6 architecture and Linux settings, need to be defined, because the default conan settings will correspond to the Windows development box.

As the resulting binary won’t be executable in windows, we change the test_package/conanfile.py so the test() method just checks the existence of the binary:

def test(self):
    if platform.system () != self.settings.os:
        assert os.path.exists("bin/example")
    else:
        self.run(os.sep.join([".", "bin", "example"]))

With this configuration, creating a debug package for the R-PI, can be just done with:

$ conan test_package -pr=rpi_gcc46

Uploading and installing in the Raspberry PI

Once the package has been created locally, it can be uploaded to any conan remote server (conan.io, Artifactory, conan_server):

$ conan upload Hello/0.1@user/testing -r=myremote --all

In the Raspberry PI side, we will just create a very simple consumer project with an example.cpp file:

#include "hello.h"
int main(){
   hello();
}

a CMakeLists.txt script to build it:

Project(Consumer)
cmake_minimum_required(VERSION 2.8.9)

include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()

add_executable(example example.cpp)
target_link_libraries(example ${CONAN_LIBS})

and the conanfile.txt to install dependencies:

[requires]
Hello/0.1@diego/testing

[generators]
cmake

[imports]
src, *.cpp -> src

Note how the the .cpp sources are copied (“imported”) from the package, to the current binary folder, so the debugger can easily locate them.

Installing the cross-built “Hello” package is easy, now we don’t need profiles, as the R-PI defaults are good, so we just set the build_type:

$ conan install .. -s build_type=Debug

Building and debugging

Building our R-PI app is now standard cmake process:

$ mkdir build && cd build
$ cmake .. -DCMAKE_BUILD_TYPE=Debug
$ cmake --build .
$ bin/example
Hello World!

The good thing, is that for this example we have built it for Debug, mode, so we can debug the application and step into the package library code:

$ gdb bin/example
> ...
> Reading symbols from /home/pi/consumer/build/bin/example...done.
(gdb) start
Temporary breakpoint 1 at 0x8914: file /home/pi/consumer/example.cpp, line 4.
Starting program: /home/pi/consumer/build/bin/example
(gdb) step
hello () at src/hello.cpp:5
5               std::cout << "Hello World!\n";

And voilá, we can see the hello.cpp source code line!

Conclusions

Conan is a pure python app, so installing it in the Raspberry PI was as easy as sudo pip install conan.

In this example we have shown how to create packages with debug information for gcc and gdb, but similar approaches can be implemented for other platforms too, for example, packaging the .pdb files of Visual Studio.

As conan is very orthogonal to the build system and compiler, cross building packages with conan is straightforward. Profiles are a very convenient feature to gather together settings, options and environment variables, for easy switching between different development environment and targets.

When cross-building toolchains are more complicated, things can require a bit more of configuration to take into account the variability of those toolchains, but can be certainly done. We are aware of conan users actively using conan to package for Android and iOS systems. In any case, we are preparing some major improvements for management of build requirements, like the Android toolchain, that you will surely love. Keep tuned, follow us on twitter or subscribe to our release annoucements mailing list!