Cross building is compiling a library or executable for a platform other than the one on which the compiler is running.
Cross compilation is especially useful if you are building software for embedded devices where you don’t have an operating system nor a compiler available. Also for building software for not too fast devices, like an Android machine, a Raspberry PI etc.
The only thing that you need to cross compile some code is have the right toolchain installed in your system. A toolchain is just a compiler with some libraries matching the target platform.
Some toochain examples:
|Linux||RaspberryPI||ARM hf compiler (apt-get install g++-arm-linux-gnueabihf)|
|Linux||Windows x64||Mingw compiler for linux (apt-get install g++-mingw-w64)|
|Windows||RaspberryPI||SysProgs toolchain http://gnutoolchains.com/raspberry/|
Once you have the toolchain installed conan can help you to build your conan package with the profiles feature.
Conan profiles contain a predefined set of
That way you can organize your builds adjusting the OS and the compiler of the target and setting CC and CXX environment
variables pointing to the compiler of the toolchain.
First create an example Hello World conan package with the conan new command:
conan new mylib/1.0@lasote/stable -t
We can try to build the hello world example for our own architecture with the
$ conan create lasote/stable ... > Hello World!
So it’s all ok, we’ve built a Hello World conan package for our own architecture.
From Linux to Windows¶
- Install the needed toolchain (for ubuntu should be
sudo apt-get install g++-mingw-w64)
- Create a file named linux_to_win64 with the contents:
[env] CC=/usr/bin/x86_64-w64-mingw32-gcc CXX=/usr/bin/x86_64-w64-mingw32-g++ CONAN_CMAKE_GENERATOR=Unix Makefiles [settings] os=Windows compiler=gcc compiler.version=6.2
CXX are standard environment variables to declare the C/C++ compiler to use respectively.
CONAN_CMAKE_GENERATOR overrides the CMake generator auto-detected by the
CMake conan helper.
conan createusing the created profile.
$ conan create lasote/testing --profile /path/to/linux_to_win64 ... [ 50%] Building CXX object CMakeFiles/example.dir/example.cpp.obj [100%] Linking CXX executable bin/example.exe [100%] Built target example
A bin/example.exe for Win64 platform has been built.
From Windows to Raspberry PI¶
- Install the toolchain: http://gnutoolchains.com/raspberry/
- Create a file named win_to_rpi with the contents:
[settings] os: Linux compiler: gcc compiler.version: 4.6 compiler.libcxx: libstdc++ build_type: Debug arch: armv7hf [env] CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++
conan createusing the created profile.
$ conan create lasote/testing --profile /path/to/win_to_rpi ... [ 50%] Building CXX object CMakeFiles/example.dir/example.cpp.obj [100%] Linking CXX executable bin/example [100%] Built target example
A bin/example for Raspberry PI (Arm hf) platform has been built.
Cross build your project and the requirements¶
Remember that the
test_package command is just a wrapper that exports the recipe, installs the requirements and builds an
example against the exported package to ensure that a package can be reused correctly.
If you want to cross compile your project’s dependencies you can also run:
$ conan install . --profile /path/to/win_to_rpi --build missing
If you have automated your project build with conan you can then just call
conan build . to crossbuild your project too:
$ conan build .
So, now you can commit your profile files to a repository and use them for cross-building your projects.
Cross bulding a library for Android is very similar to the previous examples, except the complexity of managing different architectures (armeabi, armeabi-v7a, x86, arm64-v8a) and the Android API levels.
You can create an Android toolchain or point directly to the desired folders in the NDK and then use a conan profile to declare the needed environment variables, something like:
[settings] compiler=clang compiler.version=3.9 compiler.libcxx=libstdc++ os=Android arch=armv8 build_type=Release [env] CC=clang CXX=clang++ CFLAGS=-fPIC -DPIC -march=armv8a --sysroot=/path/to/ndk/aarch64-api21/sysroot --target=aarch64-linux-android --gcc-toolchain=/path/to/ndk/aarch64-api21 CXXFLAGS=--target=aarch64-linux-android -fPIC -DPIC -march=armv8a --sysroot=/path/to/ndk/aarch64-api21/sysroot--gcc-toolchain=/path/to/ndk/aarch64-api21 LDFLAGS= --target=aarch64-linux-android --sysroot=/path/to/ndk/aarch64-api21/sysroot --gcc-toolchain=/path/to/ndk/aarch64-api21
And then call
conan install using the profile:
$ conan install --profile my_android_profile
But if you want to use different architectures or API levels, generate many profiles handling all the different flags and different paths it will be error prone and very tedious task.
So we created a recipe
android-toolchain/r13b@lasote/testing to be used as a build requirement.
It automatically builds an Android toolchain for your specified conan settings using the NDK already installed with your Android Studio or will install a NDK by itself.
android-toolchain/r13b@lasote/testing recipe will fill the
cpp_info objects with
information about the toolchain. Information like compiler name, cflags, sysroot path etc. You can take a look at the
recipe in its github repository.
To cross build a conan package to Android:
- Create a new conan profile and specify your settings:
[settings] os=Android compiler=clang compiler.version=3.8 compiler.libcxx=libstdc++ arch=armv7v # Adjust os.api_level=21 # Adjust [options] android-toolchain:ndk_path=~/Android/Sdk/ndk-bundle # If you have a NDK already installed [build_requires] android-toolchain/r13b@lasote/testing
- You can use the
installspecifying the profile.
For example, you can try to build
libpng/1.6.23@lasote/testing for Android armv7v architecture, it will also
conan install libpng/1.6.23@lasote/testing --build missing --profile my_android_profile -u
For your conan package you could do:
conan create --build missing --profile my_android_profile -u
Creating Toolchain packages¶
The Build requirements feature allows to create packages that “injects” C/C++ flags
and environment variables through
This is especially useful to create packages with toolchains for cross building because:
- The toolchain package can be specified in a profile and kept isolated from the library packages.
We won’t need to change anything in the conan package of the libraries to cross build them for different targets.
We can have different profiles using different
build_requiresto build our library for example, for Android, Windows, Raspberry PI etc.
- The toolchain package will manage all the complexity of the toolchain, just declaring the environment variables and C/C++ flags that we need to cross build a library. The toolchain package is able to read the specified user settings, so can ‘inject’ different flags for different user settings.
- The toolchain packages can be easily shared as any other conan package, using a conan server.
Let’s see an example of how to create a conan package for a toolchain:
- Create a new
conan new command
$ conan new mytoolchain/1.0@user/testing
- Edit the settings property in the
To know which settings you need to specify it is useful to answer two questions:
- Do I need different toolchains for different values of that setting?
- Do I want to restrict the toolchain usage for any value of that setting?
For example, if we are building a toolchain for Raspbian (Raspberry Pi) and we want it working both from Linux and Windows:
- Do I need “os” setting? Optionally, just to restrict to Linux (Raspbian usage). Remember that in cross build, the conan settings means the “target” settings, not the host settings.
- Do I need “compiler” setting? Yes, we are going to restrict the compiler to gcc (clang is not widely supported) and we want to support both 4.9 and 4.6 gcc versions.
- Do I need the “build_type” setting? No, the same toolchain will be able to build both debug and release packages.
- Do I need the “arch” setting? Optionally, just to restrict it to armv7/armv7hf if we would want to support both.
class MytoolchainConan(ConanFile): name = "mytoolchain" version = "1.0" settings = "os", "compiler", "arch"
- Restrict the settings if needed (Optional):
Our recipe can control which settings values and the host machine are valid with the
it will be useful if someone try to install the toolchain with an unsupported setting. But this is optional:
def configure(self): if self.settings.os != "Linux": raise Exception("Only os Linux (Raspbian) supported") if str(self.settings.compiler) != "gcc": raise Exception("Not supported compiler, only gcc available") if str(self.settings.compiler) == "gcc" and str(self.settings.compiler.version) not in ("4.6", "4.9"): raise Exception("Not supported gcc compiler version, 4.6 and 4.9 available") if str(self.settings.arch) not in ("armv7hf", "armv7"): raise Exception("Not supported architecture, only armv7hf and armv7 available") if not tools.OSInfo().is_windows and not tools.OSInfo().is_linux: raise Exception("Not supported host operating system")
- Usually the
source()method is not necessary, unless you are building the toolchain from sources. Remember that the
source()method is executed just once in the cache, and the sources are reused for all the variants of the package. If you need different downloads for different settings/options, you can better use the
- Edit the
build()method to get the toolchain (usually they are precompiled binaries or scripts):
Download the toolchain according to the introduced settings and the current platform. Remember, in cross building the settings values means the “target” settings, not the current machine platform.
def build(self): if self.settings.os == "Windows": tools.download("some windows url for 4.8", "zipname.zip") else: # Linux ...
- Edit the package() method to pack all the needed toolchain files.
You could copy all but sometimes we want to remove some help files or whatever to save disk space:
def package(self): self.copy(pattern="*", src="bin", dst="", keep_path=True)
- Edit the package_info() to export the needed cpp flags/environment variables:
def package_info(self): # Fill self.env_info object if self.settings.arch == "armv7hf": self.env_info.CC = os.path.join(self.package_folder, "bin", "arm-linux-gnueabihf-gcc") self.env_info.CXX = os.path.join(self.package_folder, "bin", "arm-linux-gnueabihf-g++) else: self.env_info.CC = os.path.join(self.package_folder, "bin", "arm-linux-gnueabi-gcc") self.env_info.CXX = os.path.join(self.package_folder, "bin", "arm-linux-gnueabi-g++) # self.env_info.CONAN_CMAKE_FIND_ROOT_PATH = os.path.join(self.package_folder, "sysroot") self.env_info.PATH.append(os.path.join(self.package_folder, "bin")) # Fill self.cpp_info object if needed, those are just an example, check your toolchain docs # to see if it's required some flag to build your code. self.cpp_info.cflags.add("-fPIC") self.cpp_info.sharedlinkflags.append("-mfloat-abi=softfp")
If you want to prepare your toolchain to work with CMake build system take a look to the useful cmake configuration variables,
you can use the
self.env_info object to set them.
- Export the recipe:
$ conan export lasote/testing
- Create one or more profile including your new toolchain build require:
[settings] os=Linux compiler=gcc compiler.version=4.9 compiler.libcxx=libstdc++ arch=armv7 [build_requires] mytoolchain/1.0@lasote/testing
- Use the profile to cross build a conan package with test_package or install:
$ conan install zlib/1.2.8@lasote/testing --profile rpi --build missing
That command will build both our toolchain and the zlib library.
zlib recipe is using
AutoToolsBuildEnvironment() helper for Linux and
CMake helper for Windows,
those helpers will automatically apply the received
env_info will be applied automatically (creating environment variables) without any helper need.
Remember that the conan settings are intended to unify the different names for operating systems, compilers, architectures etc.
Conan has different architecture settings for ARM:
The “problem” with ARM architecture is that frequently are named in different ways, so maybe you are wondering what setting
do you need to specify in your case.
Here is a table with some typical ARM platorms:
|Raspberry PI 1 and 2||
|Raspberry PI 3||
Useful CMake configuration variables¶
If you are using CMake to cross build your project you can adjust some Conan configuration variables, you can also use environment variables:
|conan.conf variable||Environment variable|
Useful conans.tools for cross building¶
from conans import ConanFile, tools class MyLibrary(ConanFile): ...
tools.OSInfo: To get information about the current host. Specially useful building toolchain packages, where we have to differenciate between the settings (that describe the target) and the host. Check the reference
tools.cross_building(self.settings)Function to check if we are cross building. Useful for libraries with cross build support where we need to apply some special flag or do a special action (different package_info…). Check the reference
Check the complete tools reference