Le système de construction prend en charge la génération de liaisons bindgen via le type de module rust_bindgen . Bindgen fournit des liaisons Rust FFI aux bibliothèques C (avec une prise en charge limitée de C++, qui nécessite la définition de la propriété cppstd ).

Utilisation de base de rust_bindgen

Ce qui suit est un exemple de la manière de définir un module qui utilise bindgen et de la manière d'utiliser ce module comme caisse. Si vous devez utiliser des liaisons bindgen via une macro include!() , par exemple pour du code externe, consultez la page Générateurs de sources .

Exemple de bibliothèque C à appeler depuis Rust

Voici un exemple de bibliothèque C qui définit une structure et une fonction à utiliser dans 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);
}

Définir un module rust_bindgen

Définissez un en-tête wrapper, external/rust/libbuzz/libbuzz_wrapper.h , qui inclut tous les en-têtes pertinents :

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

Définissez le fichier Android.bp comme 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"],
}

Pour en savoir plus sur l'utilisation des indicateurs bindgen, consultez la section du manuel bindgen sur la personnalisation des liaisons générées .

Si vous avez utilisé cette section pour définir un module rust_bindgen comme condition préalable à l'utilisation de la macro include!() , revenez à Prérequis sur la page Générateurs de sources. Sinon, passez aux sections suivantes.

Utiliser les fixations comme caisse

Créez external/rust/hello_bindgen/Android.bp avec le contenu suivant :

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

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

Créez external/rust/hello_bindgen/src/main.rs avec le contenu suivant :

//! 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) }
}

Enfin, appelez m hello_bindgen pour construire le binaire.

Tester les liaisons Bindgen

Les liaisons Bindgen contiennent généralement un certain nombre de tests de disposition générés pour éviter les incohérences de disposition de la mémoire. AOSP vous recommande de définir un module de test pour ces tests et d'exécuter les tests dans le cadre de la suite de tests normale de votre projet.

Un binaire de test pour ceux-ci peut être facilement produit en définissant un module rust_test dans 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",
}

Visibilité et lien

Les liaisons générées sont généralement très petites, car elles sont constituées de définitions de types, de signatures de fonctions et de constantes associées. En conséquence, il est généralement inutile de lier ces bibliothèques de manière dynamique. Nous avons désactivé la liaison dynamique pour ces modules afin que leur utilisation via rustlibs sélectionne automatiquement une variante statique.

Par défaut, les modules rust_bindgen ont une propriété visibility de [":__subpackages__"] , qui permettra uniquement aux modules du même fichier Android.bp ou à ceux en dessous dans la hiérarchie des répertoires de le voir. Cela répond à deux objectifs :

  • Cela décourage l'utilisation de liaisons C brutes ailleurs dans l'arborescence.
  • Il évite les problèmes de liaison en diamant avec un mélange de liaison statique et dynamique.

Habituellement, vous devez fournir une bibliothèque wrapper sécurisée autour du module généré que vous avez ajouté dans la même arborescence de répertoires que les liaisons, destinée à être utilisée par d'autres développeurs. Si cela ne fonctionne pas pour votre cas d'utilisation, vous pouvez ajouter des packages supplémentaires à visibilité . Lorsque vous ajoutez des étendues de visibilité supplémentaires, veillez à ne pas ajouter deux étendues qui pourraient être liées dans le même processus à l'avenir, car la liaison pourrait échouer.

Propriétés notables de rust_bindgen

Les propriétés définies ci-dessous s'ajoutent aux propriétés communes importantes qui s'appliquent à tous les modules. Ceux-ci sont soit particulièrement importants pour les modules Rust bindgen, soit présentent un comportement unique spécifique au type de module rust_bindgen .

tige, nom, nom_crate

rust_bindgen produit des variantes de bibliothèque, elles partagent donc les mêmes exigences avec les modules rust_library pour les propriétés stem , name et crate_name . Voir Propriétés notables de la bibliothèque Rust pour référence.

wrapper_src

Il s'agit du chemin relatif vers un fichier d'en-tête de wrapper qui inclut les en-têtes requis pour ces liaisons. L'extension de fichier détermine comment interpréter l'en-tête et détermine quel indicateur -std utiliser par défaut. Ceci est supposé être un en-tête C sauf si l'extension est .hh ou .hpp . Si votre en-tête C++ doit avoir une autre extension, définissez la propriété cpp_std pour remplacer le comportement par défaut qui suppose que le fichier est un fichier C.

source_stem

Il s'agit du nom de fichier du fichier source généré . Ce champ doit être défini, même si vous utilisez les liaisons comme caisse, puisque la propriété stem contrôle uniquement le nom du fichier de sortie pour les variantes de bibliothèque générées. Si un module dépend de plusieurs générateurs de sources (tels que bindgen et protobuf ) comme source plutôt que comme crates via rustlibs , vous devez vous assurer que tous les générateurs de sources qui sont des dépendances de ce module ont des valeurs source_stem uniques. Les modules dépendants copient les sources de toutes les dépendances SourceProvider qui sont définies dans srcs vers un répertoire OUT_DIR commun, donc des collisions dans source_stem entraîneraient l'écrasement des fichiers sources générés dans le répertoire OUT_DIR .

c_std

Il s'agit d'une chaîne représentant la version du standard C à utiliser. Les valeurs valides sont répertoriées ci-dessous :

  • Une version spécifique, telle que "gnu11" .
  • "experimental" , qui est une valeur définie par le système de build dans build/soong/cc/config/global.go , peut utiliser des versions brouillon comme C++1z lorsqu'elles sont disponibles.
  • Unset ou "" , qui indique que la valeur par défaut du système de build doit être utilisée.

Si cette option est définie, l'extension du fichier est ignorée et l'en-tête est supposé être un en-tête C. Cela ne peut pas être défini en même temps que cpp_std .

cpp_std

cpp_std est une chaîne représentant la version standard C à utiliser. Valeurs valides :

  • Une version spécifique, telle que "gnu++11"
  • "experimental" , qui est une valeur définie par le système de build dans build/soong/cc/config/global.go , peut utiliser des versions brouillon comme C++1z lorsqu'elles sont disponibles.
  • Unset ou "" , qui indique que la valeur par défaut du système de build doit être utilisée.

Si cette valeur est définie, l'extension du fichier est ignorée et l'en-tête est supposé être un en-tête C++. Cela ne peut pas être défini en même temps que c_std .

drapeaux

cflags fournit une liste de chaînes d'indicateurs Clang nécessaires pour interpréter correctement les en-têtes.

personnalisé_bindgen

Pour les cas d'utilisation avancés, bindgen peut être utilisé comme bibliothèque, fournissant une API qui peut être manipulée dans le cadre d'un binaire Rust personnalisé. Le champ custom_bindgen prend le nom d'un module rust_binary_host , qui utilise l'API bindgen au lieu du binaire bindgen normal.

Ce binaire personnalisé doit s'attendre à des arguments de la même manière que bindgen , tels que

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

La plupart de ces tâches sont gérées par la bibliothèque bindgen elle-même. Pour voir un exemple de cette utilisation, visitez external/rust/crates/libsqlite3-sys/android/build.rs .

De plus, l'ensemble complet des propriétés de la bibliothèque est disponible pour contrôler la compilation de la bibliothèque, bien qu'il soit rarement nécessaire de les définir ou de les modifier.