繫結程式庫程式碼的教學課程

繫結程式庫可自動生成程式碼,協助驅動程式庫作者從驅動程式庫程式碼參照繫結程式庫中的項目。目前建構作業會產生 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

荒漠油廠

建構作業會根據繫結程式庫的 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

荒漠油廠

這是 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 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_

荒漠油廠


// 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;

產生的常數

繫結程式庫中的每個鍵都有一個名稱相同但全為大寫的字串常數 ID。字串的值會是金鑰的完整名稱。 完整名稱包含繫結程式庫的 library_name 和金鑰名稱,且不調整大小寫,並以 . 分隔。

C++

如果我們在名為 fuchsia.example.library 的程式庫中有名為 keyName 的鍵,則產生的常數會是 std::string KEYNAME = "fuchsia.example.library.keyName";

荒漠油廠

如果我們在名為 fuchsia.example.library 的程式庫中有名為 keyName 的鍵,則產生的常數會是 pub const KEYNAME: &str = "fuchsia.example.library.keyName";

針對繫結程式庫中為鍵定義的每個值,都有一個常數 (根據鍵的類型,請參閱下表)。常數的 ID 是以全大寫形式表示的鍵和值名稱,並以底線分隔。

舉例來說,如果有一個鍵 foo,並為其定義兩個值 barbaz,則這兩個值會分別有 FOO_BARFOO_BAZ 兩個 ID。

C++

金鑰類型 C++ 常數型別 C++ 常數值
uint uint32_t 輸入的整數值
string std::string 項目中的字串值
bool bool 項目的布林值
enum std::string 列舉的全名 (請參閱下文)

荒漠油廠

金鑰類型 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";

荒漠油廠

如果程式庫 (稱為 fuchsia.example.library) 的列舉型鍵 enumeratedKey 下有名為 someValue 的值,則產生的常數會是 pub const ENUMERATEDKEY_SOMEVALUE: &str = "fuchsia.example.library.enumeratedKey.someValue";

依附元件

C++

由於我們的繫結程式庫範例依附於 //src/devices/bind/fuchsia.pci (另一個繫結程式庫),因此產生的程式碼也會自動納入從該程式庫產生的標頭。因此,只要加入這個標頭,程式碼也能參照基本繫結程式庫中的值。

荒漠油廠

由於繫結程式庫依附於另一個繫結程式庫 //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" ]
}

荒漠油廠

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;

荒漠油廠

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 開發環境中自動產生繫結程式庫構件,請參閱「為驅動程式建立新的繫結程式庫」。