ระบบบิลด์รองรับการสร้างการเชื่อมโยง bindgen ผ่านrust_bindgen ประเภทโมดูล Bindgen มีการเชื่อมโยง FFI ของ Rust กับไลบรารี C (มีการรองรับ C++ แบบจำกัดบางส่วน ซึ่งต้องตั้งค่าพร็อพเพอร์ตี้ cppstd)

การใช้งาน rust_bindgen ขั้นพื้นฐาน

ต่อไปนี้เป็นตัวอย่างวิธีกำหนดโมดูลที่ใช้ bindgen และวิธีใช้โมดูลนั้นเป็น Crate หากต้องการใช้การเชื่อมโยง bindgen ผ่านมาโคร include!() เช่น สำหรับโค้ดภายนอก โปรดดูหน้าเครื่องมือสร้างแหล่งข้อมูล

ไลบรารี Example C ที่จะเรียกใช้จาก Rust

ตัวอย่างไลบรารี C ที่กำหนดโครงสร้างและฟังก์ชันสำหรับใช้ใน Rust มีดังนี้

external/rust/libbuzz/libbuzz.h

typedef struct foo {
    int x;
} foo;

void fizz(int i, foo* cs);

external/rust/libbuzz/libbuzz.c

#include <stdio.h>
#include "libbuzz.h"

void fizz(int i, foo* my_foo){
    printf("hello from c! i = %i, my_foo->x = %i\n", i, my_foo->x);
}

กำหนดโมดูล rust_bindgen

กำหนดส่วนหัวของ Wrapper external/rust/libbuzz/libbuzz_wrapper.h ซึ่งรวมถึงส่วนหัวที่เกี่ยวข้องทั้งหมด ดังนี้

// Include headers that are required for generating bindings in a wrapper header.
#include "libbuzz.h"

กำหนดไฟล์ Android.bp เป็น external/rust/libbuzz/Android.bp ดังนี้

cc_library {
    name: "libbuzz",
    srcs: ["libbuzz.c"],
}

rust_bindgen {
     name: "libbuzz_bindgen",

     // Crate name that's used to generate the rust_library variants.
     crate_name: "buzz_bindgen",

     // Path to the wrapper source file.
     wrapper_src: "libbuzz_wrapper.h",

     // 'source_stem' controls the output filename.
     // This is the filename that's used in an include! macro.
     //
     // In this case, we just use "bindings", which produces
     // "bindings.rs".
     source_stem: "bindings",

     // Bindgen-specific flags and options to customize the bindings.
     // See the bindgen manual for more information.
     bindgen_flags: ["--verbose"],

     // Clang flags to be used when generating the bindings.
     cflags: ["-DSOME_FLAG"],

     // Shared, static, and header libraries which export the necessary
     // include directories must be specified.
     //
     // These libraries will also be included in the crate if static,
     // or propagated to dependents if shared.
     // static_libs: ["libbuzz"]
     // header_libs: ["libbuzz"]
     shared_libs: ["libbuzz"],
}

ดูข้อมูลเพิ่มเติมเกี่ยวกับการใช้แฟล็ก bindgen ได้ที่ส่วนคู่มือ bindgen ในหัวข้อการปรับแต่งการเชื่อมโยงที่สร้างขึ้น

หากใช้ส่วนนี้เพื่อกำหนดrust_bindgenโมดูลเป็นข้อกำหนดเบื้องต้นในการ ใช้มาโคร include!() ให้กลับไปที่ข้อกำหนดเบื้องต้น ในหน้าเครื่องมือสร้างแหล่งที่มา หากไม่ ให้ไปที่ส่วนถัดไป

ใช้การเชื่อมโยงเป็นแพ็กเกจ

สร้าง external/rust/hello_bindgen/Android.bp ที่มีเนื้อหาต่อไปนี้

rust_binary {
   name: "hello_bindgen",
   srcs: ["main.rs"],

   // Add the rust_bindgen module as if it were a rust_library dependency.
   rustlibs: ["libbuzz_bindgen"],
}

สร้าง external/rust/hello_bindgen/src/main.rs ที่มีเนื้อหาต่อไปนี้

//! Example crate for testing bindgen bindings

fn main() {
    let mut x = buzz_bindgen::foo { x: 2 };
    unsafe { buzz_bindgen::fizz(1, &mut x as *mut buzz_bindgen::foo) }
}

สุดท้าย ให้เรียกใช้ m hello_bindgen เพื่อสร้างไบนารี

ทดสอบการเชื่อมโยง Bindgen

โดยปกติแล้ว Binding ของ Bindgen จะมีการทดสอบเลย์เอาต์ที่สร้างขึ้นหลายรายการเพื่อป้องกัน เลย์เอาต์หน่วยความจำไม่ตรงกัน AOSP ขอแนะนำให้คุณกำหนดโมดูลทดสอบสำหรับการทดสอบเหล่านี้ และให้การทดสอบทำงานเป็นส่วนหนึ่งของชุดทดสอบปกติของโปรเจ็กต์

คุณสร้างไบนารีทดสอบสำหรับสิ่งเหล่านี้ได้อย่างง่ายดายโดยการกำหนดrust_testโมดูล ใน external/rust/hello_bindgen/Android.bp ดังนี้

rust_test {
    name: "bindings_test",
    srcs: [
        ":libbuzz_bindgen",
    ],
    crate_name: "buzz_bindings_test",
    test_suites: ["general-tests"],
    auto_gen_config: true,

    // Be sure to disable lints as the generated source
    // is not guaranteed to be lint-free.
    clippy_lints: "none",
    lints: "none",
}

ระดับการเข้าถึงและการลิงก์

โดยปกติแล้ว Binding ที่สร้างขึ้นจะมีขนาดเล็กมาก เนื่องจากประกอบด้วยคำจำกัดความประเภท ลายเซ็นฟังก์ชัน และค่าคงที่ที่เกี่ยวข้อง ดังนั้นโดยทั่วไปแล้ว การลิงก์ไลบรารีเหล่านี้แบบไดนามิกจึง สิ้นเปลือง เราได้ปิดใช้การเชื่อมโยงแบบไดนามิก สำหรับโมดูลเหล่านี้ เพื่อให้การใช้โมดูลผ่าน rustlibs จะเลือก ตัวแปรแบบคงที่โดยอัตโนมัติ

โดยค่าเริ่มต้น โมดูล rust_bindgen จะมีพร็อพเพอร์ตี้ visibility เป็น [":__subpackages__"] ซึ่งจะอนุญาตให้เฉพาะโมดูลในไฟล์ Android.bp เดียวกัน หรือโมดูลที่อยู่ใต้ไฟล์นั้นในลำดับชั้นของไดเรกทอรีเท่านั้นที่เห็น ซึ่งมีวัตถุประสงค์ 2 ประการดังนี้

  • และไม่แนะนำให้ใช้การเชื่อมโยง C แบบดิบในที่อื่นๆ ในโครงสร้าง
  • โดยจะหลีกเลี่ยงปัญหาการลิงก์แบบไดมอนด์ด้วยการผสมผสานการลิงก์แบบคงที่และแบบไดนามิก

โดยปกติแล้ว คุณควรจัดหาไลบรารี Wrapper ที่ปลอดภัยสำหรับโมดูลที่สร้างขึ้น ซึ่งคุณได้เพิ่มในโครงสร้างไดเรกทอรีเดียวกันกับ Binding ที่มีไว้สำหรับ นักพัฒนาแอปคนอื่นๆ ให้ใช้ หากวิธีนี้ใช้ไม่ได้กับ Use Case ของคุณ คุณสามารถเพิ่ม แพ็กเกจเพิ่มเติมลงในระดับการเข้าถึงได้ เมื่อเพิ่มขอบเขตการมองเห็นเพิ่มเติม โปรดระมัดระวังไม่ให้เพิ่มขอบเขต 2 รายการที่อาจลิงก์เข้ากับกระบวนการเดียวกันในอนาคต เนื่องจากอาจลิงก์ไม่สำเร็จ

พร็อพเพอร์ตี้ที่สำคัญของ rust_bindgen

พร็อพเพอร์ตี้ที่กำหนดไว้ด้านล่างนี้เป็นพร็อพเพอร์ตี้เพิ่มเติมจากพร็อพเพอร์ตี้ทั่วไปที่สำคัญ ซึ่งใช้กับโมดูลทั้งหมด ซึ่งมีความสำคัญอย่างยิ่งต่อโมดูล Rust bindgen หรือแสดงลักษณะการทำงานที่ไม่เหมือนใครซึ่งเฉพาะเจาะจงกับประเภทโมดูล rust_bindgen

stem, name, crate_name

rust_bindgen จะสร้างตัวแปรของไลบรารี ดังนั้นจึงใช้ข้อกำหนดเดียวกันกับโมดูล rust_library สำหรับพร็อพเพอร์ตี้ stem, name และ crate_name ดูคุณสมบัติที่สำคัญของไลบรารี Rust เพื่อใช้อ้างอิง

wrapper_src

นี่คือเส้นทางแบบสัมพัทธ์ไปยังไฟล์ส่วนหัวของ Wrapper ซึ่งมีส่วนหัวที่จำเป็น สำหรับการเชื่อมโยงเหล่านี้ นามสกุลไฟล์จะเป็นตัวกำหนดวิธีตีความส่วนหัว และกำหนดค่าสถานะ -std ที่จะใช้โดยค่าเริ่มต้น โดยจะถือว่าเป็นส่วนหัว C เว้นแต่ส่วนขยายจะเป็น .hh หรือ .hpp หากส่วนหัว C++ ต้องมีนามสกุลอื่น ให้ตั้งค่าพร็อพเพอร์ตี้ cpp_std เพื่อลบล้างลักษณะการทำงานเริ่มต้น ที่ถือว่าไฟล์เป็นไฟล์ C

source_stem

นี่คือชื่อไฟล์ของไฟล์ต้นฉบับที่สร้างขึ้น คุณต้อง กำหนดฟิลด์นี้ แม้ว่าคุณจะใช้การเชื่อมโยงเป็น Crate เนื่องจากพร็อพเพอร์ตี้ stem จะควบคุมเฉพาะชื่อไฟล์เอาต์พุตสำหรับตัวแปรไลบรารีที่สร้างขึ้น หากโมดูลขึ้นอยู่กับตัวสร้างแหล่งที่มาหลายรายการ (เช่น bindgen และ protobuf) เป็นแหล่งที่มาแทนที่จะเป็นครีตผ่าน rustlibs คุณต้องตรวจสอบว่าตัวสร้างแหล่งที่มาทั้งหมด ที่เป็นการอ้างอิงของโมดูลนั้นมีค่า source_stem ที่ไม่ซ้ำกัน โมดูลที่ขึ้นต่อกัน จะคัดลอกแหล่งที่มาจากSourceProviderการอ้างอิงทั้งหมดที่กำหนดไว้ใน srcs ไปยังOUT_DIRไดเรกทอรีทั่วไป ดังนั้นการชนกันใน source_stem จะ ส่งผลให้ไฟล์แหล่งที่มาที่สร้างขึ้นถูกเขียนทับในไดเรกทอรี OUT_DIR

c_std

นี่คือสตริงที่แสดงเวอร์ชัน C-standard ที่จะใช้ ค่าที่ใช้ได้ แสดงอยู่ด้านล่าง

  • เวอร์ชันที่เฉพาะเจาะจง เช่น "gnu11"
  • "experimental" ซึ่งเป็นค่าที่กำหนดโดยระบบบิลด์ใน build/soong/cc/config/global.go อาจใช้ เวอร์ชันร่าง เช่น C++1z เมื่อพร้อมใช้งาน
  • ไม่ได้ตั้งค่าหรือ "" ซึ่งบ่งบอกว่าควรใช้ค่าเริ่มต้นของระบบบิลด์

หากตั้งค่านี้ ระบบจะไม่สนใจนามสกุลไฟล์และจะถือว่าส่วนหัวเป็นส่วนหัวของ C คุณตั้งค่านี้พร้อมกับ cpp_std ไม่ได้

cpp_std

cpp_std คือสตริงที่แสดงเวอร์ชันมาตรฐาน C ที่จะใช้ ค่าที่ถูกต้อง:

  • เวอร์ชันที่เฉพาะเจาะจง เช่น "gnu++11"
  • "experimental" ซึ่งเป็นค่าที่กำหนดโดยระบบบิลด์ใน build/soong/cc/config/global.go อาจใช้ เวอร์ชันร่าง เช่น C++1z เมื่อพร้อมใช้งาน
  • ไม่ได้ตั้งค่าหรือ "" ซึ่งบ่งบอกว่าควรใช้ค่าเริ่มต้นของระบบบิลด์

หากตั้งค่านี้ ระบบจะไม่สนใจนามสกุลไฟล์และจะถือว่าส่วนหัวเป็นส่วนหัว C++ คุณตั้งค่านี้พร้อมกับ c_std ไม่ได้

cflags

cflags ระบุรายการสตริงของแฟล็ก Clang ที่จำเป็นต่อการตีความส่วนหัวอย่างถูกต้อง

custom_bindgen

สำหรับ Use Case ขั้นสูง คุณสามารถใช้ Bindgen เป็นไลบรารีได้ ซึ่งจะให้ API ที่สามารถจัดการเป็นส่วนหนึ่งของไบนารี Rust ที่กำหนดเอง ฟิลด์ custom_bindgen ใช้ชื่อโมดูล ของโมดูล rust_binary_host ซึ่งใช้ bindgen API แทน ไบนารี bindgen ปกติ

ไบนารีที่กำหนดเองนี้ต้องคาดหวังอาร์กิวเมนต์ในลักษณะเดียวกับ bindgen เช่น

$ my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]

bindgen จะจัดการส่วนใหญ่ของกระบวนการนี้เอง ดูตัวอย่างการใช้งานนี้ได้ที่ external/rust/crates/libsqlite3-sys/android/build.rs

นอกจากนี้ คุณยังใช้ชุดพร็อพเพอร์ตี้ไลบรารีทั้งหมดเพื่อควบคุม การคอมไพล์ไลบรารีได้ด้วย แม้ว่าแทบจะไม่จำเป็นต้องกำหนดหรือเปลี่ยนแปลงพร็อพเพอร์ตี้เหล่านี้ก็ตาม

handle_static_inline และ static_inline_library

พร็อพเพอร์ตี้ทั้ง 2 นี้มีไว้เพื่อใช้ร่วมกันและอนุญาตให้สร้าง Wrapper สำหรับฟังก์ชันแบบคงที่ในบรรทัดซึ่งรวมอยู่ใน Binding bindgen ที่ส่งออกได้

หากต้องการใช้ ให้ตั้งค่า handle_static_inline: true และตั้งค่า static_inline_library เป็น cc_library_static ที่สอดคล้องกันซึ่งกำหนดโมดูล rust_bindgen เป็น อินพุตแหล่งที่มา

ตัวอย่างการใช้งาน

    rust_bindgen {
        name: "libbindgen",
        wrapper_src: "src/any.h",
        crate_name: "bindgen",
        stem: "libbindgen",
        source_stem: "bindings",

        // Produce bindings for static inline fucntions
        handle_static_inline: true,
        static_inline_library: "libbindgen_staticfns"
    }

    cc_library_static {
        name: "libbindgen_staticfns",

        // This is the rust_bindgen module defined above
        srcs: [":libbindgen"],

        // The include path to the header file in the generated c file is
        // relative to build top.
        include_dirs: ["."],
    }