# Using Conan and cross-compile to EV3

## Overview

I'll start with a disclaimer. The goal of this project was to be able to create an environment which I could easily cross-compile c++ code and run on my Lego-Mindstorm running ev3dev (debian linux). I wanted to use conan, for two reasons , one to learn, and the other is that I like the idea of having a packagemanager for c++. I have struggled enough with trying to find and build libraries, and a package manager will make life much eassier. I am by no means a expert on this field. There might be easier and/or better way of doing things, but this is the way I found through trail-error and reading. And docker, well, I like the container idea, but again I did bare mininmum to get things to work..

## Giving Gorby a (debian) life

Gorby needs a system, we already talked about discussed some in the overview. I will stick to the supported one, but it is kind of old. For example the installed glibc is 2.24 which was released in 2016. So quite old, to compare with my host machine which is on version 2.32. When it comes to c++ the std libraries are on version 6.0.22, which means the c++ standard it supports is c++14, which is a bit old. But maybe in another post i will upgrade the EV3 to a later kernel and libc. There is a upgrade page which I guess you can find newer images. The docker image page also provides away to make a image of a newer distribution. This article I will just use the supported image. Which can be downloaded from here.

The download image needs to be unzipped and locate the image file (*.img) Time to add it to the MicroSd-card.

sudo dd if=ev3dev-stretch-ev3-generic-2020-04-10.img of=/dev/sdf bs=4Mb


Assuming that sdf is your inserted device name , you can determine that its the right device by opening a terminal and run journalctl -fk and then insert your microSD card. .eg

$> sudo journactl -fk . . <inser SD card> .... sd 12:0:0:2: [sdf] 124735488 512-byte logical blocks: (63.9 GB/59.5 GiB)  1 When the image has been written to the card , just put it into the slot on the EV3 and boot it up. If everyting is done correctly the ev3 should boot up, and a EV3DEV logo should be seen. ### Connection to EV3 We now need to connect the EV3 so we can ssh to it. I want do a big tutorial on this subject, since the existing alredy covers most of it. I was keen to get everything up and running as fast as I could, so I used bluetooth, and it worked like a charm. I could now connect to Gorby and make some initial assessment. #### Check the Gorby hardware So lets first checkout what kind of network devices that Gorby has and if any ip addresses are connected to them. ip addr | awk '/[0-9]: / {printf("%s %s\n",$2,$9)} /inet / {printf(" %s\n",$2)}'

lo: UNKNOWN
127.0.0.1/8
sit0@NONE: DOWN
usb0: DOWN
usb1: DOWN
bnep0: UNKNOWN
10.42.0.250/24


Just for information, what cpu information can we optain.

lscpu

Architecture:          armv5tejl
Byte Order:            Little Endian
CPU(s):                1
On-line CPU(s) list:   0
Core(s) per socket:    1
Socket(s):             1
Model:                 5
Model name:            ARM926EJ-S rev 5 (v5l)
BogoMIPS:              148.88
Flags:                 swp half thumb fastmult edsp java


There are some things that we can take note of here. First of all its a single core (32 bit), and we can basically only run active thread for that core. That doesn't mean we can't use more threads, it only means that just one instruction at a time, and they need to switch between the threads, the utlization can still be better since many times threads just waits, thats when other threads can do some work.

The Byte order is default (little endian ) so we actually don't need the compiler argument -mlittle-endian, but its good to know.

Checking the family name of the models reveals that it belongs to the ARM9E family which is quite old (2001). Though it enables the direct execution of 8-bit Java bytecode in hardware. The gcc compiler option for this processor is -march=armv5

There is no fpu in the flag field, this means that we don't have a fpu (floating point) unit, which means if we going to use it we need to emulate it, this is a compiler argument (-mfloat-abi=soft), but I don't think the docker has a floating point library installed. So it will be set to hard.

The processor supports thumb instruction, which is used to to improve compiled code-density, as i understand it , it maps directly to the arm instructions, but as far as I understand it it creates smaller binaries with the same functionality. Though its not necessary in this case. Lets checkout the available disk size:

df -h

Filesystem Size Used Avail Use% Mounted on
udev 26M 0 26M 0% /dev
tmpfs 20M 336K 20M 2% /run
/dev/mmcblk0p2 7.3G 1015M 5.9G 15% /
tmpfs 29M 0 29M 0% /dev/shm
tmpfs 5.0M 4.0K 5.0M 1% /run/lock
tmpfs 29M 0 29M 0% /sys/fs/cgroup
/dev/mmcblk0p1 48M 224K 48M 1% /boot/flash
tmpfs 5.7M 0 5.7M 0% /run/user/1000

The 5.9 Gb available is quite enough to just don't care about the thumb instruction.

The half means that the processors, well, its supports store and load register signed 8 bit bytes and 16 bit-halfwords.Unsigned halfword loads are zero-extended to 32 bits. I don't think we need to care about this one. (gcc -mthumb or -marm)

The edsp is digital signal processing unit offer high performance signal processing. We don't need to care about this either, its a feature in the processor.

Lets skip the rest, since its nothing we want to use anyway. But one thing that might be good to know is that we should probably provide the -mtune=arm926ej-s to the compiler, that might do some performace gains.

cat /proc/meminfo | awk '/(MemTotal:|MemFree:|MemAvailable:|SwapTotal:|SwapFree:)/ {printf("%s %s %s %s\n",$1,$2,$3,$2/1024)}'

Name Val Type Mb
MemTotal: 57620 kB 56.2695
MemFree: 1436 kB 1.40234
MemAvailable: 32988 kB 32.2148
SwapTotal: 98300 kB 95.9961
SwapFree: 94716 kB 92.4961

Here is a small explanation:

Memtotal
Total usable memory
MemFree
The amount of physical memory not used by the system
MemAvailable
An estimate of how much memory is available for starting new applications, without swapping.
SwapTotal
Total swap space available
SwapFree
The remaining swap space available

57Mb total memory with 32Mb for running applications is not very much, so we are on scarce resources in comparison to ie. pc. It is necessary to make sure that we don't have things running which are not in use. Though things could have been worse.

One more thing needs to be fixed. Right now it does't seem to have a proper resolver, so running a update on the system with apt doesn't work. Usually this is fixed by editing /etc/resolv.conf But for some reason it didn't work. After some investigation I found this:

systemctl cat systemd-resolved

# /lib/systemd/system/systemd-resolved.service
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Network Name Resolution
Documentation=man:systemd-resolved.service(8)
Documentation=http://www.freedesktop.org/wiki/Software/systemd/resolved
Documentation=http://www.freedesktop.org/wiki/Software/systemd/writing-network-configuration-managers
Documentation=http://www.freedesktop.org/wiki/Software/systemd/writing-resolver-clients
After=systemd-networkd.service network.target

# On kdbus systems we pull in the busname explicitly, because it
# carries policy that allows the daemon to acquire its name.
Wants=org.freedesktop.resolve1.busname
After=org.freedesktop.resolve1.busname

[Service]
Type=notify
Restart=always
RestartSec=0
ExecStart=/lib/systemd/systemd-resolved
WatchdogSec=3min
CapabilityBoundingSet=CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER CAP_NET_RAW CAP_NET_BIND_SERVICE
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=full
ProtectHome=yes
ProtectControlGroups=yes
ProtectKernelTunables=yes
MemoryDenyWriteExecute=yes
RestrictRealtime=yes
SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io

[Install]
WantedBy=multi-user.target

# /lib/systemd/system/systemd-resolved.service.d/resolvconf.conf
# tell resolvconf about resolved's builtin DNS server, so that DNS servers
# picked up via networkd are respected when using resolvconf, and that software
# like Chrome that does not do NSS (libnss-resolve) still gets proper DNS
# resolution; do not remove the entry after stop though, as that leads to
# timeouts on shutdown via the resolvconf hooks (see LP: #1648068)
[Service]
ExecStartPost=+/bin/sh -c '[ ! -e /run/resolvconf/enable-updates ] || echo "nameserver 127.0.0.53" | /sbin/resolvconf -a systemd-resolved'


Actually, its right at the end we kind of find the problem, the nameserver is pointing towards our localhost.. it seems to look for a file and if the file /run/resolvconf/enable-updates exiss the resolver provides the address to the localhost. Well, the sbin/resolvconf doesn't seem to exists?…So that seems abit odd? Well lets fix so that we can get updates.

mkdir -p /etc/systemd/resolved.conf.d
cat <<EOF > /etc/systemd/resolved.conf.d/dns_server.conf
[Resolv]
DNS=192.168.0.1
EOF



That should do the trick. Ok, thats it.. for some system configuration for now.

## Runing with python

ev3dev python interface..

As I already mentioned , I'm not a big fan of python. Its great to do some prototyping and see that everying works, but doing anything more elaborate, it quickly becomes a mess, it isn't a type safe programming language which means the compiler want validate the types during compile time. But its nice to be able to do a fast check I can control the vehicle and recieve data from the sensors.

### Using tramp

I Haven't installed the ev3dev python library on my machine, the reason is that I just wanted to test out that Gorby reacts on my commands and I don't actually care about making anything substantial python. But the documentation is quite good for ev3dev python library. Since I'm a big fan of emacs , and org-mode I can make small scripts and execute it straight away using tramp 2. Or i could just edit a file on gorby using emacs and tramp.

Here is an example:

from time import sleep

from ev3dev2.motor import LargeMotor, OUTPUT_B,OUTPUT_C, SpeedPercent, MoveTank
from ev3dev2.sensor import INPUT_1
from ev3dev2.sensor.lego import TouchSensor
from ev3dev2.led import Leds
from ev3dev2.sound import Sound

sound = Sound()
sound.speak('Hello I am a robot')

tank_drive = MoveTank(OUTPUT_B, OUTPUT_C)
tank_drive.on_for_rotations(SpeedPercent(75), SpeedPercent(75), 10)


here is the actual org-source code when using tramp:

#+NAME: hello-python
#+begin_src python :dir "/ssh:robot@10.42.0.250:/home/robot"
from time import sleep

from ev3dev2.motor import LargeMotor, OUTPUT_B,OUTPUT_C, SpeedPercent, MoveTank
from ev3dev2.sensor import INPUT_1
from ev3dev2.sensor.lego import TouchSensor
from ev3dev2.led import Leds
from ev3dev2.sound import Sound

sound = Sound()
sound.speak('Hello I am a robot')

tank_drive = MoveTank(OUTPUT_B, OUTPUT_C)
tank_drive.on_for_rotations(SpeedPercent(75), SpeedPercent(75), 10)

#+end_src


This makes it much easier to work with. I'm not going to turn this in to an emacs tutorial, this is just an alternative way to test things on target instead of using ssh and edit or alternatively edit on host and use scp.

In this regard python is great , minimal effort to make really small test utilities. On the other hand, the EV3 is quite slow both in regards of cpu speed and memory, that means it takes several seconds before python script actually executes. And as the application becomes bigger the more time and memory it will use to execute.

## Using c++

The c++ is a different beast altogheter, to be able to use the c++ we need to cross-compile. Cross compilation means that the compiler is producing binary code which is not executable on the host machine. For example running a x86_64 host machine the compiler needs to produce a binary for .eg arm which is nativly on the target. This means that when the compiler on the host machine has produced a binary it it needs to be able to run on the target machine. The binary then needs to be transffered to the target where the machine set is the same. Alternativly one could install development and build tools on target and compile there. But that is really not such a great idea. There are (at least) two major reasons why this is a bad idea.

1. It will take up alot of disk space to install a complete development enviroment
2. the EV3 is really slow in comparison to my pc (see check hardware section). It would take ages to compile.

With those reasons, it makes sense to setup a cross-compiler toolchain on the local machine and use that to produce the ELF (arm binary) output.

Setting up a toolchain for cross compilaton can be quite cumbersome. Fortunatly there is a docker with (almost) all the necessary tools installed.

### Setting up Docker

First its necessary to pull the docker image to be able to run it.

docker pull ev3dev/debian-stretch-cross


This command will essentially download the ev3dev docker image to the local machine. We can check that the image is in fact on our host by

docker images | awk '/ev3dev/ {printf("%s\t%s\t%s\n",$1,$2,$3)}'  Repository Tag Image Id ev3dev/debian-stretch-cross latest 8e3b0aa8f243 This container is a fresh new image, what we want is to create an create a writable container based on the above. The docker create command creates a writeable container layer over the specified image and prepares it for running the specified command an image is basically a blueprint of what a container should look like, it contains all the necessary binaries,source codes,tools and more. It is immutable (unchangable). A container is using a image as a blueprint to create a specific container. A container can be mutable. A Docker container is a virtualized run-time environment where users can isolate applications from the underlying system The above qoute is exactly the kind of thing that we want when building and using the cross-compiler toolchain. We don't want to mess up the host system with arm-binaries. Another benefit is that a docker container already exists, which means we can create a container that basically works out of the box. To create a docker, the docker create command is used. Here is an example: docker create -ti -u$(id -u):$(id -g) --name GorbyBuild -v$HOME:/src -w /src ev3dev/debian-stretch-cross /bin/bash

906f0913cefe3575a4a0f1bab49dda7876d4779f993cfeafeb9edfdb7ad74973


We can now check that the container exists.

docker container ls --all | awk '/(CONTAINER|GorbyBuild)/ {print $0}'  CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2751b793d5fb ev3dev/debian-stretch-cross "/bin/bash" 4 seconds ago Created GorbyBuild  Now its time to start it up. Since we gave it a name (GorbyBuild), we don't need to remeber the container id. docker start GorbyBuild  GorbyBuild  And finally we can start working with the GorbyBuild container. I prefer to work with the container executing /bin/bash at the beginning to have a more controlled enviroment. But its possible to just run a single command inside the docker. docker exec will execute a command inside the docker.Here is an example: docker exec -t GorbyBuild cat /etc/hostname  2751b793d5fb  Maybe not the most creative name for a host….If one wants to have several commands executed like a bash script, its necessary to use the shell command though this requires that the container has some kind of shell installed. docker exec -it GorbyBuild /bin/bash -c " cd /etc/ cat os-release | grep PRETTY_NAME | sed 's/.*=//' "  "Debian GNU/Linux 9 (stretch)"  And since we do have a bash installed we might just use that shell to get inside the container. > docker exec -t GorbyBuild /bin/bash compiler@2751b793d5fb:/src$


since its a debian distribution we can use apt to upgrade the system.

docker exec -it GorbyBuild bash -c "
sudo apt update
"


Now lets see if we can compile something using the provided arm-linux-gnueabi-gcc from the container.

docker exec -t GorbyBuild /bin/bash -c "
cat <<'EOF' > /tmp/main.c
#include <stdio.h>

int main()
{
printf(\"Hello Gorby\");
return 0;
}
EOF

arm-linux-gnueabi-gcc -Wall /tmp/main.c -o /src/hello_gorby
"

file ~/hello_gorby


/home/calle/hello_gorby: ELF 32-bit LSB pie executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=4d5671d991215dde587cffce20ee08ae2a22b9f5, not stripped


This example creates a small c-program on the dockers /tmp directory and compiles it inside the docker, then outside it checks the file. And since the my home directory is mounted inside the docker the file is available from my host machine.

This concludes the docker setup.

### Installing conan

Conan is a c++ packagemanager based on python. Unfortunatly python is not installed on the ev3dev image and there for not part of our newly created container (GorbyBuild). That needs to be fixed. Its also required to have pip3 install for python package management.

docker exec -t GorbyBuild /bin/bash -c "
sudo apt install python3
sudo apt install python3-pip
"


Now we need to install conan using pip3.

docker exec -t GorbyBuild /bin/bash -c "
python3 -m pip install conan
"


There are one more thing todo before the setup is complete. The container is all setup, but I want to use my editor and my configuration. This can be setup in the docker container but its kind of impractical. I also want to run unit tests, that is not possible in the docker or even on my host (actually it is possible to use qemu but thats outside this article). That means I need to be able to build both for x86_64 and arm and since my host (without using docker) is all setup a build environment (clang,gcc,cmake and so on) there should be no reason to not use that but that requires that i also install conan on my host.

python3 -m pip install conan


This will install conan on the user local directory. So to be able to use conan we need to add the path somehow, we do this by adding the path to the

docker exec -t GorbyBuild /bin/bash -c '
echo "PATH=\$HOME/.local/bin:\$PATH" >> $HOME/.bashrc echo "PROFILE_DIR=/src/.conan/profiles" >>$HOME/.bashrc
'


## Conan software package manager

Conan is a software package manager which is intended for C and C++ developers.

Conan is universal and portable. It works in all operating systems including Windows, Linux, OSX, FreeBSD, Solaris, and others, and it can target any platform, including desktop, server, and cross-building for embedded and bare metal devices. It integrates with other tools like Docker, MinGW, WSL, and with all build systems such as CMake, MSBuild, Makefiles, Meson, SCons. It can even integrate with any proprietary build systems.

When a package is installed it keeps the source, build and deploy structure in a repository and generates build files with different generators to include in the project. The generators could be for example cmake,make,visual studio,scons,qmake and many more, there is even a markdown generator that generates useful information on each of the requirement i.e: It is also possible to create your own package and install that into the local repository. If a generator doesn't exists its easy to create your own. This will just be short overview , I will leave to the reader to gain more information on conan documentation.

A conan recipe is an instruction how to build a package and what dependencies a packages has and is defined by a "conanfile.py" file. A conan package on the other hand is source and pre-compiled binaries for any possible platform and/or configuration. These binaries can be created and uploaded to a server with the same comands in all platforms. Installation of a package from a server is also very efficient. Only the necessary binaries for the current platforma and configuration are downloaded. Its possible to setup its own remote server, but I leave that to some other time and place.

### Conan remotes

Remotes are places where conan will search for packages when argument --remote=... is applied. That is remote sites which contains many (or few) conan packages/recipes. One such remote site is conan center where all kinds of different packages can be found. Another is Bincrafters remote repository, both of these remote sites have interface on the web to search for specific packages. But commony though one will use the CLI to search for packages. To add a new remote:

conan remote add Example https://example.com


This will add a remote repository. All repositories are added to the ~/.conan/remotes.json file. To list the remote added repositories:

conan remote list

conan-center: https://conan.bintray.com [Verify SSL: True]
bincrafters: https://api.bintray.com/conan/bincrafters/public-conan [Verify SSL: True]
Example: https://example.com [Verify SSL: True]


Its also possible to edit the json file directly

cat ~/.conan/remotes.json

{
"remotes": [
{
"verify_ssl": true,
"url": "https://conan.bintray.com",
"name": "conan-center"
},
{
"verify_ssl": true,
"url": "https://api.bintray.com/conan/bincrafters/public-conan",
"name": "bincrafters"
},
{
"verify_ssl": true,
"url": "https://example.com",
"name": "Example"
}
]
}


To remove one can either use the CLI or just edit the ~/.conan/remotes.json by hand

conan remote remove Example


These are the most common arguments, there are many other that can be found here.

### Conan Search

The search command is used to search for packages on a server or even locally installed packages. It searches for both recipes and binaries. Unless a --remote is specified the local cache is search.

Lets say I want to use fmt in my c++ project. Since I don't have it installed on my local cache I need to search for it remotely. The search pattern can include "*" for to obtain multiple for example:

conan search "fmt/6*" --remote=all

Existing package recipes:

Remote 'bincrafters':
fmt/6.0.0@bincrafters/stable
Remote 'conan-center':
fmt/6.0.0
fmt/6.0.0@bincrafters/stable
fmt/6.1.0
fmt/6.1.1
fmt/6.1.2
fmt/6.2.0
fmt/6.2.1


In this example we used all our remotes (see conan remotes) and searched for the fmt package. We could just used one by using its name in the remote field --remote=conan-center.

conan inspect "fmt/6.1.0" --remote=conan-center

Downloading conanmanifest.txt
name: fmt
version: 6.1.0
url: https://github.com/conan-io/conan-center-index
homepage: https://github.com/fmtlib/fmt
author: None
description: A safe and fast alternative to printf and IOStreams.
topics: ('conan', 'fmt', 'format', 'iostream', 'printf')
generators: cmake
exports: None
exports_sources: ['CMakeLists.txt', 'patches/**']
short_paths: False
apply_env: True
build_policy: None
revision_mode: hash
settings: ('os', 'compiler', 'build_type', 'arch')
options:
fPIC: [True, False]
shared: [True, False]
with_fmt_alias: [True, False]
default_options:
fPIC: True
shared: False
with_fmt_alias: False


Here we can see that there are options for using header only and building shared and which default options are used.

You can also check what different binaries exists in the repository, that is , if for example a binary is compiled for different platform or with different setting that is shown by doing.

conan search fmt/6.2.1@ --remote=all

Existing packages for recipe fmt/6.2.1:

Existing recipe in remote 'conan-center':

Package_ID: 03098e9ca3b3217acdb827caba0356e229ff04a1
[options]
shared: True
[settings]
arch: x86_64
build_type: Release
compiler: clang
compiler.libcxx: libc++
compiler.version: 3.9
os: Linux
Outdated from recipe: True

Package_ID: 0361dffd8f43a76c9aa894d20cb58fd062ae22cc
[options]
fPIC: True
shared: False
[settings]
arch: x86_64
build_type: Debug
compiler: clang
compiler.libcxx: libc++
compiler.version: 6.0
os: Linux
Outdated from recipe: True

[options]
fPIC: True
shared: False
[settings]
arch: x86_64
build_type: Debug
compiler: gcc
compiler.libcxx: libstdc++11
compiler.version: 9
os: Linux
Outdated from recipe: True

Package_ID: 038f8796e196b3dba76fcc5fd4ef5d3d9c6866ec
[options]
fPIC: True
shared: False
[settings]
arch: x86_64
build_type: Debug
compiler: gcc
compiler.libcxx: libstdc++11
compiler.version: 8
os: Linux
Outdated from recipe: True

[options]
shared: True
[settings]
arch: x86_64
build_type: Debug
compiler: Visual Studio
compiler.runtime: MTd
compiler.version: 16
os: Windows
Outdated from recipe: True
Package_ID: 95b87e2c9261497d05b76244c015fbde06fe50b3
[options]
fPIC: True
shared: True
[settings]
arch: x86_64
build_type: Release
compiler: apple-clang
compiler.libcxx: libc++
compiler.version: 10.0
os: Macos
Outdated from recipe: True



I removed some, since the I believe i counted to 221 different ways and settings that fmt/6.2.1 was compiled.

The search command needs a specific target and a @ sign at the end to retrieve the different binary specifications.

### Conan install

Now that we found out what package we want to use, its time to download it locally to our local cache. Firs we need to create a ~conanfile.txt' file, this file includes all the packages that we want, and also options and settings.

 1: cat<<EOF > conanfile.txt
2: [requires]
3:  fmt/[>6.0.0 <7.0.0]
4:
5: [build_requires]
6:  cmake/[>3.17]
7:
8: [options]
9:  fmt:shared=True
10:
11: [generators]
12:  cmake
13:  compiler_args
14:  virtualenv
15:
16: [imports]
17: lib, *.so* -> ./lib
18: include, *.h -> ./include
19: EOF
20:


Before we continue lets discuss some part of the conanfile.txt

Line 2 [require]
This is the libraries and binaries that we requries, in this case we just needed fmt and we want the version to be $$6.0.0> version < 7.0.0$$
Line 5 [build_requies]
These are tools that are needed, for example in this case we said that cmake is required to be at least of version 3.17.
Line 9 [options]
As we could see in the inspect commands each package possibly has some extra options, these one can be set here. For fmt I decided to work with shared library.
Line 11 [generators]
Are build artifacts that can be used by the build system. for example cmake generator will create a conanbuildinfo.cmake that can be included by cmake files to get the include paths,library paths and link information. We get to that later in this article. The compiler_args generator, creates a file with the content that can be used as compiler arguments (at least for gcc and clang). The last generator: virtualenv is kind of like python virtualenv where you can activate it, and it will sets enviroment variables to be able to use ie. cmake binary.
Line 16 [imports]
List of files to be imported to a local directory. It kind of states what to copy in to a local file directory, if one to use .eg LD_LIBRARY_PATH or specifying the include path. In the above example all "*.so" files are copied to the ./lib directory and all the headers to ./include which we will see later.

Now that we created the file lets uses conan install to install the package.

mkdir install ; cd install
conan install .. --build=fmt --build=openssl


The generated files will be located in the directory in which the conan install is executed. Here are the outputs of that directory

.
├── activate.ps1
├── activate.sh
├── conanbuildinfo.args
├── conanbuildinfo.cmake
├── conanbuildinfo.txt
├── conan_imports_manifest.txt
├── conaninfo.txt
├── conan.lock
├── deactivate.ps1
├── deactivate.sh
├── environment.ps1.env
├── environment.sh.env
├── graph_info.json
├── include
│   ├── fmt
│   └── openssl
└── lib
├── libfmt.so -> libfmt.so.6
├── libfmt.so.6 -> libfmt.so.6.2.1
└── libfmt.so.6.2.1

4 directories, 16 files


I omitted the header files to keep the list smaller.

I used a version range for the fmt and for some reason it used 6.1.2, which is somewhere in between the ranges that I specified , one can wonder why 6.2.1 wouldn't be a better fit? Anyhow its possible to specify explicitly what version to use. In this case we also provided the --build=fmt to actually build the package. This due to that there were no binaries on the server that matched my build options and settings, so we had to build it locally so the binaries would be added to the local cache. On the other hand , the next time we need the same requirements it will check the local cache, where it would find the binaries and therefor no build has to be executed.

### Conan new/create

Now that we got this far we could start using conan, but before doing that there is one more thing that I want to explain. Sometimes (as in this example) there might be libraries which are not part of conan remote repository (or local for that matter). It would be nice if we could create our own package, either just for local use or to be added into the remote resository. For this we need to use conan create on a python file which states how to download, configure,build and sometimes deploy a package. You can real all about it in the conan getting started.

For this article I will use e3dev-cpp to create a package for local use. I will skip the part of using a test, but if one wants to upload a reciepe to a remote server I guess that is kind of necessary.

As mentioned earlier to be able to create a package we need a python file conanfile.py, doing this from scratch would most definitely make some/most developers throw in the towel, so conan has a command to make a new template conanfile.py.

conan new ev3dev/latest

File saved: conanfile.py


This file needs to be edited, but most of the stuff is already at hand.

The diff will show the minimum effort that was needed to create a new package.

7,11c7,11
<     author = "Carl Olsen <olsen.carl@g****.com>"
<     url = "https://github.com/ddemidov/ev3dev-lang-cpp.git"
<     description = "Ev3dev cpp library for sensors and motors"
<     topics = ("ev3", "mindstorm", "sensors", "motors")
---
>     url = "<Package recipe repository url here, for issues about the package>"
>     description = "<Description of Ev3dev here>"
>     topics = ("<Put some tag here>", "<here>", "<and here>")
14a15
>     generators = "cmake"
21c22,29
<         self.run("git clone https://github.com/ddemidov/ev3dev-lang-cpp.git")
---
>         self.run("git clone https://github.com/conan-io/hello.git")
>         # This small hack might be useful to guarantee proper /MT /MD linkage
>         # in MSVC if the packaged project doesn't have variables to set it
>         # properly
>         tools.replace_in_file("hello/CMakeLists.txt", "PROJECT(HelloWorld)",
>                               '''PROJECT(HelloWorld)
> include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) > conan_basic_setup()''') 25c33 < cmake.configure(source_folder="ev3dev-lang-cpp") --- > cmake.configure(source_folder="hello") 34,35c42,43 < self.copy("*.h", dst="include", src="ev3dev-lang-cpp") < self.copy("*.a", dst="lib", keep_path=False) --- > self.copy("*.h", dst="include", src="hello") > self.copy("*hello.lib", dst="lib", keep_path=False) 40,42d47 < # Copy the demo too < self.copy("*", src="bin", dst="bin") < 45c50 < self.cpp_info.libs = ["ev3dev"] --- > self.cpp_info.libs = ["hello"] 47,48d51 < def deploy(self): < self.copy("*",src="bin",dst="bin")  Now that we edited the python file we can use conan create to create the ev3dev-cpp package into our local cache. But before that lets explain a bit about user/channel. We have seen from our searches that packages are named package/version. There is however two more attributes that could be added to the package. One can see them as a fork of a existing package, or as a git branch. user A user channel It’s a completely arbitrary string meaning that authors can put whatever they want conan create . cool/beta  Exporting package recipe ev3dev/latest@cool/beta: A new conanfile.py version was exported ev3dev/latest@cool/beta: Folder: /home/calle/.conan/data/ev3dev/latest/cool/beta/export ev3dev/latest@cool/beta: Exported revision: bca5ddeaed80faa66f4bc5f97258b11f Configuration: [settings] arch=x86_64 arch_build=x86_64 build_type=Release compiler=gcc compiler.libcxx=libstdc++ compiler.version=10 os=Linux os_build=Linux [options] [build_requires] [env] ev3dev/latest@cool/beta: Forced build from source Installing package: ev3dev/latest@cool/beta Requirements ev3dev/latest@cool/beta from local cache - Cache Packages ev3dev/latest@cool/beta:82ef5eac51c38971dea2fd342dd55ddf2ddfbbc3 - Build Installing (downloading, building) binaries... ev3dev/latest@cool/beta: Configuring sources in /home/calle/.conan/data/ev3dev/latest/cool/beta/source ev3dev/latest@cool/beta: Copying sources to build folder ev3dev/latest@cool/beta: Building your package in /home/calle/.conan/data/ev3dev/latest/cool/beta/build/82ef5eac51c38971dea2fd342dd55ddf2ddfbbc3 ev3dev/latest@cool/beta: Generator txt created conanbuildinfo.txt ev3dev/latest@cool/beta: Calling build() -- The C compiler identification is GNU 10.2.0 -- The CXX compiler identification is GNU 10.2.0 -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working C compiler: /usr/bin/cc - skipped -- Detecting C compile features -- Detecting C compile features - done -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: /usr/bin/c++ - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Configuring done -- Generating done -- Build files have been written to: /home/calle/.conan/data/ev3dev/latest/cool/beta/build/82ef5eac51c38971dea2fd342dd55ddf2ddfbbc3 Scanning dependencies of target api_tests Scanning dependencies of target ev3dev [ 5%] Building CXX object CMakeFiles/ev3dev.dir/ev3dev.cpp.o [ 10%] Building CXX object tests/CMakeFiles/api_tests.dir/api_tests.cpp.o [ 15%] Building CXX object tests/CMakeFiles/api_tests.dir/__/ev3dev.cpp.o [ 21%] Linking CXX static library libev3dev.a [ 21%] Built target ev3dev Scanning dependencies of target ev3dev-lang-test Scanning dependencies of target button-test Scanning dependencies of target sound-test Scanning dependencies of target ev3dev-lang-demo Scanning dependencies of target remote-control Scanning dependencies of target drive-test Scanning dependencies of target remote_control-test [ 26%] Building CXX object demos/CMakeFiles/ev3dev-lang-test.dir/ev3dev-lang-test.cpp.o [ 31%] Building CXX object demos/CMakeFiles/sound-test.dir/sound-test.cpp.o [ 36%] Building CXX object demos/CMakeFiles/button-test.dir/button-test.cpp.o [ 42%] Building CXX object demos/CMakeFiles/ev3dev-lang-demo.dir/ev3dev-lang-demo.cpp.o [ 47%] Building CXX object demos/CMakeFiles/drive-test.dir/drive-test.cpp.o [ 52%] Building CXX object demos/CMakeFiles/remote_control-test.dir/remote_control-test.cpp.o [ 57%] Building CXX object demos/CMakeFiles/remote-control.dir/remote-control.cpp.o [ 63%] Linking CXX executable button-test [ 68%] Linking CXX executable remote_control-test [ 68%] Built target button-test [ 68%] Built target remote_control-test [ 73%] Linking CXX executable sound-test [ 78%] Linking CXX executable remote-control [ 78%] Built target sound-test [ 78%] Built target remote-control [ 84%] Linking CXX executable drive-test [ 84%] Built target drive-test [ 89%] Linking CXX executable ev3dev-lang-test [ 89%] Built target ev3dev-lang-test [ 94%] Linking CXX executable ev3dev-lang-demo [ 94%] Built target ev3dev-lang-demo [100%] Linking CXX executable api_tests [100%] Built target api_tests ev3dev/latest@cool/beta: Package '82ef5eac51c38971dea2fd342dd55ddf2ddfbbc3' built ev3dev/latest@cool/beta: Build folder /home/calle/.conan/data/ev3dev/latest/cool/beta/build/82ef5eac51c38971dea2fd342dd55ddf2ddfbbc3 ev3dev/latest@cool/beta: Generated conaninfo.txt ev3dev/latest@cool/beta: Generated conanbuildinfo.txt ev3dev/latest@cool/beta: Generating the package ev3dev/latest@cool/beta: Package folder /home/calle/.conan/data/ev3dev/latest/cool/beta/package/82ef5eac51c38971dea2fd342dd55ddf2ddfbbc3 ev3dev/latest@cool/beta: Calling package() ev3dev/latest@cool/beta package(): Packaged 1 '.h' file: ev3dev.h ev3dev/latest@cool/beta package(): Packaged 1 '.a' file: libev3dev.a ev3dev/latest@cool/beta: Package '82ef5eac51c38971dea2fd342dd55ddf2ddfbbc3' created ev3dev/latest@cool/beta: Created package revision dde36583e10cd07371f9eb1f1dbe6217  Its possible to omit user and channel, in that case it will only show up as ev3dev/latest and if we add the user and channel, it will become ev3dev/latest@cool/beta. This could be used for example if we had different branches that we want to add , or if for example we have a beta, and then later when done testing we want it to become stable (see conan copy). One way that I use it to make a copy of an already existing release Lets say fmt, and then change some things in the package, for example adding some options. I can now search in my local cache for .eg user name. conan search "*ev3dev*cool*"  Existing package recipes: ev3dev/latest@cool/beta  Obviously im just scratching the surface here . More information can be found here ### Conan profiles So far we have the packages installed and we it seems as if we have all the necessary toolchains setup. But somehow we need to tell conan which compiler and architecture we like to build for. This can be done through conan profile. Usually there already exists a default profile , where the system default compiler and libraries are used. But what happens if we want to use something else? The profiles are located in ~/.conan/profiles directory. In my case there is a default profile. cat default  [settings] os=Linux os_build=Linux arch=x86_64 arch_build=x86_64 compiler=gcc compiler.version=10 compiler.libcxx=libstdc++ build_type=Release [options] [build_requires] [env]  Here we can see all the default used compiler and versions. But what happens if the default is lost? No worries, its easy to create a new. conan profile new myowndefault --detect  Found gcc 10.2 Found clang 11.0 gcc>=5, using the major as version ************************* WARNING: GCC OLD ABI COMPATIBILITY *********************** Conan detected a GCC version > 5 but has adjusted the 'compiler.libcxx' setting to 'libstdc++' for backwards compatibility. Your compiler is likely using the new CXX11 ABI by default (libstdc++11). If you want Conan to use the new ABI for the myowndefault profile, run:$ conan profile update settings.compiler.libcxx=libstdc++11 myowndefault

Or edit '/home/calle/.conan/profiles/myowndefault' and set compiler.libcxx=libstdc++11

************************************************************************************

Profile created with detected settings: /home/calle/.conan/profiles/myowndefault


Ohh, and it gave us a hint that it used libstdc++11 which is a newer ABI. So lets change that.

sed -i 's/libstdc++/libstdc++11/' myowndefault


Nice and smooth.

Lets get our hands a bit dirtier. Lets create a new profile which uses clang compiler instead. Its basically the same , but switching out the compiler and version.

conan profile new /tmp/profiles/clang --detect
sed -i 's/libstdc++/libstdc++11/' clang
sed -i 's/gcc/clang/' clang

CLANG_VERSION=$(clang --version | gawk 'match($0,/clang\s+version\s+([0-9]+).(.*)/,a ) { print a[1]}')
sed -i "s/compiler.version=10/compiler.version=${CLANG_VERSION}/" clang sed -i '/$env$/a CXX=/usr/bin/clang++' clang sed -i '/$env$/a CC=/usr/bin/clang' clang  [settings] os=Linux os_build=Linux arch=x86_64 arch_build=x86_64 compiler=clang compiler.version=11 compiler.libcxx=libstdc++11 build_type=Release [options] [build_requires] [env] CC=/usr/bin/clang CXX=/usr/bin/clang++  The profiles don't need to be placed in the default directory, but if one wants to use it it needs to be specified with path. So lets use the ev3dev package that we created before, but this time we the clang profile. conan create . cool/clang --profile=/tmp/profiles/clang  Exporting package recipe ev3dev/latest@cool/clang: A new conanfile.py version was exported ev3dev/latest@cool/clang: Folder: /home/calle/.conan/data/ev3dev/latest/cool/clang/export ev3dev/latest@cool/clang: Exported revision: bca5ddeaed80faa66f4bc5f97258b11f Configuration: [settings] arch=x86_64 arch_build=x86_64 build_type=Release compiler=clang compiler.libcxx=libstdc++11 compiler.version=11 os=Linux os_build=Linux [options] [build_requires] [env] CC=/usr/bin/clang CXX=/usr/bin/clang++ ev3dev/latest@cool/clang: Forced build from source Installing package: ev3dev/latest@cool/clang Requirements ev3dev/latest@cool/clang from local cache - Cache Packages ev3dev/latest@cool/clang:0ac8c9952b8651eefe078adcd928017cea3e0340 - Build Installing (downloading, building) binaries... ev3dev/latest@cool/clang: Configuring sources in /home/calle/.conan/data/ev3dev/latest/cool/clang/source ev3dev/latest@cool/clang: Copying sources to build folder ev3dev/latest@cool/clang: Building your package in /home/calle/.conan/data/ev3dev/latest/cool/clang/build/0ac8c9952b8651eefe078adcd928017cea3e0340 ev3dev/latest@cool/clang: Generator txt created conanbuildinfo.txt ev3dev/latest@cool/clang: Calling build() -- The C compiler identification is Clang 11.0.0 -- The CXX compiler identification is Clang 11.0.0 -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working C compiler: /usr/bin/clang - skipped -- Detecting C compile features -- Detecting C compile features - done -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: /usr/bin/clang++ - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Configuring done -- Generating done -- Build files have been written to: /home/calle/.conan/data/ev3dev/latest/cool/clang/build/0ac8c9952b8651eefe078adcd928017cea3e0340 Scanning dependencies of target ev3dev Scanning dependencies of target api_tests [ 5%] Building CXX object tests/CMakeFiles/api_tests.dir/api_tests.cpp.o [ 10%] Building CXX object CMakeFiles/ev3dev.dir/ev3dev.cpp.o [ 15%] Building CXX object tests/CMakeFiles/api_tests.dir/__/ev3dev.cpp.o [ 21%] Linking CXX static library libev3dev.a [ 21%] Built target ev3dev Scanning dependencies of target sound-test Scanning dependencies of target ev3dev-lang-demo Scanning dependencies of target ev3dev-lang-test Scanning dependencies of target button-test Scanning dependencies of target drive-test Scanning dependencies of target remote_control-test Scanning dependencies of target remote-control [ 26%] Building CXX object demos/CMakeFiles/sound-test.dir/sound-test.cpp.o [ 31%] Building CXX object demos/CMakeFiles/ev3dev-lang-demo.dir/ev3dev-lang-demo.cpp.o [ 42%] Building CXX object demos/CMakeFiles/ev3dev-lang-test.dir/ev3dev-lang-test.cpp.o [ 42%] Building CXX object demos/CMakeFiles/button-test.dir/button-test.cpp.o [ 47%] Building CXX object demos/CMakeFiles/remote-control.dir/remote-control.cpp.o [ 57%] Building CXX object demos/CMakeFiles/remote_control-test.dir/remote_control-test.cpp.o [ 57%] Building CXX object demos/CMakeFiles/drive-test.dir/drive-test.cpp.o [ 63%] Linking CXX executable button-test [ 68%] Linking CXX executable remote_control-test [ 68%] Built target button-test [ 68%] Built target remote_control-test [ 73%] Linking CXX executable sound-test [ 78%] Linking CXX executable remote-control [ 78%] Built target sound-test [ 78%] Built target remote-control [ 84%] Linking CXX executable drive-test [ 84%] Built target drive-test [ 89%] Linking CXX executable ev3dev-lang-test [ 89%] Built target ev3dev-lang-test [ 94%] Linking CXX executable ev3dev-lang-demo [ 94%] Built target ev3dev-lang-demo [100%] Linking CXX executable api_tests [100%] Built target api_tests ev3dev/latest@cool/clang: Package '0ac8c9952b8651eefe078adcd928017cea3e0340' built ev3dev/latest@cool/clang: Build folder /home/calle/.conan/data/ev3dev/latest/cool/clang/build/0ac8c9952b8651eefe078adcd928017cea3e0340 ev3dev/latest@cool/clang: Generated conaninfo.txt ev3dev/latest@cool/clang: Generated conanbuildinfo.txt ev3dev/latest@cool/clang: Generating the package ev3dev/latest@cool/clang: Package folder /home/calle/.conan/data/ev3dev/latest/cool/clang/package/0ac8c9952b8651eefe078adcd928017cea3e0340 ev3dev/latest@cool/clang: Calling package() ev3dev/latest@cool/clang package(): Packaged 1 '.h' file: ev3dev.h ev3dev/latest@cool/clang package(): Packaged 1 '.a' file: libev3dev.a ev3dev/latest@cool/clang: Package '0ac8c9952b8651eefe078adcd928017cea3e0340' created ev3dev/latest@cool/clang: Created package revision 9cf0c58ac490a016464a9067481d81d8  Voilá! We have two packages built from the same recipe but built with different compilers. conan search "ev3dev*cool"  Existing package recipes: ev3dev/latest@cool/beta ev3dev/latest@cool/clang  profiles can be used in many of the conan commands. • create • new • install . . profiles also supports package settings and enviroment variables There is even possibility to make profiles that compiles only some libs with clang and others with gcc. I leave that to some other time. This is an important ingredients when we start cross-compiling. Lets not get ahead of ourself, we still need to compile using conan on our host machine before fiddling with cross-compiling. ### Lets put it all together. Finally we got to a point where we can put everything together. Just to make sure that the concept works. Lets first create the cmake file and a main.cpp.  1: cat <<'EOF' > CMakeLists.txt 2: cmake_minimum_required(VERSION 3.17) 3: project ( first_test_prj ) 4: 5: set(CMAKE_CXX_STANDARD 17) 6: set(CMAKE_CXX_STANDARD_REQUIRED ON) 7: set(CMAKE_CXX_FLAGS "-Wall -Wextra -pedantic") 8: 9: ########### 10: # Include generated conan cmake file 11: ########### 12: include(${CMAKE_SOURCE_DIR}/conan/conanbuildinfo.cmake)
13: conan_basic_setup()
14:
15:
16: ###########
18: ###########
19: add_executable( ${PROJECT_NAME} main.cpp ) 20: 21: 22: target_include_directories( 23:${PROJECT_NAME} PRIVATE
24:   ${CONAN_INCLUDE_DIRS_FMT} 25: ) 26: 27: ########### 28: # Sources that compiles 29: ########### 30: set( SRC main.cpp) 31: 32: 33: ########### 34: # Library linking 35: ########### 36: target_link_libraries(${PROJECT_NAME}
37: PRIVATE
awk '{dirs[\0]++} END{ for(i in dirs){print i} }' "  /usr/arm-linux-gnueabi/lib /usr / /usr/arm-linux-gnueabi  So lets check out the compiler a bit more.. docker exec -t GorbyBuild /bin/bash -c " arm-linux-gnueabi-g++ -v -std=c++14 -xc++ -fsyntax-only /dev/null "  Using built-in specs. COLLECT_GCC=arm-linux-gnueabi-g++ COLLECT_LTO_WRAPPER=/usr/lib/gcc-cross/arm-linux-gnueabi/6/lto-wrapper Target: arm-linux-gnueabi Configured with: ../src/configure -v --with-pkgversion='Debian 6.3.0-18' --with-bugurl=file:///usr/share/doc/gcc-6/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-6 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-libitm --disable-libquadmath --enable-plugin --enable-default-pie --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-6-armel-cross/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-6-armel-cross --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-6-armel-cross --with-arch-directory=arm --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libgcj --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-sjlj-exceptions --with-arch=armv4t --with-float=soft --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=arm-linux-gnueabi --program-prefix=arm-linux-gnueabi- --includedir=/usr/arm-linux-gnueabi/include Thread model: posix gcc version 6.3.0 20170516 (Debian 6.3.0-18) COLLECT_GCC_OPTIONS='-v' '-std=c++14' '-fsyntax-only' '-shared-libgcc' '-march=armv4t' '-mfloat-abi=soft' '-mtls-dialect=gnu' /usr/lib/gcc-cross/arm-linux-gnueabi/6/cc1plus -quiet -v -imultilib . -imultiarch arm-linux-gnueabi -D_GNU_SOURCE /dev/null -quiet -dumpbase null -march=armv4t -mfloat-abi=soft -mtls-dialect=gnu -auxbase null -std=c++14 -version -fsyntax-only -o /dev/null GNU C++14 (Debian 6.3.0-18) version 6.3.0 20170516 (arm-linux-gnueabi) compiled by GNU C version 6.3.0 20170516, GMP version 6.1.2, MPFR version 3.1.5, MPC version 1.0.3, isl version 0.15 GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 ignoring nonexistent directory "/usr/local/include/arm-linux-gnueabi" ignoring nonexistent directory "/usr/include/arm-linux-gnueabi" #include "..." search starts here: #include <...> search starts here: /usr/lib/gcc-cross/arm-linux-gnueabi/6/../../../../arm-linux-gnueabi/include/c++/6 /usr/lib/gcc-cross/arm-linux-gnueabi/6/../../../../arm-linux-gnueabi/include/c++/6/arm-linux-gnueabi/. /usr/lib/gcc-cross/arm-linux-gnueabi/6/../../../../arm-linux-gnueabi/include/c++/6/backward /usr/lib/gcc-cross/arm-linux-gnueabi/6/include /usr/lib/gcc-cross/arm-linux-gnueabi/6/include-fixed /usr/lib/gcc-cross/arm-linux-gnueabi/6/../../../../arm-linux-gnueabi/include /usr/include End of search list. GNU C++14 (Debian 6.3.0-18) version 6.3.0 20170516 (arm-linux-gnueabi) compiled by GNU C version 6.3.0 20170516, GMP version 6.1.2, MPFR version 3.1.5, MPC version 1.0.3, isl version 0.15 GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 Compiler executable checksum: a5e48cccdfbd9b08b43e5f380dbad667 COMPILER_PATH=/usr/lib/gcc-cross/arm-linux-gnueabi/6/:/usr/lib/gcc-cross/arm-linux-gnueabi/6/:/usr/lib/gcc-cross/arm-linux-gnueabi/:/usr/lib/gcc-cross/arm-linux-gnueabi/6/:/usr/lib/gcc-cross/arm-linux-gnueabi/:/usr/lib/gcc-cross/arm-linux-gnueabi/6/../../../../arm-linux-gnueabi/bin/ LIBRARY_PATH=/usr/lib/gcc-cross/arm-linux-gnueabi/6/:/usr/lib/gcc-cross/arm-linux-gnueabi/6/../../../../arm-linux-gnueabi/lib/:/lib/:/usr/lib/ COLLECT_GCC_OPTIONS='-v' '-std=c++14' '-fsyntax-only' '-shared-libgcc' '-march=armv4t' '-mfloat-abi=soft' '-mtls-dialect=gnu'  This tells us where we can find the std libraries and what include paths are used. We also note that see the we are using -mfloat-abi=soft by default so we don't need to worry about that flag. Another important point is the -march=armv4t, that seems abit strange to us since we are using armv5tej and according to arm @ wiki this should be armv5 and looking at the gcc options it should be armv5te. So we'll keep that in mind and add that to the flags later on. Lets also checkout the difference on the arm-linux-gnueabihf toolchain. docker exec -t GorbyBuild /bin/bash -c " arm-linux-gnueabihf-g++ -v -std=c++14 -xc++ -fsyntax-only /dev/null 2>&1 | grep "COLLECT" "  COLLECT_GCC=arm-linux-gnueabihf-g++ COLLECT_LTO_WRAPPER=/usr/lib/gcc-cross/arm-linux-gnueabihf/6/lto-wrapper COLLECT_GCC_OPTIONS='-v' '-std=c++14' '-fsyntax-only' '-shared-libgcc' '-march=armv7-a' '-mfloat-abi=hard' '-mfpu=vfpv3-d16' '-mthumb' '-mtls-dialect=gnu' COLLECT_GCC_OPTIONS='-v' '-std=c++14' '-fsyntax-only' '-shared-libgcc' '-march=armv7-a' '-mfloat-abi=hard' '-mfpu=vfpv3-d16' '-mthumb' '-mtls-dialect=gnu'  This is using a -march=armv7-a -mfloat-abi=hard -mfpu=vfpv3-d16 that doesn't fit our purpose. So We'll leave it for now. Here is something more interesting docker exec -t GorbyBuild /bin/bash -c " arm-linux-gnueabihf-g++ -marm -march=armv5te -mcpu=arm926ej-s -mtune=arm926ej-s -Q --help=target "  The following options are target specific: -mabi= aapcs-linux -mabort-on-noreturn [disabled] -mandroid [disabled] -mapcs [disabled] -mapcs-float [disabled] -mapcs-frame [disabled] -mapcs-reentrant [disabled] -mapcs-stack-check [disabled] -march= armv5te -marm [enabled] -masm-syntax-unified [disabled] -mbig-endian [disabled] -mbionic [disabled] -mcallee-super-interworking [disabled] -mcaller-super-interworking [disabled] -mcpu= arm926ej-s -mfix-cortex-m3-ldrd [enabled] -mflip-thumb [disabled] -mfloat-abi= hard -mfp16-format= none -mfpu= vfpv3-d16 -mglibc [enabled] -mhard-float -mlittle-endian [enabled] -mlong-calls [disabled] -mmusl [disabled] -mneon-for-64bits [disabled] -mnew-generic-costs [disabled] -mold-rtx-costs [disabled] -mpic-data-is-text-relative [enabled] -mpic-register= -mpoke-function-name [disabled] -mprint-tune-info [disabled] -mrestrict-it [enabled] -msched-prolog [enabled] -msingle-pic-base [disabled] -mslow-flash-data [disabled] -msoft-float -mstructure-size-boundary= 0x20 -mthumb [disabled] -mthumb-interwork [enabled] -mtls-dialect= gnu -mtp= auto -mtpcs-frame [disabled] -mtpcs-leaf-frame [disabled] -mtune= arm926ej-s -muclibc [disabled] -munaligned-access [enabled] -mvectorize-with-neon-double [disabled] -mvectorize-with-neon-quad [enabled] -mword-relocations [disabled] Known ARM ABIs (for use with the -mabi= option): aapcs aapcs-linux apcs-gnu atpcs iwmmxt Known ARM architectures (for use with the -march= option): armv2 armv2a armv3 armv3m armv4 armv4t armv5 armv5e armv5t armv5te armv6 armv6-m armv6j armv6k armv6kz armv6s-m armv6t2 armv6z armv6zk armv7 armv7-a armv7-m armv7-r armv7e-m armv7ve armv8-a armv8-a+crc armv8.1-a armv8.1-a+crc iwmmxt iwmmxt2 native Known __fp16 formats (for use with the -mfp16-format= option): alternative ieee none Known ARM FPUs (for use with the -mfpu= option): crypto-neon-fp-armv8 fp-armv8 fpv4-sp-d16 fpv5-d16 fpv5-sp-d16 neon neon-fp-armv8 neon-fp16 neon-vfpv4 vfp vfp3 vfpv3 vfpv3-d16 vfpv3-d16-fp16 vfpv3-fp16 vfpv3xd vfpv3xd-fp16 vfpv4 vfpv4-d16 Valid arguments to -mtp=: auto cp15 soft Known floating-point ABIs (for use with the -mfloat-abi= option): hard soft softfp Known ARM CPUs (for use with the -mcpu= and -mtune= options): arm1020e arm1020t arm1022e arm1026ej-s arm10e arm10tdmi arm1136j-s arm1136jf-s arm1156t2-s arm1156t2f-s arm1176jz-s arm1176jzf-s arm2 arm250 arm3 arm6 arm60 arm600 arm610 arm620 arm7 arm70 arm700 arm700i arm710 arm7100 arm710c arm710t arm720 arm720t arm740t arm7500 arm7500fe arm7d arm7di arm7dm arm7dmi arm7m arm7tdmi arm7tdmi-s arm8 arm810 arm9 arm920 arm920t arm922t arm926ej-s arm940t arm946e-s arm966e-s arm968e-s arm9e arm9tdmi cortex-a12 cortex-a15 cortex-a15.cortex-a7 cortex-a17 cortex-a17.cortex-a7 cortex-a32 cortex-a35 cortex-a5 cortex-a53 cortex-a57 cortex-a57.cortex-a53 cortex-a7 cortex-a72 cortex-a72.cortex-a53 cortex-a8 cortex-a9 cortex-m0 cortex-m0.small-multiply cortex-m0plus cortex-m0plus.small-multiply cortex-m1 cortex-m1.small-multiply cortex-m3 cortex-m4 cortex-m7 cortex-r4 cortex-r4f cortex-r5 cortex-r7 cortex-r8 ep9312 exynos-m1 fa526 fa606te fa626 fa626te fa726te fmp626 generic-armv7-a iwmmxt iwmmxt2 marvell-pj4 mpcore mpcorenovfp native qdf24xx strongarm strongarm110 strongarm1100 strongarm1110 xgene1 xscale TLS dialect to use: gnu gnu2  This stuff is quite interesting, this shows what different options and CPU's that the compiler and can handle. ### Creating a armel conan profile We already touched on profiles , but this time we'll need to create a profile using the arm-linux-gnueabi toolchain. I will not dwelve into profiles to much , there is however more information on the conan website. This profiles will be used solely in the docker, there is at least three ways we can do this. 1. create the profile inside the docker, using the docker users ~/.conan/profiles directory. Then we can use the profile profile as usual conan search "....." --profile=armel 2. Create the profile on the host, and since the home directory is mounted in /src/ its possible to use the absolute path ie. /src/.conan/profiles directory. i.e conan search "....." --profile=/src/.conan/profiles/armel 3. The third alternative is to install it using conan config install. Conan config install can install configuration files from different sources to the local configuration directory. But before we do that we need to create an armel profile. There is a possibility to use multiple profiles in the command line. You can specify multiple profiles in the command line. The applied configuration will be the composition of all the profiles applied in the order they are specified. This gives us the opportunity to use ie [build_require] instead of injecting something that is going to be used in all builds. Lets create another profile where we set the Gorby compiler flags. cat <<EOF > Gorby_Flags [env] CXXFLAGS="-marm -march=armv5te -mcpu=arm926ej-s -mtune=arm926ej-s" EOF  Lets continue with the armel profile. In a profile its possible to declare variables that will be replaced by conan before the profile is applied. The variables have to be declared at the top of the file. armel profile: cat ~/.conan/profiles/armel  target_host=arm-linux-gnueabi os_target=Linux cc_compiler=gcc cxx_compiler=g++ [settings] os=Linux os_build=os_target
compiler=gcc
compiler.version=6
arch=armv5el
compiler.libcxx=libstdc++11

[env]
AR=$target_host-ar AS=$target_host-as
RANLIB=$target_host-ranlib LD=$target_host-ld
STRIP=$target_host-strip CC=$target_host-$cc_compiler CHOST=$target_host
CXX=$target_host-$cxx_compiler


### Host/build context

As we discussed before there is a distiction between host and build. Host is whats running on your host machine, thats probably your PC or laptop. The build context in this case is Gorby. So why is this important? The reason is that we want to make use of the conan to fetch us the build tools that we need to build binaries for the target system. In this case we want cmake installed in the docker (it already is, but version 3.7.2 so we want a newer) see this link for further information. I will keep it simple, first I will create a tools directory in which I will install the conanfile.txt which only has

[requires]
cmake/3.19.3

[generators]
virtualenv


Using that together with a profile with native gcc that is installed in the docker container. This will create a generated virtualenv which can activated and consequently give access to a newer cmake.

docker exec -t  GorbyBuild /bin/bash -c '
mkdir -p /tmp/tools
cat <<EOF > /tmp/tools/conanfile.txt
[requires]
cmake/3.19.3

[generators]
virtualenv
EOF
'

docker exec -t  GorbyBuild /bin/bash -c "
mkdir -p /tmp/tools/gen; cd /tmp/tools/gen
/home/compiler/.local/bin/conan install .. --profile=default
echo
"

[1m[36mConfiguration:[0m
[settings]
arch=x86_64
arch_build=x86_64
build_type=Release
compiler=gcc
compiler.libcxx=libstdc++
compiler.version=6
os=Linux
os_build=Linux
[options]
[build_requires]
[env]

[1m[35mconanfile.txt: [0m[1m[37mInstalling package[0m
[1m[33mRequirements[0m
[1m[36m    cmake/3.19.3 from 'conan-center' - Cache[0m
[1m[36m    openssl/1.1.1i from 'conan-center' - Cache[0m
[1m[33mPackages[0m
[1m[36m    cmake/3.19.3:5c09c752508b674ca5cb1f2d327b5a2d582866c8 - Cache[0m
[1m[36m    openssl/1.1.1i:f7e573cb501ccfc49e9e4d84de886bc1ef2e6ebb - Cache[0m

[1m[36mcmake/3.19.3: [0m[1m[37mAppending PATH environment variable: /home/compiler/.conan/data/cmake/3.19.3/_/_/package/5c09c752508b674ca5cb1f2d327b5a2d582866c8/bin[0m
[1m[36mconanfile.txt: [0m[1m[37mGenerator virtualenv created activate.sh[0m
[1m[36mconanfile.txt: [0m[1m[37mGenerator virtualenv created environment.sh.env[0m
[1m[36mconanfile.txt: [0m[1m[37mGenerator virtualenv created environment.ps1.env[0m
[1m[36mconanfile.txt: [0m[1m[37mGenerator virtualenv created deactivate.sh[0m
[1m[36mconanfile.txt: [0m[1m[37mGenerator virtualenv created activate.ps1[0m
[1m[36mconanfile.txt: [0m[1m[37mGenerator virtualenv created deactivate.ps1[0m
[1m[36mconanfile.txt: [0m[1m[37mGenerator txt created conanbuildinfo.txt[0m
[1m[36mconanfile.txt: [0m[1m[37mGenerated conaninfo.txt[0m
[1m[36mconanfile.txt: [0m[1m[37mGenerated graphinfo[0m
[0m


### Ev3dev-cpp cross compile

There is one more thing that needs to be done before we can start working on our project. The ev3dev library is not part of the conan remote repository, so we need to build it in the docker with our cross-compiler toolchain so it is available in the docker conan cache.

We already created a conanfile.py when we Conan new/create section. All we need to do is to use the same concept, but using a different profile. This time we need to do it in the docker with the my_arm and arm_flags

docker exec -t GorbyBuild /bin/bash -c "
cd /src/tmp/conan/ev3dev-latest
/home/compiler/.local/bin/conan create /src/tmp/conan/ev3dev-latest --profile=my_arm --profile=arm_flags
echo
"

Exporting package recipe
ev3dev/latest: A new conanfile.py version was exported
ev3dev/latest: Folder: /home/compiler/.conan/data/ev3dev/latest/_/_/export
Configuration:
[settings]
arch=armv5el
compiler=gcc
compiler.libcxx=libstdc++11
compiler.version=6
os=Linux
os_build=Linux
[options]
[build_requires]
[env]
AR=arm-linux-gnueabi-ar
AS=arm-linux-gnueabi-as
CC=arm-linux-gnueabi-gcc
CHOST=arm-linux-gnueabi
CPPFLAGS=-marm -march=armv5te -mcpu=arm926ej-s -mtune=arm926ej-s
CXX=arm-linux-gnueabi-g++
CXXFLAGS=-marm -march=armv5te -mcpu=arm926ej-s -mtune=arm926ej-s
LD=arm-linux-gnueabi-ld
RANLIB=arm-linux-gnueabi-ranlib
STRIP=arm-linux-gnueabi-strip
ev3dev/latest: Forced build from source
Installing package: ev3dev/latest
Requirements
ev3dev/latest from local cache - Cache
Packages
ev3dev/latest:1545952387bac72781ac7816c6257544b5c273bc - Build

Cross-build from 'Linux:x86_64' to 'Linux:armv5el'
ev3dev/latest: Configuring sources in /home/compiler/.conan/data/ev3dev/latest/_/_/source
Cloning into 'ev3dev-lang-cpp'...
remote: Enumerating objects: 800, done.
.
.
.
ev3dev/latest: Copying sources to build folder
ev3dev/latest: Building your package in /home/compiler/.conan/data/ev3dev/latest/_/_/build/1545952387bac72781ac7816c6257544b5c273bc
ev3dev/latest: Generator txt created conanbuildinfo.txt
ev3dev/latest: Calling build()
-- No build type selected, default to RelWithDebInfo
-- The C compiler identification is GNU 6.3.0
-- The CXX compiler identification is GNU 6.3.0
-- Check for working C compiler: /usr/bin/arm-linux-gnueabi-gcc
-- Check for working C compiler: /usr/bin/arm-linux-gnueabi-gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/arm-linux-gnueabi-g++
-- Check for working CXX compiler: /usr/bin/arm-linux-gnueabi-g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
.
.
.
-- Build files have been written to: /home/compiler/.conan/data/ev3dev/latest/_/_/build/1545952387bac72781ac7816c6257544b5c273bc
ev3dev/latest: WARN: build_type setting should be defined.
Scanning dependencies of target ev3dev
Scanning dependencies of target api_tests
[  5%] Building CXX object CMakeFiles/ev3dev.dir/ev3dev.cpp.o
[ 10%] Building CXX object tests/CMakeFiles/api_tests.dir/api_tests.cpp.o
[ 15%] Building CXX object tests/CMakeFiles/api_tests.dir/__/ev3dev.cpp.o
[ 21%] Linking CXX static library libev3dev.a
[ 21%] Built target ev3dev
Scanning dependencies of target button-test
Scanning dependencies of target ev3dev-lang-demo
Scanning dependencies of target remote_control-test
Scanning dependencies of target sound-test
Scanning dependencies of target ev3dev-lang-test
Scanning dependencies of target drive-test
Scanning dependencies of target remote-control
[ 26%] Building CXX object demos/CMakeFiles/remote_control-test.dir/remote_control-test.cpp.o
.
.
.
[ 94%] Built target ev3dev-lang-demo
[100%] Built target api_tests
ev3dev/latest: Package '1545952387bac72781ac7816c6257544b5c273bc' built
ev3dev/latest: Build folder /home/compiler/.conan/data/ev3dev/latest/_/_/build/1545952387bac72781ac7816c6257544b5c273bc
ev3dev/latest: Generated conaninfo.txt
ev3dev/latest: Generated conanbuildinfo.txt
ev3dev/latest: Generating the package
ev3dev/latest: Package folder /home/compiler/.conan/data/ev3dev/latest/_/_/package/1545952387bac72781ac7816c6257544b5c273bc
ev3dev/latest: Calling package()
ev3dev/latest package(): Packaged 1 '.h' file: ev3dev.h
ev3dev/latest package(): Packaged 1 '.a' file: libev3dev.a
ev3dev/latest: Package '1545952387bac72781ac7816c6257544b5c273bc' created
ev3dev/latest: Created package revision a4bc1dac9e75d06acae869ff5d027dbf



Voilá! Nice and easy… Lets verify that infact all the things that we want are there.

docker exec -t GorbyBuild /bin/bash -c '
/home/compiler/.local/bin/conan search ev3dev/latest@
echo
'

Existing packages for recipe ev3dev/latest:

Package_ID: 1545952387bac72781ac7816c6257544b5c273bc
[options]
fPIC: True
shared: False
[settings]
arch: armv5el
compiler: gcc
compiler.libcxx: libstdc++11
compiler.version: 6
os: Linux
Outdated from recipe: False


It might be possible to bind the conan cache from our host system to share it inside the docker. In that case it would be possible to see the arm version of ev3dev outside the docker. But I leave that to someone else to see if it works, and for the time being I see no benefit of sharing the arm binary. A better solution would be to set up a remote cache for all the different builds of ev3dev but then again, I leave that to someone else to figure out how to do it.

## Makeing a project

Finally, lets create a project. Nothing fancy, just to check that everything is working as expected.

Here is the tree structure.

.
├── arm_build
├── CMakeLists.txt
├── conan
│   ├── arm_gcc
│   ├── conanfile.txt
│   ├── env
│   │   ├── conanfile.txt
│   │   └── virtualenv
│   ├── x86_clang
│   └── x86_gcc
├── src
│   ├── CMakeLists.txt
│   └── main.cpp
├── x86_build
└── x86_clang

10 directories, 5 files


The concept is to be able to build the project in two different flavors.

X86_64
The X86_64 version is mainly used for testing, the host machine does not have any sensors or motors connected to it. But some of sensors can be simulated, and output signals can be read which means its possible to test the sofware in a controlled enviroment before sending it out on exploration with untested code.
ARM
The arm version is what Gorby brain will consists of. After testing and evaluating the x86_64 code , the arm code is the final product that will be added to Gorby, a controlled test environment is one thing. But sending out Gorby on a real mission will give us the complete picture on how Gorby will handle it self in different situations, many which probably haven't been thought of …..

Lets start working our way up. We have three build directories out of source tree build directories x86_build, x86_clang and arm_build These are build directories for cmake, its the directory where cmake will store all its objects,binaries and caches. The final product for the different platform will be found here in the bin directory

The src directory contains the source to the final product. The last directory conan contains the conanfile.txt which looks like

[requires]
fmt/7.1.3
asio/1.18.1
ev3dev/latest

[generators]
cmake
compiler_args


These are the required 3rd part libraries this project needs that are dependent on what compiler and arhitecture we want to build. It also includes the generators (cmake, compiler_args), though the compilers_args are not really necessary I like to include it just to be able to look what compiler args are provided. There is one more directory which I haven't mentioned yet and thats the conan/env. It has one requirement file, and that is for the tools I want to include , this will generate a virtualenv which can be activated to be able to use the tool. Lets first take a look at the conan/env/conanfile.txt/

[requires]
cmake/3.19.3

[generators]
virtualenv


When we run conan install on this we will get a activate script for the host which will setup the paths to make sure we are using cmake 3.19.3

Lets try it out.

cd conan/env/virtualenv
conan install .. --profile=default > /dev/null
source conan/env/virtualenv/activate
cmake --version

cmake version 3.19.3

CMake suite maintained and supported by Kitware (kitware.com/cmake).


I intentionally removed some of the output from install command to keep this abit shorter.

### X86_64 build

To build for x86_64 system only the host system and a native compiler is used, but before we build anything we need to activate the virtualenv so that it uses the tools that we want (in this case cmake)

The first thing that needs to be done is to generate the conan cmake files using the default profile. We do that as follows

source conan/env/virtualenv/activate.sh
cd conan/x86_gcc
conan install .. --profile=default

Configuration:
[settings]
arch=x86_64
arch_build=x86_64
build_type=Release
compiler=gcc
compiler.libcxx=libstdc++11
compiler.version=10
os=Linux
os_build=Linux
[options]
[build_requires]
[env]

conanfile.txt: Installing package
Requirements
asio/1.18.1 from 'conan-center' - Cache
ev3dev/latest from local cache - Cache
fmt/7.1.3 from 'conan-center' - Cache
Packages
asio/1.18.1:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 - Cache

conanfile.txt: Generator txt created conanbuildinfo.txt
conanfile.txt: Generator compiler_args created conanbuildinfo.args
conanfile.txt: Generator cmake created conanbuildinfo.cmake
conanfile.txt: Generated conaninfo.txt
conanfile.txt: Generated graphinfo


The generated output includes a conanbuildinfo.cmake which can be included from the CMakeLists.txt, now its time to build the project we make sure that the virtualenv is activated.

source conan/env/virtualenv/activate.sh
cmake --version

cmake version 3.19.3

CMake suite maintained and supported by Kitware (kitware.com/cmake).


Et Voilá! We can use cmake 3.19.

With that fixed, its time to fix the CmakeLists.txt to include the generated cmake. There are some different strategies that can be made here, though I haven't figured out which one is the best.

#### Approach - using variable in cmake

This approach basically checks a variable inside the cmake and based on that it chooses a conan directory.

cmake_minimum_required(VERSION 3.7)

SET(PRJ_BUILD_T "x86" CACHE STRING "Build type to use")

if( ${PRJ_BUILD_T} STREQUAL "x86_gcc") set(CMAKE_C_COMPILER gcc) set(CMAKE_CXX_COMPILER g++) set(PRJ_CONAN_DIR "${CMAKE_SOURCE_DIR}/conan/x86_gcc")
elseif(${PRJ_BUILD_T} STREQUAL "x86_clang") message(STATUS "Hello clang") set(CMAKE_C_COMPILER clang) set(CMAKE_CXX_COMPILER clang++) set(PRJ_CONAN_DIR "${CMAKE_SOURCE_DIR}/conan/x86_clang")
elseif(${PRJ_BUILD_T} STREQUAL "arm_gcc") set(CMAKE_C_COMPILER arm-linux-gnueabi-gcc) set(CMAKE_CXX_COMPILER arm-linux-gnueabi-g++) set(PRJ_CONAN_DIR "${CMAKE_SOURCE_DIR}/conan/arm_gcc")
endif()



The variable PRJ_BUILD_T has three options. The two first is focused on building on the host, the last one arm_gcc

x86_gcc
sets the compiler to gcc, and sets the variable PRJ_CONAN_DIR=${CMAKE_SOURCE_DIR}/conan/x86_gcc x86_clang sets the compiler to clang, and sets the variable PRJ_CONAN_DIR=${CMAKE_SOURCE_DIR}/conan/x86_clang
arm_gcc
set the x-compiler arm-linux-gnueabi-g++ and sets the variable PRJ_CONAN_DIR=${CMAKE_SOURCE_DIR}/conan/arm_gcc This works, but im not particularly fond of doing it this way. But it works, and for this purpose im satisfied. The pros with this approach is that when setting the PRJ_BUILD_TYPE it automatically uses the right path and conan directory, on the other hand it easily becomes an explosion of configuration if for example we start mixing the conan and compilers. I.e if we want to build the project with conan binaries from clang and the actual project with gcc and so on. #### Approach - using linked conan build Another approach is to use a soft links to a conan directory. This will leave out the CMakeLists.txt, but then we always need to make sure we have the right link. This approach has the benefit of being more versatile, on the other hand it needs manual configuration, and easy to forget to change the links when using another configuration. #### Final build (x86 gcc) The final build is to use "out-of-source" build in the directory x86_build We will use the approach where we modify the cmake with variable PRJ_BUILD_TYPE=... 1. First we activate so we get all the tools 1. since we setting the gcc inside cmake there is no need to set the environment variables 2. Change the directory to the out-of source tree build x86_build source conan/env/virtualenv/activate.sh cmake --version cd x86_build cmake -DPRJ_BUILD_TYPE=x86_gcc ..  cmake version 3.19.3 CMake suite maintained and supported by Kitware (kitware.com/cmake). -- The C compiler identification is GNU 10.2.0 -- The CXX compiler identification is GNU 10.2.0 -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working C compiler: /usr/bin/gcc - skipped -- Detecting C compile features -- Detecting C compile features - done -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: /usr/bin/g++ - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Conan: Adjusting output directories -- Conan: Using cmake global configuration -- Conan: Adjusting default RPATHs Conan policies -- Conan: Adjusting language standard -- Current conanbuildinfo.cmake directory: /home/calle/git/my_prj/conan/x86_gcc -- Conan: Compiler GCC>=5, checking major version 10 -- Conan: Checking correct version: 10 -- Configuring done -- Generating done -- Build files have been written to: /home/calle/git/my_prj/x86_build  Now finally we build it using make. source conan/env/virtualenv/activate.sh cmake --version cd x86_build make -j8 VERBOSE=1  cmake version 3.19.3 CMake suite maintained and supported by Kitware (kitware.com/cmake). /home/calle/.conan/data/cmake/3.19.3/_/_/package/5c09c752508b674ca5cb1f2d327b5a2d582866c8/bin/cmake -S/home/calle/git/my_prj -B/home/calle/git/my_prj/x86_build --check-build-system CMakeFiles/Makefile.cmake 0 /home/calle/.conan/data/cmake/3.19.3/_/_/package/5c09c752508b674ca5cb1f2d327b5a2d582866c8/bin/cmake -E cmake_progress_start /home/calle/git/my_prj/x86_build/CMakeFiles /home/calle/git/my_prj/x86_build//CMakeFiles/progress.marks make -f CMakeFiles/Makefile2 all make[1]: Entering directory '/home/calle/git/my_prj/x86_build' make -f src/CMakeFiles/third.dir/build.make src/CMakeFiles/third.dir/depend make[2]: Entering directory '/home/calle/git/my_prj/x86_build' cd /home/calle/git/my_prj/x86_build && /home/calle/.conan/data/cmake/3.19.3/_/_/package/5c09c752508b674ca5cb1f2d327b5a2d582866c8/bin/cmake -E cmake_depends "Unix Makefiles" /home/calle/git/my_prj /home/calle/git/my_prj/src /home/calle/git/my_prj/x86_build /home/calle/git/my_prj/x86_build/src /home/calle/git/my_prj/x86_build/src/CMakeFiles/third.dir/DependInfo.cmake --color= Dependee "/home/calle/git/my_prj/x86_build/src/CMakeFiles/third.dir/DependInfo.cmake" is newer than depender "/home/calle/git/my_prj/x86_build/src/CMakeFiles/third.dir/depend.internal". Dependee "/home/calle/git/my_prj/x86_build/src/CMakeFiles/CMakeDirectoryInformation.cmake" is newer than depender "/home/calle/git/my_prj/x86_build/src/CMakeFiles/third.dir/depend.internal". Scanning dependencies of target third make[2]: Leaving directory '/home/calle/git/my_prj/x86_build' make -f src/CMakeFiles/third.dir/build.make src/CMakeFiles/third.dir/build make[2]: Entering directory '/home/calle/git/my_prj/x86_build' [ 50%] Building CXX object src/CMakeFiles/third.dir/main.cpp.o cd /home/calle/git/my_prj/x86_build/src && /usr/bin/g++ -I/home/calle/.conan/data/fmt/7.1.3/_/_/package/b173bbda18164d49a449ffadc1c9e817f49e819d/include -I/home/calle/.conan/data/asio/1.18.1/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include -I/home/calle/.conan/data/ev3dev/latest/_/_/package/b173bbda18164d49a449ffadc1c9e817f49e819d/include -I/home/calle/git/my_prj/include -I/home/calle/git/my_prj/src/include -Wall -Wextra -pedantic -DASIO_STANDALONE -std=gnu++14 -o CMakeFiles/third.dir/main.cpp.o -c /home/calle/git/my_prj/src/main.cpp [100%] Linking CXX executable ../bin/third cd /home/calle/git/my_prj/x86_build/src && /home/calle/.conan/data/cmake/3.19.3/_/_/package/5c09c752508b674ca5cb1f2d327b5a2d582866c8/bin/cmake -E cmake_link_script CMakeFiles/third.dir/link.txt --verbose=1 /usr/bin/g++ -Wall -Wextra -pedantic CMakeFiles/third.dir/main.cpp.o -o ../bin/third -L/home/calle/.conan/data/fmt/7.1.3/_/_/package/b173bbda18164d49a449ffadc1c9e817f49e819d/lib -L/home/calle/.conan/data/ev3dev/latest/_/_/package/b173bbda18164d49a449ffadc1c9e817f49e819d/lib -Wl,-rpath,/home/calle/.conan/data/fmt/7.1.3/_/_/package/b173bbda18164d49a449ffadc1c9e817f49e819d/lib:/home/calle/.conan/data/ev3dev/latest/_/_/package/b173bbda18164d49a449ffadc1c9e817f49e819d/lib -lfmt -lev3dev -lpthread make[2]: Leaving directory '/home/calle/git/my_prj/x86_build' [100%] Built target third make[1]: Leaving directory '/home/calle/git/my_prj/x86_build' /home/calle/.conan/data/cmake/3.19.3/_/_/package/5c09c752508b674ca5cb1f2d327b5a2d582866c8/bin/cmake -E cmake_progress_start /home/calle/git/my_prj/x86_build/CMakeFiles 0  #### Final build (x86 clang) This is done basically the same way. The difference is that we need to use the clang profile to generate the output from conan source conan/env/virtualenv/activate.sh cmake --version cd conan/x86_clang conan install .. --profile=clang  cmake version 3.19.3 CMake suite maintained and supported by Kitware (kitware.com/cmake). Configuration: [settings] arch=x86_64 arch_build=x86_64 build_type=Release compiler=clang compiler.libcxx=libstdc++11 compiler.version=11 os=Linux os_build=Linux [options] [build_requires] [env] CC=/usr/bin/clang CXX=/usr/bin/clang++ conanfile.txt: Installing package Requirements asio/1.18.1 from 'conan-center' - Cache ev3dev/latest from local cache - Cache fmt/7.1.3 from 'conan-center' - Cache Packages asio/1.18.1:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 - Cache ev3dev/latest:0ac8c9952b8651eefe078adcd928017cea3e0340 - Cache fmt/7.1.3:0ac8c9952b8651eefe078adcd928017cea3e0340 - Cache Installing (downloading, building) binaries... asio/1.18.1: Already installed! ev3dev/latest: Already installed! fmt/7.1.3: Already installed! conanfile.txt: Generator txt created conanbuildinfo.txt conanfile.txt: Generator cmake created conanbuildinfo.cmake conanfile.txt: Generator compiler_args created conanbuildinfo.args conanfile.txt: Generated conaninfo.txt conanfile.txt: Generated graphinfo  Next we activate the build environment and executes cmake source conan/env/virtualenv/activate.sh cmake --version cd x86_clang cmake -DPRJ_BUILD_TYPE=x86_clang ..  cmake version 3.19.3 CMake suite maintained and supported by Kitware (kitware.com/cmake). -- Hello clang -- The C compiler identification is Clang 11.0.1 -- The CXX compiler identification is Clang 11.0.1 -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working C compiler: /usr/bin/clang - skipped -- Detecting C compile features -- Detecting C compile features - done -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: /usr/bin/clang++ - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Conan: Adjusting output directories -- Conan: Using cmake global configuration -- Conan: Adjusting default RPATHs Conan policies -- Conan: Adjusting language standard -- Current conanbuildinfo.cmake directory: /home/calle/git/my_prj/conan/x86_clang -- Conan: Compiler Clang>=8, checking major version 11 -- Conan: Checking correct version: 11 -- Configuring done -- Generating done -- Build files have been written to: /home/calle/git/my_prj/x86_clang  And finally we build the project cd x86_clang make -j8 VERBOSE=1  /home/calle/.conan/data/cmake/3.19.3/_/_/package/5c09c752508b674ca5cb1f2d327b5a2d582866c8/bin/cmake -S/home/calle/git/my_prj -B/home/calle/git/my_prj/x86_clang --check-build-system CMakeFiles/Makefile.cmake 0 /home/calle/.conan/data/cmake/3.19.3/_/_/package/5c09c752508b674ca5cb1f2d327b5a2d582866c8/bin/cmake -E cmake_progress_start /home/calle/git/my_prj/x86_clang/CMakeFiles /home/calle/git/my_prj/x86_clang//CMakeFiles/progress.marks make -f CMakeFiles/Makefile2 all make[1]: Entering directory '/home/calle/git/my_prj/x86_clang' make -f src/CMakeFiles/third.dir/build.make src/CMakeFiles/third.dir/depend make[2]: Entering directory '/home/calle/git/my_prj/x86_clang' cd /home/calle/git/my_prj/x86_clang && /home/calle/.conan/data/cmake/3.19.3/_/_/package/5c09c752508b674ca5cb1f2d327b5a2d582866c8/bin/cmake -E cmake_depends "Unix Makefiles" /home/calle/git/my_prj /home/calle/git/my_prj/src /home/calle/git/my_prj/x86_clang /home/calle/git/my_prj/x86_clang/src /home/calle/git/my_prj/x86_clang/src/CMakeFiles/third.dir/DependInfo.cmake --color= make[2]: Leaving directory '/home/calle/git/my_prj/x86_clang' make -f src/CMakeFiles/third.dir/build.make src/CMakeFiles/third.dir/build make[2]: Entering directory '/home/calle/git/my_prj/x86_clang' make[2]: Nothing to be done for 'src/CMakeFiles/third.dir/build'. make[2]: Leaving directory '/home/calle/git/my_prj/x86_clang' [100%] Built target third make[1]: Leaving directory '/home/calle/git/my_prj/x86_clang' /home/calle/.conan/data/cmake/3.19.3/_/_/package/5c09c752508b674ca5cb1f2d327b5a2d582866c8/bin/cmake -E cmake_progress_start /home/calle/git/my_prj/x86_clang/CMakeFiles 0  Et Voila! It worked.. ### Arm build So far all we done has been to make sure that our project is able to build on out host system, but the final goal is to have it running in Gorby, which means we need to compile it as a arm binary. The final part is to build the whole project for the target system which is using an arm processor. But since we don't have the cross-compiler locally installed this needs to be done inside the docker that we setup in previous section. Lets first start the docker docker start GorbyBuild  GorbyBuild  As with the host machine, we need to make sure that the virtualenv is installed. Since I don't want to mess with the virtual env that was installed on the host I create a new directory docker exec GorbyBuild /bin/bash -c " cd${docker_dir}/conan/env
mkdir docker_virt
cd docker_virt
~/.local/bin/conan install .. --profile=default
"

Configuration:
[settings]
arch=x86_64
arch_build=x86_64
build_type=Release
compiler=gcc
compiler.libcxx=libstdc++
compiler.version=6
os=Linux
os_build=Linux
[options]
[build_requires]
[env]

conanfile.txt: Installing package
Requirements
cmake/3.19.3 from 'conan-center' - Cache
openssl/1.1.1i from 'conan-center' - Cache
Packages
cmake/3.19.3:5c09c752508b674ca5cb1f2d327b5a2d582866c8 - Cache
openssl/1.1.1i:f7e573cb501ccfc49e9e4d84de886bc1ef2e6ebb - Cache

cmake/3.19.3: Appending PATH environment variable: /home/compiler/.conan/data/cmake/3.19.3/_/_/package/5c09c752508b674ca5cb1f2d327b5a2d582866c8/bin
conanfile.txt: Generator virtualenv created activate.ps1
conanfile.txt: Generator virtualenv created environment.sh.env
conanfile.txt: Generator virtualenv created environment.ps1.env
conanfile.txt: Generator virtualenv created deactivate.ps1
conanfile.txt: Generator virtualenv created deactivate.sh
conanfile.txt: Generator virtualenv created activate.sh
conanfile.txt: Generator txt created conanbuildinfo.txt
conanfile.txt: Generated conaninfo.txt
conanfile.txt: Generated graphinfo


Now its time to install the 3rd-part dependecies as we did with x86 builds but we need to use the profiles my_arm and arm_flags

docker exec GorbyBuild /bin/bash -c "
source ${docker_dir}/conan/env/docker_virt/activate.sh cd${docker_dir}/conan/arm_gcc
~/.local/bin/conan install .. --profile=my_arm --profile=arm_flags
"

Configuration:
[settings]
arch=armv5el
compiler=gcc
compiler.libcxx=libstdc++11
compiler.version=6
os=Linux
os_build=Linux
[options]
[build_requires]
[env]
AR=arm-linux-gnueabi-ar
AS=arm-linux-gnueabi-as
CC=arm-linux-gnueabi-gcc
CHOST=arm-linux-gnueabi
CPPFLAGS=-marm -march=armv5te -mcpu=arm926ej-s -mtune=arm926ej-s
CXX=arm-linux-gnueabi-g++
CXXFLAGS=-marm -march=armv5te -mcpu=arm926ej-s -mtune=arm926ej-s
LD=arm-linux-gnueabi-ld
RANLIB=arm-linux-gnueabi-ranlib
STRIP=arm-linux-gnueabi-strip
conanfile.txt: Installing package
Requirements
asio/1.18.1 from 'conan-center' - Cache
ev3dev/latest from local cache - Cache
fmt/7.1.3 from 'conan-center' - Cache
Packages
asio/1.18.1:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 - Cache
ev3dev/latest:1545952387bac72781ac7816c6257544b5c273bc - Cache
fmt/7.1.3:1545952387bac72781ac7816c6257544b5c273bc - Cache

Cross-build from 'Linux:x86_64' to 'Linux:armv5el'
conanfile.txt: Generator compiler_args created conanbuildinfo.args
conanfile.txt: Generator cmake created conanbuildinfo.cmake
conanfile.txt: Generator txt created conanbuildinfo.txt
conanfile.txt: Generated conaninfo.txt
conanfile.txt: Generated graphinfo


We have now make sure that the third party libraries are installed. Its time for the finalé , this is where we build the project for Gorby.

docker exec GorbyBuild /bin/bash -c "
source ${docker_dir}/conan/env/docker_virt/activate.sh cd${docker_dir}/arm_build
cmake -DPRJ_BUILD_TYPE=arm_gcc ..
"

-- The C compiler identification is GNU 6.3.0
-- The CXX compiler identification is GNU 6.3.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/arm-linux-gnueabi-gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/arm-linux-gnueabi-g++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- CMake FLAGS  -marm -march=armv5te -mcpu=arm926ej-s -mtune=arm926ej-s
-- Conan: Using cmake global configuration
-- Conan: Adjusting default RPATHs Conan policies
-- Current conanbuildinfo.cmake directory: /src/git/my_prj/conan/arm_gcc
-- Conan: Compiler GCC>=5, checking major version 6
-- Conan: Checking correct version: 6
-- Configuring done
-- Generating done
-- Build files have been written to: /src/git/my_prj/arm_build


And now to the grand-finalé!

docker exec GorbyBuild /bin/bash -c "
source ${docker_dir}/conan/env/docker_virt/activate.sh cd${docker_dir}/arm_build
make -j8 VERBOSE=1
"

/home/compiler/.conan/data/cmake/3.19.3/_/_/package/5c09c752508b674ca5cb1f2d327b5a2d582866c8/bin/cmake -S/src/git/my_prj -B/src/git/my_prj/arm_build --check-build-system CMakeFiles/Makefile.cmake 0
/home/compiler/.conan/data/cmake/3.19.3/_/_/package/5c09c752508b674ca5cb1f2d327b5a2d582866c8/bin/cmake -E cmake_progress_start /src/git/my_prj/arm_build/CMakeFiles /src/git/my_prj/arm_build//CMakeFiles/progress.marks
make  -f CMakeFiles/Makefile2 all
make[1]: Entering directory '/src/git/my_prj/arm_build'
make  -f src/CMakeFiles/third.dir/build.make src/CMakeFiles/third.dir/depend
make[2]: Entering directory '/src/git/my_prj/arm_build'
cd /src/git/my_prj/arm_build && /home/compiler/.conan/data/cmake/3.19.3/_/_/package/5c09c752508b674ca5cb1f2d327b5a2d582866c8/bin/cmake -E cmake_depends "Unix Makefiles" /src/git/my_prj /src/git/my_prj/src /src/git/my_prj/arm_build /src/git/my_prj/arm_build/src /src/git/my_prj/arm_build/src/CMakeFiles/third.dir/DependInfo.cmake --color=
Dependee "/src/git/my_prj/arm_build/src/CMakeFiles/third.dir/DependInfo.cmake" is newer than depender "/src/git/my_prj/arm_build/src/CMakeFiles/third.dir/depend.internal".
Dependee "/src/git/my_prj/arm_build/src/CMakeFiles/CMakeDirectoryInformation.cmake" is newer than depender "/src/git/my_prj/arm_build/src/CMakeFiles/third.dir/depend.internal".
Scanning dependencies of target third
make[2]: Leaving directory '/src/git/my_prj/arm_build'
make  -f src/CMakeFiles/third.dir/build.make src/CMakeFiles/third.dir/build
make[2]: Entering directory '/src/git/my_prj/arm_build'
[ 50%] Building CXX object src/CMakeFiles/third.dir/main.cpp.o
cd /src/git/my_prj/arm_build/src && /usr/bin/arm-linux-gnueabi-g++  -I/home/compiler/.conan/data/fmt/7.1.3/_/_/package/1545952387bac72781ac7816c6257544b5c273bc/include -I/home/compiler/.conan/data/asio/1.18.1/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include -I/home/compiler/.conan/data/ev3dev/latest/_/_/package/1545952387bac72781ac7816c6257544b5c273bc/include -I/src/git/my_prj/include -I/src/git/my_prj/src/include -marm -march=armv5te -mcpu=arm926ej-s -mtune=arm926ej-s   -Wall -Wextra  -DASIO_STANDALONE -std=gnu++14 -o CMakeFiles/third.dir/main.cpp.o -c /src/git/my_prj/src/main.cpp
/usr/bin/arm-linux-gnueabi-g++  -marm -march=armv5te -mcpu=arm926ej-s -mtune=arm926ej-s   -Wall -Wextra  CMakeFiles/third.dir/main.cpp.o -o ../bin/third   -L/home/compiler/.conan/data/fmt/7.1.3/_/_/package/1545952387bac72781ac7816c6257544b5c273bc/lib  -L/home/compiler/.conan/data/ev3dev/latest/_/_/package/1545952387bac72781ac7816c6257544b5c273bc/lib  -Wl,-rpath,/home/compiler/.conan/data/fmt/7.1.3/_/_/package/1545952387bac72781ac7816c6257544b5c273bc/lib:/home/compiler/.conan/data/ev3dev/latest/_/_/package/1545952387bac72781ac7816c6257544b5c273bc/lib -lfmt -lev3dev -lpthread
make[2]: Leaving directory '/src/git/my_prj/arm_build'
[100%] Built target third
make[1]: Leaving directory '/src/git/my_prj/arm_build'
/home/compiler/.conan/data/cmake/3.19.3/_/_/package/5c09c752508b674ca5cb1f2d327b5a2d582866c8/bin/cmake -E cmake_progress_start /src/git/my_prj/arm_build/CMakeFiles 0


Its just a matter of transferring the binary to Gorby and run it!

## Disclaimer

What I explained worked, but there might be better or easier way to achieve the same thing. I've taken shortcuts especially when it comes to cmake, this was not attended to be a tutorial on cmake therefor I just created the project quick and dirty. I will not state that this is the best way of doing things, I just states that this is a way of doing it.

## Footnotes:

1

The optimal block size for dd depends on disk and its not crucial it just takes longer..

2

tramp mode in emacs a short introduction.

Created: 2021-01-31 Sun 15:46

Validate