The Raspberry Pi will be used as the target device for this document. If you have another target, e.g. some bitbake
concoction, then you will have to adapt the instructions accordingly.
Cross compilation is described in two ways - one that uses the cross
tool and one that is manual. Depending on your needs you may decide on one or the other.
The cross
tool attempts to make it as simple as possible to cross-compile software by automatically fetching the appropriate cross compile toolchain and environment.
Install docker if you have not already.
$ sudo apt install docker.io
Install cross for Rust.
$ cargo install cross
Install the tool according its own instructions. Ensure your docker permissions are set. Now you can use cross
in place of cargo
. e.g.
$ cross build --all --target armv7-unknown-linux-gnueabihf
The additional argument --target armv7-unknown-linux-gnueabihf
tells cross
to set up a build environment before invoking cargo
.
The cross
tool may have an issue running cargo
on Fedora / Red Hat dists due to a SELinux policy. Read the bug for a workaround.
The manual process gives you complete control on the build process but requires a bit more work.
A bug was raised asking how to cross-compile OPC UA for Rust and someone kindly answered with references. The links below were used to produce a working solution:
Raspberry Pi is the target architecture. I used Linux Subsystem for Windows with Debian to work through the steps.
These steps are derived from from sodiumoxide readme:
Debian has convenient packages for cross compilation and emulation.
$ sudo apt update
$ sudo apt install build-essential gcc-arm-linux-gnueabihf libc6-armhf-cross libc6-dev-armhf-cross qemu-system-arm qemu-user-static -y
Derived from a Stack Overflow answer and adapted to opcua:
$ cd /tmp
$ wget https://www.openssl.org/source/openssl-1.0.1t.tar.gz
$ tar xzf openssl-1.0.1t.tar.gz
$ cat > .opcuaARMenv << EOF
export MACHINE=armv7
export ARCH=arm
export CC=arm-linux-gnueabihf-gcc
EOF
$ source .opcuaARMenv
$ cd openssl-1.0.1t && ./config shared && make && cd -
The rustup
tool allows us to add another target to the Rust toolchain.
$ rustup target add armv7-unknown-linux-gnueabihf
With the compiler ready, we move onto the project and set up the target.
$ cd /my/path/to/opcua
$ mkdir .cargo
$ cat > .cargo/config << EOF
[target.armv7-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"
EOF
You now have the following in a opcua/.cargo/config
file:
[target.armv7-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"
Building is straightforward and just requires we specify where OpenSSL was built before invoking cargo
with the
correct build target.
$ cat > .opcuaSSLenv << EOF
export OPENSSL_LIB_DIR=/tmp/openssl-1.0.1t/
export OPENSSL_INCLUDE_DIR=/tmp/openssl-1.0.1t/include
export OPENSSL_STATIC=1
export QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf
EOF
source .opcuaSSLenv
cargo build --target armv7-unknown-linux-gnueabihf
Note OPENSSL_STATIC=1
, causes rust-openssl
to link to OpenSSL's static library which saves a
little effort in the next step. Alternatively you can copy the libcrypto.so
, libcrypto.so.1.0.0
, libssl.so
and
libssl.so.1.0.0
from $OPENSSL_LIB_DIR
into $QEMU_LD_PREFIX/lib
before running.
Qemu can run Arm binaries from your host environment with a qemu-arm-static
command - convenient!
So now we can test if the build works:
$ source .opcuaSSLenv
$ cd samples/simple-client
$ qemu-arm-static ../../target/armv7-unknown-linux-gnueabihf/debug/opcua-simple-client
or
$ source .opcuaSSLenv
$ cd samples/demo-server
$ qemu-arm-static ../../target/armv7-unknown-linux-gnueabihf/debug/opcua-demo-server