Générateurs de sources

Cette page offre une vue d'ensemble de la façon dont la source générée est prise en charge et de la façon dont elle peut être utilisée dans le système de compilation.

Tous les générateurs de sources offrent des fonctionnalités de système de compilation similaires. Les trois cas d'utilisation de la génération de code source compatibles avec le système de compilation sont la génération de liaisons C à l'aide de bindgen, d'interfaces AIDL et d'interfaces protobuf.

Crates à partir de la source générée

Chaque module Rust qui génère du code source peut être utilisé comme un crate, exactement comme s'il était défini comme un rust_library. (Cela signifie qu'il peut être défini comme une dépendance dans les propriétés rustlibs, rlibs et dylibs.) Le meilleur modèle d'utilisation du code de plate-forme consiste à utiliser la source générée comme caisse. Bien que la macro include! soit compatible avec la source générée, son objectif principal est de prendre en charge le code tiers qui réside dans external/.

Il peut arriver que le code de la plate-forme utilise toujours la source générée via la macro include!(), par exemple lorsque vous utilisez un module genrule pour générer une source de manière unique.

Utiliser include!() pour inclure la source générée

L'utilisation de la source générée en tant que crate est abordée dans les exemples de chaque page de module spécifique (respective). Cette section explique comment référencer la source générée à l'aide de la macro include!(). Notez que ce processus est similaire pour tous les générateurs de sources.

Prérequis

Cet exemple part du principe que vous avez défini un module rust_bindgen (libbuzz_bindgen) et que vous pouvez passer à la section Étapes pour inclure la source générée pour utiliser la macro include!(). Si ce n'est pas le cas, veuillez consulter Définir un module bindgen Rust, créer libbuzz_bindgen, puis revenir ici.

Notez que les parties du fichier de compilation sont applicables à tous les générateurs de sources.

Étapes à suivre pour inclure une source générée

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

rust_binary {
   name: "hello_bzip_bindgen_include",
   srcs: [
         // The primary rust source file must come first in this list.
         "src/lib.rs",

         // The module providing the bindgen bindings is
         // included in srcs prepended by ":".
         ":libbuzz_bindgen",
    ],

    // Dependencies need to be redeclared when generated source is used via srcs.
    shared_libs: [
        "libbuzz",
    ],
}

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

#![allow(clippy::all)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(unused)]
#![allow(missing_docs)]

// Note that "bzip_bindings.rs" here must match the source_stem property from
// the rust_bindgen module.
include!(concat!(env!("OUT_DIR"), "/bzip_bindings.rs"));

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

mod bindings;

fn main() {
    let mut x = bindings::foo { x: 2 };
    unsafe { bindings::fizz(1, &mut x as *mut bindings::foo) }
}

Pourquoi des caisses pour la source générée ?

Contrairement aux compilateurs C/C++, rustc n'accepte qu'un seul fichier source représentant un point d'entrée vers un binaire ou une bibliothèque. Il s'attend à ce que l'arborescence source soit structurée de manière à ce que tous les fichiers sources requis puissent être découverts automatiquement. Cela signifie que la source générée doit être placée dans l'arborescence source ou fournie par le biais d'une directive include dans la source :

include!("/path/to/hello.rs");

La communauté Rust dépend des scripts build.rs et des hypothèses concernant l'environnement de compilation Cargo pour gérer cette différence. Lors de la compilation, la commande cargo définit une variable d'environnement OUT_DIR dans laquelle les scripts build.rs sont censés placer le code source généré. Exécutez la commande suivante pour inclure le code source :

include!(concat!(env!("OUT_DIR"), "/hello.rs"));

Cela pose un problème pour Soong, car les sorties de chaque module sont placées dans leur propre répertoire out/1. Il n'existe pas de OUT_DIR unique où les dépendances génèrent leur source.

Pour le code de plate-forme, AOSP préfère empaqueter la source générée dans un crate qui peut être importé, pour plusieurs raisons :

  • Empêchez les noms de fichiers sources générés d'entrer en conflit.
  • Réduisez le code récurrent enregistré dans l'arborescence et nécessitant une maintenance. Toute la boilerplate nécessaire pour que la compilation de la source générée dans un crate puisse être gérée de manière centralisée.
  • Évitez les interactions implicites2 entre le code généré et la caisse environnante.
  • Réduisez la pression sur la mémoire et le disque en associant dynamiquement les sources générées couramment utilisées.

Par conséquent, tous les types de modules de génération de sources Rust d'Android produisent du code qui peut être compilé et utilisé en tant que crate. Soong est toujours compatible avec les crates tiers sans modification si toutes les dépendances de source générées pour un module sont copiées dans un seul répertoire par module, comme Cargo. Dans ce cas, Soong définit la variable d'environnement OUT_DIR sur ce répertoire lors de la compilation du module, afin que la source générée puisse être trouvée. Toutefois, pour les raisons déjà décrites, il est recommandé de n'utiliser ce mécanisme dans le code de plate-forme que lorsque cela est absolument nécessaire.


  1. Cela ne pose aucun problème pour C/C++ et les langages similaires, car le chemin d'accès à la source générée est fourni directement au compilateur. 

  2. Comme include! fonctionne par inclusion textuelle, il peut faire référence à des valeurs de l'espace de noms englobant, modifier l'espace de noms ou utiliser des constructions telles que #![foo]. Il peut être difficile de maintenir ces interactions implicites. Privilégiez toujours les macros lorsque l'interaction avec le reste de la crate est vraiment nécessaire.