教程:绑定库生成代码

绑定库可以自动生成代码,帮助驱动程序作者从驱动程序代码引用绑定库中的项。目前,构建将生成 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_namefuchsia.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_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 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,并为其定义了两个值 barbaz,则这两个值有两个标识符,即 FOO_BARFOO_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 开发环境中自动生成绑定库制品,请参阅 为驱动程序创建新的绑定库