Система сборки поддерживает генерацию привязок bindgen через модуль типа rust_bindgen . Bindgen предоставляет привязки Rust FFI к библиотекам C (с некоторой ограниченной поддержкой C++, для которой требуется установить свойство cppstd ).

Базовое использование rust_bindgen

Ниже приведён пример того, как определить модуль, использующий bindgen, и как использовать этот модуль в качестве крейта. Если вам необходимо использовать привязки bindgen через макрос include!() , например, для внешнего кода, см. страницу «Генераторы исходного кода ».

Пример библиотеки на языке 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

Определите заголовочный файл-обертку, 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.

В привязках 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",
}

Видимость и взаимосвязь

Сгенерированные привязки обычно очень малы, поскольку состоят из определений типов, сигнатур функций и связанных констант. В результате динамическая компоновка этих библиотек, как правило, нецелесообразна. Мы отключили динамическую компоновку для этих модулей, чтобы при их использовании через rustlibs автоматически выбирался статический вариант.

По умолчанию модули rust_bindgen имеют свойство visibility [":__subpackages__"] , которое позволяет видеть их только модулям, находящимся в том же файле Android.bp или расположенным ниже в иерархии каталогов. Это служит двум целям:

  • Это препятствует использованию необработанных C-привязок в других частях дерева.
  • Это позволяет избежать проблем с ромбовидным связыванием, возникающих при сочетании статического и динамического связывания.

Обычно следует предоставлять безопасную библиотеку-обертку вокруг сгенерированного модуля, добавленного в ту же директорию, что и привязки, предназначенную для использования другими разработчиками. Если это не подходит для вашего случая, вы можете добавить дополнительные пакеты в область видимости . При добавлении дополнительных областей видимости, пожалуйста, убедитесь, что вы не добавляете две области видимости, которые могут быть связаны с одним и тем же процессом в будущем, так как это может привести к ошибке связывания.

Примечательные свойства rust_bingen

Описанные ниже свойства дополняют важные общие свойства , применимые ко всем модулям. Они либо особенно важны для модулей Rust bindgen, либо демонстрируют уникальное поведение, специфичное для типа модуля rust_bindgen .

stem, name, crat_name

rust_bindgen создает варианты библиотек, поэтому они предъявляют те же требования к свойствам stem , name и crate_name , что и модули rust_library . См. раздел «Заметные свойства библиотек Rust» для получения дополнительной информации.

wrapper_src

Это относительный путь к заголовочному файлу-обертке, содержащему заголовочные файлы, необходимые для этих привязок. Расширение файла определяет, как интерпретировать заголовок, и определяет, какой флаг -std использовать по умолчанию. Предполагается, что это заголовок C, если только расширение не .hh или .hpp . Если ваш заголовок C++ должен иметь другое расширение, установите свойство cpp_std , чтобы переопределить поведение по умолчанию, которое предполагает, что файл является файлом C.

исходная_система

Это имя файла для сгенерированного исходного файла . Это поле необходимо определить, даже если вы используете привязки в качестве крейта, поскольку свойство stem управляет только именем выходного файла для сгенерированных вариантов библиотеки. Если модуль зависит от нескольких генераторов исходного кода (таких как bindgen и protobuf ) в качестве исходного кода, а не в качестве крейтов через rustlibs , необходимо убедиться, что все генераторы исходного кода, являющиеся зависимостями этого модуля, имеют уникальные значения source_stem . Зависимые модули копируют исходные файлы из всех зависимостей SourceProvider , определенных в srcs в общий каталог OUT_DIR , поэтому конфликты в source_stem приведут к перезаписи сгенерированных исходных файлов в каталоге OUT_DIR .

c_std

Это строка, указывающая, какую версию стандарта C следует использовать. Допустимые значения перечислены ниже:

  • Конкретная версия, например, "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

Для сложных сценариев использования bindgen можно применять как библиотеку, предоставляющую API, которым можно управлять как частью пользовательского исполняемого файла Rust. Поле custom_bindgen принимает имя модуля rust_binary_host , который использует API bindgen вместо обычного исполняемого файла 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 and static_inline_library

Эти два свойства предназначены для совместного использования и позволяют создавать обертки для статических встроенных функций, которые могут быть включены в экспортируемые привязки 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: ["."],
    }