绑定库可以自动生成代码,帮助驱动程序作者从驱动程序代码引用绑定库中的项。目前,构建将生成 C++ 和 Rust 代码。 本教程将详细介绍如何使用这些自动生成的代码目标。
本指南假定您熟悉以下概念:
绑定库示例
首先,我们来定义一个包含一些不同定义的绑定库示例。我们还会让它依赖于另一个现有的绑定库。这是可选的,但为了完整起见,我们将包含它。
BUILD.gn
bind_library("my_bindlib_target") { # target_name
source = "mybindlib.bind"
name = "fuchsia.example.library" # library_name (optional, defaults to
# target_name)
public_deps = [ "//src/devices/bind/fuchsia.pci" ]
}
大多数情况下,构建目标名称和库名称是相同的(无需提供 name 属性,只需将库名称作为构建目标名称即可)。
但在这里,我们将它们分开定义,以便稍后区分每个名称的使用位置。
mybindlib.bind
library fuchsia.example.library;
using fuchsia.pci;
string Name;
string DeviceCategory {
KB = "keyboard",
Mouse = "mouse",
};
uint I2C_ADDRESS;
enum I2C_PROTOCOL {
Device,
Impl,
};
bool Flag {
ENABLE = true,
DISABLE = false,
};
extend uint fuchsia.BIND_PCI_VID {
GIZMOTRONICS = 0x314159,
};
自动生成的库
生成的构建目标
C++
构建将根据绑定库的 target_name 自动为我们提供一个新目标,
:{target_name}_cpp
在之前的示例中,这是 my_bindlib_target。因此,目标将是 :my_bindlib_target_cpp。
Rust
构建将根据绑定库的 target_name 自动为我们提供一个新目标,
:{target_name}_rust
在之前的示例中,这是 my_bindlib_target。因此,目标将是 :my_bindlib_target_rust。
使用生成的库
C++
这是一个 source_set 目标,其中包含一个头文件,该文件包含从绑定库生成的常量。驱动程序作者在创建节点属性时可以使用此文件。他们只需从可执行文件或其他基于 C++ 的目标对此文件创建依赖项即可。
include 路径和命名空间将基于绑定库的 library_name(
库名称中的 . 会被替换),
#include <bind/{library_name with slashes}/cpp/bind.h> 和
namespace bind_{library_name with underscores}。
在之前的示例中,library_name 为 fuchsia.example.library。因此,我们将有
#include <bind/fuchsia/example/library/cpp/bind.h> 和 namespace bind_fuchsia_example_library。
Rust
这是一个 rustc_library 目标,它是一个 Rust 代码库,其中包含一个根模块,该模块包含从绑定库生成的常量。驱动程序作者在创建节点属性时可以使用此文件。他们只需从基于 Rust 的目标对此文件创建依赖项即可。
在 use 语句中引用的代码库名称将基于library_name的
绑定库(库名称中的 . 会被替换),
use bind_{library_name with underscores};。
在之前的示例中,library_name 为 fuchsia.example.library,因此我们将有 use bind_fuchsia_example_library;。
生成的头文件
C++
// WARNING: This file is machine generated by bindc.
#ifndef BIND_FUCHSIA_EXAMPLE_LIBRARY_BINDLIB_
#define BIND_FUCHSIA_EXAMPLE_LIBRARY_BINDLIB_
#include <string>
#include <bind/fuchsia/pci/cpp/bind.h>
namespace bind_fuchsia_example_library {
static const char NAME[] = "fuchsia.example.library.Name";
static const char DEVICECATEGORY[] = "fuchsia.example.library.DeviceCategory";
static const char DEVICECATEGORY_KB[] = "keyboard";
static const char DEVICECATEGORY_MOUSE[] = "mouse";
static const char I2C_ADDRESS[] = "fuchsia.example.library.I2C_ADDRESS";
static const char I2C_PROTOCOL[] = "fuchsia.example.library.I2C_PROTOCOL";
static const char I2C_PROTOCOL_DEVICE[] = "fuchsia.example.library.I2C_PROTOCOL.Device";
static const char I2C_PROTOCOL_IMPL[] = "fuchsia.example.library.I2C_PROTOCOL.Impl";
static const char FLAG[] = "fuchsia.example.library.Flag";
static constexpr bool FLAG_ENABLE = true;
static constexpr bool FLAG_DISABLE = false;
static constexpr uint32_t BIND_PCI_VID_GIZMOTRONICS = 3227993;
} // namespace bind_fuchsia_example_library
#endif // BIND_FUCHSIA_EXAMPLE_LIBRARY_BINDLIB_
Rust
// WARNING: This file is machine generated by bindc.
pub use bind_fuchsia_pci;
pub const NAME: &str = "fuchsia.example.library.Name";
pub const DEVICECATEGORY: &str = "fuchsia.example.library.DeviceCategory";
pub const DEVICECATEGORY_KB: &str = "keyboard";
pub const DEVICECATEGORY_MOUSE: &str = "mouse";
pub const I2C_ADDRESS: &str = "fuchsia.example.library.I2C_ADDRESS";
pub const I2C_PROTOCOL: &str = "fuchsia.example.library.I2C_PROTOCOL";
pub const I2C_PROTOCOL_DEVICE: &str = "fuchsia.example.library.I2C_PROTOCOL.Device";
pub const I2C_PROTOCOL_IMPL: &str = "fuchsia.example.library.I2C_PROTOCOL.Impl";
pub const FLAG: &str = "fuchsia.example.library.Flag";
pub const FLAG_ENABLE: bool = true;
pub const FLAG_DISABLE: bool = false;
pub const BIND_PCI_VID_GIZMOTRONICS: u32 = 3227993;
生成的常量
键
对于绑定库中的每个键,都有一个名称相同但全部为大写的字符串常量标识符。字符串的值将是键的全名。
全名包含绑定库的 library_name 和键名称,不进行大小写调整,并以 . 分隔。
C++
如果我们在名为 fuchsia.example.library 的库中有一个名为 keyName 的键,则生成的常量将是:std::string KEYNAME = "fuchsia.example.library.keyName";。
Rust
如果我们在名为 fuchsia.example.library 的库中有一个名为 keyName 的键,则生成的常量将是:
pub const KEYNAME: &str = "fuchsia.example.library.keyName";。
值
对于为绑定库中的键定义的每个值,都有一个基于键类型的常量(请参阅下表)。常量的标识符是键名称和值名称的全大写版本,并以一个下划线分隔。
例如,如果有一个键 foo,并为其定义了两个值 bar 和 baz,则这两个值有两个标识符,即 FOO_BAR 和 FOO_BAZ。
C++
| 键类型 | C++ 常量类型 | C++ 常量值 |
|---|---|---|
| uint | uint32_t | 条目中的整数值 |
| string | std::string | 条目中的字符串值 |
| bool | bool | 条目中的布尔值 |
| enum | std::string | 枚举的全名(见下文) |
Rust
| 键类型 | Rust 常量类型 | Rust 常量值 |
|---|---|---|
| uint | u32 | 条目中的整数值 |
| string | &str | 条目中的字符串值 |
| bool | bool | 条目中的布尔值 |
| enum | &str | 枚举的全名(见下文) |
枚举的全名包含 library_name、键名称和值名称,所有名称都以 . 分隔。
C++
如果我们在名为 fuchsia.example.library 的库中有一个枚举键 enumeratedKey 下的值 someValue,则生成的常量将是
std::string ENUMERATEDKEY_SOMEVALUE = "fuchsia.example.library.enumeratedKey.someValue";。
Rust
如果我们在名为 fuchsia.example.library 的库中有一个枚举键 enumeratedKey 下的值 someValue,则生成的常量将是
pub const ENUMERATEDKEY_SOMEVALUE: &str = "fuchsia.example.library.enumeratedKey.someValue";。
依赖项
C++
由于我们的绑定库示例依赖于 //src/devices/bind/fuchsia.pci(另一个绑定库),因此生成的代码也自动包含了由此生成的头文件。
因此,通过包含此头文件,代码还可以引用基本绑定库中的值。
Rust
由于我们的绑定库依赖于 //src/devices/bind/fuchsia.pci(另一个绑定库),因此生成的代码也自动导入了由此生成的代码库。
与 C++ 头文件的不同之处在于,在 Rust 中,用户在引用依赖项代码库时必须嵌套代码库名称。如需了解其外观,请参阅下面的示例。
用法示例
BUILD.gn
C++
source_set("parent_cpp_code") {
sources = [ "parent-driver.cc" ]
deps = [ ":my_bindlib_target_cpp" ]
}
Rust
rustc_binary("parent_rust_code") {
edition = "2024"
source_root = "parent-driver.rs"
sources = [ "parent-driver.rs" ]
deps = [ ":my_bindlib_target_rust" ]
}
复合节点规范创建器
C++
#include <bind/fuchsia/example/library/cpp/bind.h>
std::string a = bind_fuchsia_example_library::NAME;
uint32_t b = bind_fuchsia_example_library::BIND_PCI_VID_GIZMOTRONICS;
uint32_t c = bind_fuchsia_pci::BIND_PROTOCOL_DEVICE;
Rust
use bind_fuchsia_example_library::bind_fuchsia_pci;
fn main() {
let _a: &str = bind_fuchsia_example_library::NAME;
let _b: u32 = bind_fuchsia_example_library::BIND_PCI_VID_GIZMOTRONICS;
let _c: u32 = bind_fuchsia_pci::BIND_PROTOCOL_DEVICE;
}
SDK 中的自动生成库
如需了解如何在 Fuchsia SDK 开发环境中自动生成绑定库制品,请参阅 为驱动程序创建新的绑定库。