笔者在嵌入式软件开发过程中,之所以注重软件的构建方式,是因为快速、合理的编译构建方式能够提升开发效率,缩短软件发布周期,加快软件迭代,并且促使软件架构朝着更合理的方式发展、演进。举个例子,分布式代码版本管理工具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-sys
中build.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++库文件。
在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
代码托管服务,这里使用了文件系统的路径作为代码的URL
。patch.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" }
笔者在运行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
监听到的消息只有两条。