Modules de liaison Bindgen

Le système de compilation permet de générer des 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 de définir la propriété cppstd).

Utilisation de base de rust_bindgen

Vous trouverez ci-dessous un exemple de définition d'un module qui utilise bindgen et de la manière d'utiliser ce module en tant que crate. Si vous devez utiliser des liaisons bindgen via une macro include!(), par exemple pour du code externe, consultez Générateurs de source.

Exemple de bibliothèque C à appeler depuis Rust

Vous trouverez ci-dessous 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 de 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 à Condition préalable sur la page "Générateurs de sources". Si ce n'est pas le cas, passez aux sections suivantes.

Utiliser des liaisons 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) }
}

Appelez ensuite m hello_bindgen pour compiler le fichier binaire.

Tester les liaisons bindgen

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

Pour ce faire, vous pouvez générer un binaire de test 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 association

Les liaisons générées sont généralement de petite taille, car elles se composent de définitions de types, de signatures de fonctions et de constantes associées. Par conséquent, il est inutile d'associer ces bibliothèques de manière dynamique. Nous avons désactivé la liaison dynamique pour ces modules afin que leur utilisation avec rustlibs sélectionne automatiquement une variante statique.

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

  • Décourage l'utilisation de liaisons C brutes ailleurs dans l'arborescence.
  • Évite les problèmes d'association en losange grâce à une combinaison d'association statique et dynamique.

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

Propriétés notables de rust_bindgen

Les propriétés définies dans cette section s'ajoutent aux propriétés communes importantes qui s'appliquent à tous les modules. Elles sont importantes pour les modules Rust bindgen ou présentent un comportement unique propre au type de module rust_bindgen.

stem, name, crate_name

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

wrapper_src

Il s'agit du chemin d'accès relatif à un fichier d'en-tête de wrapper qui inclut les en-têtes requis pour ces liaisons. L'extension du fichier détermine comment interpréter l'en-tête et quel indicateur -std utiliser par défaut. Il s'agit d'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, car la propriété stem ne contrôle que le nom de 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) en tant que source plutôt qu'en tant que caisses via rustlibs, assurez-vous 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 définies dans srcs dans un répertoire OUT_DIR commun. Par conséquent, les 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 de la norme C à utiliser. Les valeurs valides sont listées ici :

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

Si cette option est définie, l'extension de fichier est ignorée et l'en-tête est considéré comme un en-tête C. Ce paramètre 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 de C à utiliser. Les valeurs valides sont listées ici :

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

Si cette option est définie, l'extension de fichier est ignorée et l'en-tête est considéré comme un en-tête C++. Ce paramètre ne peut pas être défini en même temps que c_std.

cflags

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

custom_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 accepte le nom de module 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, par exemple :

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

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

De plus, l'ensemble 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.

handle_static_inline et static_inline_library

Ces deux propriétés sont destinées à être utilisées ensemble et permettent de produire des wrappers pour les fonctions intégrées statiques qui peuvent être inclus dans les liaisons bindgen exportées.

Pour les utiliser, définissez handle_static_inline: true et static_inline_library sur un cc_library_static correspondant qui définit le module rust_bindgen comme entrée source.

Exemple d'utilisation :

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

        // Produce bindings for static inline functions
        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: ["."],
    }