内容简介:In ourprevious post we discussed about howEmbedded systems are generally based on architectures different than the one used on the computers that developers are using to develop their software. A typical embedded device may use anSeting up such environneme
In ourprevious post we discussed about how Rust can be a great language for embedded programming. In this article, we'll explain an easy way to setup to cross build Rust code depending on system libraries, a common requirement when working on embedded systems.
Original photo by Stefan Cosma onUnsplash |
Cross compilation
Embedded systems are generally based on architectures different than the one used on the computers that developers are using to develop their software. A typical embedded device may use an ARM CPU while our laptops and desktops are based on x86-64 platforms. As a result it is important to be able to generate binaries compatible with the embedded platform (the target ) directly from the developer system (the host ). This requires a cross compiling environment on the host system configured according to the target settings. Such environment is generally the aggregation of two parts:
- the building tools (compiler, linker, etc) able to run on the host but producing code for the target platform, which is called the toolchain ;
- the set of libraries and headers for the target system which are needed to build the code, which is called the sysroot .
Seting up such environnement can be a tedious process. It's often very tricky to find the right combination of toolchains and sysroots and they are generally duct-taped together using various environment variables and hacky scripts.
Cross compiling Rust code
Fortunately, cross-building pure Rust code is generally much easier thanks to rustup
and cargo
.
Let's have a look at this example from rust-cross demonstrating how to setup a cross-build environment for ARMv7
on an Ubuntu system with three simple steps:
sudo apt-get install -qq gcc-arm-linux-gnueabihf rustup target add armv7-unknown-linux-gnueabihf ~/.cargo/config
[target.armv7-unknown-linux-gnueabihf] linker = "arm-linux-gnueabihf-gcc"
We can now very easily cross-build our Rust code using:
$ cargo build --target=armv7-unknown-linux-gnueabihf
This works well enough for simple applications relying solely on Rust code. But everything falls apart as soon as one crate wraps a C
or C++
system library, such as openssl
or GStreamer
. Such crates will need to link to the system library when building and so require to have not only the library built for target platform but also extra metadata such as a pkg-config file used by the build system to retrieve the proper compiler and linker flags.
Environment
In this article, we'll build the master
branch of gst-plugins-rs , the set of GStreamer plugins written in Rust , for the very popular Raspberry Pi board.
The Pi is running a Raspbian 10 based on Debian Buster and we will be building on a Fedora 32 using Rust 1.43.1
, the latest stable version.
For convenience we'll target ARMv7 while the Pi is actually running an ARMv6 CPU. This won't be a problem as long as the code is running on a Pi version 2, 3 or 4, as those are able to run ARMv7 code. We'll also install GStreamer from Debian armhf which is using a slightly different instructions set from Raspbian. In our case this won't be a problem as Debian Buster armfh and Raspbian are close enough.
Building with Cross
Cross is a "zero setup" cross compilation tool maintained by the Rust tools team . It's designed to make cross building Rust projects as easy as possible by providing Docker images containing the environment and toolchain needed to build for a specific target.
It's really easy to install:
$ cargo install cross
Cross building a Rust project can now simply be done by using:
$ cross build --target armv7-unknown-linux-gnueabihf
Cross will take care of picking the right image for the target, download it and do all the setup for the project to be transparently built inside the container.
So all we need to build our GStreamer plugins now is a Cross image with the required system dependencies installed. Unfortunately the default images used by Cross for building are based on Ubuntu 16.04
and the GStreamer version shipped with it is too old for our needs. When doing cross compilation is important to ensure that the sysroot used for building uses the same versions of the system libraries than the ones installed on the target. Not doing so may result in linking problem when trying to execute the binaries on the embedded device.
Furthermore we are going to use Debian multiarch support to install the arm
versions of our dependencies on the image. Multiarch has been greatly improved in Buster , the latest Debian version, so best to use it in our setup. This will also reduce the risk of incompatibility with the Pi as Raspbian is based on Debian Buster as well.
As a result we'll have to generate our own Buster-based image that Cross will use for cross building our GStreamer plugins.
As said above, Cross images are generated using Docker . As we are running Fedora we are actually going to use podman instead, a drop-in replacement for Docker properly integrated into Fedora. Podman implements the exact same command line interface as Docker so you should be able to reproduce the described steps by simply using the docker
command instead of podman
.
Let's use Cross's image Dockerfile as a template for our image:
$ git clone https://github.com/rust-embedded/cross.git $ cd cross $ cp docker/Dockerfile.armv7-unknown-linux-gnueabihf docker/Dockerfile.buster-gst-armv7-unknown-linux-gnueabihf
The file looks like this:
FROM ubuntu:16.04 COPY common.sh / RUN /common.sh COPY cmake.sh / RUN /cmake.sh COPY xargo.sh / RUN /xargo.sh RUN apt-get install --assume-yes --no-install-recommends \ g++-arm-linux-gnueabihf \ libc6-dev-armhf-cross COPY qemu.sh / RUN /qemu.sh arm softmmu COPY dropbear.sh / RUN /dropbear.sh COPY linux-image.sh / RUN /linux-image.sh armv7 COPY linux-runner / ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \ CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUNNER="/linux-runner armv7" \ CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc \ CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++ \ QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf \ RUST_TEST_THREADS=1
First we want our image to be based on buster
instead of Ubuntu 16.04
so we need to change the first line defining the base image:
FROM debian:buster
QEMU can be used by CI systems to cross run tests. We actually don't need it on modern hosts so we removed it as well as the dropbear
and linux-runner
integration.
In order to be able to build the gstreamer-rs crate, we enable Debian multiarch support and install the armhf
version of the GStreamer packages. We also need openssl
as it is another build dependency of the Rust plugins.
RUN dpkg --add-architecture armhf && \ apt-get update && \ apt-get install -y libgstreamer1.0-dev:armhf libgstreamer-plugins-base1.0-dev:armhf libssl-dev:armhf
Finally we need to tweak PKG_CONFIG_PATH
so the build system will pick-up the right lib when building:
PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig
Here is the final version of our Dockerfile
, you can also find it in this branch :
FROM debian:buster COPY common.sh / RUN /common.sh COPY cmake.sh / RUN /cmake.sh COPY xargo.sh / RUN /xargo.sh RUN apt-get install --assume-yes --no-install-recommends \ g++-arm-linux-gnueabihf \ libc6-dev-armhf-cross RUN dpkg --add-architecture armhf && \ apt-get update && \ apt-get install -y libgstreamer1.0-dev:armhf libgstreamer-plugins-base1.0-dev:armhf libssl-dev:armhf ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \ CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc \ CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++ \ QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf \ RUST_TEST_THREADS=1 \ PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig
We can now generate the image, we'll label it my/buster-gst-armv7-unknown-linux-gnueabihf:
Cross building gst-plugins-rs
Now that our image is ready we can build gst-plugins-rs . We start by retrieving it from git
:
$ git clone https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git $ cd gst-plugins-rs
We have to edit Cargo.toml
in order to disable some plugins :
-
csound
as the Debian package does not support multi-arch yet ; -
fallbackswitch
: it depends on GStreamer1.16
while Buster has1.14
; -
dav1d
: it's not yet packaged in Debian.
We also need to install Cross from git master
as one of my patch has not been released yet.
$ cargo install --git https://github.com/rust-embedded/cross.git
Finally we need to create a Cross.toml
file at the root of the project so cross
will use our image when building instead of the default one:
[target.armv7-unknown-linux-gnueabihf] image = "localhost/my/buster-gst-armv7-unknown-linux-gnueabihf"
We can now cross-compile gst-plugins-rs
:
$ cross build --target armv7-unknown-linux-gnueabihf
Once the build is done, all the .so
files for our plugins have been generated and can be safely copied to the Pi.
$ scp target/armv7-unknown-linux-gnueabihf/debug/libgst*.so pi:gst-rs
We can now ssh
to the Pi and try one of the plugins, for example using:
$ GST_PLUGIN_PATH=gst-rs gst-inspect-1.0 rsflvdemux
Conclusion
Thanks to Cross it's now easier than ever to setup cross compiling toolchains. Docker/Podman images and Debian multiarch allow us to cross build projects relying on system libraries in a well containerized setup.
One minor downside is the amount of boilerplate required to build custom Cross images. Some improvements are being discussed to simplify this process, including providing updated images . Building an image would then be really easy as we would just have to derive one of those images and install the extra system libraries we need.
The steps we followed in this tutorial should be easily adaptable for any other target supported by Cross , just use the corresponding Dockerfile
as template for your image. And of course you can use the same tools to cross build any project relying on other system libraries than GStreamer, just adapt the apt-get install
command in the Dockerfile
with your actual dependencies.
The combined convenience of Cross with the power and safety of Rust make them both great tools for embedded development. By saving developers from fighting toolchains setups or tracking memory corruption bugs, they can focus on writing robust and maintainable code. This is also what we do best at Collabora so please don't hesitate to contact us if you need any help with your Rust or embedded projects.
以上所述就是小编给大家介绍的《Cross building Rust GStreamer plugins for the Raspberry Pi》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
CSS 压缩/解压工具
在线压缩/解压 CSS 代码
HTML 编码/解码
HTML 编码/解码