为嵌入式设备编译Rust/dbus进程间通信组件

嵌入式软件的构建

笔者在嵌入式软件开发过程中,之所以注重软件的构建方式,是因为快速、合理的编译构建方式能够提升开发效率,缩短软件发布周期,加快软件迭代,并且促使软件架构朝着更合理的方式发展、演进。举个例子,分布式代码版本管理工具Git决定了Linux内核的开发方式,可以说没有Git这一工具,就没有稳定且功能强大的现代Linux内核;另外Linus本人也声称自己是一个工具迷:Because hey, I'm a big believer in tools。对于嵌入式软件的开发而言,优秀的编译构建方法,也是一种常被忽略的重要工具。

对于Rust工程而言,其包含了一个完整的包管理工具,编译构建的可发挥空间没有C/C++工程那么大,但Rust工程常常是一个庞大工程的一部分,了解、解决一些常见的编译构建问题,也非常重要。近些年流行起来的“微服务架构”的核心思想本质上是一种老生重谈,简言之是通过一些进程内同步、进程间通信的方式将各个功能分为单个微小的服务。笔者一直在嵌入式软件开发中寻找更为合适的进程间组件,本文记录了笔者为嵌入式ARM设备交叉编译Rust/dbus库过程中解决的一些问题(虽然dbus的数据传递效率不适合性能稍低的嵌入式设备)。

交叉编译Rust/dbus的错误

开源社区提供了dbus的Rust语言的编程接口,可在此处访问。为了加快开发进度,笔者使用openwrt编译了dbus的官方库;之后使用以下环境来编译Rust/dbus

#!/bin/sh

export TARGET_CC=arm-openwrt-linux-gnueabi-gcc
OPENWRT_DIR='/home/yejq/program/openwrt'

which -a ${TARGET_CC} >/dev/null 2>/dev/null
if [ $? -ne 0 ] ; then
	export STAGING_DIR="${OPENWRT_DIR}/staging_dir/target-arm_cortex-a7+neon-vfpv4_glibc_eabi"
	export PATH="${OPENWRT_DIR}/staging_dir/toolchain-arm_cortex-a7+neon-vfpv4_gcc-11.2.0_glibc_eabi/bin:${PATH}"
	export TARGET_CFLAGS='-march=armv7-a -Os -fPIC -D_GNU_SOURCE -ggdb'
	export CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=${TARGET_CC}
fi
unset OPENWRT_DIR

接下来使用以下命令编译dbus-0.9.6

cargo build --release --target armv7-unknown-linux-gnueabihf
cargo build --release --examples --target armv7-unknown-linux-gnueabihf

结果遇到以下编译失败问题:

Caused by:
  process didn't exit successfully: `/home/yejq/dbus-0.9.6/target/release/build/libdbus-sys-75db788f41890c8a/build-script-build` (exit status: 101)
  --- stdout
  cargo:rerun-if-env-changed=DBUS_1_NO_PKG_CONFIG
  cargo:rerun-if-env-changed=PKG_CONFIG_ALLOW_CROSS_armv7-unknown-linux-gnueabihf
  cargo:rerun-if-env-changed=PKG_CONFIG_ALLOW_CROSS_armv7_unknown_linux_gnueabihf
  cargo:rerun-if-env-changed=TARGET_PKG_CONFIG_ALLOW_CROSS
  cargo:rerun-if-env-changed=PKG_CONFIG_ALLOW_CROSS
  cargo:rerun-if-env-changed=PKG_CONFIG_armv7-unknown-linux-gnueabihf
  cargo:rerun-if-env-changed=PKG_CONFIG_armv7_unknown_linux_gnueabihf
  cargo:rerun-if-env-changed=TARGET_PKG_CONFIG
  cargo:rerun-if-env-changed=PKG_CONFIG
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_armv7-unknown-linux-gnueabihf
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_armv7_unknown_linux_gnueabihf
  cargo:rerun-if-env-changed=TARGET_PKG_CONFIG_SYSROOT_DIR
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR

  --- stderr
  pkg_config failed: pkg-config has not been configured to support cross-compilation.

  Install a sysroot for the target platform and configure it via
  PKG_CONFIG_SYSROOT_DIR and PKG_CONFIG_PATH, or install a
  cross-compiling wrapper for pkg-config and set it via
  PKG_CONFIG environment variable.
  One possible solution is to check whether packages
  'libdbus-1-dev' and 'pkg-config' are installed:

可见,dbus-0.9.6依赖的libdbus-sys库编译出错了,原因是不能通过pkg-config找不到libdbus-1库;上面我们已经提到,该库已经由openwrt编译,但却找不到,如何解决这个问题呢(尽管openwrt为目柡设备的交叉编译生成了可用的pkg-config,但笔者希望不对openwrt SDK产生过多的依赖)?之后,笔者尝试为宿主机编译是成功的,因为我们为主机安装了libdbus-1-dev开发库。经分析可知,为主机编译dbus-0.9.6会生成一个libdbus-sysbuild.rs编译生成的应用,执行之,可以获得以下输出:

~/dbus-0.9.6$ ./target/release/build/libdbus-sys-75db788f41890c8a/build-script-build
cargo:rerun-if-env-changed=DBUS_1_NO_PKG_CONFIG
cargo:rerun-if-env-changed=PKG_CONFIG
cargo:rerun-if-env-changed=DBUS_1_STATIC
cargo:rerun-if-env-changed=DBUS_1_DYNAMIC
cargo:rerun-if-env-changed=PKG_CONFIG_ALL_STATIC
cargo:rerun-if-env-changed=PKG_CONFIG_ALL_DYNAMIC
cargo:rerun-if-env-changed=PKG_CONFIG_PATH
cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR
cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR
cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR
cargo:rerun-if-env-changed=SYSROOT
cargo:rerun-if-env-changed=DBUS_1_STATIC
cargo:rerun-if-env-changed=DBUS_1_DYNAMIC
cargo:rerun-if-env-changed=PKG_CONFIG_ALL_STATIC
cargo:rerun-if-env-changed=PKG_CONFIG_ALL_DYNAMIC
cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu
cargo:rustc-link-lib=dbus-1
cargo:rerun-if-env-changed=PKG_CONFIG
cargo:rerun-if-env-changed=DBUS_1_STATIC
cargo:rerun-if-env-changed=DBUS_1_DYNAMIC
cargo:rerun-if-env-changed=PKG_CONFIG_ALL_STATIC
cargo:rerun-if-env-changed=PKG_CONFIG_ALL_DYNAMIC
cargo:rerun-if-env-changed=PKG_CONFIG_PATH
cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR
cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR

其中重要的一个编译选项是:cargo:rustc-link-lib=dbus-1,它指定了libdbus-sys依赖dbus的C/C++库文件。

解决Rust/dbus的编译的错误

在libdbus-sys的官方文档中,给出一个交叉编译的方法;但笔者另辟蹊径,找到了另一种方法。首先,将libdbus-sys剥离出来(版本号为libdbus-sys-0.2.2),使用Git工具进行版本控制,修改该工程下的build.rs编译脚本后在本地提交:

diff --git a/build.rs b/build.rs
index 5e2d1cd..303fa03 100644
--- a/build.rs
+++ b/build.rs
@@ -1,18 +1,3 @@
-extern crate pkg_config;
-
 fn main() {
-    // See https://github.com/joshtriplett/metadeps/issues/9 for why we don't use
-    // metadeps here, but instead keep this manually in sync with Cargo.toml.
-    if let Err(e) = pkg_config::Config::new().atleast_version("1.6").probe("dbus-1") {
-        eprintln!("pkg_config failed: {}", e);
-        eprintln!(
-            "One possible solution is to check whether packages\n\
-            'libdbus-1-dev' and 'pkg-config' are installed:\n\
-            On Ubuntu:\n\
-            sudo apt install libdbus-1-dev pkg-config\n\
-            On Fedora:\n\
-            sudo dnf install dbus-devel pkgconf-pkg-config\n"
-        );
-        panic!();
-    }
+    println!("cargo:rustc-link-lib=dbus-1");
 }

build.rs脚本中,仅输出了libdbus-sys编译可执行需要链接的库dbus-1,如果必要,也可以再加上该库所在的路径,例如:cargo:rustc-link-search=/path/to/libdbus-1.so。随后,笔者对dbus-0.9.6作以下修改:

diff --git a/Cargo.toml b/Cargo.toml
index 5a32ddb..6670586 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -48,6 +48,9 @@ default-features = false
 [dependencies.libc]
 version = "0.2.66"
 
+[patch.crates-io]
+libdbus-sys = { git = "file:///home/yejq/libdbus-sys-0.2.2/.git" }
+
 [dependencies.libdbus-sys]
 version = "0.2.2"

这里笔者使用patch.crates-io替换了libdbus-sys的默认源仓库;因笔者个人环境中没有可用的Git代码托管服务,这里使用了文件系统的路径作为代码的URLpatch.crates-io相关的详细说明请参考官方文档。至此,Rust/dbus库就能够编译成功了:

~/dbus-0.9.6/target/armv7-unknown-linux-gnueabihf/release$ ls -l *.d examples/client examples/match_signal
-rwxrwxr-x 2 yejq yejq 3656892 Nov 26 17:00 examples/client
-rwxrwxr-x 2 yejq yejq 3738332 Nov 26 17:00 examples/match_signal
-rw-rw-r-- 1 yejq yejq    1782 Nov 26 16:59 libdbus.d
~/dbus-0.9.6/target/armv7-unknown-linux-gnueabihf/release$ file examples/client examples/match_signal
examples/client:       ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 5.4.0, with debug_info, not stripped
examples/match_signal: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 5.4.0, with debug_info, not stripped

必须强调的是,dbus-0.9.6自身是一个Rust的模块(crate),如要基于此开发其他应用,也需要将dbus-0.9.6(在本地)提交,然后在其他Rust工程的Cargo.toml中加入以下代码:

[patch.crates-io]
libdbus-sys = { git = "file:///home/yejq/libdbus-sys-0.2.2/.git" }
dbus = { git = "file:///home/yejq/dbus-0.9.6/.git" }

在嵌入式ARM设备上演示DBus应用

笔者在运行openwrt的嵌入式ARM设备上运行dbus-0.9.6/examples/monitor.rs编译生成的monitor应用,会出现以下异常:

root@OpenWrt:/tmp# ./monitor 
thread 'main' panicked at 'D-Bus connection failed: D-Bus error: Using X11 for dbus-daemon autolaunch was disabled at compile time, set your DBUS_SESSION_BUS_ADDRESS instead (org.freedesktop.DBus.Error.NotSupported)', examples/monitor.rs:11:42
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

原因是未设定dbus相关的环境变量导致的,解决方法是将dbus-launch命令行输出的结果导入shell的环境变量,即可正常运行:

root@OpenWrt:/tmp# dbus-launch
DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-Zh9Y2UorMi,guid=99371ddb5dee201edfa038fa6381d7f5
DBUS_SESSION_BUS_PID=4250
root@OpenWrt:/tmp# export $(dbus-launch)
root@OpenWrt:/tmp# ./monitor 
Got message: Message { Type: Signal, Path: "/org/freedesktop/DBus", Interface: "org.freedesktop.DBus", Member: "NameAcquired", Sender: "org.freedesktop.DBus", Destination: ":1.0", Serial: 2, Args: [":1.0"] }
Got message: Message { Type: Signal, Path: "/org/freedesktop/DBus", Interface: "org.freedesktop.DBus", Member: "NameLost", Sender: "org.freedesktop.DBus", Destination: ":1.0", Serial: 4, Args: [":1.0"] }

因该设备未运行其他使用dbus进程间通信的服务(除dbus-daemon守护进程外),monitor监听到的消息只有两条。

你可能感兴趣的:(杂谈,rust,开发语言)