一、bindgen 命令简单记要
首先要安装 bindgen-cli
,其次它需要依赖 clang
。安装可以在网上找教程。
我这里主要记要,其在本次命令中使用到的一些功能,方便下次使用。
首先在命令行中,使用 --help
来展示所有的功能。
> bindgen --help
Usage: bindgen <FLAGS> <OPTIONS> <HEADER> -- <CLANG_ARGS>...
Arguments:
[HEADER] C or C++ header file
[CLANG_ARGS]... Arguments to be passed straight through to clang
从命令的帮助介绍中,可以知道,bindgen 分为两部分的配置。其一为提供信息给 bindgen,另一部分为提供给 clang 信息。
而在本次任务中,最为难的就是 clang 。这里先按下不表。
提供给 bindgen 的命令形式为
> bindgen wrapper.h -o bindings.rs --use-core --verbose
-
wrapper.h
为我们提供给 bindgen 的入口文件 -
-o bindings.rs
把生成的 FFI 内容写进 bindings.rs 文件里 -
--use-core
表明使用core::ffi
里的类型绑定,而非std::ffi
里的 -
--verbose
希望能提供更多的处理信息,以方便查错
wrapper.h 文件在后面再介绍。
二、使用 RT-thread Studio 工具来生成 rtthread 项目
关于本次任务,我是使用 野火指南者 的开发板来进行测试。其使用的 芯片是 stm32f103。而 rtthread 已经为其适配了一个 BSP ,很方便我们使用。
生成的项目信息如下:
三、书写入口文件 wrapper.h
入口文件的意思是,bindgen 工具通过分析 wrapper.h ,便能知道本项目到底提供了什么 API 给用户。 所以关键在于如何 获知本项目内哪个文件是可以连接到所需要的其他文件。
幸运的是,rtthread 的项目模板提供了一个 main.c,里面能找到相关线索。
// application/main.c
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
/* defined the LED0 pin: PF7 */
#define LED0_PIN GET_PIN(B, 0)
int main(void)
{
/* set LED0 pin mode to output */
rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
while (1)
{
rt_pin_write(LED0_PIN, PIN_HIGH);
rt_thread_mdelay(500);
rt_pin_write(LED0_PIN, PIN_LOW);
rt_thread_mdelay(500);
}
}
所以我们只需要葫芦画瓢,把相应导入即可。
// ./wrapper.h
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
四、处理 clang
需要引入了入口的头文件,但是其依赖的头文件应如何找?此时就需要应用到 clang 的一些 option
了。
另一个问题又来了。那么怎样知道其依赖的其他头文件在哪?这里就需要利用 RT-thread Studio 这个工具了。
把绿色的部分复制展开整理后得到
-I"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings"
-I"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings\applications"
-I"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings\board\CubeMX_Config\Inc"
-I"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings\board\ports"
-I"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings\board"
-I"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings\libraries\HAL_Drivers\config"
-I"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings\libraries\HAL_Drivers"
-I"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings\libraries\STM32F1xx_HAL\CMSIS\Device\ST\STM32F1xx\Include"
-I"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings\libraries\STM32F1xx_HAL\CMSIS\Include"
-I"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings\libraries\STM32F1xx_HAL\STM32F1xx_HAL_Driver\Inc"
-I"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings\rt-thread\components\drivers\include"
-I"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings\rt-thread\components\finsh"
-I"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings\rt-thread\components\libc\compilers\common"
-I"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings\rt-thread\components\libc\compilers\newlib"
-I"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings\rt-thread\include"
-I"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings\rt-thread\libcpu\arm\common"
-I"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings\rt-thread\libcpu\arm\cortex-m3"
-include"H:\001_Projects\002-Rust\003-rtthread-bindings\RTThread-source\bsp-fire-stm32f103\test-bindings\rtconfig_preinc.h"
-std=gnu11
-mcpu=cortex-m3
-mthumb
-ffunction-sections
-fdata-sections
-Dgcc
-O0
-gdwarf-2
-g
五、把命令写进脚本中
为了方便日后使用,以及在绑定过程中,处理错误。我把相应的内容写进了脚本文件中。
# 文件: ./bindings.py
import os
# 命令
# entry: 入口文件
# output: 生成的绑定文件
# option: 提供给 bindgen 的选项
# clang_option: 提供给 clang 的选项
CMD = "bindgen {entry} -o {output} {option} -- {clang_option}"
entry = "wrapper.h"
output = "bindings.rs"
option = [
"--verbose",
"--use-core",
]
# ---- clang option ----
clang_option = [
# 相应的头文件查找路径
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/applications"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/board/CubeMX_Config/Inc"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/board/ports"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/board"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/libraries/HAL_Drivers/config"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/libraries/HAL_Drivers"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/libraries/STM32F1xx_HAL/CMSIS/Device/ST/STM32F1xx/Include"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/libraries/STM32F1xx_HAL/CMSIS/Include"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/libraries/STM32F1xx_HAL/STM32F1xx_HAL_Driver/Inc"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/rt-thread/components/drivers/include"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/rt-thread/components/finsh"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/rt-thread/components/libc/compilers/common"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/rt-thread/components/libc/compilers/newlib"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/rt-thread/include"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/rt-thread/libcpu/arm/common"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/rt-thread/libcpu/arm/cortex-m3"',
# bsp 里定义了相应的宏选项,用于配置使用的是哪款 CPU
'-include"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/rtconfig_preinc.h"',
# 说明使用标准库是 GNU 11
'-std=gnu11',
# 下面的,在此应用不到
# '-mcpu=cortex-m3',
# '-mthumb',
# '-ffunction-sections',
# '-fdata-sections',
# '-Dgcc',
# '-O0',
# '-gdwarf-2',
# '-g',
]
# 传递命令
cmd = CMD.format(
entry=entry,
output=output,
option=" ".join(option),
clang_option=" ".join(clang_option),
)
reply = os.popen(cmd)
print(reply.read())
六、处理错误
当运行脚本文件时,cmd 会报错误:
clang version 18.1.6
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir:
ignoring nonexistent directory "lib\clang\18\include"
ignoring nonexistent directory "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\atlmfc\include"
#include "..." search starts here:
#include <...> search starts here:
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\include
C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\ucrt
C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\shared
C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\um
C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\winrt
C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\cppwinrt
End of search list.
......
此处错误的原因为 没有给 clang 提示当前芯片的目标架构。
# 文件: ./bindings.py
# 在 clang_option 里回入 --target 选项
clang_option = [
# ...
"--target=armv7a-none-eabi",
]
接下来,再运行后出现的错误是,
H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/rt-thread/include\libc/libc_stat.h:17:10: fatal error: 'sys/stat.h' file not found
Bindgen unexpectedly panicked
......
如果你在 RT-thread Studio 从 libc_stat.h 里打开 sys/stat.h 文件。你会发现,此文件是 arm-none-eabi-gcc 里的,即 arm-gcc 的标准库文件。因为我们现在使用的 bindgen ,是使用 clang 来分析,其链接的是自己的标准库,非 arm-gcc 的。故,我们要显式告诉其标准库的查找路径。
因为在 arm-gcc 的安装目录下,有很多文件夹,我们不知该导入哪个。此时,又要利用好 RT-thread Studio 这个工具了。
只要把这三个路径放进 clang_option
中即可。
然后又运行脚本,发现错误更新为
H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/libraries/STM32F1xx_HAL/CMSIS/Include\core_cm3.h:90:6: error: "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)"
Bindgen unexpectedly panicked
......
此处的提示可能意思为 clang 认为你应该使用 FPU 指令,但你的设备又没有 FPU。所以编译器感到困惑。因此你只里在 clang_option
里加入 "-msoft-float"
此时,便能通过编译,并生成相应绑定文件 bindings.rs
。
六、 完整的脚本
# 文件: ./bindings.py
import os
# 命令
# entry: 入口文件
# output: 生成的绑定文件
# option: 提供给 bindgen 的选项
# clang_option: 提供给 clang 的选项
CMD = "bindgen {entry} -o {output} {option} -- {clang_option}"
entry = "wrapper.h"
output = "bindings.rs"
option = [
"--verbose",
"--use-core",
]
# ---- clang option ----
clang_option = [
# 目标架构
"--target=armv7a-none-eabi",
# FPU(我也不知是什么)
"-msoft-float",
# 当前目录
# '-I./',
# 相应的头文件查找路径
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/applications"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/board/CubeMX_Config/Inc"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/board/ports"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/board"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/libraries/HAL_Drivers/config"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/libraries/HAL_Drivers"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/libraries/STM32F1xx_HAL/CMSIS/Device/ST/STM32F1xx/Include"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/libraries/STM32F1xx_HAL/CMSIS/Include"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/libraries/STM32F1xx_HAL/STM32F1xx_HAL_Driver/Inc"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/rt-thread/components/drivers/include"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/rt-thread/components/finsh"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/rt-thread/components/libc/compilers/common"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/rt-thread/components/libc/compilers/newlib"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/rt-thread/include"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/rt-thread/libcpu/arm/common"',
'-I"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/rt-thread/libcpu/arm/cortex-m3"',
# bsp 里定义了相应的宏选项,用于配置使用的是哪款 CPU
'-include"H:/001_Projects/002-Rust/003-rtthread-bindings/RTThread-source/bsp-fire-stm32f103/test-bindings/rtconfig_preinc.h"',
# 说明使用标准库是 GNU 11
'-std=gnu11',
# 下面的,在此应用不到
# '-mcpu=cortex-m3',
# '-mthumb',
# '-ffunction-sections',
# '-fdata-sections',
# '-Dgcc',
# '-O0',
# '-gdwarf-2',
# '-g',
# arm-gcc 的标准库搜索路径
"-ID:/ProgramFiles/RT-ThreadStudio/repo/Extract/ToolChain_Support_Packages/ARM/GNU_Tools_for_ARM_Embedded_Processors/5.4.1/arm-none-eabi/include",
"-ID:/ProgramFiles/RT-ThreadStudio/repo/Extract/ToolChain_Support_Packages/ARM/GNU_Tools_for_ARM_Embedded_Processors/5.4.1/lib/gcc/arm-none-eabi/5.4.1/include",
"-ID:/ProgramFiles/RT-ThreadStudio/repo/Extract/ToolChain_Support_Packages/ARM/GNU_Tools_for_ARM_Embedded_Processors/5.4.1/lib/gcc/arm-none-eabi/5.4.1/include-fixed",
]
# 传递命令
cmd = CMD.format(
entry=entry,
output=output,
option=" ".join(option),
clang_option=" ".join(clang_option),
)
# print("***** ", cmd)
reply = os.popen(cmd)
print(reply.read())