Le système de compilation permet de générer des liaisons bindgen via le type de module rust_bindgen. Bindgen fournit des liaisons FFI Rust 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

Voici un exemple de définition d'un module qui utilise bindgen et d'utilisation de 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 sources.

Exemple de bibliothèque C à appeler depuis Rust

Vous trouverez ci-dessous un exemple de bibliothèque C qui définit une struct 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 options bindgen, consultez la section Personnaliser les liaisons générées du manuel bindgen.

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

Utiliser des liaisons en tant que crate

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 créer le 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 incompatibilités de mise en page de la mémoire. AOSP vous 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 pour ces éléments peut être facilement généré 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 consistent en des définitions de type, des signatures de fonction et des constantes associées. Par conséquent, il est généralement inutile de relier ces bibliothèques de manière dynamique. Nous avons désactivé le lien dynamique pour ces modules afin que leur utilisation via rustlibs sélectionne automatiquement une variante statique.

Par défaut, la propriété visibility des modules rust_bindgen est [":__subpackages__"], ce qui permet uniquement aux modules du même fichier Android.bp ou à ceux situés en dessous dans la hiérarchie de répertoires de l'afficher. Cela a deux objectifs:

  • Elle déconseille l'utilisation de liaisons C brutes ailleurs dans l'arbre.
  • Il évite les problèmes de liaison en losange grâce à un mélange de liaisons statiques et dynamiques.

En règle générale, vous devez fournir une bibliothèque de wrapper sécurisée autour du module généré que vous avez ajouté dans le même arbre de répertoire que les liaisons, qui est destiné à être utilisé par d'autres développeurs. Si cette solution ne convient pas à votre cas d'utilisation, vous pouvez ajouter des packages supplémentaires à la visibilité. Lorsque vous ajoutez des étendues de visibilité supplémentaires, veillez à ne pas ajouter deux étendues qui pourraient être associées au même processus à l'avenir, car l'association risque de ne pas fonctionner.

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 les propriétés notables des bibliothèques 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 de fichier détermine comment interpréter l'en-tête et détermine l'option -std à utiliser par défaut. Il s'agit en principe 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 du fichier source généré. Ce champ doit être défini, même si vous utilisez les liaisons en tant que crate, car la propriété stem ne contrôle que le nom de fichier de sortie des 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 que sous forme de cratères 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, en cas de collision dans source_stem, les fichiers sources générés seront écrasé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 brouillons telles que C++1z lorsqu'elles sont disponibles.
  • Non défini ou "", ce qui indique que le paramètre par défaut du système de compilation doit être utilisé.

Si ce paramètre est défini, l'extension de fichier est ignorée et l'en-tête est supposé être un en-tête C. Cette valeur ne peut pas être définie en même temps que cpp_std.

cpp_std

cpp_std est une chaîne représentant la version standard 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 brouillons telles que C++1z lorsqu'elles sont disponibles.
  • Non défini ou "", qui indique que la valeur par défaut du système de compilation doit être utilisée.

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

cflags

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

liaison_personnalisée

Pour les cas d'utilisation avancés, linkgen peut être utilisé comme bibliothèque, fournissant ainsi une API pouvant être manipulée dans le cadre d'un binaire Rust personnalisé. Le champ custom_bindgen prend 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 manière similaire à bindgen, par exemple :

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

La plupart de ces éléments sont gérés 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 complet des propriétés de la bibliothèque est disponible pour contrôler la compilation de la bibliothèque, bien que ces éléments ne nécessitent que rarement d'être définis ou modifiés.

handle_static_inline et statique_inline_library

Ces deux propriétés sont destinées à être utilisées ensemble et permettent de générer des wrappers pour les fonctions intégrées statiques pouvant ê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.

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