Blog

Building the Mender-Rust project with Yocto

18th Nov 2020

This is part III in a series on writing a Mender-client in Rust.

Read part II here, and part I here.

In order to really get the project tested, we are going to build it along with the the Yocto sources for the Mender project. For the uninitiated, Yocto is a configurable Linux distribution, which relies on the Bitbake build-system, and comes with a reference distribution called Poky.

My initial idea was to simply build a static binary, and install it, using a Yocto recipe. However, I did not manage to complete this install, due to OpenSSL versioning errors.

This is what the binary looks like when inspected:

➜ Mender-Rust git:(master) ✗ ldd target/debug/mender-rust    
    linux-vdso.so.1 (0x00007ffcadbed000)
    libgtk3-nocsd.so.0 => /usr/lib/x86_64-linux-gnu/libgtk3-nocsd.so.0 (0x00007fe38bc00000)
    libssl.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libssl.so.1.0.0 (0x00007fe38b998000)
    libcrypto.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (0x00007fe38b555000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fe38b351000)
    librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fe38b149000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fe38af2a000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fe38ad12000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe38a921000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fe38ce5d000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe38a583000)

Also the paths are static, and frankly I am unsure if this would even work, as the paths are most likely different in the final sysroot in the image built, and I don't want to look into link-hacks like LD_LOAD, or rpath etc. Usually, this is what I have my colleagues Vratislav, and Kristian for, but since this is a Sunday night, this is probably not at the top of their priorities list.

In general, when building a Rust project with Yocto, there are two options available. These were found from https://layers.openembedded.org/layerindex/branch/master/layers/, and simply typing in rust. The first option

1) meta-rust

This project provides the rust-compiler, and the tools for building packages (cargo), and a few example projects. It also (supposedly) works well, alongside 'cargo-bitbake', which can be used to automatically generate Yocto-recipes for a rust crate.

2) meta-rust-bin

The meta-rust-bin project is a little different as it relies on pre-built tool-chains. Which means that they are using the tool-chains provided by the Rust team. In general the trade-off means that, while this provides less configureability, and is arguably a less 'Yoctoish' way of doing things, upgrading to the latest version of the tool-chain is easy as changing the checksum in the recipe.

After a little bit back and forth, I decided to go with the 'mender-rust-bin' version, as I am trying to keep things as easy as possible, and also, i will not be targeting anything else than the standard rust triplet on my machine 'x86_64-unknown-linux-gnueabihf'.

The first draft of the recipe looks like this:

inherit cargo

LICENSE = "CLOSED"
LICENSE_FLAGS = "commercial"

SRC_URI = "git:///home/olepor/misc/rust/Mender-Rust;protocol=file"
SRC_URI[md5sum] = "e9335536da6a611bd1ef58e64d3b9b88"
SRC_URI[sha256sum] = "b56b4b60ceaaa39440b59d32877f550601d2f4d5a437c1367aa3fe51aa9b1c13"

S = "${WORKDIR}/git"

SRCREV = "989e3fa50a378323bf665e23bfb0f3d4e39cdb8c"

Which is a simple Yocto recipe for building from my local git repository. This fails however, due to the dependency that the Mender-Rust project has on the local sources of 'mender-artifact-rust' project.

  Updating crates.io index
| error: failed to load source for a dependency on `mender_artifact`

Now, how to remedy this?

Let's upload the mender-artifact project to 'crates.io', which is the Rust project's index for packages. And the way Rust software is distributed, and is the way that all the other dependencies of Mender-Rust is resolved. From the 'Cargo.toml' file, we see that:

[package]
name = "mender-rust"
version = "0.1.1"
authors = ["Ole Petter <olepor@matnat.uio.no>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "target-feature=+crt-static"]

[dependencies]
serde = { version = "1.0", features = ["derive"] } # Derive Deserialization for the config struct
serde_json = "1" # serde_json
base64 = "0.10"
reqwest = "0.9"
rsa = "0.1.3"
log = "0.4"
simple_logger = "1.3.0"
hex = "0.3.2"
# OpenSSL = { version = "0.10", features = ["vendored"] }
openssl = "0.10.24"
ma = { path = "../mender-artifact-rust" , version = "0.1.1", package = "mender_artifact"  }

Since hard-coding the path now is futile, let's change the mender-artifact dependency to rely on the GitHub project.

...
...
openssl = "0.10.24"
ma = { git = "https://github.com/olepor/mender-artifact-rust.git", version = "0.1.1", package = "mender_artifact"  }

Which will then default to using the latest commit on the master branch, which suits us well.

Then now building the project with Yocto works fine, and our shiny Rust client works just fine on our own custom Linux setup :)