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 compatibilité C++ limitée, 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 la page 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) }
}

Enfin, appelez 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.

Un binaire de test 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 association

Les liaisons générées sont généralement très petites, car elles se composent de définitions de types, de signatures de fonctions et de constantes associées. Par conséquent, il est généralement inutile d'associer 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 n'autorise que les modules du même fichier Android.bp ou ceux situés en dessous dans la hiérarchie des répertoires à le voir. Cela a deux objectifs :

  • Il décourage l'utilisation de liaisons C brutes ailleurs dans l'arborescence.
  • Il évite les problèmes d'association en losange en combinant association statique et dynamique.

En règle générale, 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 à la visibilité. Lorsque vous ajoutez des niveaux de visibilité supplémentaires, veillez à ne pas ajouter deux niveaux qui pourraient être liés au même processus à l'avenir, car cela pourrait entraîner un échec de l'association.

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. Ils sont particulièrement importants 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 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 quelle option -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, 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 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 ci-dessous :

  • 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. Valeurs correctes :

  • 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 que ces propriétés n'aient que rarement besoin d'être définies ou modifiées.

handle_static_inline et static_inline_library

Ces deux propriétés sont conçues pour être utilisées ensemble et permettent de générer des wrappers pour les fonctions intégrées statiques qui peuvent être incluses 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.

Exemples d'utilisation :

    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: ["."],
    }