前情提要
最近在折腾远程唤醒电脑,希望可以使用小爱同学远程打开关闭电脑,那么就需要一个载体来运行程序,这个载体当然是选择路由器啦,低功耗,在局域网内,7x24小时运行。刚好我的 redmi ac2100 路由器安装了 openwrt 路由器系统。
程序的功能很简单,当接到打开电脑的指令时,运行命令执行 WOL 唤醒。这里我直接采用巴法云对接到小爱同学,巴法云也可以对接到天猫精灵之类的平台。
当我快速学习了一下 C 语言以及 cmake 的使用后,写了一个程序,它通过 MQTT 订阅巴法云的开机消息。理所当然的,我依赖了 GitHub - eclipse/paho.mqtt.embedded-c
开始报错
然后在 Windows 中成功编译及执行,但是在 WSL Ubuntu 中使用 openwrt-sdk 进行编译打包时,却报错了
Package bfWakePc is missing dependencies for the following libraries:
libpaho-embed-mqtt3c.so
libpaho-embed-mqtt3cc.so
很奇怪,我的 CMakeLists.txt 文件明明用了 link_libraries(paho-embed-mqtt3c) 指令进行静态链接,但是 openwrt 打包时却并没有执行静态链接。我在 unbuntu 中直接进行编译的时候是会静态链接的呀。
这里贴一下我的 CMakeLists.txt 文件
cmake_minimum_required(VERSION 3.0.0)
project(bfWakePc VERSION 0.1.0)
include(CTest)
enable_testing()
# 添加头文件扫描
include_directories(./lib/paho.mqtt.embedded-c/MQTTPacket/src)
include_directories(./lib/paho.mqtt.embedded-c/MQTTClient-C/src)
include_directories(./lib/paho.mqtt.embedded-c/MQTTClient-C/src/linux)
# 添加子目录,目录中有 CMakeLists.txt,这将会把依赖添加并安装上
add_subdirectory(./lib/paho.mqtt.embedded-c/MQTTPacket/src)
add_subdirectory(./lib/paho.mqtt.embedded-c/MQTTClient-C/src)
# 链接静态库
link_libraries(paho-embed-mqtt3c)
link_libraries(paho-embed-mqtt3cc)
# 添加可执行文件
add_executable(bfWakePc main.c)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
如何解决这个报错?
原因也许是我项目中内嵌了一个 mqtt 的依赖,编译后的 libpaho-embed-mqtt3c.so 和 libpaho-embed-mqtt3cc.so 没有正确的连接。一开始搜索错误信息,大家给出的方案都是添加 DEPENDS 字段。于是我尝试了直接在 DEPENDS 字段添加依赖,但是还是报这个错误,本次尝试如下:
define Package/bfWakePc
SECTION:=utils
CATEGORY:=Utilities
SUBMENU:=bfWakePc
TITLE:=bfWakePc
DEPENDS:=+libpaho-embed-mqtt3c +libpaho-embed-mqtt3cc
endef
最后再次根据错误信息进行谷歌必应百度一通搜索,找到一篇文章终于是解决了。文章链接在此:
OpenWRT开发之——对C++的支持(解决库依赖问题)-阿里云开发者社区 (aliyun.com)
使用了这篇文章的解决方案2,最终解决方案是移除 DEPENDS 中的 mqtt 库。然后在 install 中新增如下代码
define Package/bfWakePc/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/bfWakePc $(1)/usr/bin/
# 新增代码开始
$(INSTALL_DIR) $(1)/usr/lib
$(INSTALL_DATA) $(PKG_BUILD_DIR)/ipkg-install/usr/lib/libpaho-embed-mqtt3c.so $(1)/usr/lib/
$(INSTALL_DATA) $(PKG_BUILD_DIR)/ipkg-install/usr/lib/libpaho-embed-mqtt3cc.so $(1)/usr/lib/
# 新增代码结束
endef
提示:复制代码时应注意 tab 缩进符,复制后可能是空格缩进,需要手动替换为 tab 缩进,否则会报错
新增的代码可以理解为在包中创建了一个 /usr/lib 目录,然后把编译得到的两个 so 文件复制到 /usr/lib 目录中,这样再打包就不会再报错找不到依赖了。这个操作会在ipk安装完成后把 so 文件复制到 openwrt 操作系统的 /usr/lib 目录中
我是如何找到 so 文件地址的?其实是打包的时候,有一条信息打印了 so 文件就在 $(PKG_BUILD_DIR)/ipkg-install/usr/lib/ cd 过去看了下,还真在哪里。
也许你会对 $(INSTALL_DIR)、$(INSTALL_BIN)、$(1)、$(PKG_BUILD_DIR) 感到迷惑。
这里首先解释 $(PKG_BUILD_DIR),然后我会引用另外一篇文章的说明来解释另外 3 个指令。
$(PKG_BUILD_DIR) 指的就是 openwrt 的打包目录,在 make 时,openwrt 会把我们的源码复制到 这个目录。
至于另外三个指令,引用如下说明:
再比如,我们要指定包的安装方法:
define Package/helloworld/install
$(INSTALL_DIR) $(1)/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/helloworld $(1)/bin/
endef
上面的这个宏就是指定了包的安装过程。其中 INSTALL_DIR 定义在 rules.mk 文件里。
INSTALL_DIR = install -d -m0755
INSTALL_BIN = install -m0755
$(1)为第一个参数是./configure时的--prefix参数,通常为""
展开之后就是:
define Package/helloworld/install
install -d -m0755 /bin
install -m0755 $(PKG_BUILD_DIR)/helloworld /bin/
endef
它的意思就一目了然了。
这段说明引用自OpenWRT开发之——研究包的Makefile-阿里云开发者社区 (aliyun.com)
需要注意的是,如果依赖的是 openwrt 已有的包,例如 libopenssl 之类的,则此方案不适用,此方案适用于自己项目中编译的动态链接库。
项目目前开源在 jethroHuang/bfWakePc (github.com) 。感兴趣的小伙伴可以看一眼