教程:绑定库生成代码

绑定库可以自动生成代码,以帮助驱动程序作者从驱动程序代码中引用绑定库中的内容。目前,build 将生成适用于 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" ]
}

大多数情况下,build 目标名称和库名称是相同的(无需提供 name 属性,只需将库名称作为 build 目标名称即可)。不过,我们在这里分别对它们进行了定义,以便区分它们以后的使用场景。

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++

build 将根据绑定库的 target_name 自动为我们提供一个新目标,即 :{target_name}_cpp

在之前的示例中,这是 my_bindlib_target。因此,目标是 :my_bindlib_target_cpp

Rust

build 将根据绑定库的 target_name 自动为我们提供一个新目标,即 :{target_name}_rust

在之前的示例中,这是 my_bindlib_target。因此,目标是 :my_bindlib_target_rust

使用生成的库

C++

这是一个 source_set 目标,其中包含的头文件具有从绑定库生成的常量。驱动程序作者在创建节点属性时可以使用此方法。他们只需要基于该依赖项从其可执行文件或其他基于 C++ 的目标创建依赖项。

包含路径和命名空间将基于绑定库的 library_name(并替换了库名称中的 .)、#include <bind/{library_name with slashes}/cpp/bind.h>namespace bind_{library_name with underscores}

在之前的示例中,library_namefuchsia.example.library。因此,我们将有 #include <bind/fuchsia/example/library/cpp/bind.h>namespace bind_fuchsia_example_library

Rust

这是一个 rustc_library 目标,它是一个 Rust crate,具有根模块,其中包含从绑定库生成的常量。驱动程序作者在创建节点属性时可以使用此方法。他们只需基于基于 Rust 的目标创建一个依赖项。

要在 use 语句中引用的 crate 名称将基于绑定库的 library_name(并替换了库名称中的 .),即 use bind_{library_name with underscores};

在之前的示例中,library_namefuchsia.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 std::string NAME = "fuchsia.example.library.Name";

static const std::string DEVICECATEGORY = "fuchsia.example.library.DeviceCategory";
static const std::string DEVICECATEGORY_KB = "keyboard";
static const std::string DEVICECATEGORY_MOUSE = "mouse";

static const std::string I2C_ADDRESS = "fuchsia.example.library.I2C_ADDRESS";

static const std::string I2C_PROTOCOL = "fuchsia.example.library.I2C_PROTOCOL";
static const std::string I2C_PROTOCOL_DEVICE = "fuchsia.example.library.I2C_PROTOCOL.Device";
static const std::string I2C_PROTOCOL_IMPL = "fuchsia.example.library.I2C_PROTOCOL.Impl";

static const std::string 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,且为其定义了两个值 barbaz,则这些值将有两个标识符:FOO_BARFOO_BAZ

C++

密钥类型 C++ 常量类型 C++ 常量值
uint uint32_t 条目中的整数值
string std::string 来自条目的字符串值
bool bool 来自条目的布尔值
枚举 std::string 枚举的全名(见下文)

Rust

密钥类型 Rust 常量类型 Rust 常量值
uint u32 条目中的整数值
string &str 来自条目的字符串值
bool bool 来自条目的布尔值
枚举 &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,因此生成的代码也已自动从中导入生成的 crate。与 C++ 头文件的不同之处在于,在 Rust 中,用户在引用依赖项 crate 时,必须嵌套 crate 名称。请参见下面的示例,了解其外观如何。

用法示例

BUILD.gn

C++

source_set("parent_cpp_code") {
  sources = [ "parent-driver.cc" ]
  deps = [ ":my_bindlib_target_cpp" ]
}

Rust

rustc_binary("parent_rust_code") {
  edition = "2021"
  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 开发环境中自动生成绑定库工件,请参阅为驱动程序创建新的绑定库